Skravering, der gengives som én flad grå blok, er den klassiske fejl ved tiling-mønstre. HotPDF, den native VCL PDF-komponent til Delphi og C++Builder, tegner PatternType 1 ved at gøre den aktuelle sti til en midlertidig klipning og genafspille mønster-indholdsstrømmen én gang pr. synlig tile, med mønstervalg holdt i grafiktilstanden og gendannet af q og Q
Symptomerne kommer i to varianter, og de ser urelaterede ud, indtil man kender årsagen. En CAD-tegning mister sin snitskravering og kommer tilbage som solide fyld, fordi rendereren løste mønsteret til en gennemsnitsfarve og malede den. Eller skraveringen slipper ud: en titelblok, der skulle være ren hvid, samler de diagonale linjer op fra en detaljetegning to stier tidligere. Begge er mønstertilstandsproblemer, og kun det ene af dem handler om at tegne tiles overhovedet
Hvorfor bløder et tiling-mønster ud på den næste sti?
Fordi det valgte mønsternavn er del af grafiktilstanden, ikke en egenskab ved den operator, der brugte det. ISO 32000-1 §8.6.6.2 definerer et Pattern-farverum som ét, hvis farveværdi er et mønsternavn leveret til scn eller SCN, og hver anden komponent af farvetilstanden gemmes af q og genoprettes af Q. Mønsternavnet skal følge samme regel. HotPDF holder det i state-recorden som FillPatternName og StrokePatternName, sammen med farverumsfamilien for fyld og strøg, så en Q lægger det forrige valg tilbage nøjagtig, som den lægger den forrige CTM tilbage
Gemmer man det navn i en lokal variabel inde i operator-dispatcheren i stedet, overlever den hver Q i strømmen. Fejlen dukker så op et uventet sted: en Form-XObject tegnet efter den mønstrede sti arver et mønstervalg, dens egen indholdsstrøm aldrig foretog, og dens fyld kommer ud skraveret. Indlejrede formularer gør det værre, fordi hvert indlejringsniveau skubber og popper tilstand, som den forvildede variabel ignorerer. At sætte et ikke-mønster-farverum med cs eller CS, eller udstede et almindeligt g / rg / k, skal også rydde mønsternavnet, ellers overlever det forældede valg det farverum, der gav det mening
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
Et mønster males gennem en klipning, aldrig som et fyld
Den korrekte model er subtraktiv: begræns enhedens klipning til formen, der males, og kør så mønster-indholdet inde i den. HotPDF tegner aldrig en solid tilnærmelse først og overmaler den, fordi den mellemliggende solide ville være synlig gennem hullerne mellem tiles og ville kollidere med enhver transparens i tile-indholdet. §8.7.3.2 beskriver et tiling-mønster som en indholdsstrøm, der gentages med faste horisontale og vertikale mellemrum, og gentagelse giver kun mening mod en klipning, der allerede har den rigtige form. For fyld er konverteringen direkte: HPDFSelectFillPathClip sætter polygonfyldtilstanden til ALTERNATE for f*, B* og b* og til WINDING for nonzero-varianterne, bygger GDI-stien og skærer den ind i klipningen med SelectClipPath. Den ene linje er det, der får et even-odd mønstret fyld til at efterlade de samme huller som et even-odd solidt fyld, hvilket er præcis, hvad en donutformet skraveret region behøver
Strøg er den del, det er let at få galt. En strøget sti har intet indre, så at skære selve stien ind i klipningen giver en tom region, og intet males. HPDFSelectStrokePathClip bygger derfor en geometrisk pen fra den aktuelle tilstand først, ved brug af PS_GEOMETRIC med endehætten fra J, samlingen fra j, miter-grænsen fra M, og PS_USERSTYLE, når et stiplet array er aktivt, og kalder så WidenPath for at konvertere det strøgede omrids til en fyldbar region, før klipning. Endehætte-, samlings-, miter- og stiple-adfærd på en mønster-strøget sti matcher så en normal strøg ved konstruktion frem for ved en anden implementering. To ærlige grænser bor her: linjebredder under én enhedspixel klemmes til én pixel, og det stiplede array afkortes ved seksten poster, hvilket er loftet, ExtCreatePen accepterer
Hvilke tiles er faktisk synlige?
Det synlige interval kommer fra at køre transformen baglæns. Tile-placering sker i mønsterrum, men det eneste, der ved, hvor meget af siden der berøres, er enhedens klipningsboks, som er i enhedsrum. HotPDF sammensætter BaseMatrix := CTM * PatternMatrix, inverterer den og mapper de fire hjørner af GDI-klipningsboksen tilbage gennem inversen. De aksejusterede grænser af de fire mappede hjørner giver det mønsterrums-rektangel, der potentielt kan dækkes, og at dividere det rektangel med XStep og YStep mod mønster-BBox'en giver lukkede indeksintervaller. Hver celle rendrer så med en CTM på CTM * PatternMatrix * Translate(i * XStep, j * YStep), og klippes en anden gang til sin egen transformerede BBox-polygon. Den anden klipning betyder noget, når XStep er mindre end bounding box-bredden, hvilket er, hvordan overlappende tile-design udtrykkes; uden den ville nabo-celler male over hinanden uden for deres erklærede udstrækning. Kommer per-celle-klipningen tilbage som NULLREGION, springes cellen over uden tokenisering eller udførelse af noget
// 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));
Ufarvede mønstre og farven, der kommer udefra
Et PaintType 2-mønster bærer form men ingen farve, og farven ankommer med mønsternavnet. §8.7.3.2 specificerer, at et ufarvet mønster kun bruges med et Pattern-farverum, der erklærer et underliggende rum, så scn modtager komponentværdierne først og mønsternavnet sidst. HotPDF løser de komponenter gennem det underliggende rum, gemt på mønster-farverum-posten, hvilket betyder, at en ufarvet skravering kan tones med en Separation-blæk eller en DeviceN-kombination nøjagtig som ethvert andet fyld; mekanikken i den løsning er dækket i rendering af Separation- og DeviceN-spotfarver. Inde i tilen afviger de to maletyper skarpt. For PaintType 2 sætter rendereren et flag, der undertrykker farveoperatorer for tilens varighed, så enhver g, rg, k eller scn i mønster-indholdet ignoreres, og hvert mærke tager den eksternt leverede farve. For PaintType 1 gælder det modsatte: fyld- og strøg-tilstand nulstilles til PDF-standarderne, DeviceGray sort med et identitets-farverum, og tilen farver sig selv. Springer man den nulstilling over, lækker den farve, der tilfældigvis var aktuel ved f-operatoren, ind i et mønster, der skulle have været selvbeskrivende
Hvorfor skal grafiktilstandens stackdybde genoprettes efter hver tile?
Fordi en mønster-indholdsstrøm har lov til at være ubalanceret, og skaden akkumuleres på tværs af celler. En tile, hvis strøm indeholder tre q-operatorer og to Q-operatorer, efterlader stacken én ramme dybere, end den startede. Genopretter man kun den aktuelle state-record mellem celler, bliver dybden ved med at vokse, så celle nummer to hundrede udføres fra en stack-ramme, der hører til celle nummer et hundrede og nioghalvfems, med hvad end CTM og klipning den ramme bar. HotPDF snapshotter derfor state-recorden og stack-dybden før tile-løkken og kalder RestoreSnapshot øverst i hver iteration, hvilket afkorter stacken tilbage til den gemte længde og geninstallerer den gemte tilstand i ét trin. Sidens Resources-dictionary og flaget, der undertrykker farveoperatorer, genoprettes ved samme grænse, da en tile kan referere sine egne ressourcer og ikke må aflevere dem til naboen. GDI-klipningstilstanden får samme behandling gennem et SaveDC / RestoreDC-par omkring hver celle, så en tile, der installerer sin egen W n-klipning, ikke kan formindske regionen tilgængelig for den næste
Budgetter, afvisninger, og hvad rendereren ikke vil tegne
Tiling-mønstre er det letteste sted i en PDF at skrive en denial-of-service-fil, så grænserne er hårde tal frem for heuristik. Mønsterindlejring er begrænset til dybde 4, samme vagt brugt til Form-XObject-rekursion, hvilket stopper et mønster, der refererer til sig selv gennem sit eget ressource-dictionary. En enkelt sti-maling må udføre højst 16.384 tiles i alt, talt ned på tværs af indlejrede mønstre og nulstillet kun, når det yderste mønsters maling begynder. Et tile-grid, hvis planlagte celletal overstiger, hvad der er tilbage af det budget, afvises straks, før en eneste celle kører
Degenereret geometri afvises frem for tilnærmet. En manglende eller nul-areal BBox, en XStep eller YStep, hvis størrelse er under 1e-6, et CTM * PatternMatrix-produkt uden invers, mappede klipningskoordinater ud over 1e9, eller en indeksstørrelse ud over en million forårsager alle, at mønstermalingen returnerer uden at tegne. Resultatet er en umalet region i stedet for en hængende render-tråd, hvilket er den afvejning, man ønsker i en batch-konverter. Ydeevne kommer fra én beslutning: mønster-strømmen tokeniseres én gang pr. maling med HPDFTokenizeContentStream, og token-arrayet genbruges på tværs af hver synlig celle, så tile-antal multiplicerer udførelsesomkostningen, men aldrig lexing-omkostningen
Rendering af en mønstret side fra Delphi
Intet ved mønsterstøtte ændrer den kaldende kode. Indlæs dokumentet, bed om en side, og tile-arbejdet sker inde i den indholdsstrøm-fortolker, som side-til-bitmap-rendering allerede kører. Samme fortolker fodrer bitmap-, metafil- og printer-enhedskontekster, så en skraveret tegning, der ser rigtig ud i en preview-thumbnail, printer med samme tile-geometri. PatternType 2-skygge-mønstre tager en anden gren, der deler sin evalueringssti med den blotte sh-operator, beskrevet i detaljer under rendering af aksiale og radiale gradienter
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;
Når en mønstret region stadig ser forkert ud, tjek de tre fejlklasser i rækkefølge. En region, der er helt tom, betyder som regel en afvisning: undersøg XStep, YStep og BBox for degenererede værdier, eller tæl de tiles, gridet ville behøve, mod loftet på 16.384. En region malet i én flad farve betyder, at mønsternavnet aldrig nåede male-operatoren, hvilket peger på cs- og scn-rækkefølge i strømmen. Et mønster, der dukker op, hvor det ikke hører til, betyder tilstandsgenopretning, og stedet at kigge er q / Q-håndteringen omkring den formular eller sti, der arvede det
Tiling-mønstre er en af de PDF-funktioner, der forbliver usynlige, indtil filen, der behøver dem, lander i indbakken, og så er de hele opgaven. Bygger man tegningsviewere, tekniske dokumentkonvertere eller rapportrenderere på Delphi eller C++Builder, er den fulde komponent og dens render-API dokumenteret på siden for HotPDF Delphi PDF-komponent