Tehnični članak

Uvoz vektorjev EMF in WMF v datoteke PDF Delphi s HotPDF

HotPDF, izvorna komponenta PDF za Delphi in C++Builder, uvaža metadatoteke EMF in WMF sistema Windows tako, da vsak zapis GDI neposredno raztolmači v operatorje PDF, namesto da bi datoteko sploščila v bitno sliko: gradientne polnitve postanejo vzorci osnega senčenja PDF, vzorčasti čopiči postanejo vzorci ploščic PDF, osrednja zapornica stanja poti pa preprečuje, da bi nepravilni zapisi poškodovali izhod. Vsak grafikon, ki ga lahko TChart, površina GDI+ ali navaden TCanvas izvozi kot izboljšano metadatoteko, je kandidat za to pot, razlika pa se pokaže takoj, ko nekdo stran poveča ali jo pošlje na tiskalnik visoke ločljivosti

Alternativa, po kateri večina razvijalcev Delphi privzeto poseže, je rasterizacija metadatoteke v bitno sliko, preden jo postavi na stran, cena pa se pokaže šele pozneje: stolpčni graf, ki je bil na zaslonu oster, postane vidno kockast, ko je PDF natisnjen pri 600 DPI ali prikazan na zaslonu konferenčne sobe, območje CAD z vzorčasto polnitvijo pa se sesede v en sam raven siv pravokotnik, če se slog polnitve ne ohrani. Branje metadatoteke kot programa namesto slike prepreči obe težavi, vendar je to težja pot za pravilno izvedbo, zato je vredno poznati spodnje pasti, preden poročilo pošljete naprej

Zakaj metadatoteko raztolmačiti namesto sploščiti v bitno sliko?

HotPDF ohranja uvoz EMF in WMF na vektorski poti, ker je metadatoteka sistema Windows zaporedje zabeleženih klicev GDI, ne slika, ponovitev teh klicev kot operatorjev poti, besedila in senčenja PDF pa omogoči, da se rezultat povečuje skupaj z ostalo stranjo. THPDFPage.ShowMetafile in njegov dvojnik ShowMetafileEx sta vstopni točki, ki ju kliče aplikacija, oba pa metadatoteko predata razredu THPDFWmf, ki pregleda vsak zapis GDI in ga prevede. Razlika ni absolutna in HotPDF tega ne skriva: zapis metadatoteke, ki je v resnici rastrski podatek, na primer bitno kopiranje StretchDIBits, se prek AddImage in ShowImage vstavi kot pravi slikovni objekt PDF XObject, enako kot vsaka druga slika na strani, namesto da bi bil prisiljen v operatorje poti, ki ne morejo izraziti fotografije. Črte, polnitve in besedilo ostanejo vektorji; piksli, ki so bili že piksli v viru, ostanejo piksli tudi v izhodu. Najpreprostejši klic ne potrebuje ničesar več kot naloženo metadatoteko:

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 tolmač pretvori koordinate GDI v prostor strani PDF?

HotPDF na to odgovori z enim prehodom čez lasten tok zapisov metadatoteke, namesto z drugo izvedbo GDI. THPDFWmf.Analyse prebere glavo metadatoteke s klicem Win32 GetEnhMetaFileHeader, ponastavi notranje stanje risanja in pokliče EnumEnhMetafile, isti API za naštevanje, kot bi ga uporabil pregledovalnik metadatotek, zato vsak zapis EMR_* pride do THPDFWmf.ExecuteRecord v prvotnem zaporedju. GDI izraža koordinate od zgoraj navzdol v napravnih ali logičnih enotah, ki jih izbere lastni način preslikave metadatoteke; stran PDF je v uporabniškem prostoru točk usmerjena od spodaj navzgor, kot je opisano v modelu risanja na platno HotPDF za poti in polnitve. Vsak obdelovalnik zapisov to razliko razreši prek ScaleX in ScaleY, ki pokličeta ProjectX in ProjectY za ponovitev lastne GDI-jeve formule za preslikavo okna v vidno polje pri anizotropnih in izotropnih načinih preslikave, zato oblika, zabeležena s širino petih logičnih enot, pristane na pravilni širini v točkah PDF ne glede na obsege okna in vidnega polja, ki jih je nastavila izvorna aplikacija

