Portion (Format Effective) Data Missing when Saving Presentation in Java (2772)

Hi,

I am writing to you about a problem we encountered in working with MSO PowerPoint document using the Aspose Slides for Java library (v21.10).

Specifically, when we try to delete and then immediately add a Portion object (new one, custom made, similar to the original) to the same Paragraph portion list of a SmartArt shape object Node, there is a problem, more precisely throwing an exception (NullPointerException), when trying to read the next Portion object format effective data (in this case, it is a Portion object that belongs to even a different Node object).

If no change is applied to the first mentioned Portion object, the data for the second is read without problems (try to comment lines of deleting, making and adding new portion for the first Node object).

I am enclosing the zip file with test code, as well as the source PP file.

In the hope that together we will quickly overcome the problem!
Best regards!

Nenad

test code and file.zip (52.6 KB)

@zpredojevic,
Thank you for the issue description.

I’ve reproduced the problem with the NullPointerException error and added a ticket with ID SLIDESJAVA-38693 in our issue tracking system. Our development team will investigate this case. You will be notified when the issue is resolved.

@zpredojevic,
Our development team has investigated the issue you described above. This exception occurs because you are trying to work with the SmartArt object in Paragraph-Portion logic. But SmartArt object is a complex object, not just Paragraphs and Portions. Unfortunately, the issue description does not contain what you wanted to achieve, but we fixed your code, now it works without exceptions.

String presentationPath = "file/source.pptx";

Presentation presentation = loadPresentationDocument(presentationPath);

ISmartArt shape = (SmartArt) presentation.getSlides().get_Item(0).getShapes().get_Item(1);

/*** FIRST NODE PORTION ***/

ISmartArtNode firstNode = shape.getAllNodes().get_Item(0);
IParagraph firstNodeParagraph = firstNode.getTextFrame().getParagraphs().get_Item(0);
IPortion firstNodePortion = firstNodeParagraph.getPortions().get_Item(0);

assertEquals("Kaitseringkond", firstNodePortion.getText());

IPortionFormatEffectiveData effective = firstNodePortion.getPortionFormat().getEffective();
assertNotNull(effective);

System.out.println("First node paragraph's portion data read properly (before changes).");

// reading Portion's format data
double fontSize = effective.getFontHeight();
Color color = effective.getFillFormat().getSolidFillColor();
String fontName = effective.getLatinFont().getFontName();
String portionText = firstNodePortion.getText();

// deleting Portion from Paragraph
firstNodeParagraph.getPortions().clear();
assertEquals(0, firstNodeParagraph.getPortions().getCount());

// creating new Portion and formatting it
IPortion newFirstPortion = new Portion();
// adding to Paragraph
firstNodeParagraph.getPortions().add(newFirstPortion);
assertEquals(1, firstNodeParagraph.getPortions().getCount());

// formating
newFirstPortion.getPortionFormat().setFontHeight((float) fontSize);
newFirstPortion.getPortionFormat().getFillFormat().setFillType(FillType.Solid);
newFirstPortion.getPortionFormat().getFillFormat().getSolidFillColor().setColorType(ColorType.RGB);
newFirstPortion.getPortionFormat().getFillFormat().getSolidFillColor().setColor(color);
newFirstPortion.getPortionFormat().setLatinFont(new FontData(fontName));
// writing text and checking it
firstNode.getTextFrame().setText(portionText);
assertEquals("Kaitseringkond", newFirstPortion.getText());

// checking the existence of first Node Paragraph's new Portion
effective = firstNodePortion.getPortionFormat().getEffective();
assertNotNull(effective);

System.out.println("First node paragraph's portion data read properly (after changes).");


/*** SECOND NODE PORTION ***/

ISmartArtNode secondNode = shape.getAllNodes().get_Item(1);
IParagraph secondNodeParagraph = secondNode.getTextFrame().getParagraphs().get_Item(0);
IPortion secondNodePortion = secondNodeParagraph.getPortions().get_Item(0);

assertEquals("Kaitseringkonna staap", secondNodePortion.getText());

