ÐооÑдинаÑиÑе в PDF Ñа в ÑоÑки (points), а Ñези на пÑинÑеÑа Ñа в Ñ Ð°ÑдÑеÑни единиÑи на ÑÑÑÑойÑÑвоÑо (device units). ÐвеÑе ÑиÑÑеми нÑÐ¼Ð°Ñ Ð½Ð¸Ñо обÑо Ð¿Ð¾Ð¼ÐµÐ¶Ð´Ñ Ñи, докаÑо не ги пÑеобÑазÑваÑе ÑÑзнаÑелно. Това Ñазминаване е пÑиÑинаÑа за повеÑеÑо пÑоблеми Ñ Ð»Ð¾Ñо каÑеÑÑво пÑи пеÑÐ°Ñ Ð² Delphi пÑиложениÑ: кодÑÑ Ð¸Ð·Ð¿ÑаÑа пÑÐ°Ð²Ð¸Ð»Ð½Ð¸Ñ Ñайл, но ÑÑÑаниÑаÑа излиза изÑÑзана, ÑазÑегнаÑа или пÑазна. PDFium VCL ÑпÑавлÑва оÑлиÑно ÑендиÑанеÑо, а ÑиÑÑемнаÑа ÑаÑÑ Ð½Ð° пÑинÑеÑа е ÑÑандаÑÑен VCL. ÐвеÑе Ñе ÑÑÑеÑÐ°Ð²Ð°Ñ Ð»ÐµÑно Ñ Ð¼Ð°Ð»ÐºÐ¾ колиÑеÑÑво код, Ñлед каÑо ÑазбеÑеÑе оÑакваниÑÑа на вÑÑка ÑÑÑана.
Ðак ÑабоÑи веÑигаÑа âÑендиÑане пÑеди пеÑаÑâ?/h2>
PDFium VCL не комÑникиÑа диÑекÑно Ñ Ð¿ÑинÑеÑи. ÐоделÑÑ Ð½Ð° ÑабоÑа е ÑледниÑÑ: ÑендиÑаÑе ÑÑÑаниÑаÑа в TBitmap Ñ Ð¶ÐµÐ»Ð°Ð½Ð°Ñа Ð¾Ñ Ð²Ð°Ñ ÑазделиÑелна ÑпоÑобноÑÑ, Ñлед коеÑо пÑеÑ
вÑÑлÑÑе Ñози биÑмап вÑÑÑ
Ñ Ð¿Ð»Ð°ÑноÑо на пÑинÑеÑа ÑÑез StretchDIBits. ÐеÑодÑÑ TPdf.RenderPage вÑÑÑа биÑмап, ÑобÑÑвеноÑÑ Ð½Ð° извикваÑаÑа пÑогÑама, Ñака Ñе вие конÑÑолиÑаÑе ÑазмеÑиÑе в пикÑели. ÐодайÑе [rePrinting] в ÑпиÑÑка Ñ Ð¾Ð¿Ñии и PDFium Ñе пÑевклÑÑи пÑоÑеÑа на ÑендиÑане кÑм ÑакÑв, койÑо изклÑÑва ÑпеÑиÑиÑниÑе за екÑан еÑекÑи (каÑо LCD ÑÑбпикÑелно Ñ
инÑиÑане) и ÑпÑавлÑва пÑавилно MediaBox на ÑÑÑаниÑаÑа за ÑелиÑе на пеÑаÑа. Ðко пÑопÑÑнеÑе rePrinting, Ñова, коеÑо изпÑаÑаÑе кÑм пÑинÑеÑа, е екÑанно ÑендиÑане, коеÑо изглежда добÑе на мониÑоÑ, но води до по-ÑазмиÑи оÑеÑÑÐ°Ð½Ð¸Ñ Ð¿Ñи пеÑÐ°Ñ Ñ Ð²Ð¸Ñока DPI ÑазделиÑелна ÑпоÑобноÑÑ, ÑÑй каÑо ÑеÑениÑÑа за Ñ
инÑиÑане на 96 DPI екÑани не Ñа подÑ
одÑÑи за пеÑÐ°Ñ Ð¿Ñи 300 или 600 DPI.
СвойÑÑвоÑо TPdf.Active е единÑÑвенаÑа пÑовеÑка, коÑÑо ÑÑÑбва да напÑавиÑе, пÑеди да ÑабоÑиÑе Ñ ÐºÐ¾ÐµÑо и да е ÑвойÑÑво на ÑÑÑаниÑаÑа. ÐомпоненÑÑÑ Ð¿Ð¾ÑиÑка мÑлÑаливо гÑеÑкиÑе пÑи заÑеждане: задаванеÑо на Active := True за повÑеден или заÑиÑен Ñ Ð¿Ð°Ñола Ñайл не генеÑиÑа изклÑÑение, а пÑоÑÑо оÑÑÐ°Ð²Ñ Active на ÑÑойноÑÑ False. Ðинаги пÑавеÑе Ñази пÑовеÑка Ñлед пÑиÑвоÑванеÑо. ЧеÑенеÑо на PageCount или PageWidth за неакÑивен докÑÐ¼ÐµÐ½Ñ Ð²ÑÑÑа нÑла, коеÑо води до липÑа на дейÑÑвие без никакви ÑÑобÑÐµÐ½Ð¸Ñ Ð·Ð° гÑеÑка, коеÑо е много ÑÑÑдно за диагноÑÑиÑиÑане, Ñлед каÑо задаÑаÑа доÑÑигне до опаÑкаÑа за пеÑÐ°Ñ (spooler).
Ðинимален ÑикÑл за пеÑаÑ
Ðай-пÑоÑÑиÑÑ ÑабоÑÐµÑ ÑлÑÑай заÑежда Ñайл, оÑваÑÑ Ð·Ð°Ð´Ð°Ñа за пеÑаÑ, пÑеминава пÑез ÑÑÑаниÑиÑе и заÑваÑÑ Ð·Ð°Ð´Ð°ÑаÑа. ÐдинÑÑвениÑÑ Ð´ÐµÐ»Ð¸ÐºÐ°Ñен деÑайл е, Ñе Printer.NewPage не ÑÑÑбва да Ñе извиква пÑеди пÑÑваÑа ÑÑÑаниÑа, оÑкÑдеÑо идва и необÑ
одимоÑÑÑа Ð¾Ñ Ñлага FirstPage. ÐÑеÑ
вÑÑлÑнеÑо ÑÑез StretchDIBits пÑеминава пÑез GetDIBSizes и GetDIB, за да извлеÑе Ñ
аÑдÑеÑно незавиÑими биÑове Ð¾Ñ Ð´ÐµÑкÑипÑоÑа на биÑмапа (bitmap handle), Ñлед коеÑо ги изÑиÑÑва вÑÑÑ
Ñ Ð¿Ð»Ð°ÑноÑо на пÑинÑеÑа в пÑÐ»Ð½Ð¸Ñ ÑÐ°Ð·Ð¼ÐµÑ Ð½Ð° ÑÑÑаниÑаÑа:
procedure PrintPdfFile(const FileName: string);
var
Pdf: TPdf;
I: Integer;
Bitmap: TBitmap;
InfoHeaderSize, ImageSize: DWORD;
InfoHeader: PBitmapInfo;
Image: Pointer;
FirstPage: Boolean;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := FileName;
Pdf.Active := True;
if not Pdf.Active then
Exit; // load failed silently; bail out
Printer.Title := Pdf.Title;
Printer.BeginDoc;
try
FirstPage := True;
for I := 1 to Pdf.PageCount do
begin
if FirstPage then
FirstPage := False
else
Printer.NewPage;
Pdf.PageNumber := I;
// Render at printer resolution; rePrinting adjusts the render path
Bitmap := Pdf.RenderPage(
0, 0,
Printer.PageWidth,
Printer.PageHeight,
ro0,
[rePrinting]
);
try
GetDIBSizes(Bitmap.Handle, InfoHeaderSize, ImageSize);
InfoHeader := AllocMem(InfoHeaderSize);
try
Image := AllocMem(ImageSize);
try
GetDIB(Bitmap.Handle, 0, InfoHeader^, Image^);
StretchDIBits(
Printer.Canvas.Handle,
0, 0, Printer.PageWidth, Printer.PageHeight,
0, 0, Bitmap.Width, Bitmap.Height,
Image, InfoHeader^, DIB_RGB_COLORS, SRCCOPY
);
finally
FreeMem(Image);
end;
finally
FreeMem(InfoHeader);
end;
finally
Bitmap.Free;
end;
end;
finally
Printer.EndDoc;
end;
finally
Pdf.Active := False;
Pdf.Free;
end;
end;
ÐÑедаванеÑо на Printer.PageWidth и Printer.PageHeight как ÑазмеÑи на биÑмапа ознаÑава, Ñе ÑендиÑаÑе в еÑÑеÑÑÐ²ÐµÐ½Ð¸Ñ ÑÐ°Ð·Ð¼ÐµÑ Ð½Ð° пикÑелиÑе на пÑинÑеÑа, коеÑо веÑе оÑÑиÑа DPI ÑазделиÑелнаÑа ÑпоÑобноÑÑ Ð½Ð° ÑÑÑÑойÑÑвоÑо. ÐзвикванеÑо на StretchDIBits Ñлед Ñова каÑÑогÑаÑиÑа Ñези пикÑели 1:1 вÑÑÑ
Ñ ÑÑÑаниÑаÑа. Това оÑигÑÑÑва вÑзможно най-добÑоÑо каÑеÑÑво без изÑиÑна DPI аÑиÑмеÑика, но ÑабоÑи Ñамо когаÑо PDF ÑÑÑаниÑаÑа и ÑизиÑеÑкаÑа Ñ
аÑÑÐ¸Ñ Ñа Ñ ÐµÐ´Ð½Ð°ÐºÑв ÑазмеÑ. ÐогаÑо Ñе Ñе ÑазлиÑаваÑ, Ñе нÑждаеÑе Ð¾Ñ Ð¸Ð·ÑиÑно маÑабиÑане.
ÐаÑабиÑане, когаÑо ÑазмеÑиÑе на ÑÑÑаниÑаÑа и Ñ Ð°ÑÑиÑÑа Ñе ÑазлиÑаваÑ
PDF ÑÑÑаниÑа вÑв ÑоÑÐ¼Ð°Ñ A4 Portrait (поÑÑÑеÑ) не ÑÑоÑвеÑÑÑва авÑомаÑиÑно на пÑинÑеÑ, заÑеден Ñ Ñ
аÑÑÐ¸Ñ US Letter, а Ñ
оÑизонÑална ÑÑÑаниÑа (Landscape), подадена кÑм пÑинÑÐµÑ Ñ Ð¿Ð¾ÑÑÑеÑна оÑиенÑаÑиÑ, Ñе бÑде изÑÑзана. СÑандаÑÑниÑÑ Ð¿Ð¾Ð´Ñ
од е да Ñе изÑиÑли ÑниÑиÑиÑан коеÑиÑÐ¸ÐµÐ½Ñ Ð½Ð° маÑабиÑане вÑз оÑнова на ÑÑоÑноÑениеÑо Ð¼ÐµÐ¶Ð´Ñ Ð¿Ð¸ÐºÑелиÑе на пÑинÑеÑа и PDF ÑоÑкиÑе, Ñлед коеÑо Ñой да Ñе пÑиложи кÑм дваÑа ÑазмеÑа, запазвайки пÑопоÑÑиÑÑа (aspect ratio). СвойÑÑваÑа Pdf.PageWidth и Pdf.PageHeight Ð¿Ð¾ÐºÐ°Ð·Ð²Ð°Ñ ÑекÑÑиÑе ÑазмеÑи на ÑÑÑаниÑаÑа в ÑоÑки (points), кÑдеÑо една ÑоÑка е 1/72 Ð¾Ñ Ð¸Ð½Ñа. УмножаванеÑо по ÑелевоÑо DPI и ÑазделÑнеÑо на 72 ги пÑевÑÑÑа в пикÑели за ÑÑоÑвеÑнаÑа ÑазделиÑелна ÑпоÑобноÑÑ. ÐзползвайÑе по-малкаÑа ÑÑойноÑÑ (Min) Ð¾Ñ ÑÑоÑноÑениÑÑа по X и Y, за да полÑÑиÑе най-Ð³Ð¾Ð»ÐµÐ¼Ð¸Ñ Ð¼Ð°Ñаб, койÑо вÑе оÑе Ñе впиÑва в пеÑаÑнаÑа облаÑÑ:
// Fit PDF page to printable area, preserving aspect ratio
var
ScaleX, ScaleY, Scale: Double;
DestWidth, DestHeight: Integer;
Dpi: Integer;
begin
Dpi := 300; // target render resolution
Pdf.PageNumber := PageIndex;
ScaleX := Printer.PageWidth / (Pdf.PageWidth * Dpi / 72);
ScaleY := Printer.PageHeight / (Pdf.PageHeight * Dpi / 72);
Scale := Min(ScaleX, ScaleY);
// Clamp to 1.0 for shrink-to-fit only (no enlargement)
if Scale > 1.0 then Scale := 1.0;
DestWidth := Round(Pdf.PageWidth * Dpi / 72 * Scale);
DestHeight := Round(Pdf.PageHeight * Dpi / 72 * Scale);
Bitmap := Pdf.RenderPage(0, 0, DestWidth, DestHeight, ro0,
[rePrinting, reAnnotations]);
// ... transfer with StretchDIBits as above
end;
РендиÑанеÑо пÑи Dpi = 300 е подÑ
одÑÑо за повеÑеÑо оÑÐ¸Ñ Ð¿ÑинÑеÑи. ÐÑи 600 DPI биÑмапÑÑ Ð·Ð° единиÑна ÑÑÑаниÑа ÑоÑÐ¼Ð°Ñ A4 доÑÑига пÑиблизиÑелно 34 мегапикÑела, коеÑо е около 100 MB как 32-биÑов биÑмап; подобÑениеÑо в каÑеÑÑвоÑо за обикновени ÑекÑÑови докÑменÑи е минимално, а конÑÑмаÑиÑÑа на Ð¿Ð°Ð¼ÐµÑ Ð½Ð° ÑÑÑаниÑа е знаÑиÑелна. ÐапазеÑе 600 DPI за пеÑаÑниÑи или за ÑеÑÑежи Ñ Ð¼Ð½Ð¾Ð³Ð¾ векÑоÑни деÑайли, кÑдеÑо ÑазликаÑа наиÑÑина има знаÑение.
ФлагÑÑ reAnnotations вÑв вÑоÑÐ¸Ñ ÐºÐ¾Ð´Ð¾Ð² блок е незавиÑим Ð¾Ñ rePrinting. ÐклÑÑеÑе го, когаÑо поÑÑебиÑелÑÑ Ð¾Ñаква пеÑаÑи, маÑкиÑÐ°Ð½Ð¸Ñ Ð¸ полеÑа за коменÑаÑи да Ñе поÑвÑÑ Ð½Ð° Ñ
аÑÑиÑ. ÐÑопÑÑнеÑе го за ÑÑдÑÑжание без аноÑаÑии. ÐваÑа Ñлага Ð¼Ð¾Ð³Ð°Ñ Ð´Ð° Ñе комбиниÑÐ°Ñ Ñвободно.
ÐавÑÑÑане на ÑÑÑаниÑаÑа
PDFium ÑÑÑ
ÑанÑва завÑÑÑанеÑо на ÑÑÑаниÑаÑа в PDF докÑменÑа каÑо Ð·Ð°Ð¿Ð¸Ñ /Rotate, доÑÑÑпен ÑÑез Pdf.PageRotation, койÑо вÑÑÑа ÑÑойноÑÑ Ð¾Ñ Ñип TRotation (ro0, ro90, ro180, ro270). ÐооÑдинаÑнаÑа ÑиÑÑема на пÑинÑеÑа обÑÑÑа завÑÑÑаниÑÑа на 90 и 270 гÑадÑÑа ÑпÑÑмо екÑана. Ðко подадеÑе оÑигиналнаÑа ÑÑойноÑÑ Ð½Ð° PageRotation диÑекÑно кÑм RenderPage без никаква коÑекÑиÑ, Ñ
оÑизонÑалниÑе ÑÑÑаниÑи в поÑÑÑеÑен докÑÐ¼ÐµÐ½Ñ Ñе Ñе оÑпеÑаÑÐ°Ñ Ð½Ð°Ð¾Ð±ÑаÑно на повеÑеÑо дÑайвеÑи за пÑинÑеÑи под Windows. РеÑениеÑо е обикновена ÑазмÑна пÑеди извикванеÑо за ÑендиÑане: ÑÑпоÑÑавеÑе ro90 кÑм ro270 и ro270 обÑаÑно кÑм ro90, оÑÑавÑйки ro0 и ro180 непÑоменени.
ТеÑÑвайÑе Ñова поведение на ваÑÐ¸Ñ ÐºÐ¾Ð½ÐºÑеÑен Ñелеви пÑинÑÐµÑ Ð¿Ñеди пÑÑканеÑо на ÑоÑÑÑеÑа. ÐоведениеÑо на дÑайвеÑиÑе по оÑноÑение на завÑÑÑанеÑо не е еднакво пÑи ÑазлиÑниÑе пÑоизводиÑели, каÑо нÑкои дÑайвеÑи пÑÐ¸Ð»Ð°Ð³Ð°Ñ ÑобÑÑвена коÑекÑÐ¸Ñ Ð·Ð° завÑÑÑане на GDI ниво. Ðко забележиÑе двойно завÑÑÑане, пÑÐµÐ¼Ð°Ñ Ð½ÐµÑе ÑазмÑнаÑа на ÑÑойноÑÑиÑе; ако не виждаÑе никаква коÑекÑиÑ, Ñ Ð´Ð¾Ð±Ð°Ð²ÐµÑе. ÐокÑÐ¼ÐµÐ½Ñ ÑÑÑ ÑмеÑена оÑиенÑаÑÐ¸Ñ (ÑменÑÑи Ñе поÑÑÑеÑни и Ñ Ð¾ÑизонÑални ÑÑÑаниÑи) е най-бÑÑзиÑÑ Ð½Ð°Ñин да оÑкÑиеÑе Ñези пÑоблеми по вÑеме на ÑеÑÑове.
УпÑавление на памеÑÑа пÑи големи задаÑи за пеÑаÑ
ÐÑÑко извикване на RenderPage Ð·Ð°Ð´ÐµÐ»Ñ Ð½Ð¾Ð² Ð¾Ð±ÐµÐºÑ TBitmap, койÑо е ÑобÑÑвеноÑÑ Ð½Ð° извикваÑаÑа пÑогÑама и ÑÑÑбва да бÑде оÑвободен Ð¾Ñ Ð½ÐµÑ. РгоÑÐ½Ð¸Ñ ÑикÑл блокÑÑ try/finally Bitmap.Free ÑпÑавлÑва Ñова пÑавилно за вÑÑка оÑделна ÑÑÑаниÑа. Ðе наÑÑÑпвайÑе биÑмапи на ÑазлиÑни ÑÑÑаниÑи в памеÑÑа: ÑендиÑанеÑо на 200-ÑÑÑаниÑен докÑÐ¼ÐµÐ½Ñ Ð¿Ñи 300 DPI би конÑÑмиÑало гигабайÑи Ð¿Ð°Ð¼ÐµÑ Ð¾Ñе пÑеди пÑÑваÑа ÑÑÑаниÑа да е доÑÑигнала до опаÑкаÑа за пеÑаÑ. ÐÑвобождавайÑе вÑеки биÑмап, пÑеди да пÑеминеÑе кÑм ÑледваÑаÑа ÑÑÑаниÑа.
ÐвойкаÑа ÑÑнкÑии AllocMem / FreeMem вÑÑÑе в блока за пÑеÑ
вÑÑлÑне Ñледва ÑÑÑоÑо пÑавило. ФÑнкÑиÑÑа GetDIBSizes ви казва колко Ð¿Ð°Ð¼ÐµÑ Ð¸Ð·Ð¸ÑÐºÐ²Ð°Ñ DIB заглавиеÑо и пикÑелниÑе данни. Ðие ги заделÑÑе, попÑлваÑе, изÑиÑÑваÑе и оÑвобождаваÑе в ÑамкиÑе на обÑабоÑкаÑа на една ÑÑÑаниÑа. ÐзÑиÑанеÑо на Ð¿Ð°Ð¼ÐµÑ Ð¾Ñ ÐºÐ¾Ð¹Ñо и да е Ð¾Ñ Ð±Ð»Ð¾ÐºÐ¾Ð²ÐµÑе Ñе доведе до изÑеÑпване на ÑиÑÑемнаÑа Ð¿Ð°Ð¼ÐµÑ Ð¿Ñи докÑменÑи Ñ Ð´Ñлжина над нÑколко деÑеÑки ÑÑÑаниÑи.
Ðко ÑÑÑбва да изпÑлнÑваÑе задаÑи за пеÑÐ°Ñ Ð²Ñв Ñонова ниÑка (background thread), дÑÑжÑе TPdf и вÑиÑки VCL Ð¸Ð·Ð²Ð¸ÐºÐ²Ð°Ð½Ð¸Ñ Ð·Ð° пеÑÐ°Ñ Ð² ÑÑÑаÑа ниÑка. СамиÑÑ ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÐµÐ½Ñ TPdf не е ниÑково безопаÑен Ð¼ÐµÐ¶Ð´Ñ ÐµÐºÐ·ÐµÐ¼Ð¿Ð»ÑÑи, коиÑо ÑподелÑÑ Ð³Ð»Ð¾Ð±Ð°Ð»Ð½Ð¾Ñо ÑÑÑÑоÑние на PDFium DLL библиоÑекаÑа. Ðай-ÑигÑÑниÑÑ Ð¼Ð¾Ð´ÐµÐ» е по един екземплÑÑ Ð½Ð° TPdf за вÑÑка ниÑка, каÑо вÑÑка заÑежда Ñвое ÑобÑÑвено копие на Ñайла.
ÐÑедÑÑавениÑÑ ÑÑк API за ÑендиÑане и обÑабоÑка на докÑменÑи е ÑаÑÑ Ð¾Ñ PDFium VCL Component за Delphi и C++Builder.