class CT_Path2D(BaseOxmlElement): """`a:path` custom element class.""" close = ZeroOrMore("a:close", successors=()) lnTo = ZeroOrMore("a:lnTo", successors=()) moveTo = ZeroOrMore("a:moveTo", successors=()) w = OptionalAttribute("w", ST_PositiveCoordinate) h = OptionalAttribute("h", ST_PositiveCoordinate) def add_close(self): """Return a newly created `a:close` element. The new `a:close` element is appended to this `a:path` element. """ return self._add_close() def add_lnTo(self, x, y): """Return a newly created `a:lnTo` subtree with end point *(x, y)*. The new `a:lnTo` element is appended to this `a:path` element. """ lnTo = self._add_lnTo() pt = lnTo._add_pt() pt.x, pt.y = x, y return lnTo def add_moveTo(self, x, y): """Return a newly created `a:moveTo` subtree with point *(x, y)*. The new `a:moveTo` element is appended to this `a:path` element. """ moveTo = self._add_moveTo() pt = moveTo._add_pt() pt.x, pt.y = x, y return moveTo
class CT_TextParagraph(BaseOxmlElement): """`a:p` custom element class""" pPr = ZeroOrOne("a:pPr", successors=("a:r", "a:br", "a:fld", "a:endParaRPr")) r = ZeroOrMore("a:r", successors=("a:endParaRPr", )) br = ZeroOrMore("a:br", successors=("a:endParaRPr", )) endParaRPr = ZeroOrOne("a:endParaRPr", successors=()) def add_br(self): """ Return a newly appended <a:br> element. """ return self._add_br() def add_r(self, text=None): """ Return a newly appended <a:r> element. """ r = self._add_r() if text: r.text = text return r def append_text(self, text): """Append `a:r` and `a:br` elements to *p* based on *text*. Any `\n` or `\v` (vertical-tab) characters in *text* delimit `a:r` (run) elements and themselves are translated to `a:br` (line-break) elements. The vertical-tab character appears in clipboard text from PowerPoint at "soft" line-breaks (new-line, but not new paragraph). """ for idx, r_str in enumerate(re.split("\n|\v", text)): # ---breaks are only added *between* items, not at start--- if idx > 0: self.add_br() # ---runs that would be empty are not added--- if r_str: self.add_r(r_str) @property def content_children(self): """Sequence containing text-container child elements of this `a:p` element. These include `a:r`, `a:br`, and `a:fld`. """ text_types = {CT_RegularTextRun, CT_TextLineBreak, CT_TextField} return tuple(elm for elm in self if type(elm) in text_types) @property def text(self): """str text contained in this paragraph.""" # ---note this shadows the lxml _Element.text--- return "".join([child.text for child in self.content_children]) def _new_r(self): r_xml = "<a:r %s><a:t/></a:r>" % nsdecls("a") return parse_xml(r_xml)
class CT_TextParagraph(BaseOxmlElement): """`a:p` custom element class""" pPr = ZeroOrOne('a:pPr', successors=('a:r', 'a:br', 'a:fld', 'a:endParaRPr')) r = ZeroOrMore('a:r', successors=('a:endParaRPr', )) br = ZeroOrMore('a:br', successors=('a:endParaRPr', )) endParaRPr = ZeroOrOne('a:endParaRPr', successors=()) def add_br(self): """ Return a newly appended <a:br> element. """ return self._add_br() def add_r(self, text=None): """ Return a newly appended <a:r> element. """ r = self._add_r() if text: r.t.text = text return r def append_text(self, text): """Append `a:r` and `a:br` elements to *p* based on *text*. Any `\n` characters in *text* delimit `a:r` (run) elements and themselves are translated to `a:br` (line-break) elements. """ for idx, r_str in enumerate(text.split('\n')): # ---breaks are only added *between* items, not at start--- if idx > 0: self.add_br() # ---runs that would be empty are not added--- if r_str: self.add_r(r_str) @property def content_children(self): """ A sequence containing the text-container child elements of this ``<a:p>`` element, i.e. (a:r|a:br|a:fld). """ text_types = {CT_RegularTextRun, CT_TextLineBreak, CT_TextField} return tuple(elm for elm in self if type(elm) in text_types) @property def text(self): """str text contained in this paragraph.""" # ---note this shadows the lxml _Element.text--- return ''.join([child.text for child in self.content_children]) def _new_r(self): r_xml = '<a:r %s><a:t/></a:r>' % nsdecls('a') return parse_xml(r_xml)
class CT_SlideLayoutIdList(BaseOxmlElement): """ ``<p:sldLayoutIdLst>`` element, child of ``<p:sldMaster>`` containing references to the slide layouts that inherit from the slide master. """ sldLayoutId = ZeroOrMore("p:sldLayoutId")
class PresetShapeDefinition(BaseOxmlElement): """`drawml:presetShapeDefinition` element class.""" presetShapeLst = ZeroOrMore("drawml:presetShape") @classmethod def new(cls, xml): """Return shape definitions configured as ...""" return oxml.parse_xml(xml)
class CT_TextParagraph(BaseOxmlElement): """ <a:p> custom element class """ pPr = ZeroOrOne('a:pPr', successors=('a:r', 'a:br', 'a:fld', 'a:endParaRPr')) r = ZeroOrMore('a:r', successors=('a:endParaRPr', )) br = ZeroOrMore('a:br', successors=('a:endParaRPr', )) endParaRPr = ZeroOrOne('a:endParaRPr', successors=()) def add_br(self): """ Return a newly appended <a:br> element. """ return self._add_br() def add_r(self, text=None): """ Return a newly appended <a:r> element. """ r = self._add_r() if text: r.t.text = text return r def append_text(self, text): """ Add *text* at the end of this paragraph element, translating line feed characters ('\n') into ``<a:br>`` elements. """ _ParagraphTextAppender.append_to_p_from_text(self, text) @property def content_children(self): """ A sequence containing the text-container child elements of this ``<a:p>`` element, i.e. (a:r|a:br|a:fld). """ text_types = (CT_RegularTextRun, CT_TextLineBreak, CT_TextField) return tuple(elm for elm in self if isinstance(elm, text_types)) def _new_r(self): r_xml = '<a:r %s><a:t/></a:r>' % nsdecls('a') return parse_xml(r_xml)
class CT_Path2DList(BaseOxmlElement): """`a:pathLst` custom element class.""" path = ZeroOrMore("a:path", successors=()) def add_path(self, w, h): """Return a newly created `a:path` child element.""" path = self._add_path() path.w, path.h = w, h return path
class CT_TableGrid(BaseOxmlElement): """ ``<a:tblGrid>`` custom element class """ gridCol = ZeroOrMore('a:gridCol') def add_gridCol(self, width): """ Return a reference to a newly created <a:gridCol> child element having its ``w`` attribute set to *width*. """ return self._add_gridCol(w=width)
class CT_TableRow(BaseOxmlElement): """ ``<a:tr>`` custom element class """ tc = ZeroOrMore('a:tc', successors=('a:extLst', )) h = RequiredAttribute('h', ST_Coordinate) def add_tc(self): """ Return a reference to a newly added minimal valid ``<a:tc>`` child element. """ return self._add_tc() def _new_tc(self): return CT_TableCell.new()
class CT_Parent(BaseOxmlElement): """ ``<p:parent>`` element, an invented element for use in testing. """ eg_zooChoice = ZeroOrOneChoice( (Choice('p:choice'), Choice('p:choice2')), successors=('p:oomChild', 'p:oooChild') ) oomChild = OneOrMore('p:oomChild', successors=( 'p:oooChild', 'p:zomChild', 'p:zooChild' )) oooChild = OneAndOnlyOne('p:oooChild') zomChild = ZeroOrMore('p:zomChild', successors=('p:zooChild',)) zooChild = ZeroOrOne('p:zooChild', successors=()) optAttr = OptionalAttribute('p:optAttr', ST_IntegerType) reqAttr = RequiredAttribute('reqAttr', ST_IntegerType)
class CT_TableRow(BaseOxmlElement): """ ``<a:tr>`` custom element class """ tc = ZeroOrMore('a:tc', successors=('a:extLst', )) h = RequiredAttribute('h', ST_Coordinate) def add_tc(self): """ Return a reference to a newly added minimal valid ``<a:tc>`` child element. """ return self._add_tc() @property def row_idx(self): """Offset of this row in its table.""" return self.getparent().tr_lst.index(self) def _new_tc(self): return CT_TableCell.new()
class CT_Table(BaseOxmlElement): """`a:tbl` custom element class""" _tag_seq = ("a:tblPr", "a:tblGrid", "a:tr") tblPr = ZeroOrOne("a:tblPr", successors=_tag_seq[1:]) tblGrid = OneAndOnlyOne("a:tblGrid") tr = ZeroOrMore("a:tr", successors=_tag_seq[3:]) del _tag_seq def add_tr(self, height): """ Return a reference to a newly created <a:tr> child element having its ``h`` attribute set to *height*. """ return self._add_tr(h=height) @property def bandCol(self): return self._get_boolean_property("bandCol") @bandCol.setter def bandCol(self, value): self._set_boolean_property("bandCol", value) @property def bandRow(self): return self._get_boolean_property("bandRow") @bandRow.setter def bandRow(self, value): self._set_boolean_property("bandRow", value) @property def firstCol(self): return self._get_boolean_property("firstCol") @firstCol.setter def firstCol(self, value): self._set_boolean_property("firstCol", value) @property def firstRow(self): return self._get_boolean_property("firstRow") @firstRow.setter def firstRow(self, value): self._set_boolean_property("firstRow", value) def iter_tcs(self): """Generate each `a:tc` element in this tbl. tc elements are generated left-to-right, top-to-bottom. """ return (tc for tr in self.tr_lst for tc in tr.tc_lst) @property def lastCol(self): return self._get_boolean_property("lastCol") @lastCol.setter def lastCol(self, value): self._set_boolean_property("lastCol", value) @property def lastRow(self): return self._get_boolean_property("lastRow") @lastRow.setter def lastRow(self, value): self._set_boolean_property("lastRow", value) @classmethod def new_tbl(cls, rows, cols, width, height, tableStyleId=None): """Return a new ``<p:tbl>`` element tree.""" # working hypothesis is this is the default table style GUID if tableStyleId is None: tableStyleId = "{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}" xml = cls._tbl_tmpl() % (tableStyleId) tbl = parse_xml(xml) # add specified number of rows and columns rowheight = height // rows colwidth = width // cols for col in range(cols): # adjust width of last col to absorb any div error if col == cols - 1: colwidth = width - ((cols - 1) * colwidth) tbl.tblGrid.add_gridCol(width=colwidth) for row in range(rows): # adjust height of last row to absorb any div error if row == rows - 1: rowheight = height - ((rows - 1) * rowheight) tr = tbl.add_tr(height=rowheight) for col in range(cols): tr.add_tc() return tbl def tc(self, row_idx, col_idx): """Return `a:tc` element at *row_idx*, *col_idx*.""" return self.tr_lst[row_idx].tc_lst[col_idx] def _get_boolean_property(self, propname): """ Generalized getter for the boolean properties on the ``<a:tblPr>`` child element. Defaults to False if *propname* attribute is missing or ``<a:tblPr>`` element itself is not present. """ tblPr = self.tblPr if tblPr is None: return False propval = getattr(tblPr, propname) return {True: True, False: False, None: False}[propval] def _set_boolean_property(self, propname, value): """ Generalized setter for boolean properties on the ``<a:tblPr>`` child element, setting *propname* attribute appropriately based on *value*. If *value* is True, the attribute is set to "1"; a tblPr child element is added if necessary. If *value* is False, the *propname* attribute is removed if present, allowing its default value of False to be its effective value. """ if value not in (True, False): raise ValueError( "assigned value must be either True or False, got %s" % value ) tblPr = self.get_or_add_tblPr() setattr(tblPr, propname, value) @classmethod def _tbl_tmpl(cls): return ( "<a:tbl %s>\n" ' <a:tblPr firstRow="1" bandRow="1">\n' " <a:tableStyleId>%s</a:tableStyleId>\n" " </a:tblPr>\n" " <a:tblGrid/>\n" "</a:tbl>" % (nsdecls("a"), "%s") )
class CT_PlotArea(BaseOxmlElement): """ ``<c:plotArea>`` element. """ catAx = ZeroOrMore("c:catAx") valAx = ZeroOrMore("c:valAx") def iter_sers(self): """ Generate each of the `c:ser` elements in this chart, ordered first by the document order of the containing xChart element, then by their ordering within the xChart element (not necessarily document order). """ for xChart in self.iter_xCharts(): for ser in xChart.iter_sers(): yield ser def iter_xCharts(self): """ Generate each xChart child element in document. """ plot_tags = ( qn("c:area3DChart"), qn("c:areaChart"), qn("c:bar3DChart"), qn("c:barChart"), qn("c:bubbleChart"), qn("c:doughnutChart"), qn("c:line3DChart"), qn("c:lineChart"), qn("c:ofPieChart"), qn("c:pie3DChart"), qn("c:pieChart"), qn("c:radarChart"), qn("c:scatterChart"), qn("c:stockChart"), qn("c:surface3DChart"), qn("c:surfaceChart"), ) for child in self.iterchildren(): if child.tag not in plot_tags: continue yield child @property def last_ser(self): """ Return the last `<c:ser>` element in the last xChart element, based on series order (not necessarily the same element as document order). """ last_xChart = self.xCharts[-1] sers = last_xChart.sers if not sers: return None return sers[-1] @property def next_idx(self): """ Return the next available `c:ser/c:idx` value within the scope of this chart, the maximum idx value found on existing series, incremented by one. """ idx_vals = [s.idx.val for s in self.sers] if not idx_vals: return 0 return max(idx_vals) + 1 @property def next_order(self): """ Return the next available `c:ser/c:order` value within the scope of this chart, the maximum order value found on existing series, incremented by one. """ order_vals = [s.order.val for s in self.sers] if not order_vals: return 0 return max(order_vals) + 1 @property def sers(self): """ Return a sequence containing all the `c:ser` elements in this chart, ordered first by the document order of the containing xChart element, then by their ordering within the xChart element (not necessarily document order). """ return tuple(self.iter_sers()) @property def xCharts(self): """ Return a sequence containing all the `c:{x}Chart` elements in this chart, in document order. """ return tuple(self.iter_xCharts())
class CT_Path2DList(BaseOxmlElement): """ """ path = ZeroOrMore('a:path')
class CT_Path2D(BaseOxmlElement): """ """ moveTo = ZeroOrMore('a:moveTo') lnTo = ZeroOrMore('a:lnTo') cubicBezTo = ZeroOrMore('a:cubicBezTo') arcTo = ZeroOrMore('a:arcTo') close = ZeroOrMore('a:close') w = RequiredAttribute('w', ST_PositiveCoordinate) h = RequiredAttribute('h', ST_PositiveCoordinate) def add_moveTo(self, x, y): """ Create a moveTo element to the provided x and y points, specified in pptx.Length units. """ mt = self._add_moveTo() mt.pt.x = x mt.pt.y = y return mt def add_lnTo(self, x, y): """ Create a lineTo element to the provided x and y points, specified in pptx.Length units. """ lt = self._add_lnTo() lt.pt.x = x lt.pt.y = y return lt def add_cubicBezTo(self, x1, y1, x2, y2, x, y): """ Create a cubicBezTo element to provided points. """ cbt = self._add_cubicBezTo() pt = cbt._add_pt() pt.x = x1 pt.y = y1 pt = cbt._add_pt() pt.x = x2 pt.y = y2 pt = cbt._add_pt() pt.x = x pt.y = y return cbt def add_arcTo(self, hR, wR, stAng, swAng): """ Create a arcTo element to provided info. """ at = self._add_arcTo() at.hR = hR at.wR = wR at.stAng = stAng at.swAng = swAng return at def _new_moveTo(self): return CT_Path2DMoveTo.new() def _new_lnTo(self): return CT_Path2DLineTo.new()
class CT_GeomGuideList(BaseOxmlElement): """ ``<a:avLst>`` custom element class """ gd = ZeroOrMore("a:gd")
class CT_Table(BaseOxmlElement): """ ``<a:tbl>`` custom element class """ tblPr = ZeroOrOne('a:tblPr', successors=('a:tblGrid', 'a:tr')) tblGrid = OneAndOnlyOne('a:tblGrid') tr = ZeroOrMore('a:tr', successors=()) def add_tr(self, height): """ Return a reference to a newly created <a:tr> child element having its ``h`` attribute set to *height*. """ return self._add_tr(h=height) @property def bandCol(self): return self._get_boolean_property('bandCol') @bandCol.setter def bandCol(self, value): self._set_boolean_property('bandCol', value) @property def bandRow(self): return self._get_boolean_property('bandRow') @bandRow.setter def bandRow(self, value): self._set_boolean_property('bandRow', value) @property def firstCol(self): return self._get_boolean_property('firstCol') @firstCol.setter def firstCol(self, value): self._set_boolean_property('firstCol', value) @property def firstRow(self): return self._get_boolean_property('firstRow') @firstRow.setter def firstRow(self, value): self._set_boolean_property('firstRow', value) @property def lastCol(self): return self._get_boolean_property('lastCol') @lastCol.setter def lastCol(self, value): self._set_boolean_property('lastCol', value) @property def lastRow(self): return self._get_boolean_property('lastRow') @lastRow.setter def lastRow(self, value): self._set_boolean_property('lastRow', value) def _get_boolean_property(self, propname): """ Generalized getter for the boolean properties on the ``<a:tblPr>`` child element. Defaults to False if *propname* attribute is missing or ``<a:tblPr>`` element itself is not present. """ tblPr = self.tblPr if tblPr is None: return False propval = getattr(tblPr, propname) return {True: True, False: False, None: False}[propval] def _set_boolean_property(self, propname, value): """ Generalized setter for boolean properties on the ``<a:tblPr>`` child element, setting *propname* attribute appropriately based on *value*. If *value* is True, the attribute is set to "1"; a tblPr child element is added if necessary. If *value* is False, the *propname* attribute is removed if present, allowing its default value of False to be its effective value. """ if value not in (True, False): raise ValueError( "assigned value must be either True or False, got %s" % value) tblPr = self.get_or_add_tblPr() setattr(tblPr, propname, value) @classmethod def new_tbl(cls, rows, cols, width, height, tableStyleId=None): """ Return a new ``<p:tbl>`` element tree """ # working hypothesis is this is the default table style GUID if tableStyleId is None: tableStyleId = '{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}' xml = cls._tbl_tmpl() % (tableStyleId) tbl = parse_xml(xml) # add specified number of rows and columns rowheight = height // rows colwidth = width // cols for col in range(cols): # adjust width of last col to absorb any div error if col == cols - 1: colwidth = width - ((cols - 1) * colwidth) tbl.tblGrid.add_gridCol(width=colwidth) for row in range(rows): # adjust height of last row to absorb any div error if row == rows - 1: rowheight = height - ((rows - 1) * rowheight) tr = tbl.add_tr(height=rowheight) for col in range(cols): tr.add_tc() return tbl @classmethod def _tbl_tmpl(cls): return ('<a:tbl %s>\n' ' <a:tblPr firstRow="1" bandRow="1">\n' ' <a:tableStyleId>%s</a:tableStyleId>\n' ' </a:tblPr>\n' ' <a:tblGrid/>\n' '</a:tbl>' % (nsdecls('a'), '%s'))
class CT_DLbls(BaseOxmlElement): """`c:dLbls` element specifying properties for a set of data labels.""" _tag_seq = ('c:dLbl', 'c:numFmt', 'c:spPr', 'c:txPr', 'c:dLblPos', 'c:showLegendKey', 'c:showVal', 'c:showCatName', 'c:showSerName', 'c:showPercent', 'c:showBubbleSize', 'c:separator', 'c:showLeaderLines', 'c:leaderLines', 'c:extLst') dLbl = ZeroOrMore('c:dLbl', successors=_tag_seq[1:]) numFmt = ZeroOrOne('c:numFmt', successors=_tag_seq[2:]) txPr = ZeroOrOne('c:txPr', successors=_tag_seq[4:]) dLblPos = ZeroOrOne('c:dLblPos', successors=_tag_seq[5:]) showLegendKey = ZeroOrOne('c:showLegendKey', successors=_tag_seq[6:]) showVal = ZeroOrOne('c:showVal', successors=_tag_seq[7:]) showCatName = ZeroOrOne('c:showCatName', successors=_tag_seq[8:]) showSerName = ZeroOrOne('c:showSerName', successors=_tag_seq[9:]) showPercent = ZeroOrOne('c:showPercent', successors=_tag_seq[10:]) del _tag_seq @property def defRPr(self): """ ``<a:defRPr>`` great-great-grandchild element, added with its ancestors if not present. """ txPr = self.get_or_add_txPr() defRPr = txPr.defRPr return defRPr def get_dLbl_for_point(self, idx): """ Return the `c:dLbl` child representing the label for the data point at index *idx*. """ matches = self.xpath('c:dLbl[c:idx[@val="%d"]]' % idx) if matches: return matches[0] return None def get_or_add_dLbl_for_point(self, idx): """ Return the `c:dLbl` element representing the label of the point at index *idx*. """ matches = self.xpath('c:dLbl[c:idx[@val="%d"]]' % idx) if matches: return matches[0] return self._insert_dLbl_in_sequence(idx) @classmethod def new_dLbls(cls): """Return a newly created "loose" `c:dLbls` element.""" return parse_xml('<c:dLbls %s>\n' ' <c:showLegendKey val="0"/>\n' ' <c:showVal val="0"/>\n' ' <c:showCatName val="0"/>\n' ' <c:showSerName val="0"/>\n' ' <c:showPercent val="0"/>\n' ' <c:showBubbleSize val="0"/>\n' ' <c:showLeaderLines val="1"/>\n' '</c:dLbls>' % nsdecls('c')) def _insert_dLbl_in_sequence(self, idx): """ Return a newly created `c:dLbl` element having `c:idx` child of *idx* and inserted in numeric sequence among the `c:dLbl` children of this element. """ new_dLbl = self._new_dLbl() new_dLbl.idx.val = idx dLbl = None for dLbl in self.dLbl_lst: if dLbl.idx_val > idx: dLbl.addprevious(new_dLbl) return new_dLbl if dLbl is not None: dLbl.addnext(new_dLbl) else: self.insert(0, new_dLbl) return new_dLbl def _new_dLbl(self): return CT_DLbl.new_dLbl() def _new_showCatName(self): """Return a new `c:showCatName` with value initialized. This method is called by the metaclass-generated code whenever a new `c:showCatName` element is required. In this case, it defaults to `val=true`, which is not what we need so we override to make val explicitly False. """ return parse_xml('<c:showCatName %s val="0"/>' % nsdecls('c')) def _new_showLegendKey(self): return parse_xml('<c:showLegendKey %s val="0"/>' % nsdecls('c')) def _new_showPercent(self): return parse_xml('<c:showPercent %s val="0"/>' % nsdecls('c')) def _new_showSerName(self): return parse_xml('<c:showSerName %s val="0"/>' % nsdecls('c')) def _new_showVal(self): return parse_xml('<c:showVal %s val="0"/>' % nsdecls('c')) def _new_txPr(self): return CT_TextBody.new_txPr()