„PDFium Component“ kuria teksto žymėjimo (markup) anotacijas – tai yra paryškinimą (highlight), pabraukimą (underline), perbraukimą (strikeout) bei vingiuotą pabraukimą (squiggly) – per TPdf.CreateAnnotation: nustatote HasAttachmentPoints := True įraše TPdfAnnotation ir užpildote jo AttachmentPoints keturkampį, o komponentas įrašo „QuadPoints“ įrašą, apibrėžtą ISO 32000-1 §12.5.6.10. Tai yra visa API sąsaja. Priežastis, kodėl atsirado šis straipsnis, yra tai, kas vyksta po ja, nes neapdorotų „PDFium“ iškvietimų grandinė turi klaidų režimą, sukeliantį patį nepatogiausią simptomą: FPDFAnnot_SetAttachmentPoints grąžina false ką tik sukurtoje anotacijoje, kiekvieną kartą, be jokio klaidos kodo ar užuominos. Tai yra kūrimo pusės kompanionas mūsų straipsniui apie esamų anotacijų skaitymą bei peržiūrą, kuris eina kita kryptimi per tas pačias struktūras
Derinimo (debugging) scenarijus visada yra toks pat. Sukuriate paryškinimo anotaciją, iškviečiate tvirtinimo taškų (attachment-points) nustatymą su indeksu 0, funkcija grąžina false ir pradedate abejoti savo koordinatėmis. Perkeliate taškus, apverčiate Y ašį, sukeičiate puslapio erdvę su įrenginio erdve. Nieko nepadeda, nes koordinatės niekada nebuvo problema. Problema yra C API indeksų semantika, ir kai ją suprantate, pataisymą sudaro vos dvi eilutės
Ką reiškia QuadPoints standarte ISO 32000-1
„QuadPoints“ yra 8×n skaičių masyvas, aprašantis n keturkampių, o ISO 32000-1 §12.5.6.10 reikalauja jo kiekvienoje teksto žymėjimo anotacijoje: kiekvienas keturkampis žymi žodį ar gretimų žodžių grupę, kuriai taikomas paryškinimas, pabraukimas ar perbraukimas. Anotacijos įrašas Rect vis dar egzistuoja, tačiau žymėjimo po-tipams jis tik apriboja sritį; keturkampiai (quads) yra tai, ką vizualizatorius (renderer) iš tikrųjų nupiešia. Keturkampis, o ne stačiakampis naudojamas todėl, kad tekstas gali būti pasuktas arba pakreiptas, todėl keturi kampai saugomi kaip keturi nepriklausomi taškai: x1 y1 x2 y2 x3 y3 x4 y4
Šių keturių taškų tvarka yra ta vieta, kur specifikacija ir reali praktika išsiskiria. Specifikacijos tekstas aprašo taškus kaip keturkampio apėjimą prieš laikrodžio rodyklę, tačiau pačios „Adobe“ vizualizatorius juos visada interpretavo Z šablonu: pirmiausia viršutinis kraštas iš kairės į dešinę, tada apatinis kraštas iš kairės į dešinę. Kadangi visi autoriai testavo pagal „Acrobat“, faktiškai kiekvienas vizualizatorius, įskaitant „PDFium“, seka Z šabloną. Failai, kurie seka specifikacijos pažodinę formuluotę, kai kuriose peržiūros programose atvaizduojami kaip subliuškę ar susukti paryškinimai. „PDFium“ struktūra FS_QUADPOINTSF koduoja būtent šią taisyklę: (x1,y1) yra viršutinis kairysis kampas, (x2,y2) – viršutinis dešinysis, (x3,y3) – apatinis kairysis, (x4,y4) – apatinis dešinysis puslapio koordinatėse, kur Y didėja į viršų. Sekite šią tvarką; vizualizatoriai yra atlaidūs daugeliui dalykų, bet sumaišytas keturkampis nėra vienas iš jų
Kodėl FPDFAnnot_SetAttachmentPoints grąžina false?
FPDFAnnot_SetAttachmentPoints sugenda su nauja anotacija, nes jos funkcija yra pakeisti keturkampį tam tikrame indekse, o ką tik sukurta anotacija turi nulį keturkampių, kuriuos būtų galima pakeisti. Parašas priima anotacijos valdiklį (handle), quad_index ir taškus; indeksas 0 nereiškia „pirmoji vieta, sukuriant ją prireikus“, jis reiškia „esamas keturkampis numeris 0“, ir kai FPDFAnnot_CountAttachmentPoints praneša apie 0, tokio keturkampio nėra ir iškvietimas grąžina false. Funkcija, kuri sukuria vietą (slot), yra FPDFAnnot_AppendAttachmentPoints. Kiekviena anotacija, sukurta per FPDFPage_CreateAnnot, prasideda nuo nulinio skaičiaus, todėl kūrimo kelias pirmiausia turi iškviesti Append, ir tik vėlesni atnaujinimai gali iškviesti Set
Tai palietė patį „PDFium Component“. Iš tiesų iki versijos v1.79.0 vidinė procedūra, kuria dalijasi CreateAnnotation ir SetAnnotation, naudojo įrašytą reikšmę FPDFAnnot_SetAttachmentPoints(Annotation, 0, ...), kas buvo teisinga atnaujinant esamą žymėjimo anotaciją ir garantuotai turėjo suveikti klaidingai su nauja, sukeliant EPdfException su pranešimu „Cannot set attachment points“. Sprendimas, pristatytas versijoje v1.79.1, šakojasi priklausomai nuo skaičiaus
// 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');
Tas pats šablonas galioja, jei tiesiogiai iškviečiate eksportuotas C funkcijas, ką komponentas leidžia daryti, nes visi FPDFAnnot_* įėjimo taškai pateikiami PDFium.pas modulyje. Kaskart, kai turite FPDF_ANNOTATION valdiklį ir norite įrašyti keturkampius, pirmiausia paklauskite FPDFAnnot_CountAttachmentPoints ir atitinkamai nukreipkite srautą. Jei ieškote, kodėl „FPDFAnnot_SetAttachmentPoints grąžina false“, ši patikros-ir-pridėjimo (count-then-append) šaka beveik neabejotinai yra jūsų atsakymas
Paryškinimo kūrimas su TPdf.CreateAnnotation
Kai komponentas atlieka Append-versus-Set nukreipimą už jus, paryškinimo kūrimas tampa tik įrašo užpildymu. Žemiau pateiktas pavyzdys sukuria A4 formato puslapį ir uždeda pusiau permatomą geltoną paryškinimą virš 200×20 taškų srities; atkreipkite dėmesį, kad keturkampis seka aukščiau aprašytą Z tvarką, o Rectangle nustatytas taip, kad apimtų keturkampį, todėl peržiūros programos, kurios tikrina pataikymą pagal Rect, elgiasi protingai
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;
Po-tipų pakeitimas kainuoja vieną eilutę. anUnderline, anStrikeout ir anSquiggly naudoja identišką įrašo formą, keturkampius ir visa kita, nes ISO 32000-1 traktuoja visus keturis kaip tą pačią anotacijų šeimą, besiskiriančią tik tuo, kaip papuošta keturkampio sritis. Po-tipai, kurie nėra teksto žymėjimai, pavyzdžiui, anSquare, anCircle bei anText, nustato savo poziciją tik iš Rectangle; palikite HasAttachmentPoints ties False šiems tipams, ir keturkampių mechanizmas niekada nebus paleistas
Kodėl AttachmentPoints[0] kompiuterizuojamas Delphi aplinkoje, bet sugenda FPC?
TQuadrilateralPoint yra deklaruojamas kaip array [1..4] of TPdfPoint (masyvas nuo 1), ir tai suklaidina kiekvieną, kurio pirštai automatiškai renka nuliu pagrįstą indeksavimą. Parašykite A.AttachmentPoints[0] ir Delphi „dcc32“ sukompiliuos tai be jokių priekaištų, nes ribų tikrinimas pagal numatytuosius nustatymus yra išjungtas; vykdymo metu ši išraiška tyliai nuskaito arba įrašo atmintį prieš pat masyvą, kas TPdfAnnotation įraše yra gretimas laukas. Jūsų paryškinimas gauna vieną sugadintą kampą arba sugadinamas gretimas laukas, ir jokia išimtis nekyla. „Free Pascal“ pagavo būtent šią klaidą mūsų demonstraciniuose šaltiniuose perkeliant į „Lazarus“: „fpc“ atlieka kompiuterizavimo laiko ribų tikrinimą konstantų indeksams ir visiškai atmetė AttachmentPoints[0..3], kas padėjo vienu metu atskleisti tiek vieneto paklaidos klaidą (off-by-one), tiek Set-versus-Append bibliotekos klaidą
Iš to seka du įpročiai. Indeksuokite keturkampį nuo 1 iki 4, suderindami su kampų tvarka aukščiau pateiktame kode, ir bent kartą sukurkite anotacijų kodą su įjungtu ribų tikrinimu – {$R+} Delphi aplinkoje arba bet kuriuo „fpc“ kūrimu, prieš juo pasitikėdami. Numatytasis „dcc32“ sukompiliavimas nėra įrodymas, kad indeksai teisingi; tai tik įrodymas, kad niekas nesugedo atmintyje, kuri atsitiktinai ten buvo
Keturkampio koordinačių gavimas iš tikro teksto
Įrašytos stačiakampių koordinatės tinka demonstracijai, tačiau gamybiniai paryškinimai seka tikrus spaudos ženklus (glyphs), o koordinatės turėtų būti gaunamos iš „PDFium“ teksto puslapio geometrijos, o ne iš spėjimų. Procedūros, aprašytos mūsų teksto išgavimo su „PDFium Component“ vadove, suteikia jums simbolių lygio ribojančius rėmelius toje pačioje puslapio koordinačių erdvėje, kurią naudoja keturkampiai, todėl paieškos atitikmuo tiesiogiai konvertuojamas į kampų taškus: kairysis pirmojo simbolio kraštas, dešinysis paskutinio, viršus bei apačia iš eilutės ribų. Jei patys generuojate tekstą ir norite sužinoti, kur atsiras eilutės prieš joms atsirandant, straipsnis apie teksto matavimą ir eilučių perkėlimą aprašo šių ribų apskaičiavimą iš anksto
Viena sąžininga riba: TPdfAnnotation įrašas turi vieną TQuadrilateralPoint, todėl vienas CreateAnnotation iškvietimas įrašo vieną keturkampį. Pasirinkimui, apimančiam tris eilutes, reikia trijų keturkampių – po vieną eilutei pagal §12.5.6.10, ir yra du būdai tai pasiekti. Paprastas būdas yra viena anotacija eilutei, kas vizualizuojama teisingai visur ir išlaiko komponento lygio API. Kompaktiškas būdas – viena anotacija, turinti tris keturkampius – reiškia anotacijos sukūrimą per komponentą ir tada pačių eksportuoto FPDFAnnot_AppendAttachmentPoints iškvietimą antram bei trečiam keturkampiui, kas veikia būtent todėl, kad Append sukuria vietas (slots), o ne jas pakeičia. Nebandykite pasiekti kelių keturkampių per pasikartojančius SetAttachmentPoints iškvietimus; kiekvienas indeksas už esamo skaičiaus ribų tiesiog grąžina false dėl tos pačios priežasties, kaip ir indeksas 0 su nauja anotacija
Įrašę patikrinkite tikroje peržiūros programoje, užuot pasikliovę grąžinimo kodais: atidarykite failą „Acrobat“ ar bet kurioje „PDFium“ pagrindu veikiančioje programoje ir įsitikinkite, kad žymėjimas atsiduria ant teksto, turi numatytą permatomumą bei išlieka po išsaugojimo ir pakartotinio įkėlimo ciklo. Šiame straipsnyje parodyti anotacijų tipai, keturkampių apdorojimas bei skaičių sekantis rašytuvas yra standartinės „PDFium Component“, skirtos Delphi, C++Builder ir Lazarus, dalys; produkto puslapyje rasite pilną anotacijų API aprašą kartu su likusia bibliotekos dalimi