diff options
Diffstat (limited to '.venv/lib/python3.12/site-packages/xlsxwriter/drawing.py')
-rw-r--r-- | .venv/lib/python3.12/site-packages/xlsxwriter/drawing.py | 1196 |
1 files changed, 1196 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/xlsxwriter/drawing.py b/.venv/lib/python3.12/site-packages/xlsxwriter/drawing.py new file mode 100644 index 00000000..078e532c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/xlsxwriter/drawing.py @@ -0,0 +1,1196 @@ +############################################################################### +# +# Drawing - A class for writing the Excel XLSX Drawing file. +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2013-2025, John McNamara, jmcnamara@cpan.org +# + +from . import xmlwriter +from .shape import Shape +from .utility import _get_rgb_color + + +class Drawing(xmlwriter.XMLwriter): + """ + A class for writing the Excel XLSX Drawing file. + + + """ + + ########################################################################### + # + # Public API. + # + ########################################################################### + + def __init__(self): + """ + Constructor. + + """ + + super().__init__() + + self.drawings = [] + self.embedded = 0 + self.orientation = 0 + + ########################################################################### + # + # Private API. + # + ########################################################################### + + def _assemble_xml_file(self): + # Assemble and write the XML file. + + # Write the XML declaration. + self._xml_declaration() + + # Write the xdr:wsDr element. + self._write_drawing_workspace() + + if self.embedded: + index = 0 + for drawing_properties in self.drawings: + # Write the xdr:twoCellAnchor element. + index += 1 + self._write_two_cell_anchor(index, drawing_properties) + + else: + # Write the xdr:absoluteAnchor element. + self._write_absolute_anchor(1) + + self._xml_end_tag("xdr:wsDr") + + # Close the file. + self._xml_close() + + def _add_drawing_object(self): + # Add a chart, image or shape sub object to the drawing. + + drawing_object = { + "anchor_type": None, + "dimensions": [], + "width": 0, + "height": 0, + "shape": None, + "anchor": None, + "rel_index": 0, + "url_rel_index": 0, + "tip": None, + "name": None, + "description": None, + "decorative": False, + } + + self.drawings.append(drawing_object) + + return drawing_object + + ########################################################################### + # + # XML methods. + # + ########################################################################### + + def _write_drawing_workspace(self): + # Write the <xdr:wsDr> element. + schema = "http://schemas.openxmlformats.org/drawingml/" + xmlns_xdr = schema + "2006/spreadsheetDrawing" + xmlns_a = schema + "2006/main" + + attributes = [ + ("xmlns:xdr", xmlns_xdr), + ("xmlns:a", xmlns_a), + ] + + self._xml_start_tag("xdr:wsDr", attributes) + + def _write_two_cell_anchor(self, index, drawing_properties): + # Write the <xdr:twoCellAnchor> element. + anchor_type = drawing_properties["type"] + dimensions = drawing_properties["dimensions"] + col_from = dimensions[0] + row_from = dimensions[1] + col_from_offset = dimensions[2] + row_from_offset = dimensions[3] + col_to = dimensions[4] + row_to = dimensions[5] + col_to_offset = dimensions[6] + row_to_offset = dimensions[7] + col_absolute = dimensions[8] + row_absolute = dimensions[9] + width = drawing_properties["width"] + height = drawing_properties["height"] + shape = drawing_properties["shape"] + anchor = drawing_properties["anchor"] + rel_index = drawing_properties["rel_index"] + url_rel_index = drawing_properties["url_rel_index"] + tip = drawing_properties["tip"] + name = drawing_properties["name"] + description = drawing_properties["description"] + decorative = drawing_properties["decorative"] + + attributes = [] + + # Add attribute for positioning. + if anchor == 2: + attributes.append(("editAs", "oneCell")) + elif anchor == 3: + attributes.append(("editAs", "absolute")) + + # Add editAs attribute for shapes. + if shape and shape.edit_as: + attributes.append(("editAs", shape.edit_as)) + + self._xml_start_tag("xdr:twoCellAnchor", attributes) + + # Write the xdr:from element. + self._write_from(col_from, row_from, col_from_offset, row_from_offset) + + # Write the xdr:from element. + self._write_to(col_to, row_to, col_to_offset, row_to_offset) + + if anchor_type == 1: + # Graphic frame. + # Write the xdr:graphicFrame element for charts. + self._write_graphic_frame(index, rel_index, name, description, decorative) + elif anchor_type == 2: + # Write the xdr:pic element. + self._write_pic( + index, + rel_index, + col_absolute, + row_absolute, + width, + height, + shape, + description, + url_rel_index, + tip, + decorative, + ) + else: + # Write the xdr:sp element for shapes. + self._write_sp( + index, + col_absolute, + row_absolute, + width, + height, + shape, + description, + url_rel_index, + tip, + decorative, + ) + + # Write the xdr:clientData element. + self._write_client_data() + + self._xml_end_tag("xdr:twoCellAnchor") + + def _write_absolute_anchor(self, frame_index): + self._xml_start_tag("xdr:absoluteAnchor") + # Write the <xdr:absoluteAnchor> element. + + # Different coordinates for horizontal (= 0) and vertical (= 1). + if self.orientation == 0: + # Write the xdr:pos element. + self._write_pos(0, 0) + + # Write the xdr:ext element. + self._write_xdr_ext(9308969, 6078325) + + else: + # Write the xdr:pos element. + self._write_pos(0, -47625) + + # Write the xdr:ext element. + self._write_xdr_ext(6162675, 6124575) + + # Write the xdr:graphicFrame element. + self._write_graphic_frame(frame_index, frame_index) + + # Write the xdr:clientData element. + self._write_client_data() + + self._xml_end_tag("xdr:absoluteAnchor") + + def _write_from(self, col, row, col_offset, row_offset): + # Write the <xdr:from> element. + self._xml_start_tag("xdr:from") + + # Write the xdr:col element. + self._write_col(col) + + # Write the xdr:colOff element. + self._write_col_off(col_offset) + + # Write the xdr:row element. + self._write_row(row) + + # Write the xdr:rowOff element. + self._write_row_off(row_offset) + + self._xml_end_tag("xdr:from") + + def _write_to(self, col, row, col_offset, row_offset): + # Write the <xdr:to> element. + self._xml_start_tag("xdr:to") + + # Write the xdr:col element. + self._write_col(col) + + # Write the xdr:colOff element. + self._write_col_off(col_offset) + + # Write the xdr:row element. + self._write_row(row) + + # Write the xdr:rowOff element. + self._write_row_off(row_offset) + + self._xml_end_tag("xdr:to") + + def _write_col(self, data): + # Write the <xdr:col> element. + self._xml_data_element("xdr:col", data) + + def _write_col_off(self, data): + # Write the <xdr:colOff> element. + self._xml_data_element("xdr:colOff", data) + + def _write_row(self, data): + # Write the <xdr:row> element. + self._xml_data_element("xdr:row", data) + + def _write_row_off(self, data): + # Write the <xdr:rowOff> element. + self._xml_data_element("xdr:rowOff", data) + + def _write_pos(self, x, y): + # Write the <xdr:pos> element. + + attributes = [("x", x), ("y", y)] + + self._xml_empty_tag("xdr:pos", attributes) + + def _write_xdr_ext(self, cx, cy): + # Write the <xdr:ext> element. + + attributes = [("cx", cx), ("cy", cy)] + + self._xml_empty_tag("xdr:ext", attributes) + + def _write_graphic_frame( + self, index, rel_index, name=None, description=None, decorative=None + ): + # Write the <xdr:graphicFrame> element. + attributes = [("macro", "")] + + self._xml_start_tag("xdr:graphicFrame", attributes) + + # Write the xdr:nvGraphicFramePr element. + self._write_nv_graphic_frame_pr(index, name, description, decorative) + + # Write the xdr:xfrm element. + self._write_xfrm() + + # Write the a:graphic element. + self._write_atag_graphic(rel_index) + + self._xml_end_tag("xdr:graphicFrame") + + def _write_nv_graphic_frame_pr(self, index, name, description, decorative): + # Write the <xdr:nvGraphicFramePr> element. + + if not name: + name = "Chart " + str(index) + + self._xml_start_tag("xdr:nvGraphicFramePr") + + # Write the xdr:cNvPr element. + self._write_c_nv_pr(index + 1, name, description, None, None, decorative) + + # Write the xdr:cNvGraphicFramePr element. + self._write_c_nv_graphic_frame_pr() + + self._xml_end_tag("xdr:nvGraphicFramePr") + + def _write_c_nv_pr(self, index, name, description, url_rel_index, tip, decorative): + # Write the <xdr:cNvPr> element. + attributes = [("id", index), ("name", name)] + + # Add description attribute for images. + if description and not decorative: + attributes.append(("descr", description)) + + if url_rel_index or decorative: + self._xml_start_tag("xdr:cNvPr", attributes) + + if url_rel_index: + self._write_a_hlink_click(url_rel_index, tip) + + if decorative: + self._write_decorative() + + self._xml_end_tag("xdr:cNvPr") + else: + self._xml_empty_tag("xdr:cNvPr", attributes) + + def _write_decorative(self): + self._xml_start_tag("a:extLst") + + self._write_uri_ext("{FF2B5EF4-FFF2-40B4-BE49-F238E27FC236}") + self._write_a16_creation_id() + self._xml_end_tag("a:ext") + + self._write_uri_ext("{C183D7F6-B498-43B3-948B-1728B52AA6E4}") + self._write_adec_decorative() + self._xml_end_tag("a:ext") + + self._xml_end_tag("a:extLst") + + def _write_uri_ext(self, uri): + # Write the <a:ext> element. + attributes = [("uri", uri)] + + self._xml_start_tag("a:ext", attributes) + + def _write_adec_decorative(self): + # Write the <adec:decorative> element. + xmlns = "http://schemas.microsoft.com/office/drawing/2017/decorative" + val = "1" + + attributes = [ + ("xmlns:adec", xmlns), + ("val", val), + ] + + self._xml_empty_tag("adec:decorative", attributes) + + def _write_a16_creation_id(self): + # Write the <a16:creationId> element. + + xmlns_a_16 = "http://schemas.microsoft.com/office/drawing/2014/main" + creation_id = "{00000000-0008-0000-0000-000002000000}" + + attributes = [ + ("xmlns:a16", xmlns_a_16), + ("id", creation_id), + ] + + self._xml_empty_tag("a16:creationId", attributes) + + def _write_a_hlink_click(self, rel_index, tip): + # Write the <a:hlinkClick> element. + schema = "http://schemas.openxmlformats.org/officeDocument/" + xmlns_r = schema + "2006/relationships" + + attributes = [ + ("xmlns:r", xmlns_r), + ("r:id", "rId" + str(rel_index)), + ] + + if tip: + attributes.append(("tooltip", tip)) + + self._xml_empty_tag("a:hlinkClick", attributes) + + def _write_c_nv_graphic_frame_pr(self): + # Write the <xdr:cNvGraphicFramePr> element. + if self.embedded: + self._xml_empty_tag("xdr:cNvGraphicFramePr") + else: + self._xml_start_tag("xdr:cNvGraphicFramePr") + + # Write the a:graphicFrameLocks element. + self._write_a_graphic_frame_locks() + + self._xml_end_tag("xdr:cNvGraphicFramePr") + + def _write_a_graphic_frame_locks(self): + # Write the <a:graphicFrameLocks> element. + attributes = [("noGrp", 1)] + + self._xml_empty_tag("a:graphicFrameLocks", attributes) + + def _write_xfrm(self): + # Write the <xdr:xfrm> element. + self._xml_start_tag("xdr:xfrm") + + # Write the xfrmOffset element. + self._write_xfrm_offset() + + # Write the xfrmOffset element. + self._write_xfrm_extension() + + self._xml_end_tag("xdr:xfrm") + + def _write_xfrm_offset(self): + # Write the <a:off> xfrm sub-element. + + attributes = [ + ("x", 0), + ("y", 0), + ] + + self._xml_empty_tag("a:off", attributes) + + def _write_xfrm_extension(self): + # Write the <a:ext> xfrm sub-element. + + attributes = [ + ("cx", 0), + ("cy", 0), + ] + + self._xml_empty_tag("a:ext", attributes) + + def _write_atag_graphic(self, index): + # Write the <a:graphic> element. + self._xml_start_tag("a:graphic") + + # Write the a:graphicData element. + self._write_atag_graphic_data(index) + + self._xml_end_tag("a:graphic") + + def _write_atag_graphic_data(self, index): + # Write the <a:graphicData> element. + uri = "http://schemas.openxmlformats.org/drawingml/2006/chart" + + attributes = [ + ( + "uri", + uri, + ) + ] + + self._xml_start_tag("a:graphicData", attributes) + + # Write the c:chart element. + self._write_c_chart("rId" + str(index)) + + self._xml_end_tag("a:graphicData") + + def _write_c_chart(self, r_id): + # Write the <c:chart> element. + + schema = "http://schemas.openxmlformats.org/" + xmlns_c = schema + "drawingml/2006/chart" + xmlns_r = schema + "officeDocument/2006/relationships" + + attributes = [ + ("xmlns:c", xmlns_c), + ("xmlns:r", xmlns_r), + ("r:id", r_id), + ] + + self._xml_empty_tag("c:chart", attributes) + + def _write_client_data(self): + # Write the <xdr:clientData> element. + self._xml_empty_tag("xdr:clientData") + + def _write_sp( + self, + index, + col_absolute, + row_absolute, + width, + height, + shape, + description, + url_rel_index, + tip, + decorative, + ): + # Write the <xdr:sp> element. + + if shape and shape.connect: + attributes = [("macro", "")] + self._xml_start_tag("xdr:cxnSp", attributes) + + # Write the xdr:nvCxnSpPr element. + self._write_nv_cxn_sp_pr(index, shape) + + # Write the xdr:spPr element. + self._write_xdr_sp_pr(col_absolute, row_absolute, width, height, shape) + + self._xml_end_tag("xdr:cxnSp") + else: + # Add attribute for shapes. + attributes = [("macro", ""), ("textlink", shape.textlink)] + + self._xml_start_tag("xdr:sp", attributes) + + # Write the xdr:nvSpPr element. + self._write_nv_sp_pr( + index, shape, url_rel_index, tip, description, decorative + ) + + # Write the xdr:spPr element. + self._write_xdr_sp_pr(col_absolute, row_absolute, width, height, shape) + + # Write the xdr:style element. + self._write_style() + + # Write the xdr:txBody element. + if shape.text is not None: + self._write_tx_body(shape) + + self._xml_end_tag("xdr:sp") + + def _write_nv_cxn_sp_pr(self, index, shape): + # Write the <xdr:nvCxnSpPr> element. + self._xml_start_tag("xdr:nvCxnSpPr") + + name = shape.name + " " + str(index) + if name is not None: + self._write_c_nv_pr(index, name, None, None, None, None) + + self._xml_start_tag("xdr:cNvCxnSpPr") + + attributes = [("noChangeShapeType", "1")] + self._xml_empty_tag("a:cxnSpLocks", attributes) + + if shape.start: + attributes = [("id", shape.start), ("idx", shape.start_index)] + self._xml_empty_tag("a:stCxn", attributes) + + if shape.end: + attributes = [("id", shape.end), ("idx", shape.end_index)] + self._xml_empty_tag("a:endCxn", attributes) + + self._xml_end_tag("xdr:cNvCxnSpPr") + self._xml_end_tag("xdr:nvCxnSpPr") + + def _write_nv_sp_pr( + self, index, shape, url_rel_index, tip, description, decorative + ): + # Write the <xdr:NvSpPr> element. + attributes = [] + + self._xml_start_tag("xdr:nvSpPr") + + name = shape.name + " " + str(index) + + self._write_c_nv_pr( + index + 1, name, description, url_rel_index, tip, decorative + ) + + if shape.name == "TextBox": + attributes = [("txBox", 1)] + + self._xml_empty_tag("xdr:cNvSpPr", attributes) + + self._xml_end_tag("xdr:nvSpPr") + + def _write_pic( + self, + index, + rel_index, + col_absolute, + row_absolute, + width, + height, + shape, + description, + url_rel_index, + tip, + decorative, + ): + # Write the <xdr:pic> element. + self._xml_start_tag("xdr:pic") + + # Write the xdr:nvPicPr element. + self._write_nv_pic_pr(index, description, url_rel_index, tip, decorative) + # Write the xdr:blipFill element. + self._write_blip_fill(rel_index) + + # Write the xdr:spPr element. + self._write_sp_pr(col_absolute, row_absolute, width, height, shape) + + self._xml_end_tag("xdr:pic") + + def _write_nv_pic_pr(self, index, description, url_rel_index, tip, decorative): + # Write the <xdr:nvPicPr> element. + self._xml_start_tag("xdr:nvPicPr") + + # Write the xdr:cNvPr element. + self._write_c_nv_pr( + index + 1, + "Picture " + str(index), + description, + url_rel_index, + tip, + decorative, + ) + + # Write the xdr:cNvPicPr element. + self._write_c_nv_pic_pr() + + self._xml_end_tag("xdr:nvPicPr") + + def _write_c_nv_pic_pr(self): + # Write the <xdr:cNvPicPr> element. + self._xml_start_tag("xdr:cNvPicPr") + + # Write the a:picLocks element. + self._write_a_pic_locks() + + self._xml_end_tag("xdr:cNvPicPr") + + def _write_a_pic_locks(self): + # Write the <a:picLocks> element. + attributes = [("noChangeAspect", 1)] + + self._xml_empty_tag("a:picLocks", attributes) + + def _write_blip_fill(self, index): + # Write the <xdr:blipFill> element. + self._xml_start_tag("xdr:blipFill") + + # Write the a:blip element. + self._write_a_blip(index) + + # Write the a:stretch element. + self._write_a_stretch() + + self._xml_end_tag("xdr:blipFill") + + def _write_a_blip(self, index): + # Write the <a:blip> element. + schema = "http://schemas.openxmlformats.org/officeDocument/" + xmlns_r = schema + "2006/relationships" + r_embed = "rId" + str(index) + + attributes = [("xmlns:r", xmlns_r), ("r:embed", r_embed)] + + self._xml_empty_tag("a:blip", attributes) + + def _write_a_stretch(self): + # Write the <a:stretch> element. + self._xml_start_tag("a:stretch") + + # Write the a:fillRect element. + self._write_a_fill_rect() + + self._xml_end_tag("a:stretch") + + def _write_a_fill_rect(self): + # Write the <a:fillRect> element. + self._xml_empty_tag("a:fillRect") + + def _write_sp_pr(self, col_absolute, row_absolute, width, height, shape=None): + # Write the <xdr:spPr> element, for charts. + + self._xml_start_tag("xdr:spPr") + + # Write the a:xfrm element. + self._write_a_xfrm(col_absolute, row_absolute, width, height) + + # Write the a:prstGeom element. + self._write_a_prst_geom(shape) + + self._xml_end_tag("xdr:spPr") + + def _write_xdr_sp_pr(self, col_absolute, row_absolute, width, height, shape): + # Write the <xdr:spPr> element for shapes. + self._xml_start_tag("xdr:spPr") + + # Write the a:xfrm element. + self._write_a_xfrm(col_absolute, row_absolute, width, height, shape) + + # Write the a:prstGeom element. + self._write_a_prst_geom(shape) + + if shape.fill: + if not shape.fill["defined"]: + # Write the a:solidFill element. + self._write_a_solid_fill_scheme("lt1") + elif "none" in shape.fill: + # Write the a:noFill element. + self._xml_empty_tag("a:noFill") + elif "color" in shape.fill: + # Write the a:solidFill element. + self._write_a_solid_fill(_get_rgb_color(shape.fill["color"])) + + if shape.gradient: + # Write the a:gradFill element. + self._write_a_grad_fill(shape.gradient) + + # Write the a:ln element. + self._write_a_ln(shape.line) + + self._xml_end_tag("xdr:spPr") + + def _write_a_xfrm(self, col_absolute, row_absolute, width, height, shape=None): + # Write the <a:xfrm> element. + attributes = [] + + if shape: + if shape.rotation: + rotation = shape.rotation + rotation *= 60000 + attributes.append(("rot", rotation)) + + if shape.flip_h: + attributes.append(("flipH", 1)) + if shape.flip_v: + attributes.append(("flipV", 1)) + + self._xml_start_tag("a:xfrm", attributes) + + # Write the a:off element. + self._write_a_off(col_absolute, row_absolute) + + # Write the a:ext element. + self._write_a_ext(width, height) + + self._xml_end_tag("a:xfrm") + + def _write_a_off(self, x, y): + # Write the <a:off> element. + attributes = [ + ("x", x), + ("y", y), + ] + + self._xml_empty_tag("a:off", attributes) + + def _write_a_ext(self, cx, cy): + # Write the <a:ext> element. + attributes = [ + ("cx", cx), + ("cy", cy), + ] + + self._xml_empty_tag("a:ext", attributes) + + def _write_a_prst_geom(self, shape=None): + # Write the <a:prstGeom> element. + attributes = [("prst", "rect")] + + self._xml_start_tag("a:prstGeom", attributes) + + # Write the a:avLst element. + self._write_a_av_lst(shape) + + self._xml_end_tag("a:prstGeom") + + def _write_a_av_lst(self, shape=None): + # Write the <a:avLst> element. + adjustments = [] + + if shape and shape.adjustments: + adjustments = shape.adjustments + + if adjustments: + self._xml_start_tag("a:avLst") + + i = 0 + for adj in adjustments: + i += 1 + # Only connectors have multiple adjustments. + if shape.connect: + suffix = i + else: + suffix = "" + + # Scale Adjustments: 100,000 = 100%. + adj_int = str(int(adj * 1000)) + + attributes = [("name", "adj" + suffix), ("fmla", "val" + adj_int)] + + self._xml_empty_tag("a:gd", attributes) + + self._xml_end_tag("a:avLst") + else: + self._xml_empty_tag("a:avLst") + + def _write_a_solid_fill(self, rgb): + # Write the <a:solidFill> element. + if rgb is None: + rgb = "FFFFFF" + + self._xml_start_tag("a:solidFill") + + # Write the a:srgbClr element. + self._write_a_srgb_clr(rgb) + + self._xml_end_tag("a:solidFill") + + def _write_a_solid_fill_scheme(self, color, shade=None): + attributes = [("val", color)] + + self._xml_start_tag("a:solidFill") + + if shade: + self._xml_start_tag("a:schemeClr", attributes) + self._write_a_shade(shade) + self._xml_end_tag("a:schemeClr") + else: + self._xml_empty_tag("a:schemeClr", attributes) + + self._xml_end_tag("a:solidFill") + + def _write_a_ln(self, line): + # Write the <a:ln> element. + width = line.get("width", 0.75) + + # Round width to nearest 0.25, like Excel. + width = int((width + 0.125) * 4) / 4.0 + + # Convert to internal units. + width = int(0.5 + (12700 * width)) + + attributes = [("w", width), ("cmpd", "sng")] + + self._xml_start_tag("a:ln", attributes) + + if "none" in line: + # Write the a:noFill element. + self._xml_empty_tag("a:noFill") + + elif "color" in line: + # Write the a:solidFill element. + self._write_a_solid_fill(_get_rgb_color(line["color"])) + + else: + # Write the a:solidFill element. + self._write_a_solid_fill_scheme("lt1", "50000") + + # Write the line/dash type. + line_type = line.get("dash_type") + if line_type: + # Write the a:prstDash element. + self._write_a_prst_dash(line_type) + + self._xml_end_tag("a:ln") + + def _write_tx_body(self, shape): + # Write the <xdr:txBody> element. + attributes = [] + + if shape.text_rotation != 0: + if shape.text_rotation == 90: + attributes.append(("vert", "vert270")) + if shape.text_rotation == -90: + attributes.append(("vert", "vert")) + if shape.text_rotation == 270: + attributes.append(("vert", "wordArtVert")) + if shape.text_rotation == 271: + attributes.append(("vert", "eaVert")) + + attributes.append(("wrap", "square")) + attributes.append(("rtlCol", "0")) + + if not shape.align["defined"]: + attributes.append(("anchor", "t")) + else: + if "vertical" in shape.align: + align = shape.align["vertical"] + if align == "top": + attributes.append(("anchor", "t")) + elif align == "middle": + attributes.append(("anchor", "ctr")) + elif align == "bottom": + attributes.append(("anchor", "b")) + else: + attributes.append(("anchor", "t")) + + if "horizontal" in shape.align: + align = shape.align["horizontal"] + if align == "center": + attributes.append(("anchorCtr", "1")) + else: + attributes.append(("anchorCtr", "0")) + + self._xml_start_tag("xdr:txBody") + self._xml_empty_tag("a:bodyPr", attributes) + self._xml_empty_tag("a:lstStyle") + + lines = shape.text.split("\n") + + # Set the font attributes. + font = shape.font + # pylint: disable=protected-access + style_attrs = Shape._get_font_style_attributes(font) + latin_attrs = Shape._get_font_latin_attributes(font) + style_attrs.insert(0, ("lang", font["lang"])) + + if shape.textlink != "": + attributes = [ + ("id", "{B8ADDEFE-BF52-4FD4-8C5D-6B85EF6FF707}"), + ("type", "TxLink"), + ] + + self._xml_start_tag("a:p") + self._xml_start_tag("a:fld", attributes) + + self._write_font_run(font, style_attrs, latin_attrs, "a:rPr") + + self._xml_data_element("a:t", shape.text) + self._xml_end_tag("a:fld") + + self._write_font_run(font, style_attrs, latin_attrs, "a:endParaRPr") + + self._xml_end_tag("a:p") + else: + for line in lines: + self._xml_start_tag("a:p") + + if line == "": + self._write_font_run(font, style_attrs, latin_attrs, "a:endParaRPr") + self._xml_end_tag("a:p") + continue + + if "text" in shape.align: + if shape.align["text"] == "left": + self._xml_empty_tag("a:pPr", [("algn", "l")]) + if shape.align["text"] == "center": + self._xml_empty_tag("a:pPr", [("algn", "ctr")]) + if shape.align["text"] == "right": + self._xml_empty_tag("a:pPr", [("algn", "r")]) + + self._xml_start_tag("a:r") + + self._write_font_run(font, style_attrs, latin_attrs, "a:rPr") + + self._xml_data_element("a:t", line) + + self._xml_end_tag("a:r") + self._xml_end_tag("a:p") + + self._xml_end_tag("xdr:txBody") + + def _write_font_run(self, font, style_attrs, latin_attrs, run_type): + # Write a:rPr or a:endParaRPr. + has_color = font.get("color") is not None + + if latin_attrs or has_color: + self._xml_start_tag(run_type, style_attrs) + + if has_color: + self._write_a_solid_fill(_get_rgb_color(font["color"])) + + if latin_attrs: + self._write_a_latin(latin_attrs) + self._write_a_cs(latin_attrs) + + self._xml_end_tag(run_type) + else: + self._xml_empty_tag(run_type, style_attrs) + + def _write_style(self): + # Write the <xdr:style> element. + self._xml_start_tag("xdr:style") + + # Write the a:lnRef element. + self._write_a_ln_ref() + + # Write the a:fillRef element. + self._write_a_fill_ref() + + # Write the a:effectRef element. + self._write_a_effect_ref() + + # Write the a:fontRef element. + self._write_a_font_ref() + + self._xml_end_tag("xdr:style") + + def _write_a_ln_ref(self): + # Write the <a:lnRef> element. + attributes = [("idx", "0")] + + self._xml_start_tag("a:lnRef", attributes) + + # Write the a:scrgbClr element. + self._write_a_scrgb_clr() + + self._xml_end_tag("a:lnRef") + + def _write_a_fill_ref(self): + # Write the <a:fillRef> element. + attributes = [("idx", "0")] + + self._xml_start_tag("a:fillRef", attributes) + + # Write the a:scrgbClr element. + self._write_a_scrgb_clr() + + self._xml_end_tag("a:fillRef") + + def _write_a_effect_ref(self): + # Write the <a:effectRef> element. + attributes = [("idx", "0")] + + self._xml_start_tag("a:effectRef", attributes) + + # Write the a:scrgbClr element. + self._write_a_scrgb_clr() + + self._xml_end_tag("a:effectRef") + + def _write_a_scrgb_clr(self): + # Write the <a:scrgbClr> element. + + attributes = [ + ("r", "0"), + ("g", "0"), + ("b", "0"), + ] + + self._xml_empty_tag("a:scrgbClr", attributes) + + def _write_a_font_ref(self): + # Write the <a:fontRef> element. + attributes = [("idx", "minor")] + + self._xml_start_tag("a:fontRef", attributes) + + # Write the a:schemeClr element. + self._write_a_scheme_clr("dk1") + + self._xml_end_tag("a:fontRef") + + def _write_a_scheme_clr(self, val): + # Write the <a:schemeClr> element. + attributes = [("val", val)] + + self._xml_empty_tag("a:schemeClr", attributes) + + def _write_a_shade(self, shade): + # Write the <a:shade> element. + attributes = [("val", shade)] + + self._xml_empty_tag("a:shade", attributes) + + def _write_a_prst_dash(self, val): + # Write the <a:prstDash> element. + + attributes = [("val", val)] + + self._xml_empty_tag("a:prstDash", attributes) + + def _write_a_grad_fill(self, gradient): + # Write the <a:gradFill> element. + + attributes = [("flip", "none"), ("rotWithShape", "1")] + + if gradient["type"] == "linear": + attributes = [] + + self._xml_start_tag("a:gradFill", attributes) + + # Write the a:gsLst element. + self._write_a_gs_lst(gradient) + + if gradient["type"] == "linear": + # Write the a:lin element. + self._write_a_lin(gradient["angle"]) + else: + # Write the a:path element. + self._write_a_path(gradient["type"]) + + # Write the a:tileRect element. + self._write_a_tile_rect(gradient["type"]) + + self._xml_end_tag("a:gradFill") + + def _write_a_gs_lst(self, gradient): + # Write the <a:gsLst> element. + positions = gradient["positions"] + colors = gradient["colors"] + + self._xml_start_tag("a:gsLst") + + for i, color in enumerate(colors): + pos = int(positions[i] * 1000) + attributes = [("pos", pos)] + self._xml_start_tag("a:gs", attributes) + + # Write the a:srgbClr element. + color = _get_rgb_color(color) + self._write_a_srgb_clr(color) + + self._xml_end_tag("a:gs") + + self._xml_end_tag("a:gsLst") + + def _write_a_lin(self, angle): + # Write the <a:lin> element. + + angle = int(60000 * angle) + + attributes = [ + ("ang", angle), + ("scaled", "0"), + ] + + self._xml_empty_tag("a:lin", attributes) + + def _write_a_path(self, gradient_type): + # Write the <a:path> element. + + attributes = [("path", gradient_type)] + + self._xml_start_tag("a:path", attributes) + + # Write the a:fillToRect element. + self._write_a_fill_to_rect(gradient_type) + + self._xml_end_tag("a:path") + + def _write_a_fill_to_rect(self, gradient_type): + # Write the <a:fillToRect> element. + + if gradient_type == "shape": + attributes = [ + ("l", "50000"), + ("t", "50000"), + ("r", "50000"), + ("b", "50000"), + ] + else: + attributes = [ + ("l", "100000"), + ("t", "100000"), + ] + + self._xml_empty_tag("a:fillToRect", attributes) + + def _write_a_tile_rect(self, gradient_type): + # Write the <a:tileRect> element. + + if gradient_type == "shape": + attributes = [] + else: + attributes = [ + ("r", "-100000"), + ("b", "-100000"), + ] + + self._xml_empty_tag("a:tileRect", attributes) + + def _write_a_srgb_clr(self, val): + # Write the <a:srgbClr> element. + + attributes = [("val", val)] + + self._xml_empty_tag("a:srgbClr", attributes) + + def _write_a_latin(self, attributes): + # Write the <a:latin> element. + self._xml_empty_tag("a:latin", attributes) + + def _write_a_cs(self, attributes): + # Write the <a:latin> element. + self._xml_empty_tag("a:cs", attributes) |