// IF THERE IS ANY CHANGE ON PREVIOUS (FIRST) PORTION
// (regardless of the fact that it does not even belong to the same Node object),
// IT LEADS TO IMPOSSIBILITY TO READ THIS (SECOND) PORTION'S FORMAT EFFECTIVE DATA
// AND THROWS AN NULL_POINTER_EXCEPTION
// (if the previous change, on the first Portion object, is commented there will be no problem)
effective = secondNodePortion.getPortionFormat().getEffective();
assertNotNull(effective);

System.out.println("Second node paragraph's portion data read properly (before changes).");

fontSize = effective.getFontHeight();
color = effective.getFillFormat().getSolidFillColor();
fontName = effective.getLatinFont().getFontName();
portionText = secondNodePortion.getText();

secondNodeParagraph.getPortions().clear();
assertEquals(0, secondNodeParagraph.getPortions().getCount());

// creating new Portion and formatting it
IPortion newSecondPortion = new Portion();
// adding to Paragraph
secondNodeParagraph.getPortions().add(newSecondPortion);
assertEquals(1, secondNodeParagraph.getPortions().getCount());

// formating
newSecondPortion.getPortionFormat().setFontHeight((float) fontSize);
newSecondPortion.getPortionFormat().getFillFormat().setFillType(FillType.Solid);
newSecondPortion.getPortionFormat().getFillFormat().getSolidFillColor().setColorType(ColorType.RGB);
newSecondPortion.getPortionFormat().getFillFormat().getSolidFillColor().setColor(color);
newSecondPortion.getPortionFormat().setLatinFont(new FontData(fontName));
// writing text and checking it
secondNode.getTextFrame().setText(portionText);
assertEquals("Kaitseringkond", newSecondPortion.getText());

// checking the existence of first Node Paragraph's new Portion
effective = firstNodePortion.getPortionFormat().getEffective();
assertNotNull(effective);

System.out.println("Second node paragraph's portion data read properly (after changes).");

try {
    File tempTargetFile = File.createTempFile("target", ".pptx");
    presentation.save(tempTargetFile.getAbsolutePath(), SaveFormat.Pptx);
} catch (Exception e) {
    fail("There is problem saving document.");
}

Documents:
Manage SmartArt
Manage SmartArt Shape
Manage SmartArt Shape Node

@Andrey_Potapov

The solution you provided us is unfortunately not functional, and in addition it is not operational for our use because we need to provide more formatting over the text we put in TextFrame to meet our needs.

We are still waiting for the solution of the reported problem (impossibility to read the effective data of the next Portion object after the change of the state on the current one) which you keep under ID SLIDESJAVA-38693.

Nenad

@zpredojevic,
Please isolate the problem you encountered and share the following data:

  • minimal code example that reproduces the problem
  • description of what exactly you want to achive

Then we will do our best to help you.

@Andrey_Potapov

The problem, the loss of the EffectiveData on the Portion object if a change is made to the previous one, is already isolated and provided to you when we reported the same.

On that occasion, as always, we provided to you both, the test file and the minimal test code, that reproduce the problem, and that code sample follows the part of out API flow which relies on your API.

All we want from you in this regard is that the Portion’s object EffectiveData exists/persists when a change is made to the previous Portion object (the current state is that the EffectiveData exists if the change is not made, but if it is made the EffectiveData is null).

@zpredojevic,
I’ve added a ticket with ID SLIDESJAVA-38779 to our issue tracking system and described your requirements. Our development team will continue investigating the issue. We will infrom you of any progress.

@zpredojevic,
Our development team has reviewed your requirements and would like to clarify some points.

You can apply any additional formatting using the newFirstPortion and newSecondPortion variables. Could you please share a detailed description of what is not working?

In the corrected code example, this is exactly how it works. How exactly does it not meet your requirements?

First of all, if you look at the attached file (presentation) you can see that it has only one slide, which contains one SmartArt diagram, which has only two fields of hierarchy (according to the terminology of your API two nodes, more precisely two SmartArtNode).

