Conținutul marcat este mecanismul pe care ISO 32000-1 §14.6 îl definește pentru marcarea conținutului paginii, iar PDF etichetat și PDF/UA sunt ambele construite pe el. PDFium Component îl expune direct: PageObjectMarks citește fiecare etichetă BDC și lista ei de proprietăți de pe un obiect de pagină, AddPageObjectMark scrie una, RemovePageObjectMark șterge una, iar PageObjectMarkedContentID raportează MCID-ul care leagă conținutul de arborele de structură
Până când arborele de structură poate fi împreunat înapoi la conținutul pe care îl descrie, instrumentele de accesibilitate sunt o ghicire. Arborele de structură spune „acesta este un titlu”; MCID-ul spune care mărci pe care pagină este de fapt acel titlu. Ambele jumătăți trebuie să fie lizibile înainte ca o aplicație să poată verifica, repara sau raporta despre marcare
Ce este o marcă, în octeți?
Un operator BDC cu un nume de etichetă și o listă de proprietăți opțională, închis de EMC. În fluxul de conținut arată ca /P <</MCID 3>> BDC ... EMC: eticheta /P numește rolul, dicționarul poartă proprietăți, iar tot ce e între operatori este conținutul marcat. Un obiect de pagină în interiorul acelei întinderi poartă marca, ceea ce returnează PDFium și ceea ce PDFium Component transformă într-o înregistrare
TPdfContentMark ține un handle, Name-ul etichetei, și o matrice de TPdfContentMarkParam. Fiecare parametru are o Key, un Kind și un singur câmp de valoare semnificativ selectat de acel kind: pmpInt, pmpFloat, pmpString sau pmpBlob. Kind-ul vine din propriul raport de tip al PDFium mai degrabă decât din getter-ul care s-a întâmplat să reușească, ceea ce este diferența dintre a citi o listă de proprietăți și a ghici la una
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;
De ce înseamnă pmpUnknown două lucruri diferite
pmpUnknown este returnat când PDFium raportează FPDF_OBJECT_UNKNOWN, iar PDFium returnează asta și pentru o cheie care nu există. Cele două cazuri nu pot fi distinse la acest strat, iar pretinderea contrarie ar fi mai rea decât să o spui
Consecința practică pentru codul dumneavoastră: tratați pmpUnknown ca „nicio valoare utilizabilă aici” mai degrabă decât ca pe un tip pe care l-ați putea oricum decoda. Dacă o proprietate contează pentru fluxul de lucru, verificați că este prezentă cu un kind pe care îl recunoașteți, și nu inferați absența dintr-un unknown — o marcă a cărei listă de proprietăți nu o puteți citi este o marcă pe care ar trebui să o raportați, nu una pe care ar trebui să o acceptați tăcut
O înregistrare de marcă este un instantaneu, nu un handle pe care îl dețineți
Câmpul Handle aparține bibliotecii. Devine staling în momentul în care marca este eliminată, obiectul de pagină este distrus sau pagina este descărcată, astfel încât înregistrarea este un instantaneu doar-citire cu viață scurtă. Puneți-o în cache peste o comutare de pagină și țineți un pointer în memorie pe care motorul a reclamat-o
Aceasta este aceeași disciplină care se aplică handle-urilor de obiect de pagină în general în PDFium, iar prinde oamenii în același loc: un control listă populat cu înregistrări de mărci, un utilizator care navighează la altă pagină, și un crash care pare nesus legat de navigare. Copiați valorile de care aveți nevoie — numele, cheile, numerele — și lăsați handle-ul să plece. Notele despre handle-urile de obiect de pagină care devin staling după o transformare acoperă regula generală și cum mușcă în altă parte
Adăugarea unei mărci, și pasul de salvare care este ușor de ratat
AddPageObjectMark ia indexul obiectului de pagină, un nume de etichetă și un set complet de parametri. Parametrii sunt scriși ca un set mai degrabă decât peticiți o cheie la un moment dat, motivul pentru care TPdfContentMarkParam nu are Has* sentinel — cazul „actualizează un câmp al unei înregistrări existente” pe care l-ar păzi aceia nu apare
Partea care merită spusă explicit: adăugarea unei mărci reconstruiește fluxul de conținut al paginii astfel încât eticheta supraviețuiește unei salvări. Asta a trebuit să fie explicit pentru că SaveAs nu regenerează conținut de la sine — o schimbare care a trăit doar în modelul de obiecte ar fi abandonată, iar fișierul salvat ar arăta exact ca cel cu care ați început. Dacă ați adăugat vreodată ceva pe o pagină PDFium și l-ați găsit lipsă din ieșire, de obicei acesta este motivul
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;
Ce face și ce nu face documentul prin asta
Mărcile singure nu fac un PDF etichetat. Un document etichetat conform cere un arbore de structură ale cărui elemente referențiază aceste MCID-uri, o intrare /MarkInfo care declară documentul marcat, și nume de rol care înseamnă ce spune standardul că înseamnă. Scrierea unei mărci /P cu un MCID pe care niciun element de structură nu îl pointează vă dă conținut care pretinde a fi etichetat și un arbore de structură care nu îl menționează niciodată
Unde conținutul marcat își câștigă cu adevărat valoarea la acest nivel este inspecția și repararea: auditarea care obiecte de pagină sunt etichetate, găsirea artefactelor care ar fi trebuit marcate ca atare, sau potrivirea MCID-urilor cu un arbore de structură pentru a găsi orfanii. Pentru jumătatea arborelui de structură a acelei munci, vedeți prezentarea detaliată despre validarea arborelui de structură PDF/UA, iar pentru experiența de citire pentru care sunt în ultimă instanță etichetele, notele despre construirea unui cititor PDF accesibil în Delphi
PDFium Component dă aplicațiilor Delphi, C++Builder și Lazarus un API VCL de nivel înalt peste motorul PDFium, cu conținut marcat, arbori de structură și validare de accesibilitate accesibile din cod Pascal obișnuit — vedeți pagina produsului PDFium Component pentru întreaga suprafață API