Műszaki cikk

PDF csempézési minta renderelése Delphiben HotPDF-fel

A satírozás, amely egyetlen sík szürke tömbként jelenik meg, a klasszikus csempézésiminta-hiba. A HotPDF, a Delphi és C++Builder natív VCL PDF-komponense, a PatternType 1-et úgy festi meg, hogy az aktuális útvonalat ideiglenes vágássá alakítja, és a mintatartalom-folyamot egyszer lejátssza minden látható csempénél, a mintaválasztást pedig a grafikai állapotban tárolja, és a q és Q állítja vissza

A tünetek kétféle formában érkeznek, és összefüggéstelennek tűnnek, amíg nem ismerjük az okot. Egy CAD-rajz elveszíti a metszetsatírozását, és tömör kitöltésként tér vissza, mert a renderelő a mintát egy átlagos színre oldotta fel, és azt festette meg. Vagy a satírozás kiszökik: egy címblokk, amelynek egyszínű fehérnek kellene lennie, felveszi az átlós vonalakat egy néhány útvonallal korábbi részletnézetből. Mindkettő mintaállapot-probléma, és csak az egyik szól egyáltalán csempék rajzolásáról

Miért szivárog egy csempézési minta a következő útvonalra?

Mert a kiválasztott mintanév a grafikai állapot része, nem az azt használó operátor tulajdonsága. Az ISO 32000-1 §8.6.6.2 a Pattern színteret olyanként definiálja, amelynek színértéke egy, az scn vagy SCN számára megadott mintanév, és a színállapot minden más összetevőjét a q menti, a Q pedig visszaállítja. A mintanévnek ugyanezt a szabályt kell követnie. A HotPDF az állapotrekordban tárolja FillPatternName és StrokePatternName néven, a kitöltési és vonalvezetési színtér-család mellett, így egy Q pontosan úgy állítja vissza az előző kiválasztást, ahogy a korábbi CTM-et is visszaállítja

Ha ezt a nevet ehelyett egy lokális változóban tárolnánk az operátor-diszpécserben, az túléli a folyamban minden Q-t. A hiba ekkor valahol váratlan helyen jelenik meg: egy, a mintázott útvonal után rajzolt Form XObject örököl egy mintaválasztást, amelyet a saját tartalomfolyama sosem tett meg, és a kitöltései satírozottan jelennek meg. A beágyazott formák tovább rontják a helyzetet, mert minden beágyazási szint tol és pop-ol egy állapotot, amelyet a kóbor változó figyelmen kívül hagy. Egy nem minta színtér beállítása cs-szel vagy CS-szel, vagy egy egyszerű g / rg / k kiadása szintén törölnie kell a mintanevet, különben az elavult kiválasztás túléli azt a színteret, amely jelentést adott neki

q
  /Pattern cs              % pattern colour space, ISO 32000-1 8.6.6.2
  /P1 scn                  % coloured tiling pattern, PaintType 1
  10 10 200 120 re f       % this rectangle is hatched
Q
0 0 300 200 re f           % must be black again, not hatched

q
  /Cs2 cs                  % [/Pattern /DeviceCMYK] array
  0 0.6 1 0 /P2 scn        % uncoloured pattern plus its underlying colour
  20 20 160 90 re f*
Q

Egy minta vágáson keresztül kerül megfestésre, sosem kitöltésként

A helyes modell szubtraktív: korlátozzuk az eszközvágást a festendő alakzatra, majd futtassuk a mintatartalmat annak belsejében. A HotPDF soha nem rajzol előbb egy tömör közelítést, majd fest rá, mert a köztes tömör alakzat látszana a csempék közötti résekben, és ütközne a csempetartalom bármilyen áttetszőségével. A §8.7.3.2 egy csempézési mintát fix vízszintes és függőleges intervallumokban megismételt tartalomfolyamként ír le, és a megismétlésnek csak egy, már a megfelelő alakú vágás mellett van értelme. Kitöltéseknél az átalakítás közvetlen: a HPDFSelectFillPathClip a poligon-kitöltési módot ALTERNATE-re állítja az f*, B* és b* esetén, és WINDING-ra a nullától eltérő variánsoknál, felépíti a GDI útvonalat, majd metszi azt a vágásba a SelectClipPath-tal. Ez az egyetlen sor teszi lehetővé, hogy egy páros-páratlan mintázott kitöltés ugyanazokat a lyukakat hagyja, mint egy páros-páratlan tömör kitöltés, ami pontosan az, amire egy fánk alakú satírozott területnek szüksége van

A vonalvezetés az a rész, amelyet könnyű elrontani. Egy vonalvezetett útvonalnak nincs belseje, így ha magát az útvonalat metsszük a vágásba, üres régiót kapunk, és semmi nem kerül megfestésre. A HPDFSelectStrokePathClip ezért először egy geometriai tollat épít fel az aktuális állapotból, PS_GEOMETRIC-et használva a J-ből származó végzáróval, a j-ből származó csatlakozással, az M-ből származó csúcshatárral, és PS_USERSTYLE-lal, amikor egy szaggatásmintázat aktív, majd meghívja a WidenPath-ot, hogy a vonalvezetett körvonalat kitölthető régióvá alakítsa, mielőtt vágna. A záró, a csatlakozás, a csúcshatár és a szaggatás viselkedése egy mintával vonalvezetett útvonalon ezután konstrukció szerint egyezik egy normál vonalvezetéssel, nem egy második implementáció révén. Két becsületes korlát él itt: az egy eszközegység alatti vonalszélességek egy pixelre vannak fixálva, a szaggatástömb pedig tizenhat bejegyzésnél levágásra kerül, ami az ExtCreatePen által elfogadott felső korlát

