singerla / pptx-automizer

A template based pptx generator for Node.js
MIT License
50 stars 10 forks source link

How to get "custom tag" values for a shape via a relationship? #103

Closed edwardmcleodjones closed 2 months ago

edwardmcleodjones commented 2 months ago

Custom metadata can be added to a shape via "custom tags". See: https://learn.microsoft.com/en-us/office/dev/add-ins/powerpoint/tagging-presentations-slides-shapes

The format of the relationships in the XML looks as shown in this issue (for a different library): https://github.com/scanny/python-pptx/issues/578#issuecomment-1492500399

Is there a way to access that custom tag information starting from the shape element? For the example above, it'd be nice to get the <p:tag elements referenced by the shape, e.g. starting from:

<p:custDataLst>
  <p:tags r:id="rId3"/>
</p:custDataLst>

then going via the r:id="rId3" relationship to eventually get the values:

<p:tag name="USE" val="1"/>
<p:tag name="NAME" val="spont_sub_footer"/>
<p:tag name="CODENAME" val="txt_spont_sub_footer"/>

I've been trying various things with XmlRelationshipHelper, but not really sure how it's meant to work.


I am using this open source add-in to add the custom tags, if that helps you create a pptx to replicate/test against: https://github.com/iappyx/Instrumenta

image

edwardmcleodjones commented 2 months ago

Some additional context (albeit for .Net): https://stackoverflow.com/questions/75085311/openxml-sdk-how-to-get-tag-ptag-val-of-a-powerpoint-shape

Also, looks like it's quite easy to add tags using the "Script Lab" add-in: https://stackoverflow.com/questions/75085311/openxml-sdk-how-to-get-tag-ptag-val-of-a-powerpoint-shape#comment132722760_75203796

singerla commented 2 months ago

Hi! I was playing around bit with XmlRelationshipHelper in general. The following could help:

  // Taken from __tests__ templates
  const pres = automizer
    .loadRoot(`RootTemplate.pptx`)
    .load(`ChartBarsStacked.pptx`, 'charts');

  await pres
    .addSlide('charts', 1, async (slide: any) => {
      const sourceArchive = slide.sourceTemplate.archive;
      const sourceSlideNumber = slide.sourceNumber;

      const relsHelper = new XmlRelationshipHelper();
      await relsHelper.initialize(
        sourceArchive,
        `slides/_rels/slide${sourceSlideNumber}.xml.rels`,
        'ppt',
      );

      // Find your target by "rId"...
      const target = relsHelper.getTargetByRelId('rId2');
      // ... or retrieve a list of all relations matching a certain "Type" attribute
      // const targets = relsHelper.getTargetsByType(
      //   'http://schemas.openxmlformats.org/officeDocument/2006/relationships/tags',
      // );

      const targetXml = await XmlHelper.getXmlFromArchive(
        slide.sourceTemplate.archive,
        'ppt/tags/' + target.filename,
      );

      const tagsList = targetXml.getElementsByTagName('p:tag');
    })

Please let me know if this is feasible for custom tags. In case of doubt, please provide an example pptx to investigate this more detailed.

edwardmcleodjones commented 2 months ago

Thanks @singerla!

I can iterate over each shape in each slide with const allSlideElements = await slide.getAllElements(); and then get the XML for each shape with slideEl.getXmlElement(); then get the tags within that shape with getElementsByTagName("p:tags"), and then get the rId for each tag with: tag.getAttribute("r:id"), and then use the getTargetByRelId method as above and go from there.

Works perfectly, thanks!