Skip to content

Commit

Permalink
Add documentation for Factur-X / ZUGFeRD
Browse files Browse the repository at this point in the history
  • Loading branch information
liZe committed Jan 19, 2025
1 parent 2f70ca3 commit 6f8a2f5
Showing 1 changed file with 179 additions and 6 deletions.
185 changes: 179 additions & 6 deletions docs/common_use_cases.rst
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ such as page numbers, headers, etc. Read more about the page_ at-rule.
.. _page: https://developer.mozilla.org/en-US/docs/Web/CSS/@page


Generate PDFs Specialized for Accessibility (PDF/UA) and Archiving (PDF/A)
--------------------------------------------------------------------------
Generate Specialized PDFs
-------------------------

WeasyPrint can generate different PDF variants, including PDF/UA and PDF/A. The
feature is available by using the ``--pdf-variant`` CLI option, or the
Expand All @@ -125,8 +125,8 @@ Even if WeasyPrint tries to generate valid documents, the result is not
guaranteed: the HTML, CSS and PDF features chosen by the user must follow the
limitations defined by the different specifications.

PDF/A
.....
PDF/A (Archiving)
.................

PDF/A documents are specialized for archiving purposes. They are a simple
subset of PDF, with a lot of limitations: no audio, video or JavaScript,
Expand All @@ -145,8 +145,8 @@ valid PDF identifier, but you can provide your own with the
If your document includes images, you must set the ``image-rendering:
crisp-edges`` property to avoid anti-aliasing, that is forbidden by PDF/A.

PDF/UA
......
PDF/UA (Universal Accessibility)
................................

PDF/UA documents are specialized for accessibility purposes. They include extra
metadata that define document information and content structure.
Expand All @@ -158,6 +158,179 @@ also used to define the order of the PDF content.
Some information is required in your HTML file, including a ``<title>`` tag,
and a ``lang`` attribute set on the ``<html>`` tag.

Factur-X / ZUGFeRD (Electronic Invoices)
........................................

Factur-X / ZUGFeRD is a Franco-German standard for hybrid e-invoice, the first
implementation of the European Semantic Standard EN 16931. It enables users to
include normalized metadata in PDF invoices, such as companies information or
invoice amounts, so that compatible software can automatically read this
information. This standard is based on PDF/A-3b.

WeasyPrint can generate Factur-X / ZUGFeRD documents. Invoice metadata must be
generated by the user and included in the PDF document when rendered. Two
different metadata files are required:

- the first one is RDF metadata, containing document metadata and PDF/A
extension information;
- the second one is Factur-X / ZUGFeRD metadata, containing invoice amounts,
plus seller and buyer information.

Here is an example of Factur-X document generation.

``rdf.xml``:

.. code-block:: xml
<x:xmpmeta
xmlns:x="adobe:ns:meta/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:pdf="http://ns.adobe.com/pdf/1.3/"
xmlns:fx="urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0#"
xmlns:pdfaExtension="http://www.aiim.org/pdfa/ns/extension/"
xmlns:pdfaSchema="http://www.aiim.org/pdfa/ns/schema#"
xmlns:pdfaProperty="http://www.aiim.org/pdfa/ns/property#">
<!-- placeholder -->
<rdf:RDF>
<rdf:Description rdf:about="">
<fx:ConformanceLevel>MINIMUM</fx:ConformanceLevel>
<fx:DocumentFileName>factur-x.xml</fx:DocumentFileName>
<fx:DocumentType>INVOICE</fx:DocumentType>
<fx:Version>1.0</fx:Version>
</rdf:Description>
<rdf:Description rdf:about="">
<pdfaExtension:schemas>
<rdf:Bag>
<rdf:li rdf:parseType="Resource">
<pdfaSchema:schema>Factur-X PDFA Extension Schema</pdfaSchema:schema>
<pdfaSchema:namespaceURI>urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0#</pdfaSchema:namespaceURI>
<pdfaSchema:prefix>fx</pdfaSchema:prefix>
<pdfaSchema:property>
<rdf:Seq>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>DocumentFileName</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>name of the embedded XML invoice file</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>DocumentType</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>INVOICE</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>Version</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>The actual version of the Factur-X XML schema</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>ConformanceLevel</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>The conformance level of the embedded Factur-X data</pdfaProperty:description>
</rdf:li>
</rdf:Seq>
</pdfaSchema:property>
</rdf:li>
</rdf:Bag>
</pdfaExtension:schemas>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>
``factur-x.xml``:

