Műszaki cikk

PDF-oldalak exportálása SVG-be Delphiben a HotPDF segítségével

A HotPDF egyetlen hívással, a BuildLoadedPageSVG metódussal exportálja bármely betöltött PDF-dokumentum egy oldalát önálló SVG-jelöléssé, amely a teljes SVG-dokumentumot stringként adja vissza. Az exportált jelölés tartalmazza az oldal geometriáját, a szöveget valódi SVG text elemekként, a beágyazott raszterképeket, valamint azt a vonalállapotot, amelyet a PDF-operátorok az egyes rajzolási műveleteknél beállítottak

Éppen ez az utolsó rész az, ahol a legtöbb házi fejlesztésű konverter csendben megbukik. A PDF-oldal SVG-vé alakítása koordináta-problémának tűnik, valójában azonban állapot-probléma. A PDF egy veremgép, amelynek grafikai állapota a tartalomfolyam értelmezése közben változik; az SVG ezzel szemben egy deklaratív fa, amelynek minden eleme saját megjelenítési attribútumokat hordoz. Amit az értelmező nem rögzít abban a pillanatban, amikor egy elem kiíródik, az egyszerűen eltűnik a kimenetből, és a hiba néma: érvényes SVG-t kapunk, amely egy finoman hibás oldalt jelenít meg

Miért nem alakítható át egyszerűen egy PDF-oldal SVG-vé?

Három eltérés teszi nem-triviálissá az átalakítást, és mindhárom olyan kimenetet eredményez, amely hihetőnek tűnik, amíg egymás mellé nem teszed az eredetivel. Az első az y tengely. A PDF felhasználói tere az oldal bal alsó sarkából felfelé nő; az SVG a bal felső sarokból lefelé. Egyetlen oldalszintű tükrözés megoldja a rajzolási koordinátákat, ám ezzel minden glifát elront, mert a teljes vászon tükrözése a betűformákat is tükrözi

A második eltérés az öröklődés. A PDF-ben a q és a Q egy grafikai állapotot tesz verembe, illetve vesz ki onnan, amely magában foglalja a vonalvastagságot, a vonalvéget, a vonalösszekötést, a miter-határt, a szaggatásmintát, a szaggatásfázist és az alfa-értéket. Az SVG-ben az az elem, amely nem nevez meg egy attribútumot, azt egy szülő csoporttól örökli, ami egészen más hatókör-szabály. Az az exportáló, amely csak az aktuális transzformációs mátrixot követi, és megfeledkezik a vonalállapotról, hagyja, hogy a Q utáni visszaállított állapot átszivárogjon a következő elemekbe

A harmadik az, hogy a PDF több dolgot konvenció útján fejez ki, nem érték szerint. A vonalvégek és -összekötések egész számok, a nulla vonalvastagság eszköztér-szintű hajszálvonalat jelent, nem láthatatlan vonalat, a rajzolóoperátorok csillagos változatai pedig a feltöltési szabályt változtatják meg, nem a színt. Mindegyiket le kell fordítani, nem egyszerűen átmásolni

Egyetlen hívás az általános esetre

Egy webes megjelenítőhöz, egy összehasonlító eszközhöz vagy egy tervezői átadáshoz szükséges oldalak exportálásának hétköznapi feladatához az API-felület egyetlen függvényből áll. A BuildLoadedPageSVG a jelenleg betöltött dokumentumhoz nulla alapú oldalindexet vár, és az SVG-dokumentumot AnsiString-ként adja vissza:

var
  Pdf: THotPDF;
  I: Integer;
  Svg: AnsiString;
  Output: TFileStream;
begin
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.LoadFromFile('statements.pdf', '') <= 0 then
      Exit;                     // A LoadFromFile az oldalak számával tér vissza
    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;

Ugyanezt az exportot a HotPDF parancssori eszköze is elérhetővé teszi export-svg parancsként, ami build-folyamatokban és regressziós szkriptekben hasznos, ahol egy oldal szövegesen összehasonlítható reprezentációjára van szükség Pascal kód írása nélkül. Mivel az SVG szöveg, természetes párja a PDF-oldal bitképpé renderelésében leírt raszteres útnak: a bitkép megmutatja, hogyan néz ki az oldal, az SVG pedig azt, miből épül fel

Hogyan képződik le a PDF-szöveg SVG text elemekre?

A HotPDF a szövegmátrix-láncot előtag szorozva CTM-mel, majd szövegmátrixszal, majd glifatükrözéssel állítja össze, ahol a glifatükrözés egy jobbról szorzás a matrix(1,0,0,-1,0,0) mátrixszal. Ez a jobb oldali tényező kizárólag azért létezik, hogy semlegesítse az oldalszintű függőleges tükrözést a glifaformák esetében, mivel a tükrözött helyi keretben rajzolt SVG-szöveg egyébként fejjel lefelé jelenne meg. Azzal, hogy a korrekció a mátrixban van elhelyezve, nem pedig speciális esetkezelő kódban, a forgatott, tükrözött és nyírt szöveg is helyesen jelenik meg, extra elágazások nélkül

