opensagres / xdocreport

XDocReport means XML Document reporting. It's Java API to merge XML document created with MS Office (docx) or OpenOffice (odt), LibreOffice (odt) with a Java model to generate report and convert it if you need to another format (PDF, XHTML...).
https://github.com/opensagres/xdocreport
1.23k stars 374 forks source link

ODT -> PDF converter : exception when first footer is different #400

Open Dimitriio opened 4 years ago

Dimitriio commented 4 years ago

Hello, An exception is thrown if an ODT document's footer (i guess its the same for header) is selected to be different on the first page.

fr.opensagres.xdocreport.converter.XDocConverterException: fr.opensagres.odfdom.converter.core.ODFConverterException: java.lang.NullPointerException
    at fr.opensagres.xdocreport.converter.odt.odfdom.itext.ODF2PDFViaITextConverter.convert(ODF2PDFViaITextConverter.java:66)
    at fr.opensagres.xdocreport.document.AbstractXDocReport.convert(AbstractXDocReport.java:710)
Caused by: fr.opensagres.odfdom.converter.core.ODFConverterException: java.lang.NullPointerException
    at fr.opensagres.odfdom.converter.pdf.PdfConverter.doConvert(PdfConverter.java:94)
    at fr.opensagres.odfdom.converter.pdf.PdfConverter.doConvert(PdfConverter.java:44)
    at fr.opensagres.odfdom.converter.core.AbstractODFConverter.convert(AbstractODFConverter.java:42)
    at fr.opensagres.xdocreport.converter.odt.odfdom.itext.ODF2PDFViaITextConverter.convert(ODF2PDFViaITextConverter.java:62)
    ... 104 common frames omitted
Caused by: java.lang.NullPointerException: null
    at fr.opensagres.odfdom.converter.pdf.internal.ElementVisitorForIText.addITextContainer(ElementVisitorForIText.java:843)
    at fr.opensagres.odfdom.converter.pdf.internal.ElementVisitorForIText.addITextContainer(ElementVisitorForIText.java:831)
    at fr.opensagres.odfdom.converter.pdf.internal.ElementVisitorForIText.processParagraphOrHeading(ElementVisitorForIText.java:387)
    at fr.opensagres.odfdom.converter.pdf.internal.ElementVisitorForIText.visit(ElementVisitorForIText.java:325)
    at org.odftoolkit.odfdom.dom.element.text.TextPElement.accept(TextPElement.java:1681)
    at fr.opensagres.odfdom.converter.core.ElementVisitorConverter.visit(ElementVisitorConverter.java:83)
    at org.odftoolkit.odfdom.pkg.OdfElement.accept(OdfElement.java:513)
    at fr.opensagres.odfdom.converter.core.ElementVisitorConverter.visit(ElementVisitorConverter.java:83)
    at org.odftoolkit.odfdom.dom.DefaultElementVisitor.visit(DefaultElementVisitor.java:2965)
    at fr.opensagres.odfdom.converter.pdf.internal.ElementVisitorForIText.visit(ElementVisitorForIText.java:192)
    at org.odftoolkit.odfdom.dom.element.style.StyleMasterPageElement.accept(StyleMasterPageElement.java:707)
    at fr.opensagres.odfdom.converter.core.ElementVisitorConverter.visit(ElementVisitorConverter.java:83)
    at org.odftoolkit.odfdom.dom.DefaultElementVisitor.visit(DefaultElementVisitor.java:2541)
    at org.odftoolkit.odfdom.dom.element.office.OfficeMasterStylesElement.accept(OfficeMasterStylesElement.java:109)
    at fr.opensagres.odfdom.converter.pdf.PdfConverter.processBody(PdfConverter.java:134)
    at fr.opensagres.odfdom.converter.pdf.PdfConverter.doConvert(PdfConverter.java:66)
    ... 107 common frames omitted

I am using OpenJDK 11.0.5, xdocreport : 2.0.2 and LibreOffice 6.2.3.2 (x64). I convert to PDF using this method i made :

    public byte[] convertDocumentToPdf(byte[] document) {
        IXDocReport xdocGenerator;
        try (final ByteArrayInputStream sourceStream = new ByteArrayInputStream(
                document); ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            xdocGenerator = XDocReportRegistry.getRegistry().loadReport(sourceStream, TemplateEngineKind.Velocity);
            Options options = Options.getFrom(DocumentKind.ODT).to(ConverterTypeTo.PDF);
            IContext context = xdocGenerator.createContext();

            //Merge Java model with the ODT and convert it to PDF
            xdocGenerator.convert(context, options, outputStream);
            return outputStream.toByteArray();
        } catch (IOException | XDocReportException e) {
            LOGGER.error("Exception",e);
        }
        return null;
    }

I wanted put sample files, but its not supported on github. So to reproduce it, you can use a simple blank ODT with 2 pages and specific footer on first page and different one on second.

In fr.opensagres.odfdom.converter.pdf.internal.ElementVisitorForIText : this method seems be the problem as oldContainer is null (and throwing the exception when calling addElement)

private void addITextContainer(OdfElement ele, IStylableContainer newContainer, boolean add) {
        IStylableContainer oldContainer = this.currentContainer;

        try {
            this.currentContainer = newContainer;
            super.visit(ele);
            if (add) {
                oldContainer.addElement(newContainer.getElement());
            }
        } finally {
            this.currentContainer = oldContainer;
        }

    }

Thanks in advance

Dimitriio commented 4 years ago

I searched some more and i found out that the first page footer correspond to this tag and is define in xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0". Its not part of the implementation of OpenDocument v1.2 so i think is the odfdom-java lib can't load it correctly when converting as they only can scan and manipulate Open Document Format.

Convert method in fr.opensagres.xdocreport.converter.odt.odfdom.itext.ODF2PDFViaITextConverter :

 public void convert( InputStream in, OutputStream out, Options options )
        throws XDocConverterException
    {
        try
        {
            OdfTextDocument odfDocument = OdfTextDocument.loadDocument( in );
            PdfConverter.getInstance().convert( odfDocument, out, toPdfOptions( options ) );
        }
        catch ( ODFConverterException e )
        {
            throw new XDocConverterException( e );
        }
        catch ( IOException e )
        {
            throw new XDocConverterException( e );
        }
        catch ( Exception e )
        {
            throw new XDocConverterException( e );
        }
    }

PS : sorry wrongly closed

Dimitriio commented 4 years ago

I found a way around to solve my problem. In this Apache OO writer pdf page 14-15, it explains how to deal with different header/footer content using different page style and page break.