Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature set: SmartArt support #83

Open
pipern opened this issue Feb 12, 2014 · 3 comments
Open

feature set: SmartArt support #83

pipern opened this issue Feb 12, 2014 · 3 comments
Labels
Milestone

Comments

@pipern
Copy link

pipern commented Feb 12, 2014

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.

@scanny
Copy link
Owner

scanny commented Feb 13, 2014

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 :)

@pipern
Copy link
Author

pipern commented Feb 14, 2014

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?

@scanny
Copy link
Owner

scanny commented Feb 17, 2014

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 :)

@scanny scanny added this to the later milestone Mar 30, 2014
@scanny scanny added the other label Sep 19, 2014
@scanny scanny changed the title SmartArt (http://schemas.openxmlformats.org/drawingml/2006/diagram) support feature set: SmartArt support Sep 19, 2014
@scanny scanny mentioned this issue Oct 3, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants