All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javax0.jamal.poi.word.MacroDocxInclude Maven / Gradle / Ivy

package javax0.jamal.poi.word;

import javax0.jamal.api.BadSyntax;
import javax0.jamal.api.Input;
import javax0.jamal.api.Macro;
import javax0.jamal.api.Processor;
import javax0.jamal.tools.FileTools;
import org.apache.poi.xwpf.usermodel.IBodyElement;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFTable;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class MacroDocxInclude implements Macro {


    private static class CallBack implements XWPFContext.DocxIntermediaryCallBack {
        private List paragraphs;
        private int paragraphStartIndex;
        private XWPFDocument document;
        private Consumer positionSetter;
        private final File file;

        CallBack(final File file) {
            this.file = file;
        }

        @Override
        public void setPosition(final Consumer positionSetter) {
            this.positionSetter = positionSetter;
        }

        @Override
        public void setDocument(XWPFDocument document) {
            this.document = document;
        }

        @Override
        public void setParagraphs(final List paragraphs) {
            this.paragraphs = paragraphs;
        }

        @Override
        public void setParagraphStartIndex(int paragraphStartIndex) {
            this.paragraphStartIndex = paragraphStartIndex;
        }

        @Override
        public void process() throws BadSyntax {
            try {
                final var tools = new DocxTools(document);
                final var includedDocument = new XWPFDocument(new FileInputStream(file));
                final var newPars = new ArrayList();
                for (int j = 0; j < paragraphStartIndex; j++) {
                    newPars.add(paragraphs.get(j));
                }
                boolean noTable = true;
                for (final var element : includedDocument.getBodyElements()) {
                    final var cursor = paragraphs.get(paragraphStartIndex).getCTP().newCursor();
                    if (element instanceof XWPFParagraph) {
                        final var paragraph = (XWPFParagraph) element;
                        final var p = document.insertNewParagraph(cursor);
                        if (noTable) {
                            newPars.add(p);
                        }
                        tools.copyParagraph(paragraph, p);
                    } else if (element instanceof XWPFTable) {
                        final var table = (XWPFTable) element;
                        final var t = document.insertNewTbl(cursor);
                        if (noTable) {
                            positionSetter.accept(t);
                            noTable = false;
                        }
                        tools.copyTable(table, t);
                    }
                }
                if (noTable) {
                    for (int j = paragraphStartIndex + 1; j < paragraphs.size(); j++) {
                        newPars.add(paragraphs.get(j));
                    }
                }
                paragraphs.clear();
                paragraphs.addAll(newPars);
            } catch (IOException e) {
                throw new BadSyntax(String.format("Cannot include the doc file '%s'", file.getAbsolutePath()), e);
            }
            DebugTool.debugDoc("doc:INCLUDE\n", document, paragraphs);
        }


    }

    /*
     *
     * tag::include[]
     * {%macro docx::include%}
     *
     * Using this macro you can include the formatted content of another docx file into the currently processed one.
     * The syntax of the macro is
     *
     * [source]
     * ----
     *   {@docx:include file_name}
     * ----
     * The file name can be absolute or relative to the processed file.
     * The macro will copy the content of the included file into the current file.
     * After that the copied parts will be processed by Jamal the same way as they had been in the document before.
     * This means that including a file from an already included file should use a file name relative to the top level document and not the included one.
     *
     * end::include[]
     */
    @Override
    public String evaluate(final Input in, final Processor processor) throws BadSyntax {
        final var position = in.getPosition();
        var reference = position.file;
        var fileName = FileTools.absolute(reference, in.toString().trim());
        final var context = XWPFContext.getXWPFContext(processor);
        context.register(new CallBack(new File(fileName)));
        return "";
    }

    @Override
    public String getId() {
        return "docx:include";
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy