Fax gateway ne želi vaš 24-bitni render stranice. Ne želi ga ni arhivski pipeline koji sprema milijun skeniranih računa, niti OCR front-end koji sve pragira na crno i bijelo prije nego što uopće pogleda znak. Svima je ista stvar potrebna: čist 1-bitni bitmap, jedan bit po pikselu, gdje je svaka točka ili tinta ili papir. Dajte im punobojni BMP i oni će ionako baciti 23 bita po pikselu, obično s gorim dithering prolazom nego što biste ga sami mogli napraviti. Zanimljivo pitanje je gdje ta down-conversion treba dogoditi, a odgovor u PDFlibPasu pokazuje nešto korisno o tome kako proširiti renderer koji ne biste radije prepisivali
PDFlibPas je nativna Object Pascal PDF biblioteka za Delphi i C++Builder. Njegov rendering core rasterizira stranicu u bitmapu i može isporučiti BMP, PNG, JPEG, WMF i nekoliko drugih formata. Ono što do nedavno nije radio bilo je vraćanje prave monochrome bitmapke ili prikaz samo dijela stranice. Oba su stigla u v3.83.0, i oba su izgrađena kao tanke convenience layeri na vrhu postojećeg renderera, a ne kao promjene samog rasterizera. To je cijela priča
Zašto down-convert nakon renderiranja, a ne unutar renderera
Očigledan način za proizvodnju 1-bitne slike jest reći rasterizeru da crta u 1-bitu. To je također način da se razbije sve ostalo. Interna bitmapa renderera stvara se s hardkodiranim PixelFormat := pf24bit u PDFlibRenderer konstruktoru, i ta 24-bitna površina dijeli se sa svakim render putem: PNG export, device-context preview, JPEG output, sve. Prebacite je na pf1bit na izvoru i niste dodali monochrome feature, nego ste degradirali color fidelity za svakog pozivatelja u biblioteci i prijavili se za debugiranje desetak downstream regresija
Dakle, RenderPageToMonochromeFile ide suprotnim putem. Stranicu renderira normalno, u privremeni 24-bitni BMP, i tek je zatim kolabira u 1-bit kao post-processing korak. Renderer je netaknut. Monochrome ponašanje živi isključivo u convenience metodi, što znači da ne može utjecati na ikoga tko je ne pozove. To je vrsta trade-offa koju vrijedi izričito imenovati: post-process plaća jednu dodatnu bitmap alokaciju i temp datoteku, a zauzvrat potpuno izvan opsega drži nosivi core. Za značajku koja postoji da služi fax i arhivskim rubnim slučajevima, to je prava strana računa
var
Pdf: TPDFlib;
begin
Pdf := TPDFlib.Create(nil);
try
Pdf.LoadFromFile('invoice.pdf');
// 200 DPI is the classic Group 4 fax resolution; page index is 1-based
Pdf.RenderPageToMonochromeFile(200, 1, 'invoice-page1.bmp');
finally
Pdf.Free;
end;
end;
Kako se 1-bitni collapse zapravo događa
Down-conversion se oslanja na GDI, a ne na ručno pisan threshold loop, i taj izbor utječe na kvalitetu izlaza. Unutar metode 24-bitni temp bitmap učitava se u TBitmap, druga TBitmap se stvara s PixelFormat := pf1bit na istim dimenzijama, a pikseli prelaze s jednim blitom:
// inside RenderPageToMonochromeFile, after loading the 24-bit ColorBmp
MonoBmp.PixelFormat := pf1bit;
MonoBmp.Width := ColorBmp.Width;
MonoBmp.Height := ColorBmp.Height;
// HALFTONE tells GDI to dither the 24-bit source down to 1-bit
SetStretchBltMode(MonoBmp.Canvas.Handle, HALFTONE);
StretchBlt(MonoBmp.Canvas.Handle, 0, 0, MonoBmp.Width, MonoBmp.Height,
ColorBmp.Canvas.Handle, 0, 0, ColorBmp.Width, ColorBmp.Height, SRCCOPY);
MonoBmp.SaveToFile('out.bmp');
Trik je SetStretchBltMode s HALFTONE. Iako su izvor i odredište iste veličine, pa nema skaliranja, stretch mode i dalje određuje kako GDI mapira boje u 1-bitnu paletu. HALFTONE ga tjeraju da primijeni halftone dithering, pretvarajući sive zone i antialiased rubove teksta u uzorke crnih i bijelih točkica umjesto u tvrdi clip na najbližu od dvije boje. Uklonite poziv na mode ili upotrijebite zadani BLACKONWHITE, i grayscale sadržaj posterizira u blokaste threshold oblike. Za izlaz namijenjen skeniranju dokumenata i OCR predobradi, dithered rezultat gotovo je uvijek ono što želite
Jedan detalj je neupitan i lako ga je pogriješiti: privremeni render mora biti BMP. RenderPageToMonochromeFile poziva generalni renderer s options kodom od 0, što je BMP. Options argument na RenderPageToFile je mali integer enum, a vrijednosti za tu svrhu nisu zamjenjive: 0 je BMP, 1 JPEG, 2 WMF, 3 EMF, 5 PNG, i tako dalje. Down-converter zatim radi TBitmap.LoadFromStream na temp datoteci. Dajte mu WMF, tako da proslijedite 2, i to učitavanje baca "Bitmap image is not valid", jer je Windows Metafile stream vektorskih zapisa, a ne DIB. Monochrome down-conversion je raster operacija od početka do kraja, pa međukorak mora biti raster format
Prikaz samo podregije stranice
Druga metoda, RenderPageRegionToFile, prikazuje samo pravokutnik stranice umjesto cijele stvari. Upotrebe su poznate čim ste izgradili bilo koji document viewer: izrezivanje bloka potpisa iz ugovora, generiranje pločice za zumiranu kartu velikog crteža ili izdvajanje jednog žigosanog područja za sličicu bez plaćanja rasterizacije cijele stranice na visokom DPI-ju. Potpis je jednostavan:
// Clip is "Left,Top,Width,Height" in PDF points (72 pt = 1 inch)
// Here: a 2.5in x 1in box, one inch in from the top-left of the page
Pdf.RenderPageRegionToFile(150, 1, '72,72,180,72', 'sig-block.bmp');
Clip string je četiri decimalna broja odvojena zarezima u PDF točkama, ručno parsiran unutar metode kako bi se zaobišle locale i DelimitedText quirks. Iz širine i visine metoda izračuna veličinu izlazne bitmapke kao Round(Width * DPI / 72) by Round(Height * DPI / 72), alocira in-memory pf24bit bitmap točno te veličine i renderira u njezin device context kroz RenderPageToDCClip. Izlazna datoteka sadrži samo izrezani pravokutnik, veličinom prilagođen području, a ne cijeloj stranici
Clip parametar koji nije radio ništa
Ovdje je posao bio oštriji nego što izgleda. RenderPageToDCClip je dugo nosio Clip parametar, a to je bila laž. Poziv je prihvaćao argument, prosljeđivao ga TPDFPageTree.RenderPageToDC, a ta je implementacija potpuno ignorirala, nikad ga nije predavala rendereru. Mogli ste poslati bilo koji pravokutnik i dobiti cijelu stranicu natrag. Svaki tko je povezao RenderPageToDCClip očekujući crop dobivao je render cijele stranice i, ovisno o layoutu, možda to nije ni primijetio
v3.83.0 je spojio žicu. RenderPageToDC sada parsira isti "Left,Top,Width,Height" point pravokutnik i primjenjuje ga kao pravi GDI clip region na ciljnom device contextu prije nego renderer nacrta. Pretvorba iz točaka u device piksele je uobičajeni DPI / 72 scale faktor, primijenjen na sva četiri ruba. Sekvenca oko rendera standardni je save/clip/restore ples:
// inside TPDFPageTree.RenderPageToDC, when Clip is non-empty
ScaleFactor := DPI / 72;
SaveDC(TargetDC);
IntersectClipRect(TargetDC,
Round(ClipLeft * ScaleFactor),
Round(ClipTop * ScaleFactor),
Round((ClipLeft + ClipWidth) * ScaleFactor),
Round((ClipTop + ClipHeight) * ScaleFactor));
// ... renderer draws the page here ...
// in the finally block:
RestoreDC(TargetDC, -1);
SaveDCSaveDC / RestoreDC(-1)RestoreDC(-1)RestoreDC(TargetDC, -1) par je ono što ovo čini sigurnim za višekratni poziv: clip region se gura na DC state stack, stranica se nacrta i izvorni clip se vraća bez obzira na to kako render završi. RenderPageRegionToFile vraća najnovije spremljeno stanje, što je standardni idiom za balanced save/restore. Preskočite restore i pozivatelj koji isti DC koristi za sljedeći full-page render zatekao bi ga misteriozno odrezanog na zadnju regiju. Popravak mrtvog parametra usput je popravio i
za slobodno, jer ta nova metoda prolazi upravo ovim putem.Jedna ponašajna točka koju treba usvojiti: clip crops, it does not scaleLeft. Stranica se i dalje rasterizira na DPI koji ste tražili, u normalnom položaju, a clip region jednostavno odbacuje sve izvan pravokutnika. Ne zumirate regiju da ispuni izlaz; režete prozor iz full-resolution rendera. Ako želite uvećanu regiju, povećajte DPI. Koordinate pravokutnika tumače se u device spaceu nakon points-to-pixels skaliranja, mjereno od gornjeg lijevog kuta renderirane površine, pa planirajte svoje Top i od vrha stranice prema dolje. Za dublji obilazak kako PDFlibPas upravlja device contextom za on-screen izlaz, prateći članak o print preview and device-context output
prolazi kroz isti DC plumbing s display strane
Poštena granica: 1-bitni BMP, ne G4 TIFFRenderPageToMonochromeFileBilo bi lako ovo prenapuhati kao "fax-ready output", pa evo granice izrečene jasno. pf1bit proizvodi BMP. Ne proizvodi CCITT Group 4 TIFF, koji je format koji stvarni fax workflow ili TIFF arhiva obično očekuje. Razlog je konkretan, a ne previd: PDFlibPasov CCITT unit trenutno dekodira G4 streamove, ali nema G4 encoder
. Bez encoder-a nema gdje zapisati komprimirane monochrome runove, pa monochrome put staje na nekomprimiranom 1-bitnom DIB-u
U praksi je to i dalje korisno. 1-bitni BMP je ispravan pixel format, dithered i spreman, a većina fax, arhivskih ili OCR toolchainova ga rado ingestira ili sama pretvori u G4 u jednom downstream koraku. Ali ako vam je zahtjev doslovno Group 4 TIFF ravno iz biblioteke, to još nije to, i trebali biste planirati vlastiti compression stage. Znati gdje značajka staje vrijedno je koliko i znati što radi.Obje metode su namjerno male, i to je design lekcija koju vrijedi ponijeti s ove stranice: convenience API koji sjedi na vrhu renderera može dodati stvarnu sposobnost, monochrome izlaz, region cropping, bez diranja rasterizera i destabilizacije svakog drugog pozivatelja. Kad trebate birati između render enginea za underlying rasterization, pregled multi-engine PDF rendering in Delphi pokriva trade-offe u dubinu. Da biste vidjeli punu rendering surface i ostatak API-ja, PDFlibPas Delphi PDF Library