3.29. XFA Daten

Überblick

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>

Existenz und Abwesenheit von XFA

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>

Vergleich gegen eine XML-Datei

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" /> 1
    </hasXFAData>
  </assertThat>
</testcase>

1

Whitespaces werden beim Vergleich der XML-Daten nicht berücksichtigt.

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.

Einzelne XML-Tags validieren

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/" /> 1
    </hasXFAData>
  </assertThat>
</testcase>

1

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-basierte XFA-Tests

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.

Default-Namensraum in XPath

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"                                     1
                value="memory" 
                defaultNamespace="http://www.xfa.org/schema/xci/2.6/" />
    </hasXFAData>
  </assertThat>
</testcase>

1

Der Alias für den Default-Namensraum kann beliebig gewählt werden.