Furthermore, if you follow the attached test code and the comments contained in it, you can see that it performs the following actions on the document (presentation):

  • loads the given diagram
    • accesses the first node (SmartArtNode) and then its first Portion object (via TextFrame and Paragraph objects)
      • after access, checks its contents and the existence of its (PortionFormat)EffectiveData object
      • after the check is successful, the properties of the Portion object are stored in auxiliary variables and the (original) Portion object is deleted
      • after that a new Portion (empty) object is created, it is assigned to the place from which the previous one was deleted, and then all saved values ​​of the previous (original) are set to it
    • after the above, the second node (SmartArtNode) is accessed, ie its Portion object in an identical way as previous (also via TextFrame and Paragraph objects)
      • this is followed by checking its contents (which is OK) and accessing and checking the existence of its (PortionFormat)EffectiveData object (WHICH IS NOT OK).

This is where the whole story stops, because the second read Portion object for the value of its EffectiveData object in this place has null, which should not be and it is a blocker for us in terms of processing this document. The same does not happen unless the first Portion object (the previous SmartArtNode) is modified.

Given the whole story so far, let’s conclude that the part of our needs that relies on your API, related to the reported problem, is access to the original Portion objects of a particular SmartArt object (diagram) and saving its original values ​​for later use. What blocks us from this is the inability to read the value of the EffectiveData property of a particular Portion object in a situation where the Portion object of the previous SmartArtNode was changed, because the same (EffectiveData) is null.

Why this is happening, I don’t know. The answer to that question and the solution to the problem should be given by someone from your DEV team.

As for the solution you provided …
In the form in which the solution was sent, an exception is thrown when checking the contents of a new Portion object. I guess whoever worked on the ticket didn’t want to write what was sent (to set the text for SmartArtNode, and check for its first (new one) Portion object).
On the other hand, the approach you provided us is not appropriate because SmartArtNode itself, over which you directly place text in the solution, has a collection of paragraphs, each of which has a collection of Portion objects, and what is the subject of our interest in this matter are Portion objects, i.e. their content (text) and property values ​​(EffectiveData), that, as can be seen from the said, can be more (than one) at the level of one SmartArtNode.

@zpredojevic,
Thank you for the additional information. Our developers separately checked all the asserts and fixed the errors in them. All asserts now works fine. Please check the code example below.

Presentation presentation = new Presentation("source.pptx");

ISmartArt shape = (SmartArt) presentation.getSlides().get_Item(0).getShapes().get_Item(1);

/*** FIRST NODE PORTION ***/

ISmartArtNode firstNode = shape.getAllNodes().get_Item(0);
IParagraph firstNodeParagraph = firstNode.getTextFrame().getParagraphs().get_Item(0);
IPortion firstNodePortion = firstNodeParagraph.getPortions().get_Item(0);

Assert.assertTrue("Kaitseringkond".equals(firstNodePortion.getText()));

IPortionFormatEffectiveData effective = firstNodePortion.getPortionFormat().getEffective();
Assert.assertNotNull(effective);

System.out.println("First node paragraph's portion data read properly (before changes).");

// reading Portion's format data
double fontSize = effective.getFontHeight();
Color color = effective.getFillFormat().getSolidFillColor();
String fontName = effective.getLatinFont().getFontName();
String portionText = firstNodePortion.getText();

// deleting Portion from Paragraph
firstNodeParagraph.getPortions().clear();
Assert.assertTrue(0 == firstNodeParagraph.getPortions().getCount());

// creating new Portion and formatting it
IPortion newFirstPortion = new Portion();
// adding to Paragraph
firstNodeParagraph.getPortions().add(newFirstPortion);
Assert.assertTrue(1 == firstNodeParagraph.getPortions().getCount());

// formating
newFirstPortion.getPortionFormat().setFontHeight((float) fontSize);
newFirstPortion.getPortionFormat().getFillFormat().setFillType(FillType.Solid);
newFirstPortion.getPortionFormat().getFillFormat().getSolidFillColor().setColorType(ColorType.RGB);
newFirstPortion.getPortionFormat().getFillFormat().getSolidFillColor().setColor(color);
newFirstPortion.getPortionFormat().setLatinFont(new FontData(fontName));
newFirstPortion.setText(portionText);
// writing text and checking it
firstNode.getTextFrame().setText(portionText);

