Technisch artikel

PDF-annotaties importeren en exporteren als XFDF in Delphi

HotPDF exporteert en importeert PDF-annotaties als XFDF via twee functies die inwerken op het momenteel geladen document, ExportLoadedAnnotationsToXFDF en ImportLoadedAnnotationsFromXFDF. XFDF is het XML-formaat voor annotatie-uitwisseling gestandaardiseerd als ISO 19444-1, and dit tweetal stelt een Delphi- of C++Builder-programma in staat zijn opmerkingen aan Acrobat of een reviewtool van derden te overhandigen en de gemarkeerde resultaten terug te ontvangen, allemaal zonder de paginacontent waarop de annotaties liggen te herschrijven

Stel u de twee richtingen voor die dit oplost. Een revisor opent uw gegenereerde rapport in Acrobat, plaatst een rode pijl op een verkeerd uitgelijnde figuur, omcirkelt een foutief totaal en typt een notitie in de marge, en exporteert de opmerkingen vervolgens naar een klein XFDF-bestand. Of andersom: uw programma produceert zelf de markeringen en u moet deze verzenden naar iemand wiens tool niet HotPDF is. In beide gevallen reizen de annotaties als XML die beide partijen begrijpen, en blijven de PDF-pagina's byte-voor-byte wat ze waren

Wat is het verschil tussen FDF en XFDF?

FDF en XFDF dragen dezelfde lading in twee verschillende syntactische vormen, en het onderscheid is van belang zodra u beslist welk bestand u aan een andere tool overhandigt. FDF is het oudere Forms Data Format dat binnen de PDF-specificatie zelf is gedefinieerd: het gebruikt de PDF-objectsyntaxis, waardoor een FDF-bestand eruitziet als een uitgeklede PDF en een PDF-bewuste parser nodig heeft om te worden gelezen. XFDF is de XML-uitdrukking van dezelfde gegevens, onafhankelijk gestandaardiseerd als ISO 19444-1, wat betekent dat elke XML-bibliotheek op elk platform het kan openen, vergelijken (diffen) of genereren. Beide formaten kunnen formulierveldwaarden dragen in een <fields>-boomstructuur, wat wordt beheerst door ISO 19444-1 clausule 6.3, en annotaties in een <annots>-boomstructuur; HotPDF splitst die verantwoordelijkheden, waarbij formuliergegevens via ExportLoadedFormToXFDF worden geleid en ExportLoadedAnnotationsToXFDF wordt gereserveerd voor de <annots>-kant. Wanneer u opmerkingen uitwisselt met een webservice, een Java-reviewserver of een script, is XFDF het formaat dat de andere partij niet dwingt om een PDF-parser in te bedden

Hoe exporteert u PDF-annotaties als XFDF in Delphi?

HotPDF exporteert annotaties door elke pagina van het geladen document te doorlopen, één XFDF-element uit te sturen voor elke ondersteunde annotatie en het aantal geschreven annotaties te retourneren. Laad eerst de PDF en roep vervolgens ExportLoadedAnnotationsToXFDF aan met een doelpad. Het integer-resultaat is het aantal geserialiseerde annotaties; een resultaat van nul of minder betekent dat er niets is geëxporteerd en er geen bestand is geschreven, wat uw signaal is dat het document geen annotaties van een ondersteund subtype bevatte

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;

De XFDF die eruit komt is gewone, leesbare XML. HotPDF schrijft een <xfdf>-root in de ISO 19444-1-naamruimte, een <annots>-container, en één kind per annotatie met zijn 0-gebaseerde pagina-index, kleur en geometrie als attributen of subelementen. Een lijn met een gele vulling en een open pijlpunt, liggend naast een gevulde polygoon, wordt als volgt geserialiseerd

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

Hoe annotatie-subtypen verwijzen naar XFDF-elementen

Elk annotatiesubtype verwijst naar een specifiek ISO 19444-1-element met zijn eigen geometrie-conventie, en HotPDF volgt die structuren in plaats van zijn eigen te verzinnen. Lijnannotaties bevatten een start- en een end-attribuut die de coördinatenparen van de twee eindpunten bevatten, rechtstreeks overgenomen uit de L-array van de annotatie, terwijl de LE-lijneindestijlen head- en tail-attributen worden. Polygoon- en polylijnannotaties verplaatsen hun puntenlijst naar een <vertices>-subelement als door puntkomma's gescheiden x,y-paren, niet als een attribuut, omdat een lezer die het subelement verwacht, punten die elders verborgen zijn, stilletjes zal negeren. Inktannotaties, die meerdere afzonderlijke streken kunnen bevatten, nesten een <inklist>-element met één <gesture>-subelement per streek, zodat een handtekening met meerdere streken de reis overleeft als afzonderlijke gebaren in plaats van als één samengevoegde blob

