feature set: SmartArt support
I had a look into what it might take to be able to use placeholders that are within SmartArt elements (as a workaround which may be easier than full support for inserting new SmartArt), but it seems like some extra plumbing is needed - the placeholders are in related parts, and have a different representation (<dgm:prSet phldrT="[Text]" phldr="1"/> vs. the normal p:nvPr/p:ph)
The general approach seems to be a graphicFrame which contains a graphicData:
<a:graphic>
<a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/diagram">
<dgm:relIds xmlns:dgm="http://schemas.openxmlformats.org/drawingml/2006/diagram" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:dm="rId2" r:lo="rId3" r:qs="rId4" r:cs="rId5"/>
</a:graphicData>
</a:graphic>
The relationships provide the layout of the shape, and the actual content:
<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/diagramLayout" Target="../diagrams/layout1.xml"/>
<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/diagramData" Target="../diagrams/data1.xml"/>
<Relationship Id="rId5" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/diagramColors" Target="../diagrams/colors1.xml"/>
<Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/diagramQuickStyle" Target="../diagrams/quickStyle1.xml"/>
The placeholders then look like this (from data1.xml):
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<dgm:dataModel xmlns:dgm="http://schemas.openxmlformats.org/drawingml/2006/diagram" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<dgm:ptLst>
<dgm:pt modelId="{201DA834-84A2-4476-AFF7-041FCF65859D}" type="doc">
<dgm:prSet loTypeId="urn:microsoft.com/office/officeart/2011/layout/CircleProcess" loCatId="process" qsTypeId="urn:microsoft.com/office/officeart/2005/8/quickstyle/simple1" qsCatId="simple" csTypeId="urn:microsoft.com/office/officeart/2005/8/colors/accent1_2" csCatId="accent1" phldr="0"/>
<dgm:spPr/>
<dgm:t>
<a:bodyPr/>
<a:lstStyle/>
<a:p>
<a:endParaRPr lang="en-GB"/>
</a:p>
</dgm:t>
</dgm:pt>
<dgm:pt modelId="{16AB87A3-ECFD-4530-9FA8-036018B97AE3}">
<dgm:prSet phldrT="[Text]" phldr="1"/>
<dgm:spPr/>
<dgm:t>
<a:bodyPr/>
<a:lstStyle/>
<a:p>
<a:endParaRPr lang="en-GB"/>
</a:p>
</dgm:t>
</dgm:pt>
...
I saw that I can find the related part's XML with shape.part.related_parts['rId2']. Possibly I can manipulate that directly.
This is the output of print prs.slides[0].shapes[0].part.blob:
<p:sld xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"><p:cSld><p:spTree><p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr><p:grpSpPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/><a:chOff x="0" y="0"/><a:chExt cx="0" cy="0"/></a:xfrm></p:grpSpPr><p:graphicFrame><p:nvGraphicFramePr><p:cNvPr id="4" name="Diagram 3"/><p:cNvGraphicFramePr/><p:nvPr><p:extLst><p:ext uri="{D42A27DB-BD31-4B8C-83A1-F6EECF244321}"><p14:modId xmlns:p14="http://schemas.microsoft.com/office/powerpoint/2010/main" val="533975228"/></p:ext></p:extLst></p:nvPr></p:nvGraphicFramePr><p:xfrm><a:off x="1524000" y="1397000"/><a:ext cx="6096000" cy="4064000"/></p:xfrm><a:graphic><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/diagram"><dgm:relIds xmlns:dgm="http://schemas.openxmlformats.org/drawingml/2006/diagram" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:dm="rId2" r:lo="rId3" r:qs="rId4" r:cs="rId5"/></a:graphicData></a:graphic></p:graphicFrame></p:spTree><p:extLst><p:ext uri="{BB962C8B-B14F-4D97-AF65-F5344CB8AC3E}"><p14:creationId xmlns:p14="http://schemas.microsoft.com/office/powerpoint/2010/main" val="646705655"/></p:ext></p:extLst></p:cSld><p:clrMapOvr><a:masterClrMapping/></p:clrMapOvr></p:sld>
To have something which can generate SmartArt, I thought of putting the SmartArt items I'd like to use into the slide layouts - and then populating the placeholders. This would be a temporary measure until an add_smartart() is created.
Unfortunately for now, it looks like this will take longer than I have to implement, but I wanted to at least create an issue to track and say what I know so far.
Thanks for this Nick. Definitely looks like a serious job to get right. This will be a handy start when the time comes. Let me know if you decide to dig into it later, happy to help you find internal bits that might be useful for injecting templated XML or what have you :)
Is there a bit of sample code that would demonstrate changing the XML of prs.slides[0].shapes[0].part.blob directly, and then writing it back out when I save the presentation?
Hi Piper, I don't have time to test something at the moment, but this should point you in the right direction. Essentially you can read part.blob and write part._element. The former is XML text (probably unicode), the latter is an lxml.objectify tree.
In general you don't have to trace back up to the part from a shape, the slide object itself is the part. So accessing the XML would be:
slide = prs.slides[0]
xml = slide.blob
If you prefer to work directly with the objectify tree (like ElementTree, but can access children mostly with attribute access), you can use element = slide._element.
once you're done, you'll need to put it back in element form. If you've directly manipulated the element tree using the slide._element reference, there's nothing to do, everything has been updated in place. If you manipulated the XML text using .blob for access, you'll need to re-parse it and put it back in ._element. This should get that done:
from pptx.oxml import parse_xml_bytes
element = parse_xml_bytes(your_xml)
slide._element = element
Never mind the _bytes on the end, parse_xml_bytes likes unicode just fine.
From there you should be able to save normally. Theoretically you should be able to do additional operations on the slide, but I'll bet I'm cacheing some references somewhere, so accessing shapes after reparsing the XML might not work. That might be something worth looking into if the use case is compelling. That and providing built-in hooks for accessing the XML directly.
Let me know how you go :)
Hi all, I'm trying to access Diagram/SmartArt texts (read, translate, and save texts). I can do the first 2 steps now, but still cannot save to texts. I really need some help, thanks a lot !
This is what I've be trying to do:
- In
pptx.oxml.ns.py: add namespace for diagram txBody:
_nsmap = { ... "dsp": "http://schemas.microsoft.com/office/drawing/2008/diagram", }
- In
pptx.oxml.__init__.py: add register function for "dsp:txBody":
register_element_cls("dsp:txBody", CT_TextBody)
- Then I use this piece of code to parse diagram texts in /ppt/diagrams/drawing.xml into TextFrames. It works and resulted TextFrames, paragraphs and runs behaves exactly like in normal shapes:
DRAWING_TEXTFRAME_TAG = "{http://schemas.microsoft.com/office/drawing/2008/diagram}txBody"
...
def get_dgm_txfrms(src_sld:Slide):
tx_frms = []
for rId in list(src_sld.part._rels._rels.keys()):
partname = src_sld.part._rels._rels[rId].target_partname
reg_res = re.search('/ppt/diagrams/([a-zA-Z]+)(\\d+).xml', partname)
if reg_res and len(reg_res.regs) > 2:
dgm_type = partname[reg_res.regs[-2][0]:reg_res.regs[-2][1]]
if dgm_type.lower() == 'drawing':
dgm_data = src_sld.part._rels._rels[rId].target_part.blob
drawing_tree = parse_xml(dgm_data)
tx_frms.extend([TextFrame(frm, src_sld.part) for \
frm in drawing_tree.iter(DRAWING_TEXTFRAME_TAG)])
return tx_frms
- Problem:
After I translated and set texts in TextFrams.Paragraphs.Runs.text, no error or warning, but it cannot affect the actual texts in
src_sld.part._rels._rels[rId].target_part.blob, so the texts cannot be saved todrawing<i>.xml.
Is there any tips and thoughts to make this work? Thanks again!