def new_p_txBody(cls): """ Return a new ``<p:txBody>`` element tree, suitable for use in an ``<p:sp>`` element. """ xml = cls._p_txBody_tmpl() return parse_xml(xml)
def create_footer(slide, footercopy): footer = None pageno = None for shape in slide.placeholders: if "SLIDE_NUMBER" in str(shape.placeholder_format.type ): # Master must have placeholder to function pageno = slide.placeholders[shape.placeholder_format.idx] elif "FOOTER" in str(shape.placeholder_format.type ): # Master must have placeholder to function footer = slide.placeholders[shape.placeholder_format.idx] footer_text_frame = footer.text_frame footer_pageno = pageno.text_frame.paragraphs[0]._p # edits XML directly—not ideal, but can't be updated until Python-pptx adds support. fld_xml = ( '<a:fld %s id="{1F4E2DE4-8ADA-4D4E-9951-90A1D26586E7}" type="slidenum">\n' ' <a:rPr lang="en-US" smtClean="0"/>\n' ' <a:t>2</a:t>\n' '</a:fld>\n' % nsdecls("a")) fld = parse_xml(fld_xml) footer_pageno.append(fld) footer_a = 'Note: Due to small base, data is directional' for para_idx, para in enumerate(footercopy): new_para = para.replace("'", "", -1) footercopy[para_idx] = new_para insert_text(footercopy, footer_text_frame, one_level=True)
def _new_fgClr(self): """Override default to add minimum subtree.""" xml = ('<a:fgClr %s>\n' ' <a:srgbClr val="000000"/>\n' '</a:fgClr>\n') % nsdecls('a') fgClr = parse_xml(xml) return fgClr
def adjust_fixture(self, request): snippet_offset, new_ser_count = request.param chartSpace_xml, expected_xml = snippet_seq('adjust-ser-count', snippet_offset, count=2) chartSpace = parse_xml(chartSpace_xml) return chartSpace, new_ser_count, expected_xml
def new_chart(rId): """ Return a new ``<c:chart>`` element """ xml = CT_Chart._chart_tmpl % (rId) chart = parse_xml(xml) return chart
def new_dLbl(cls): """Return a newly created "loose" `c:dLbl` element. The `c:dLbl` element contains the same (fairly extensive) default subtree added by PowerPoint when an individual data label is customized in the UI. Note that the idx value must be set by the client. Failure to set the idx value will likely result in any changes not being visible and may result in a repair error on open. """ return parse_xml( "<c:dLbl %s>\n" ' <c:idx val="666"/>\n' " <c:spPr/>\n" " <c:txPr>\n" " <a:bodyPr/>\n" " <a:lstStyle/>\n" " <a:p>\n" " <a:pPr>\n" " <a:defRPr/>\n" " </a:pPr>\n" " </a:p>\n" " </c:txPr>\n" ' <c:showLegendKey val="0"/>\n' ' <c:showVal val="1"/>\n' ' <c:showCatName val="0"/>\n' ' <c:showSerName val="0"/>\n' ' <c:showPercent val="0"/>\n' ' <c:showBubbleSize val="0"/>\n' "</c:dLbl>" % nsdecls("c", "a") )
def adjust_fixture(self, request): snippet_offset, new_ser_count = request.param chartSpace_xml, expected_xml = snippet_seq( 'adjust-ser-count', snippet_offset, count=2 ) chartSpace = parse_xml(chartSpace_xml) return chartSpace, new_ser_count, expected_xml
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 new_dLbl(cls): """Return a newly created "loose" `c:dLbl` element. The `c:dLbl` element contains the same (fairly extensive) default subtree added by PowerPoint when an individual data label is customized in the UI. Note that the idx value must be set by the client. Failure to set the idx value will likely result in any changes not being visible and may result in a repair error on open. """ return parse_xml('<c:dLbl %s>\n' ' <c:idx val="666"/>\n' ' <c:spPr/>\n' ' <c:txPr>\n' ' <a:bodyPr/>\n' ' <a:lstStyle/>\n' ' <a:p>\n' ' <a:pPr>\n' ' <a:defRPr/>\n' ' </a:pPr>\n' ' </a:p>\n' ' </c:txPr>\n' ' <c:showLegendKey val="0"/>\n' ' <c:showVal val="1"/>\n' ' <c:showCatName val="0"/>\n' ' <c:showSerName val="0"/>\n' ' <c:showPercent val="0"/>\n' ' <c:showBubbleSize val="0"/>\n' '</c:dLbl>' % nsdecls('c', 'a'))
def transition(slide, spec: Union[str, dict], data: dict): ''' Apply transition on slide based on spec. python-pptx does not have this feature, so we tweak XML directly. :arg Slide slide: slide to apply the transition to :arg dict/str spec: type of transition to apply. ``config.yaml`` lists transitions and options. It can also be a dict specifying ``type`` (type of transition), ``duration`` (in seconds) and ``advance`` (auto-advance after seconds) :arg dict data: data context for the ``spec`` expression ''' if spec is None: return # Convert spec into this format: {type: ..., advance: ..., duration: ...} if isinstance(spec, str): spec = {'type': spec} if not isinstance(spec, dict): raise ValueError('transition: %r is not a str or dict' % spec) # conf is from config.yaml, and has all transition types and options conf = commands.conf type = commands.expr(spec.get('type', None), data) if type is not None: # Parse the type into OXML: "glitter diamond left" -> <glitter pattern="diamond" dir="r"/> tag, *options = type.split() attrs = {} if tag in conf['transition-alias']: attrs.update(conf['transition-alias'][tag]) tag = attrs.pop('tag') if tag not in conf['transition']: raise ValueError('transition.type: %s is an unknown transition' % type) trans = conf['transition'][tag] options = trans['default'] if (not options and 'default' in trans) else options for option in options: if option not in trans: raise ValueError( 'transition.type: "%s" has invalid option %s' % (type, option)) attrs.update(trans[option]) # Remove existing transition el = slide.element.find(qn('mc:AlternateContent')) if el is not None: slide.element.remove(el) # Add transition OXML # TODO: fails on slides with equations, zoom, or any other mc:alternateContent if tag != 'none': ns = trans.get('ns', 'p') attrs = ' '.join('%s="%s"' % (k, v) for k, v in attrs.items()) xml = conf['transition-tmpl'][ns].format(tag=tag, attrs=attrs) el = parse_xml(xml)[0] slide.element.append(el) # Add attributes for duration: and advance: for key, attr in (('duration', qn('p14:dur')), ('advance', 'advTm')): val = commands.expr(spec.get(key, None), data) if val is not None: trans = slide.element.find( 'mc:AlternateContent/mc:Choice/p:transition', _nsmap) if trans is not None: trans.set(attr, '%s' % int(float(val) * 1000))
def new_placeholder_sp(id_, name, ph_type, orient, sz, idx): """ Return a new ``<p:sp>`` element tree configured as a placeholder shape. """ tmpl = CT_Shape._ph_sp_tmpl() xml = tmpl % (id_, name) sp = parse_xml(xml) ph = sp.nvSpPr.nvPr.get_or_add_ph() ph.type = ph_type ph.idx = idx ph.orient = orient ph.sz = sz placeholder_types_that_have_a_text_frame = ( PP_PLACEHOLDER.TITLE, PP_PLACEHOLDER.CENTER_TITLE, PP_PLACEHOLDER.SUBTITLE, PP_PLACEHOLDER.BODY, PP_PLACEHOLDER.OBJECT, ) if ph_type in placeholder_types_that_have_a_text_frame: sp.append(CT_TextBody.new()) return sp
def new(cls): """ Return a new ``<p:txBody>`` element tree """ xml = cls._txBody_tmpl() txBody = parse_xml(xml) return txBody
def new(cls): """ Return a new ``<a:tc>`` element tree. """ xml = cls._tc_tmpl() tc = parse_xml(xml) return tc
def _new_bgClr(self): """Override default to add minimum subtree.""" xml = ("<a:bgClr %s>\n" ' <a:srgbClr val="FFFFFF"/>\n' "</a:bgClr>\n") % nsdecls("a") bgClr = parse_xml(xml) return bgClr
def rewrite_fixture(self): ser_xml, expected_xml = snippet_seq('rewrite-ser')[:2] chart_data = CategoryChartData() chart_data.categories = ('foo', 'bar') series_data = chart_data.add_series('Series 1', (1, 2)) rewriter = _CategorySeriesXmlRewriter(chart_data) ser = parse_xml(ser_xml) return rewriter, ser, series_data, expected_xml
def new_title(): """Return "loose" `c:title` element containing default children.""" return parse_xml( "<c:title %s>" " <c:layout/>" ' <c:overlay val="0"/>' "</c:title>" % nsdecls("c") )
def new_a_txBody(cls): """ Return a new ``<a:txBody>`` element tree, suitable for use in a table cell and possibly other situations. """ xml = cls._a_txBody_tmpl() txBody = parse_xml(xml) return txBody
def from_xml(cls, content_types_xml): """Return |_ContentTypeMap| instance populated from `content_types_xml`.""" types_elm = parse_xml(content_types_xml) overrides = CaseInsensitiveDict((o.partName.lower(), o.contentType) for o in types_elm.override_lst) defaults = CaseInsensitiveDict((d.extension.lower(), d.contentType) for d in types_elm.default_lst) return cls(overrides, defaults)
def new_autoshape_sp(id_, name, prst, left, top, width, height): """ Return a new ``<p:sp>`` element tree configured as a base auto shape. """ tmpl = CT_Shape._autoshape_sp_tmpl() xml = tmpl % (id_, name, left, top, width, height, prst) sp = parse_xml(xml) return sp
def new_graphicFrame(cls, id_, name, x, y, cx, cy): """ Return a new ``<p:graphicFrame>`` element tree suitable for containing a table or chart. Note that a graphicFrame element is not a valid shape until it contains a graphical object such as a table. """ xml = cls._graphicFrame_tmpl() % (id_, name, x, y, cx, cy) graphicFrame = parse_xml(xml) return graphicFrame
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 add_noFill_bgPr(self): """Return a new `p:bgPr` element with noFill properties.""" xml = ("<p:bgPr %s>\n" " <a:noFill/>\n" " <a:effectLst/>\n" "</p:bgPr>" % nsdecls("a", "p")) bgPr = parse_xml(xml) self._insert_bgPr(bgPr) return bgPr
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 add_noFill_bgPr(self): """Return a new `p:bgPr` element with noFill properties.""" xml = ('<p:bgPr %s>\n' ' <a:noFill/>\n' ' <a:effectLst/>\n' '</p:bgPr>' % nsdecls('a', 'p')) bgPr = parse_xml(xml) self._insert_bgPr(bgPr) return bgPr
def _xml_rels_for(self, partname): """Return CT_Relationships object formed by parsing rels XML for `partname`. A CT_Relationships object is returned in all cases. A part that has no relationships receives an "empty" CT_Relationships object, i.e. containing no `CT_Relationship` objects. """ rels_xml = self._package_reader.rels_xml_for(partname) return CT_Relationships.new() if rels_xml is None else parse_xml( rels_xml)
def _new_rich(self): return parse_xml("<c:rich %s>" " <a:bodyPr/>" " <a:lstStyle/>" " <a:p>" " <a:pPr>" " <a:defRPr/>" " </a:pPr>" " </a:p>" "</c:rich>" % nsdecls("c", "a"))
def new_freeform_sp(shape_id, name, x, y, cx, cy): """Return new `p:sp` element tree configured as freeform shape. The returned shape has a `a:custGeom` subtree but no paths in its path list. """ tmpl = CT_Shape._freeform_sp_tmpl() xml = tmpl % (shape_id, name, x, y, cx, cy) sp = parse_xml(xml) return sp
def _new_rich(self): return parse_xml('<c:rich %s>' ' <a:bodyPr/>' ' <a:lstStyle/>' ' <a:p>' ' <a:pPr>' ' <a:defRPr/>' ' </a:pPr>' ' </a:p>' '</c:rich>' % nsdecls('c', 'a'))
def _add_childTnLst(self): """Add `./p:timing/p:tnLst/p:par/p:cTn/p:childTnLst` descendant. Any existing `p:timing` child element is ruthlessly removed and replaced. """ self.remove(self.get_or_add_timing()) timing = parse_xml(self._childTnLst_timing_xml()) self._insert_timing(timing) return timing.xpath("./p:tnLst/p:par/p:cTn/p:childTnLst")[0]
def rewrite_fixture(self, request): snippet_offset, categories = request.param chart_data = CategoryChartData() chart_data.categories = categories rewriter = _CategorySeriesXmlRewriter(chart_data) snippets = snippet_seq('rewrite-ser') ser_xml = snippets[snippet_offset] ser = parse_xml(ser_xml) series_data = chart_data.add_series('Series 1', (1, 2)) expected_xml = snippets[snippet_offset+1] return rewriter, ser, series_data, expected_xml
def rewrite_fixture(self): ser_xml, expected_xml = snippet_seq('rewrite-ser')[4:6] chart_data = BubbleChartData() series_data = chart_data.add_series('Series 1') series_data.add_data_point(1, 2, 10) series_data.add_data_point(3, 4, 20) rewriter = _BubbleSeriesXmlRewriter(chart_data) ser = parse_xml(ser_xml) return rewriter, ser, series_data, expected_xml
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 rewrite_fixture(self): ser_xml, expected_xml = snippet_seq('rewrite-ser')[2:4] chart_data = XyChartData() series_data = chart_data.add_series('Series 1') series_data.add_data_point(1, 2) series_data.add_data_point(3, 4) rewriter = _XySeriesXmlRewriter(chart_data) ser = parse_xml(ser_xml) return rewriter, ser, series_data, expected_xml
def rewrite_fixture(self, request): snippet_offset, categories = request.param chart_data = CategoryChartData() chart_data.categories = categories rewriter = _CategorySeriesXmlRewriter(chart_data) snippets = snippet_seq("rewrite-ser") ser_xml = snippets[snippet_offset] ser = parse_xml(ser_xml) series_data = chart_data.add_series("Series 1", (1, 2)) expected_xml = snippets[snippet_offset + 1] return rewriter, ser, series_data, expected_xml
def rewrite_fixture(self): ser_xml, expected_xml = snippet_seq("rewrite-ser")[2:4] chart_data = XyChartData() series_data = chart_data.add_series("Series 1") series_data.add_data_point(1, 2) series_data.add_data_point(3, 4) rewriter = _XySeriesXmlRewriter(chart_data) ser = parse_xml(ser_xml) return rewriter, ser, series_data, expected_xml
def rewrite_fixture(self): ser_xml, expected_xml = snippet_seq("rewrite-ser")[4:6] chart_data = BubbleChartData() series_data = chart_data.add_series("Series 1") series_data.add_data_point(1, 2, 10) series_data.add_data_point(3, 4, 20) rewriter = _BubbleSeriesXmlRewriter(chart_data) ser = parse_xml(ser_xml) return rewriter, ser, series_data, expected_xml
def _new_rich(self): return parse_xml( "<c:rich %s>" " <a:bodyPr/>" " <a:lstStyle/>" " <a:p>" " <a:pPr>" " <a:defRPr/>" " </a:pPr>" " </a:p>" "</c:rich>" % nsdecls("c", "a") )
def it_raises_when_shape_type_called_on_unrecognized_shape(self): xml_ = ( '<p:sp xmlns:p="http://schemas.openxmlformats.org/presentationml/' '2006/main" xmlns:a="http://schemas.openxmlformats.org/drawingml/' '2006/main"><p:nvSpPr><p:cNvPr id="9" name="Unknown Shape Type 8"' '/><p:cNvSpPr/><p:nvPr/></p:nvSpPr><p:spPr/></p:sp>' ) sp = parse_xml(xml_) shape = Shape(sp, None) # verify ----------------------- with pytest.raises(NotImplementedError): shape.shape_type
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 but_accepts_unicode_providing_there_is_no_encoding_declaration(self): non_enc_decl = '<?xml version="1.0" standalone="yes"?>' enc_decl = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' xml_body = '<foo><bar>føøbår</bar></foo>' # unicode body by itself doesn't raise parse_xml(xml_body) # adding XML decl without encoding attr doesn't raise either xml_text = '%s\n%s' % (non_enc_decl, xml_body) parse_xml(xml_text) # but adding encoding in the declaration raises ValueError xml_text = '%s\n%s' % (enc_decl, xml_body) with pytest.raises(ValueError): parse_xml(xml_text)
def new_grpSp(cls, id_, name): """Return new "loose" `p:grpSp` element having *id_* and *name*.""" xml = ( "<p:grpSp %s>\n" " <p:nvGrpSpPr>\n" ' <p:cNvPr id="%%d" name="%%s"/>\n' " <p:cNvGrpSpPr/>\n" " <p:nvPr/>\n" " </p:nvGrpSpPr>\n" " <p:grpSpPr>\n" " <a:xfrm>\n" ' <a:off x="0" y="0"/>\n' ' <a:ext cx="0" cy="0"/>\n' ' <a:chOff x="0" y="0"/>\n' ' <a:chExt cx="0" cy="0"/>\n' " </a:xfrm>\n" " </p:grpSpPr>\n" "</p:grpSp>" % nsdecls("a", "p", "r") ) % (id_, name) grpSp = parse_xml(xml) return grpSp
def element(self): """Return element based on XML generated by builder""" return parse_xml(self.xml)
def flat_fixture(self, request): snippet_idx, expected_values = request.param xChart_xml = snippet_seq("cat-labels")[snippet_idx] categories = Categories(parse_xml(xChart_xml)) return categories, expected_values
def it_prefers_to_parse_bytes(self, xml_bytes): parse_xml(xml_bytes)
def rewrite_ser_fixture(self): ser_xml, expected_xml = snippet_seq('rewrite-ser') ser = parse_xml(ser_xml) series_data = _SeriesData(2, 'Series 4', (1, 2), ('Foo', 'Bar')) return ser, series_data, expected_xml
def it_uses_oxml_configured_parser_to_parse_xml( self, mock_xml_bytes, fromstring, mock_oxml_parser): element = parse_xml(mock_xml_bytes) fromstring.assert_called_once_with(mock_xml_bytes, mock_oxml_parser) assert element is fromstring.return_value
def _new_showLegendKey(self): return parse_xml('<c:showLegendKey %s val="0"/>' % nsdecls("c"))
def element(self): """ Element parsed from XML generated by builder in current state """ elm = parse_xml(self.xml()) return elm
def _new_showSerName(self): return parse_xml('<c:showSerName %s val="0"/>' % nsdecls("c"))
def _new_showPercent(self): return parse_xml('<c:showPercent %s val="0"/>' % nsdecls("c"))
def part_elm(self): return parse_xml( '<f:foo xmlns:f="http://foo" xmlns:b="http://bar">\n <f:bar>fØØ' "bÅr</f:bar>\n</f:foo>\n" )
def new(cls): """ Return a new ``<p:notes>`` element configured as a base slide shape. """ return parse_xml(cls._notes_xml())
def _new_showVal(self): return parse_xml('<c:showVal %s val="0"/>' % nsdecls("c"))
def element(cxel_str): """ Return an oxml element parsed from the XML generated from *cxel_str*. """ _xml = xml(cxel_str) return parse_xml(_xml)
# -*- coding: utf-8 -*- from pptx.oxml import parse_xml import pdb from .unitutil import serialize_xml nsmap = 'http://schemas.openxmlformats.org/presentationml/2006/main' xml = '<p:sp xmlns:p="%s"><p:nvSpPr/></p:sp>' % nsmap sp = parse_xml(xml) pdb.set_trace() msg = 'got:\n\n%s' % serialize_xml(sp)