3.11. Formularfelder

Überblick

Wenn Inhalte eines PDF-Dokumentes weiterverarbeitet werden sollen, spielen Formularfelder eine entscheidende Rolle. Diese sollten schon bei der Erstellung des PDF-Dokumentes korrekt erstellt sein. Dafür sind vor allem korrekte und eindeutige Feldnamen wichtig.

Mit dem Hilfsprogramm ExtractFieldsInfo können alle Informationen zu Formularfeldern in eine XML-Datei extrahiert und für XML- und XPath-basierte Tests genutzt werden.

In den folgenden Abschnitten werden viele Tests auf Feldeigenschaften, Größe und natürlich auch die Inhalte von Feldern beschrieben. Je nach Anwendungkontext kann der eine oder andere Tests für Sie wichtig sein:

<!-- Tags for tests on fields: -->
  
<hasField   withName                (required) 
            
            width=".."              (optional, ...
            height=".."             ... but used together)
            unit=".."               (optional, default = MILLIMETER)

            hasMultipleLines".."    (optional)
            hasSingleLine".."       (optional)
            isEditable".."          (optional)
            isExportable".."        (optional)
            isHidden".."            (optional)
            isMultiSelectable".."   (optional)
            isPasswordProtected".." (optional)
            isReadOnly".."          (optional)
            isPrintable".."         (optional)
            isRequired".."          (optional)
            isSigned".."            (optional)
            isVisible".."           (optional)
    
            withType".."            (optional)
            
/>

...  continued
... continuation

<!-- Nested tags of <hasField /> are described later in this chapter -->
<!-- The constants for the attribute 'withType' ard described later in this chapter -->

<hasFields                  />
<hasNumberOfFields          />
<hasSignedSignatureFields   />
<hasUnsignedSignatureFields />

<!-- Nested tags of <hasFields /> are: -->

<allithoutDuplicateNames />
<allWithoutTextOverflow  />  1
<matchingXPath           />
<matchingXML             />


1

Dieser Test wird in Kapitel 3.12: „Formularfelder, Textüberlauf“ separat beschrieben.

Existenz

Mit dem folgenden Test können Sie prüfen, ob es überhaupt Felder gibt:

<testcase name="hasFields_NoFieldsAvailable"
          errorExpected="YES"
>
  <assertThat testDocument="acrofields/noAcrofieldDemo.pdf">
    <hasFields />
  </assertThat>
</testcase>

Anzahl

Wenn es lediglich wichtig ist, wieviele Formularfelder ein PDF-Dokument enthält, nutzen Sie das Tag <hasNumberOfFields />:

<testcase name="hasNumberOfFields">
  <assertThat testDocument="acrofields/simpleRegistrationForm.pdf">
    <hasNumberOfFields>4</hasNumberOfFields>
  </assertThat>
</testcase>

Möglicherweise ist es auch interessant, sicherzustellen, dass ein PDF-Dokument keine Felder (mehr) besitzt:

<testcase name="hasNumberOfFields_NoFieldsAvailable">
  <assertThat testDocument="acrofields/noAcrofieldDemo.pdf">
    <hasNumberOfFields>0</hasNumberOfFields>
  </assertThat>
</testcase>

Feldnamen

Da bei der Verarbeitung von PDF-Dokumenten über die Feldnamen auf deren Inhalte zugegriffen wird, muss sichergestellt sein, dass es das erwartete Feld auch gibt:

<testcase name="hasField_MultipleInvocation">
  <assertThat testDocument="acrofields/simpleRegistrationForm.pdf">
    <hasField withName="name" />
    <hasField withName="address" />
    <hasField withName="postal_code" />
    <hasField withName="email" />
  </assertThat>
</testcase>

Doppelte Feldnamen sind zwar nach der PDF-Spezifikation erlaubt, bereiten bei der Weiterverarbeitung von PDF-Dokumenten höchstwahrscheinlich aber Überraschungen. PDFUnit stellt deshalb ein Tag zur Verfügung, um die Abwesenheit doppelter Namen zu prüfen:

<testcase name="hasFields_AllWithoutDuplicateNames">
  <assertThat testDocument="acrofields/javaScriptForFields.pdf">
    <hasFields>
      <allWithoutDuplicateNames />
    </hasFields>
  </assertThat>
</testcase>

Inhalte von Feldern

