IPortionEffectiveData Instance Variables Change When the Method Is Called Again

Hello Aspose Team!

I’ve been under the assumption that when I call .getFormat().getEffective() on a Portion object, the resulting IPortionFormatEffective object was immutable and basically a ‘snapshot’ of the format style at the time of calling. However this doesn’t seem to always be the case as I’ve ran into an issue where an existing instance of IPortionFormatEffectiveData returns different objects after a subjective .getEffective() call on either the original portion or the paragraph parent for the portion, after the format of either changes.

I’m not sure if this is a bug or intended, and some clarification would be greatly appreciated as I’ve failed to reproduce the issue when instantiating a Table/AutoShape via code rather than the template provided.

Test template:
test-template.zip (1.4 MB)
Test code:

  @Test
  void iPortionFormatEffectiveData_doesNotChangeAfterGetEffectiveMethodCall() {
   try {
     presentation = new Presentation("test-template.pptx");
     ISlide slide = presentation.getSlides().get_Item(0);
     for (IShape shape : slide.getShapes()) {
       if(shape instanceof ITable) {
         testPortionFormatEffectiveDataDoesNotChange((ITable) shape);
       }
     }
   } finally {
     if (presentation != null) {
       presentation.dispose();
     }
   }
  }

  private static void testPortionFormatEffectiveDataDoesNotChange(ITable table) {
    // This cell triggered the issue originally, but happens with other cells too
    ICell testCell = table.get_Item(2,2);
    // Keeping the paragraph around for triggering the 'bug' later
    IParagraph sourceParagraph = testCell.getTextFrame().getParagraphs().get_Item(0);
    IPortion sourcePortion = sourceParagraph.getPortions().get_Item(0);

    Color originalFillColor = sourcePortion.getPortionFormat().getFillFormat().getSolidFillColor().getColor();
    Color testFillColor = new Color(34, 64, 128);

    SoftAssertions softly = new SoftAssertions();
    // Verify the original fill color is not the same as the test color
    softly.assertThat(originalFillColor.getRed()).isNotEqualTo(testFillColor.getRed());
    softly.assertThat(originalFillColor.getGreen()).isNotEqualTo(testFillColor.getGreen());
    softly.assertThat(originalFillColor.getBlue()).isNotEqualTo(testFillColor.getBlue());

    // Generate the original portions IPortionFormatEffective before changing anything
    IPortionFormatEffectiveData sourcePortionFormatEffective =
      sourcePortion.getPortionFormat().getEffective();
    // Check fill color in IPortionFormatEffective is the expected original color
    softly.assertThat(sourcePortionFormatEffective.getFillFormat().getSolidFillColor()).isEqualTo(originalFillColor);
    // Change the portion fill Color
    sourcePortion.getPortionFormat().getFillFormat().setFillType(FillType.Solid);
    sourcePortion.getPortionFormat().getFillFormat().getSolidFillColor().setColor(testFillColor);
    // Check color in the source portion actually changed
    softly.assertThat(sourcePortion.getPortionFormat().getFillFormat().getSolidFillColor().getColor())
      .isEqualTo(testFillColor);
    // Check fill color in instance of IPortionFormatEffective hasn't changed
    softly.assertThat(sourcePortionFormatEffective.getFillFormat().getSolidFillColor()).isEqualTo(originalFillColor);
    // 'Bug' happens here, generating either IParagraphFormatEffective from parent paragraph
    // or IPortionFormatEffective from the source portion mutates existing instance of IPortionFormatEffective
    // Calling either function below causes following test to fail.
//    sourceParagraph.getParagraphFormat().getEffective();
    sourcePortion.getPortionFormat().getEffective();
    // Color of existing IEffectivePortionFormat should still be the original color
    softly.assertThat(sourcePortionFormatEffective.getFillFormat().getSolidFillColor()).isEqualTo(originalFillColor);
    softly.assertAll();
  }

For clarification: I’ve been using IPortionFormatEffective as a way to capturing the text styling of keywords, and using said styling for different portions. Sometimes the original portion style changes, but I still need the original styling from the template to change/add other portions and apply that style. Do I have to make my own POJO to hold these values or is the use of IPortionFormatEffective correct?

@ecodev

Cause

