https://bugs.documentfoundation.org/show_bug.cgi?id=167290

            Bug ID: 167290
           Summary: Since version 24 of LibreOffice, the PDF/A-1 export is
                    invalid with java API
           Product: LibreOffice
           Version: 24.2.4.2 release
          Hardware: x86-64 (AMD64)
                OS: Windows (All)
            Status: UNCONFIRMED
          Severity: normal
          Priority: medium
         Component: Writer
          Assignee: [email protected]
          Reporter: [email protected]

Description:
A code that worked in previous versions no longer works in LibreOffice 24 and
25. The method that checks the PDF/A-1 format returns the following errors:

Le fichier n'est pas valide PDF/A-1b voici les 1 erreur(s) :
7.2 : Error on MetaData, CreationDate present in the document catalog
dictionary doesn't match with XMP information

Steps to Reproduce:
1.To generate pdf file from odt file

StringBuffer _sbUrl = new StringBuffer( fileDest.startsWith( "file:///" ) ? ""
: "file:///" );

_sbUrl.append( fileDest );

PropertyValue[] mediaDescriptorProperties = new PropertyValue[4];
PropertyValue[] filtersProperties = new PropertyValue[4];

// Utiliser la norme A1
filtersProperties[0] = new PropertyValue();
filtersProperties[0].Name = "SelectPdfVersion";
filtersProperties[0].Value = 1;

filtersProperties[1] = new PropertyValue();
filtersProperties[1].Name = "UseTaggedPDF";
filtersProperties[1].Value = Boolean.TRUE;

filtersProperties[2] = new PropertyValue();
filtersProperties[2].Name = "UseLosslessCompression";
filtersProperties[2].Value = Boolean.TRUE;

filtersProperties[3] = new PropertyValue();
filtersProperties[3].Name = "Quality";
filtersProperties[3].Value = 100;

PropertyValue[] filterData = null;
if (pages != null && pages.length() > 0 && !"All".equals( pages )) {
        filterData = new PropertyValue[1];
        filterData[0] = new PropertyValue();
        filterData[0].Name = "PageRange";
        filterData[0].Value = pages;
}

mediaDescriptorProperties[0] = new PropertyValue();
mediaDescriptorProperties[0].Name = "FilterName";
mediaDescriptorProperties[0].Value = "writer_pdf_Export";

mediaDescriptorProperties[1] = new PropertyValue();
mediaDescriptorProperties[1].Name = "FilterData";
mediaDescriptorProperties[1].Value = filtersProperties;

mediaDescriptorProperties[3] = new PropertyValue();
mediaDescriptorProperties[3].Name = "Overwrite";
mediaDescriptorProperties[3].Value = new Boolean( true );

if (filterData != null) {
        mediaDescriptorProperties[2] = new PropertyValue();
        mediaDescriptorProperties[2].Name = "FilterData";
        mediaDescriptorProperties[2].Value = filterData;
} else {
        mediaDescriptorProperties[2] = new PropertyValue();
        mediaDescriptorProperties[2].Name = "PageRange";
        mediaDescriptorProperties[2].Value = pages;
}

String adresseFichier = _sbUrl.toString().replace( ".odt", ".pdf" ).replace(
".rtf", ".pdf" );

try {
           XStorable xstorable = (XStorable) UnoRuntime.queryInterface(
XStorable.class, _xcomponent );
           xstorable.storeToURL( adresseFichier, mediaDescriptorProperties );//
On enregistre le pdf
     }catch(Exception e){
         e.printStackTrace();
     }


*******************************************************************************************************************
2.To control the pdf a1 validity
package isc.template.pdf;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentInformation;
import org.apache.pdfbox.pdmodel.common.PDMetadata;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.graphics.color.PDOutputIntent;
import org.apache.pdfbox.preflight.Format;
import org.apache.pdfbox.preflight.PreflightDocument;
import org.apache.pdfbox.preflight.ValidationResult;
import org.apache.pdfbox.preflight.ValidationResult.ValidationError;
import org.apache.pdfbox.preflight.exception.SyntaxValidationException;
import org.apache.pdfbox.preflight.parser.PreflightParser;
import org.apache.xmpbox.XMPMetadata;
import org.apache.xmpbox.schema.DublinCoreSchema;
import org.apache.xmpbox.schema.PDFAIdentificationSchema;
import org.apache.xmpbox.schema.XMPBasicSchema;
import org.apache.xmpbox.type.BadFieldValueException;
import org.apache.xmpbox.xml.XmpSerializer;

