Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/xlsxwriter/vml.py: 15%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1###############################################################################
2#
3# Vml - A class for writing the Excel XLSX Vml file.
4#
5# SPDX-License-Identifier: BSD-2-Clause
6# Copyright 2013-2024, John McNamara, jmcnamara@cpan.org
7#
9# Package imports.
10from . import xmlwriter
13class Vml(xmlwriter.XMLwriter):
14 """
15 A class for writing the Excel XLSX Vml file.
18 """
20 ###########################################################################
21 #
22 # Public API.
23 #
24 ###########################################################################
26 def __init__(self):
27 """
28 Constructor.
30 """
32 super(Vml, self).__init__()
34 ###########################################################################
35 #
36 # Private API.
37 #
38 ###########################################################################
39 def _assemble_xml_file(
40 self,
41 data_id,
42 vml_shape_id,
43 comments_data=None,
44 buttons_data=None,
45 header_images_data=None,
46 ):
47 # Assemble and write the XML file.
48 z_index = 1
50 self._write_xml_namespace()
52 # Write the o:shapelayout element.
53 self._write_shapelayout(data_id)
55 if buttons_data:
56 # Write the v:shapetype element.
57 self._write_button_shapetype()
59 for button in buttons_data:
60 # Write the v:shape element.
61 vml_shape_id += 1
62 self._write_button_shape(vml_shape_id, z_index, button)
63 z_index += 1
65 if comments_data:
66 # Write the v:shapetype element.
67 self._write_comment_shapetype()
69 for comment in comments_data:
70 # Write the v:shape element.
71 vml_shape_id += 1
72 self._write_comment_shape(vml_shape_id, z_index, comment)
73 z_index += 1
75 if header_images_data:
76 # Write the v:shapetype element.
77 self._write_image_shapetype()
79 index = 1
80 for image in header_images_data:
81 # Write the v:shape element.
82 vml_shape_id += 1
83 self._write_image_shape(vml_shape_id, index, image)
84 index += 1
86 self._xml_end_tag("xml")
88 # Close the XML writer filehandle.
89 self._xml_close()
91 def _pixels_to_points(self, vertices):
92 # Convert comment vertices from pixels to points.
94 left, top, width, height = vertices[8:12]
96 # Scale to pixels.
97 left *= 0.75
98 top *= 0.75
99 width *= 0.75
100 height *= 0.75
102 return left, top, width, height
104 ###########################################################################
105 #
106 # XML methods.
107 #
108 ###########################################################################
109 def _write_xml_namespace(self):
110 # Write the <xml> element. This is the root element of VML.
111 schema = "urn:schemas-microsoft-com:"
112 xmlns = schema + "vml"
113 xmlns_o = schema + "office:office"
114 xmlns_x = schema + "office:excel"
116 attributes = [
117 ("xmlns:v", xmlns),
118 ("xmlns:o", xmlns_o),
119 ("xmlns:x", xmlns_x),
120 ]
122 self._xml_start_tag("xml", attributes)
124 def _write_shapelayout(self, data_id):
125 # Write the <o:shapelayout> element.
126 attributes = [("v:ext", "edit")]
128 self._xml_start_tag("o:shapelayout", attributes)
130 # Write the o:idmap element.
131 self._write_idmap(data_id)
133 self._xml_end_tag("o:shapelayout")
135 def _write_idmap(self, data_id):
136 # Write the <o:idmap> element.
137 attributes = [
138 ("v:ext", "edit"),
139 ("data", data_id),
140 ]
142 self._xml_empty_tag("o:idmap", attributes)
144 def _write_comment_shapetype(self):
145 # Write the <v:shapetype> element.
146 shape_id = "_x0000_t202"
147 coordsize = "21600,21600"
148 spt = 202
149 path = "m,l,21600r21600,l21600,xe"
151 attributes = [
152 ("id", shape_id),
153 ("coordsize", coordsize),
154 ("o:spt", spt),
155 ("path", path),
156 ]
158 self._xml_start_tag("v:shapetype", attributes)
160 # Write the v:stroke element.
161 self._write_stroke()
163 # Write the v:path element.
164 self._write_comment_path("t", "rect")
166 self._xml_end_tag("v:shapetype")
168 def _write_button_shapetype(self):
169 # Write the <v:shapetype> element.
170 shape_id = "_x0000_t201"
171 coordsize = "21600,21600"
172 spt = 201
173 path = "m,l,21600r21600,l21600,xe"
175 attributes = [
176 ("id", shape_id),
177 ("coordsize", coordsize),
178 ("o:spt", spt),
179 ("path", path),
180 ]
182 self._xml_start_tag("v:shapetype", attributes)
184 # Write the v:stroke element.
185 self._write_stroke()
187 # Write the v:path element.
188 self._write_button_path()
190 # Write the o:lock element.
191 self._write_shapetype_lock()
193 self._xml_end_tag("v:shapetype")
195 def _write_image_shapetype(self):
196 # Write the <v:shapetype> element.
197 shape_id = "_x0000_t75"
198 coordsize = "21600,21600"
199 spt = 75
200 o_preferrelative = "t"
201 path = "m@4@5l@4@11@9@11@9@5xe"
202 filled = "f"
203 stroked = "f"
205 attributes = [
206 ("id", shape_id),
207 ("coordsize", coordsize),
208 ("o:spt", spt),
209 ("o:preferrelative", o_preferrelative),
210 ("path", path),
211 ("filled", filled),
212 ("stroked", stroked),
213 ]
215 self._xml_start_tag("v:shapetype", attributes)
217 # Write the v:stroke element.
218 self._write_stroke()
220 # Write the v:formulas element.
221 self._write_formulas()
223 # Write the v:path element.
224 self._write_image_path()
226 # Write the o:lock element.
227 self._write_aspect_ratio_lock()
229 self._xml_end_tag("v:shapetype")
231 def _write_stroke(self):
232 # Write the <v:stroke> element.
233 joinstyle = "miter"
235 attributes = [("joinstyle", joinstyle)]
237 self._xml_empty_tag("v:stroke", attributes)
239 def _write_comment_path(self, gradientshapeok, connecttype):
240 # Write the <v:path> element.
241 attributes = []
243 if gradientshapeok:
244 attributes.append(("gradientshapeok", "t"))
246 attributes.append(("o:connecttype", connecttype))
248 self._xml_empty_tag("v:path", attributes)
250 def _write_button_path(self):
251 # Write the <v:path> element.
252 shadowok = "f"
253 extrusionok = "f"
254 strokeok = "f"
255 fillok = "f"
256 connecttype = "rect"
258 attributes = [
259 ("shadowok", shadowok),
260 ("o:extrusionok", extrusionok),
261 ("strokeok", strokeok),
262 ("fillok", fillok),
263 ("o:connecttype", connecttype),
264 ]
266 self._xml_empty_tag("v:path", attributes)
268 def _write_image_path(self):
269 # Write the <v:path> element.
270 extrusionok = "f"
271 gradientshapeok = "t"
272 connecttype = "rect"
274 attributes = [
275 ("o:extrusionok", extrusionok),
276 ("gradientshapeok", gradientshapeok),
277 ("o:connecttype", connecttype),
278 ]
280 self._xml_empty_tag("v:path", attributes)
282 def _write_shapetype_lock(self):
283 # Write the <o:lock> element.
284 ext = "edit"
285 shapetype = "t"
287 attributes = [
288 ("v:ext", ext),
289 ("shapetype", shapetype),
290 ]
292 self._xml_empty_tag("o:lock", attributes)
294 def _write_rotation_lock(self):
295 # Write the <o:lock> element.
296 ext = "edit"
297 rotation = "t"
299 attributes = [
300 ("v:ext", ext),
301 ("rotation", rotation),
302 ]
304 self._xml_empty_tag("o:lock", attributes)
306 def _write_aspect_ratio_lock(self):
307 # Write the <o:lock> element.
308 ext = "edit"
309 aspectratio = "t"
311 attributes = [
312 ("v:ext", ext),
313 ("aspectratio", aspectratio),
314 ]
316 self._xml_empty_tag("o:lock", attributes)
318 def _write_comment_shape(self, shape_id, z_index, comment):
319 # Write the <v:shape> element.
320 shape_type = "#_x0000_t202"
321 insetmode = "auto"
322 visibility = "hidden"
324 # Set the shape index.
325 shape_id = "_x0000_s" + str(shape_id)
327 # Get the comment parameters
328 row = comment[0]
329 col = comment[1]
330 visible = comment[4]
331 fillcolor = comment[5]
332 vertices = comment[9]
334 (left, top, width, height) = self._pixels_to_points(vertices)
336 # Set the visibility.
337 if visible:
338 visibility = "visible"
340 style = (
341 "position:absolute;"
342 "margin-left:%.15gpt;"
343 "margin-top:%.15gpt;"
344 "width:%.15gpt;"
345 "height:%.15gpt;"
346 "z-index:%d;"
347 "visibility:%s" % (left, top, width, height, z_index, visibility)
348 )
350 attributes = [
351 ("id", shape_id),
352 ("type", shape_type),
353 ("style", style),
354 ("fillcolor", fillcolor),
355 ("o:insetmode", insetmode),
356 ]
358 self._xml_start_tag("v:shape", attributes)
360 # Write the v:fill element.
361 self._write_comment_fill()
363 # Write the v:shadow element.
364 self._write_shadow()
366 # Write the v:path element.
367 self._write_comment_path(None, "none")
369 # Write the v:textbox element.
370 self._write_comment_textbox()
372 # Write the x:ClientData element.
373 self._write_comment_client_data(row, col, visible, vertices)
375 self._xml_end_tag("v:shape")
377 def _write_button_shape(self, shape_id, z_index, button):
378 # Write the <v:shape> element.
379 shape_type = "#_x0000_t201"
381 # Set the shape index.
382 shape_id = "_x0000_s" + str(shape_id)
384 # Get the button parameters.
385 # row = button["_row"]
386 # col = button["_col"]
387 vertices = button["vertices"]
389 (left, top, width, height) = self._pixels_to_points(vertices)
391 style = (
392 "position:absolute;"
393 "margin-left:%.15gpt;"
394 "margin-top:%.15gpt;"
395 "width:%.15gpt;"
396 "height:%.15gpt;"
397 "z-index:%d;"
398 "mso-wrap-style:tight" % (left, top, width, height, z_index)
399 )
401 attributes = [
402 ("id", shape_id),
403 ("type", shape_type),
404 ]
406 if button.get("description"):
407 attributes.append(("alt", button["description"]))
409 attributes.append(("style", style))
410 attributes.append(("o:button", "t"))
411 attributes.append(("fillcolor", "buttonFace [67]"))
412 attributes.append(("strokecolor", "windowText [64]"))
413 attributes.append(("o:insetmode", "auto"))
415 self._xml_start_tag("v:shape", attributes)
417 # Write the v:fill element.
418 self._write_button_fill()
420 # Write the o:lock element.
421 self._write_rotation_lock()
423 # Write the v:textbox element.
424 self._write_button_textbox(button["font"])
426 # Write the x:ClientData element.
427 self._write_button_client_data(button)
429 self._xml_end_tag("v:shape")
431 def _write_image_shape(self, shape_id, z_index, image_data):
432 # Write the <v:shape> element.
433 shape_type = "#_x0000_t75"
435 # Set the shape index.
436 shape_id = "_x0000_s" + str(shape_id)
438 # Get the image parameters
439 width = image_data[0]
440 height = image_data[1]
441 name = image_data[2]
442 position = image_data[3]
443 x_dpi = image_data[4]
444 y_dpi = image_data[5]
445 ref_id = image_data[6]
447 # Scale the height/width by the resolution, relative to 72dpi.
448 width = width * 72.0 / x_dpi
449 height = height * 72.0 / y_dpi
451 # Excel uses a rounding based around 72 and 96 dpi.
452 width = 72.0 / 96 * int(width * 96.0 / 72 + 0.25)
453 height = 72.0 / 96 * int(height * 96.0 / 72 + 0.25)
455 style = (
456 "position:absolute;"
457 "margin-left:0;"
458 "margin-top:0;"
459 "width:%.15gpt;"
460 "height:%.15gpt;"
461 "z-index:%d" % (width, height, z_index)
462 )
464 attributes = [
465 ("id", position),
466 ("o:spid", shape_id),
467 ("type", shape_type),
468 ("style", style),
469 ]
471 self._xml_start_tag("v:shape", attributes)
473 # Write the v:imagedata element.
474 self._write_imagedata(ref_id, name)
476 # Write the o:lock element.
477 self._write_rotation_lock()
479 self._xml_end_tag("v:shape")
481 def _write_comment_fill(self):
482 # Write the <v:fill> element.
483 color_2 = "#ffffe1"
485 attributes = [("color2", color_2)]
487 self._xml_empty_tag("v:fill", attributes)
489 def _write_button_fill(self):
490 # Write the <v:fill> element.
491 color_2 = "buttonFace [67]"
492 detectmouseclick = "t"
494 attributes = [
495 ("color2", color_2),
496 ("o:detectmouseclick", detectmouseclick),
497 ]
499 self._xml_empty_tag("v:fill", attributes)
501 def _write_shadow(self):
502 # Write the <v:shadow> element.
503 on = "t"
504 color = "black"
505 obscured = "t"
507 attributes = [
508 ("on", on),
509 ("color", color),
510 ("obscured", obscured),
511 ]
513 self._xml_empty_tag("v:shadow", attributes)
515 def _write_comment_textbox(self):
516 # Write the <v:textbox> element.
517 style = "mso-direction-alt:auto"
519 attributes = [("style", style)]
521 self._xml_start_tag("v:textbox", attributes)
523 # Write the div element.
524 self._write_div("left")
526 self._xml_end_tag("v:textbox")
528 def _write_button_textbox(self, font):
529 # Write the <v:textbox> element.
530 style = "mso-direction-alt:auto"
532 attributes = [("style", style), ("o:singleclick", "f")]
534 self._xml_start_tag("v:textbox", attributes)
536 # Write the div element.
537 self._write_div("center", font)
539 self._xml_end_tag("v:textbox")
541 def _write_div(self, align, font=None):
542 # Write the <div> element.
544 style = "text-align:" + align
546 attributes = [("style", style)]
548 self._xml_start_tag("div", attributes)
550 if font:
551 # Write the font element.
552 self._write_font(font)
554 self._xml_end_tag("div")
556 def _write_font(self, font):
557 # Write the <font> element.
558 caption = font["caption"]
559 face = "Calibri"
560 size = 220
561 color = "#000000"
563 attributes = [
564 ("face", face),
565 ("size", size),
566 ("color", color),
567 ]
569 self._xml_data_element("font", caption, attributes)
571 def _write_comment_client_data(self, row, col, visible, vertices):
572 # Write the <x:ClientData> element.
573 object_type = "Note"
575 attributes = [("ObjectType", object_type)]
577 self._xml_start_tag("x:ClientData", attributes)
579 # Write the x:MoveWithCells element.
580 self._write_move_with_cells()
582 # Write the x:SizeWithCells element.
583 self._write_size_with_cells()
585 # Write the x:Anchor element.
586 self._write_anchor(vertices)
588 # Write the x:AutoFill element.
589 self._write_auto_fill()
591 # Write the x:Row element.
592 self._write_row(row)
594 # Write the x:Column element.
595 self._write_column(col)
597 # Write the x:Visible element.
598 if visible:
599 self._write_visible()
601 self._xml_end_tag("x:ClientData")
603 def _write_button_client_data(self, button):
604 # Write the <x:ClientData> element.
605 macro = button["macro"]
606 vertices = button["vertices"]
608 object_type = "Button"
610 attributes = [("ObjectType", object_type)]
612 self._xml_start_tag("x:ClientData", attributes)
614 # Write the x:Anchor element.
615 self._write_anchor(vertices)
617 # Write the x:PrintObject element.
618 self._write_print_object()
620 # Write the x:AutoFill element.
621 self._write_auto_fill()
623 # Write the x:FmlaMacro element.
624 self._write_fmla_macro(macro)
626 # Write the x:TextHAlign element.
627 self._write_text_halign()
629 # Write the x:TextVAlign element.
630 self._write_text_valign()
632 self._xml_end_tag("x:ClientData")
634 def _write_move_with_cells(self):
635 # Write the <x:MoveWithCells> element.
636 self._xml_empty_tag("x:MoveWithCells")
638 def _write_size_with_cells(self):
639 # Write the <x:SizeWithCells> element.
640 self._xml_empty_tag("x:SizeWithCells")
642 def _write_visible(self):
643 # Write the <x:Visible> element.
644 self._xml_empty_tag("x:Visible")
646 def _write_anchor(self, vertices):
647 # Write the <x:Anchor> element.
648 (col_start, row_start, x1, y1, col_end, row_end, x2, y2) = vertices[:8]
650 strings = [col_start, x1, row_start, y1, col_end, x2, row_end, y2]
651 strings = [str(i) for i in strings]
653 data = ", ".join(strings)
655 self._xml_data_element("x:Anchor", data)
657 def _write_auto_fill(self):
658 # Write the <x:AutoFill> element.
659 data = "False"
661 self._xml_data_element("x:AutoFill", data)
663 def _write_row(self, data):
664 # Write the <x:Row> element.
665 self._xml_data_element("x:Row", data)
667 def _write_column(self, data):
668 # Write the <x:Column> element.
669 self._xml_data_element("x:Column", data)
671 def _write_print_object(self):
672 # Write the <x:PrintObject> element.
673 self._xml_data_element("x:PrintObject", "False")
675 def _write_text_halign(self):
676 # Write the <x:TextHAlign> element.
677 self._xml_data_element("x:TextHAlign", "Center")
679 def _write_text_valign(self):
680 # Write the <x:TextVAlign> element.
681 self._xml_data_element("x:TextVAlign", "Center")
683 def _write_fmla_macro(self, data):
684 # Write the <x:FmlaMacro> element.
685 self._xml_data_element("x:FmlaMacro", data)
687 def _write_imagedata(self, ref_id, o_title):
688 # Write the <v:imagedata> element.
689 attributes = [
690 ("o:relid", "rId" + str(ref_id)),
691 ("o:title", o_title),
692 ]
694 self._xml_empty_tag("v:imagedata", attributes)
696 def _write_formulas(self):
697 # Write the <v:formulas> element.
698 self._xml_start_tag("v:formulas")
700 # Write the v:f elements.
701 self._write_formula("if lineDrawn pixelLineWidth 0")
702 self._write_formula("sum @0 1 0")
703 self._write_formula("sum 0 0 @1")
704 self._write_formula("prod @2 1 2")
705 self._write_formula("prod @3 21600 pixelWidth")
706 self._write_formula("prod @3 21600 pixelHeight")
707 self._write_formula("sum @0 0 1")
708 self._write_formula("prod @6 1 2")
709 self._write_formula("prod @7 21600 pixelWidth")
710 self._write_formula("sum @8 21600 0")
711 self._write_formula("prod @7 21600 pixelHeight")
712 self._write_formula("sum @10 21600 0")
714 self._xml_end_tag("v:formulas")
716 def _write_formula(self, eqn):
717 # Write the <v:f> element.
718 attributes = [("eqn", eqn)]
720 self._xml_empty_tag("v:f", attributes)