HotPDF vienu iškvietimu – BuildLoadedPageSVG – eksportuoja bet kurio įkelto PDF dokumento puslapį į savarankišką SVG žymėjimą; šis metodas grąžina visą SVG dokumentą kaip eilutę. Eksportuotas žymėjimas apima puslapio geometriją, tekstą kaip tikrus SVG text elementus, įterptus rastrinius vaizdus ir potėpio būseną, kurią PDF operatoriai buvo nustatę kiekvienos piešimo operacijos metu
Būtent ties šia paskutine dalimi tyliai lūžta dauguma savadarbių konverterių. Iš pirmo žvilgsnio PDF puslapio pavertimas SVG atrodo kaip koordinačių uždavinys, tačiau iš tikrųjų tai yra būsenos uždavinys. PDF yra dėklo mašina, kurios grafinė būsena kinta interpretuojant turinio srautą; SVG yra deklaratyvus medis, kurio kiekvienas elementas turi savo pateikimo atributus. Viskas, ko interpretatorius nespėja užfiksuoti tuo momentu, kai elementas išvedamas, tiesiog dingsta iš rezultato, o šis gedimas yra tylus: gaunamas taisyklingas SVG, kuriame puslapis atvaizduojamas subtiliai neteisingai
Kodėl PDF puslapis tiesiog nepersikonvertuoja į SVG?
Konversiją apsunkina trys neatitikimai, ir visi trys duoda rezultatą, kuris atrodo įtikinamai, kol nepalygini jo su originalu greta. Pirmasis – y ašis. PDF vartotojo erdvė auga aukštyn nuo puslapio apatinio kairiojo kampo; SVG auga žemyn nuo viršutinio kairiojo kampo. Vienas puslapio lygio apvertimas ištaiso piešimo koordinates, bet sugadina kiekvieną glifą, nes apverčiant visą drobę kartu veidrodiškai apverčiamos ir raidžių formos
Antrasis neatitikimas – paveldėjimas. PDF formate q ir Q įstumia ir ištraukia grafinę būseną, apimančią linijos plotį, linijos galą, linijos jungtį, jungties ribą, brūkšninės linijos masyvą, brūkšninės linijos fazę ir alfa reikšmę. SVG formate elementas, kuriam atributas nenurodytas, jį paveldi iš protėvio grupės – tai visiškai kitokia taisyklių apimties logika. Eksporteris, kuris seka tik esamą transformacijos matricą ir pamiršta potėpio būseną, leidžia po Q atkurtai būsenai „nutekėti“ į po jos einančius elementus
Trečia – PDF nemažai dalykų reiškia pagal susitarimą, o ne pagal reikšmę. Linijos galai ir jungtys yra sveikieji skaičiai, nulinis linijos plotis reiškia ne nematomą liniją, o plonutę liniją įrenginio erdvėje, o žvaigždutės variantai piešimo operatoriuose keičia ne spalvą, o vingiavimo taisyklę. Kiekvienam iš jų reikia vertimo, o ne paprasto kopijavimo
Vienas iškvietimas įprastam atvejui
Įprastai eksportuojant puslapius žiniatinklio peržiūros priemonei, palyginimo įrankiui ar dizaino perdavimui, API paviršius susiveda į vieną funkciją. BuildLoadedPageSVG priima nuo nulio skaičiuojamą puslapio indeksą pagal šiuo metu įkeltą dokumentą ir grąžina SVG dokumentą kaip 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 grąžina puslapių skaičių
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;
Tą patį eksportą siūlo ir HotPDF komandinės eilutės įrankis per komandą export-svg, kuri naudinga kūrimo konvejeriuose ir regresijos scenarijuose, kai reikia tekstiniu būdu palyginamo puslapio atvaizdavimo nerašant nė vienos Pascal eilutės. Kadangi SVG yra tekstas, jis natūraliai papildo rastrinį kelią, aprašytą straipsnyje PDF puslapio atvaizdavimas į bitmap: bitmap parodo, kaip puslapis atrodo, o SVG parodo, iš ko jis sudarytas
Kaip PDF tekstas susiejamas su SVG teksto elementais?
HotPDF sudėlioja teksto matricų grandinę kaip priešdėlį, padaugintą iš CTM, padaugintą iš teksto matricos, padaugintą iš glifo apvertimo, kur glifo apvertimas yra dešininis dauginimas iš matrix(1,0,0,-1,0,0). Šis dešinysis daugiklis egzistuoja vien tam, kad panaikintų puslapio lygio vertikalų apvertimą glifų formoms, nes SVG tekstas, nupieštas apverstame vietiniame rėme, kitaip atrodytų apverstas aukštyn kojomis. Korekciją įtraukus į matricą, o ne į specialiai apdorojamą kodą, pasukti, veidrodiškai apversti ir pakreipti tekstai visi išeina teisingi be papildomų šakojimų
Horizontaliam pozicionavimui naudojama daugiareikšmė SVG text elemento x sintaksė, po vieną koordinatę kiekvienam simboliui, sukaupiama iš kiekvieno glifo pastūmos plius tuo metu galiojantis simbolių tarpas Tc ir žodžių tarpas Tw. Horizontalus mastelio keitimas Tz įtraukiamas į teksto matricos a ir c stulpelius, o ne išvedamas atskirai, todėl peržiūros priemonė, kuri ignoruoja egzotiškus teksto atributus, vis tiek patalpina kiekvieną glifą ten, kur jį patalpino PDF. Tekstas, sukurtas per sudėtingą formavimą, aprašytą straipsnyje sudėtingų raštų teksto formavimas, keliauja tuo pačiu keliu, nes formuoklis jau būna pavertęs klasterius į pozicionuotus glifus tuo metu, kai interpretuojamas turinio srautas
Pasukimas ir vaizdai: du apvertimai, kuriuos lengva sumaišyti
Puslapiui su nenuliniu /Rotate įrašu reikia iš anksto pritaikytos transformacijos, sudarytos iš apvertimo pagal pasukto puslapio aukštį ir pasukimo, išreikšto y-aukštyn ekrano erdvėje. Trys pasukimo matricos yra (0,-1,1,0,0,W) 90 laipsnių, (-1,0,0,-1,W,H) 180 laipsnių ir (0,1,-1,0,H,0) 270 laipsnių, kur W ir H yra puslapio matmenys prieš pasukimą. Šias matricas skaičiuojant ranka lengva suklysti ženklu lygiai trijose vietose, todėl eksporteris jas sudaro per tą pačią matricų daugybos procedūrą, kuri apdoroja visas kitas transformacijas
Įterptiems vaizdams reikia savo apvertimo, nes PDF vaizdo erdvė pirmąją mėginio eilutę patalpina prie vienetinio kvadrato viršutinio krašto, o SVG image elementas turi y-žemyn vietinį rėmą. Todėl išvedama transformacija yra CTM, dešininiu būdu padaugintas iš matrix(1,0,0,-1,0,1). Suklydus čia gaunamos vertikaliai apverstos nuotraukos kitu atžvilgiu nepriekaištingame puslapyje – tokį defektą recenzentas pastebi akimirksniu, o automatinis testas dažnai nepastebi
Ką iš tikrųjų išsaugo grafinės būsenos įrenginys?
HotPDF potėpio būsenos operatorius w, J, j, M ir d siunčia per atskirą pasirinktinę įrenginio sąsają, todėl potėpio tikslumas buvo pridėtas nekeičiant esamo turinio įrenginio vtable ir nesulaužant dvejetainio suderinamumo su kodu, sukurtu prieš ankstesnes versijas. Konkrečiai, eksportuotas SVG gauna išverstus raktažodžius, o ne žalius PDF sveikuosius skaičius:
// PDF sveikųjų skaičių išvardijimai tampa SVG raktažodžių atributais
// linijos galas 0, 1, 2 -> butt, round, square
// linijos jungtis 0, 1, 2 -> miter, round, bevel
//
// Nulinis linijos plotis PDF reiškia ploną liniją įrenginio erdvėje,
// todėl eksporteris išveda vector-effect="non-scaling-stroke", kad
// potėpis liktų matomas ir maždaug vieno įrenginio pikselio pločio po CTM
//
// f* B* b* pasirenka lyginio-nelyginio taisyklę ir išveda fill-rule="evenodd",
// o f B b palieka numatytąjį SVG nenulinio vingiavimo režimą
Būsenos atkūrimas ties Q apima kartu nepermatomumą, linijos plotį, galą, jungtį, jungties ribą, brūkšninės linijos masyvą ir fazę. Įdėtieji Form XObjects objektai užfiksuoja ir atkuria tą patį pilną rinkinį savo ribose, todėl brūkšninis rėmelis, apibrėžtas antspaudo viduje, negali „ištekėti“ savo rašto į po jo einantį puslapio turinį. Jei jau sekate iškirpimo (clipping) ir CTM elgseną dėl kitų priežasčių, tai ta pati būsenos modelė, kuri pasirodo straipsnyje EMF ir WMF vektorinis importas, veikianti priešinga kryptimi
Ribos, kurias verta žinoti prieš pradedant naudoti gamyboje
Eksporteris sąžiningai apibrėžia savo apimtį, o šias ribas išsiaiškinti iš anksto pigiau nei atrasti jas gamyboje. Spalva pasiekia SVG įrenginį per operatorius rg, RG, g ir G. Užpildai, nustatyti per spalvų erdvę plius scn, kuriuo tapomos Separation, DeviceN ir ICCBased spalvos, iki įrenginio nepasiekia kaip išspręstas RGB trejetas, todėl puslapiai, kurie taip naudoja specialiąsias (spot) spalvas, eksportuoja savo geometriją, bet ne tas spalvas. Spausdinimui skirtiems šaltiniams verčiau rastrizuoti arba iš anksto suplokštinti specialiąsias spalvas; pati piešimo modelė aprašyta straipsnyje Separation ir DeviceN specialiųjų spalvų atvaizdavimas
Dvi smulkesnės pastabos sutaupo derinimo laiko. Šešioliktainiai spalvų literalai išvedami didžiosiomis raidėmis, todėl testas, tikrinantis #ff0000, nepavyksta prieš visiškai teisingą #FF0000. Be to, SVG įrenginys skaičiuojamas nuorodomis per savo sąsają, todėl jo atlaisvinimas reiškia leidimą sąsajai išeiti iš apimties, o ne Free iškvietimą objektui – šis skirtumas svarbus, jei plėsite įrenginį, kad jis kartu su puslapio turiniu išvestų ir savo žymėjimą
SVG eksportas natūraliai dera su struktūrine palyginimo funkcija, kai reikia sužinoti, ar sugeneruotas dokumentas iš tikrųjų pasikeitė tarp dviejų versijų. Platesnis įrankių rinkinys, skirtas įkeltiems dokumentams – nuo atvaizdavimo iki redagavimo ir eksporto – aprašytas HotPDF Delphi PDF komponento puslapyje