Tehnički članak

Čitanje i pisanje označenog sadržaja PDF-a u Delphiju

Označeni sadržaj mehanizam je koji ISO 32000-1 §14.6 definira za označavanje sadržaja stranice, a na njemu su izgrađeni i označeni PDF i PDF/UA. PDFium Component izlaže ga izravno: PageObjectMarks čita svaku BDC oznaku i njen popis svojstava s objekta stranice, AddPageObjectMark zapisuje jednu, RemovePageObjectMark briše jednu, a PageObjectMarkedContentID prijavljuje MCID koji povezuje sadržaj sa stablom strukture

Dok se stablo strukture ne može spojiti natrag sa sadržajem koji opisuje, alati za pristupačnost puko su nagađanje. Stablo strukture kaže ovo je naslov; MCID kaže koje oznake na kojoj stranici taj naslov doista jesu. Obje polovice moraju biti čitljive prije nego što aplikacija može provjeriti, popraviti ili izviještavati o označavanju

Što je oznaka, u bajtovima?

Operator BDC s imenom oznake i neobaveznim popisom svojstava, zatvoren EMC-om. U streamu sadržaja izgleda kao /P <</MCID 3>> BDC ... EMC: oznaka /P imenuje ulogu, rječnik nosi svojstva, a sve između operatora jest označeni sadržaj. Objekt stranice unutar tog raspona nosi oznaku, što je što PDFium vraća i što PDFium Component pretvara u zapis

TPdfContentMark drži ručicu, oznaku Name i polje TPdfContentMarkParam. Svaki parametar ima Key, Kind i jedno značajno polje vrijednosti koje bira taj tip: pmpInt, pmpFloat, pmpString ili pmpBlob. Tip dolazi iz PDFiumovog vlastitog izvješća o tipu, a ne iz toga koji getter slučajno uspije, što je razlika između čitanja popisa svojstava i pogađanja istog

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;

Zašto pmpUnknown znači dvije različite stvari

pmpUnknown vraća se kad PDFium prijavi FPDF_OBJECT_UNKNOWN, a PDFium to vraća i za ključ koji ne postoji. Ta se dva slučaja na ovom sloju ne mogu razlikovati, a pretvaranje da se može bilo bi gore od toga da se kaže

Praktična posljedica za vaš kod: tretirajte pmpUnknown kao ovdje nema upotrebljive vrijednosti, a ne kao tip koji biste mogli dekodirati ionako. Ako svojstvo važi za vaš cjevovod, provjerite je li prisutno s tipom koji prepoznajete, i ne zaključujte o odsutnosti iz nepoznatog; oznaka čiji popis svojstava ne možete pročitati jeste oznaka o kojoj biste trebali izvijestiti, a ne ona koju biste šutke prihvatili

Zapis oznake jest snimka, a ne ručica koju posjedujete

Polje Handle pripada knjižnici. Postaje ustajalo čim je oznaka uklonjena, objekt stranice uništen ili stranica istovarena, pa je zapis snimka samo za čitanje s kratkim životom. Predmemorirajte je preko prebacivanja stranice i držite pokazivač u memoriju koju je motor povratio

To je ista disciplina koja se općenito primjenjuje na ručice objekata stranice u PDFiumu, i hvata ljude na istom mjestu: kontrola popisa popunjena zapisima oznaka, korisnik koji navigira na drugu stranicu i rušenje koje izgleda nepovezano s navigacijom. Kopirajte van vrijednosti koje trebate, ime, ključeve, brojeve, i pustite ručicu. Bilješke o ručicama objekata stranice koje postaju ustajale nakon transformacije pokrivaju opće pravilo i kako grize drugdje

Dodavanje oznake i korak spremanja koji je lako propustiti

AddPageObjectMark uzima indeks objekta stranice, ime oznake i potpuni skup parametara. Parametri se zapisuju kao skup, a ne zakrpljuju jedan ključ u vrijeme, što je razlog zašto TPdfContentMarkParam nema Has* čuvare: slučaj ažuriranja jednog polja postojećeg zapisa koji bi oni čuvali ne pojavljuje se

Dio koji vrijedi izreći izričito: dodavanje oznake ponovno izgrađuje stream sadržaja stranice tako da oznaka preživi spremanje. To je moralo biti izričito jer SaveAs sam po sebi ne obnavlja sadržaj; promjena koja je živjela samo u objektnom modelu bi se odbacila, a spremljena datoteka izgledala bi točno kao ona s kojom ste započeli. Ako ste ikad dodali nešto na PDFium stranicu i našli ga nedostajućeg u izlazu, to je obično razlog

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;

Što ovo čini, a što ne čini od dokumenta

Samo oznake ne čine označeni PDF. Sukladni označeni dokument treba stablo strukture čiji elementi referenciraju te MCID-ove, unos /MarkInfo koji deklarira dokument označenim te imena uloga koja znače ono što standard kaže da znače. Pisanje oznake /P s MCID-om na koji nijedan element strukture ne pokazuje daje vam sadržaj koji tvrdi da je označen i stablo strukture koje ga nikad ne spominje

Gdje označeni sadržaj doista opravdava svoje mjesto na ovoj razini jest pregled i popravak: revizija koji su objekti stranice označeni, pronalaženje artefakata koji su trebali biti označeni kao takvi, ili usporedba MCID-ova sa stablom strukture da se pronađu siročad. Za polovicu stabla strukture u tom radu, pogledajte prikaz provjere valjanosti stabla strukture PDF/UA, a za iskustvo čitanja kojem su oznake naposljetku namijenjene, bilješke o izgradnji pristupačnog PDF čitača u Delphiju

PDFium Component daje aplikacijama Delphi, C++Builder i Lazarus visokorazinski VCL API preko PDFium motora, s označenim sadržajem, stablima strukture i provjerom valjanosti pristupačnosti dohvatljivima iz običnog Pascal koda. Pogledajte stranicu proizvoda PDFium Component za cijelu površinu API-ja