Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Bump node version requirement to 20+
- Bump minimum supported browsers to Firefox 115, iOS/Safari 16
- Fix text with input x as null
- Added initial support for e-invoices (ZUGFeRD and Factur-X)

### [v0.18.0] - 2026-03-14

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ yarn add pdfkit
- Encryption
- Access privileges (printing, copying, modifying, annotating, form filling, content accessibility, document assembly)
- Accessibility support (marked content, logical structure, Tagged PDF, PDF/UA)
- Long-term preservation of electronic documents (PDF/A)
- E-invoice PDFs (ZUGFeRD, Factur-X)

## Coming soon!

Expand Down
45 changes: 45 additions & 0 deletions docs/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,51 @@ In order to verify the generated document for PDF/A and its subsets conformance,

Please note that PDF/A requires fonts to be embedded, as such the standard fonts PDFKit comes with cannot be used because they are in AFM format, which only provides neccessary metrics, without the font data. You should use `registerFont()` and use embeddable fonts such as `ttf`.

## E-invoices

Electronic invoices are hybrid PDFs that are both human-readable and machine-processable, which is achieved by embedding structured XML documents.

Currently, PDFKit aims to support ZUGFeRD v2.X and Factur-X, which both require the e-invoices to be embedded into a PDF/A-3 document. Make sure your `PDFDocument` is created with the `subset` set to either `PDF/A-3b` or `PDF/A-3a`:

const doc = new PDFDocument({ subset: 'PDF/A-3b', pdfVersion: '1.7' });

Call `doc.einvoice(format, src, options = {})` after creating the document and pass the invoice XML as a `Buffer`, `ArrayBuffer` or base64 encoded `string` or path to file:

// for ZUGFeRD
doc.einvoice('zugferd', '/invoices/invoice1234.xml');

// or for Factur-X
doc.einvoice('facturx', '/invoices/invoice1234.xml');

Note: only one e-invoice can be embedded per document and a second call will throw an error.

The following options are supported for both ZUGFeRD and Factur-X:

- `profile` a string indicating the conformance profile, defaults to 'EN 16931' and the value is case insensitive
- `documentType` a string indicating the type of document, e.g. 'INVOICE', 'ORDER'. Defaults to 'INVOICE'. See the ZUGFeRD/Factur-X specification for all accepted values.
- `version` a string to override the default version PDFKit chooses from the profile

The following profiles are supported for ZUGFeRD:

- `minimum` default version 2.4
- `basic wl` default version 2.4
- `basic` default version 2.4
- `en 16931` default version 2.4
- `extended` default version 2.4
- `xrechnung` default version 3.0

The following profiles are supported for Factur-X:

- `minimum` default version 1.0
- `basic wl` default version 1.0
- `basic` default version 1.0
- `en 16931` default version 1.0
- `extended` default version 1.0

PDFKit does not validate your XML against the selected profile, therefore please ensure you select the adequate profile based on your XML.

Note: Unknown profiles fall back to 'EN 16931'.

### Adding content

Once you've created a `PDFDocument` instance, you can add content to the
Expand Down
Binary file modified docs/guide.pdf
Binary file not shown.
182 changes: 182 additions & 0 deletions examples/einvoices/en16931.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
<?xml version="1.0" encoding="UTF-8"?>
<rsm:CrossIndustryInvoice
xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
xmlns:qdt="urn:un:unece:uncefact:data:standard:QualifiedDataType:100"
xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<rsm:ExchangedDocumentContext>
<ram:GuidelineSpecifiedDocumentContextParameter>
<ram:ID>urn:cen.eu:en16931:2017</ram:ID>
</ram:GuidelineSpecifiedDocumentContextParameter>
</rsm:ExchangedDocumentContext>

<rsm:ExchangedDocument>
<ram:ID>RE-2024-0001</ram:ID>
<ram:TypeCode>380</ram:TypeCode>
<ram:IssueDateTime>
<udt:DateTimeString format="102">20240101</udt:DateTimeString>
</ram:IssueDateTime>
<ram:IncludedNote>
<ram:Content>Test invoice</ram:Content>
</ram:IncludedNote>
</rsm:ExchangedDocument>

