Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature Request]: Generate PDF/A-3 documents #230

Closed
zimt28 opened this issue Nov 1, 2019 · 11 comments
Closed

[Feature Request]: Generate PDF/A-3 documents #230

zimt28 opened this issue Nov 1, 2019 · 11 comments

Comments

@zimt28
Copy link

zimt28 commented Nov 1, 2019

This is the second half of my feature requests, see: #229.

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

@Hopding
Copy link
Owner

Hopding commented Dec 23, 2019

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.

@Hopding Hopding changed the title [Feature Request] Generate PDF/A-3 documents [Feature Request]: Generate PDF/A-3 documents Jan 1, 2020
@13thirteen
Copy link

13thirteen commented Jan 1, 2020

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
    • I added XMP metadata again (in addition to the metadata setters introduced in release 1.2.0) as explained in Add metadata #55 (comment)
    • This gave me a followup error violating this rule
      • I had to remove the line <pdf:Subject>${options.subject}</pdf:Subject> to fix this.
  • 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 How to add text with a link in V1.x.x #161 (comment)
    • 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!

@dotob
Copy link

dotob commented May 18, 2020

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

@DkDavid
Copy link
Contributor

DkDavid commented Oct 8, 2020

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,
...

@maximebochon
Copy link

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.

@Hopding
Copy link
Owner

Hopding commented Sep 24, 2021

Added this to the roadmap for tracking: #998.

@ram-you
Copy link

ram-you commented Jan 28, 2023

Hi @DkDavid , @13thirteen and @Hopding
can you please provide us a full working example?
The whole community will be grateful to you.
Thank you.

@Xenope
Copy link

Xenope commented Apr 25, 2023

+1 @ram-you especially for those in Europe dealing with zugferd/factur-x invoices. Is this repo still alive? since there has been no activity for a year and a half.

@necessarylion
Copy link

@ram-you @Xenope I wrote the instruction here.

@Xenope
Copy link

Xenope commented Sep 5, 2023

Thank you man! I will check it and give it a try!

@Xenope
Copy link

Xenope commented Nov 30, 2023

Thank you @necessarylion with the info provided here and in your link we were able to generate Factur-X compliant invoices !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants