von Patrick Gundlach |

ZUGFeRD

Immer wieder erreichen mich Anfragen von Auftraggebern, wo ich kurz grübeln und ausprobieren muss, ob die mit dem Publisher umsetzbar sind. Kürzlich hatte ich eine Anfrage zu elektronischen Rechnungen.

ZUGFeRD (Zentraler User Guide des Forums elektronische Rechnung Deutschland) ist ein Dateiformat für elektronische Rechnungen. Es wird vermutlich nur in Deutschland verwendet, dafür aber recht häufig.

Das Dateiformat besteht aus einer XML-Datei die in eine PDF-Datei eingebettet wird, insofern gibt es hier zwei getrennte »Baustellen«. Die XML-Datei muss genau die Angaben enthalten, die auch in der PDF-Datei sichtbar sind, aber in strukturierter Form. Sowohl die XML-Datei als auch die PDF-Datei müssen einem bestimmten Schema entsprechen.

Die XML-Seite

Für die XML-Datei gibt es entsprechende (XML)Schema-Dateien, die aber leider nicht ausreichend sind, um die Dateien vollständig zu validieren. Hier hätte ich mir ein strengeres Schema gewünscht, für die erste Überprüfung ist es aber auf jeden Fall in Ordnung. Es gibt mehrere Konformitätslevel: Basic, Comfort und Extended, die je nach Typ der darzustellenden Rechnung benutzt werden.

Im Moment interessiert mich die XML-Seite nicht so sehr, dafür um so mehr die PDF-Seite.

ZUGFeRD-PDF

Das ZUGFeRD-PDF baut auf der Spezifikation PDF/A3 auf. Hier fängt das Problem auch schon an, denn die Spezifikation ist nur als verhältnismäßig teure ISO-Norm (ISO 19005-3) verfügbar. Ob ich gewillt bin, 180 Euro für ein Dokument zu bezahlen, das mir möglicherweise nicht wirklich weiter hilft, weiß ich noch nicht.

Unproblematisch ist aber, sich die Beispiele anzusehen, die in der ZUGFeRD-Dokumentation mitgeliefert werden (ZIP-Datei) und eine Art »Reverse-Engineering« zu betreiben.

Das PDF-Format ist eigentlich ein Textformat, aber durch Komprimierung nicht einfach zugänglich. Um die Komprimierung zu entfernen nutze ich qpdf:

qpdf --qdf --object-streams=disable ZUGFeRD_1p0_BASIC_Einfach.pdf uncompressed.pdf

Der Befehl erzeugt die Datei uncompressed.pdf, die man sich mit einem Texteditor anschauen kann. Verändern geht nicht (so einfach), weil die Offsets der einzelnen Objekte in einer Tabelle am Ende des Dokumentes kodiert sind. Wenn sich durch Einfügen oder Löschen von Zeichen die Objekte verschieben, muss die Tabelle neu berechnet werden.

Auf den Aufbau von PDF-Dateien will ich an dieser Stelle nicht eingehen (vielleicht sollte ich das mal in einem eigenen Blogbeitrag machen), daher zeige ich nur Auszüge der Datei. Nur soviel: die PDF-Datei besteht aus einzelnen Objekten. Objekte sind z.B. Zahlen, Zeichenketten und Namen, aber auch komplexere Dinge wie Dictionaries, Arrays und Streams.

Objekte sind entweder direkte Objekte wie die Zeichenkette (Hallo) oder indirekte Objekte, die mit einer Objektnummer referenziert werden wie 6 0 R, das ist eine Referenz auf Objekt 6.

Eine PDF-Datei hat als Einstiegspunkt (Catalog) ein Objekt vom Typ »Dictionary«. Im einfachsten Fall ist das

1 0 obj
<<
  /Type /Catalog
  /Pages 9 0 R
>>
endobj

wobei 9 0 R die Referenz auf ein Objekt ist, das Verweise auf die einzelnen Seiten im PDF enthält. Die Bedeutung der einzelnen Objekttypen sind in der PDF-Spezifikation zu finden.

