Odborný článok

Anotácie textových značiek s PDFium QuadPoints v Delphi

Komponent PDFium vytvára anotácie textových značiek, teda zvýraznenie, podčiarknutie, prečiarknutie a vlnovku, prostredníctvom metódy TPdf.CreateAnnotation: v zázname TPdfAnnotation nastavíte vlastnosť HasAttachmentPoints := True a vyplníte štvoruholník AttachmentPoints, na čo komponent zapíše položku QuadPoints definovanú v norme ISO 32000-1 §12.5.6.10. To je celé rozhranie API. Dôvod, prečo tento článok vznikol, je to, čo sa odohráva pod ním, pretože surová linka volaní PDFium ma chybový stav, ktorý vyvoláva ten najmenej užitočný príznak: funkcia FPDFAnnot_SetAttachmentPoints vráti hodnotu false pri čerstvo vytvorenej anotácii, a to zakaždým, bez chybového kódu a bez akéhokoľvek náznaku. Toto je sprievodný článok na strane vytvárania k nášmu článku o čítaní a recenzovaní existujúcich anotácií, ktorý prechádza rovnakými štruktúrami z opačnej strany

Scenár ladenia je vždy rovnaký. Vytvoríte anotáciu zvýraznenia, zavoláte priradenie bodov pripojenia s indexom 0, funkcia vráti false a vy začnete pochybovať o svojich súradniciach. Transponujete body, otočíte os Y, zameníte priestor stránky za priestor zariadenia. Nič z toho nepomáha, pretože súradnice nikdy neboli problémom. Problémom je indexová sémantika rozhrania C API, a keď ju raz pochopíte, oprava zaberie dva riadky

Čo znamenajú QuadPoints v norme ISO 32000-1

Položka QuadPoints je pole 8×n čísel popisujúcich n štvoruholníkov (quadrilaterals) a norma ISO 32000-1 §12.5.6.10 ju vyžaduje pri každej anotácii textových značiek: každý štvoruholník označuje slovo alebo skupinu susediacich slov, na ktoré sa vzťahuje zvýraznenie, podčiarknutie alebo prečiarknutie. Záznam Rect anotácie naďalej existuje, ale pri podtypoch značiek ohraničuje iba oblasť; vykresľovač reálne vykresľuje práve tieto štvoruholníky. Používa sa štvoruholník a nie obdĺžnik, pretože text môže byť otočený alebo skosený, takže štyri rohy sú uložené ako štyri nezávislé body: x1 y1 x2 y2 x3 y3 x4 y4

Poradie týchto štyroch bodov je miestom, kde sa špecifikácia a zavedená prax rozchádzajú. Text špecifikácie popisuje body ako obchádzanie štvoruholníka proti smeru hodinových ručičiek, ale vlastný vykresľovač od Adobe ich vždy interpretoval v tvare vzoru Z: najprv horná hrana zľava doprava, potom dolná hrana zľava doprava. Keďže každý autor testoval svoje riešenia proti Acrobatu, v podstate každý vykresľovač, vrátinu PDFium, nasleduje vzor Z, a súbory, ktoré sledujú doslovné znenie špecifikácie, sa v niektorých prehliadačoch zobrazujú ako zrútené alebo skrútené zvýraznenia. Štruktúra FS_QUADPOINTSF v PDFium kóduje presne túto konvenciu: (x1,y1) je ľavý horný roh, (x2,y2) pravý horný roh, (x3,y3) ľavý dolný roh a (x4,y4) pravý dolný roh v súradniciach stránky, kde os Y rastie smerom nahor. Dodržujte toto poradie a budete mať pokoj; vykresľovače sú benevolentné k mnohým veciam, ale nesprávne usporiadaný štvoruholník medzi ne nepatrí

Prečo FPDFAnnot_SetAttachmentPoints vracia false?

