PDFium Component oppretter tekstmarkeringsannotasjoner (fremheving, understreking, gjennomstreking og bølgestrek) gjennom TPdf.CreateAnnotation: du setter HasAttachmentPoints := True på TPdfAnnotation-posten og fyller ut dens AttachmentPoints-firkant (quadrilateral), og komponenten skriver QuadPoints-oppføringen definert i ISO 32000-1 §12.5.6.10. Det er hele API-overflaten. Grunnen til at denne artikkelen eksisterer er det som skjer under overflaten, fordi den rå PDFium-kallkjeden har en feilmodus som produserer det minst nyttige symptomet i verktøysettet: FPDFAnnot_SetAttachmentPoints returnerer usant på en nyopprettet annotasjon, hver gang, uten feilkode og uten hint. Dette er den opprettelses-side-ledsageren til artikkelen vår om lesing og gjennomgang av eksisterende annotasjoner, som går den andre veien gjennom de samme strukturene
Feilsøkingsscenarioet er alltid det samme. Du oppretter en fremhevingsannotasjon, kaller attachment-points-setteren med indeks 0, funksjonen returnerer usant, og du begynner å tvile på koordinatene dine. Du bytter om på punktene, speilvender Y-aksen, bytter ut ark-rommet med enhets-rommet (device space). Ingenting av det hjelper, fordi koordinatene aldri var problemet. Problemet er indeks-semantikken i C-API-et, og når du først har sett den, er løsningen to linjer
Hva QuadPoints betyr i ISO 32000-1
QuadPoints er en tabell (array) med 8×n tall som beskriver n firkanter, og ISO 32000-1 §12.5.6.10 krever dette på enhver tekstmarkeringsannotasjon: hver firkant markerer et ord eller en gruppe sammenhengende ord som fremhevingen, understrekingen eller gjennomstrekingen gjelder for. Annotasjonens Rect-oppføring eksisterer fremdeles, men for markeringstyper avgrenser den bare området; det er firkantene (quads) som rendereren faktisk tegner. En firkant brukes i stedet for et rektangel fordi tekst kan roteres eller skråstilles, så de fire hjørnene lagres som fire uavhengige punkter: x1 y1 x2 y2 x3 y3 x4 y4
Rekkefølgen på disse fire punktene er der spesifikasjonen og den installerte programvarebasen skilles. Spesifikasjonsteksten beskriver punktene som en motklokke-omkrets av firkanten, men Adobes egen renderer har alltid tolket dem i et Z-mønster i stedet: først den øverste kanten fra venstre til høyre, deretter den nederste kanten fra venstre til høyre. Fordi alle utviklere testet mot Acrobat, følger i praksis alle renderere (inkludert PDFium) Z-mønsteret, og filer som følger spesifikasjonens ordrett definisjon rendres som kollapsede eller forvrengte fremhevinger i noen visningsprogrammer. PDFiums FS_QUADPOINTSF-struktur koder akkurat denne konvensjonen: (x1,y1) er øverste venstre hjørne, (x2,y2) øverste høyre, (x3,y3) nederste venstre, (x4,y4) nederste høyre, i sidekoordinater der Y vokser oppover. Følg den rekkefølgen og bli ferdig med det; renderere er overbærende med mye, men en rotete firkant er ikke en av dem
Hvorfor returnerer FPDFAnnot_SetAttachmentPoints usant?
FPDFAnnot_SetAttachmentPoints feiler på en ny annotasjon fordi dens kontrakt er å erstatte firkanten ved en gitt indeks, og en nyopprettet annotasjon har null firkanter å erstatte. Signaturen tar et annotasjonshåndtak, en quad_index og punktene; indeks 0 betyr ikke "den første plassen, og opprett den om nødvendig", det betyr "den eksisterende firkanten nummer 0", og når FPDFAnnot_CountAttachmentPoints rapporterer 0, finnes det ingen slik firkant og kallet returnerer usant. Funksjonen som oppretter en plass er FPDFAnnot_AppendAttachmentPoints. Enhver annotasjon opprettet via FPDFPage_CreateAnnot starter med et antall på null, så opprettelsesbanen må kalle Append først, og solo etterfølgende oppdateringer kan kalle Set
Dette rammet PDFium Component selv. Frem til og med v1.79.0 det interne rutinen som deles av CreateAnnotation og SetAnnotation hardkodet FPDFAnnot_SetAttachmentPoints(Annotation, 0, ...), noe som var riktig for oppdatering av en eksisterende markeringsannotasjon, men garantert ville feile for en ny en, og dukket opp som en EPdfException med meldingen 'Cannot set attachment points'. Løsningen, levert i v1.79.1, forgrener seg basert på antallet
Opprette en fremheving med TPdf.CreateAnnotation
Når komponenten tar seg av Append-eller-Set-rutingen for deg, opprettelsen av en fremheving reduseres til å fylle ut en post (record). Eksempelet nedenfor oppretter en A4-side og legger til en halvtransparent gul fremheving over et område på 200×20 punkter; merk at firkanten følger Z-rekkefølgen beskrevet ovenfor, og at Rectangle er satt til å omslutte firkanten, noe som sikrer at visningsprogrammer som tester treff mot Rect fungerer fornuftig
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;
Hvorfor kompilerer AttachmentPoints[0] i Delphi, men feiler i FPC?
TQuadrilateralPoint er deklarert som array [1..4] of TPdfPoint, en 1-basert tabell, og det skaper trøbbel for alle som automatisk bruker null-basert indeksering. Skriv A.AttachmentPoints[0], og Delphis dcc32 vil kompilere det uten klager, fordi grensekontroll (range checking) er av som standard; ved kjøring vil uttrykket i stillhet lese eller skrive til minnet rett før tabellen, som i en TPdfAnnotation-post er et tilstøtende felt. Fremhevingen din får et ubrukelig hjørne, eller et nabofelt blir korrupt, uten at det utløses noen feil. Free Pascal fanget opp akkurat denne feilen i våre egne demokilder under Lazarus-porteringen: FPC utfører grensekontroll ved kompilering på konstante indekser og avviste AttachmentPoints[0..3] kontant, noe som var måten både off-by-one-feilen og Set-versus-Append-biblioteksfeilen ble avdekket på sammen
Hente firkantkoordinater fra ekte tekst
Hardkodede rektangler er greit for en demo, men fremhevinger i produksjon sporer faktiske tegn (glyphs), og koordinatene bør komme fra PDFiums tekstside-geometri i stedet for gjetting. Rutinene dekket i veiledningen vår for tekstuthenting med PDFium Component gir deg tegn-for-tegn avgrensningsbokser i samme sidekoordinatrom som firkantene bruker, slik at et søketreff kan konverteres direkte til hjørnepunkter: venstre for det første tegnet, høyre for det siste, topp og bunn fra linjens utstrekning. Hvis du genererer teksten selv og trenger å vite hvor linjene vil havne før de eksisterer, dekker artikkelen om tekstmåling og tekstbryting hvordan du beregner disse utstrekningene på forhånd
Én reell grense: TPdfAnnotation-posten bærer bare en enkelt TQuadrilateralPoint, så ett CreateAnnotation-kall skriver én firkant. Et utvalg som spenner over tre linjer trenger tre firkanter (en per linje i henhold til §12.5.6.10), og du har to måter å oppnå det på. Den enkle måten er én annotasjon per linje, noe som rendres riktig overalt og beholder API-et på komponentnivå. Den kompakte måten, der én annotasjon bærer tre firkanter, betyr at du oppretter annotasjonen via komponenten og deretter kaller den eksporterte FPDFAnnot_AppendAttachmentPoints selv for den andre og tredje firkanten, noe som fungerer nettopp fordi Append oppretter plasser i stedet for å erstatte dem. Ikke prøv å oppnå flere firkanter gjennom gjentatte SetAttachmentPoints-kall; enhver indeks forbi gjeldende antall returnerer bare usant, av samme grunn som indeks 0 gjorde på den nye annotasjonen
Etter skriving bør du verifisere i et ekte visningsprogram i stedet for å stole på returkodene: åpne filen i Acrobat eller en hvilken som helst PDFium-basert visning, og bekreft at markeringen havner på teksten, har den tiltenkte ugjennomsiktigheten og overlever en lagre-og-laste-tur-retur. Annotasjonstypene, firkanthåndteringen og den antalls-bevisste skriveren som er vist her, er alle en del av standarden for PDFium Component for Delphi, C++Builder og Lazarus; produktsiden inneholder den fullstendige API-referansen for annotasjoner sammen med resten av biblioteket