Tehnički članak

Izvoz PDF stranica u SVG u Delphiju uz HotPDF

HotPDF izvozi stranicu bilo kojeg učitanog PDF dokumenta u samostalni SVG zapis jednim pozivom, BuildLoadedPageSVG, koji vraća cjelokupni SVG dokument kao string. Izvezeni zapis sadrži geometriju stranice, tekst kao prave SVG text elemente, ugrađene rasterske slike te stanje poteza koje su PDF operatori uspostavili pri svakoj operaciji crtanja

Upravo taj posljednji dio je mjesto gdje većina domaćih pretvarača tiho zakaže. Pretvaranje PDF stranice u SVG djeluje kao problem koordinata, a zapravo je problem stanja. PDF je stack stroj čije se grafičko stanje mijenja tijekom tumačenja tijeka sadržaja; SVG je deklarativno stablo čiji svaki element nosi vlastite prezentacijske atribute. Sve što interpreter ne uspije snimiti u trenutku emitiranja elementa jednostavno nestaje iz izlaza, a greška je tiha: dobijete valjani SVG koji prikazuje suptilno pogrešnu stranicu

Zašto se PDF stranica jednostavno ne pretvori u SVG?

Tri nepodudarnosti čine pretvorbu netrivijalnom, a sve tri proizvode izlaz koji djeluje uvjerljivo sve dok ga ne usporedite bok uz bok s izvornikom. Prva je os y. PDF korisnički prostor raste prema gore od donjeg lijevog kuta stranice; SVG raste prema dolje od gornjeg lijevog kuta. Jedno zrcaljenje na razini cijele stranice ispravlja koordinate crtanja, a zatim uništava svaki glif, jer zrcaljenje cijelog platna zrcali i same oblike slova

Druga nepodudarnost je nasljeđivanje. U PDF-u q i Q guraju i skidaju grafičko stanje koje uključuje širinu linije, oblik završetka linije, oblik spoja linija, granicu šiljka (miter limit), niz crtkanja, fazu crtkanja i alfa vrijednost. U SVG-u element koji ne navede atribut nasljeđuje ga od nadređene grupe, što je posve drugačije pravilo dosega. Izvoznik koji prati samo trenutnu matricu transformacije, a zaboravi stanje poteza, dopušta da obnovljeno stanje nakon Q procuri u elemente koji slijede

Treća je da PDF nekoliko stvari izražava konvencijom, a ne vrijednošću. Završeci i spojevi linija su cijeli brojevi, nulta širina linije znači liniju debljine dlake u prostoru uređaja, a ne nevidljivu liniju, a zvjezdaste varijante operatora bojenja mijenjaju pravilo namatanja umjesto boje. Svako od toga treba prijevod, a ne kopiju

Jedan poziv za uobičajeni slučaj

Za uobičajen zadatak izvoza stranica za web preglednik, alat za usporedbu razlika ili predaju dizajna, API površina svodi se na jednu funkciju. BuildLoadedPageSVG prima indeks stranice koji počinje od nule unutar trenutno učitanog dokumenta i vraća SVG dokument kao AnsiString:

var
  Pdf: THotPDF;
  I: Integer;
  Svg: AnsiString;
  Output: TFileStream;
begin
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.LoadFromFile('statements.pdf', '') <= 0 then
      Exit;                     // LoadFromFile vraća broj stranica
    for I := 0 to Pdf.LoadedPageCount - 1 do
    begin
      Svg := Pdf.BuildLoadedPageSVG(I);
      if Length(Svg) = 0 then
        Continue;
      Output := TFileStream.Create(Format('page-%d.svg', [I + 1]), fmCreate);
      try
        Output.WriteBuffer(Svg[1], Length(Svg));
      finally
        Output.Free;
      end;
    end;
  finally
    Pdf.Free;
  end;
end;

Isti izvoz dostupan je i putem HotPDF alata naredbenog retka kao naredba export-svg, što je korisno u build pipelineovima i regresijskim skriptama gdje želite tekstualno usporediv prikaz stranice bez pisanja ijednog retka Pascala. Budući da je SVG tekst, prirodno se nadopunjuje s rasterskim putem opisanim u renderiranju PDF stranice u bitmapu: bitmapa vam govori kako stranica izgleda, SVG vam govori od čega je sačinjena

Kako se PDF tekst preslikava na SVG text elemente?

HotPDF sastavlja lanac matrica teksta kao prefiks pomnožen s CTM-om, zatim s matricom teksta, pa sa zrcaljenjem glifa, pri čemu je zrcaljenje glifa desno množenje s matrix(1,0,0,-1,0,0). Taj faktor s desne strane postoji isključivo kako bi poništio vertikalno zrcaljenje na razini stranice za oblike glifova, jer bi se inače SVG tekst nacrtan u zrcaljenom lokalnom okviru prikazao naopako. Time što se ispravak stavlja u matricu, a ne u posebno kodirane iznimke, rotirani, zrcaljeni i nakošeni tekst ispravno se prikazuju bez dodatnih grananja

