Zurück |  | Weiter

## 3.36. 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:

    // Methods around XFA data:
    .hasXFAData()
    .hasXFAData().matchingXPath(..)
    .hasXFAData().withNode(..)

    .hasNoXFAData()

### Existenz und Abwesenheit von XFA

Der erste Test zielt auf die reine Existenz von XFA-Daten:

    @Test
    public void hasXFAData() throws Exception {
      String filename = "documentUnderTest.pdf";

      AssertThat.document(filename)
                .hasXFAData()
      ;
    }

Es ist auch möglich, explizit zu testen, dass ein PDF-Dokument keine XFA-Daten enthält:

    @Test
    public void hasNoXFAData() throws Exception {
      String filename = "documentUnderTest.pdf";

      AssertThat.document(filename)
                .hasNoXFAData()
      ;
    }

### 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>

Um auf einen bestimmten Knoten zu testen, muss eine Instanz von `com.pdfunit.XMLNode` mit dem XPath-Ausdruck für den Knoten erzeugt werden. Als zweiter Parameter wird der erwartete Wert übergeben:

    @Test
    public void hasXFAData_WithNode() throws Exception {
      String filename = "documentUnderTest.pdf";
      XMLNode xmpNode = new XMLNode("xmp:MetadataDate", "2009-12-03T17:50:52Z");

      AssertThat.document(filename)
                .hasXFAData()
                .withNode(xmpNode)
      ;
    }

| PDFUnit analysiert die XFA-Daten des aktuellen PDF-Dokumentes und ermittelt die Namensräume selbständig, lediglich der Default-Namespace muss angegeben werden.

Für die internen Verarbeitung ergänzt PDFUnit vor dem Knoten den Pfad-Bestandteil `"//"`. Aus diesem Grund darf der Knoten im Test kein Pfad sein, der die Wurzel `"/"` enthält.

Sollte der XPath-Ausdruck für den Knoten zu mehreren Treffern führen, wird der erste Treffer verwendet.

Wenn das erwartete Ergebnis für den XPath-Ausdruck bei der Erzeugung der XMLNode-Instanz angegeben wird, dann wird dieses auch mit dem tatsächlichen Ergebnis verglichen. Andernfalls wird nur die Existenz des Knotens festgestellt.

Prüfungen auf Attribut-Knoten sind selbstverständlich auch möglich:

    @Test
    public void hasXFAData_WithNode_NamespaceDD() throws Exception {
      String filename = "documentUnderTest.pdf";
      XMLNode ddNode = new XMLNode("dd:dataDescription/@dd:name", "movie");

      AssertThat.document(filename)
                .hasXFAData()
                .withNode(ddNode)
      ;
    }

### XPath-basierte XFA-Tests

In PDFUnit gibt es noch die Methode `matchingXPath(..)`, um das ganze Potential von XPath nutzen zu können. Die nächsten beiden Beispiele zeigen, was damit machbar ist:

    @Test
    public void hasXFAData_MatchingXPath_FunctionStartsWith() throws Exception {
      String filename = "documentUnderTest.pdf";
      String xpathString = "starts-with(//dd:dataDescription/@dd:name, 'mov')";
      XPathExpression expressionWithFunction = new XPathExpression(xpathString);

      AssertThat.document(filename)
                .hasXFAData()
                .matchingXPath(expressionWithFunction)
      ;
    }

    @Test
    public void hasXFAData_MatchingXPath_FunctionCount_MultipleInvocation() throws Exception {
      String filename = "documentUnderTest.pdf";

      String xpathProducer = "//pdf:Producer[. ='Adobe LiveCycle Designer ES 8.2']";
      String xpathPI       = "count(//processing-instruction()) = 30";

      XPathExpression exprPI       = new XPathExpression(xpathPI);
      XPathExpression exprProducer = new XPathExpression(xpathProducer);

      AssertThat.document(filename)
                .hasXFAData()
                .matchingXPath(exprProducer)
                .matchingXPath(exprPI)
      ;

      // The same test in a different style:
      AssertThat.document(filename)
                .hasXFAData().matchingXPath(exprProducer)
                .hasXFAData().matchingXPath(exprPI)
      ;
    }

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.

Das Kapitel 13.12: „JAXP-Konfiguration“ erläutert am Beispiel von Xerces, wie ein beliebiger XML-Parser genutzt werden kann.

### Default-Namensraum in XPath

XML-Namensräume werden automatisch ermittelt. Wenn aber ein Default-Namensraum verwendet wird, muss dieser als Instanz von `DefaultNamespace` angegeben werden. Für diese Instanz muss in Java ein Prefix verwendet werden. Der Wert für das Prefix kann beliebig sein:

    @Test
    public void hasXFAData_WithDefaultNamespace_XPathExpression() throws Exception {
      String filename = "documentUnderTest.pdf";

      String namespaceURI  = "http://www.xfa.org/schema/xfa-template/2.6/";
      String xpathSubform  = "count(//default:subform[@name ='movie']//default:field) = 5";

      DefaultNamespace defaultNS   = new DefaultNamespace(namespaceURI);
      XPathExpression exprSubform  = new XPathExpression(xpathSubform, defaultNS);

      AssertThat.document(filename)
                .hasXFAData()
                .matchingXPath(exprSubform)
      ;
    }

Auch bei der Verwendung der Klasse `XMLNode` muss der Default-Namensraum mitgegeben werden:

    /**
     * The default namespace has to be declared,
     * but any alias can be used for it.
     */
    @Test
    public void hasXFAData_WithDefaultNamespace_XMLNode() throws Exception {
      String filename = "documentUnderTest.pdf";

      String namespaceXCI = "http://www.xfa.org/schema/xci/2.6/";
      DefaultNamespace defaultNS = new DefaultNamespace(namespaceXCI);
      XMLNode aliasFoo = new XMLNode("foo:log/foo:to", "memory", defaultNS);

      AssertThat.document(filename)
                .hasXFAData()
                .withNode(aliasFoo)
      ;
    }

Zurück | Nach oben | Weiter
3.35. Version | Zum Anfang | 3.37. XMP-Daten