.. code-block:: xml
<rsm:CrossIndustryInvoice
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:qdt="urn:un:unece:uncefact:data:standard:QualifiedDataType:100"
xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"
xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100">
<rsm:ExchangedDocumentContext>
<ram:BusinessProcessSpecifiedDocumentContextParameter>
<ram:ID>A1</ram:ID>
</ram:BusinessProcessSpecifiedDocumentContextParameter>
<ram:GuidelineSpecifiedDocumentContextParameter>
<ram:ID>urn:factur-x.eu:1p0:minimum</ram:ID>
</ram:GuidelineSpecifiedDocumentContextParameter>
</rsm:ExchangedDocumentContext>
<rsm:ExchangedDocument>
<ram:ID>123</ram:ID>
<ram:TypeCode>380</ram:TypeCode>
<ram:IssueDateTime>
<udt:DateTimeString format="102">20200131</udt:DateTimeString>
</ram:IssueDateTime>
</rsm:ExchangedDocument>
<rsm:SupplyChainTradeTransaction>
<ram:ApplicableHeaderTradeAgreement>
<ram:BuyerReference>Buyer</ram:BuyerReference>
<ram:SellerTradeParty>
<ram:Name>Supplyer Corp</ram:Name>
<ram:SpecifiedLegalOrganization>
<ram:ID schemeID="0002">123456782</ram:ID>
</ram:SpecifiedLegalOrganization>
<ram:PostalTradeAddress>
<ram:CountryID>FR</ram:CountryID>
</ram:PostalTradeAddress>
<ram:SpecifiedTaxRegistration>
<ram:ID schemeID="VA">FR11123456782</ram:ID>
</ram:SpecifiedTaxRegistration>
</ram:SellerTradeParty>
<ram:BuyerTradeParty>
<ram:Name>Buyer Corp</ram:Name>
<ram:SpecifiedLegalOrganization>
<ram:ID schemeID="0002">987654324</ram:ID>
</ram:SpecifiedLegalOrganization>
</ram:BuyerTradeParty>
<ram:BuyerOrderReferencedDocument >
<ram:IssuerAssignedID>456</ram:IssuerAssignedID>
</ram:BuyerOrderReferencedDocument>
</ram:ApplicableHeaderTradeAgreement>
<ram:ApplicableHeaderTradeDelivery/>
<ram:ApplicableHeaderTradeSettlement>
<ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>
<ram:SpecifiedTradeSettlementHeaderMonetarySummation>
<ram:TaxBasisTotalAmount>100.00</ram:TaxBasisTotalAmount>
<ram:TaxTotalAmount currencyID="EUR">20.00</ram:TaxTotalAmount>
<ram:GrandTotalAmount>120.00</ram:GrandTotalAmount>
<ram:DuePayableAmount>120.00</ram:DuePayableAmount>
</ram:SpecifiedTradeSettlementHeaderMonetarySummation>
</ram:ApplicableHeaderTradeSettlement>
</rsm:SupplyChainTradeTransaction>
</rsm:CrossIndustryInvoice>
``invoice.py``:

.. code-block:: python
from pathlib import Path
from weasyprint import Attachment, HTML
def generate_rdf_metadata(metadata, variant, version, conformance):
original_rdf = generate_original_rdf_metadata(metadata, variant, version, conformance)
return Path("rdf.xml").read_bytes().replace(b"<!-- placeholder -->", original_rdf)
document = HTML(string="<h1>Invoice</h1>").render()
generate_original_rdf_metadata = document.metadata.generate_rdf_metadata
factur_x_xml = Path("factur-x.xml").read_text()
attachment = Attachment(string=factur_x_xml, name="factur-x.xml", relationship="Data")
document.metadata.attachments = [attachment]
document.metadata.generate_rdf_metadata = generate_rdf_metadata
document.write_pdf("invoice.pdf", pdf_variant="pdf/a-3b")
Of course, the content of these files has to be adapted to the content of real
invoices. Using XML generators instead of plain text manipulation is also
highly recommended.

A more detailed blog article is available on `Binary Butterfly’s website
<https://binary-butterfly.de/artikel/factur-x-zugferd-e-invoices-with-python/>`_.


Include PDF Forms
-----------------
Expand Down

0 comments on commit 6f8a2f5

Please sign in to comment.