Vodoravno pozicioniranje koristi sintaksu s više vrijednosti atributa x SVG text elementa, jednu koordinatu po znaku, akumuliranu iz pomaka svakog glifa plus razmaka među znakovima Tc i razmaka među riječima Tw koji su tada na snazi. Vodoravno skaliranje Tz ugrađeno je u stupce a i c matrice teksta umjesto da se izdvaja zasebno, tako da preglednik koji ignorira egzotične atribute teksta i dalje postavlja svaki glif točno tamo gdje ga je postavio PDF. Tekst nastao složenim oblikovanjem, opisan u oblikovanju teksta za složena pisma, prolazi istim putem, jer je oblikovatelj do trenutka tumačenja tijeka sadržaja već razriješio klastere u pozicionirane glifove

Rotacija i slike: dva zrcaljenja koja je lako pobrkati

Stranica s vrijednošću /Rotate različitom od nule treba predtransformaciju sastavljenu od zrcaljenja prema visini rotiranog platna i rotacije izražene u prikaznom prostoru s y osi prema gore. Tri matrice rotacije su (0,-1,1,0,0,W) za 90 stupnjeva, (-1,0,0,-1,W,H) za 180 i (0,1,-1,0,H,0) za 270, pri čemu su W i H dimenzije stranice prije rotacije. Ručno izvođenje ovih matrica poziva na pogreške predznaka na točno tri mjesta, pa ih izvoznik sastavlja kroz istu rutinu množenja matrica koja obrađuje svaku drugu transformaciju

Ugrađene slike trebaju vlastito zrcaljenje, jer PDF prostor slike smješta prvi redak uzorka na gornji rub jedinične kvadrature, dok SVG image element nosi lokalni okvir s y osi prema dolje. Emitirana transformacija stoga je CTM pomnožen s desne strane s matrix(1,0,0,-1,0,1). Ako se ovo pogriješi, dobiju se okomito zrcaljene fotografije na inače savršenoj stranici, a to je vrsta greške koju recenzent primijeti trenutačno, a automatizirani test to često ne uoči

Što uređaj grafičkog stanja zapravo čuva?

HotPDF prosljeđuje operatore stanja poteza w, J, j, M i d kroz zasebno neobavezno sučelje uređaja, tako da je vjernost poteza dodana bez promjene vtable postojećeg uređaja sadržaja i bez narušavanja binarne kompatibilnosti za kod izgrađen prema ranijim verzijama. Konkretno, izvezeni SVG prima prevedene ključne riječi umjesto sirovih PDF cijelih brojeva:

// PDF cjelobrojne enumeracije postaju SVG atributi s ključnim riječima
//   završetak linije  0, 1, 2  ->  butt, round, square
//   spoj linija 0, 1, 2  ->  miter, round, bevel
//
// Nulta širina linije u PDF-u znači liniju debljine dlake u prostoru
// uređaja, pa izvoznik emitira vector-effect="non-scaling-stroke" kako bi
// potez ostao vidljiv i širok približno jedan piksel uređaja nakon CTM-a
//
// f* B* b* biraju pravilo even-odd i emitiraju fill-rule="evenodd",
// dok f B b zadržavaju zadano SVG pravilo nonzero namatanja

Obnavljanje stanja kod Q obuhvaća zajedno prozirnost, širinu linije, završetak, spoj, granicu šiljka, niz crtkanja i fazu crtkanja. Ugniježđeni Form XObjecti snimaju i obnavljaju isti potpuni skup na svojim granicama, tako da crtkani rub definiran unutar pečata ne može procuriti svoj uzorak u sadržaj stranice koji slijedi. Ako već pratite ponašanje isijecanja (clipping) i CTM-a iz drugih razloga, ovo je isti model stanja koji se pojavljuje u uvozu EMF i WMF vektora, samo u suprotnom smjeru

Granice koje vrijedi znati prije nego što isporučite proizvod

Izvoznik je iskren o svom dosegu, a upoznavanje granica unaprijed jeftinije je nego njihovo otkrivanje u produkciji. Boja stiže do SVG uređaja kroz operatore rg, RG, g i G. Ispune uspostavljene kroz prostor boja plus scn, što je način na koji se boje Separation, DeviceN i ICCBased slikaju, ne stižu do uređaja kao razriješena RGB trojka, pa stranice koje na taj način koriste posebne (spot) boje izvoze svoju geometriju, ali ne i te boje. Za izvore namijenjene tisku umjesto toga rasterizirajte ili prvo spljoštite posebne boje; sam model slikanja opisan je u renderiranju Separation i DeviceN posebnih boja

Dvije manje napomene štede vrijeme otklanjanja grešaka. Heksadecimalni literali boja emitiraju se velikim slovima, pa test koji provjerava #ff0000 ne prolazi naspram potpuno ispravnog #FF0000. Osim toga, SVG uređaj broji reference kroz svoje sučelje, što znači da je njegovo oslobađanje pitanje izlaska sučelja iz dosega, a ne pozivanja Free na objektu, razlika koja je važna ako proširujete uređaj kako biste emitirali vlastiti zapis uz sadržaj stranice

Izvoz u SVG prirodno se nadopunjuje sa strukturnom usporedbom kada trebate znati je li se generirani dokument doista promijenio između dviju verzija builda. Širi skup alata oko učitanih dokumenata, od renderiranja preko uređivanja do izvoza, dokumentiran je na stranici HotPDF Delphi PDF komponente