InvalidOperationException thrown when replacing MasterSlide with Gradient Background

Hello,

We need to replace a MasterSlide from a Presentation (aka “target”) with a MasterSlide from a “base” Presentation. We noticed that when the MasterSlide from the target Presentation has a gradient background, an InvalidOperationException occurs.

This behavior can be seen in the latest Aspose Slides for Java version 20.3, the attached Presentations and the following Java code:

try {
  final String basetPptxFile = [PATH] + "basePresentation.pptx";
  final String targetPptxFile = [PATH] + "targetWithGradient.pptx";
  final String masterName = "Main_Master"; //MasterSlide exists in both PPTX

  IPresentation targetPpt = new Presentation(targetPptxFile);

  // Save targetPpt to a byte[]. While redundant here, it mimics our flow.
  try (ByteArrayOutputStream pptStream = new ByteArrayOutputStream()) {
    targetPpt.save(pptStream, SaveFormat.Pptx);
  }

  // Find the MasterSlide in targetPpt that will be replaced
  IMasterSlide masterToReplace = null;
  for (IMasterSlide master : targetPpt.getMasters()) {
    if (masterName.equals(master.getName())) {
        masterToReplace = master;
        break;
    }
  }
  Objects.requireNonNull(masterToReplace, "MasterSlide in TARGET PPTX mssing");

  // Manipulate the targetPpt by replacing the MasterSlide

  // Find the MasterSlide from basePpt to clone onto targetPpt
  IPresentation basetPpt = new Presentation(basetPptxFile);
  IMasterSlide masterToClone = null;
  for (IMasterSlide master : basetPpt.getMasters()) {
    if (masterName.equals(master.getName())) {
        masterToClone = master;
        break;
    }
  }
  Objects.requireNonNull(masterToClone, "MasterSlide in BASE PPTX mssing");

  // Clone the masterToClone into the target PPTX
  IMasterSlide clonedMaster = targetPpt.getMasters().addClone(masterToClone);

  // Map the layouts in clonedMaster
  Map<String, ILayoutSlide> layoutsFromClone = new HashMap<>();
  for (ILayoutSlide layout : clonedMaster.getLayoutSlides()) {
    layoutsFromClone.put(layout.getName(), layout);
  }

  // Migrate all depending slides
  for (ILayoutSlide layout : masterToReplace.getLayoutSlides()) {
    if (layout.hasDependingSlides()) {
       ILayoutSlide layoutFromClone = layoutsFromClone.get(layout.getName());
       if (layoutFromClone == null) {
          // clone the layout onto clonedMaster
          layoutFromClone = clonedMaster.getLayoutSlides().addClone(layout);
          // add cloned layout to map
          layoutsFromClone.put(layout.getName(), layoutFromClone);
       }

       for (ISlide slide : layout.getDependingSlides()) {
          slide.setLayoutSlide(layoutFromClone);
       }

       // Confirm there are no longer any dependencies
       if (layout.hasDependingSlides()) {
          throw new IllegalStateException("Layout stil has dependencies");
       }
    }
  }

  // Confirm there are no longer any dependencies
  if (masterToReplace.hasDependingSlides()) {
    throw new IllegalStateException("MasterSlide stil has dependencies");
  }

  // Finally remove the masterToReplace - which is no longer unused.
  targetPpt.getMasters().remove(masterToReplace);

  // Try to save the targetPpt
  String newFile = targetPptxFile.replace(".pptx", "_merged.pptx");
  Files.deleteIfExists(Paths.get(newFile));

  // This is where the InvalidOperationException occurs
  targetPpt.save(newFile, SaveFormat.Pptx);
  System.out.println("Saved Merged Presentation: " + newFile);

} catch (Exception ex) {
  System.out.println("Unexpected exception: " + ex.getMessage());
  ex.printStackTrace();
}

Running the sample code should throw InvalidOperationException with the following stack:

class com.aspose.slides.exceptions.InvalidOperationException: Color is not resolved yet.
com.aspose.slides.fu.do(Unknown Source)
com.aspose.slides.GradientStopEffectiveData.do(Unknown Source)
com.aspose.slides.pu.int(Unknown Source)
com.aspose.slides.ms.do(Unknown Source)
com.aspose.slides.ms.do(Unknown Source)
com.aspose.slides.ms.do(Unknown Source)
com.aspose.slides.ms.do(Unknown Source)
com.aspose.slides.ms.<init>(Unknown Source)
com.aspose.slides.BaseSlide.do(Unknown Source)
com.aspose.slides.Slide.do(Unknown Source)
com.aspose.slides.Slide.do(Unknown Source)
com.aspose.slides.Slide.do(Unknown Source)
com.aspose.slides.af3.try(Unknown Source)
com.aspose.slides.af3.do(Unknown Source)
com.aspose.slides.Presentation.do(Unknown Source)
com.aspose.slides.Presentation.do(Unknown Source)
com.aspose.slides.Presentation.save(Unknown Source)

Additional Observations:

  • Gradient Background: As stated above, the culprit seems to be the MasterSlide with a gradient background. For example the exception does not occur if you replace the 3rd line of code with:
    final String targetPptxFile = [PATH] + "targetWithoutGradient.pptx";

  • Multiple Saves: The code above saves the targetPpt two times. If you comment out the lines that save targetPpt as a byte array - the exception does not occur. It seems that saving targetPpt as a byte array causes the Presentation instance to be in a weird state. Note that for us, it is necessary to generate the byte array.

  • Prior Slides Versions: Please also note that this seems to be a regressive issue that did not occur under prior Slides versions. For example, if you run the same code under Aspose Slides for Java version 19.7 - the exception does not occur.

  • Workaround: In lieu of removing the code that saves the Presentation to a byte array, we found a workaround that circumvents the exception. The exception does not occur if you reapply the background of the cloned MasterSlide before it’s saved for the second time. For example, adding the following code prior to calling targetPpt.save circumvents the exception:

    clonedMaster.getBackground().getFillFormat().setFillType(
       clonedMaster.getBackground().getEffective().getFillFormat().getFillType());
    

Environment Details:

  • Aspose Slides for Java 20.3
  • Java version 1.8.0_211
  • Windows 10 OS (but also reproducible under Linux).

File descriptions in the Presentations.zip (180.4 KB) attachment:

  • basePresentation.pptx: Presentation whose MasterSlide will be cloned.
  • targetWithGradient.pptx: Presentation whose MasterSlide will be replaced. The MasterSlide has a gradient background.
  • targetWithoutGradient.pptx: Presentation whose MasterSlide will be replaced. The MasterSlide does not have a gradient background.
  • targetWithoutGradient_merged.pptx: Presentation generated from our environment after running the code above, when using ‘targetWithoutGradient.pptx’ as the targetPpt

Thanks!

@oraspose,

I have worked with the issue shared by you and have created a ticket with ID SLIDESJAVA-38077 in our issue tracking system to further investigate and resolve the issue. This thread has been linked with the issue so that you may be notified once the issue will be fixed.

The issues you have found earlier (filed as SLIDESJAVA-38077) have been fixed in this update.