A vízszintes pozicionálás az SVG text elem többértékű x szintaxisát használja, karakterenként egy koordinátával, amely az egyes glifák előrehaladásából, valamint az adott pillanatban érvényes Tc karakterközből és Tw szóközből tevődik össze. A vízszintes skálázást (Tz) a szövegmátrix a és c oszlopába építi be, ahelyett hogy külön adná ki, így az az olvasó, amely figyelmen kívül hagyja az egzotikus szövegattribútumokat, akkor is oda helyezi minden glifát, ahová a PDF tette. A bonyolult formázással előállított szöveg, amelyet a komplex írásrendszerek szövegformázása tárgyal, ugyanazon az úton halad végig, mert a formázó a tartalomfolyam értelmezésének idejére már feloldotta a klasztereket pozicionált glifákká

Forgatás és képek: két tükrözés, amelyet könnyű elrontani

Egy nem nulla /Rotate bejegyzéssel rendelkező oldalhoz olyan előtranszformáció szükséges, amely a forgatott vászon magasságához viszonyított tükrözésből és egy y-felfelé megjelenítési térben kifejezett forgatásból áll. A három forgatási mátrix (0,-1,1,0,0,W) 90 foknál, (-1,0,0,-1,W,H) 180 foknál és (0,1,-1,0,H,0) 270 foknál, ahol W és H a forgatás előtti oldalméretek. Ezek kézi levezetése pontosan három helyen csábít előjelhibára, ezért az exportáló ugyanazon a mátrixszorzási rutinon keresztül állítja össze őket, amely minden más transzformációt is kezel

A beágyazott képeknek saját tükrözésre van szükségük, mert a PDF képtere az első mintasort az egységnégyzet felső élére helyezi, míg az SVG image eleme y-lefelé helyi keretet visel. A kiadott transzformáció ezért a CTM jobbról szorozva a matrix(1,0,0,-1,0,1) mátrixszal. Ha ezt elrontjuk, függőlegesen tükrözött fényképeket kapunk egy egyébként tökéletes oldalon, ami az a fajta hiba, amelyet egy ellenőr azonnal észrevesz, egy automatizált teszt viszont gyakran nem

Mit őriz meg valójában a grafikaiállapot-eszköz?

A HotPDF a w, J, j, M és d vonalállapot-operátorokat egy külön, opcionális eszközinterfészen keresztül küldi el, így a vonalhűség hozzáadása nem változtatta meg a meglévő tartalomeszköz vtábláját, és nem tört meg semmilyen bináris kompatibilitást a korábbi verziókra épített kód számára. Konkrétan az exportált SVG lefordított kulcsszavakat kap a nyers PDF-egész számok helyett:

// A PDF egész szám alapú felsorolásai SVG kulcsszó-attribútumokká válnak
//   vonalvég  0, 1, 2  ->  butt, round, square
//   vonalösszekötés 0, 1, 2  ->  miter, round, bevel
//
// A nulla vonalvastagság a PDF-ben eszköztér-szintű hajszálvonalat jelent,
// ezért az exportáló vector-effect="non-scaling-stroke" attribútumot ad ki,
// hogy a vonal látható maradjon, és a CTM után is kb. egy eszközpixel széles legyen
//
// Az f* B* b* a páratlan-páros szabályt választja ki, és fill-rule="evenodd"-et ad ki,
// míg az f B b megtartja az SVG alapértelmezett nonzero feltöltési irányát

A Q-nál történő állapot-visszaállítás együtt fedi le az átlátszatlanságot, a vonalvastagságot, a vonalvéget, a vonalösszekötést, a miter-határt, a szaggatásmintát és a szaggatásfázist. Az egymásba ágyazott Form XObjectek a határaikon ugyanazt a teljes készletet rögzítik és állítják vissza, így egy bélyegzőn belül definiált szaggatott szegély mintázata nem szivároghat át az azt követő oldaltartalomba. Ha más okból már követed a vágási és CTM-viselkedést, ez ugyanaz az állapotmodell, amely az EMF- és WMF-vektorimportban is megjelenik, ellentétes irányban futtatva

Korlátok, amelyeket érdemes ismerni, mielőtt élesbe állítod

Az exportáló őszinte a hatókörét illetően, és a határok előzetes megismerése olcsóbb, mint éles környezetben felfedezni őket. A szín az rg, RG, g és G operátorokon keresztül jut el az SVG-eszközhöz. A színtér plusz scn révén létrehozott kitöltések, amelyekkel a Separation, DeviceN és ICCBased színeket festik, nem érkeznek meg feloldott RGB-hármasként az eszközhöz, így az így direktszíneket használó oldalak exportálják a geometriájukat, de nem azokat a színeket. Nyomdai célú forrásoknál inkább rasztereld, vagy előbb lapítsd le a direktszíneket; magát a festési modellt a Separation és DeviceN direktszínek renderelése tárgyalja

Két kisebb megjegyzés időt takarít meg a hibakeresésnél. A hexadecimális színliterálok nagybetűvel kerülnek kiadásra, így egy #ff0000-t ellenőrző teszt elbukik egy tökéletesen helyes #FF0000 mellett. Az SVG-eszköz emellett az interfészén keresztül referenciaszámlált, ami azt jelenti, hogy a felszabadítása abból áll, hogy hagyod, hogy az interfész kikerüljön a hatókörből, nem pedig Free hívásából az objektumon — ez a különbség akkor számít, ha kiterjeszted az eszközt, hogy saját jelölést adjon ki az oldaltartalom mellett

Az SVG-export természetes párja a strukturális összehasonlításnak, amikor azt kell tudnod, hogy egy generált dokumentum valóban megváltozott-e két build között. A betöltött dokumentumok körüli szélesebb eszközkészlet — a rendereléstől a szerkesztésen át az exportig — a HotPDF Delphi PDF-komponens oldalán van dokumentálva