You are experiencing unexpected behavior with the IPortionFormatEffectiveData instance when calling the .getEffective() method. This method is designed to provide a snapshot of the formatting at the time of the call, but subsequent changes to the formatting of the portion or its parent paragraph are affecting the previously retrieved instance.

Solution

The behavior you are encountering is likely due to the way the library handles effective formatting. When you call .getEffective(), it should return a static snapshot. However, if the formatting of the portion or its parent is modified afterward, the instance may reflect those changes.

To maintain the original formatting, create a custom data structure (POJO) to store the values you need. This approach will allow you to preserve the original formatting independently of any subsequent changes.

Code

Here’s a simple example of how to implement a custom class to hold the formatting values:

class PortionFormatSnapshot {
    private Color fillColor;
    private float fontHeight;

    public PortionFormatSnapshot(Color fillColor, float fontHeight) {
        this.fillColor = fillColor;
        this.fontHeight = fontHeight;
    }

    // Getters
    public Color getFillColor() {
        return fillColor;
    }

    public float getFontHeight() {
        return fontHeight;
    }
}

// Usage
IPortion sourcePortion = sourceParagraph.getPortions().get_Item(0);
PortionFormatSnapshot snapshot = new PortionFormatSnapshot(
    sourcePortion.getPortionFormat().getFillFormat().getSolidFillColor().getColor(),
    sourcePortion.getPortionFormat().getFontHeight()
);

// Later, you can use snapshot.getFillColor() and snapshot.getFontHeight() as needed.

Next Steps

  1. Implement the custom class to store the formatting values you need.
  2. Replace your calls to getEffective() with your custom logic to capture the formatting before any changes are made.
  3. Test your implementation to ensure it behaves as expected.

If you have any further questions or need additional assistance, please let me know!

@ecodev,

Thanks for the PowerPoint presentation file, details and code snippet.

I reproduced the issue as you mentioned by using your sample file and code snippet. I found IPortionEffectiveData instance variables change after method is recalled.

We require thorough evaluation of the issue. We have opened the following new ticket(s) in our internal issue tracking system and will deliver their fixes according to the terms mentioned in Free Support Policies.

Issue ID(s): SLIDESNET-45046

You can obtain Paid Support Services if you need support on a priority basis, along with the direct access to our Paid Support management team.

Thanks Amjad! I’ll keep an eye on this thread.

@ecodev,

You are welcome. We will be sure to keep you informed with any new updates as soon as they become available.

1 Like

@ecodev,
Our developers have investigated the case.

The getEffective() method returns an object representing the computed—inherited and applied—formatting for the specified portion. However, in the current implementation, the IPortionFormatEffectiveData instance obtained via getEffective() is cached and mutable within the library, meaning it isn’t a true “snapshot” of the state but rather a representation of the current calculated formatting.

Consequently:

  • Calling getEffective() again may update this internal cache and even modify a previously obtained object.
  • This is expected behavior, not a bug, although it can be misleading.

Recommendation:
To preserve the formatting “as is” for later reuse—regardless of subsequent changes—you should create your own copy of the data (for example, a POJO containing the necessary properties such as color, font, style, etc.).

Got it, thanks Andrey!
Just a quick question, can you confirm that the values only change if the .getEffective() method is called again?
Also could you confirm that the values won’t change if the parents are deleted/removed from the slide?

@ecodev,
Thank you for the additional questions. I’ve forwarded them to our developers.

@ecodev,

  1. Do values only change if .getEffective() is called again?
    Yes—values update only when you call .getEffective() again. The object returned by .getEffective() reflects the current state of the formatting hierarchy (portion → paragraph → shape → master, etc.). Internally, Aspose caches this object, but a subsequent call will recompute it if any upstream styles have changed.

  2. Will values change if parents are removed from the slide?
    Yes—but only on the next call to .getEffective(). Removing a parent (for example, deleting the master shape or resetting the layout) alters the formatting tree. If you’ve already called .getEffective(), the returned object may become stale, even if it still appears valid. Once you call .getEffective() again, it will re-evaluate the formatting and reflect the missing parent—potentially changing fonts, colors, etc.

Awesome, this will be helpful for later.

Thanks so much Andrey!

@ecodev,
Thank you for using Aspose.Slides.