مقاله فنی

چاپ یک PDF بارگذاری‌شده در دلفی با استفاده از HotPDF و TPrinter

ابزار HotPDF یک فایل PDF بارگذاری‌شده را به یک چاپگر فیزیکی، هر دستگاه‌متن (device context) ویندوز یا کلیپ‌بورد از طریق سه فراخوانی چاپ می‌کند: PrintLoadedDocument یک TPrinter از نوع VCL را هدایت می‌کند، RenderLoadedPageToDC یک صفحه را مستقیماً در هر HDC رسم می‌نماید، و RenderLoadedPageToClipboard یک صفحه را به عنوان بیت‌مپ یا متاسند (metafile) برداری کپی می‌کند. HotPDF یک کامپوننت بومی VCL PDF برای دلفی و سی‌پلاس‌پلاس‌بیلدر است و این مسیر چاپ باکیفیت را در نسخه v2.331.0 اضافه کرد و در نسخه v2.348.0 بهبود بخشید، به طوری که دیگر نیازی نیست خودتان یک حلقه TPrinter را دور یک رندرکننده بیت‌مپ بنویسید. هر سه فراخوانی محتوای برداری صفحه را مستقیماً در جایی که اهمیت دارد ترسیم می‌کنند، بنابراین نسخه چاپی به همان اندازه که چاپگر می‌تواند رندر کند، واضح و شفاف باقی می‌ماند

چرا به جای بیت‌مپ، یک صفحه PDF را مستقیماً در چاپگر رسم کنیم؟

یک صفحه PDF برنامه‌ای از اپراتورهای ترسیم برداری است — مسیرها، متن و تصاویر که بر اساس مدل گرافیکی ISO 32000-1 اجرا می‌شوند — نه شبکه‌ای از پیکسل‌ها. روش بدیهی برای چاپ آن، شطرنجی‌سازی صفحه به یک بیت‌مپ و انتقال آن بیت‌مپ به بوم چاپگر است؛ این روش کار می‌کند، اما تنها ویژگی عالی چاپگر را هدر می‌دهد. یک چاپگر لیزری مدرن وضوح ۶۰۰ یا ۱۲۰۰ DPI را ارائه می‌دهد؛ یک بیت‌مپ شطرنجی‌شده با وضوح ۱۵۰ یا ۳۰۰ DPI که برای جا شدن در صفحه کشیده شده است، کدر، با لبه‌های متنی ناهموار و تداخل‌های بصری واضح به چاپگر می‌رسد. متد RenderLoadedPageToDC مسیر بهتری را در پیش می‌گیرد: جریان محتوای صفحه را به عنوان فراخوانی‌های GDI که مستقیماً در دستگاه‌متن مقصد صادر می‌شوند بازپخش می‌کند، بنابراین متن برداری و خطوط ترسیمی به صورت بردار به چاپگر می‌رسند و فقط یک بار، توسط چاپگر و با وضوح خود چاپگر، شطرنجی می‌شوند

چگونه از یک PDF بارگذاری‌شده به یک چاپگر فیزیکی در دلفی خروجی بگیریم؟

ابزار HotPDF یک سند بارگذاری‌شده را با PrintLoadedDocument(Printer, Options) چاپ می‌کند، که یک TPrinter اختصاص‌یافته VCL و یک رکورد THPDFPrintOptions را دریافت می‌نماید، صفحات انتخاب‌شده را چاپ می‌کند، و تعداد صفحات ارسال‌شده را برمی‌گرداند. ابتدا Printer.PrinterIndex را تنظیم کنید تا دستگاه انتخاب شود، یک رکورد گزینه‌ها را پر کنید — تابع کلاس پیش‌فرض THPDFPrintOptions.Default مقادیر مناسبی را به شما می‌دهد — و آن را یک بار فراخوانی کنید. کتابخانه کار چاپ را با Printer.BeginDoc باز می‌کند، محدوده صفحات را پیمایش می‌کند و یک Printer.NewPage بین صفحات صادر می‌نماید، و در نهایت آن را با Printer.EndDoc می‌بندد، به طوری که شما هرگز چرخه حیات کار چاپ را به صورت دستی مدیریت نمی‌کنید

رشته PageRange مبتنی بر یک است و همانند کادر گفتگوی چاپ خوانده می‌شود: '1,3-5' صفحه ۱ و صفحات ۳ تا ۵ را چاپ می‌کند، و یک رشته خالی کل سند را چاپ می‌نماید. Copies مستقیماً به Printer.Copies نگاشت می‌شود. با این حال، در مورد ویژگی Collate باید واقع‌بین باشید: از آنجا که VCL ترتیب چاپ (collation) را به طور متناقض در نسخه‌های مختلف دلفی ارائه می‌دهد، HotPDF تعداد کپی را تنظیم کرده و ترتیب چاپ را به پیش‌فرض درایور چاپگر واگذار می‌کند، بنابراین وقتی به ترتیب چاپ تضمین‌شده نیاز دارید، آن را از طریق TPrinterSetupDialog روی درایور تنظیم کنید به جای اینکه به پرچم تکیه کنید

چگونه DPI و FitPage جهت مقیاس چاپ را تعیین می‌کنند؟

دو گزینه FitPage و DPI به گونه‌ای با یکدیگر تعامل دارند که درک آن قبل از ارائه محصول ارزشمند است. با مقدار True برای FitPage — که پیش‌فرض است — HotPDF کادر MediaBox هر صفحه را می‌خواند، آن را به اندازه Printer.PageWidth و Printer.PageHeight با حفظ نسبت تصویر مقیاس‌بندی می‌کند، و در مقیاسی رندر می‌کند که صفحه کاغذ را پر کند. در این حالت، ویژگی Options.DPI به طور موثری توسط مقیاس فیت لغو می‌شود، زیرا خروجی باید با ابعاد فیزیکی صفحه چاپگر مطابقت داشته باشد. با مقدار False برای FitPage، ابزار HotPDF هر صفحه را به صورت ۱:۱ در دقیقاً مقدار Options.DPI رندر می‌کند، به طوری که ۷۲ واحد PDF را به پیکسل‌های دستگاه با مقیاس DPI نگاشت می‌نماید؛ این همان چیزی است که وقتی صفحه منبع و کاغذ از قبل هم‌اندازه هستند و وضوح دقیق برایتان مهم است می‌خواهید. زمانی که اندازه صفحه منبع و سینی چاپگر متفاوت است به سراغ FitPage بروید؛ و زمانی که هم‌اندازه هستند و مقیاس قطعی می‌خواهید، از نسبت ۱:۱ استفاده کنید

دو گزینه اضافه شده در نسخه v2.348.0، چاپ‌هایی را که تقریباً درست به نظر می‌رسند اصلاح می‌کنند. هر چاپگر فیزیکی دارای یک حاشیه سخت (hard margin) است — یک مرز غیرقابل چاپ که سخت‌افزار نمی‌تواند به آن برسد — و به طور پیش‌فرض مبدأ صفحه در داخل آن قرار می‌گیرد، بنابراین یک صفحه تمام‌صفحه (full-bleed) نواری از دو لبه خود را از دست می‌دهد. ویژگی IgnoreHardMargin را روی True تنظیم کنید و HotPDF مقادیر PHYSICALOFFSETX و PHYSICALOFFSETY چاپگر را استعلام کرده، سپس مبدأ دستگاه را تغییر می‌دهد تا آن افست را لغو کند تا مبدأ صفحه به گوشه واقعی کاغذ نگاشت شود. گزینه دوم، AutoRotate، به طور پیش‌فرض فعال است: کادر MediaBox هر صفحه را می‌خواند و زمانی که عرض یک صفحه بیشتر از ارتفاع آن باشد، چاپگر را به حالت افقی (landscape) تغییر می‌دهد تا یک صفحه عریض بدون بریده شدن چاپ شود به جای اینکه به صورت عمودی (portrait) بریده شود. AutoRotate به محض اینکه Orientation را به طور صریح تنظیم کنید کنار می‌رود، بنابراین انتخاب جهت آگاهانه شما همیشه اولویت دارد

چگونه یک صفحه PDF را در هر دستگاه‌متنی رندر کنیم؟

متد RenderLoadedPageToDC(PageIndex, DC, DPI, W, H) متد پایه‌ای در زیر حلقه چاپ است و به خودی خود مفید است. متد PrintLoadedDocument آن را یک بار برای هر صفحه در برابر بوم چاپگر فراخوانی می‌کند، اما مقصد می‌تواند هر HDC ویندوز باشد: یک DC حافظه خارج از صفحه (off-screen)، بوم یک کنترل در یک نمایشگر سفارشی، یا یک سطح پیش‌نمایش چاپ. مقدار PageIndex مبتنی بر صفر است، W و H واحدهای دستگاه سطح مقصد هستند، و رندرکننده بخش /Contents صفحه را پیمایش کرده و فراخوانی‌های GDI را در DC شما صادر می‌کند. یک رفتار آگاهانه ارزش دانستن دارد: این متد هیچ پس‌زمینه رنگی رسم نمی‌کند. فراخوان‌کننده مالک سطح است، بنابراین اگر می‌خواهید کاغذ سفید در پشت صفحه داشته باشید، خودتان قبل از رندر DC را پر می‌کنید؛ این دقیقاً همان چیزی است که به شما امکان می‌دهد وقتی می‌خواهید، یک صفحه را روی بوم موجود ترکیب کنید

// Draw page 1 into an off-screen 24-bit surface at 150 DPI
var
  Surface: TBitmap;
begin
  Surface := TBitmap.Create;
  try
    Surface.PixelFormat := pf24bit;
    Surface.SetSize(1275, 1650);           // 8.5 x 11 in at 150 DPI
    Surface.Canvas.Brush.Color := clWhite;
    Surface.Canvas.FillRect(Surface.Canvas.ClipRect);  // caller paints the background
    Pdf.RenderLoadedPageToDC(0, Surface.Canvas.Handle, 150, Surface.Width, Surface.Height);
    // Surface now holds the composited page, ready to blit or save
  finally
    Surface.Free;
  end;
end;

چگونه یک صفحه PDF را در Word یا یک ویرایشگر تصویر پیست کنیم؟

متد RenderLoadedPageToClipboard(PageIndex, DPI, AsMetafile) یک صفحه را روی کلیپ‌بورد ویندوز قرار می‌دهد که آماده پیست کردن در Word، PowerPoint، یک ایمیل یا یک ویرایشگر تصویر است. پرچم AsMetafile فرمت را انتخاب می‌کند و این انتخاب بیش از آنچه به نظر می‌رسد اهمیت دارد. با ارسال مقدار False، ابزار HotPDF یک بیت‌مپ دستگاه ۲۴ بیتی (CF_BITMAP) رندرشده در وضوح DPI شما قرار می‌دهد — یک شبکه ثابت از پیکسل‌ها که در هر کجا پیست می‌شود اما زمانی که دریافت‌کننده آن را بزرگ می‌کند، کیفیت خود را از دست می‌دهد (پیکسلی می‌شود). با ارسال مقدار True، این متد یک متاسند پیشرفته (CF_ENHMETAFILE) قرار می‌دهد که در آن متن و طرح برداری صفحه به عنوان دستورات ترسیم GDI منتقل می‌شوند، بنابراین نتیجه پیست‌شده در هر بزرگ‌نمایی واضح باقی می‌ماند و به هر اندازه‌ای که سند مقصد ارائه دهد تغییر شکل می‌دهد

جایی که مسیر چاپ متوقف می‌شود

همه چیز در این صفحه فقط مخصوص ویندوز است و این موضوع باید به صراحت بیان شود. متدهای PrintLoadedDocument، RenderLoadedPageToDC و RenderLoadedPageToClipboard بر اساس TPrinter، مدل دستگاه‌متن GDI، کلیپ‌بورد ویندوز و متاسندهای پیشرفته ساخته شده‌اند — که همگی ساختارهای Win32 و Win64 هستند. در سایر مقصدها، بقیه بخش‌های HotPDF همچنان فایل PDF را ایجاد، ویرایش و می‌خوانند، اما این سطح چاپ و کلیپ‌بورد مبتنی بر GDI در دسترس نیست. رندرکننده همچنین زیرمجموعه اسناد معمولی شامل متن، مسیرها و تصاویر را هدف قرار می‌دهد، بنابراین طرح‌های اولیه با گرادیان‌های سنگین و رنگ‌های مدیریت‌شده با ICC به عنوان یک پیش‌نمایش وفادار چاپ می‌شوند تا یک نسخه چاپی دقیق رنگی، که این همان محدودیتی است که رندرکننده بیت‌مپ نیز دارد

برای چاپ دسته‌ای در میان فایل‌های بسیار، هر سند را به نوبت بارگذاری و چاپ کنید و مراقب مصرف حافظه باشید: PrintLoadedDocument یک صفحه را در هر زمان رندر می‌کند، اما خود نمودار سند بارگذاری‌شده برای کل کار در حافظه باقی می‌ماند. هنگامی که یک صف چاپ را روی صدها فایل بزرگ هدایت می‌کنید، API جریان مستقیم فایل برای گردش‌های کاری PDFهای بزرگ به خوبی با مسیر چاپ جفت می‌شود — ابتدا صفحات مورد نیاز خود را روی دیسک تقسیم، ادغام یا انتخاب کنید، سپس فقط نتیجه سبک‌تر را برای چاپ بارگذاری نمایید. به این ترتیب است که PrintLoadedDocument، RenderLoadedPageToDC و RenderLoadedPageToClipboard به عنوان بخشی از HotPDF Component برای دلفی و سی‌پلاس‌پلاس‌بیلدر ارائه می‌شوند — یک کتابخانه بومی VCL بدون وابستگی به DLLهای خارجی که ایجاد، ویرایش، استخراج متن، رندر و اکنون چاپ مستقیم به TPrinter، هر دستگاه‌متن یا کلیپ‌بورد را پوشش می‌دهد