En PDF-annotasjon er en ordbok festet til en side, ikke et merke tegnet på den. ISO 32000-1 §12.5 definerer omtrent to dusin undertyper, og hver bærer en /Subtype, et rektangel i sidekoordinater, et sett med flagg, og som regel en visningsstrøm som avgjør hva en fremviser faktisk maler. Undertypene betyr ikke alle det samme for en person som gjennomgår et dokument. En Highlight og et Ink-strøk er kommentarer; en Link er navigasjon; en Popup er det lille vinduet som åpnes når du klikker på en gul lapp, lagret som sitt eget objekt og pekt på av en forelder. Svar er fullstendige Text-annotasjoner som refererer til kommentaren de svarer på gjennom en svar-til-oppføring. Så annotasjonsarrayet på sidenivå er ikke gjennomgangslesernes liste over kommentarer. Det er en flat sekk som inneholder kommentarer, rørleggingen som kobler dem sammen, og flere ting ingen gjennomgangsleser ville kalt en kommentar i det hele tatt. Et panel som behandler arrayet som kommentarlisten, vil være uenig med hver eneste andre fremviser kunden kjører
Å bygge en arbeidsflyt for annotasjonsgjennomgang på PDFium Component, den PDFium-baserte VCL/LCL-komponenten for Delphi, C++Builder og Lazarus, betyr å konsentrere seg om punktene hvor det gapet mellom det rå arrayet og det menneskelige synet skaper trøbbel: telling, indeksering, omfarging av merker motoren allerede har frosset, sletting uten å etterlate spøkelser, og å legge til egne merker
Hvorfor tellingen din aldri matcher Acrobats kommentarpanel
Åpne en markert kontrakt i fremviseren din og i Acrobat side om side, og totalsummene stemmer sjelden overens. Acrobat viser en kuratert visning: markering gruppert i svartråder, popups foldet inn i notatene de hører til, lenker og skjemakontroller utelatt. Det rå arrayet holder alt sammen udifferensiert, så en naiv telling går høy på noen måter og lav på andre samtidig
Popups blåser opp totalen, fordi hver gule lapp følger med et separat Popup-objekt, og å telle begge dobler notatet. Svar trekker den ned hvis du filtrerer på synlige merker, siden et svar er en Text-annotasjon med ingenting malt før noen utvider tråden, og å droppe det mister diskusjonen. Flaggene Hidden og NoView tar en annotasjon vekk fra skjermen uten å ta den ut av arrayet, så en flagg-blind telling inkluderer merker brukeren ikke kan se. Link-annotasjoner sitter i det samme arrayet som kommentarene og hører hverken hjemme i tellingen eller listen. Bestem tellingsregelen før du skriver løkken, og skriv ned beslutningen, for "hvorfor viser panelet ditt et annet tall enn Acrobat" er den første saken en gjennomgangsfunksjon opptjener
Indekser alt én gang, og parse aldri en side på nytt
Én designregel styrer alt som følger: filtrering etter forfatter, type eller side skal aldri parse sideobjekter på nytt. På et dokument på 300 sider med tung markering gjør ny-parsing ved hver nedtrekksendring panelet til noe som stotrer i sekunder om gangen. Komponenten eksponerer AnnotationCount og den indekserte Annotation[]-egenskapen, begge avgrenset til den for øyeblikket lastede siden, og TPdfAnnotation-posten de gir tilbake, bærer det en listevisning trenger: Subtype, Flags, Color, Rectangle, ContentsText, AuthorText. Det riktige grepet er å feie gjennom hver side én gang ved åpningstidspunktet og holde din egen flate indeks:
procedure TReviewPanel.BuildIndex;
var
PageNo, i: Integer;
A: TPdfAnnotation;
begin
FItems.Clear;
for PageNo := 1 to Pdf.PageCount do
begin
Pdf.PageNumber := PageNo;
for i := 0 to Pdf.AnnotationCount - 1 do
begin
A := Pdf.Annotation[i];
// Behold bare undertyper relevante for gjennomgangsleseren; noter siden og
// indekspar fordi alle senere endringer adresseres av det
if A.Subtype in [anText, anHighlight, anInk] then
FItems.Add(TReviewItem.Create(PageNo, i,
A.AuthorText, A.ContentsText, A.Rectangle, A.Color));
end;
end;
end;
Paret verdt å understreke er (PageNo, i). Hver senere endring, enten en omfarging eller en sletting, adresseres etter sidenummer pluss annotasjonsindeks, og indeksen er skjør: å fjerne en annotasjon nummererer om alt etter den på den siden. Så planlegg å bygge den berørte sidens oppføringer på nytt etter enhver sletting i stedet for å lappe indeksnumre på plass. Ombyggingen koster ett millisekund. En foreldet indeks sletter derimot feil gjennomgangslesers kommentar, noe som er den typen feil som svekker tilliten til hele funksjonen
Tråding fortjener en plass i indeksen selv om den første utgivelsen din bare teller svar fremfor å vise dem. Grupper elementer etter foreldrereferansen deres mens du har siden åpen, slik at panelet senere kan folde en tråd slik Acrobat gjør. Å rekonstruere den grupperingen lat under rulling ødelegger hele poenget med å indeksere én gang, fordi det åpner sider du allerede har betalt for å parse, på nytt. Geometri vil ha den samme disiplinen. Rectangle i hver post er i sidekoordinater, og å konvertere den til visningskoordinater hører hjemme i én delt hjelpefunksjon, ikke spredt gjennom koden. Paneler utvikler koordinatfeil når utvalg, treffdeteksjon og maling hver oppfinner sin egen zoom- og rotasjonsmatematikk; led alle tre gjennom én enkelt konvertering, så holder en utheving, raden i listen og klikkmålet seg festet til den samme blekket
Omfarging av markering og vetoretten til visningsstrømmen
Å endre en utheving fra gul til rav høres ut som en enlinjer, og noen ganger er det det. Fangsten er ISO 32000-1 §12.5.5. Når en annotasjon bærer en /AP-visningsstrøm, maler en samsvarende fremviser den ferdigbygde strømmen og behandler fargeoppføringen i ordboken som dødt metadata. Acrobat skriver visningsstrømmer for praktisk talt alt den lager, så de fleste annotasjoner som kommer fra kunder, er allerede i denne tilstanden, og fargen du så selvsikkert satte, når aldri skjermen. Omfarging er en les-endre-skriv-operasjon gjennom Annotation[]-egenskapen, og komponenten er ærlig om konflikten: når motoren nekter å la en ordbokfarge overstyre en bakt-inn visning, kaster skrivingen EPdfError
A := Pdf.Annotation[Item.Index];
A.HasColor := True;
A.Color := $0000B0FF; // rav
A.ColorAlpha := 160;
try
Pdf.Annotation[Item.Index] := A;
except
on EPdfError do
begin
// Annotasjonen eier en ferdigrendret /AP-strøm; ordbok-
// fargen alene kan ikke endre hva fremvisere maler
Item.AppearanceLocked := True;
StatusBar.SimpleText := 'Color is fixed by the annotation appearance';
end;
end;
Fang det unntaket hver gang, og behandle det som informasjon fremfor feil. Hopp over vaktsjekken, og panelet ditt viser muntert rav-farge i sin egen liste mens siden fortsetter å male gul; brukeren melder det inn uker senere som "fremviseren din ignorerer redigeringene mine," og du bruker en ettermiddag på å mislykkes med å gjenskape det på en fil som tilfeldigvis ikke har noen visningsstrøm. Når du først vet at visningen er låst, har du to ærlige svar: omfarg din egen utvalgsoverlegg i stedet for annotasjonen, slik at gjennomgangsleseren i det minste ser uthevingen de valgte, eller merk raden som visningslåst slik at ingen forventer at endringen fester seg
Å slette annotasjoner uten å etterlate spøkelser
DeleteAnnotation fjerner objektet fra den gjeldende sidens annotasjonstre, men den lar det bufrede sideraster være i fred. Mal umiddelbart etter kallet, og den slettede uthevingen er fortsatt på skjermen, sittende i et bitkart som ikke lenger matcher dokumentmodellen bak det. Løsningen er å behandle omrendringen som en del av slettingen, ikke et trinn den kallende koden kan glemme:
Pdf.PageNumber := Item.PageNo;
Pdf.DeleteAnnotation(Item.Index); // kaster EPdfError ved feil
Bmp := Pdf.RenderPage(0, 0, ViewWidth, ViewHeight, ro0, [reAnnotations]);
try
PaintPageBitmap(Bmp);
finally
Bmp.Free; // RenderPage overfører eierskapet av bitkartet til den kallende koden
end;
RebuildPageEntries(Item.PageNo); // indeksene etter Item.Index er forskjøvet
To detaljer i den blokken er lett å gjøre feil. reAnnotations-alternativet må være til stede, ellers dropper det nye rasteret hver gjenværende annotasjon, og siden ser ut som om du visket ut hele kommentarsettet i stedet for ett merke. Og Bmp.Free er ikke valgfritt: den funksjonsstil-RenderPage-overlastingen overfører eierskapet av bitkartet til den kallende koden, så en manglende frigjøring lekker et helsides raster ved hver eneste sletting, noe en gjennomgangsleser som jobber gjennom et langt dokument, vil gjøre om til reelt minnepress innen få minutter
Å legge til gjennomgangslesermerker fra ditt eget UI
Å opprette annotasjoner går gjennom CreateAnnotation, som tar en utfylt TPdfAnnotation-post (undertype, rektangel, farge, innhold, forfatter) og fester den til den gjeldende siden. En gul lapp, undertype anText, er det enkle tilfellet: sett posisjonen, innholdet og forfatteren, og du er ferdig. Ink-annotasjoner er der folk går i fellen. Postens rektangel avgrenser bare tegningen; selve strøkene er arrayer av punkter som må festes separat gjennom motorens ink-strøk-kall, FPDFAnnot_AddInkStroke matet med FS_POINTF-data, fanget fra mus- eller penninndata ett strøk om gangen. Bygg en ink-annotasjon fra et rektangel og ingenting annet, og du får en tom krusedull som rendres som tomt rom, noe som ser ut som en feil i motoren og egentlig er en halvferdig annotasjon
Avklar forfatterpolicyen i samme åndedrag. Hvert merke UI-et ditt lager, bør bære en konsistent AuthorText, fordi gjennomgangsleser-filteret du bygger neste måned, er bare så godt som navnene du stempler på kommentarer i dag. Blanke eller inkonsekvente forfatterstrenger kan ikke repareres retroaktivt uten å åpne hver fil på nytt
Å få gjennomgangen ut av fremviseren
Gjennomgangsdata betaler for seg selv når de kan forlate fremviseren, som et sammendrag prosjektlederen leser uten å åpne filen, eller en CSV som mater et sporingsark. Eksporter fra indeksen du allerede bygde, aldri fra en fersk parsing, og velg en stabil måte å referere tilbake til hvert merke. Et sidenummer paret med annotasjonens rektangel overlever rundturer en array-indeks ikke gjør, fordi den neste slettingen stille nummererer om indeksene, og CSV-en din begynner å peke på feil kommentarer
En rad verdt å beholde, bærer siden, undertypen, forfatteren, opprettelsestidsstemplet når filen registrerer ett, innholdsteksten, og en statuskolonne du eier fremfor én PDF-en leverer. Det samme indekseringspasset er nyttig tidligere, under mottak, når et dokument kommer utenfra teamet og du vil vite hva som er i det før noen gjennomgår det. Artikkelen om arbeidsbenken for PDF-mottak går gjennom den triageringen, og navigering av skjemafelt dekker speilbildeproblemet: å gjennomgå dokumenter bygget for å samle inn data fremfor kommentarer
Ett tilfelle arrayet ikke vil vise deg
Én feilmodus fortjener et flagg fordi den ser ut som en defekt i koden din og ikke er det. En kunde rapporterer synlige uthevinger over hele en side, men panelet ditt lister ingenting, og AnnotationCount kommer tilbake null. Den vanlige forklaringen er at merkene ble flatet ut et sted oppstrøms. Flating baker annotasjonsvisninger inn i vanlig sideinnhold, slik at uthevingene blir en del av sidegrafikken og slutter å eksistere som annotasjonsobjekter i det hele tatt. Det er ingenting igjen for et annotasjons-API å telle opp, omfarge eller slette. Når du ser malt markering med et nulltall, slutt å lete etter feilen i opptellingsløkken din og spør heller hvordan filen ble produsert
Annotasjonsoverflaten brukt her, fra opptelling og opprettelse gjennom omfarging, sletting, og rendringsalternativene som holder visningen ærlig, følger med PDFium Component for Delphi, C++Builder og Lazarus/FPC