Metóda FPDFAnnot_SetAttachmentPoints zlyháva pri novej anotácii, pretože jej kontraktom je nahradiť štvoruholník na danom indexe a čerstvo vytvorená anotácia má nula štvoruholníkov na nahradenie. Signatúra preberá ukazovateľ anotácie, index quad_index a body; index 0 neznamená „prvú pozíciu, v prípade potreby ju vytvor“, znamená „existujúci štvoruholník číslo 0“ a keď FPDFAnnot_CountAttachmentPoints nahlási 0, neexistuje žiadny takýto štvoruholník a volanie vráti false. Funkcia, ktorá vytvára pozíciu, je FPDFAnnot_AppendAttachmentPoints. Každá anotácia vytvorená prostredníctvom FPDFPage_CreateAnnot začína s počtom nula, takže cesta vytvárania musí najprv zavolať Append a až následné aktualizácie môžu volať Set

Toto zasiahlo aj samotný komponent PDFium. Do verzie v1.79.0 mala interná rutina zdieľaná metódami CreateAnnotation a SetAnnotation natvrdo zapísané FPDFAnnot_SetAttachmentPoints(Annotation, 0, ...), čo bolo správne pre aktualizáciu existujúcej anotácie, ale zaručene to zlyhalo pri novej, čo sa prejavilo ako EPdfException so správou „Cannot set attachment points“. Oprava dodaná vo verzii v1.79.1 sa vetví podľa počtu

// Inside the component's annotation writer (v1.79.1+):
// a new annotation has no quad slots yet, so Append creates
// the first one; Set only replaces a slot that already exists
if FPDFAnnot_CountAttachmentPoints(Annotation) = 0 then
  Check(FPDFAnnot_AppendAttachmentPoints(Annotation, QuadPoints) <> 0,
    'Cannot set attachment points')
else
  Check(FPDFAnnot_SetAttachmentPoints(Annotation, 0, QuadPoints) <> 0,
    'Cannot set attachment points');

Vytvorenie zvýraznenia pomocou TPdf.CreateAnnotation

Keď za vás komponent rieši vetvenie medzi Append a Set, vytvorenie zvýraznenia sa redukuje na vyplnenie záznamu. Príklad nižšie vytvorí stránku formátu A4 a umiestni polopriehľadné žlté zvýraznenie nad oblasť s rozmermi 200×20 bodov; všimnite si, že štvoruholník sleduje vyššie opísané poradie Z a že vlastnosť Rectangle je nastavená tak, aby štvoruholník ohraničovala, čo zabezpečí správne správanie prehliadačov testujúcich zásahy voči Rect

var
  Pdf: TPdf;
  A: TPdfAnnotation;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.CreateDocument;
    Pdf.AddPage(0, 595, 842);

    FillChar(A, SizeOf(A), 0);
    A.Subtype := anHighlight;
    A.HasColor := True;
    A.Color := clYellow;
    A.ColorAlpha := $80;                     // 50% opacity
    A.HasAttachmentPoints := True;
    A.AttachmentPoints[1].X := 50;  A.AttachmentPoints[1].Y := 700; // top-left
    A.AttachmentPoints[2].X := 250; A.AttachmentPoints[2].Y := 700; // top-right
    A.AttachmentPoints[3].X := 50;  A.AttachmentPoints[3].Y := 680; // bottom-left
    A.AttachmentPoints[4].X := 250; A.AttachmentPoints[4].Y := 680; // bottom-right
    A.Rectangle.Left := 50;  A.Rectangle.Top := 700;
    A.Rectangle.Right := 250; A.Rectangle.Bottom := 680;
    A.ContentsText := 'Highlighted region';
    Pdf.CreateAnnotation(A);

    Pdf.SaveAs('highlighted.pdf');
  finally
    Pdf.Free;
  end;
end;

Zmena podtypu stojí jeden riadok. anUnderline, anStrikeout a anSquiggly majú rovnakú štruktúru záznamu, vrátane štvoruholníkov, pretože norma ISO 32000-1 ich považuje za rovnakú rodinu anotácií, ktoré sa líšia iba spôsobom dekorovania oblasti. Podtypy, ktoré nie sú textovými značkami, ako anSquare, anCircle a anText, sa umiestňujú výhradne pomocou Rectangle; pre ne ponechajte HasAttachmentPoints na False a logika štvoruholníkov sa nespustí

Prečo sa AttachmentPoints[0] v Delphi skompiluje, ale vo FPC zlyhá?

