Tehnički članak

Renderovanje PDF tiling patterna u Delphi sa HotPDF

Šrafiranje koje se renderuje kao jedan ravan sivi blok je klasičan otkaz tiling patterna. HotPDF, nativna VCL PDF komponenta za Delphi i C++Builder, boji PatternType 1 tako što pretvara trenutnu putanju u privremeni isečak i ponovo pušta tok sadržaja patterna jednom po vidljivoj pločici, sa selekcijom patterna koja se drži u stanju grafike i vraća pomoću q i Q

Simptomi dolaze u dve varijante, i izgledaju nepovezano dok ne znate uzrok. CAD crtež gubi svoje presečno šrafiranje i vraća se kao pune ispune, jer je renderer razrešio pattern u prosečnu boju i naslikao je. Ili šrafiranje pobegne: naslovni blok koji treba da bude potpuno beo pokupi dijagonalne linije iz detaljnog prikaza dve putanje ranije. Oba su problemi stanja patterna, i samo jedan od njih se uopšte tiče crtanja pločica

Zašto tiling pattern iscuri na sledeću putanju?

Zato što je izabrano ime patterna deo stanja grafike, ne svojstvo operatora koji ga je koristio. ISO 32000-1 §8.6.6.2 definiše prostor boja Pattern kao onaj čija je vrednost boje ime patterna prosleđeno scn ili SCN, i svaka druga komponenta stanja boje se čuva sa q i vraća sa Q. Ime patterna mora slediti isto pravilo. HotPDF ga drži u zapisu stanja kao FillPatternName i StrokePatternName, uz porodicu prostora boja za popunu i potez, tako da Q vraća prethodnu selekciju nazad tačno onako kako vraća prethodni CTM

Sačuvajte to ime umesto toga u lokalnoj promenljivoj unutar dispečera operatora, i ono preživi svaki Q u toku. Otkaz se onda pojavljuje negde neočekivano: Form XObject nacrtan posle putanje sa patternom nasleđuje selekciju patterna koju sopstveni tok sadržaja nikad nije napravio, i njegove ispune izlaze šrafirane. Ugnežđene forme to pogoršavaju, jer svaki nivo ugnežđavanja gura i skida stanje koje zalutala promenljiva ignoriše. Postavljanje prostora boja koji nije pattern sa cs ili CS, ili izdavanje običnog g / rg / k, takođe mora očistiti ime patterna, inače zastarela selekcija nadživljuje prostor boja koji joj je dao značenje

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

Pattern se slika kroz isečak, nikad kao ispuna

Ispravan model je oduzimajući: ograniči isečak uređaja na oblik koji se boji, zatim pokreni sadržaj patterna unutar njega. HotPDF nikad prvo ne crta punu aproksimaciju i preko nje ne slika, jer bi međurezultat bio vidljiv kroz razmake između pločica i sukobio bi se sa bilo kojom providnošću u sadržaju pločice. §8.7.3.2 opisuje tiling pattern kao tok sadržaja ponovljen na fiksnim horizontalnim i vertikalnim intervalima, a ponavljanje ima smisla samo naspram isečka koji već ima ispravan oblik. Za ispune je konverzija direktna: HPDFSelectFillPathClip postavlja režim popune poligona na ALTERNATE za f*, B* i b*, a na WINDING za varijante nonzero, gradi GDI putanju, i preseca je u isečak sa SelectClipPath. Taj jedan red je ono što čini da isečena ispuna sa patternom po even-odd pravilu ostavlja iste rupe kao puna ispuna po even-odd pravilu, što je tačno ono što oblast šrafirana u obliku krofne zahteva

