Die „XML Forms Architecture, (XFA)“ ist eine Erweiterung der PDF-Strukturen um XML-Informationen, mit dem Ziel, PDF-Formulare in den Prozessen eines Workflow's besser verarbeiten zu können.
XFA Formulare sind nicht kompatibel zu „AcroForms“. Deshalb sind die PDFUnit-Tests für Acroforms auch nicht verwendbar. Test auf XFA-Daten basieren überwiegend auf XPath:
<!-- Tags to test XFA data: --> <hasXFAData /> <hasNoXFAData /> <!-- Inner tags of hasXFAData: --> <hasXFAData> <matchingXPath /> (optional) <matchingXML /> (optional) <withNode /> (optional) </hasXFAData>
Der erste Test zielt auf die reine Existenz von XFA-Daten:
<testcase name="hasXFAData"> <assertThat testDocument="xfa/xfa-movie.pdf"> <hasXFAData /> </assertThat> </testcase>
Es ist auch möglich, explizit zu testen, dass ein PDF-Dokument keine XFA-Daten enthält:
<testcase name="hasNoXFAData"> <assertThat testDocument="xfa/no-xfa.pdf"> <hasNoXFAData /> </assertThat> </testcase>
Die XFA-Daten eines PDF-Dokumentes können mit dem Hilfsprogramm
ExtractXFAData
in eine XML-Datei exportiert werden.
Diese Datei kann vollständig mit den XFA-Daten eines PDF-Dokumentes
verglichen werden:
<testcase name="hasXFAData_MatchingXML"> <assertThat testDocument="xfa/xfa-movie.pdf"> <hasXFAData> <matchingXML file="xfa/xfa-movie.xml" /> </hasXFAData> </assertThat> </testcase>
Oftmals ist es nicht sinnvoll, die kompletten XFA-Daten eines PDF-Dokumentes gegen eine XML-Vorlage zu vergleichen. Für solche Fälle gibt es sowohl die Möglichkeit, auf einzelne XML-Knoten zu testen, als auch Tests mit XPath-Ausdrücken zu formulieren. Beide Möglichkeiten werden in den folgenden Abschnitten beschrieben.
Das im nächsten Beispiel verwendete PDF-Dokument enthält folgende XFA-Daten (Ausschnitt):
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/"> ... <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.2.1-c041 52.337767, 2008/04/13-15:41:00" > <config xmlns="http://www.xfa.org/schema/xci/2.6/"> ... <log xmlns="http://www.xfa.org/schema/xci/2.6/"> <to>memory</to> <mode>overwrite</mode> </log> ... </config> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> ... <rdf:Description xmlns:xmp="http://ns.adobe.com/xap/1.0/" > <xmp:MetadataDate>2009-12-03T17:50:52Z</xmp:MetadataDate> </rdf:Description> ... </rdf:RDF> ... </x:xmpmeta> </xdp:xdp>
Der Wert eines bestimmten XML-Knotens kann mit dem Tag
<withNode />
getestet werden:
<testcase name="hasXFAData_WithNode"> <assertThat testDocument="xfa/xfa-enabled.pdf"> <hasXFAData> <withNode tag="xmp:MetadataDate" value="2009-12-03T17:50:52Z" defaultNamespace="http://www.xfa.org/schema/xci/2.6/" /> </hasXFAData> </assertThat> </testcase>
PDFUnit analysiert die XFA-Daten des aktuellen PDF-Dokumentes und ermittelt die Namensräume selbständig, lediglich der Default-Namespace muss angegeben werden. |
Sollte der XPath-Ausdruck für den Knoten zu mehreren Treffern führen, wird der erste Treffer verwendet.
Zur Umsetzung ergänzt PDFUnit intern vor dem Knoten den Pfad-Bestandteil "//"
. Aus
diesem Grund darf der Knoten im Test kein Pfad sein, der die Wurzel "/"
enthält.
Prüfungen auf Attribut-Knoten sind selbstverständlich auch möglich:
<testcase name="hasXFAData_WithNode_NamespaceDD"> <assertThat testDocument="xfa/xfa-enabled.pdf"> <hasXFAData> <withNode tag="dd:dataDescription/@dd:name" value="movie" /> </hasXFAData> </assertThat> </testcase>
XPath kann mehr, als nur einzelne Knoten zu benennen. Mit dem Tag
<matchingXPath />
können alle Möglichkeiten von
XPath genutzt werden.
Die nächsten beiden Beispiele zeigen, was machbar ist:
<testcase name="hasXFAData_FunctionStartsWith"> <assertThat testDocument="xfa/xfa-enabled.pdf"> <hasXFAData> <!-- complete value: 'movie' --> <matchingXPath expr="starts-with(//dd:dataDescription/@dd:name, 'mov')" /> </hasXFAData> </assertThat> </testcase>
<testcase name="hasXFAData_MatchingXPath_FunctionCount_MultipleInvocation"> <assertThat testDocument="xfa/xfa-movie.pdf"> <hasXFAData> <matchingXPath expr="//pdf:Producer[. ='Adobe LiveCycle Designer ES 8.2']" /> <matchingXPath expr="count(//processing-instruction()) = 30" /> </hasXFAData> </assertThat> </testcase>
Eine kleine Einschränkung muss genannt werden. Die XPath-Ausdrücke können nur mit den Möglichkeiten ausgewertet werden, die die verwendete XPath-Implementierung bietet. PDFUnit nutzt normalerweise die JAXP-Implementierung des verwendeten JDK. Damit ist die XPath-Kompatibilität aber vom JDK/JRE abhängig und unterliegt dem Wandel der Zeit.
XML-Namensräume werden automatisch ermittelt, aber der verwendete Default-Namensraum muss explizit angegeben werden. Weil der XML-Standard mehrere, verschiedene Deklarationen in einem Dokument zulässt, ist nicht automatisch klar, welcher Default-Namensraum benutzt werden soll, wenn tatsächlich mehrere Deklarationen verwendet werden. Deshalb muss er im Test angegeben werden:
<testcase name="hasXFAData_DefaultNamespace_matchingXPath"> <assertThat testDocument="xfa/xfa-movie.pdf"> <hasXFAData> <matchingXPath expr="count(//default:subform[@name ='movie']//default:field) = 5" defaultNamespace="http://www.xfa.org/schema/xfa-template/2.6/" /> </hasXFAData> </assertThat> </testcase>
Aus dem gleichen Grund muss der Default-Namensraum auch bei der Verwendung
des Tags <withNode />
mitgegeben werden:
<!-- The default namespace has to be declared, but any alias can be used for it. --> <testcase name="hasXFAData_DefaultNamespace_WithNode_AnyAlias"> <assertThat testDocument="xfa/xfa-enabled.pdf"> <hasXFAData> <withNode tag="foo:log/foo:to" value="memory" defaultNamespace="http://www.xfa.org/schema/xci/2.6/" /> </hasXFAData> </assertThat> </testcase>