Typ TQuadrilateralPoint je deklarovaný ako array [1..4] of TPdfPoint, čo je pole indexované od 1, a to mätie každého, kto automaticky používa indexovanie od nuly. Ak napíšete A.AttachmentPoints[0], delphijský kompilátor dcc32 to skompiluje bez sťažností, pretože kontrola rozsahov (range checking) je predvolene vypnutá; za behu výraz potichu číta alebo zapisuje pamäť tesne pred poľom, čo je v zázname TPdfAnnotation susedné pole. Vaše zvýraznenie získa jeden chybný roh, prípadne sa poškodí susedné pole, a nič nevyvolá chybu. Free Pascal zachytil presne túto chybu v našich demo zdrojoch počas portovania na Lazarus: fpc vykonáva kontrolu rozsahov v čase kompilácie pre konštantné indexy a úplne odmietol AttachmentPoints[0..3], čo viedlo k odhaleniu chyby posunu o jedna a chyby knižnice Set-versus-Append

Z toho vyplývajú dva návyky. Indexujte štvoruholník od 1 do 4, v súlade s poradím rohov v kóde vyššie, a pred nasadením aspoň raz skompilujte kód anotácií so zapnutou kontrolou rozsahov — buď pomocou {$R+} v Delphi, alebo v akomkoľvek zostavení fpc. Úspešná kompilácia s predvoleným dcc32 nie je dôkazom, že indexy sú správne; je len dôkazom, že systém nespadol na pamäti, ktorá sa tam náhodou nachádzala

Získanie súradníc štvoruholníka z reálneho textu

Natvrdo zapísané obdĺžniky sú dobré pre demo, ale produkčné zvýraznenia kopírujú skutočné glyfy a súradnice by mali pochádzať z geometrie textovej stránky PDFium, nie z odhadov. Rutiny popísané v našom sprievodcovi extrakciou textu s komponentom PDFium vám poskytnú ohraničenia pre každý znak v rovnakom súradnicovom systéme stránky, aký používajú štvoruholníky, takže nájdený text sa priamo prevedie na rohové body: vľavo od prvého znaku, vpravo od posledného, s výškou a šírkou z rozsahov riadku. Ak text generujete sami a potrebujete vedieť, kam riadky spadnú ešte predtým, ako existujú, článok o meraní textu a zalamovaní slov popisuje výpočet týchto rozsahov vopred

Jeden úprimný limit: záznam TPdfAnnotation nesie iba jeden TQuadrilateralPoint, so jedno volanie CreateAnnotation zapíše jeden štvoruholník. Výber presahujúci tri riadky vyžaduje tri štvoruholníky, jeden na riadok, podľa §12.5.6.10, a máte dve cesty, ako to dosiahnuť. Jednoduchý spôsob je jedna anotácia na riadok, čo sa všade správne vykreslí a zachová API na úrovni komponentu. Kompaktný spôsob, kedy jedna anotácia nesie tri štvoruholníky, znamená vytvoriť anotáciu cez komponent a potom ručne zavolať exportovanú funkciu FPDFAnnot_AppendAttachmentPoints pre druhý a tretí štvoruholník, čo funguje práve preto, že Append vytvára pozície a nenahrádza ich. Nepokúšajte sa dosiahnuť viacero štvoruholníkov opakovaným volaním SetAttachmentPoints; každý index presahujúci aktuálny počet iba vráti false, z rovnakého dôvodu ako index 0 pri novej anotácii

Po zápise výsledok overte v reálnom prehliadači a nespoliehajte sa len na chybové kódy: otvorte súbor v Acrobate alebo akomkoľvek prehliadači založenom na PDFium a uistite sa, že značka sedí na texte, má požadovanú priehľadnosť a prežije cyklus uloženia a opätovného načítania. Typy anotácií, spracovanie štvoruholníkov a zápis zohľadňujúci počty popísané v tomto článku sú súčasťou štandardného komponentu PDFium pre Delphi, C++Builder a Lazarus; stránka produktu obsahuje kompletnú referenciu API pre anotácie a zvyšok knižnice

Domov · Hľadať · losLab.com