Fax gateway ne želi vaš 24-bitni render strane. Ne želi ga ni arhivski pipeline koji čuva milion skeniranih računa, niti OCR front-end koji sve pragira na crno-belo pre nego što uopšte krene da traži znak. Sva tri žele istu stvar: čistu 1-bit bitmapu, jedan bit po pikselu, gde je svaka tačka ili mastilo ili papir. Ako im date punu kolor BMP, ionako će baciti 23 bita po pikselu, obično uz lošije dithering nego što biste vi mogli da uradite sami. Zanimljivo pitanje je gde ta down-conversion treba da se desi, a odgovor u PDFlibPas-u govori nešto korisno o tome kako da proširite renderer koji ne biste radije prepisivali
PDFlibPas je nativna Object Pascal PDF biblioteka za Delphi i C++Builder. Njeno rendering jezgro rasterizuje stranu u bitmapu i može da emitujе BMP, PNG, JPEG, WMF i još nekoliko formata. Ono što do nedavno nije mogla bilo je da vrati pravu monochrome bitmapu ili da prikaže samo deo strane. Obe mogućnosti su stigle u v3.83.0 i obe su izgrađene kao tanke convenience slojeve iznad postojećeg renderera, a ne kao promene samog rasterizera. To ograničenje je cela priča
Zašto posle renderovanja, a ne unutar renderera
Najočigledniji način da proizvedete 1-bit sliku jeste da rasterizeru kažete da crta u 1-bit. To je i način da se sve ostalo pokvari. Unutrašnja bitmapa renderera se kreira sa hardcoded PixelFormat := pf24bit u PDFlibRenderer konstruktoru, i ta 24-bitna površina se deli svim render putanjama: PNG eksportu, device-context preview-u, JPEG izlazu, svemu. Ako je prebacite na pf1bit u izvoru, niste dodali monochrome funkciju, nego ste degradirali fidelity boja za svakog pozivaoca u biblioteci i preuzeli na sebe debagovanje desetak downstream regresija
Zato RenderPageToMonochromeFile bira suprotan put. On prvo normalno renderuje stranu u privremeni 24-bit BMP, a tek onda je postprocesom spušta na 1-bit. Renderer ostaje netaknut. Monochrome ponašanje živi isključivo u convenience metodi, što znači da ne može da utiče ni na koga ko je ne pozove. To je baš takav trade-off koji vredi imenovati: post-process plaća jednu dodatnu alokaciju bitmap-a i privremeni fajl, a zauzvrat drži opterećeno jezgro potpuno van opsega. Za funkciju koja postoji da služi fax i arhivske edge-case-ove, to je prava strana bilansa
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 1-bit spuštanje zapravo radi
Down-conversion se oslanja na GDI umesto na ručno pisanu threshold petlju, i taj izbor utiče na kvalitet izlaza. Unutar metode 24-bitni privremeni bitmap se učitava u TBitmap, drugi TBitmap se pravi sa PixelFormat := pf1bit na istim dimenzijama, i pikseli prelaze preko jednim blit-om:
// 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 sa 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-bit paletu. HALFTONE čini da primeni halftone dithering, pretvarajući sive površine i antialiasovane ivice teksta u šare crnih i belih tačaka umesto da ih hard clipuje na najbliže od dve boje. Izbacite poziv mode-a ili koristite podrazumevani BLACKONWHITE, i grayscale sadržaj se posterizuje u blokaste threshold oblike. Za izlaz namenjen skeniranom dokumentu i OCR pripremi, ditherovani rezultat je gotovo uvek ono što želite
Jedan detalj je neupitan i lako ga je pogrešno uraditi: privremeni render mora biti BMP. RenderPageToMonochromeFile poziva opšti renderer sa options kodom 0, što je BMP. Options argument na RenderPageToFile je mali celobrojni enum, a vrednosti nisu zamenljive za ovu svrhu: 0 je BMP, 1 JPEG, 2 WMF, 3 EMF, 5 PNG, i tako dalje. Down-converter zatim radi TBitmap.LoadFromStream nad privremenim fajlom. Dajte mu WMF, tako što ćete proslediti 2, i to učitavanje baca "Bitmap image is not valid", jer je Windows Metafile vektorski stream zapisa, a ne DIB. Monochrome down-conversion je raster operacija od početka do kraja, pa intermedijar mora da bude raster format
Prikazivanje samo podregiona strane
Druga metoda, RenderPageRegionToFile, prikazuje samo pravougaonik strane umesto cele strane. Slučajevi upotrebe su poznati čim napravite bilo koji dokument viewer: iseći blok potpisa iz ugovora, napraviti tile za zumiranu mapu velikog crteža ili izvući jednu označenu oblast za thumbnail bez trošenja na rasterizaciju cele strane pri 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 zarezom odvojena double-a u PDF poentama, parsirana ručno unutar metode da bi se zaobišle locale i DelimitedText mušice. Iz širine i visine metoda računa veličinu izlazne bitmap-e kao Round(Width * DPI / 72) po Round(Height * DPI / 72), dodeljuje joj memorijsku pf24bit bitmap-u tačno te veličine i renderuje u njen device context preko RenderPageToDCClip. Rezultat sadrži samo isečeni pravougaonik, dimenzionisan prema regionu, a ne prema celoj strani
Clip parametar koji nije radio ništa
Ovde je posao bio oštriji nego što izgleda. RenderPageToDCClip je dugo imao Clip parametar, i to je bila laž. Poziv je prihvatao argument, prosleđivao ga TPDFPageTree.RenderPageToDC, a ta implementacija ga je potpuno ignorisala, nikada ga ne prosleđujući rendereru. Mogli ste da prosledite bilo koji pravougaonik i dobijete celu stranu nazad. Svako ko je to koristio očekujući crop dobijao je render cele strane i, zavisno od rasporeda, možda to nije ni primetio.RenderPageToDCClipv3.83.0 je spojio žicu
sada parsira isti RenderPageToDC tačkasti pravougaonik i primenjuje ga kao pravi GDI clip region na ciljnom device context-u pre nego što renderer crta. Pretvaranje iz poenti u device piksele je uobičajeni "Left,Top,Width,Height" faktor skale, primenjen na sve četiri ivice. Redosled oko rendera je standardni save/clip/restore ples:DPI / 72The
// 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);
/ SaveDC pair je ono što ovo čini bezbednim za višestruko pozivanje: clip region se gura na DC state stack, strana se iscrta, a originalni clip se vraća nazad bez obzira kako render izlazi. RestoreDC(-1) vraća poslednje sačuvano stanje, što je standardni idiom za upareni save/restore. Preskočite restore i pozivalac koji ponovo koristi isti DC za sledeći full-page render našao bi ga misteriozno isečenog na poslednji region. Popravka mrtvog parametra takođe je besplatno popravila RestoreDC(TargetDC, -1) za RenderPageRegionToFile, jer ta nova metoda prolazi baš ovom putanjom
Jedna stvar ponašanja koju treba upamtiti: clip seče, ne skalira. Strana se i dalje rasterizuje na DPI koji ste tražili, na svom normalnom mestu, a clip region jednostavno odbacuje sve van pravougaonika. Ne zumirate region da ispuni izlaz; izrezujete prozor iz rendera pune rezolucije. Ako hoćete uvećan region, povećajte DPI. Koordinate pravougaonika tumače se u device prostoru nakon skaliranja poenti u piksele, merene od gornjeg levog ugla renderovane površine, pa svoje Left i Top planirajte od vrha strane nadole. Za dublji pregled kako PDFlibPas upravlja device context-om za izlaz na ekranu, prateći tekst o print preview i device-context izlazu prolazi kroz isti DC plumbing sa strane prikaza
Poštena granica: 1-bit BMP, a ne G4 TIFF
It would be easy to oversell this as "fax-ready output," so here is the limit stated plainly. RenderPageToMonochromeFile produces a pf1bit BMP. It does not produce a CCITT Group 4 TIFF, which is the format a real fax workflow or a TIFF archive usually expects. The reason is concrete rather than an oversight: PDFlibPas's CCITT unit currently decodes G4 streams but has no G4 encoder. Without an encoder there is nowhere to write compressed monochrome runs, so the monochrome path stops at an uncompressed 1-bit DIB
In practice that is still useful. A 1-bit BMP is the correct pixel format, dithered and ready, and most fax, archival, or OCR toolchains will happily ingest it or convert it to G4 themselves with one downstream step. But if your requirement is literally a Group 4 TIFF straight out of the library, this is not that yet, and you should plan a compression stage of your own. Knowing where a feature stops is worth as much as knowing what it does
Both methods are deliberately small, and that is the design lesson worth carrying off this page: a convenience API that sits on top of a renderer can add real capability, monochrome output, region cropping, without reaching into the rasterizer and destabilizing every other caller. When you do need to pick between rendering engines for the underlying rasterization, the overview of multi-engine PDF rendering in Delphi covers the trade-offs in depth. To see the full rendering surface and the rest of the API, the PDFlibPas Delphi PDF biblioteka stranica proizvoda ima potpunu sliku