HotPDF, natiivi Delphi- ja C++Builder-PDF-komponentti, tuo Windowsin EMF- ja WMF-metatiedostoja tulkitsemalla jokaisen GDI-tietueen suoraan PDF-operaattoreiksi sen sijaan, että tiedosto litistettäisiin bittikartaksi: liukuvärjäystäytöistä tulee PDF:n aksiaalisia varjostuskuvioita, viivoitusharjoista tulee PDF:n toistokuvioita, ja keskitetty polkutilan portti estää virheellisiä tietueita turmelemasta tulostetta. Jokainen kaavio, jonka TChart, GDI+-pinta tai tavallinen TCanvas voi viedä laajennettuna metatiedostona, on tämän polun ehdokas, ja ero näkyy heti, kun joku zoomaa sivulle tai lähettää sen tarkkuudeltaan korkealle tulostimelle
Vaihtoehto, johon useimmat Delphi-kehittäjät turvautuvat oletuksena, on metatiedoston rasterointi bittikartaksi ennen sen pudottamista sivulle, ja kustannus paljastuu vasta myöhemmin: pylväskaavio, joka oli näytöllä terävä, muuttuu selvästi rakeiseksi heti, kun PDF tulostetaan 600 DPI:n tarkkuudella tai heijastetaan kokoushuoneen näytölle, ja viivoitustäyttöinen CAD-alue romahtaa yhdeksi tasaiseksi harmaaksi suorakulmioksi, jos täyttötyyliä ei siirretä mukana. Metatiedoston lukeminen ohjelmana kuvan sijaan välttää molemmat ongelmat, ja se on vaikeampi polku toteuttaa oikein, minkä vuoksi alla olevat sudenkuopat kannattaa tuntea ennen raportin julkaisua
Miksi tulkita metatiedosto sen sijaan, että se litistettäisiin bittikartaksi?
HotPDF pitää EMF- ja WMF-tuonnin vektoripolulla, koska Windows-metatiedosto on tallennettu sarja GDI-piirtokutsuja, ei kuva, ja noiden kutsujen toistaminen PDF:n polku-, teksti- ja varjostusoperaattoreina on se, mikä saa lopputuloksen skaalautumaan kuten sivun muukin sisältö. THPDFPage.ShowMetafile ja sen vastine ShowMetafileEx ovat sovelluksen kutsumat aloituspisteet, ja molemmat välittävät metatiedoston luokalle THPDFWmf, joka käy läpi jokaisen GDI-tietueen ja kääntää sen. Jako ei ole ehdoton, eikä HotPDF väitä muutakaan: metatiedoston tietue, joka on aidosti rasteridataa, esimerkiksi StretchDIBits-bittikarttasiirto, upotetaan todellisena PDF-kuva-XObjectina AddImage- ja ShowImage-kutsujen kautta, samojen kutsujen, joita mikä tahansa muukin sivun kuva käyttää, sen sijaan että se pakotettaisiin polkuoperaattoreiksi, jotka eivät voi ilmaista valokuvaa. Viivat, täytöt ja teksti pysyvät vektorimuotoisina; pikselit, jotka olivat pikseleitä jo lähteessä, pysyvät pikseleinä myös tulosteessa. Yksinkertaisin kutsu ei tarvitse muuta kuin ladatun metatiedoston:
var
Pdf: THotPDF;
Chart: TMetafile;
begin
Pdf := THotPDF.Create(nil);
Chart := TMetafile.Create;
try
Chart.LoadFromFile('quarterly-revenue.emf'); // exported from TChart or GDI+
Pdf.FileName := 'quarterly-report.pdf';
Pdf.BeginDoc;
Pdf.CurrentPage.ShowMetafile(Chart);
Pdf.EndDoc;
finally
Chart.Free;
Pdf.Free;
end;
end;
Miten tulkki muuttaa GDI-koordinaatit PDF-sivutilaksi?
HotPDF vastaa tähän yhdellä läpikäynnillä metatiedoston omaa tietuevirtaa, ei toisella GDI-toteutuksella. THPDFWmf.Analyse lukee metatiedoston otsikon Win32:n GetEnhMetaFileHeader-kutsun kautta, nollaa sisäisen piirtotilansa ja kutsuu EnumEnhMetafile-funktiota, samaa luettelointi-API:a, jota metatiedoston katseluohjelmakin käyttäisi, joten jokainen EMR_*-tietue saapuu funktioon THPDFWmf.ExecuteRecord siinä järjestyksessä, jossa se alun perin tallennettiin. GDI ilmaisee koordinaatit ylhäältä alas laite- tai loogisina yksikköinä, jotka metatiedoston oma kuvaustila valitsee; PDF-sivu taas on alhaalta ylös käyttäjätilan pisteinä, koordinaatistona, joka käsitellään artikkelissa HotPDF:n canvas-piirtomallista polkuja ja täyttöjä varten. Jokainen tietuekäsittelijä ratkaisee tämän eron funktioilla ScaleX ja ScaleY, jotka kutsuvat funktioita ProjectX ja ProjectY toistaakseen GDI:n oman ikkuna-näyttöalue-kaavan anisotrooppisille ja isotrooppisille kuvaustiloille, joten viiden loogisen yksikön levyinen tallennettu muoto päätyy oikean levyisenä PDF-pisteinä riippumatta siitä, mitkä ikkuna- ja näyttöalue-laajuudet lähdesovellus asetti
Miten GDI-liukuvärjäystäytöstä tulee PDF-varjostuskuvio?
EMR_GRADIENTFILL-tietueesta tulee todellinen PDF Type 2 -aksiaalinen varjostuskuvio (ISO 32000-1 §8.7.4.5) aina, kun GDI tallensi sen jommassakummassa kahdesta suorakulmiotilasta. THPDFWmf.VEMRGradientFill lukee tietueen oman rakenteen suoraan raa'asta tavupuskurista, noudattaen MS-EMF §2.3.1.6 -rakennetta: 16-bittisten RGBA-kulmien kärkitaulukko, jota seuraa luettelo suorakulmioista, joista kukin viittaa kahteen näistä kärjistä. GRADIENT_FILL_RECT_H-tilassa värit vaihtuvat vasemmalta oikealle suorakulmion vaakasuoraa keskiviivaa pitkin; GRADIENT_FILL_RECT_V-tilassa ne vaihtuvat ylhäältä alas pystysuoraa keskiviivaa pitkin. Kummassakin tapauksessa kaksi kulmaväriä ja projisoidut suorakulmiokoordinaatit menevät suoraan funktioon THotPDF.RegisterAxialGradient, joka palauttaa kuvion nimen, ja sivu piirtää suorakulmion ja täyttää sen kyseisen kuvion kautta (SetFillPattern) tasaisen SetRGBFillColor-kutsun sijaan, joten laskentataulukkomaisen raidoitetun otsikon tai kaavion liukuvärjätyn piirtoalueen sekoitus säilyy sen sijaan, että se romahtaisi yhdeksi keskiarvoväriksi
Gouraud-kolmiotila on rehellinen aukko. Kun tietueen ulMode-kenttä ilmoittaa arvon GRADIENT_FILL_TRIANGLE, VEMRGradientFill tunnistaa sen, kirjaa lokiin, että kolmiotilaa ei ole vielä toteutettu, ja ohittaa suorakulmion sen sijaan, että arvaisi kaksivärisen approksimaation. Kärkikohtainen, pikselikohtainen interpolointi mielivaltaisen kolmioverkon yli ei redusoidu kaksipysäkkiseksi aksiaaliseksi tai säteittäiseksi varjostukseksi, ja sen ilmaiseminen asianmukaisesti tarkoittaisi PDF Type 4- tai Type 5 -verkkovarjostuksen tuottamista, samaa varjostusperhettä, jonka myös HotPDF:n sivunrenderöijä jättää maalaamatta lukiessaan PDF:n takaisin. Kaksi toisiinsa liittymätöntä koodipolkua päätyvät samaan rajaan: verkkovarjostukset ovat aukko sekä kirjoitus- että lukupuolella, ja lähdekaavio, joka käyttää Gouraud-kolmioita pehmeään säteittäiseen hehkuun, palautuu viimeiseen kiinteään siveltimeen sen sijaan, että se renderöitäisiin approksimaationa
Viivoitusharjoista tulee toistokuvioita, ei litistettyä harmaata
GDI-viivoitusharja säilyttää tekstuurinsa PDF:ssä, koska THPDFWmf.SetBrushColor tarkistaa CurrentBrush.lbStyle-arvon BS_HATCHED-tyylin varalta, ennen kuin se koskaan palautuu tasaiseen täyttöön, ohjaten tämän tapauksen funktiolle SetHatchBrushPattern. Tuo metodi kirjoittaa 8x8-yksikön kokoisen PDF-sisältövirran ääriviivaoperaattoreista, m, l ja S, jotka valitaan GDI:n viivoitustyylin mukaan: yksi vaaka- tai pystyviiva tyyleille HS_HORIZONTAL ja HS_VERTICAL, kolme yhdensuuntaista vinoviivaa tyyleille HS_FDIAGONAL ja HS_BDIAGONAL, sekä vaaka- ja pystyviivan tai molempien vinoviivojen yhdistelmät tyyleille HS_CROSS ja HS_DIAGCROSS. THotPDF.RegisterTilingPattern rekisteröi tuon sisältövirran väritettynä toistokuviona (PaintType 1, ISO 32000-1 §8.7.3.1), jonka XStep ja YStep ovat 8 yksikköä, ja sivu täyttää sen SetFillPattern-kutsun kautta samalla tavalla kuin aksiaalinen varjostuskin. CAD-pohjapiirustus tai tekninen piirustus, joka nojaa viivoitustäyttöihin materiaalien erottamiseksi, säilyttää tuon visuaalisen kielen PDF:ssä sen sijaan, että jokainen alue muuttuisi samaksi harmaaksi
Jokainen sivellin ei ansaitse tätä kohtelua, ja aukko kannattaa tuntea ennen CAD-tuonnin julkaisua. EMR_CREATEDIBPATTERNBRUSHPT, tietue mukautetulle bittikartta-kuvakuviosiveltimelle GDI:n kuuden vakioviivoitustyylin sijaan, rekisteröi vain kahvansa, jotta myöhemmät SELECTOBJECT- ja DELETEOBJECT-tietueet pysyvät johdonmukaisina; HotPDF ei vielä tarjoa PDF Pattern -resurssiputkea mielivaltaisille toistokuville, joten tuon siveltimen valitseminen palautuu tasaväriseen varakeinoon lähteen tekstuurin sijaan. Jos täyttö renderöityy tasaisena, kun alkuperäinen selvästi käytti toistuvaa kuvatekstuuria, lähdesivellin on lähes varmasti mukautettu DIB-kuvio eikä vakioviivoitus, ja se on juuri se tapaus, joka kannattaa tarkistaa ensin käsin. Tällaisen piirustuksen tuonnin määrittäminen kulkee silti saman asetusolion kautta:
var
Pdf: THotPDF;
Drawing: TMetafile;
Options: THPDFEmfOptions;
begin
Pdf := THotPDF.Create(nil);
Drawing := TMetafile.Create;
Options := THPDFEmfOptions.Create;
try
Drawing.LoadFromFile('floor-plan.emf');
Options.Assign(Pdf.EmfOptions); // start from the document-wide defaults
Options.Redraw := False; // interpret the original EMF bytes, no GDI re-record pass
Options.ShowNullBrush := True; // keep explicitly unfilled CAD regions visible
Options.UseFrame := True; // clip output to the frame the EMF header declares
Pdf.FileName := 'floor-plan.pdf';
Pdf.BeginDoc;
Pdf.CurrentPage.ShowMetafileEx(Drawing, Options);
Pdf.EndDoc;
finally
Options.Free;
Drawing.Free;
Pdf.Free;
end;
end;
Mikä estää virheellistä metatiedostoa turmelemasta sivua?
HotPDF:n vastaus on yksi ainoa portti funktion ExecuteRecord alussa sen sijaan, että puolustava tarkistus toistettaisiin jokaisessa sen noin kahdeksassakymmenessä tietuekäsittelijässä. GDI-polkusulkeen, jonka EMR_BEGINPATH avaa ja EMR_ENDPATH tai EMR_ABORTPATH sulkee, tilaa seuraa yksityinen PathContinue-ominaisuus, jonka takana on FPathContinue-kenttä. Kun tuo sulje on auki, ExecuteRecord päästää läpi vain polunrakennustietueet, siirto-, viiva-, murtoviiva-, monikulmio-, polybezier- ja polydraw-muunnelmat, sekä CLOSEFIGURE-tietueen ja pienen joukon muunnos- ja laitekontekstitilan tietueita, kuten SETWORLDTRANSFORM, SAVEDC ja RESTOREDC. Jokainen muu tietuetyyppi, joka saapuu funktioon ExecuteRecord sulkeen ollessa auki, esimerkiksi eksynyt EXTTEXTOUT tai bittikarttasiirto, hylätään keskitetysti yhdellä Exit-lauseella heti sen saapuessa
Tämä portti on olemassa, koska käsin laadittu, työkalulla tuotettu tai yksinkertaisesti vioittunut metatiedosto ei takaa, että sen polkusulkeen sisällä on vain sitä, mitä hyvin muodostettu tiedosto laittaisi avaus- ja sulkutietueidensa väliin. Tekstintulostustietue, joka päätyy EMR_BEGINPATH- ja EMR_ENDPATH-tietueiden väliin, ilman porttia joko saastuttaisi rakenteilla olevan polun geometrian tai tuottaisi PDF:n tekstinäyttöoperaattorin keskelle sarjaa, jonka pitäisi olla puhdasta polunrakennusta, ja molemmat vikatilat ovat sellaisia, jotka paljastuvat yhdellä kolmannen osapuolen työkalun tuottamalla virheellisellä syötteellä, ei millään, mitä tavallinen testisarja sattuu kattamaan. Tarkistuksen keskittäminen funktioon ExecuteRecord tarkoittaa, ettei yksittäisten VEMR*-käsittelijöiden kunkin tarvitse puolustautua väärään aikaan kutsumista vastaan; portti päättää sen kerran, ennen jakelua, sen sijaan että se tehtäisiin kahdeksankymmentä kertaa sen jälkeen
Vektorikaavion asettaminen tekstin ja kuvien viereen samalle sivulle
Raporttisivulla on harvoin vain kaavio, ja ShowMetafile yhdistyy HotPDF:n muiden sivuoperaattoreiden kanssa aivan kuten mikä tahansa muukin piirtokutsu. TextOut-funktiolla piirretty otsikko, EMF:nä tuotu viivoitustäyttöinen pylväskaavio ja ShowImage-funktiolla sijoitettu logo voivat kaikki päätyä samalle sivulle samaan sisältövirtaan, kukin säilyttäen natiivin tarkkuutensa, kompositiomallin, joka käsitellään artikkelissa HotPDF:n opas tekstin, fonttien ja kuvien asetteluun raportissa:
Pdf.CurrentPage.SetFont('Arial', [fsBold], 14);
Pdf.CurrentPage.TextOut(50, 760, 0, 'Q2 Regional Sales');
Pdf.CurrentPage.ShowMetafile(RegionChart); // hatch-filled bars, still vector
Pdf.CurrentPage.ShowImage(LogoIndex, 450, 760, 90, 30, 0);
EMF- ja WMF-tulkki, sen liukuvärjäystäytöille rekisteröimät aksiaaliset varjostuskuviot ja tässä kuvattu viivoitusharjojen kartoitus toistokuvioiksi kaikki toimitetaan osana vakiomuotoista HotPDF-komponenttia Delphille ja C++Builderille, natiivia VCL-kirjastoa ilman ulkoista DLL-riippuvuutta mistään näistä toiminnoista