Potezi su deo koji je lako pogrešno uraditi. Potez putanje nema unutrašnjost, tako da presecanje same putanje u isečak daje praznu oblast i ništa se ne oboji. HPDFSelectStrokePathClip zato prvo gradi geometrijsku olovku iz trenutnog stanja, koristeći PS_GEOMETRIC sa kapom kraja iz J, spojem iz j, granicom oštrog ugla iz M, i PS_USERSTYLE kad je niz isprekidane linije aktivan, zatim poziva WidenPath da pretvori kontur poteza u oblast koja se može popuniti pre isecanja. Ponašanje kape, spoja, oštrog ugla i isprekidane linije na putanji obojenoj patternom preko poteza onda odgovara normalnom potezu konstrukcijom, a ne drugom implementacijom. Dva iskrena ograničenja žive ovde: širine linije ispod jedne jedinice uređaja se stežu na jedan piksel, a niz isprekidane linije se skraćuje na šesnaest unosa, što je tavanica koju ExtCreatePen prihvata

Koje su pločice zapravo vidljive?

Vidljiv opseg dolazi iz pokretanja transformacije unazad. Postavljanje pločica se dešava u prostoru patterna, ali jedino što zna koliko stranice se dodiruje je okvir isečka uređaja, koji je u prostoru uređaja. HotPDF sastavlja BaseMatrix := CTM * PatternMatrix, invertuje je, i mapira četiri ugla GDI okvira isečka nazad kroz inverz. Osno poravnate granice ta četiri mapirana ugla daju pravougaonik u prostoru patterna koji se eventualno može pokriti, a deljenje tog pravougaonika sa XStep i YStep naspram BBox patterna daje zatvorene opsege indeksa. Svaka ćelija se onda renderuje sa CTM od CTM * PatternMatrix * Translate(i * XStep, j * YStep), i biva isečena drugi put na sopstveni transformisani BBox poligon. Taj drugi isečak je bitan kad je XStep manji od širine granične kutije, što je način na koji se izražavaju dizajni pločica koji se preklapaju; bez njega bi susedne ćelije slikale jedna preko druge van svog deklarisanog opsega. Ako isečak po ćeliji vrati NULLREGION, ćelija se preskače bez tokenizacije ili izvršavanja bilo čega

// 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));

Neobojeni patterni i boja koja dolazi spolja

Pattern tipa PaintType 2 nosi oblik, ali ne i boju, a boja stiže sa imenom patterna. §8.7.3.2 propisuje da se neobojen pattern koristi samo sa prostorom boja Pattern koji deklariše osnovni prostor, tako da scn prima vrednosti komponenti prvo, a ime patterna poslednje. HotPDF razrešava te komponente kroz osnovni prostor sačuvan u unosu prostora boja patterna, što znači da se neobojeno šrafiranje može obojiti Separation mastilom ili DeviceN kombinacijom tačno kao bilo koja druga ispuna; mehanika tog razrešenja pokrivena je u renderovanju Separation i DeviceN spot boja. Unutar pločice, dva tipa bojenja oštro se razilaze. Za PaintType 2 renderer postavlja zastavicu za potiskivanje operatora boje za trajanje pločice, tako da se svaki g, rg, k ili scn u sadržaju patterna ignoriše i svaki potez uzima spolja dostavljenu boju. Za PaintType 1 važi suprotno: stanje ispune i poteza se resetuje na PDF podrazumevane vrednosti, DeviceGray crna sa identitetskim prostorom boja, i pločica se sama boji. Preskakanje tog reseta pušta boju koja je slučajno bila trenutna kod operatora f da procuri u pattern koji je trebalo da bude samoopisujući

Zašto se dubina steka stanja grafike mora vratiti posle svake pločice?