Kako gradientna polnitev GDI postane vzorec senčenja PDF?

Zapis EMR_GRADIENTFILL postane pravi osni vzorec senčenja PDF tipa 2 (ISO 32000-1 §8.7.4.5), kadar ga je GDI zabeležil v enem od dveh pravokotnih načinov. THPDFWmf.VEMRGradientFill prebere lastno razporeditev zapisa neposredno iz surovega medpomnilnika bajtov in sledi strukturi MS-EMF §2.3.1.6: polju oglišč 16-bitnih vogalov RGBA sledi seznam pravokotnikov, od katerih se vsak sklicuje na dve od teh oglišč. Pri GRADIENT_FILL_RECT_H barve potujejo od leve proti desni vzdolž vodoravne sredinske črte pravokotnika; pri GRADIENT_FILL_RECT_V potujejo od zgoraj navzdol vzdolž navpične sredinske črte. V obeh primerih se barvi vogalov in projicirane koordinate pravokotnika neposredno prenesejo v THotPDF.RegisterAxialGradient, ki vrne ime vzorca, stran pa pravokotnik nariše in ga zapolni prek tega vzorca (SetFillPattern) namesto z ravnim klicem SetRGBFillColor, zato pasovnata glava v slogu preglednice ali gradientno območje izrisa grafikona ohrani preliv, namesto da bi se sesedla v eno povprečno barvo

Trikotni način Gouraud je pošteno priznana vrzel. Ko polje ulMode zapisa sporoči GRADIENT_FILL_TRIANGLE, ga VEMRGradientFill prepozna, zabeleži, da trikotni način še ni izveden, in preskoči pravokotnik, namesto da bi ugibal z dvobarvnim približkom. Interpolacija po ogliščih in pikslih čez poljubno mrežo trikotnikov se ne skrči v osno ali radialno senčenje z dvema postankoma, pravilna izrazitev pa bi zahtevala oddajanje mrežnega senčenja PDF tipa 4 ali tipa 5, iste družine senčenja, ki jo upodobljevalnik strani HotPDF prav tako pusti nepobarvano pri branju PDF. Dve nepovezani kodni poti prideta do iste meje: mrežna senčenja so vrzel tako na strani zapisovanja kot na strani branja, izvorni diagram, ki uporablja Gouraudove trikotnike za gladek radialni sij, pa se vrne na zadnji polni čopič in ne na upodobljen približek

Vzorčasti čopiči postanejo vzorci ploščic, ne sploščena sivina

Čopič GDI z vzorcem ohrani svojo teksturo v PDF, ker THPDFWmf.SetBrushColor preveri CurrentBrush.lbStyle za BS_HATCHED, preden se sploh vrne na polno polnitev, in ta primer usmeri v SetHatchBrushPattern. Ta metoda zapiše 8-krat-8-enotni tok vsebine PDF z operatorji potez, m, l in S, ki jih izbere slog vzorca GDI: eno vodoravno ali navpično potezo za HS_HORIZONTAL in HS_VERTICAL, tri vzporedne diagonale za HS_FDIAGONAL in HS_BDIAGONAL ter vodoravno-navpične ali obojestransko diagonalne kombinacije za HS_CROSS in HS_DIAGCROSS. THotPDF.RegisterTilingPattern ta tok vsebine registrira kot barvni vzorec ploščic (PaintType 1, ISO 32000-1 §8.7.3.1) z 8-enotnima korakoma XStep in YStep, stran pa polni prek SetFillPattern na enak način kot pri osnem senčenju. Tloris CAD ali tehnična risba, ki uporablja vzorce za razlikovanje materialov, v PDF ohrani ta vizualni jezik, namesto da bi vsako območje izgubilo v enaki sivini

