Tehnični članak

Izrisovanje PDF vzorčnih ploščic v Delphiju s HotPDF

Šrafiranje, ki se izriše kot en sam raven sivi blok, je klasična napaka vzorčnih ploščic. HotPDF, izvorna VCL PDF komponenta za Delphi in C++Builder, pobarva PatternType 1 tako, da trenutno pot spremeni v začasno obrezovanje in nato predvaja vsebinski tok vzorca enkrat na vsako vidno ploščico, izbira vzorca pa se hrani v grafičnem stanju in obnovi z q in Q

Simptomi prihajajo v dveh okusih, videti pa sta nepovezana, dokler ne poznate vzroka. CAD risba izgubi svoje presečno šrafiranje in se vrne kot polna polnila, ker je izrisovalnik vzorec razrešil v povprečno barvo in pobarval to. Ali pa šrafiranje pobegne: naslovni blok, ki bi moral biti navadno bel, prevzame diagonalne črte iz podrobnostnega pogleda nekaj poti prej. Oba sta problema stanja vzorca, le eden od njiju pa je sploh povezan z risanjem ploščic

Zakaj vzorčna ploščica prekrvavi na naslednjo pot?

Ker je izbrano ime vzorca del grafičnega stanja, ne lastnost operatorja, ki ga je uporabil. ISO 32000-1 §8.6.6.2 opredeljuje barvni prostor Pattern kot takega, katerega barvna vrednost je ime vzorca, podano scn ali SCN, vsaka druga komponenta stanja barve pa se shrani z q in obnovi z Q. Ime vzorca mora slediti istemu pravilu. HotPDF ga hrani v zapisu stanja kot FillPatternName in StrokePatternName, poleg družine barvnega prostora polnjenja in poteze, tako da Q postavi prejšnjo izbiro nazaj natanko tako, kot postavi nazaj prejšnji CTM

Shranite to ime v lokalno spremenljivko znotraj razpošiljevalnika operatorjev, in preživi vsak Q v toku. Napaka se nato pokaže nekje nepričakovano: obrazčni XObject, narisan po poti z vzorcem, podeduje izbiro vzorca, ki je njegov lasten vsebinski tok nikoli ni naredil, njegova polnila pa pridejo šrafirana. Gnezdeni obrazci to poslabšajo, saj vsaka raven gnezdenja potisne in izvleče stanje, ki ga potepuška spremenljivka ignorira. Nastavitev barvnega prostora, ki ni vzorec, z cs ali CS, ali izdaja navadnega g / rg / k, mora prav tako počistiti ime vzorca, sicer zastarela izbira preživi barvni prostor, ki ji je dal pomen

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

Vzorec se pobarva prek obrezovanja, nikoli kot polnilo

Pravilni model je odštevalen: omejite obrezovanje naprave na obliko, ki se barva, nato pa v njej poženite vsebino vzorca. HotPDF nikoli najprej ne nariše polnega približka in ga nato prebarva, ker bi bilo vmesno polnilo vidno skozi vrzeli med ploščicami in bi se prepiralo s kakršno koli prosojnostjo v vsebini ploščice. §8.7.3.2 opisuje vzorčno ploščico kot vsebinski tok, podvojen v fiksnih vodoravnih in navpičnih intervalih, podvajanje pa ima smisel le proti obrezovanju, ki že ima pravilno obliko. Za polnila je pretvorba neposredna: HPDFSelectFillPathClip nastavi način polnjenja poligona na ALTERNATE za f*, B* in b* ter na WINDING za varianti brez ničle, zgradi pot GDI in jo preseka z obrezovanjem prek SelectClipPath. Prav ta vrstica poskrbi, da polnjenje z vzorcem po pravilu sodo-liho pusti enake luknje kot polno polnjenje po pravilu sodo-liho, kar je natanko tisto, kar potrebuje krofasto oblikovana šrafirana regija