Am einfachsten ist ein Test, der prüft, ob ein bestimmtes Feld überhaupt Daten enthält:

<testcase name="hasField_WithAnyValue">
  <assertThat testDocument="acrofields/javaScriptForFields.pdf">
    <hasField withName="ageField">
      <withAnyValue />
    </hasField>
  </assertThat>
</testcase>

Zur Überprüfung der Inhalte von Feldern stehen ähnliche Tags zur Verfügung, wie für die Überprüfung der Inhalte von Dokumenteneigenschaften:

<!-- Tags to check content in fields -->

<containing             />
<endingWith             />
<havingJavaScriptAction />
<matchingComplete       />
<matchingRegex          />
<notContaining          />
<notMatchingRegex       />  (useful, because regular expressions are 
                             not designed to find 'Not-Matches')
<startingWith           />
<withAnyValue           />
<withoutTextOverflow    />

Die nachfolgenden Beispiele sollen Ihnen ein paar Anregungen für die Verwendung dieser Tags geben:

<testcase name="hasField_MatchingComplete">
  <assertThat testDocument="acrofields/plugin-pdf_form_maker.pdf">
    <hasField withName="Text 1">
      <matchingComplete>
        Single Line Text
      </matchingComplete>
    </hasField>
  </assertThat>
</testcase>
<!-- This is a small test to protect fields against SQL injection. -->

<testcase name="hasField_NotContaining_SQLComment">
  <assertThat testDocument="acrofields/plugin-pdf_form_maker.pdf">
    <hasField withName="Text 1">
      <notContaining>--</notContaining>
      <notContaining>/></notContaining>
    </hasField>
  </assertThat>
</testcase>

Feldtypen

Formularfelder haben einen bestimmten Typ. Auch wenn die Bedeutung des Typs wohl nicht so groß ist, wie die des Namens, so gibt es trotzdem das Attribut withType="..", um den Typ eines Feldes zu überprüfen:

<testcase name="hasFieldWithType_MultipleInvocation">
  <assertThat testDocument="acrofields/plugin-pdf_form_maker.pdf">
    <hasField withName="Text 25"        withType="TEXT"        />
    <hasField withName="Check Box 7"    withType="CHECKBOX"    />
    <hasField withName="Radio Button 4" withType="RADIOBUTTON" />
    <hasField withName="Button 19"      withType="PUSHBUTTON"  />
    <hasField withName="List Box 1"     withType="LIST"        />
    <hasField withName="List Box 1"     withType="CHOICE"      />
    <hasField withName="Combo Box 5"    withType="CHOICE"      />
    <hasField withName="Combo Box 5"    withType="COMBO"       />
  </assertThat>
</testcase>

Die möglichen Feldtypen sind als Konstanten für das Attribut withType definiert. Die Namen der Konstanten entsprechen den gängigen, sichtbaren Elementen einer graphischen Anwendung. Innerhalb von PDF gibt es aber andere Typen. Weil die in einer Fehlermeldungen auftauchen können, gibt die folgende Liste die Zuordnung wider:

<!-- Mapping between PDFUnit constants and PDF-internal types. -->

PDFUnit-Constant    PDF-intern
-------------------------------
CHOICE          ->  "choice"
COMBO           ->  "choice"
LIST            ->  "choice"
CHECKBOX        ->  "button"
PUSHBUTTON      ->  "button"
RADIOBUTTON     ->  "button"
SIGNATURE       ->  "sig"
TEXT            ->  "text"

Das vorhergehende Code-Listing enthält bis auf das Signaturfeld alle Feldtypen, die überprüft werden können. Mit dem nächsten Beispiel wird auf Signaturfelder geprüft:

<testcase name="hasField_WithType_Signature">
  <assertThat testDocument="signed/sampleSignedPDFDocument.pdf">
    <hasField withName="Signature2" isSigned="YES" />
  </assertThat>
</testcase>

Ausführliche Tests zu Signaturen und Zertifikaten werden in Kapitel 3.21: „Signaturen und Zertifikate“ beschrieben.

Größe von Feldern

Falls die Größe von Formularfeldern wichtig ist, stehen für die Überprüfung von Länge und Breite die Attribute width=".." und height=".." zur Verfügung:

<testcase name="hasField_WidthAndHeight">
  <assertThat testDocument="acrofields/notExportableAcrofield.pdf">
    <hasField withName="Title of 'someField'"
              width="159"     (default is MILLIMETER)
              height="11"     (default is MILLIMETER) 
    />
  </assertThat>
</testcase>
<!-- 
  When @unit is omitted, the values of width are taken as "MILLIMETER".
 -->

<testcase name="hasField_Width">
  <assertThat testDocument="acrofields/notExportableAcrofield.pdf">
    <hasField withName="Title of 'someField'" width="159"                    />
    <hasField withName="Title of 'someField'" width="159"  unit="MILLIMETER" />
    <hasField withName="Title of 'someField'" width="15.9" unit="CENTIMETER" /> 1
    <hasField withName="Title of 'someField'" width="450"  unit="DPI72"      /> 2
    <hasField withName="Title of 'someField'" width="450"  unit="POINTS"     />
    <hasField withName="Title of 'someField'" width="6.26" unit="INCH"       />
  </assertThat>
</testcase>

1 2

Die Formate POINTS und DPI72 sind identisch.

Sie werden beim Erstellen eines Testes wahrscheinlich nicht die Maße eines Feldes kennen. Kein Problem, nehmen Sie eine beliebige Zahl für die Höhe und Breite und starten den Test. Die dann auftretende Fehlermeldung enthält die richtigen Werte in Millimetern.

Ob ein Text tatsächlich in ein Formularfeld passt, lässt sich durch die Größenbestimmung alleine nicht sicherstellen. Neben der Schriftgröße bestimmen auch die Worte am Zeilenende in Zusammenhang mit der Silbentrennung die Anzahl der benötigten Zeilen und damit die benötigte Höhe. Das Kapitel 3.12: „Formularfelder, Textüberlauf“ beschäftigt sich ausführlich mit diesem Thema.

Weitere Eigenschaften von Feldern

Formularfelder haben neben ihrer Größe noch weitere Eigenschaften, wie z.B. editable und printable. Viele dieser Eigenschaften können manuell gar nicht getestet werden. Deshalb gehören passende Tests in jedes PDF-Testwerkzeug. Das folgende Beispiele stellt das Prinzip dar:

<testcase name="hasField_Editable">
  <assertThat testDocument="acrofields/plugin-pdf_form_maker.pdf">
    <hasField withName="Combo Box 4" isEditable="YES" />
  </assertThat>
</testcase>

Insgesamt stehen folgende Attribute zur Überprüfung von Feldeigenschaften zur Verfügung:

<!-- Attributes to check field properties: -->

hasMultipleLines="YES"   
hasSingleLine="YES"

isEditable="YES/NO"
isExportable="YES/NO"
isHidden="YES/NO"
isMultiSelectable="YES/NO"
isPasswordProtected="YES"
isPrintable="YES/NO"
isReadOnly="YES/NO"
isRequired="YES/NO"
isSigned="YES"
isVisible="YES/NO"

Bei den Vergleichen spielen Whitespaces keine Rolle:

<testcase name="hasField_MultiLineField_MultipleInvocations">
  <assertThat testDocument="acrofields/plugin-pdf_form_maker.pdf">
    <hasField withName="Text multi" 
              hasMultipleLines="YES"
              isExportable="YES"
    >
      <matchingComplete>
         Multiple Line Support:
         First Line;
         Second Line;
      </matchingComplete>
    </hasField>
  </assertThat>
</testcase>

JavaScript Aktionen zur Validierung von Feldern

Wenn PDF-Dokumente Teil eines Workflows sind, unterliegen Formularfelder normalerweise bestimmten Plausibilitäten. Diese Plausibilitäten werden häufig durch eingebettetes JavaScript umgesetzt, um die Prüfungen schon zum Zeitpunkt der Eingabe auszuführen.

Mit PDFUnit kann geprüft werden, ob ein Formularfeld mit einer Aktion verknüft ist:

<testcase name="hasField_HavingJavaScriptAction_MultipleInvocation">
  <assertThat testDocument="acrofields/javaScriptForFields.pdf">
    <hasField withName="ageField" >
      <havingJavaScriptAction>Validate</havingJavaScriptAction>
    </hasField>
    <hasField withName="nameField" >
      <havingJavaScriptAction>Keystroke</havingJavaScriptAction>
    </hasField>
    <hasField withName="commentField" >
      <havingJavaScriptAction>Keystroke</havingJavaScriptAction>
    </hasField>
  </assertThat>