<rsm:SupplyChainTradeTransaction>
<ram:IncludedSupplyChainTradeLineItem>
<ram:AssociatedDocumentLineDocument>
<ram:LineID>1</ram:LineID>
</ram:AssociatedDocumentLineDocument>
<ram:SpecifiedTradeProduct>
<ram:Name>Test Product A</ram:Name>
</ram:SpecifiedTradeProduct>
<ram:SpecifiedLineTradeAgreement>
<ram:NetPriceProductTradePrice>
<ram:ChargeAmount>100.00</ram:ChargeAmount>
<ram:BasisQuantity unitCode="H87">1</ram:BasisQuantity>
</ram:NetPriceProductTradePrice>
</ram:SpecifiedLineTradeAgreement>
<ram:SpecifiedLineTradeDelivery>
<ram:BilledQuantity unitCode="H87">1</ram:BilledQuantity>
</ram:SpecifiedLineTradeDelivery>
<ram:SpecifiedLineTradeSettlement>
<ram:ApplicableTradeTax>
<ram:TypeCode>VAT</ram:TypeCode>
<ram:CategoryCode>S</ram:CategoryCode>
<ram:RateApplicablePercent>19</ram:RateApplicablePercent>
</ram:ApplicableTradeTax>
<ram:SpecifiedTradeSettlementLineMonetarySummation>
<ram:LineTotalAmount>100.00</ram:LineTotalAmount>
</ram:SpecifiedTradeSettlementLineMonetarySummation>
</ram:SpecifiedLineTradeSettlement>
</ram:IncludedSupplyChainTradeLineItem>

<ram:ApplicableHeaderTradeAgreement>
<ram:BuyerReference>0</ram:BuyerReference>
<ram:SellerTradeParty>
<ram:ID>S-001</ram:ID>
<ram:Name>Acme Media GmbH</ram:Name>
<ram:DefinedTradeContact>
<ram:PersonName>Anna Seller</ram:PersonName>
<ram:TelephoneUniversalCommunication>
<ram:CompleteNumber>030/11111111</ram:CompleteNumber>
</ram:TelephoneUniversalCommunication>
<ram:EmailURIUniversalCommunication>
<ram:URIID>anna@seller.example</ram:URIID>
</ram:EmailURIUniversalCommunication>
</ram:DefinedTradeContact>
<ram:PostalTradeAddress>
<ram:PostcodeCode>10115</ram:PostcodeCode>
<ram:LineOne>Test Street 1</ram:LineOne>
<ram:CityName>Berlin</ram:CityName>
<ram:CountryID>DE</ram:CountryID>
</ram:PostalTradeAddress>
<ram:URIUniversalCommunication>
<ram:URIID schemeID="EM">billing@seller.example</ram:URIID>
</ram:URIUniversalCommunication>
<ram:SpecifiedTaxRegistration>
<ram:ID schemeID="VA">DE111111111</ram:ID>
</ram:SpecifiedTaxRegistration>
<ram:SpecifiedTaxRegistration>
<ram:ID schemeID="FC">11111111111</ram:ID>
</ram:SpecifiedTaxRegistration>
</ram:SellerTradeParty>
<ram:BuyerTradeParty>
<ram:ID>B-001</ram:ID>
<ram:Name>Test Buyer GmbH</ram:Name>
<ram:PostalTradeAddress>
<ram:PostcodeCode>20095</ram:PostcodeCode>
<ram:LineOne>Sample Avenue 2</ram:LineOne>
<ram:CityName>Hamburg</ram:CityName>
<ram:CountryID>DE</ram:CountryID>
</ram:PostalTradeAddress>
<ram:URIUniversalCommunication>
<ram:URIID schemeID="EM">accounts@buyer.example</ram:URIID>
</ram:URIUniversalCommunication>
<ram:SpecifiedTaxRegistration>
<ram:ID schemeID="VA">DE222222222</ram:ID>
</ram:SpecifiedTaxRegistration>
</ram:BuyerTradeParty>
<ram:SellerOrderReferencedDocument>
<ram:IssuerAssignedID>ORD-0001</ram:IssuerAssignedID>
</ram:SellerOrderReferencedDocument>
</ram:ApplicableHeaderTradeAgreement>