Poteze so del, ki ga je lahko zgrešiti. Potezana pot nima notranjosti, zato presek same poti z obrezovanjem da prazno regijo in nič se ne pobarva. HPDFSelectStrokePathClip zato najprej zgradi geometrijski svinčnik iz trenutnega stanja, z uporabo PS_GEOMETRIC z zaključkom iz J, spojem iz j, mejo zajeda iz M, in PS_USERSTYLE, kadar je aktivno polje črtkanja, nato pa pokliče WidenPath, da pretvori potezan obris v regijo, ki jo je mogoče zapolniti, preden jo obreže. Obnašanje zaključka, spoja, zajeda in črtkanja na poti z vzorcem potem ujema navadno potezo po konstrukciji, ne po drugi implementaciji. Tu obstajata dve iskreni meji: širine črt pod eno napravno enoto so priklenjene na en piksel, polje črtkanja pa je odrezano pri šestnajstih vnosih, kar je strop, ki ga sprejema ExtCreatePen

Katere ploščice so dejansko vidne?

Vidni obseg izhaja iz izvedbe transformacije nazaj. Postavljanje ploščic se zgodi v prostoru vzorca, edino, kar pa ve, koliko strani je dotaknjeno, pa je škatla obrezovanja naprave, ki je v prostoru naprave. HotPDF sestavi BaseMatrix := CTM * PatternMatrix, jo obrne in preslika štiri vogale škatle obrezovanja GDI nazaj skozi inverzno matriko. Osi poravnane meje teh štirih preslikanih vogalov dajo pravokotnik v prostoru vzorca, ki bi lahko bil pokrit, deljenje tega pravokotnika z XStep in YStep proti BBox vzorca pa da zaprte obsege indeksov. Vsaka celica se nato izriše s CTM CTM * PatternMatrix * Translate(i * XStep, j * YStep) in se drugič obreže na svoj lasten transformiran poligon BBox. To drugo obrezovanje je pomembno, kadar je XStep manjši od širine mejne škatle, kar je način, kako se izrazijo prekrivajoče se zasnove ploščic; brez njega bi sosednje celice barvale ena čez drugo zunaj svojega deklariranega obsega. Če se obrezovanje posamezne celice vrne kot NULLREGION, se celica preskoči brez tokenizacije ali izvajanja česar koli

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

Nepobarvani vzorci in barva, ki prihaja od zunaj

Vzorec PaintType 2 nosi obliko, a ne barve, barva pa pride skupaj z imenom vzorca. §8.7.3.2 navaja, da se nepobarvan vzorec uporablja le z barvnim prostorom Pattern, ki deklarira osnovni prostor, zato scn najprej prejme vrednosti komponent, nato pa ime vzorca. HotPDF te komponente razreši prek osnovnega prostora, shranjenega na vnosu barvnega prostora vzorca, kar pomeni, da je mogoče nepobarvano šrafuro tonirati z barvilom Separation ali kombinacijo DeviceN natanko tako kot katero koli drugo polnilo; mehaniko tega razreševanja pokriva izrisovanje barv Separation in DeviceN spot. Znotraj ploščice se oba tipa barvanja ostro razideta. Pri PaintType 2 izrisovalnik za trajanje ploščice nastavi zastavico za zatiranje barvnih operatorjev, tako da se vsak g, rg, k ali scn v vsebini vzorca ignorira, vsaka oznaka pa prevzame zunaj podano barvo. Za PaintType 1 velja nasprotno: stanje polnjenja in poteze se ponastavi na privzete vrednosti PDF, DeviceGray črna z identitetnim barvnim prostorom, ploščica pa se pobarva sama. Izpuščanje te ponastavitve pusti barvo, ki je bila trenutna ob operatorju f, da pricurlja v vzorec, ki naj bi bil samoopisen

Zakaj je treba globino sklada grafičnega stanja obnoviti po vsaki ploščici?

