Marked content er den mekanisme, ISO 32000-1 §14.6 definerer til mærkning af side-indhold, og både tagget PDF og PDF/UA bygger på den. PDFium Component udstiller den direkte: PageObjectMarks læser hvert BDC-mærke og dens egenskabs-liste af et side-objekt, AddPageObjectMark skriver et, RemovePageObjectMark sletter et, og PageObjectMarkedContentID rapporterer den MCID, der linker indhold til strukturtræet
Indtil strukturtræet kan kobles tilbage til det indhold, det beskriver, er tilgængeligheds-værktøj gætterier. Strukturtræet siger "dette er en overskrift"; MCID'en siger, hvilke mærker på hvilken side, den overskrift reelt er. Begge halvdele skal være læsbare, før en applikation kan tjekke, reparere eller rapportere på mærkning
Hvad er et mærke, i byte?
En BDC-operator med et tag-navn og en valgfri egenskabs-liste, lukket af EMC. I indholds-strømmen ser det ud som /P <</MCID 3>> BDC ... EMC: tag'et /P navngiver rollen, dictionary'en bærer egenskaber, og alt mellem operatorerne er det markerede indhold. Et side-objekt inden for det spænd bærer mærket, hvilket er det, PDFium håndterer tilbage, og hvad PDFium Component forvandler til en record
TPdfContentMark holder et handle, tag'ets Name, og et array af TPdfContentMarkParam. Hver parameter har en Key, en Kind og ét meningsfuldt værdi-felt valgt af den kind: pmpInt, pmpFloat, pmpString eller pmpBlob. Kind'en kommer fra PDFiums egen type-rapport frem for fra den getter, der tilfældigvis lykkedes, hvilket er forskellen mellem at læse en egenskabs-liste og at gætte på én
var
Marks: TPdfContentMarks;
M: TPdfContentMark;
P: TPdfContentMarkParam;
I: Integer;
begin
Pdf.PageNumber := 1; // PageNumber is 1-based
for I := 0 to Pdf.ObjectCount - 1 do // page object indexes are 0-based
begin
Marks := Pdf.PageObjectMarks(I);
for M in Marks do
begin
Memo1.Lines.Add('mark ' + M.Name +
' (MCID ' + IntToStr(Pdf.PageObjectMarkedContentID(I)) + ')');
for P in M.Params do
case P.Kind of
pmpInt: Memo1.Lines.Add(' ' + P.Key + ' = ' + IntToStr(P.IntValue));
pmpString: Memo1.Lines.Add(' ' + P.Key + ' = ' + P.StringValue);
pmpFloat: Memo1.Lines.Add(' ' + P.Key + ' = ' + FloatToStr(P.FloatValue));
pmpBlob: Memo1.Lines.Add(' ' + P.Key + ' = ' +
IntToStr(Length(P.BlobValue)) + ' bytes');
end;
end;
end;
end;
Hvorfor pmpUnknown betyder to forskellige ting
pmpUnknown returneres, når PDFium rapporterer FPDF_OBJECT_UNKNOWN, og PDFium returnerer også det for en nøgle, der ikke eksisterer. De to tilfælde kan ikke skelnes på dette lag, og at lade som om ville være værre end at sige det
Den praktiske konsekvens for din kode: behandl pmpUnknown som "ingen brugbar værdi her" frem for som en type, du alligevel kunne afkode. Hvis en egenskab betyder noget for din arbejdsgang, så verificér at den er til stede med en kind, du genkender, og udled ikke fravær fra en unknown — et mærke, hvis egenskabs-liste du ikke kan læse, er et mærke, du bør rapportere på, ikke et, du tavst bør acceptere
En mærke-record er et snapshot, ikke et handle du ejer
Handle-feltet tilhører biblioteket. Det bliver stale det øjeblik, mærket fjernes, side-objektet destrueres, eller siden unloades, så recorden er et skrivebeskyttet snapshot med et kort liv. Cache den på tværs af et side-skift, og du holder en pointer ind i hukommelse, motoren har reclaim-et
Dette er samme disciplin, der gælder for side-objekt-handles generelt i PDFium, og den fanger folk det samme sted: et list-kontrol befolket med mærke-record'er, en bruger der navigerer til en anden side, og et crash, der ser urelateret ud til navigationen. Kopiér de værdier ud, du har brug for — navnet, nøglerne, tallene — og lad handle'et gå. Noterne om side-objekt-handles der bliver stale efter en transform dækker den generelle regel og hvordan den bider andetsteds
Tilføjelse af et mærke, og det gem-trin der er let at misse
AddPageObjectMark tager side-objekt-indekset, et tag-navn og et komplet parameter-sæt. Parametre skrives som et sæt frem for at blive patchet én nøgle ad gangen, hvilket er grunden til, at TPdfContentMarkParam ikke har nogen Has*-sentinels — "opdatér ét felt af en eksisterende record"-tilfældet, de ville beskytte, opstår ikke
Det, der er værd at sige eksplicit: at tilføje et mærke genopbygger side-indholds-strømmen, så tag'et overlever en gemning. Det var nødt til at være eksplicit, fordi SaveAs ikke regenererer indhold på egen hånd — en ændring, der kun levede i objekt-modellen, ville blive kasseret, og den gemte fil ville se præcis ud som den, du startede med. Har du nogensinde tilføjet noget til en PDFium-side og fundet det manglende i outputtet, er det sædvanligvis hvorfor
var
Params: TPdfContentMarkParams;
begin
SetLength(Params, 1);
Params[0].Key := 'MCID';
Params[0].Kind := pmpInt;
Params[0].IntValue := NextMcid;
Pdf.AddPageObjectMark(ObjectIndex, 'P', Params); // rebuilds the content stream
Pdf.UpdatePage;
Pdf.SaveAs('tagged-out.pdf');
end;
Hvad dette gør og ikke gør et dokument til
Mærker alene gør ikke en PDF tagget. Et overholdende tagget dokument har brug for et strukturtræ, hvis elementer refererer disse MCID'er, en /MarkInfo-post der erklærer dokumentet markeret, og rolle-navne der betyder, hvad standarden siger, de betyder. At skrive et /P-mærke med en MCID, intet struktur-element peger på, giver dig indhold, der giver sig ud for at være tagget, og et strukturtræ, der aldrig nævner det
Hvor marked content reelt tjener sig ind på dette niveau, er inspektion og reparation: revision af hvilke side-objekter der er tagget, find artefakter der burde have været mærket som sådanne, eller match MCID'er mod et strukturtræ for at finde de forældreløse. For strukturtræs-halvdelen af det arbejde, se gennemgangen af PDF/UA-strukturtræs-validering, og for den læseoplevelse, tag'ene i sidste ende er til, noterne om at bygge en tilgængelig PDF-læser i Delphi
PDFium Component giver Delphi-, C++Builder- og Lazarus-applikationer en high-level VCL-API over PDFium-motoren, med marked content, strukturtræer og tilgængeligheds-validering nåbart fra almindelig Pascal-kode — se PDFium Component-produktsiden for den fulde API-flade