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

Additional support for factur-x (PDF-A3) #508

Merged
merged 5 commits into from
Nov 29, 2020
Merged

Additional support for factur-x (PDF-A3) #508

merged 5 commits into from
Nov 29, 2020

Conversation

DkDavid
Copy link
Contributor

@DkDavid DkDavid commented Jun 29, 2020

The factur-x (ZUGFeRD) standard needs the attached files listed in the AF-tag.
Furthermore, additional metadata is necessary for the embedded files.

Easy solution: Allow users to supply additionalParams.

To access the attached files of factur-x invoices, access to the PDFRawStream is needed.

@Hopding
Copy link
Owner

Hopding commented Jul 5, 2020

Hello @DkDavid!

I'm not familiar with the details of the ZUGFeRD standard. I don't know anything about the AF entry and the PDF spec doesn't seem to list it in the table of catalog entries. Can you please point me to some documentation about this? I'd like to understand it better before merging this.

@DkDavid
Copy link
Contributor Author

DkDavid commented Jul 21, 2020

Hi Hopding,

sorry for my late reply.
The standard is used to attach invoice meta-data to PDF/A3 documents (Germany: ZUGFeRD, France: Factur-X, since version 2.1 the name is Factur-X and a European standard exists). Most additions can easily be made using the existing API. I can add a minimal example to get the conformance with the standard, if desired. However, additional params for the embedded file as well as the AF entry are necessary.

The specification can be downloaded here: https://www.ferd-net.de/standards/zugferd-versionsarchiv/zugferd-2.1.html
The additional AF entry is mentioned in "ZUGFeRD-2.1.0 - Spezifikation_TA_Anlage-A" - page 8 (diagram) for example.

@Hopding
Copy link
Owner

Hopding commented Oct 31, 2020

@DkDavid Would it be possible to add specific options to the embedded file options? I would prefer that approach as it is easier to document and use than allowing users to pass arbitrary values. Note that I haven't had a chance to familiarize myself with the ZUGFeRD specification, so just let me know if there's a specific reason this wouldn't work.

@DkDavid
Copy link
Contributor Author

DkDavid commented Oct 31, 2020

@Hopding Yes, that would be possible. The "additional param" needed for factur-x is: additionalParams: {'AFRelationship': 'Alternative'} for the xml attachment.
I just thought it would be more flexible for future standards. However, the optional param "AFRelationship" would be sufficient for factur-x.

This as well as the proposed change in PDFEmbeddedFile.ts, and the addition of the CIDToGIDMap would be necessary to fulfill compliance. Everything else is easily possible with the current api.
For CIDToGIDMap see #230 (comment)

Copy link
Owner

@Hopding Hopding left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DkDavid Thanks for working on this! I certainly appreciate your desire to make the API extensible. It might alleviate the need to add specific new options in the future. However, I'm concerned that it could also result in a more obscure and less well-documented API that would be more difficult to understand, use, and maintain. So I've requested some changes to implement this feature more in-line with existing API options.

In addition to my other comments, we'll need to add an assertion to the following method for the new option it accepts:

async attach(
attachment: string | Uint8Array | ArrayBuffer,
name: string,
options: AttachmentOptions = {},
): Promise<void> {
assertIs(attachment, 'attachment', ['string', Uint8Array, ArrayBuffer]);
assertIs(name, 'name', ['string']);
assertOrUndefined(options.mimeType, 'mimeType', ['string']);
assertOrUndefined(options.description, 'description', ['string']);
assertOrUndefined(options.creationDate, 'options.creationDate', [Date]);
assertOrUndefined(options.modificationDate, 'options.modificationDate', [
Date,
]);

The assertion should be:

assertIsOneOfOrUndefined(options.afRelationship, 'options.afRelationship', AFRelationship);

(see this assertion for reference).

src/api/PDFEmbeddedFile.ts Show resolved Hide resolved
src/core/embedders/FileEmbedder.ts Outdated Show resolved Hide resolved
src/core/embedders/FileEmbedder.ts Outdated Show resolved Hide resolved
src/core/objects/PDFArray.ts Outdated Show resolved Hide resolved
src/core/PDFContext.ts Outdated Show resolved Hide resolved
@DkDavid
Copy link
Contributor Author

DkDavid commented Nov 24, 2020

@Hopding I digged into the Factur-X (european harmonized name of ZugFERD) and the PDF-A3 specification. Nearly all of the changes can be led back to the PDF-A3 specification. Therefore I changed the references accordingly and implemented your comments.

To reach PDF-A3 compliance three additional things have to be done by the user. All of them are reachable by the current API.

    `const hash =   crypto.createHash('sha256').update(DOCUMENTID + new Date().toISOString ).digest();
    const hashArray = Array.from(new Uint8Array(hash));
    const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
    const permanentDocumentId = PDFHexString.of(hashHex);
    const changingDocumentID = permanentDocumentId; // updated when document is changed 
    doc.context.trailerInfo.ID = doc.context.obj([permanentDocumentId, changing]);`

   `// MarkInfo specifies if the document is a tagged pdf file. => 9.7 https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/pdf_reference_archives/PDFReference.pdf
    const rootref = doc.context.obj({Marked: true})
    doc.catalog.set(PDFName.of('MarkInfo'), rootref);`
  
  ` // => 14.7.2 https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/PDF32000_2008.pdf
    const structTreedata = doc.context.obj({Type: PDFName.of('StructTreeRoot')})
    const structTreeref = doc.context.register(structTreedata);
    doc.catalog.set(PDFName.of('StructTreeRoot'), structTreeref);`

Copy link
Owner

@Hopding Hopding left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DkDavid This is perfect! Thanks for working on this and figuring out what is necessary to achieve PDF-A3 compliance. This feature will go out in the next release of pdf-lib 🎉

@Hopding Hopding merged commit f8ef124 into Hopding:master Nov 29, 2020
@Hopding
Copy link
Owner

Hopding commented Dec 6, 2020

The changes in this PR were just released in v1.13.0.

Hopding pushed a commit that referenced this pull request Aug 30, 2021
* Added support for factur-x, additional metadata for embedded files, add embedded files to AF tag, make PDFRawStram available.

* Add PDFRawStram to lookup function

* Added CIDToGIDMap to achieve PDF-A3 compliance

* implement requested changes, update reference to PDF-A3

* cleanup from previous implementation

Co-authored-by: David Dillkötter <dillkoetter@kampundkoetter.de>
Co-authored-by: David Dillkötter <david.dillkoetter@gmail.com>
@ram-you
Copy link

ram-you commented Jan 19, 2023

Hello everyone.
First of all, I apologize for commenting on a closed issue.
I wanted to ask if it is possible to provide us with a working example to generate a PDF with the Factur-X (european harmonized name of ZugFERD) and the PDF-A3 specification.
Thanks in advance and sorry for the inconvenience.

@kemotx90
Copy link

kemotx90 commented Mar 6, 2024

Hello everyone. First of all, I apologize for commenting on a closed issue. I wanted to ask if it is possible to provide us with a working example to generate a PDF with the Factur-X (european harmonized name of ZugFERD) and the PDF-A3 specification. Thanks in advance and sorry for the inconvenience.

for PDF-A3 follow this #230

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

Successfully merging this pull request may close these issues.

4 participants