Pdf-lib: [Feature Request]: Generate PDF/A-3 documents

Created on 1 Nov 2019  路  5Comments  路  Source: Hopding/pdf-lib

This is the second half of my feature requests, see: https://github.com/Hopding/pdf-lib/issues/229.

It would be great if pdf-lib could generate PDF/A-3 documents, which in my case are required by the ZUGFeRD specification.

feature-request

Most helpful comment

Hi! First of all thank you so much @Hopding for this awesome library!

I'm not the original poster of this issue, but I'm also interested in generating PDF/A documents. So, I tried to generate a valid PDF/A-3B document with the help of veraPDF (a PDF/A conformance checker).
Apparently, it's not enough to leave away certain PDF features. Some things need to be added.

Here are my findings:

  • Rule: The catalog dictionary shall contain the Metadata key with a metadata stream as value
  • Rule: The PDF/A version and conformance level shall be specified in XMP metadata

    • For this, I added the following lines just before the closing </rdf:RDF> in the XMP metadata added above:

      <rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/"> <pdfaid:part>3</pdfaid:part> <pdfaid:conformance>B</pdfaid:conformance> </rdf:Description>

  • Rule: The file trailer dictionary shall contain the ID keyword

    • I used the following code to add file identifiers (see also PDF 1.7 specification chapter 14.4)

      const encoder = new TextEncoder(); const data = ... // Use document information and current time to make the identifier unique const hash = await crypto.subtle.digest('SHA-512', data); const hashArray = Array.from(new Uint8Array(hash)); const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join(''); const permanent = PDFHexString.of(hashHex); const changing = permanent; doc.context.trailerInfo.ID = doc.context.obj([permanent, changing]);

  • Rule: DeviceRGB shall only be used (...) if the file has a PDF/A OutputIntent that contains an RGB destination profile

    • I was able to add the ICC v2 sRGB color profile (file sRGB2014.icc from http://www.color.org/srgbprofiles.xalter#v2) as follows:

      ```

      public static setColorProfile(doc: PDFDocument) {

      const profile = ...; // Content of file sRGB2014.icc

      const profileStream = doc.context.stream(profile, {

      Length: profile.length,

      });

      const profileStreamRef = doc.context.register(profileStream);

    const outputIntent = doc.context.obj({
      Type: 'OutputIntent',
      S: 'GTS_PDFA1',
      OutputConditionIdentifier: PDFString.of('sRGB'),
      DestOutputProfile: profileStreamRef,
    });
    const outputIntentRef = doc.context.register(outputIntent);
    doc.catalog.set(PDFName.of('OutputIntents'), doc.context.obj([outputIntentRef]));
  }
```

  • In my document, I added a link annotation as explained in https://github.com/Hopding/pdf-lib/issues/161#issuecomment-523186749

    • This gave me validation errors for this rule and that rule

    • I had to add F: 4, (setting the Print flag) to the annotation dictionary.

  • In my document, I am embedding a custom TrueType font (Liberation Sans) which leads to the following validation error:

    • Rule: All embedded Type 2 CIDFonts in the CIDFont dictionary shall contain a CIDToGIDMap entry that shall be a stream mapping from CIDs to glyph indices or the name Identity

    • This is the only validation error that I was not able to resolve by myself (yet).

    • Maybe such a CIDToGIDMap could be added in CustomFontEmbedder.ts somehow?

(Disclaimer: I'm not sure if my code snippets are clean & correct. At least, they removed (almost all) the veraPDF validation errors for my specific use case. There might be further issues for other PDF features. @Hopding feel free to edit the code snippets if you see need.)

Edit: Oh, and Happy New Year!

All 5 comments

Hello @zimt28! This is an interesting idea. What exactly do you envision this feature looking like?

As far as I can tell, the PDF/A standard is just a subset of the PDF standard. Since pdf-lib adheres to the PDF 1.7 spec, I would think that all you need to do to generate a PDF/A document with pdf-lib is simply not use the PDF features that are disallowed in PDF/A. Or put another way, generating a PDF/A document with pdf-lib shouldn't require any new features. It just means not using particular tools offered by pdf-lib.

Hi! First of all thank you so much @Hopding for this awesome library!

I'm not the original poster of this issue, but I'm also interested in generating PDF/A documents. So, I tried to generate a valid PDF/A-3B document with the help of veraPDF (a PDF/A conformance checker).
Apparently, it's not enough to leave away certain PDF features. Some things need to be added.

Here are my findings:

  • Rule: The catalog dictionary shall contain the Metadata key with a metadata stream as value
  • Rule: The PDF/A version and conformance level shall be specified in XMP metadata

    • For this, I added the following lines just before the closing </rdf:RDF> in the XMP metadata added above:

      <rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/"> <pdfaid:part>3</pdfaid:part> <pdfaid:conformance>B</pdfaid:conformance> </rdf:Description>

  • Rule: The file trailer dictionary shall contain the ID keyword

    • I used the following code to add file identifiers (see also PDF 1.7 specification chapter 14.4)

      const encoder = new TextEncoder(); const data = ... // Use document information and current time to make the identifier unique const hash = await crypto.subtle.digest('SHA-512', data); const hashArray = Array.from(new Uint8Array(hash)); const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join(''); const permanent = PDFHexString.of(hashHex); const changing = permanent; doc.context.trailerInfo.ID = doc.context.obj([permanent, changing]);

  • Rule: DeviceRGB shall only be used (...) if the file has a PDF/A OutputIntent that contains an RGB destination profile

    • I was able to add the ICC v2 sRGB color profile (file sRGB2014.icc from http://www.color.org/srgbprofiles.xalter#v2) as follows:

      ```

      public static setColorProfile(doc: PDFDocument) {

      const profile = ...; // Content of file sRGB2014.icc

      const profileStream = doc.context.stream(profile, {

      Length: profile.length,

      });

      const profileStreamRef = doc.context.register(profileStream);

    const outputIntent = doc.context.obj({
      Type: 'OutputIntent',
      S: 'GTS_PDFA1',
      OutputConditionIdentifier: PDFString.of('sRGB'),
      DestOutputProfile: profileStreamRef,
    });
    const outputIntentRef = doc.context.register(outputIntent);
    doc.catalog.set(PDFName.of('OutputIntents'), doc.context.obj([outputIntentRef]));
  }
```

  • In my document, I added a link annotation as explained in https://github.com/Hopding/pdf-lib/issues/161#issuecomment-523186749

    • This gave me validation errors for this rule and that rule

    • I had to add F: 4, (setting the Print flag) to the annotation dictionary.

  • In my document, I am embedding a custom TrueType font (Liberation Sans) which leads to the following validation error:

    • Rule: All embedded Type 2 CIDFonts in the CIDFont dictionary shall contain a CIDToGIDMap entry that shall be a stream mapping from CIDs to glyph indices or the name Identity

    • This is the only validation error that I was not able to resolve by myself (yet).

    • Maybe such a CIDToGIDMap could be added in CustomFontEmbedder.ts somehow?

(Disclaimer: I'm not sure if my code snippets are clean & correct. At least, they removed (almost all) the veraPDF validation errors for my specific use case. There might be further issues for other PDF features. @Hopding feel free to edit the code snippets if you see need.)

Edit: Oh, and Happy New Year!

this would be great as there are not other pure js libs doing this.

Just came accross this issue because I am working on adding factur-x conformance, which needs PDF-A3 conformance.
@13thirteen Your information was very helpful. Regarding your last point:
I added the following to the file "CustomFontEmbedder.ts" and achieved full PDFA-3 compliance:
In the function embedCIDFontDict I changed
const cidFontDict = context.obj({
Type: 'Font',
Subtype: this.isCFF() ? 'CIDFontType0' : 'CIDFontType2',
BaseFont: this.baseFontName,
...
to:

const cidFontDict = context.obj({
Type: 'Font',
Subtype: this.isCFF() ? 'CIDFontType0' : 'CIDFontType2',
CIDToGIDMap: 'Identity',
BaseFont: this.baseFontName,
...

Similarly, I would like to be able to choose the specification version (1.4, ..., 1.7) of the PDF document produced by pdf-lib. This would make it possible to start from an 1.7 document and remove from it features that are too recent in order to keep only the compatible part.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

emilsedgh picture emilsedgh  路  5Comments

DanielJackson-Oslo picture DanielJackson-Oslo  路  8Comments

nachum37 picture nachum37  路  3Comments

ihmc3jn09hk picture ihmc3jn09hk  路  4Comments

zimt28 picture zimt28  路  5Comments