import isc.appli.template.ooo.OooWriter;
import isc.appli.util.AppliInitializer;
import isc.appli.util.AppliToolkit;
import isc.appli.web.spring.ServeurSpringConnector;
import isc.util.text.TextUtil;

public class PdfA1 {
        public static boolean controleValidite( String fileName ) {

                ValidationResult result = null;

                try {
                        PreflightParser parser = new PreflightParser( fileName
);

                        /*
                         * Parse the PDF file with PreflightParser that
inherits from the NonSequentialParser. Some additional controls are present to
check a set of PDF/A requirements. (Stream length
                         * consistency, EOL after some Keyword...)
                         */
                        PDDocument pd = parser.parse( Format.PDF_A1A );

                        /*
                         * Once the syntax validation is done, the parser can
provide a PreflightDocument (that inherits from PDDocument) This document
process the end of PDF/A validation.
                         */
                        PreflightDocument document = (PreflightDocument) pd;

                        // Get validation result
                        result = document.validate();

                        document.close();

                } catch (SyntaxValidationException e) {
                        /*
                         * the parse method can throw a
SyntaxValidationException if the PDF file can't be parsed. In this case, the
exception contains an instance of ValidationResult
                         */
                        result = e.getResult();
                } catch (Exception e) {
                        /*
                         * the parse method can throw a
SyntaxValidationException if the PDF file can't be parsed. In this case, the
exception contains an instance of ValidationResult
                         */
                        e.printStackTrace();
                        return false;
                }

                AppliToolkit.afficher(
"¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤"
);

                // display validation result
                if (result.isValid()) {
                        AppliToolkit.afficher( "Le fichier " + fileName + " est
valide PDF/A-1b" );
                } else {

                        System.out.println(
"***************************************************" );

                        AppliToolkit
                                        .afficher( "Le fichier " + fileName + "
n'est pas valide PDF/A-1b voici les " + result.getErrorsList().size() + "
erreur(s) :" );

                        for (ValidationError error : result.getErrorsList()) {
                                AppliToolkit.afficher( error.getErrorCode() + "
: " + error.getDetails() );
                        }
                        System.out.println(
"***************************************************" );
                }

                AppliToolkit.afficher(
"¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤"
);

                return result.isValid();
        }

        private static void createPDF( String file ) {
                try {
                        // Creating PDF document object

                        PDDocument document = Loader.loadPDF( new File(
"c:\\fichiers_travail\\testa1.pdf" ) );

                        isc.util.log.LogUtil.getLogger().info( "NOMBRE de
pages: " + document.getNumberOfPages() );

                        // Color profile
                        // Create output intent
                        InputStream colorProfile =
PdfA1.class.getResourceAsStream( "/pdfbox/pdfa/sRGB.icc" );
                        PDOutputIntent intent = new PDOutputIntent( document,
colorProfile );
                        intent.setInfo( "sRGB IEC61966-2.1" );
                        intent.setOutputCondition( "sRGB IEC61966-2.1" );
                        intent.setOutputConditionIdentifier( "sRGB
IEC61966-2.1" );
                        intent.setRegistryName( "http://www.color.org"; );
                        document.getDocumentCatalog().addOutputIntent( intent
);

                        // load the font as this needs to be embedded
                        InputStream arialFont =
PdfA1.class.getResourceAsStream( "/pdfbox/ttf/arial.ttf" );
                        PDFont font = PDType0Font.load( document, arialFont );

                        // A PDF/A file needs to have the font embedded if the
font is used for text rendering
                        // in rendering modes other than text rendering mode 3.
                        //
                        // This requirement includes the PDF standard fonts, so
don't use their static PDFType1Font classes such as
                        // PDFType1Font.HELVETICA.
                        //
                        // As there are many different font licenses it is up
to the developer to check if the license terms for the
                        // font loaded allows embedding in the PDF.
                        //
                        if (!font.isEmbedded()) {
                                throw new IllegalStateException( "PDF/A
compliance requires that all fonts used for"
                                                + " text rendering in rendering
modes other than rendering mode 3 are embedded." );
                        }

                        // add XMP metadata
                        XMPMetadata xmp = XMPMetadata.createXMPMetadata();

                        try {
                                DublinCoreSchema dc =
xmp.createAndAddDublinCoreSchema();
                                dc.setTitle( file );
                                // Format A1

                                PDFAIdentificationSchema id =
xmp.createAndAddPDFAIdentificationSchema();
                                id.setPart( 1 );
                                id.setConformance( "B" );
                                // Creation date doit apparaitre si elle est
dans les infos du doc, idem pour le producer
                                XMPBasicSchema basicSchema =
xmp.createAndAddXMPBasicSchema();
                                basicSchema.setCreateDate(
document.getDocumentInformation().getCreationDate() );

                                // Le producer gène, soit on le place sur un
schéma soit on le retire
                                document.getDocumentInformation().setProducer(
null );

                                XmpSerializer serializer = new XmpSerializer();
                                ByteArrayOutputStream baos = new
ByteArrayOutputStream();
                                serializer.serialize( xmp, baos, true );

                                PDMetadata metadata = new PDMetadata( document
);
                                metadata.importXMPMetadata( baos.toByteArray()
);
                                document.getDocumentCatalog().setMetadata(
metadata );
                        } catch (BadFieldValueException e) {
                                // won't happen here, as the provided value is
valid
                                throw new IllegalArgumentException( e );
                        }

                        // Saving the document
                        document.save( file );
                        System.out.println( "PDF created" );
                        // Closing the document
                        document.close();
                } catch (Exception e) {
                        e.printStackTrace();
                }
        }

        private static String getODTAsPDFA1( String file ) {
                OooWriter ooo = new OooWriter();

                ooo.open( "file:///" + file, false, true, false, true, false );
                return getWriterAsPDFA1( ooo, file );
        }

        public static String getWriterAsPDFA1( OooWriter ooo, String file ) {
                try {
                        String fileDest = TextUtil.replace( TextUtil.replace(
TextUtil.replace( file, ".odt", ".pdf" ), "ODT", "pdf" ), "\\", "/" );
                        ooo.saveAsPdf( fileDest, null );
                        ooo.close();
                        boolean valide = PdfA1.controleValidite( fileDest );
                        if (!valide) {
                                return null;
                        }
                        isc.util.log.LogUtil.getLogger().info( "transformation
en pdf " );
                        isc.util.log.LogUtil.getLogger().info( "source " + file
);
                        isc.util.log.LogUtil.getLogger().info( "dest " +
fileDest );
                        return fileDest;
                } catch (Exception e) {
                        isc.util.log.LogUtil.getLogger().error( e.getMessage(),
e );
                        return null;
                }

        }

        /**
         * Obtenir un PDF A1 depuis un PDF: utilise le serveur de webservice
qui utilise ghostscript en ligne de commande
         * 
         * @param file
         * @return
         */
        private static String getPDFAsPDFA1( String file ) {

                if (controleValidite( file )) {
                        return file;
                }

                String fichierConverti =
ServeurSpringConnector.pdf2pdfA1Service( file );// Creating PDF document object

                if (controleValidite( fichierConverti )) {
                        return fichierConverti;
                }

                return null;
        }

        /**
         * Méthode pour ODT et PDF qui renvoie dans tous les cas un PDFA1
         * 
         * @param file
         * @return
         */
        public static String getFileAsPDFA1( String file ) {
                if (file.toUpperCase().endsWith( ".PDF" )) {
                        return getPDFAsPDFA1( file );
                } else {
                        return getODTAsPDFA1( file );
                }
        }

        /**
         * @param file
         *            Le nom du fichier d'origine qui n'est pas PDF/A1 :
courrier_genere_timestamp.pdf
         * @return Le nom du fichier qui est PDF/A1 et avec une date création
ok : courrier_genere_timestamp_corrected.pdf
         */
        public static String correctCreationDateGetPDFA1( String file ) {

                String adresseFichierPdf = excludeFilePrefixGetPath( file );
                String destFile = TextUtil.replace( adresseFichierPdf, ".pdf",
"_corrected.pdf" );

                Calendar creationDate = null;
                try (FileInputStream fis = new FileInputStream(
adresseFichierPdf ); BufferedReader br = new BufferedReader( new
InputStreamReader( fis ) )) {

                        String ligne;
                        while ((creationDate == null) && (ligne =
br.readLine()) != null) {
                                String buffer = ligne;

                                if (buffer.trim().startsWith(
"<xmp:CreateDate>" )) {
                                        String xmpCreateDate =
buffer.substring( buffer.indexOf( "<xmp:CreateDate>" ) +
"<xmp:CreateDate>".length(),
                                                        buffer.indexOf(
"</xmp:CreateDate>" ) );
                                        System.out.println( "xmp create date
trouvée : " + xmpCreateDate );

                                        xmpCreateDate = xmpCreateDate.replace(
"-", "" ).replace( "T", "" ).replace( ":", "" ).replace( "+0200", "" ).replace(
"+0100",
                                                        "" );
                                        System.out.println( "xmp create date
après replace : " + xmpCreateDate );

                                        Date d = new SimpleDateFormat(
"yyyyMMddHHmmss" ).parse( xmpCreateDate );
                                        creationDate = Calendar.getInstance();
                                        creationDate.setTimeInMillis(
d.getTime() );
                                }

                        }
                } catch (FileNotFoundException e) {
                        isc.util.log.LogUtil.getLogger().error( e.getMessage(),
e );
                } catch (IOException e) {
                        isc.util.log.LogUtil.getLogger().error( e.getMessage(),
e );
                } catch (ParseException e) {
                        isc.util.log.LogUtil.getLogger().error( e.getMessage(),
e );
                }
                if (creationDate != null) {

                        try {

                                PreflightParser parser = new PreflightParser(
adresseFichierPdf );

                                /*
                                 * Parse the PDF file with PreflightParser that
inherits from the NonSequentialParser. Some additional controls are present to
check a set of PDF/A requirements. (Stream length
                                 * consistency, EOL after some Keyword...)
                                 */
                                PDDocument pd = parser.parse( Format.PDF_A1A );

                                /*
                                 * Once the syntax validation is done, the
parser can provide a PreflightDocument (that inherits from PDDocument) This
document process the end of PDF/A validation.
                                 */
                                PreflightDocument document =
(PreflightDocument) pd;

                                PDDocumentInformation info =
document.getDocumentInformation();
                                info.setCreationDate( creationDate );
                                document.setDocumentInformation( info );
                                document.save( destFile );
                                return destFile;
                        } catch (Exception e) {
                                isc.util.log.LogUtil.getLogger().error(
e.getMessage(), e );
                        }
                }
                return null;
        }

        public static String excludeFilePrefixGetPath( String file ) {
                return TextUtil.replace( TextUtil.replace( TextUtil.replace(
file, "file:///", "" ), "file://", "" ), "file:/", "" );
        }

        // PdfA1.java
        public static void main( String[] args ) {
                AppliInitializer.initializeMyApplication();
                controleValidite( "C:/temp/courrier_genere_1729858583829_2.pdf"
);
                System.exit( 0 );
        }
}





Actual Results:
Le fichier toto.pdf n'est pas valide PDF/A-1b voici les 1 erreur(s) :
7.2 : Error on MetaData, CreationDate present in the document catalog
dictionary doesn't match with XMP information

Expected Results:
Le fichier toto.pdf est valide PDF/A-1b


Reproducible: Always


User Profile Reset: No

Additional Info:
Does not work with the latest version either (Libre office 25.2.4)

-- 
You are receiving this mail because:
You are the assignee for the bug.

Reply via email to