Mely csempék láthatók valójában?

A látható tartomány az átalakítás visszafelé futtatásából ered. A csempeelhelyezés mintaterületen történik, de az egyetlen dolog, amely tudja, mennyi az oldalból, ami érintve van, az eszközvágódoboz, amely eszközterületen van. A HotPDF felépíti a BaseMatrix := CTM * PatternMatrix-et, invertálja, és a GDI vágódoboz négy sarkát visszaképezi az inverzen keresztül. E négy leképezett sarok tengelyillesztett határai adják meg a lehetségesen lefedhető mintaterületi téglalapot, és e téglalap elosztása az XStep és YStep szerint a minta BBox-jához viszonyítva zárt indextartományokat ad. Minden cella ezután a CTM * PatternMatrix * Translate(i * XStep, j * YStep) CTM-mel kerül renderelésre, és másodszor is vágásra kerül a saját, átalakított BBox poligonjához. Ez a második vágás akkor számít, amikor az XStep kisebb, mint a határoló doboz szélessége, ami az átlapoló csempeterveket fejezi ki; e nélkül a szomszédos cellák egymáson festenének a deklarált kiterjedésükön kívül. Ha a cellánkénti vágás NULLREGION-ként tér vissza, a cella kihagyásra kerül, tokenizálás vagy végrehajtás nélkül

// Map the device clip box back into pattern space through the inverse of
// CTM * PatternMatrix, then convert those bounds into tile index ranges.
BaseMatrix := HPDFMatMul(FGSStack.State.CTM, PatternMatrix);
if not HPDFMatInvert(BaseMatrix, InverseMatrix) then Exit;   // singular: refuse
if GetClipBox(FDC, ClipRect) = ERROR then Exit;

// MinX..MaxY are the axis-aligned bounds of the four mapped clip corners.
I0 := Floor((MinX - BBox[2]) / StepXAbs);
I1 := Ceil ((MaxX - BBox[0]) / StepXAbs);
J0 := Floor((MinY - BBox[3]) / StepYAbs);
J1 := Ceil ((MaxY - BBox[1]) / StepYAbs);

PlannedTiles := Int64(I1 - I0 + 1) * Int64(J1 - J0 + 1);
if (PlannedTiles <= 0) or (PlannedTiles > FPatternTilesRemaining) then Exit;
Dec(FPatternTilesRemaining, Integer(PlannedTiles));

Színezetlen minták és a kívülről érkező szín

Egy PaintType 2 minta alakot hordoz, de színt nem, a szín a mintanévvel érkezik. A §8.7.3.2 kimondja, hogy egy színezetlen mintát csak olyan Pattern színtérrel szabad használni, amely egy alatta lévő teret deklarál, így az scn először az összetevő-értékeket kapja, és utoljára a mintanevet. A HotPDF ezeket az összetevőket a minta-színtér bejegyzésen tárolt alatta lévő téren keresztül oldja fel, ami azt jelenti, hogy egy színezetlen satírozás pontosan úgy tinteződhet egy Separation tintával vagy egy DeviceN kombinációval, mint bármely más kitöltés; ennek a feloldásnak a mechanikáját a Separation és DeviceN direktszínek renderelése cikk tárgyalja. A csempén belül a két festéstípus élesen eltér. PaintType 2 esetén a renderelő beállít egy szín-operátor-elfojtási jelzőt a csempe idejére, így a mintatartalomban lévő bármilyen g, rg, k vagy scn figyelmen kívül marad, és minden jelölés a kívülről megadott színt veszi fel. PaintType 1 esetén az ellenkezője érvényes: a kitöltési és vonalvezetési állapot visszaáll a PDF alapértelmezésekre, DeviceGray feketére identitás színtérrel, és a csempe önmagát színezi. Ha kihagyjuk ezt a visszaállítást, az az f operátornál éppen aktuális szín beszivároghat egy olyan mintába, amelynek önmagát kellett volna leírnia

Miért kell visszaállítani a grafikai állapotverem mélységét minden csempe után?