Rich text, kleur- en randstijlen overleven naast de geometrie. De rich-text hoofdtekst van een notitie wordt geschreven als een <contents-richtext>-subelement; de binnenste vulling die de PDF opslaat in de IC-array, the verf in een cirkel, vierkant, polygoon of pijlpunt en de vulling van een redactiekader, komt over als een interior-color-attribuut in de vorm #RRGGBB; en de randbreedte, het stippelpatroon en een wolkachtig randeffect verwijzen naar de attributen width, dashes, style en intensity, zodat een wolkachtige callout nog steeds als wolkachtig wordt gelezen aan de andere kant. HotPDF behoudt ook het pop-upvenster dat aan een markering is gekoppeld, waarbij de geometrie van het pop-up-subelement en de geopende of gesloten status ervan worden geïmporteerd in het Popup-dictionary van de annotatie, en het draagt de geopende en beoordelingsstatussen van de tekstannotatie over, zodat een beoordeeld document niet alleen zijn vormen behoudt, maar ook de werkstroom-metadata waarop revisoren vertrouwen

XFDF weer importeren in een geladen document

HotPDF importeert XFDF door de XML te parsen, een nieuwe annotatie aan te maken voor elk element via NewLoadedAnnotation, deze te koppelen aan de pagina die het element noemt, en te retourneren hoeveel annotaties het heeft toegevoegd. De werkstroom is symmetrisch met de export: laad de basis-PDF, roep ImportLoadedAnnotationsFromXFDF aan met het bestand van de revisor, en sla het geladen document vervolgens op om de nieuwe markeringen te behouden. Als het bestand ontbreekt of de XML niet kan worden geparst, retourneert de functie nul en blijft het geladen document onaangetast

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;

Omdat elk XFDF-element zijn eigen pagina-index specificeert, landen annotaties op de pagina's waarvoor ze zijn geschreven, zelfs wanneer u meerdere bestanden achter elkaar importeert. Dit maakt het veilig om opmerkingen van meer dan één revisor te verzamelen in hetzelfde geladen document vóór een enkele opslag. Het onderstaande voorbeeld voegt opmerkingen van twee revisoren samen in één master-kopie. Voor het bouwen en bewerken van annotatie-objecten in code in plaats van het uitwisselen ervan via bestanden, zie hoe HotPDF PDF-annotatie-objecten rechtstreeks vanuit Delphi maakt en bewerkt

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;

Wat wisselt schoon uit, en wat niet

HotPDF wisselt de annotatiesubtypen uit die in ISO 19444-1 zijn ondergebracht, en slaat de rest bewust over in plaats van iets te produceren dat een lezer verkeerd zou interpreteren. De ondersteunde set omvat de markeringstypes die het echte reviewwerk domineren: tekstnotities, vrije tekst, lijn, vierkant, cirkel, polygoon, polylijn, the vier tekstmarkeringstypes (markeren, onderstrepen, doorhalen en golvend), stempel, inkt en caret, plus bestandsbijlage, geluid, redactie en link, achttien subtypen in totaal. Een annotatie waarvan het subtype buiten die lijst valt, wordt bij de export overgeslagen, en omdat deze wordt overgeslagen in plaats van leeg geschreven, verhoogt dit de teller die de functie retourneert niet

Rich text is het eerlijke voorbehoud. HotPDF behoudt de <contents-richtext>-body zodat de opgemaakte tekst en de platte inhoud de reis maken, maar XFDF draagt de tekst- en stijlopmaak van een opmerking, niet een gerenderde appearance-stream, dus de ontvangende applicatie tekent de pop-up opnieuw met zijn eigen lettertypen en lay-out in plaats van de exacte pixels van HotPDF te reproduceren. Behandel de uitwisseling als getrouw aan de inhoud en intentie, niet aan de weergave op het scherm tot op de pixel nauwkeurig. Als uw opgemaakte inhoud in XFA-formuliergegevens staat in plaats van in annotatiestreams, gelden er andere regels, en hoe HotPDF omgaat met XFA exData, rich text en hyperlinks behandelt dat afzonderlijke pad

De verhandling op tekenniveau is strikter dan het lijkt, wat precies is wat u wilt. HotPDF past de escaping-regels van ISO 19444-1 clausule 5.8.2 toe wanneer het tekst schrijft, en codeert de XML-significante tekens en besturingsbytes zodat een opmerking met een ampersand, een punthaakje of een regeleinde goed gevormde XML oplevert die elke compatibele parser accepteert, en het keert diezelfde regels om bij import. Dat is de reden waarom een opmerking die is geplakt vanuit een spreadsheet, inclusief interpunctie en al, intact terugkomt in plaats van het bestand te beschadigen

Annotatie-uitwisseling is één facet van wat de geladen-document-API doet, en het is te combineren met de rest: importeer de XFDF van een revisor, pas de pagina's aan of bewerk de documentmetadata, vlak het bestand af of wijzig de machtigingen, en exporteer vervolgens een verse XFDF voor de volgende ronde. Dit alles wordt geleverd in de standaard HotPDF-component voor Delphi en C++Builder, waarvan de referentie de volledige dekking van annotatiesubtypen en de bijbehorende formuliergegevens-XFDF-functies documenteert