HotPDF konvertuje XPS i OpenXPS pakete u PDF unutar Delphi-ja i C++Builder-a bez print drajvera, preslikavajući svaku 96-DPI koordinatu fiksne stranice kroz jednu matricu stranice sa razmerom 0.75 i okretanjem Y ose, objavljujući svaki VisualBrush kao deljeni Form XObject, i pretvarajući ImageBrush tile režime u nativne PDF tiling obrasce umesto ponovljenih iscrtavanja slika
Scenario koji u ovo uvlači većinu Windows timova je dosadan i neizbežan. Nešto već štampa na Microsoft XPS Document Writer — izveštaj iz zastarelog ERP-a, potpisan obrazac, serija izvoda — a arhivska politika kaže PDF. XPS je sasvim dobar format za hvatanje i užasan onaj koji ćete za deceniju predati sistemu evidencije. Zato spool fajl mora postati PDF stranicu po stranicu, i onog trenutka kad počnete pisati taj konvertor otkrivate da zanimljivi deo nije XML. To je to da se XPS i PDF ne slažu oko toga gde je koordinatni početak, koliko vredi jedinica i kakva četkica sme da bude
Od paketa do PDF-a u jednom prolazu
Ulazna tačka je registar dokument rukovaoca, ne posebna XPS klasa. THPDFDocumentHandlerRegistry.RegisterStandardHandlers instalira XPS, EPUB i CBZ rukovaoce; prepoznavanje se zasniva na sadržaju, pa paket koji nosi [Content_Types].xml plus najmanje jedan .fpage deo dobija 95 čak i kada ekstenzija fajla laže, dok gola .xps ili .oxps ekstenzija dobija samo 10. Taj redosled je bitan kada primate upload-e, jer napadač koji preimenuje EPUB u .xps ne sme upravljati procesnom linijom
var
Handled: IHPDFHandledDocument;
Info: THPDFDocumentHandlerInfo;
Options: THPDFDocumentHandlerOptions;
Registry: THPDFDocumentHandlerRegistry;
Output: TFileStream;
begin
Registry := THPDFDocumentHandlerRegistry.Create;
Output := TFileStream.Create('spool.pdf', fmCreate);
try
Registry.RegisterStandardHandlers;
Options := THPDFDocumentHandlerOptions.Default;
if not Registry.OpenFromFile('spool.xps', '', Options, Handled, Info) then
raise Exception.Create('no registered handler recognised the package');
if Handled.Format = hdfXPS then
Handled.WritePDF(Output, Options, Info);
// Info.UnsupportedFeatureCount je iskrena ocena za ovu konverziju
finally
Output.Free;
Registry.Free;
end;
end;
Sve oko konverzije je budžetirano pre nego što se pokuša. THPDFDocumentHandlerOptions.Default ograničava arhivske unose na 10.000, proširene arhivske bajtove na 1 GiB, odnos kompresije na 200, resurse na 4.096 i stranice na 10.000, i nosi opcionu CancellationToken da se posao na strani servera može zaustaviti usred paketa. Pročitajte posle Info.UnsupportedFeatureCount i tretirajte nenultu vrednost kao stvaran nalaz: HotPDF namerno broji ono što nije moglo da se preslika, umesto da nacrta približno i ćuti o tome
Zašto XPS stranica treba matricu umesto prepisanih koordinata?
Jer prepisivanje koordinata gubi stek transformacija. XPS FixedPage je specificiran u 96-DPI jedinicama sa početkom gore levo i Y osom koja raste nadole; PDF korisnički prostor je 72-DPI sa početkom dole levo i Y osom koja raste nagore. Naivna popravka je da pomnožite svaki broj sa 0.75 i oduzmete svako Y od visine stranice pri ispisivanju. To radi za jedan ravan put i raspada se onog trenutka kad uđe RenderTransform, ugnježden Canvas ili matrica lokalna za četkicu, jer su te transformacije definisane u XPS prostoru, a vaše prepisivanje po koordinati ga je već napustilo. HotPDF zato drži projekciju kao matricu i komponuje je. HPDFXPSPageMatrix vraća fiksne konstante jednom po stranici, HPDFMultiplyXPSMatrix nadovezuje je na akumuliranu transformaciju puta, a rezultat se ispisuje kao jedan cm operator pre geometrije. Podaci puta se zatim pišu u nemodifikovanim XPS brojevima, što je i razlog zašto skraćena geometrijska sintaksa može da deli isti ograničeni parser korišćen za SVG path podatke — samo vodeći F0 ili F1 fill-rule token rukuje XPS adapter. Ako ste pratili isto razmišljanje kod EMF i WMF vektorskog uvoza, oblik argumenta vam je poznat: uvozni formati se konvertuju matricom, nikad aritmetikom po koordinatama listova
function HPDFXPSPageMatrix(PageHeight: Double): THPDFXPSMatrix;
begin
Result.A := 0.75; // 96-DPI XPS jedinica u 72-DPI PDF poen
Result.B := 0;
Result.C := 0;
Result.D := -0.75; // XPS Y raste nadole, PDF Y nagore
Result.E := 0;
Result.F := PageHeight; // visina PDF stranice, u poenima
end;
// Jedna komponovana CTM po vizuelu, ispisana pre bilo kog operatora puta
Effective := HPDFMultiplyXPSMatrix(HPDFXPSPageMatrix(PageHeightPDF), PathMatrix);
Page.AppendRawContent(HPDFXPSMatrixOperator(Effective));
Kako iskoristiti VisualBrush bez dvostrukog iscrtavanja?
VisualBrush boji proizvoljno vizuelno drvo — decu Canvas, Path i Glyphs — u region, moguće ponovljeno preko njega. HotPDF to vizuelno jednom iskompajlira u PDF Form XObject pa ga postavlja, što je ista strategija resursa opisana u SVG uvozu preko Form XObject-ova. Dva detalja odlučuju da li radi. Prvo, vizuelno drvo mora se obići kroz direktnu XML decu: ravan sken za elemente dostojne tiling-a izvlači ugnježdene vizuele na vrh stranice i uništava i opseg resursa i red bojenja. Drugo, sadržaj se hvata sa već primenjenom XPS-u-PDF matricom stranice, pa objavljivanje Form-a zahteva množenje inverzom te matrice, inače svako postavljanje ponovo primenjuje razmeru 0.75 i okretanje Y ose. Form takođe mora posedovati svoje resurse: HotPDF kopira samo fontove, XObject-e, obrasce, ExtGState-ove i prostore boja na koje uhvaćeni content tok stvarno referencira; kloniranje celog rečnika resursa stranice bi uvolilo Form koji se registruje u sopstveni graf resursa i napravilo ciklus. Fontovi ostaju u direktnom rečniku na običnim stranicama i bivaju unapređeni u deljeni indirektan rečnik tek kad uhvaćeni sadržaj zaista sadrži Tf, pa dokument bez ponovo upotrebljivih vizuela ne plaća tu mašineriju. Zapamtite jednu granicu specifikacije pre nego što prijavite bug: ECMA-388 sekcija 13.4 zahteva da i ViewboxUnits i ViewportUnits na VisualBrush-u budu Absolute, pa relativne jedinice nisu nedostajuća funkcija — to je ulaz van specifikacije, i HotPDF odbija da izmisli semantiku koordinata za njih
ImageBrush tiling: četiri režima, četiri veličine ćelije
XPS tile režimi preslikavaju se na PDF tiling obrasce iz ISO 32000-1 sekcije 8.7.3 umesto da se rašire u ponovljena postavljanja slika preko pokrivene oblasti, što veličinu izlaza i vreme konverzije drži nezavisnim od toga koliko stranice četkica pokriva. Preslikavanje je mehaničko jednom kad ga vidite: ogledanje se izražava stavljanjem ogledalnih postavljanja unutar jedne ćelije obrasca i uvećanjem ćelije da odgovori
Tile— jedno postavljanje, ćelija ostaje 1×1 viewportFlipX— dva postavljanja, ćelija proširena na 2×1FlipY— dva postavljanja, ćelija podignuta na 1×2FlipXY— četiri postavljanja, ćelija raširena na 2×2
Svako postavljanje nosi svoj clip pravougaonik, jer bi Viewbox preslikavanje koje preplavi svoju pod-ćeliju krvarilo u susedno ogledanje. /Matrix obrasca je deo koji ljude iznenadi. Tiling obrazac sidren je na podrazumevani korisnički prostor svog roditeljskog content toka, ne na grafičko stanje tekuće u trenutku izbora obrasca, pa matrica mora eksplicitno komponovati sva tri sloja — projekciju fiksne stranice, transformaciju Path-a i Transform lokalnu za četkicu — umesto da se oslanja na ambijentalnu CTM. HotPDF takođe validira pre alokacije: RegisterImageTilingPattern ograničava obrazac na 1.024 postavljanja i odbija degenerisane clipove, neinvertibilne matrice i nevažeće indekse slika. Ako želite opšti model sa PDF strane iza ovoga, tiling obrasci i Pattern prostor boja pokriva donje operatore
// Projekcija fiksne stranice uvijena u matricu obrasca, pa tek ona lokalna za četkicu
PatternMatrix := HPDFMatFromOps( 0.75 * PathMatrix.A, -0.75 * PathMatrix.B,
0.75 * PathMatrix.C, -0.75 * PathMatrix.D,
0.75 * PathMatrix.E,
PageHeight - 0.75 * PathMatrix.F);
if Brush.HasTransform then
PatternMatrix := HPDFMatMul(PatternMatrix, BrushMatrix);
PatternName := Document.RegisterImageTilingPattern(Resource.ImageIndex,
Brush.Viewport.Left, Brush.Viewport.Top,
Brush.Viewport.Left + CellWidth, Brush.Viewport.Top + CellHeight,
CellWidth, CellHeight, Placements, pttNoDistortion, PatternMatrix);
Šta se dešava kad radijalni gradijent nije krug?
XPS definiše RadialGradientBrush sa GradientOrigin, Center, RadiusX i RadiusY, pa je četkica elipsa. PDF shading tip 3, u ISO 32000-1 sekciji 8.7.4.5.4, meša između dva kruga i nema načina da elipsu izrazi direktno. Prosečiti dva poluprečnika u jedan broj je primamljiva prečica i vidno je pogrešna na svakoj četkici koja nije blizu okrugle. HotPDF umesto toga pomera problem u koordinatni sistem: razmeri Y po RadiusY / RadiusX, registruje iskreno kružno senčenje u tom razmerenom prostoru, bira obrazac, i odmah ispisuje recipročnu razmeru da geometrija puta upisana dalje ostane u originalnom XPS korisničkom prostoru
ScaleY := Brush.RadiusY / Brush.RadiusX;
PatternName := Document.RegisterMultiStopRadialGradient(
Brush.StartX, Brush.StartY / ScaleY, 0,
Brush.EndX, Brush.EndY / ScaleY, Brush.RadiusX,
StopPositions, StopColours, 3);
if Abs(ScaleY - 1) > 0.000001 then
Page.AppendRawContent('1 0 0 ' + HPDFPDFNumber(ScaleY) + ' 0 0 cm'#10);
Page.SetFillPattern(PatternName); // obrazac hvata CTM baš ovde
if Abs(ScaleY - 1) > 0.000001 then
Page.AppendRawContent('1 0 0 ' + HPDFPDFNumber(1 / ScaleY) + ' 0 0 cm'#10);
Redosled u tom isečku je celi trik, i nije stvar stila. PDF shading obrazac hvata matricu tekuće transformacije u trenutku kad je izabran kao tekuća boja, pa privremena razmera mora biti ispisana pre SetFillPattern ili SetStrokePattern, a recipročna mora doći posle izbora ali pre operatora puta. Pogrešite redosled u bilo kom smeru i dobijate gradijent koji se ispravno renderuje na prvom putu a na svakom narednom raste odstupanje. Srodno ograničenje važi za režim relativnih koordinata: RadiusX i RadiusY moraju se razrešiti odvojeno prema širini i visini puta, jer razmera obe jednom dužinom ivice tiho menja odnos stranica elipse na svakom nekvadratnom putu
Gde je konverzija iskrena prema svojim granicama
Neki XPS konstrkti konvertuju se približno, a neki se ne konvertuju uopšte, i dizajnerski izbor kroz celinu je da se broje umesto da se lažu. TIFF i JPEG XR delovi rasterizuju se kroz WIC i ne nose obećanje o očuvanom alfakanalu, dok se PNG sa važećim alfakanalom deli na baznu sliku plus /SMask. Unutrašnja veličina slike izvodi se kao pixel * 96 / DPI, čitajući prvo PNG pHYs ili JPEG JFIF gustinu i vraćajući se na 96 DPI, pa loš header gustine dospeva u predvidivu veličinu umesto proizvoljne. Ne razrešeni resursi matrica, nestandardne relativne transformacije, ColorConvertedBitmap, nepodržani režimi rasprostiranja gradijenta i deformisana geometrija svi uvećavaju UnsupportedFeatureCount, i deformisan ulaz je fail closed umesto da se degradira u tiho drugačiji crtež
To je koristan stav za arhivski konvertor: konverzija koja tiho aproksimira je gora od one koja vam kaže koja četiri elementa nije mogla da predstavi, jer samo druga daje nešto što možete proveriti pre nego što dokument zapečati u sistem evidencije. Ako procenjujete XPS i OpenXPS konverziju uz ostatak dokumentne procesne linije — kompoziciju stranica, fontove, potpisivanje, PDF/A izlaz — stranica HotPDF Delphi PDF component izlaže kompletan skup funkcija i podržane Delphi i C++Builder verzije