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

modify.replaceText not working properly #73

Closed
singerla opened this issue Oct 21, 2023 · 3 comments
Closed

modify.replaceText not working properly #73

singerla opened this issue Oct 21, 2023 · 3 comments
Labels
tip Hints and best practices

Comments

@singerla
Copy link
Owner

Aside from that, I'm having a weird issue where modify isn't working. I think this should work, but the modify() doesn't actually replace the text.

let pres = automizer.loadRoot("review-template.pptx");
pres.load("review-template.pptx", "base");
const templates = await pres.getInfo();
const slides = templates.slidesByTemplate("base");

slides.forEach((slide) => {
    pres.addSlide("base", slide.number, async(slide) => {
        const elements = await slide.getAllTextElementIds();

        elements.forEach(element => {

            slide.modifyElement(element, () => {
                modify.replaceText([
                    {
                        replace: 'NAME',
                        by: {
                            text: client.displayName
                        }
                    }]
                )
            })
        })
    });
});

await pres.write("myPresentation.pptx").then((summary) => {
    console.log(summary);
});

Originally posted by @chriscap-fd in #71 (comment)

@singerla singerla added the bug Something isn't working label Oct 21, 2023
@singerla
Copy link
Owner Author

I remember some weird issues in the past which could be related to this. PowerPoint's way to store text in xml can be surprisingly complex. Depending on the order of typing letters into a textbox, they eventually might split up in the file:

<a:t>{{NAME}</a:t>
<a:t>}</a:t>

Although I was trying to handle this by TextReplaceHelper.isolateTaggedNodes(), it seems not to work in all cases.

You can try a workaround:

  • Cut out your {{NAME}} tag from textbox to clipboard.
  • Paste it into a plaintext editor (e.g. notepad) to remove all (visible and invisible) formatting.
  • Copy & paste the tag back to the textbox.

This will not make any visible difference, but join the xml elements.

In general, it seems as if varying text format inside a textbox can break modify.replaceText.

Good luck on this!

@chriscappy16
Copy link

chriscappy16 commented Oct 23, 2023

Well, that was not the issue. I'm a bit confused to be honest. This code works. There's a slight difference on the slide.modifyElement line. I thought modifyElement() took a callback, so I used arrow syntax. I looked closely at the samples, and they don't use that approach. I replaced with just the function call and it worked. Don't quite understand it.

let pres = automizer.loadRoot("review-template.pptx");
pres.load("review-template.pptx", "base");
const templates = await pres.getInfo();
const slides = templates.slidesByTemplate("base");


slides.forEach((slide) => {
    pres.addSlide("base", slide.number, async(slide) => {
        const elements = await slide.getAllTextElementIds();

        elements.forEach(element => {

            slide.modifyElement(element,
                modify.replaceText([
                    {
                        replace: 'NAME',
                        by: {
                            text: client.displayName
                        }
                    }]
                )
            )
        })
    });
});

I think the TS shows it taking a call back. My intellisense says it can take a call back or an array of callbacks.
image

@singerla singerla added tip Hints and best practices and removed bug Something isn't working labels Oct 24, 2023
@singerla
Copy link
Owner Author

Maybe it will become clearer if we take a look at modify.replaceText. I have edited the original code to emphasize where the required callback is constructed:

  /**
   * Replace tagged text content within modified shape
   */
  static replaceText = (
    replaceText: ReplaceText | ReplaceText[],
    options?: ReplaceTextOptions,
  ): ShapeModificationCallback => {
    const shapeModCallback = (element: XmlElement): void => {
      const replaceTexts = GeneralHelper.arrayify(replaceText);

      new TextReplaceHelper(options, element as XmlElement)
        .isolateTaggedNodes()
        .applyReplacements(replaceTexts);
    };
    
    return shapeModCallback;
  };

Applied to your code, we could add a few lines to increase readability:

const replaceParams = [{
    replace: 'NAME',
    by: {
        text: client.displayName
    }
}]

slides.forEach((slide) => {
    pres.addSlide("base", slide.number, async(slide) => {
        const elements = await slide.getAllTextElementIds();
        const shapeModCb = modify.replaceText(replaceParams) as ShapeModificationCallback;

        elements.forEach(element => {
            slide.modifyElement(element, shapeModCb);
            // which equals
            // slide.modifyElement(element, [ shapeModCb ]);
        })
    });
});

For convenience, you can use a single Callback or an array of callbacks. You could also use multiple calls of slide.modifyElement() instead of an array of callbacks, the result will always be the same.

Later on, your callback(s) will be applied to the element XML:

// taken from src/classes/shape.ts
  applyCallbacks(
    callbacks: ShapeModificationCallback[],
    element: XmlElement,
    relation?: XmlElement,
  ): void {
    callbacks.forEach((callback) => {
      if (typeof callback === 'function') {
        try {
          callback(element, relation);
        } catch (e) {
          console.warn(e);
        }
      }
    });
  }

If I have to handle complex code constructions like the above, it helps to add a few lines and declare a some more const. You have more longer, but it is easier to understand.

The latest version v0.4.3 will contain a small type refactoring that differs ShapeModificationCallback and ChartModificationCallback.

I hope this helps! 😃

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
tip Hints and best practices
Projects
None yet
Development

No branches or pull requests

2 participants