Ker je vsebinski tok vzorca lahko neuravnotežen, škoda pa se seštevaje čez celice. Ploščica, katere tok vsebuje tri operatorje q in dva operatorja Q, pusti sklad za en okvir globlji, kot se je začel. Obnovite le trenutni zapis stanja med celicami, in globina se še naprej povečuje, tako da se celica številka dvesto izvede iz okvira sklada, ki pripada celici številka sto devetindevetdeset, s katerim koli CTM in obrezovanjem, ki ga je ta okvir nosil. HotPDF zato pred zanko ploščic zajame zapis stanja in globino sklada ter na vrhu vsake iteracije pokliče RestoreSnapshot, ki sklad skrajša nazaj na shranjeno dolžino in v enem koraku ponovno namesti shranjeno stanje. Slovar virov strani in zastavica zatiranja barvnih operatorjev se obnovita na isti meji, ker se ploščica lahko sklicuje na svoje lastne vire in jih ne sme predati sosedu. Stanje obrezovanja GDI dobi enako obravnavo prek para SaveDC / RestoreDC okoli vsake celice, tako da ploščica, ki namesti svoje lastno obrezovanje W n, ne more skrčiti regije, ki je na voljo naslednji

Proračuni, zavrnitve in kaj izrisovalnik ne bo narisal

Vzorčne ploščice so najlažje mesto v PDF-ju za pisanje datoteke za zavrnitev storitve, zato so omejitve trde številke, ne hevristike. Gnezdenje vzorcev je omejeno na globino 4, isto varovalko, uporabljeno za rekurzijo obrazčnega XObjecta, ki ustavi vzorec, ki se sklicuje sam nase prek lastnega slovarja virov. En sam prehod barvanja poti lahko izvede največ 16.384 ploščic skupno, štetih navzdol čez gnezdene vzorce in ponastavljenih le, ko se začne barvanje najbolj zunanjega vzorca. Mreža ploščic, čigar načrtovano število celic presega, kar je ostalo od tega proračuna, se povsem zavrne, preden teče ena sama celica

Degenerirana geometrija se zavrne namesto približa. Manjkajoč ali ničelne površine BBox, XStep ali YStep, katerega velikost je pod 1e-6, produkt CTM * PatternMatrix brez inverza, preslikane koordinate obrezovanja preko 1e9 ali velikost indeksa preko milijona, vsi povzročijo, da se barvanje vzorca vrne brez risanja. Rezultat je nepobarvana regija namesto obešene niti izrisovanja, kar je kompromis, ki ga želite v paketnem pretvorniku. Zmogljivost izhaja iz ene odločitve: tok vzorca se tokenizira enkrat na barvanje z HPDFTokenizeContentStream, polje žetonov pa se ponovno uporabi čez vsako vidno celico, tako da število ploščic pomnoži strošek izvajanja, nikoli pa strošek leksikalne analize

Izrisovanje strani z vzorcem iz Delphija

Nič v podpori za vzorce ne spremeni klicne kode. Naložite dokument, zahtevajte stran, delo s ploščicami pa se zgodi znotraj tolmača vsebinskega toka, ki ga že poganja izrisovanje strani v sličico. Isti tolmač hrani bitne slike, metadatoteke in kontekste naprave tiskalnika, tako da šrafirana risba, ki je videti pravilno v predogledni sličici, natisne z isto geometrijo ploščic. Senčilni vzorci PatternType 2 gredo po drugi veji, ki si svojo pot ovrednotenja deli z golim operatorjem sh, opisano podrobno pod izrisovanjem osnih in radialnih senčenj

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;

Kadar regija z vzorcem še vedno izgleda narobe, preverite tri razrede napak po vrsti. Regija, ki je povsem prazna, običajno pomeni zavrnitev: preverite XStep, YStep in BBox za degenerirane vrednosti ali preštejte ploščice, ki bi jih mreža potrebovala, proti stropu 16.384. Regija, pobarvana v eno samo ravno barvo, pomeni, da ime vzorca ni nikoli doseglo operatorja barvanja, kar kaže na vrstni red cs in scn v toku. Vzorec, ki se pojavi tam, kamor ne spada, pomeni obnovitev stanja, mesto za pogled pa je obravnava q / Q okoli obrazca ali poti, ki ga je podedovala

Vzorčne ploščice so ena tistih PDF funkcij, ki ostanejo nevidne, dokler datoteka, ki jih potrebuje, ne pristane v vašem nabiralniku, nato pa so cela naloga. Če gradite ogledovalnike risb, pretvornike inženirskih dokumentov ali izrisovalnike poročil v Delphiju ali C++Builderju, sta celotna komponenta in njen API za izrisovanje dokumentirana na strani komponente HotPDF Delphi PDF