Tekninen artikkeli

PDF-laattakuvioiden renderöinti Delphissä HotPDF:llä

Viivoitus, joka renderöityy yhdeksi tasaiseksi harmaaksi lohkoksi, on klassinen laattakuvion vikatapaus. HotPDF, natiivi VCL PDF-komponentti Delphille ja C++Builderille, maalaa PatternType 1:n muuttamalla nykyisen polun väliaikaiseksi leikkaukseksi ja toistamalla kuvion sisältövirran kerran per näkyvä laatta, kuviovalinnan pysyessä grafiikkatilassa ja palautuen q- ja Q-operaattoreilla

Oireet saapuvat kahdessa muodossa, ja ne näyttävät toisiinsa liittymättömiltä, ennen kuin tietää syyn. CAD-piirustus menettää osaviivoituksensa ja palaa täysinä täyttöinä, koska renderöijä ratkaisi kuvion keskiarvoväriksi ja maalasi sen. Tai viivoitus karkaa: otsikkolohko, jonka pitäisi olla tasainen valkoinen, poimii viistoviivat yksityiskohtanäkymästä kaksi polkua aiemmin. Molemmat ovat kuviotilaongelmia, ja vain toinen niistä koskee lainkaan laattojen piirtämistä

Miksi laattakuvio vuotaa seuraavaan polkuun?

Koska valittu kuvion nimi on osa grafiikkatilaa, ei sitä käyttäneen operaattorin ominaisuus. ISO 32000-1 §8.6.6.2 määrittelee Pattern-väriavaruuden sellaiseksi, jonka väriarvo on kuvionimi, joka annetaan scn:lle tai SCN:lle, ja jokainen muu väritilan komponentti tallennetaan q:lla ja palautetaan Q:lla. Kuvion nimen täytyy noudattaa samaa sääntöä. HotPDF pitää sen tilatietueessa FillPatternName- ja StrokePatternName-nimillä, täyttö- ja ääriviivaväriavaruusperheen rinnalla, joten Q panee edellisen valinnan takaisin täsmälleen samalla tavalla kuin se panee takaisin edellisen CTM:n

Tallenna tuo nimi sen sijaan paikalliseen muuttujaan operaattorilähettäjän sisällä, ja se selviää jokaisen Q:n virrassa. Vika ilmenee sitten jossain odottamattomassa paikassa: kuvioidun polun jälkeen piirretty lomake-XObject perii kuviovalinnan, jota sen oma sisältövirta ei koskaan tehnyt, ja sen täytöt tulevat viivoitettuina. Sisäkkäiset lomakkeet pahentavat asiaa, koska jokainen sisäkkäisyystaso työntää ja poistaa tilaa, jota harhautunut muuttuja jättää huomiotta. Ei-kuviollisen väriavaruuden asettaminen cs:llä tai CS:llä, tai pelkän g:n / rg:n / k:n antaminen, täytyy myös tyhjentää kuvion nimi, muuten vanhentunut valinta elää pidempään kuin väriavaruus, joka antoi sille merkityksen

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

Kuvio maalataan leikkauksen kautta, ei koskaan täyttönä

Oikea malli on vähentävä: rajoita laiteleikkaus maalattavaan muotoon, aja sitten kuvion sisältö sen sisällä. HotPDF ei koskaan piirrä ensin kiinteää likiarvoa ja ylimaalaa sitä, koska väli-kiinteä olisi näkyvissä laattojen välisten rakojen läpi ja se taistelisi mitä tahansa laattasisällössä olevaa läpinäkyvyyttä vastaan. §8.7.3.2 kuvaa laattakuvion sisältövirraksi, joka toistetaan kiinteillä vaaka- ja pystyväleillä, ja toistaminen on järkevää vain leikkausta vasten, jolla jo on oikea muoto. Täytöille muunnos on suora: HPDFSelectFillPathClip asettaa monikulmion täyttötilan ALTERNATE-tilaan f*-, B*- ja b*-operaattoreille ja WINDING-tilaan nollasta-poikkeaville varianteille, rakentaa GDI-polun ja leikkaa sen leikkaukseen SelectClipPath:lla. Tuo yksi rivi on se, mikä saa parillinen-pariton-kuvioidun täytön jättämään samat reiät kuin parillinen-pariton-kiinteä täyttö, mikä on juuri se, mitä donitsinmuotoinen viivoitettu alue tarvitsee

Ääriviivat ovat osa, joka on helppo tehdä väärin. Ääriviivoitetulla polulla ei ole sisätilaa, joten polun itsensä leikkaaminen leikkaukseen tuottaa tyhjän alueen eikä mitään maalata. HPDFSelectStrokePathClip rakentaa siis ensin geometrisen kynän nykyisestä tilasta, käyttäen PS_GEOMETRIC:tä J:stä tulevan päätekorkin, j:stä tulevan liitoksen, M:stä tulevan viistorajan kanssa, ja PS_USERSTYLE:tä kun katkoviivataulukko on aktiivinen, kutsuu sitten WidenPath:ia muuttaakseen ääriviivoitetun ääripiirron täytettäväksi alueeksi ennen leikkaamista. Päätekorkin, liitoksen, viistorajan ja katkoviivan käyttäytyminen kuviolla ääriviivoitetulla polulla täsmää siis normaaliin ääriviivaan rakenteen perusteella eikä toisen toteutuksen. Kaksi rehellistä rajaa asuu täällä: laitetta yhden yksikön alle jäävät viivanleveydet rajataan yhteen pikseliin, ja katkoviivataulukko katkaistaan kuudentoista merkinnän kohdalla, mikä on katto, jonka ExtCreatePen hyväksyy

Mitkä laatat ovat todella näkyvissä?

Näkyvä alue tulee ajamalla muunnos taaksepäin. Laattojen sijoittelu tapahtuu kuvioavaruudessa, mutta ainoa asia, joka tietää, kuinka paljon sivua kosketaan, on laiteleikkauslaatikko, joka on laiteavaruudessa. HotPDF laatii BaseMatrix := CTM * PatternMatrix:n, kääntää sen ja kuvaa GDI-leikkauslaatikon neljä kulmaa takaisin käänteisen läpi. Noiden neljän kuvatun kulman akselinsuuntaiset rajat antavat kuvioavaruuden suorakulmion, joka voidaan mahdollisesti peittää, ja tuon suorakulmion jakaminen XStep:llä ja YStep:llä kuvion BBox:ia vasten antaa suljetut indeksialueet. Jokainen solu renderöityy sitten CTM:llä CTM * PatternMatrix * Translate(i * XStep, j * YStep), ja se leikataan toisen kerran omaan muunnettuun BBox-monikulmioonsa. Tuo toinen leikkaus merkitsee, kun XStep on pienempi kuin rajaavan laatikon leveys, mikä on tapa, jolla päällekkäiset laattasuunnittelut ilmaistaan; ilman sitä naapurisolut maalaisivat toistensa päälle ilmoitetun laajuutensa ulkopuolella. Jos solukohtainen leikkaus tulee takaisin NULLREGION:na, solu ohitetaan tokenisoimatta tai suorittamatta mitään

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

Väritömät kuviot ja ulkopuolelta tuleva väri

PaintType 2 -kuvio kantaa muodon mutta ei väriä, ja väri saapuu kuvion nimen mukana. §8.7.3.2 määrittelee, että väritöntä kuviota käytetään vain Pattern-väriavaruuden kanssa, joka ilmoittaa taustalla olevan avaruuden, joten scn vastaanottaa ensin komponenttiarvot ja viimeiseksi kuvion nimen. HotPDF ratkaisee nuo komponentit kuvion väriavaruusmerkintään tallennetun taustalla olevan avaruuden kautta, mikä tarkoittaa, että väritön viivoitus voidaan sävyttää Separation-musteella tai DeviceN-yhdistelmällä täsmälleen kuten mikä tahansa muu täyttö; tuon ratkaisun mekaniikka käsitellään artikkelissa Separation- ja DeviceN-lisävärien renderöinti. Laatan sisällä kaksi maalaustyyppiä eroavat jyrkästi. PaintType 2:lle renderöijä asettaa väriopraattorin vaimennuslipun laatan ajaksi, joten mikä tahansa g, rg, k tai scn kuvion sisällössä jätetään huomiotta ja jokainen merkki ottaa ulkoisesti annetun värin. PaintType 1:lle pätee päinvastainen: täyttö- ja ääriviivatila palautetaan PDF-oletuksiin, DeviceGray-musta identiteettiväriavaruudella, ja laatta värittää itse itsensä. Tuon palautuksen ohittaminen antaa värin, joka sattui olemaan käytössä f-operaattorin kohdalla, vuotaa kuvioon, jonka piti olla itsekuvaava

Miksi grafiikkatilapinon syvyys täytyy palauttaa jokaisen laatan jälkeen?

Koska kuvion sisältövirralla on lupa olla epätasapainossa, ja vahinko kasautuu solujen yli. Laatta, jonka virta sisältää kolme q-operaattoria ja kaksi Q-operaattoria, jättää pinon yhden kehyksen syvemmäksi kuin se alkoi. Palauta vain nykyinen tilatietue solujen välillä, ja syvyys jatkaa kasvamistaan, joten solu numero kaksisataa suoritetaan pinokehyksestä, joka kuuluu solulle numero sata yhdeksänkymmentäyhdeksän, sillä CTM:llä ja leikkauksella, joita tuo kehys kantoi. HotPDF ottaa siis tilannekuvan tilatietueesta ja pinon syvyydestä ennen laattasilmukkaa ja kutsuu RestoreSnapshot:ia jokaisen iteraation alussa, mikä katkaisee pinon takaisin tallennettuun pituuteen ja asentaa tallennetun tilan yhdessä vaiheessa. Sivun Resources-sanakirja ja väriopraattorin vaimennuslippu palautetaan samalla rajalla, koska laatta voi viitata omiin resursseihinsa eikä saa antaa niitä naapurilleen. GDI-leikkaustila saa saman kohtelun SaveDC / RestoreDC-parin kautta jokaisen solun ympärillä, joten laatta, joka asentaa oman W n -leikkauksensa, ei voi kutistaa seuraavalle käytettävissä olevaa aluetta

Budjetit, kieltäytymiset ja se, mitä renderöijä ei piirrä

Laattakuviot ovat helpoin paikka PDF:ssä kirjoittaa palvelunestotiedosto, joten rajat ovat kovia lukuja eivätkä heuristiikkaa. Kuvion sisäkkäisyys on rajattu syvyyteen 4, sama vartija, jota käytetään lomake-XObject-rekursiossa, mikä pysäyttää kuvion, joka viittaa itseensä oman resurssisanakirjansa kautta. Yksi polun maalaus saa suorittaa enintään 16 384 laattaa yhteensä, laskettuna alaspäin sisäkkäisten kuvioiden yli ja nollattuna vain kun uloin kuvion maalaus alkaa. Laattaruudukko, jonka suunniteltu solumäärä ylittää sen, mitä tuosta budjetista on jäljellä, hylätään suoralta kädeltä, ennen kuin yhtäkään solua ajetaan

Rappeutunut geometria hylätään sen sijaan, että se approksimoitaisiin. Puuttuva tai nollapinta-alainen BBox, XStep tai YStep, jonka suuruus on alle 1e-6, CTM * PatternMatrix-tulo, jolla ei ole käänteistä, kuvatut leikkauskoordinaatit yli 1e9, tai indeksin suuruus yli miljoonan, kaikki saavat kuvion maalauksen palaamaan piirtämättä. Tulos on maalaamaton alue jumiutuneen renderöintisäikeen sijaan, mikä on kompromissi, jonka haluat eräkonvertterissa. Suorituskyky tulee yhdestä päätöksestä: kuviovirta tokenisoituu kerran per maalaus HPDFTokenizeContentStream:lla, ja tokenitaulukkoa käytetään uudelleen jokaisen näkyvän solun yli, joten laattamäärä kertautuu suoritusajassa mutta ei koskaan leksikaalisessa jäsentämisessä

Kuviollisen sivun renderöinti Delphistä

Mikään kuviotuessa ei muuta kutsuvaa koodia. Lataa asiakirja, pyydä sivua, ja laattatyö tapahtuu sisältövirran tulkin sisällä, jota sivusta bittikartaksi -renderöinti jo ajaa. Sama tulkki ruokkii bittikartta-, metatiedosto- ja tulostinlaitekonteksteja, joten viivoitettu piirustus, joka näyttää oikealta esikatselun pikkukuvassa, tulostuu samalla laattageometrialla. PatternType 2 -sävytyskuviot ottavat eri haaran, joka jakaa arviointipolkunsa paljaan sh-operaattorin kanssa, kuvattu yksityiskohtaisesti kohdassa aksiaali- ja radiaalisävytysten renderöinti

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;

Kun kuvioitu alue vielä näyttää väärältä, tarkista kolme vikaluokkaa järjestyksessä. Kokonaan tyhjä alue tarkoittaa yleensä kieltäytymistä: tarkasta XStep, YStep ja BBox rappeutuneiden arvojen varalta, tai laske laatat, joita ruudukko tarvitsisi, 16 384:n kattoa vasten. Yhdellä tasaisella värillä maalattu alue tarkoittaa, että kuvion nimi ei koskaan saavuttanut maalausoperaattoria, mikä osoittaa cs:n ja scn:n järjestykseen virrassa. Kuvio, joka ilmestyy sinne, minne se ei kuulu, tarkoittaa tilan palautusta, ja paikka, johon kannattaa katsoa, on q / Q-käsittely lomakkeen tai polun ympärillä, joka perii sen

Laattakuviot ovat yksi niistä PDF-ominaisuuksista, jotka pysyvät näkymättöminä, kunnes tiedosto, joka niitä tarvitsee, päätyy postilaatikkoosi, ja sitten ne ovat koko työ. Jos rakennat piirustuskatseluohjelmia, teknisten asiakirjojen konverttereita tai raporttirenderöijiä Delphillä tai C++Builderilla, täysi komponentti ja sen renderöinti-API on dokumentoitu HotPDF Delphi PDF -komponentin sivulla