Takšne obravnave si ne zasluži vsak čopič in to vrzel je vredno poznati, preden uvoz CAD pošljete naprej. EMR_CREATEDIBPATTERNBRUSHPT, zapis za čopič z vzorcem po meri iz bitne slike namesto enega od šestih standardnih slogov vzorcev GDI, registrira samo svoj ročaj, da poznejša zapisa SELECTOBJECT in DELETEOBJECT ostaneta usklajena; HotPDF še ne ponuja cevovoda virov vzorcev PDF za poljubne slikovne ploščice, zato izbira tega čopiča pade na enobarvni nadomestek namesto na izvorno teksturo. Če je polnitev ravna, čeprav je izvirnik očitno uporabljal ponavljajočo se slikovno teksturo, je izvorni čopič skoraj zagotovo vzorec DIB po meri in to je primer, ki ga je najprej smiselno preveriti ročno. Konfiguracija uvoza za takšno risbo še vedno poteka prek istega objekta možnosti:

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;

Kaj prepreči, da bi nepravilna metadatoteka poškodovala stran?

Odgovor HotPDF je ena sama zapornica na vrhu ExecuteRecord, ne obrambni pregled, ponovljen v vsakem od približno osemdesetih obdelovalnikov zapisov. Okvir poti GDI, ki ga odpre EMR_BEGINPATH in zapre EMR_ENDPATH ali EMR_ABORTPATH, spremlja zasebna lastnost PathContinue, podprta s poljem FPathContinue. Med odprtim okvirjem ExecuteRecord spusti skozi samo zapise za gradnjo poti, različice premika, črte, poličrt, mnogokotnika, polibezierja in polirisanja, poleg CLOSEFIGURE ter majhnega nabora zapisov za pretvorbe in stanje DC, kot so SETWORLDTRANSFORM, SAVEDC in RESTOREDC. Vsaka druga vrsta zapisa, ki doseže ExecuteRecord, ko je okvir odprt, na primer osamljeni EXTTEXTOUT ali bitno kopiranje, se centralno zavrže z enim samim Exit takoj, ko prispe

Ta zapornica obstaja zato, ker okvir poti v ročno napisani, z orodjem ustvarjeni ali preprosto poškodovani metadatoteki ni nujno sestavljen samo iz vsega, kar bi dobro oblikovana datoteka postavila med začetni in končni zapis. Zapis izhoda besedila med EMR_BEGINPATH in EMR_ENDPATH bi brez zapornice bodisi onesnažil geometrijo poti v izdelavi bodisi oddal operator PDF za prikaz besedila sredi zaporedja, ki bi moralo biti namenjeno izključno gradnji poti, oba načina odpovedi pa se pojavita pri enem nepravilnem vhodu orodja tretje osebe in ne pri čem, kar bi običajen testni paket naključno zajel. Centralizacija pregleda v ExecuteRecord pomeni, da se posameznim obdelovalnikom VEMR* ni treba vsakemu posebej braniti pred klicem ob napačnem času; zapornica odloči enkrat pred razpošiljanjem namesto osemdesetkrat po njem

Postavitev vektorskega grafikona ob besedilo in slike na eni strani

Na strani poročila je redko samo grafikon, ShowMetafile pa se z drugimi operatorji strani HotPDF sestavi natančno tako kot vsak drug klic risanja. Naslov, narisan s TextOut, stolpčni graf, uvožen kot EMF z vzorčasto polnitvijo, in logotip, postavljen s ShowImage, lahko pristanejo na isti strani v istem toku vsebine, pri čemer vsak ohrani svojo izvorno kakovost, kot je opisano v vodniku HotPDF za razporeditev besedila, pisav in slik v poročilu:

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

Tolmač EMF in WMF, vzorci osnega senčenja, ki jih registrira za gradientne polnitve, in preslikava vzorcev ploščic za vzorčaste čopiče, opisana tukaj, so del standardne komponente HotPDF za Delphi in C++Builder, izvorne knjižnice VCL brez zunanje odvisnosti DLL za katero koli od teh funkcij