Das ZUGFeRD-PDF hat noch weitere Einträge im Einstiegspunkt der Datei:

/Metadata 4 0 R
/Names <<
  /EmbeddedFiles <<
    /Names [  (ZUGFeRD-invoice.xml)  6 0 R ]
  >>
>>

Die beiden Einträge /Metadata und /Names sind Teil des Dokumentkatalogs oben. Den Inhalt von /Metadata beschreibe ich weiter unten. Der /Names Eintrag im Katalog beschreibt alle im PDF vorkommenden Namen, auch die der eingebetteten Dateien (hier als einziger Eintrag).

6 0 obj
<<
  /Type /Filespec
  /AFRelationship /Alternative
  /Desc (ZUGFeRD Rechnung)
  /EF <<
    /F  11 0 R
    /UF 11 0 R
  >>
  /F (ZUGFeRD-invoice.xml)
  /UF <feff005a005500470046006500520044002d0069006e0076006f006900630065002e0078006d006c>
>>
endobj

Der Aufbau ist wie oben, ein Dictionary mit Schlüssel/Werte Paaren. Bei ZUGFeRD ist der Name der Rechnung fest auf ZUGFeRD-invoice.xml festgelegt. Der Wert ist auch in /UF in der Kodierung »PDFDocEncoding« (UTF-16BE + BOM) enthalten. Die Einträge unter /EF (für embedded file) zeigen auf die tatsächlich inkludierte Datei.

11 0 obj
<<
  /Type    /EmbeddedFile
  /Subtype /text#2fxml
  /Length  8415
  /Params <<
    /ModDate (D:20170725161353+02'00')
  >>
>>
stream
<?xml version="1.0" encoding="UTF-8"?>
<rsm:CrossIndustryDocument
...
(hier ist die eigentliche ZUGFeRD XML-Datei)
...
</rsm:CrossIndustryDocument>
endstream
endobj

Der Subtype ist der MimeType der Datei in einer PDF spezifischen Kodierung und muss bei ZUGFeRD text/xml sein.

Nun fehlt noch das Metadata-Objekt von oben. Metadaten sind eine formale Beschreibung des sichtbaren Inhalts der PDF-Datei und gehören immer in eine PDF/A Datei. Die Daten sind im XMP-Format bereitzustellen, das wiederum ein XML-Format ist und auf dem RDF-Standard aufbaut. Am einfachsten ist, sich den Inhalt der originalen PDF-Datei anzuschauen und die XMP/RDF-Datei dann nachzubauen. Wichtig ist, dass die Metadaten unkomprimiert im PDF sichtbar sind.

4 0 obj
<<
  /Type /Metadata
  /Subtype /XML
  /Length 3307
>>
stream
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/">
 <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
...
 </rdf:RDF>
</x:xmpmeta>
<?xpacket end="w"?>
endstream
endobj

Für die, die Details wissen wollen: in LuaTeX kann man ein unkomprimiertes Objekt in einer ansonsten komprimierten Datei mit pdf.obj() erzeugen:

pdf.obj({type = "stream",
                 string = metadata,
                 immediate = true,
                 attr = [[  /Subtype /XML /Type /Metadata  ]],
                 compresslevel = 0,
                 })

Hat man dann eine Datei erstellt, die diese Objekte mit den richtigen Inhalten enthält (mit LuaTeX geht das wunderbar), dann kann man diese online im ZUGFeRD Validator überprüfen.

Zusammenspiel mit dem Publisher

Das Feature ist noch in der Entwicklung, bisher habe ich nur einen (funktionierenden) Prototypen fertig gestellt. Dieser ist auf Github zu finden.


Nachtrag: ZUGFeRD ist im Publisher seit der Version 3.1 integriert. Version 3.1 (und später) sind wie gehabt unter https://download.speedata.de/publisher/development/ zu laden.

Die Schnittstelle zur Rechnung ist derzeit wie folgt:

<AttachFile filename="rechnung.xml" description="ZUGFeRD Rechnung" type="ZUGFeRD invoice"/>

Den Rest übernimmt dann der Publisher, der die richtigen PDF-Objekte in die Datei schreibt.