Technical Article

Import and Export PDF Annotations as XFDF in Delphi

HotPDF exportiert und importiert PDF-Anmerkungen (Annotations) als XFDF über zwei Funktionen, die auf dem aktuell geladenen Dokument arbeiten: ExportLoadedAnnotationsToXFDF und ImportLoadedAnnotationsFromXFDF. XFDF ist das in ISO 19444-1 standardisierte XML-Format für den Austausch von Anmerkungen. Dieses Paar ermöglicht es einem Delphi- oder C++Builder-Programm, seine Kommentare an Acrobat oder ein beliebiges Review-Tool eines Drittanbieters zu übergeben und die markierten Ergebnisse wieder einzulesen, ohne den Seiteninhalt zu verändern, auf dem die Anmerkungen liegen

Stellen Sie sich die beiden Richtungen vor, die dies löst: Ein Prüfer öffnet Ihren erstellten Bericht in Acrobat, platziert einen roten Pfeil auf einer falsch ausgerichteten Abbildung, kreist eine falsche Summe ein, schreibt eine Notiz an den Rand und exportiert die Kommentare in eine kleine XFDF-Datei. Oder umgekehrt: Ihr Programm erstellt die Markierungen selbst und Sie müssen diese an jemanden senden, der HotPDF nicht nutzt. In beiden Fällen reisen die Anmerkungen als XML-Daten, die beide Seiten verstehen, während die PDF-Seiten Byte für Byte unverändert bleiben

Was ist der Unterschied zwischen FDF und XFDF?

FDF und XFDF übertragen dieselben Nutzdaten in zwei unterschiedlichen Syntaxen. Dieser Unterschied wird wichtig, sobald Sie entscheiden, welche Datei Sie an ein anderes Tool übergeben. FDF ist das ältere Forms Data Format, das in der PDF-Spezifikation selbst definiert ist: Es verwendet die PDF-Objektsyntax, weshalb eine FDF-Datei wie eine abgespeckte PDF-Datei aussieht und einen PDF-fähigen Parser zum Lesen erfordert. XFDF ist die XML-Entsprechung derselben Daten, die unabhängig als ISO 19444-1 standardisiert wurde, was bedeutet, dass jede XML-Bibliothek auf jeder Plattform sie öffnen, vergleichen oder generieren kann. Beide Formate können Formularfeldwerte in einem <fields>-Baum übertragen, was durch ISO 19444-1 Abschnitt 6.3 geregelt ist, sowie Anmerkungen in einem <annots>-Baum. HotPDF trennt diese Aufgaben, leitet Formulardaten über ExportLoadedFormToXFDF und reserviert ExportLoadedAnnotationsToXFDF für die <annots>-Seite. Wenn Sie Kommentare mit einem Webdienst, einem Java-Review-Server oder einem Skript austauschen, ist XFDF das Format, das die Gegenseite nicht zwingt, einen PDF-Parser einzubetten

Wie exportiert man PDF-Anmerkungen als XFDF in Delphi?

HotPDF exportiert Anmerkungen, indem es jede Seite des geladenen Dokuments durchläuft, ein XFDF-Element für jede unterstützte Anmerkung ausgibt und die Anzahl der geschriebenen Anmerkungen zurückgibt. Laden Sie zuerst das PDF und rufen Sie dann ExportLoadedAnnotationsToXFDF mit einem Zielpfad auf. Das Integer-Ergebnis ist die Anzahl der serialisierten Anmerkungen. Ein Ergebnis von null oder kleiner bedeutet, dass nichts exportiert und keine Datei geschrieben wurde, was Ihr Signal dafür ist, dass das Dokument keine Anmerkungen eines unterstützten Untertyps enthielt

var
  Pdf: THotPDF;
  Written: Integer;
begin
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.LoadFromFile('report-reviewed.pdf', '') > 0 then
    begin
      // Write one XFDF element per supported annotation on every page
      Written := Pdf.ExportLoadedAnnotationsToXFDF('comments.xfdf');
      if Written <= 0 then
        ShowMessage('No supported annotations were found');
    end;
  finally
    Pdf.Free;
  end;
end;

Das ausgegebene XFDF ist einfaches, lesbares XML. HotPDF schreibt eine <xfdf>-Wurzel im Namensraum ISO 19444-1, einen <annots>-Container und ein Kindelement pro Anmerkung mit ihrem nullbasierten Seitenindex, ihrer Farbe und ihrer Geometrie als Attribute oder Kindelemente. Eine Linie mit gelber Füllung und offener Pfeilspitze neben einem gefüllten Polygon wird beispielsweise wie folgt serialisiert:

<?xml version="1.0" encoding="UTF-8"?>
<xfdf xmlns="http://ns.adobe.com/xfdf/">
  <annots>
    <line page="0" start="72,700" end="220,700"
          color="#FF0000" interior-color="#FFFF00"
          head="OpenArrow" tail="None">
      <contents-richtext>Baseline looks off</contents-richtext>
    </line>
    <polygon page="0" color="#0000FF" interior-color="#CCE5FF">
      <vertices>72,120;180,120;180,200;72,200</vertices>
    </polygon>
  </annots>
</xfdf>

Wie Anmerkungsuntertypen auf XFDF-Elemente abgebildet werden

Jeder Anmerkungsuntertyp wird auf ein bestimmtes ISO 19444-1-Element mit eigener Geometriekonvention abgebildet, und HotPDF folgt diesen Strukturen, statt eigene zu erfinden. Linienanmerkungen tragen ein start- und ein end-Attribut mit den Koordinatenpaaren der beiden Endpunkte, die direkt aus dem L-Array der Anmerkung stammen, während die LE-Linienendstile zu den Attributen head und tail werden. Polygon- und Polylinienanmerkungen verschieben ihre Punktliste in ein <vertices>-Kindelement als durch Semikolons getrennte x,y-Paare und nicht in ein Attribut, da ein Reader, der das Kindelement erwartet, andernfalls verborgene Punkte übersehen würde. Freihandanmerkungen (Ink Annotations), die mehrere separate Striche enthalten können, betten ein <inklist>-Element mit einem <gesture>-Kindelement pro Strich ein, sodass eine aus mehreren Strichen bestehende Unterschrift den Weg als separate Gesten und nicht als eine verschmolzene Grafik übersteht

Rich-Text, Farben und Rahmenstile bleiben neben der Geometrie erhalten. Der Rich-Text-Körper einer Notiz wird als <contents-richtext>-Kind geschrieben. Die innere Füllung, die PDF im IC-Array speichert — z. B. die Farbe in einem Kreis, Quadrat, Polygon oder einer Pfeilspitze und die Füllung eines Schwärzungsbereichs —, wird als Attribut interior-color in der Form #RRGGBB übertragen. Rahmenbreite, Strichmuster und wolkenförmige Randeffekte werden den Attributen width, dashes, style und intensity zugewiesen, sodass ein wolkenförmig umrandeter Callout auch auf der Gegenseite als Wolke dargestellt wird. HotPDF behält zudem das Popup-Fenster einer Markup-Anmerkung bei, indem es die Geometrie des Popup-Kindes sowie dessen Zustand (geöffnet oder geschlossen) in das Popup-Verzeichnis der Anmerkung importiert. Es übernimmt auch die Status von Textanmerkungen (geöffnet und überprüft), sodass ein überprüftes Dokument nicht nur seine Formen, sondern auch die von den Prüfern genutzten Arbeitsablauf-Metadaten behält

Importieren von XFDF zurück in ein geladenes Dokument

HotPDF importiert XFDF, indem es das XML parst, eine neue Anmerkung für jedes Element über NewLoadedAnnotation erstellt, diese an die vom Element benannte Seite anhängt und die Anzahl der hinzugefügten Anmerkungen zurückgibt. Der Ablauf ist symmetrisch zum Export: Laden Sie die Basis-PDF-Datei, rufen Sie ImportLoadedAnnotationsFromXFDF mit der Datei des Prüfers auf und speichern Sie anschließend das geladene Dokument, um die neuen Markierungen zu sichern. Wenn die Datei fehlt oder das XML nicht geparst werden kann, gibt die Funktion null zurück und das geladene Dokument bleibt unverändert

var
  Pdf: THotPDF;
  Added: Integer;
begin
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.LoadFromFile('report.pdf', '') > 0 then
    begin
      Added := Pdf.ImportLoadedAnnotationsFromXFDF('comments.xfdf');
      if Added > 0 then
        Pdf.SaveLoadedDocument('report-annotated.pdf');
    end;
  finally
    Pdf.Free;
  end;
end;

