PowerpointStamper.java

package pro.verron.officestamper.experimental;

import org.docx4j.dml.CTBlip;
import org.docx4j.dml.CTBlip;
import org.docx4j.dml.CTBlipFillProperties;
import org.docx4j.dml.CTRegularTextRun;
import org.docx4j.dml.CTShapeProperties;
import org.docx4j.dml.CTTextParagraph;
import org.docx4j.openpackaging.packages.PresentationMLPackage;
import org.docx4j.openpackaging.parts.PresentationML.SlidePart;
import org.pptx4j.Pptx4jException;
import org.pptx4j.pml.Shape;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import pro.verron.officestamper.api.Insert;
import pro.verron.officestamper.api.OfficeStamper;
import pro.verron.officestamper.api.OfficeStamperException;
import pro.verron.officestamper.preset.Image;
import pro.verron.officestamper.utils.openpackaging.OpenPackage;

import java.util.ArrayList;
import java.util.List;

/// The PowerpointStamper class implements the OfficeStamper interface to provide capability for stamping PowerPoint
/// presentations with context and writing the result to an OutputStream.
public class PowerpointStamper
        implements OfficeStamper<PresentationMLPackage> {
    /// Constructs a new instance of the PowerpointStamper class. This constructor initializes an instance of
    /// PowerpointStamper, which implements the OfficeStamper interface. The class provides functionality to apply
    /// variable-based stamping on PowerPoint templates and outputs the modified presentation.
    public PowerpointStamper() {
        // Explicit default constructor for Javadoc
    }

    @Override
    public PresentationMLPackage stamp(PresentationMLPackage template, Object context)
            throws OfficeStamperException {
        try {
            List<SlidePart> slideParts = template.getMainPresentationPart()
                                                 .getSlideParts();
            for (SlidePart slide : slideParts) {
                List<Shape> shapes = PowerpointCollector.collect(slide, Shape.class);
                for (Shape shape : shapes) {
                    processShape(slide, shape, context);
                }
            }
            return template;
        } catch (Pptx4jException e) {
            throw new OfficeStamperException(e);
        }
    }

    private void processShape(SlidePart slide, Shape shape, Object context) {
        if (shape.getTxBody() == null) return;
        List<CTTextParagraph> paragraphs = new ArrayList<>(shape.getTxBody()
                                                                .getP());
        for (CTTextParagraph paragraph : paragraphs) {
            PowerpointParagraph paragraph1 = new PowerpointParagraph(new PptxPart((PresentationMLPackage) slide.getPackage()),
                    paragraph);
            String string = paragraph1.asString();
            for (var variable : Placeholders.findVariables(string)) {
                var evaluationContext = new StandardEvaluationContext(context);
                var parserConfiguration = new SpelParserConfiguration();
                var parser = new SpelExpressionParser(parserConfiguration);
                var expression = parser.parseExpression(variable.content());
                var value = expression.getValue(evaluationContext);

                if (value instanceof Image image) {
                    fillShapeWithImage(slide, shape, image);
                    return;
                }
                else {
                    var replacement = new CTRegularTextRun();
                    replacement.setT(String.valueOf(value));
                    var expression1 = variable.expression();
                    paragraph1.replace(expression1, new Insert(replacement));
                }
            }
        }
    }

    private void fillShapeWithImage(SlidePart slide, Shape shape, Image image) {
        PresentationMLPackage presentationMLPackage = (PresentationMLPackage) slide.getPackage();
        var openPackage = OpenPackage.getOrCreate(presentationMLPackage, slide);
        var imgPart = openPackage.findOrCreateImgPart(image::getBytes, true);
        var relId = imgPart.relationship()
                           .getId();

        var factory = new org.docx4j.dml.ObjectFactory();
        var blipFill = factory.createCTBlipFillProperties();
        var blip = factory.createCTBlip();
        blip.setEmbed(relId);
        blipFill.setBlip(blip);

        var stretch = factory.createCTStretchInfoProperties();
        stretch.setFillRect(factory.createCTRelativeRect());
        blipFill.setStretch(stretch);

        if (shape.getSpPr() == null) shape.setSpPr(factory.createCTShapeProperties());
        shape.getSpPr()
             .setBlipFill(blipFill);

        shape.setTxBody(null);
    }
}