HotPDF, izvorna Delphi i C++Builder komponenta za PDF, uvozi Windows EMF i WMF metadatoteke tako što svaki GDI zapis direktno tumači kao PDF operator umesto da datoteku pretvori u bitmapu: gradijentna popunjavanja postaju PDF obrasci aksijalnog senčenja, šrafirane četkice postaju PDF obrasci za popločavanje, a centralna kontrola stanja putanje sprečava da neispravni zapisi pokvare izlaz. Svaki grafikon koji TChart, GDI+ površina ili običan TCanvas može da izveze kao poboljšanu metadatoteku kandidat je za ovaj tok, a razlika postaje očigledna čim neko uveća stranicu ili je pošalje štampaču visoke rezolucije
Alternativa kojoj većina Delphi programera podrazumevano pribegava jeste rasterizovanje metadatoteke u bitmapu pre postavljanja na stranicu, a cena se vidi tek kasnije: stubičasti grafikon koji je na ekranu bio oštar postaje vidljivo kockast čim se PDF odštampa na 600 DPI ili prikaže na velikom ekranu, a CAD oblast ispunjena šrafurom pretvara se u jedan ravan sivi pravougaonik ako stil popune nije prenet. Čitanje metadatoteke kao programa umesto slike izbegava oba problema, a to je zahtevniji put za ispravnu implementaciju, zbog čega vredi poznavati sledeće zamke pre objavljivanja izveštaja
Zašto tumačiti metadatoteku umesto pretvaranja u bitmapu?
HotPDF zadržava EMF i WMF uvozu vektorski tok zato što je Windows metadatoteka snimljeni niz GDI poziva za crtanje, a ne slika, pa ponovno izvršavanje tih poziva kao PDF operatora za putanje, tekst i senčenje omogućava da se rezultat uvećava kao i ostatak stranice. THPDFPage.ShowMetafile i njegov parnjak ShowMetafileEx predstavljaju ulazne tačke koje aplikacija poziva, a oba prosleđuju metadatoteku klasi THPDFWmf, koja prolazi kroz svaki GDI zapis i prevodi ga. Razlika nije apsolutna i HotPDF se ne pretvara da jeste: zapis metadatoteke koji je zaista rasterski podatak, na primer bitmapa koju prenosi StretchDIBits, ugrađuje se kao pravi PDF Image XObject kroz AddImage i ShowImage, isti par poziva kroz koji prolazi svaka druga slika na stranici, umesto da se nasilno pretvori u operatore putanja koji ne mogu da izraze fotografiju. Linije, popune i tekst ostaju vektorski, a pikseli koji su već bili pikseli u izvoru ostaju pikseli u izlazu. Najjednostavniji poziv ne zahteva ništa osim učitane metadatoteke
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;
Kako tumač pretvara GDI koordinate u prostor PDF stranice?
HotPDF na to odgovara jednim prolaskom kroz sopstveni tok zapisa metadatoteke umesto druge implementacije GDI-ja. THPDFWmf.Analyse čita zaglavlje metadatoteke kroz Win32 poziv GetEnhMetaFileHeader, resetuje svoje interno stanje crtanja i poziva EnumEnhMetafile, isti API za nabrajanje koji bi koristio i preglednik metadatoteke, tako da svaki zapis EMR_* stiže do THPDFWmf.ExecuteRecord redosledom kojim je prvobitno snimljen. GDI izražava koordinate odozgo nadole u uređajskim ili logičkim jedinicama koje bira sopstveni režim mapiranja metadatoteke, dok je PDF stranica odozdo nagore u korisničkim tačkama, u koordinatnom sistemu opisanom u HotPDF modelu crtanja putanja i popuna na platnu. Svaki obrađivač zapisa rešava tu razliku pomoću ScaleX i ScaleY, koji pozivaju ProjectX i ProjectY da ponove GDI-jevu sopstvenu formulu za prevođenje prozora u prikaz za anizotropne i izotropne režime mapiranja, pa oblik snimljen kao širok pet logičkih jedinica završava na ispravnoj širini u PDF tačkama bez obzira na opsege prozora i prikaza koje je postavila izvorna aplikacija
Kako GDI gradijentna popuna postaje PDF obrazac senčenja?
Zapis EMR_GRADIENTFILL postaje pravi PDF aksijalni obrazac senčenja tipa 2 (ISO 32000-1 §8.7.4.5) kada ga je GDI snimio u jednom od dva režima za pravougaonike. THPDFWmf.VEMRGradientFill direktno čita raspored zapisa iz sirovog bafera, prateći strukturu MS-EMF §2.3.1.6: niz temena sa RGBA komponentama od 16 bitova, iza kojeg sledi spisak pravougaonika koji upućuju na po dva temena. Za GRADIENT_FILL_RECT_H boje prelaze sleva nadesno duž horizontalne srednje linije pravougaonika, a za GRADIENT_FILL_RECT_V prelaze odozgo nadole duž vertikalne srednje linije. U oba slučaja dve boje temena i projektovane koordinate pravougaonika direktno ulaze u THotPDF.RegisterAxialGradient, koji vraća ime obrasca, a stranica crta pravougaonik i popunjava ga tim obrascem (SetFillPattern) umesto ravnim pozivom SetRGBFillColor, pa zaglavlje u stilu tabele ili gradijentna oblast grafikona zadržava svoj prelaz umesto da se svede na jednu prosečnu boju
Gouraudov režim trouglova predstavlja stvarnu prazninu. Kada polje ulMode u zapisu prijavi GRADIENT_FILL_TRIANGLE, VEMRGradientFill prepoznaje da režim trouglova još nije implementiran, beleži tu činjenicu i preskače pravougaonik umesto da nagađa dvobojnu aproksimaciju. Interpolacija po temenima i pikselima kroz proizvoljnu mrežu trouglova ne svodi se na aksijalno ili radijalno senčenje sa dve krajnje tačke, a pravilno izražavanje zahtevalo bi PDF senčenje mrežom tipa 4 ili tipa 5, istu porodicu senčenja koju HotPDF-ov prikazivač stranica takođe ostavlja neiscrtanom pri čitanju PDF-a. Dva nepovezana toka koda nailaze na istu granicu: mrežasta senčenja predstavljaju prazninu i pri upisivanju i pri čitanju, pa se izvorni dijagram koji koristi Gouraudove trouglove za glatki radijalni sjaj vraća na poslednju punu četkicu, a ne na renderovanu aproksimaciju
Šrafirane četkice postaju obrasci za popločavanje, a ne ravna siva površina
GDI šrafirana četkica zadržava svoju teksturu u PDF-u zato što THPDFWmf.SetBrushColor proverava CurrentBrush.lbStyle da li je BS_HATCHED pre nego što pređe na punu popunu, pa taj slučaj usmerava na SetHatchBrushPattern. Ta metoda upisuje PDF tok sadržaja veličine 8 sa 8 jedinica sa operatorima za iscrtavanje linija, m, l i S, izabranim prema GDI stilu šrafure: jednu horizontalnu ili vertikalnu liniju za HS_HORIZONTAL i HS_VERTICAL, tri paralelne dijagonale za HS_FDIAGONAL i HS_BDIAGONAL, kao i kombinacije horizontalne i vertikalne ili obe dijagonale za HS_CROSS i HS_DIAGCROSS. THotPDF.RegisterTilingPattern registruje taj tok sadržaja kao obojeni obrazac za popločavanje (PaintType 1, ISO 32000-1 §8.7.3.1) sa XStep i YStep od 8 jedinica, a stranica se popunjava kroz SetFillPattern na isti način kao kod aksijalnog senčenja. Tlocrt CAD-a ili tehnički crtež koji se oslanja na šrafure za razlikovanje materijala zadržava taj vizuelni jezik u PDF-u umesto da se svaka oblast izgubi u istoj sivoj boji
Ne zaslužuje svaka četkica takvu obradu, a tu prazninu vredi poznavati pre objavljivanja CAD uvoza. EMR_CREATEDIBPATTERNBRUSHPT, zapis za prilagođenu četkicu sa bitmapom umesto jedne od šest standardnih GDI šrafura, samo registruje svoju ručicu kako bi kasniji zapisi SELECTOBJECT i DELETEOBJECT ostali usklađeni; HotPDF još ne izlaže tok PDF resursa za proizvoljne slikovne pločice, pa izbor te četkice prelazi na punu boju umesto na izvornu teksturu. Ako se popuna prikaže ravno tamo gde je izvor očigledno koristio teksturu slike koja se ponavlja, izvorna četkica je gotovo sigurno prilagođeni DIB obrazac, a to je jedini slučaj koji prvo treba ručno proveriti. Konfigurisanje uvoza za takav crtež i dalje ide kroz isti objekat sa opcijama
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;
Šta sprečava neispravnu metadatoteku da pokvari stranicu?
HotPDF na to odgovara jednom kontrolnom tačkom na početku ExecuteRecord, umesto odbrambene provere koja bi se ponavljala u svakom od približno osamdeset obrađivača zapisa. GDI blok putanje, koji otvara EMR_BEGINPATH, a zatvara EMR_ENDPATH ili EMR_ABORTPATH, prati privatno svojstvo PathContinue potkrepljeno poljem FPathContinue. Dok je taj blok otvoren, ExecuteRecord propušta samo zapise za izgradnju putanje, varijante pomeranja, linije, polilinije, poligona, polibezjea i polucrtanja, uz CLOSEFIGURE i mali skup zapisa transformacije i stanja DC-a kao što su SETWORLDTRANSFORM, SAVEDC i RESTOREDC. Svaki drugi tip zapisa koji stigne do ExecuteRecord dok je blok otvoren, na primer zalutali EXTTEXTOUT ili prenos bitmapa, centralno se odbacuje jednim Exit čim stigne
Ta kontrolna tačka postoji zato što blok putanje u ručno napravljenoj, alatkom generisanoj ili jednostavno oštećenoj metadatoteci ne mora da sadrži samo ono što bi dobro oblikovana datoteka postavila između početnog i završnog zapisa. Zapis za ispis teksta između EMR_BEGINPATH i EMR_ENDPATH bez kontrole bi ili zagadio geometriju putanje koja se gradi ili emitovao PDF operator za prikaz teksta usred niza koji treba da bude čista izgradnja putanje, a oba načina kvara pojavila bi se na jednom neispravnom ulazu iz alata treće strane, ne nužno u onome što pokriva uobičajeni skup testova. Centralizovanje provere u ExecuteRecord znači da pojedinačni obrađivači VEMR* ne moraju svaki posebno da se brane od poziva u pogrešnom trenutku; kontrolna tačka odlučuje jednom, pre slanja zapisa obrađivaču, umesto osamdeset puta posle toga
Postavljanje vektorskog grafikona uz tekst i slike na istoj stranici
Izveštajna stranica retko sadrži samo grafikon, a ShowMetafile se kombinuje sa ostalim HotPDF operatorima stranice upravo kao bilo koji drugi poziv za crtanje. Naslov iscrtan pomoću TextOut, stubičasti grafikon sa šrafurom uvezen kao EMF i logo postavljen pomoću ShowImage mogu da završe na istoj stranici u istom toku sadržaja, pri čemu svaki zadržava svoju izvornu vernost, prema obrascu slaganja opisanom u HotPDF vodiču za raspoređivanje teksta, fontova i slika u izveštaju
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);
Tumač EMF i WMF metadatoteka, obrasci aksijalnog senčenja koje registruje za gradijentne popune i mapiranje obrazaca za popločavanje šrafiranih četkica opisani ovde isporučuju se kao deo standardne HotPDF komponente za Delphi i C++Builder, izvorne VCL biblioteke bez spoljne DLL zavisnosti za bilo koji od tih postupaka