Skip to content
New issue

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

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

Already on GitHub? # to your account

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

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

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

zimt28 opened this issue Nov 1, 2019 · 12 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 !

@AutomGuy
Copy link

Hello at All, but especially @Xenope, at your post from 2023/11/30 you say you have successfully created Factur-X compliant invoice. is it possible that you can help me out with a code example how to achieve this. occasionally my wife has to send invoices in that style. I want to use this for my wife's small business.

Many thanks in advance

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

No branches or pull requests

10 participants