<ram:ApplicableHeaderTradeDelivery>
<ram:ActualDeliverySupplyChainEvent>
<ram:OccurrenceDateTime>
<udt:DateTimeString format="102">20240101</udt:DateTimeString>
</ram:OccurrenceDateTime>
</ram:ActualDeliverySupplyChainEvent>
</ram:ApplicableHeaderTradeDelivery>

<ram:ApplicableHeaderTradeSettlement>
<ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>
<ram:SpecifiedTradeSettlementPaymentMeans>
<ram:TypeCode>30</ram:TypeCode>
<ram:Information>SEPA credit transfer</ram:Information>
<ram:PayeePartyCreditorFinancialAccount>
<ram:IBANID>DE89370400440532013000</ram:IBANID>
<ram:AccountName>Acme Media GmbH</ram:AccountName>
</ram:PayeePartyCreditorFinancialAccount>
</ram:SpecifiedTradeSettlementPaymentMeans>
<ram:ApplicableTradeTax>
<ram:CalculatedAmount>21.85</ram:CalculatedAmount>
<ram:TypeCode>VAT</ram:TypeCode>
<ram:BasisAmount>115.00</ram:BasisAmount>
<ram:CategoryCode>S</ram:CategoryCode>
<ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
</ram:ApplicableTradeTax>
<ram:SpecifiedTradeAllowanceCharge>
<ram:ChargeIndicator>
<udt:Indicator>false</udt:Indicator>
</ram:ChargeIndicator>
<ram:ActualAmount>30.00</ram:ActualAmount>
<ram:Reason>Discount 30%</ram:Reason>
<ram:CategoryTradeTax>
<ram:TypeCode>VAT</ram:TypeCode>
<ram:CategoryCode>S</ram:CategoryCode>
<ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
</ram:CategoryTradeTax>
</ram:SpecifiedTradeAllowanceCharge>
<ram:SpecifiedTradeAllowanceCharge>
<ram:ChargeIndicator>
<udt:Indicator>true</udt:Indicator>
</ram:ChargeIndicator>
<ram:ActualAmount>60.00</ram:ActualAmount>
<ram:Reason>Colour surcharge 60%</ram:Reason>
<ram:CategoryTradeTax>
<ram:TypeCode>VAT</ram:TypeCode>
<ram:CategoryCode>S</ram:CategoryCode>
<ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
</ram:CategoryTradeTax>
</ram:SpecifiedTradeAllowanceCharge>
<ram:SpecifiedTradeAllowanceCharge>
<ram:ChargeIndicator>
<udt:Indicator>false</udt:Indicator>
</ram:ChargeIndicator>
<ram:ActualAmount>15.00</ram:ActualAmount>
<ram:Reason>Agency commission 15%</ram:Reason>
<ram:CategoryTradeTax>
<ram:TypeCode>VAT</ram:TypeCode>
<ram:CategoryCode>S</ram:CategoryCode>
<ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
</ram:CategoryTradeTax>
</ram:SpecifiedTradeAllowanceCharge>
<ram:SpecifiedTradePaymentTerms>
<ram:Description>Payable within 14 days without deduction</ram:Description>
</ram:SpecifiedTradePaymentTerms>
<ram:SpecifiedTradeSettlementHeaderMonetarySummation>
<ram:LineTotalAmount>100.00</ram:LineTotalAmount>
<ram:ChargeTotalAmount>60.00</ram:ChargeTotalAmount>
<ram:AllowanceTotalAmount>45.00</ram:AllowanceTotalAmount>
<ram:TaxBasisTotalAmount>115.00</ram:TaxBasisTotalAmount>
<ram:TaxTotalAmount currencyID="EUR">21.85</ram:TaxTotalAmount>
<ram:GrandTotalAmount>136.85</ram:GrandTotalAmount>
<ram:DuePayableAmount>136.85</ram:DuePayableAmount>
</ram:SpecifiedTradeSettlementHeaderMonetarySummation>
</ram:ApplicableHeaderTradeSettlement>
</rsm:SupplyChainTradeTransaction>

</rsm:CrossIndustryInvoice>
Loading
Loading