Mert egy mintatartalom-folyam lehet kiegyenlítetlen, és a kár cellánként halmozódik. Egy csempe, amelynek folyama három q operátort és két Q operátort tartalmaz, a vermet egy kerettel mélyebben hagyja, mint ahogy elkezdte. Ha cellák között csak az aktuális állapotrekordot állítjuk vissza, a mélység folyamatosan nő, így a kétszázadik cella egy olyan veremkeretből fut, amely a száz-kilencvenkilencedik cellához tartozik, azzal a CTM-mel és vágással, amit az a keret hordozott. A HotPDF ezért pillanatképet készít az állapotrekordról és a veremmélységről a csempeciklus előtt, és minden iteráció elején meghívja a RestoreSnapshot-ot, amely visszavágja a vermet a mentett hosszra, és egy lépésben újratelepíti a mentett állapotot. Az oldal Resources szótára és a szín-operátor-elfojtási jelző ugyanazon a határon kerül visszaállításra, mivel egy csempe hivatkozhat saját erőforrásaira, és nem adhatja tovább azokat a szomszédjának. A GDI vágásállapot ugyanezt a kezelést kapja egy SaveDC / RestoreDC pár révén minden cella körül, így egy csempe, amely saját W n vágást telepít, nem tudja lezsugorítani a következő cella számára elérhető régiót

Keretek, elutasítások, és amit a renderelő nem fog megrajzolni

A csempézési minták egy PDF-ben a legkönnyebb hely szolgáltatásmegtagadási fájl megírására, így a korlátok kemény számok, nem heurisztikák. A mintabeágyazás mélysége 4-re van korlátozva, ugyanaz a védelem, amelyet a Form XObject rekurzióhoz is használunk, ami megállítja a saját erőforrás-szótárán keresztül önmagára hivatkozó mintát. Egyetlen útvonalfestés legfeljebb összesen 16 384 csempét hajthat végre, a beágyazott mintákon átívelve visszaszámolva, és csak a legkülső mintafestés kezdetekor kerül visszaállításra. Egy csemperács, amelynek tervezett cellaszáma meghaladja azt, ami e keretből még megmaradt, teljesen elutasításra kerül, mielőtt egyetlen cella is lefutna

A degenerált geometria elutasításra kerül, nem közelítésre. Egy hiányzó vagy nulla területű BBox, egy XStep vagy YStep, amelynek nagysága 1e-6 alatt van, egy CTM * PatternMatrix szorzat, amelynek nincs inverze, a leképezett vágáskoordináták 1e9 fölött, vagy egy indexnagyság egymillió fölött, mind a mintafestés rajzolás nélküli visszatérését okozza. Az eredmény egy festetlen régió egy elakadt renderelő szál helyett, ami a kívánt kompromisszum egy kötegelt konverterben. A teljesítmény egyetlen döntésből ered: a mintafolyam festésenként egyszer kerül tokenizálásra a HPDFTokenizeContentStream-mel, és a tokentömb minden látható cellán újrahasznosításra kerül, így a csempeszám a végrehajtási költséget szorozza, de sosem a lexelési költséget

Egy mintázott oldal renderelése Delphiből

A mintatámogatás semmit nem változtat a hívó kódon. Töltsük be a dokumentumot, kérjünk egy oldalt, és a csempézési munka azon a tartalomfolyam-értelmezőn belül történik, amelyet az oldal-bitkép renderelés már úgyis meghajt. Ugyanaz az értelmező táplálja a bitmap-, metafájl- és nyomtató-eszközkontextusokat, így egy satírozott rajz, amely egy előnézeti miniatűrben helyesen néz ki, ugyanazzal a csempegeometriával nyomtatódik ki. A PatternType 2 árnyékolási minták egy másik ágat vesznek, amely megosztja kiértékelési útvonalát a puszta sh operátorral, ezt részletesen az axiális és radiális árnyékolás renderelés tárgyalja

var
  Pdf: THotPDF;
  Bmp: TBitmap;
begin
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.LoadFromFile('assembly-drawing.pdf') > 0 then
    begin
      // Section hatching that previously flattened to a solid block now
      // replays the tile content once per visible cell.
      Bmp := Pdf.RenderLoadedPageToBitmap(0, 200);
      if Assigned(Bmp) then
      try
        Bmp.SaveToFile('sheet1.bmp');
      finally
        Bmp.Free;
      end;
    end;
  finally
    Pdf.Free;
  end;
end;

Amikor egy mintázott terület még mindig rosszul néz ki, ellenőrizzük sorrendben a három hibaosztályt. Egy teljesen üres terület általában elutasítást jelent: vizsgáljuk meg az XStep, YStep és BBox degenerált értékeit, vagy számoljuk meg, hány csempére lenne szüksége a rácsnak a 16 384-es felső korláthoz képest. Egy egyetlen sík színnel megfestett terület azt jelenti, hogy a mintanév sosem jutott el a festési operátorig, ami a cs és scn folyambeli sorrendjére mutat. Egy minta, amely olyan helyen jelenik meg, ahol nem lenne szabad, állapot-visszaállítási problémát jelent, és ott érdemes megnézni a q / Q kezelését az azt öröklő forma vagy útvonal körül

A csempézési minták a PDF-nek egyike azoknak a funkcióknak, amelyek láthatatlanok maradnak, amíg az őket igénylő fájl meg nem érkezik a postaládánkba, és onnantól ez lesz az egész feladat. Ha rajzmegjelenítőket, mérnöki dokumentumkonvertereket vagy jelentés-renderelőket építünk Delphiben vagy C++Builderben, a teljes komponens és renderelési API-ja a HotPDF Delphi PDF component oldalán van dokumentálva