</testcase>

Tags für das Testen des eigentlichen JavaScript-Codes sind in Kapitel 3.13: „JavaScript“ beschrieben.

Unicode-Feldnamen

Wenn PDF-erstellende Werkzeuge Unicode-Sequenzen nicht richtig verarbeiten, wird es schwierig, diese Sequenzen in PDFUnit-Tests zu verwenden. Schwierig heißt aber nicht unmöglich. Das folgende Bild zeigt, dass der Name eines Feldes PDF-intern unglücklicherweise als UTF-16BE mit Byte-Order-Mark (BOM) gespeichert wird:

Auch wenn es schwierig ist, dieser Feldname kann als Java-Unicode-Sequenz getestet werden:

<!--
  The name of the field consists of UTF-16BE code represented as ASCII.
  Use a Unicode sequence for the field name to test it.
 -->

<testcase name="hasField_NameContainingUnicode_UTF16">
  <assertThat testDocument="unicode/unicode_inFieldnames.pdf">
    <hasField withName="\u00fe\u00ff\u0000F\u0000o\u0000r\u0000m\...\u0000]" />
  </assertThat>
</testcase>

<!-- The Unicode sequence in this example is abbreviated. -->

Mehr Informationen zu Unicode und Byte-Order-Mark liefern gute Artikel auf Wikipedia.

Feldinformationen gegen XML prüfen

Das Kapitel 9.4: „Feldeigenschaften nach XML extrahieren“ beschreibt, wie mit dem Extraktionsprogramm ExtractFieldsInfo Informationen über alle Formularfelder eines PDF-Dokumentes extrahiert werden können. Die dabei entstehende XML-Datei kann in Tests als Vergleichsinstanz benutzt werden:

<testcase name="hasField_MatchingXML">
  <assertThat testDocument="acrofields/plugin-pdf_form_maker.pdf">
    <hasFields>
      <matchingXML file="acrofields/plugin-pdf_form_maker.xml"/>
    </hasFields>
  </assertThat>
</testcase>

Feldinformation mit XPath validieren

Diese extrahierten XML-Daten von Formularfeldern können auch für XPath-Abfragen genutzt werden. Das ermöglicht es, Abhängigkeiten zwischen mehreren Feldern zu testen (cross-constraints). Die folgenden Beispiele vermitteln eine Idee von den Möglichkeiten:

<testcase name="hasField_MatchingXPath_NumberOfTextFields">
  <assertThat testDocument="acrofields/plugin-pdf_form_maker.pdf">
    <hasFields>
      <matchingXPath expr="count(//field[./@type='text']) = 43"/>
    </hasFields>
  </assertThat>
</testcase>

Das Tag <matchingXPath /> kann mehrfach in einem Test auftauchen:

<testcase name="hasField_MatchingXPath_MultipleInvocation">
  <assertThat testDocument="acrofields/plugin-pdf_form_maker.pdf">
    <hasFields>
      <matchingXPath expr="count(//field[./@type='text'])     = 43"/>
      <matchingXPath expr="count(//field[./@type='button'])   = 54"/>
      <matchingXPath expr="count(//field[./@type='choice'])   =  5"/>
      <matchingXPath expr="count(//field[./@type='signatur']) =  0"/>
    </hasFields>
  </assertThat>
</testcase>

Die beiden folgenden Beispiele überprüfen, ob es unsignierte Signaturfelder gibt:

<testcase name="hasField_MatchingXPath_HavingUnSignedSignatureFields_1">
  <assertThat testDocument="acrofields/certificateform.pdf">
    <hasUnsignedSignatureFields />
  </assertThat>
</testcase>
<testcase name="hasField_MatchingXPath_HavingUnSignedSignatureFields_2">
  <assertThat testDocument="acrofields/certificateform.pdf">
    <hasFields>
      <matchingXPath expr="count(//field[./@type='sig'][./@isSigned='false']) > 0"/>
    </hasFields>
  </assertThat>
</testcase>

PDFUnit verwendet den XSLT-Prozessor der aktuellen Java Runtime. Ob alle Syntaxelemente und Funktionen von XPath 2.0 unterstützt werden, entnehmen Sie daher bitte der Dokumentation Ihrer eingesetzten JRE bzw. des JDK. Einschränkungen seitens PDFUnit bestehen nicht.