Da jedes XFDF-Element seinen eigenen Seitenindex nennt, landen Anmerkungen genau auf den Seiten, auf denen sie erstellt wurden, selbst wenn Sie mehrere Dateien nacheinander importieren. Dies macht es sicher, Kommentare von mehr als einem Prüfer auf demselben geladenen Dokument vor einem einzigen Speichern zu sammeln. Das folgende Beispiel führt zwei Prüfer in eine gemeinsame Kopie zusammen. Wenn Sie die Anmerkungsobjekte im Code selbst erstellen und bearbeiten möchten, anstatt sie als Dateien auszutauschen, lesen Sie, wie HotPDF PDF-Anmerkungsobjekte direkt in Delphi erstellt und bearbeitet

var
  Pdf: THotPDF;
  Total, I: Integer;
  Files: array[0..1] of string;
begin
  Files[0] := 'alice-comments.xfdf';
  Files[1] := 'bob-comments.xfdf';
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.LoadFromFile('master.pdf', '') > 0 then
    begin
      Total := 0;
      for I := Low(Files) to High(Files) do
        Inc(Total, Pdf.ImportLoadedAnnotationsFromXFDF(Files[I]));
      if Total > 0 then
        Pdf.SaveLoadedDocument('master-merged.pdf');
    end;
  finally
    Pdf.Free;
  end;
end;

Was sauber hin- und zurückübertragen wird und was nicht

HotPDF überträgt jene Anmerkungsuntertypen hin und zurück, für die ISO 19444-1 eine Struktur vorgibt, und überspringt den Rest bewusst, statt ungültige Daten auszugeben, die ein Reader falsch interpretieren könnte. Das unterstützte Set umfasst die Markup-Typen, die die eigentliche Review-Arbeit dominieren: Textnotizen, freier Text, Linien, Quadrate, Kreise, Polygone, Polylinien, die vier Text-Markup-Typen (Hervorheben, Unterstreichen, Durchstreichen und Schlangellinie), Stempel, Freihandzeichnungen, Carets, Dateianhänge, Sound, Schwärzungen und Links — insgesamt achtzehn Untertypen. Eine Anmerkung, deren Untertyp nicht in dieser Liste enthalten ist, wird beim Export übersprungen. Da sie übersprungen und nicht leer geschrieben wird, erhöht sie auch nicht den von der Funktion zurückgegebenen Zähler

Einschränkungen bei Rich-Text

HotPDF behält den <contents-richtext>-Körper bei, sodass der formatierte Text und der einfache Inhalt übertragen werden. XFDF transportiert jedoch den Text und das Formatierungs-Markup eines Kommentars, nicht aber den gerenderten Appearance-Stream. Das bedeutet, dass die empfangende Anwendung das Popup-Fenster mit ihren eigenen Schriftarten und Layouts neu zeichnet, statt die exakten Pixel von HotPDF zu reproduzieren. Betrachten Sie den Austausch daher als inhalts- und absichtsgetreu, nicht als pixelgenau. Wenn Ihre formatierten Inhalte in XFA-Formulardaten statt in Anmerkungsströmen liegen, gelten andere Regeln, die unter Wie HotPDF mit XFA exData, Rich-Text und Hyperlinks umgeht behandelt werden

Die Behandlung auf Zeichenebene ist präzise, was genau das ist, was Sie wollen: HotPDF wendet beim Schreiben von Text die Maskierungsregeln nach ISO 19444-1 Abschnitt 5.8.2 an. Es kodiert XML-relevante Zeichen und Steuerbytes, sodass ein Kommentar, der ein kaufmännisches Und-Zeichen, eine spitze Klammer oder einen Zeilenumbruch enthält, wohlgeformtes XML erzeugt, das jeder standardkonforme Parser akzeptiert, und kehrt diese Regeln beim Import wieder um. Deshalb kommt eine aus einer Tabellenkalkulation kopierte Notiz samt Interpunktion intakt zurück, statt die Datei zu beschädigen

Der Austausch von Anmerkungen ist ein Teilbereich dessen, was die API für geladene Dokumente leisten kann, und lässt sich mit den übrigen Funktionen kombinieren: Importieren Sie das XFDF eines Prüfers, passen Sie die Seiten an oder bearbeiten Sie die Dokument-Metadaten, flachen Sie das Formular ab oder ändern Sie die Berechtigungen der Datei und exportieren Sie anschließend ein frisches XFDF für die nächste Runde. All dies wird in der standardmäßigen HotPDF-Komponente für Delphi und C++Builder ausgeliefert, deren Referenz die vollständige Abdeckung der Anmerkungsuntertypen und die zugehörigen XFDF-Formulardatenfunktionen dokumentiert