Search code examples
javapdfapache-poidocx

How to change margins when converting with apache poi


I need to change the margin of the PDF file when I convert from Microsoft Word document.

public class TestCon {
    public static final String DEST = "./test.pdf";
    public static final String SRC = "./test.docx";

    public static void main(String[] args) {
        try {
            InputStream doc = new FileInputStream(new File(SRC));

            XWPFDocument document = new XWPFDocument(doc );
            CTSectPr addNewSectPr = document.getDocument().getBody().addNewSectPr();
            CTPageMar addNewPgMar = addNewSectPr.addNewPgMar();
            addNewPgMar.setLeft(BigInteger.valueOf(720L));
            addNewPgMar.setTop(BigInteger.valueOf(720L));
            addNewPgMar.setRight(BigInteger.valueOf(720L));
            addNewPgMar.setBottom(BigInteger.valueOf(720L));

            OutputStream out = new FileOutputStream(new File(DEST));
            PdfOptions options = PdfOptions.create();
            PdfConverter.getInstance().convert(document, out, options);
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
}

This does not work. The margin does not change in pdf


But when I do this:

        FileOutputStream out = new FileOutputStream(new File(SRC1));
        InputStream doc = new FileInputStream(new File(SRC));

        XWPFDocument document = new XWPFDocument(doc );
        CTSectPr addNewSectPr = document.getDocument().getBody().addNewSectPr();
        CTPageMar addNewPgMar = addNewSectPr.addNewPgMar();
        addNewPgMar.setLeft(BigInteger.valueOf(720L));
        addNewPgMar.setTop(BigInteger.valueOf(720L));
        addNewPgMar.setRight(BigInteger.valueOf(720L));
        addNewPgMar.setBottom(BigInteger.valueOf(720L));
        document.write(out);
        out.close();

Without converting to PDF, it works.


Solution

  • Solution:

    Adjust code part related to sectPr and pgMar to not add new sections, but reuse them:

    CTSectPr getSectPr = document.getDocument().getBody().getSectPr();
    getSectPr.unsetPgMar();
    CTPageMar addNewPgMar = getSectPr.addNewPgMar();
    addNewPgMar.setLeft(BigInteger.valueOf(720L));
    addNewPgMar.setTop(BigInteger.valueOf(720L));
    addNewPgMar.setRight(BigInteger.valueOf(720L));
    addNewPgMar.setBottom(BigInteger.valueOf(720L));
    // Also good to handle footer and header for more expectable result
    addNewPgMar.setFooter(BigInteger.valueOf(0L));
    addNewPgMar.setHeader(BigInteger.valueOf(0L));
    

    Explanation:

    Reason of the issue is that XDocReport converter (which is a separate project from Apache POI) handles only the first sectPr entry of the document.

    Your sample will generate WordprocessingML >> below:

    <w:sectPr w:rsidR="003F19CD" w:rsidRPr="005E1322">
      <w:pgSz w:h="16838" w:w="11906"/>
      <w:pgMar w:bottom="1134" w:footer="708" w:header="708" w:left="1701" w:right="850" w:top="1134"/>
      <w:cols w:space="708"/>
      <w:docGrid w:linePitch="360"/>
    </w:sectPr>
    <w:sectPr>
      <w:pgMar w:bottom="620" w:left="620" w:right="620" w:top="620"/>
    </w:sectPr>
    

    Which during conversion to PDF will be handled in the way second pgmar (<w:pgMar w:bottom="620" w:left="620" w:right="620" w:top="620"/>) will be ignored, since it is part of the second sectPr.

    Same time in case of saving adjusted document to a new Word document pgMars will be merged and you will see needed result (adjusted margin), new WordprocessingML will look so:

    <w:sectPr w:rsidR="003F19CD" w:rsidRPr="005E1322">
      <w:pgSz w:h="16838" w:w="11906"/>
      <w:pgMar w:left="620" w:top="620" w:right="620" w:bottom="620" w:footer="0" w:header="0"/>
      <w:cols w:space="708"/>
      <w:docGrid w:linePitch="360"/>
    </w:sectPr>
    <w:sectPr>
      <w:pgMar w:bottom="620" w:left="620" w:right="620" w:top="620"/>
    </w:sectPr>
    

    Code sample from the Solution section will generate single sectPr with single pgMar so PDFConverter will work as needed.


    Additional information:

    Also need to mention that XDocReport provides configuration possibility >>:

    options.setConfiguration(new IPdfWriterConfiguration() {
        public void configure(PdfWriter writer) {
            writer.setPDFXConformance(PdfWriter.PDFA1A);
        }
    });
    

    But unfortunately it is not possible to handle margins this way (also margin values from the docx document will overwrite them after configuration will be done anyway).


    Additionally below is pom.xml dependencies used:

    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>3.15</version>
    </dependency>
    
    <dependency>
        <groupId>fr.opensagres.xdocreport</groupId>
        <artifactId>fr.opensagres.poi.xwpf.converter.pdf</artifactId>
        <version>2.0.1</version>
    </dependency>