Assert.assertTrue("Kaitseringkond".equals(newFirstPortion.getText()));

// checking the existence of first Node Paragraph's new Portion
effective = firstNodePortion.getPortionFormat().getEffective();
Assert.assertNotNull(effective);

System.out.println("First node paragraph's portion data read properly (after changes).");


/*** SECOND NODE PORTION ***/

ISmartArtNode secondNode = shape.getAllNodes().get_Item(1);
IParagraph secondNodeParagraph = secondNode.getTextFrame().getParagraphs().get_Item(0);
IPortion secondNodePortion = secondNodeParagraph.getPortions().get_Item(0);

Assert.assertTrue("Kaitseringkonna staap".equals(secondNodePortion.getText()));

// IF THERE IS ANY CHANGE ON PREVIOUS (FIRST) PORTION
// (regardless of the fact that it does not even belong to the same Node object),
// IT LEADS TO IMPOSSIBILITY TO READ THIS (SECOND) PORTION'S FORMAT EFFECTIVE DATA
// AND THROWS AN NULL_POINTER_EXCEPTION
// (if the previous change, on the first Portion object, is commented there will be no problem)
effective = secondNodePortion.getPortionFormat().getEffective();
Assert.assertNotNull(effective, "First EffectiveData object is null");

System.out.println("Second node paragraph's portion data read properly (before changes).");

fontSize = effective.getFontHeight();
color = effective.getFillFormat().getSolidFillColor();
fontName = effective.getLatinFont().getFontName();
portionText = secondNodePortion.getText();

secondNodeParagraph.getPortions().clear();
Assert.assertTrue(0 == secondNodeParagraph.getPortions().getCount());

// creating new Portion and formatting it
IPortion newSecondPortion = new Portion();
// adding to Paragraph
secondNodeParagraph.getPortions().add(newSecondPortion);
Assert.assertTrue(1 == secondNodeParagraph.getPortions().getCount());

// formating
newSecondPortion.getPortionFormat().setFontHeight((float) fontSize);
newSecondPortion.getPortionFormat().getFillFormat().setFillType(FillType.Solid);
newSecondPortion.getPortionFormat().getFillFormat().getSolidFillColor().setColorType(ColorType.RGB);
newSecondPortion.getPortionFormat().getFillFormat().getSolidFillColor().setColor(color);
newSecondPortion.getPortionFormat().setLatinFont(new FontData(fontName));
newSecondPortion.setText(portionText);
// writing text and checking it
secondNode.getTextFrame().setText(portionText);

Assert.assertTrue("Kaitseringkonna staap".equals(newSecondPortion.getText()));

// checking the existence of first Node Paragraph's new Portion
effective = firstNodePortion.getPortionFormat().getEffective();
Assert.assertNotNull(effective, "Second EffectiveData object is null");

System.out.println("Second node paragraph's portion data read properly (after changes).");

try {
    presentation.save("output.pptx", SaveFormat.Pptx);
} catch (Exception e) {
    Assert.fail("There is problem saving document.");
}

@Andrey_Potapov

There was no need to send us the same solution with corrected assert statements.

First of all, we did it (corrected your solution) the moment you sent us the original form. If we hadn’t done that, we wouldn’t have had a basis to write everything that followed, and it would be totally unserious of us to waste your time and energy on that.
However, when it comes to the newly provided solution (last one, with corrected assert statements), the same as such is not executable, i.e. has syntax errors. In assert statements where you prove that the EffectiveData object is not null, you need to replace the places of parameters, because this way, as I said, there are the syntax errors, and the code as such is not executable.

Now the main thing, over and over again. The solution you provided us is not good and as such does not meet our needs.

Within the TextFrame object of a particular SmartArtNode object is a collection of Paragrapf objects, each of which contains a collection of Portion objects.
With the solution you provided, in a situation where we have multiple Paragraph/Portion objects in the TextFrame object of a SmartArtNode object, all nested (contained) objects are lost and only one is set in their place - the one for which you called the node.getTextFrame().setText(portionText) syntax.
I really don’t know how to tell you this in a closer and clearer way, because who knows how many times I repeat in a loop the same story.

I am enclosing a piece of code that will, as an example, delete the first (and only) Portion object of the first (and at that moment also the only one) Paragraph object over the second SmartArtNode, then create a new Portion object and set it in the place of the just deleted one, and then, for the same Paragraph object, make a new Portion object (with some different properties - they can be the same, it doesn’t matter). Once this is done, a new Paragraph object is created (for that same SmartArtNode object, i.e. its TextFrame object), and then two new Portion objects are created and placed in it.
At the moment, the SmartArtNode object, i.e. its TextFrame object, contains two Paragraph objects with two Portion objects each, where for all four Portion objects you can access to their EffectiveData objects and read its (ED) properties values (as can be seen from the test).

What is shown here (in the attached test code) at the level of one SmartArtNode object (existence of EffectiveData object of one Portion object due to a change in the previous one, regardless of whether it is in the same or previous Paragraph object), should be provided on multiple SmartArtNode objects models (as reported at the beginning of whole this story - the current Portion object loses data about its EffectiveData object (it becomes null) if there is a change in the Portion object of the previous SmartArtNode object).

As proof of the fact that the solution you provided us is not good and does not meet our needs, at the end of the attached test code there is a commented part of the code, and if the mentioned part of the code remains commented, after the save operation, you will get a document (presentation) which, in the second diagram’s field (SmartArtNode/TextFrame), has two Paragraphs, with two Portions, where each Portion has its own text and EffectiveData properties - as it should be.
On the other hand, if you uncomment commented code lines and run the test, you will get a document which, in the second diagram’s field, has only the content defined using your approach (setting text over the TextFrame object of the given SmartArtNode object - node.getTextFrame().setText(portionText)) - so there is a loss of all pre-existing data of the entire TextFrame object of a given SmartArtNode object (more precisely, data on all its Paragraph and Portion objects are lost).
This whole story has been screenshoted, which is also attached.

files.zip (28.8 KB)

@zpredojevic,
The code example you provided contains unknown methods formatPortion and showReport. Could you please also share them?

@Andrey_Potapov

You’re right.

I omitted to add that part of the code as well.

Below are the updated files.

files.zip (29.0 KB)

@zpredojevic,
Your last code example works fine. I don’t understand the problem. You have shared the presentation example. Could you please also share the expected presentation example that you want to create from the original one?

@Andrey_Potapov

People, please read what is written in the correspondence.

Initially, data loss of the EffectiveData object of a particular Portion object is reported if the Portion object of the previous SmartArtNode object is changed.

The last thing I sent you is how it should work and everything presented is at the level of one SmartArtNode object, and the problem is reproduced when there are more than one SmartArtNode object, emphasizing (the reason why the solution you provided is not good) the need for multiple Paragraph objects for TextFrame of the same SmartArtNode object, where each of the Paragraph objects can (and should) contain more than one Portion objects, where it is necessary to customize each of them separately, which requires the existence of a valid EffectiveData object of each Portion object (and not as reported - null).

Please take seriously what I am writing and let us not waste any more time.

If you do not have a solution currently, write so and when you ensure that the code sample originally sent works (the one with the issue report), let us know.

I really hope that we will not waste time and energy again.

@zpredojevic,
As far as I can see, our development team is working on the issue. We apologize for any inconvenience. We will inform you of any progress.

Hello,

I am writing regarding the problem described above. Please use the same test code in order to reproduce problem.

In the attachment below, you can find the ZIP file with PPTX file.

Best regards!
Aleksandar

pptx-file.zip (29.1 KB)

@zpredojevic,
Thank you for the presentation file. I need some time to remember and understand the problem. We will reply to you as soon as possible.

@zpredojevic,
Thank you for your patience. I’ve forwarded the presentation file to our developers.

I am adding some new examples of the same nature issues.

Attached are the test code with the corresponding PPTX file and its original form.

Nenad

report (2774).zip (4.9 MB)
report (2940).zip (5.8 MB)
report (2997).zip (4.0 MB)
report (3011).zip (5.6 MB)