Teknisk artikel

Importer og eksporter PDF-annotationer som XFDF i Delphi

HotPDF eksporterer og importerer PDF-annotationer som XFDF via to funktioner, der fungerer på det aktuelt indlæste dokument, ExportLoadedAnnotationsToXFDF og ImportLoadedAnnotationsFromXFDF. XFDF er XML-annotationsudvekslingsformatet standardiseret som ISO 19444-1, og dette par lader et Delphi- eller C++Builder-program overdrage sine kommentarer til Acrobat eller et tredjepartsanmeldelsesværktøj og tage de markerede resultater tilbage, alt sammen uden at omskrive det sideindhold, som annotationerne sidder på

Forestil dig de to retninger, dette løser. En anmelder åbner din genererede rapport i Acrobat, placerer en rød pil på en fejlplaceret figur, cirkler en forkert total og skriver en note i margenen, og eksporterer derefter kommentarerne til en lille XFDF-fil. Eller det modsatte: Dit program producerer selv markeringerne, og du skal sende dem to en, hvis værktøj ikke er HotPDF. Uanset hvad rejser annotationerne som XML, som begge sider forstår, og PDF-siderne forbliver byte-for-byte, hvad de var

Hvad er forskellen på FDF og XFDF?

FDF og XFDF bærer den samme indholdspakke i to forskellige syntakser, og forskellen betyder noget i det øjeblik, du beslutter, hvilken fil du vil give til et andet værktøj. FDF er det ældre Forms Data Format defineret i selve PDF-specifikationen: Det bruger PDF-objektsyntaks, så en FDF-fil ligner en skrabet PDF og kræver en PDF-kompatibel parser for at blive læst. XFDF er XML-udtrykket af de samme data, standardiseret uafhængigt som ISO 19444-1, hvilket betyder, at ethvert XML-bibliotek på enhver platform kan åbne det, sammenligne det eller generere det. Begge formater kan bære formularfeltværdier i et <fields>-træ, som ISO 19444-1 paragraf 6.3 styrer, og annotationer i en <annots>-beholder; HotPDF deler disse ansvarsområder ved at dirigere formulardata via ExportLoadedFormToXFDF og reservere ExportLoadedAnnotationsToXFDF til <annots>-siden. Når du udveksler kommentarer med en webservice, en Java-anmeldelsesserver eller et script, XFDF er formatet, der ikke tvinger den anden side til at indlejre en PDF-parser

Hvordan eksporterer du PDF-annotationer som XFDF i Delphi?

HotPDF eksporterer annotationer by walking every page of the loaded document, emitting one XFDF element for each supported annotation, and returning the number of annotations written. Indlæs PDF'en først, og kald derefter ExportLoadedAnnotationsToXFDF med en målsti. Heltalsresultatet er antallet af serialiserede annotationer; et resultat på nul eller derunder betyder, at intet blev eksporteret, og at der ikke blev skrevet nogen fil, hvilket er dit signal om, at dokumentet ikke bar nogen annotationer af en understøttet undertype

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;

XFDF'en, der kommer ud, er almindelig, læsbar XML. HotPDF skriver en <xfdf>-rod i ISO 19444-1-navnerummet, en <annots>-beholder og ét barn pr. annotation med dets 0-baserede sideindeks, farve og geometri som attributter eller underelementer. En linje med et gult fyld og en åben pilspids, der sidder ved siden af en udfyldt polygon, serialiseres således

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

Hvordan annotationers undertyper kortlægges til XFDF-elementer

Hver annotationsundertype kortlægges til et specifikt ISO 19444-1-element med sin egen geometrikonvention, og HotPDF følger disse strukturer frem for at opfinde sine egne. Linje-annotationer bærer en start- og en end-attribut, der indeholder de to slutpunktkoordinatpar, taget direkte fra annotationens L-array, mens LE-linjeafslutningsstilene bliver til head- og tail-attributter. Polygon- og polylinje-annotationer flytter deres punktliste ind i et <vertices>-underelement som semikolon-separerede x,y-par, ikke en attribut, fordi en læser, der forventer underelementet, lydløst vil tabe punkter, der er gemt andre steder. Blækannotationer, som kan indeholde flere separate streger, indlejrer et <inklist>-element med ét <gesture>-underelement pr. streg, så en underskrift med flere streger overlever turen som særskilte bevægelser snarere end én samlet klat