Zato što je toku sadržaja patterna dozvoljeno da bude neuravnotežen, a šteta se sabira preko ćelija. Pločica čiji tok sadrži tri operatora q i dva operatora Q ostavlja stek jedan frejm dublji nego što je počeo. Vratite samo trenutni zapis stanja između ćelija i dubina nastavlja da raste, tako da se ćelija broj dvesta izvršava iz frejma steka koji pripada ćeliji broj sto devedeset devet, sa kakvim god CTM i isečkom taj frejm nosio. HotPDF zato snima zapis stanja i dubinu steka pre petlje pločica i poziva RestoreSnapshot na vrhu svake iteracije, koja skraćuje stek nazad na sačuvanu dužinu i ponovo instalira sačuvano stanje u jednom koraku. Rečnik Resources stranice i zastavica potiskivanja operatora boje se vraćaju na istoj granici, jer pločica može referencirati sopstvene resurse i ne sme ih predati susedu. GDI stanje isečka dobija isti tretman kroz par SaveDC / RestoreDC oko svake ćelije, tako da pločica koja instalira sopstveni isečak W n ne može smanjiti oblast dostupnu sledećoj

Budžeti, odbijanja, i šta renderer neće nacrtati

Tiling patterni su najlakše mesto u PDF-u za pisanje fajla za uskraćivanje usluge, tako da su ograničenja tvrdi brojevi, ne heuristike. Ugnežđavanje patterna je ograničeno na dubinu 4, ista straža koja se koristi za rekurziju Form XObject, koja zaustavlja pattern koji referencira sam sebe kroz sopstveni rečnik resursa. Jedno bojenje putanje sme izvršiti najviše 16.384 pločica ukupno, brojano unazad preko ugnežđenih patterna i resetuje se samo kad počne bojenje najspoljašnjeg patterna. Mreža pločica čiji planirani broj ćelija prekoračuje ono što je ostalo od tog budžeta se u potpunosti odbacuje, pre nego što se ijedna ćelija izvrši

Degenerativna geometrija se odbija, a ne aproksimira. Nedostajući ili nulti BBox, XStep ili YStep čija je magnituda ispod 1e-6, proizvod CTM * PatternMatrix bez inverza, mapirane koordinate isečka preko 1e9, ili magnituda indeksa preko milion, sve to izaziva da bojenje patterna vrati bez crtanja. Rezultat je neobojena oblast umesto obešene niti renderovanja, što je kompromis koji želite u batch konverteru. Performanse dolaze iz jedne odluke: tok patterna se tokenizuje jednom po bojenju sa HPDFTokenizeContentStream, a niz tokena se ponovo koristi kroz svaku vidljivu ćeliju, tako da broj pločica množi cenu izvršavanja, ali nikad cenu leksiranja

Renderovanje stranice sa patternom iz Delphi

Ništa u vezi sa podrškom za pattern ne menja pozivajući kod. Učitajte dokument, zatražite stranicu, i posao pločica se dešava unutar interpretera toka sadržaja koji renderovanje stranice u bitmapu već pokreće. Isti interpreter hrani bitmap, metafile i printer kontekste uređaja, tako da šrafiran crtež koji izgleda ispravno u pregledu thumbnail-a štampa se sa istom geometrijom pločica. Shading patterni tipa PatternType 2 idu drugom granom koja deli svoju putanju evaluacije sa golim operatorom sh, opisanom detaljno pod renderovanjem aksijalnih i radijalnih shading-a

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;

Kad oblast sa patternom i dalje izgleda pogrešno, proverite tri klase otkaza po redu. Oblast koja je potpuno prazna obično znači odbijanje: proverite XStep, YStep i BBox za degenerativne vrednosti, ili izbrojte pločice koje bi mreži trebale naspram tavanice od 16.384. Oblast obojena u jednu ravnu boju znači da ime patterna nikad nije stiglo do operatora bojenja, što ukazuje na redosled cs i scn u toku. Pattern koji se pojavi tamo gde ne pripada znači vraćanje stanja, i mesto za pogledati je rukovanje q / Q oko forme ili putanje koja ga je nasledila

Tiling patterni su jedna od onih PDF funkcija koje ostaju nevidljive dok fajl kome su potrebne ne sleti u vaš inbox, i onda su ceo posao. Ako gradite pregledače crteža, konvertere inženjerskih dokumenata ili renderere izveštaja na Delphi ili C++Builder, kompletna komponenta i njen API renderovanja dokumentovani su na stranici HotPDF Delphi PDF komponente