diff options
Diffstat (limited to '.venv/lib/python3.12/site-packages/xlsxwriter/vml.py')
-rw-r--r-- | .venv/lib/python3.12/site-packages/xlsxwriter/vml.py | 707 |
1 files changed, 707 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/xlsxwriter/vml.py b/.venv/lib/python3.12/site-packages/xlsxwriter/vml.py new file mode 100644 index 00000000..f97bc210 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/xlsxwriter/vml.py @@ -0,0 +1,707 @@ +############################################################################### +# +# Vml - A class for writing the Excel XLSX Vml file. +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2013-2025, John McNamara, jmcnamara@cpan.org +# + +# Package imports. +from . import xmlwriter + + +class Vml(xmlwriter.XMLwriter): + """ + A class for writing the Excel XLSX Vml file. + + + """ + + ########################################################################### + # + # Private API. + # + ########################################################################### + def _assemble_xml_file( + self, + data_id, + vml_shape_id, + comments_data=None, + buttons_data=None, + header_images_data=None, + ): + # Assemble and write the XML file. + z_index = 1 + + self._write_xml_namespace() + + # Write the o:shapelayout element. + self._write_shapelayout(data_id) + + if buttons_data: + # Write the v:shapetype element. + self._write_button_shapetype() + + for button in buttons_data: + # Write the v:shape element. + vml_shape_id += 1 + self._write_button_shape(vml_shape_id, z_index, button) + z_index += 1 + + if comments_data: + # Write the v:shapetype element. + self._write_comment_shapetype() + + for comment in comments_data: + # Write the v:shape element. + vml_shape_id += 1 + self._write_comment_shape(vml_shape_id, z_index, comment) + z_index += 1 + + if header_images_data: + # Write the v:shapetype element. + self._write_image_shapetype() + + index = 1 + for image in header_images_data: + # Write the v:shape element. + vml_shape_id += 1 + self._write_image_shape(vml_shape_id, index, image) + index += 1 + + self._xml_end_tag("xml") + + # Close the XML writer filehandle. + self._xml_close() + + def _pixels_to_points(self, vertices): + # Convert comment vertices from pixels to points. + + left, top, width, height = vertices[8:12] + + # Scale to pixels. + left *= 0.75 + top *= 0.75 + width *= 0.75 + height *= 0.75 + + return left, top, width, height + + ########################################################################### + # + # XML methods. + # + ########################################################################### + def _write_xml_namespace(self): + # Write the <xml> element. This is the root element of VML. + schema = "urn:schemas-microsoft-com:" + xmlns = schema + "vml" + xmlns_o = schema + "office:office" + xmlns_x = schema + "office:excel" + + attributes = [ + ("xmlns:v", xmlns), + ("xmlns:o", xmlns_o), + ("xmlns:x", xmlns_x), + ] + + self._xml_start_tag("xml", attributes) + + def _write_shapelayout(self, data_id): + # Write the <o:shapelayout> element. + attributes = [("v:ext", "edit")] + + self._xml_start_tag("o:shapelayout", attributes) + + # Write the o:idmap element. + self._write_idmap(data_id) + + self._xml_end_tag("o:shapelayout") + + def _write_idmap(self, data_id): + # Write the <o:idmap> element. + attributes = [ + ("v:ext", "edit"), + ("data", data_id), + ] + + self._xml_empty_tag("o:idmap", attributes) + + def _write_comment_shapetype(self): + # Write the <v:shapetype> element. + shape_id = "_x0000_t202" + coordsize = "21600,21600" + spt = 202 + path = "m,l,21600r21600,l21600,xe" + + attributes = [ + ("id", shape_id), + ("coordsize", coordsize), + ("o:spt", spt), + ("path", path), + ] + + self._xml_start_tag("v:shapetype", attributes) + + # Write the v:stroke element. + self._write_stroke() + + # Write the v:path element. + self._write_comment_path("t", "rect") + + self._xml_end_tag("v:shapetype") + + def _write_button_shapetype(self): + # Write the <v:shapetype> element. + shape_id = "_x0000_t201" + coordsize = "21600,21600" + spt = 201 + path = "m,l,21600r21600,l21600,xe" + + attributes = [ + ("id", shape_id), + ("coordsize", coordsize), + ("o:spt", spt), + ("path", path), + ] + + self._xml_start_tag("v:shapetype", attributes) + + # Write the v:stroke element. + self._write_stroke() + + # Write the v:path element. + self._write_button_path() + + # Write the o:lock element. + self._write_shapetype_lock() + + self._xml_end_tag("v:shapetype") + + def _write_image_shapetype(self): + # Write the <v:shapetype> element. + shape_id = "_x0000_t75" + coordsize = "21600,21600" + spt = 75 + o_preferrelative = "t" + path = "m@4@5l@4@11@9@11@9@5xe" + filled = "f" + stroked = "f" + + attributes = [ + ("id", shape_id), + ("coordsize", coordsize), + ("o:spt", spt), + ("o:preferrelative", o_preferrelative), + ("path", path), + ("filled", filled), + ("stroked", stroked), + ] + + self._xml_start_tag("v:shapetype", attributes) + + # Write the v:stroke element. + self._write_stroke() + + # Write the v:formulas element. + self._write_formulas() + + # Write the v:path element. + self._write_image_path() + + # Write the o:lock element. + self._write_aspect_ratio_lock() + + self._xml_end_tag("v:shapetype") + + def _write_stroke(self): + # Write the <v:stroke> element. + joinstyle = "miter" + + attributes = [("joinstyle", joinstyle)] + + self._xml_empty_tag("v:stroke", attributes) + + def _write_comment_path(self, gradientshapeok, connecttype): + # Write the <v:path> element. + attributes = [] + + if gradientshapeok: + attributes.append(("gradientshapeok", "t")) + + attributes.append(("o:connecttype", connecttype)) + + self._xml_empty_tag("v:path", attributes) + + def _write_button_path(self): + # Write the <v:path> element. + shadowok = "f" + extrusionok = "f" + strokeok = "f" + fillok = "f" + connecttype = "rect" + + attributes = [ + ("shadowok", shadowok), + ("o:extrusionok", extrusionok), + ("strokeok", strokeok), + ("fillok", fillok), + ("o:connecttype", connecttype), + ] + + self._xml_empty_tag("v:path", attributes) + + def _write_image_path(self): + # Write the <v:path> element. + extrusionok = "f" + gradientshapeok = "t" + connecttype = "rect" + + attributes = [ + ("o:extrusionok", extrusionok), + ("gradientshapeok", gradientshapeok), + ("o:connecttype", connecttype), + ] + + self._xml_empty_tag("v:path", attributes) + + def _write_shapetype_lock(self): + # Write the <o:lock> element. + ext = "edit" + shapetype = "t" + + attributes = [ + ("v:ext", ext), + ("shapetype", shapetype), + ] + + self._xml_empty_tag("o:lock", attributes) + + def _write_rotation_lock(self): + # Write the <o:lock> element. + ext = "edit" + rotation = "t" + + attributes = [ + ("v:ext", ext), + ("rotation", rotation), + ] + + self._xml_empty_tag("o:lock", attributes) + + def _write_aspect_ratio_lock(self): + # Write the <o:lock> element. + ext = "edit" + aspectratio = "t" + + attributes = [ + ("v:ext", ext), + ("aspectratio", aspectratio), + ] + + self._xml_empty_tag("o:lock", attributes) + + def _write_comment_shape(self, shape_id, z_index, comment): + # Write the <v:shape> element. + shape_type = "#_x0000_t202" + insetmode = "auto" + visibility = "hidden" + + # Set the shape index. + shape_id = "_x0000_s" + str(shape_id) + + # Get the comment parameters + row = comment[0] + col = comment[1] + visible = comment[4] + fillcolor = comment[5] + vertices = comment[9] + + (left, top, width, height) = self._pixels_to_points(vertices) + + # Set the visibility. + if visible: + visibility = "visible" + + style = ( + f"position:absolute;" + f"margin-left:{left:.15g}pt;" + f"margin-top:{top:.15g}pt;" + f"width:{width:.15g}pt;" + f"height:{height:.15g}pt;" + f"z-index:{z_index};" + f"visibility:{visibility}" + ) + + attributes = [ + ("id", shape_id), + ("type", shape_type), + ("style", style), + ("fillcolor", fillcolor), + ("o:insetmode", insetmode), + ] + + self._xml_start_tag("v:shape", attributes) + + # Write the v:fill element. + self._write_comment_fill() + + # Write the v:shadow element. + self._write_shadow() + + # Write the v:path element. + self._write_comment_path(None, "none") + + # Write the v:textbox element. + self._write_comment_textbox() + + # Write the x:ClientData element. + self._write_comment_client_data(row, col, visible, vertices) + + self._xml_end_tag("v:shape") + + def _write_button_shape(self, shape_id, z_index, button): + # Write the <v:shape> element. + shape_type = "#_x0000_t201" + + # Set the shape index. + shape_id = "_x0000_s" + str(shape_id) + + # Get the button parameters. + # row = button["_row"] + # col = button["_col"] + vertices = button["vertices"] + + (left, top, width, height) = self._pixels_to_points(vertices) + + style = ( + f"position:absolute;" + f"margin-left:{left:.15g}pt;" + f"margin-top:{top:.15g}pt;" + f"width:{width:.15g}pt;" + f"height:{height:.15g}pt;" + f"z-index:{z_index};" + f"mso-wrap-style:tight" + ) + + attributes = [ + ("id", shape_id), + ("type", shape_type), + ] + + if button.get("description"): + attributes.append(("alt", button["description"])) + + attributes.append(("style", style)) + attributes.append(("o:button", "t")) + attributes.append(("fillcolor", "buttonFace [67]")) + attributes.append(("strokecolor", "windowText [64]")) + attributes.append(("o:insetmode", "auto")) + + self._xml_start_tag("v:shape", attributes) + + # Write the v:fill element. + self._write_button_fill() + + # Write the o:lock element. + self._write_rotation_lock() + + # Write the v:textbox element. + self._write_button_textbox(button["font"]) + + # Write the x:ClientData element. + self._write_button_client_data(button) + + self._xml_end_tag("v:shape") + + def _write_image_shape(self, shape_id, z_index, image_data): + # Write the <v:shape> element. + shape_type = "#_x0000_t75" + + # Set the shape index. + shape_id = "_x0000_s" + str(shape_id) + + # Get the image parameters + width = image_data[0] + height = image_data[1] + name = image_data[2] + position = image_data[3] + x_dpi = image_data[4] + y_dpi = image_data[5] + ref_id = image_data[6] + + # Scale the height/width by the resolution, relative to 72dpi. + width = width * 72.0 / x_dpi + height = height * 72.0 / y_dpi + + # Excel uses a rounding based around 72 and 96 dpi. + width = 72.0 / 96 * int(width * 96.0 / 72 + 0.25) + height = 72.0 / 96 * int(height * 96.0 / 72 + 0.25) + + style = ( + f"position:absolute;" + f"margin-left:0;" + f"margin-top:0;" + f"width:{width:.15g}pt;" + f"height:{height:.15g}pt;" + f"z-index:{z_index}" + ) + + attributes = [ + ("id", position), + ("o:spid", shape_id), + ("type", shape_type), + ("style", style), + ] + + self._xml_start_tag("v:shape", attributes) + + # Write the v:imagedata element. + self._write_imagedata(ref_id, name) + + # Write the o:lock element. + self._write_rotation_lock() + + self._xml_end_tag("v:shape") + + def _write_comment_fill(self): + # Write the <v:fill> element. + color_2 = "#ffffe1" + + attributes = [("color2", color_2)] + + self._xml_empty_tag("v:fill", attributes) + + def _write_button_fill(self): + # Write the <v:fill> element. + color_2 = "buttonFace [67]" + detectmouseclick = "t" + + attributes = [ + ("color2", color_2), + ("o:detectmouseclick", detectmouseclick), + ] + + self._xml_empty_tag("v:fill", attributes) + + def _write_shadow(self): + # Write the <v:shadow> element. + on = "t" + color = "black" + obscured = "t" + + attributes = [ + ("on", on), + ("color", color), + ("obscured", obscured), + ] + + self._xml_empty_tag("v:shadow", attributes) + + def _write_comment_textbox(self): + # Write the <v:textbox> element. + style = "mso-direction-alt:auto" + + attributes = [("style", style)] + + self._xml_start_tag("v:textbox", attributes) + + # Write the div element. + self._write_div("left") + + self._xml_end_tag("v:textbox") + + def _write_button_textbox(self, font): + # Write the <v:textbox> element. + style = "mso-direction-alt:auto" + + attributes = [("style", style), ("o:singleclick", "f")] + + self._xml_start_tag("v:textbox", attributes) + + # Write the div element. + self._write_div("center", font) + + self._xml_end_tag("v:textbox") + + def _write_div(self, align, font=None): + # Write the <div> element. + + style = "text-align:" + align + + attributes = [("style", style)] + + self._xml_start_tag("div", attributes) + + if font: + # Write the font element. + self._write_font(font) + + self._xml_end_tag("div") + + def _write_font(self, font): + # Write the <font> element. + caption = font["caption"] + face = "Calibri" + size = 220 + color = "#000000" + + attributes = [ + ("face", face), + ("size", size), + ("color", color), + ] + + self._xml_data_element("font", caption, attributes) + + def _write_comment_client_data(self, row, col, visible, vertices): + # Write the <x:ClientData> element. + object_type = "Note" + + attributes = [("ObjectType", object_type)] + + self._xml_start_tag("x:ClientData", attributes) + + # Write the x:MoveWithCells element. + self._write_move_with_cells() + + # Write the x:SizeWithCells element. + self._write_size_with_cells() + + # Write the x:Anchor element. + self._write_anchor(vertices) + + # Write the x:AutoFill element. + self._write_auto_fill() + + # Write the x:Row element. + self._write_row(row) + + # Write the x:Column element. + self._write_column(col) + + # Write the x:Visible element. + if visible: + self._write_visible() + + self._xml_end_tag("x:ClientData") + + def _write_button_client_data(self, button): + # Write the <x:ClientData> element. + macro = button["macro"] + vertices = button["vertices"] + + object_type = "Button" + + attributes = [("ObjectType", object_type)] + + self._xml_start_tag("x:ClientData", attributes) + + # Write the x:Anchor element. + self._write_anchor(vertices) + + # Write the x:PrintObject element. + self._write_print_object() + + # Write the x:AutoFill element. + self._write_auto_fill() + + # Write the x:FmlaMacro element. + self._write_fmla_macro(macro) + + # Write the x:TextHAlign element. + self._write_text_halign() + + # Write the x:TextVAlign element. + self._write_text_valign() + + self._xml_end_tag("x:ClientData") + + def _write_move_with_cells(self): + # Write the <x:MoveWithCells> element. + self._xml_empty_tag("x:MoveWithCells") + + def _write_size_with_cells(self): + # Write the <x:SizeWithCells> element. + self._xml_empty_tag("x:SizeWithCells") + + def _write_visible(self): + # Write the <x:Visible> element. + self._xml_empty_tag("x:Visible") + + def _write_anchor(self, vertices): + # Write the <x:Anchor> element. + (col_start, row_start, x1, y1, col_end, row_end, x2, y2) = vertices[:8] + + strings = [col_start, x1, row_start, y1, col_end, x2, row_end, y2] + strings = [str(i) for i in strings] + + data = ", ".join(strings) + + self._xml_data_element("x:Anchor", data) + + def _write_auto_fill(self): + # Write the <x:AutoFill> element. + data = "False" + + self._xml_data_element("x:AutoFill", data) + + def _write_row(self, data): + # Write the <x:Row> element. + self._xml_data_element("x:Row", data) + + def _write_column(self, data): + # Write the <x:Column> element. + self._xml_data_element("x:Column", data) + + def _write_print_object(self): + # Write the <x:PrintObject> element. + self._xml_data_element("x:PrintObject", "False") + + def _write_text_halign(self): + # Write the <x:TextHAlign> element. + self._xml_data_element("x:TextHAlign", "Center") + + def _write_text_valign(self): + # Write the <x:TextVAlign> element. + self._xml_data_element("x:TextVAlign", "Center") + + def _write_fmla_macro(self, data): + # Write the <x:FmlaMacro> element. + self._xml_data_element("x:FmlaMacro", data) + + def _write_imagedata(self, ref_id, o_title): + # Write the <v:imagedata> element. + attributes = [ + ("o:relid", "rId" + str(ref_id)), + ("o:title", o_title), + ] + + self._xml_empty_tag("v:imagedata", attributes) + + def _write_formulas(self): + # Write the <v:formulas> element. + self._xml_start_tag("v:formulas") + + # Write the v:f elements. + self._write_formula("if lineDrawn pixelLineWidth 0") + self._write_formula("sum @0 1 0") + self._write_formula("sum 0 0 @1") + self._write_formula("prod @2 1 2") + self._write_formula("prod @3 21600 pixelWidth") + self._write_formula("prod @3 21600 pixelHeight") + self._write_formula("sum @0 0 1") + self._write_formula("prod @6 1 2") + self._write_formula("prod @7 21600 pixelWidth") + self._write_formula("sum @8 21600 0") + self._write_formula("prod @7 21600 pixelHeight") + self._write_formula("sum @10 21600 0") + + self._xml_end_tag("v:formulas") + + def _write_formula(self, eqn): + # Write the <v:f> element. + attributes = [("eqn", eqn)] + + self._xml_empty_tag("v:f", attributes) |