Formateret tekst, farve og kantstil overlever sammen med geometrien. En notes formaterede tekstkrop skrives som et <contents-richtext>-underelement; det indvendige fyld, som PDF gemmer i IC-arrayet, malingen inde i en cirkel, firkant, polygon eller linjepil og fyldet af en redigeringsboks, overføres som en interior-color-attribut i #RRGGBB-form; og kantbredde, stiplet mønster og en sky-kant-effekt kortlægges til attributterne width, dashes, style og intensity, så en forklaring med skykant stadig læses som skyet i den anden ende. HotPDF bevarer også popup-vinduet, der er knyttet til en markup-annotation, ved at importere popup-underelementets geometri og dets åbne eller lukkede tilstand i annotationens Popup-ordbog, og den bærer tekstannotationens åbne- og anmeldelsestilstande, så et anmeldt dokument beholder ikke kun sine former, men også de workflow-metadata, som anmeldere stoler på

Import af XFDF tilbage på et indlæst dokument

HotPDF importerer XFDF ved at fortolke XML'en, oprette en frisk annotation for hvert element via NewLoadedAnnotation, knytte den til den side, elementet navngiver, og returnere, hvor mange annotationer den tilføjede. Arbejdsgangen er symmetrisk med eksport: Indlæs basis-PDF'en, kald ImportLoadedAnnotationsFromXFDF med anmelderens fil, og gem derefter det indlæste dokument for at bevare de nye markeringer. Hvis filen mangler, eller XML'en ikke kan fortolkes, returnerer funktionen nul, og det indlæste dokument efterlades uberørt

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;

Fordi hvert XFDF-element navngiver sit eget sideindeks, lander annotationer på de sider, de blev oprettet på, selv når du importerer flere filer i træk, hvilket gør det sikkert at samles kommentarer fra mere end én anmelder på det samme indlæste dokument før en enkelt gemning. Eksemplet nedenfor folder to anmeldere ind i én sammenlagt kopi. For opbygning og redigering af selve annotationsobjekterne i kode frem for at udveksle dem som filer, se hvordan HotPDF opretter og redigerer PDF-annotationsobjekter direkte fra Delphi

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;

Hvor der kører rent frem og tilbage, og hvad der ikke gør

HotPDF kører de annotationsundertyper, som ISO 19444-1 giver et hjem, rent frem og tilbage, og den springer bevidst resten over i stedet for at udsende noget, som en læser ville fejlfortolke. Det understøttede sæt dækker de markeringstyper, der dominerer reelt anmeldelsesarbejde: Tekstnotater, fritext, linje, firkant, cirkel, polygon, polylinje, de fire tekstmarkeringstyper (fremhævning, understregning, gennemstregning og bølgestreg), stempel, blæk og caret, samt filvedhæftning, lyd, redigering og link, i alt atten undertyper. En annotation, hvis undertype falder uden for denne liste, springes over ved eksport, og fordi den springes over frem for at blive skrevet tom, puster den ikke det antal op, som funktionen returnerer

Formateret tekst er det ærlige forbehold. HotPDF bevarer <contents-richtext>-kroppen, så den stylede tekst og det almindelige indhold klarer turen, men XFDF bærer en kommentars tekst og stil-markup, ikke en renderet udseendestrøm, så det modtagende program tegner popup'en igen med sine egne skrifttyper og layout frem for at reproducere HotPDF's præcise pixels. Behandl turen frem og tilbage som tro mod indhold og hensigt, ikke mod visning på skærmen ned til den enkelte pixel. Hvis dit stylede indhold lever i XFA-formulardata snarere end annotationsstrømme, gælder der andre regler, og hvordan HotPDF håndterer XFA exData, formateret tekst og hyperlinks dækker den separate sti

Håndteringen på tegnniveau er strengere, end den ser ud til, hvilket er præcis, hvad du ønsker. HotPDF anvender undvigelsesreglerne i ISO 19444-1 paragraf 5.8.2, når den skriver tekst, og koder de XML-betydningsfulde tegn og kontrolbytes, så en kommentar, der indeholder et ampersand, en vinkelparentes eller et linjeskift, producerer velformet XML, som enhver overensstemmende parser accepterer, og den vender de samme regler om ved import. Det er derfor, en note kopieret fra et regneark, tegnsætning og det hele, kommer intakt tilbage i stedet for at beskadige filen

Annotationsudveksling er en del af, hvad API'en til det indlæste dokument gør, og det kan sammensættes med resten: Importer en anmelders XFDF, juster siderne eller rediger dokumentets metadata, fladgør eller gen-tillad filen, og eksporter derefter en frisk XFDF til næste runde. Alt dette leveres i standarden HotPDF Component til Delphi og C++Builder, hvis reference dokumenterer den fulde dækning af annotationsundertyper og de ledsagende form-data-XFDF-funktioner