מאמר טכני

הדפסת PDF טעון בדלפי באמצעות HotPDF ו-TPrinter

HotPDF מדפיסה PDF טעון למדפסת פיזית, לכל הקשר התקן (device context) של Windows, או ללוח הגזירים באמצעות שלוש קריאות: PrintLoadedDocument מפעילה TPrinter של VCL,‏ RenderLoadedPageToDC מציירת עמוד ישירות לתוך כל HDC, ו-RenderLoadedPageToClipboard מעתיקה עמוד כמפת סיביות (bitmap) או כקובץ מטא-פייל וקטורי. HotPDF היא רכיב PDF מקורי של VCL עבור דלפי ו-C++Builder, והיא הוסיפה נתיב הדפסה ממדרגה ראשונה זה בגרסה v2.331.0, ושיפרה אותו בגרסה v2.348.0, כך שאינך צריך לכתוב ידנית לולאת TPrinter סביב מרנדר מפת סיביות בעצמך. שלוש הקריאות מציירת את התוכן הווקטורי של העמוד ישירות היכן שזה חשוב, כך שההדפסה נשארת חדה ככל שהמדפסת מסוגלת להציג

עמוד PDF הוא תוכנית של אופרטורים של ציור וקטורי — נתיבים, טקסט ותמונות המבוצעים מול מודל הגרפיקה ISO 32000-1 — ולא גריד של פיקסלים. הדרך הברורה להדפיס אחד כזה היא לבצע רסטריזציה של העמוד למפת סיביות (bitmap) ולהעביר את מפת הסיביות הזו לקנבס של המדפסת, וזה עובד, אך זה משליך את הדבר היחיד שמדפסת טובה בו. מדפסת לייזר מודרנית מגיעה לרזולוציה של 600 או 1200 DPI; מפת סיביות שעברה רסטריזציה ב-150 או 300 DPI ואז נמתחה כדי להתאים תגיע מטושטשת, עם קצוות טקסט משוננים ואינטרפולציה גלויה לעין. RenderLoadedPageToDC נוקטת בנתיב הטוב יותר: היא משחזרת את זרם התוכן של העמוד כקריאות GDI הנפלטות ישירות לתוך הקשר ההתקן (device context) של היעד, כך שטקסט וקטורי ואמנות קווים מגיעים למדפסת ככווקטורים ועוברים רסטריזציה פעם אחת בלבד, על ידי המדפסת, ברזולוציה המקורית שלה

כיצד מדפיסים PDF טעון למדפסת פיזית בדלפי?

HotPDF מדפיסה מסמך טעון באמצעות PrintLoadedDocument(Printer, Options), המקבלת TPrinter של VCL שהוקצה ורשומת THPDFPrintOptions, מדפיסה את העמודים הנבחרים, ומחזירה את מספר העמודים שנשלחו. הגדר תחילה את Printer.PrinterIndex כך שייבחר התקן, מלא רשומת אפשרויות — פונקציית המחלקה THPDFPrintOptions.Default מעניקה לך ערכים סבירים — וקרא לה פעם אחת. הספרייה פותחת את עבודת ההדפסה באמצעות Printer.BeginDoc, עוברת על טווח העמודים תוך פליטת Printer.NewPage בין עמוד לעמוד, וסוגרת אותה באמצעות Printer.EndDoc, כך שלעולם אינך מנהל את מחזור החיים של עבודת ההדפסה ידנית

uses Printers;

var
  Pdf: THotPDF;
  Opts: THPDFPrintOptions;
begin
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.LoadFromFile('invoice.pdf') > 0 then
    begin
      Printer.PrinterIndex := -1;         // -1 selects the default printer
      Opts := THPDFPrintOptions.Default;  // all pages, 300 DPI, FitPage, colour
      Opts.PageRange := '1,3-5';          // one-based; empty prints every page
      Opts.Copies := 2;
      Pdf.PrintLoadedDocument(Printer, Opts);
    end;
  finally
    Pdf.Free;
  end;
end;

מחרוזת ה-PageRange מבוססת 1 ונקראת כפי שנקראת תיבת דו-שיח של הדפסה: '1,3-5' מדפיסה את עמוד 1 ועמודים 3 עד 5, ומחרוזת ריקה מדפיסה את המסמך כולו. Copies ממופה ישירות ל-Printer.Copies. היה כנה עם עצמך לגבי Collate, עם זאת: מכיוון ש-VCL חושפת איסוף דפים (collation) בצורה לא עקבית בין גרסאות דלפי שונות, HotPDF מגדירה את ספירת העותקים ומשאירה את האיסוף לברירת המחדל של מנהל התקן המדפסת, כך שכאשר אתה זקוק לסדר איסוף מובטח, הגדר אותו במנהל ההתקן באמצעות TPrinterSetupDialog במקום לסמוך על כך שהדגל יישא אותו

כיצד DPI ו-FitPage קובעים את קנה המידה של ההדפסה?

כיצד DPI ו-FitPage קובעים את קנה המידה של ההדפסה?

FitPage ו-DPI מקיימים אינטראקציה בצורה ששווה להבין לפני שאתה משלח. כאשר FitPage מוגדר True — ברירת המחדל — HotPDF קוראת את ה-MediaBox של כל עמוד, מתאימה את קנה המידה שלו ל-Printer.PageWidth ול-Printer.PageHeight תוך שמירה על יחס הגובה-רוחב, ומרנדרת בקנה המידה שגורם לעמוד למלא את הנייר. במצב זה ל-Options.DPI אין השפעה והוא נעקף על ידי קנה המידה של ההתאמה, מכיוון שהפלט חייב להתאים לממדים הפיזיים של דף המדפסת. כאשר FitPage מוגדר False,‏ HotPDF מרנדרת כל עמוד ביחס של 1:1 בדיוק ב-Options.DPI שצוין, וממפה 72 יחידות PDF לפיקסלי התקן של DPI, וזהמה שאתה רוצה כאשר עמוד המקור והנייר הם כבר באותו גודל ואכפת לך מרזולוציה מדויקת. פנה ל-FitPage כאשר גודל עמוד המקור וגודל מגש הנייר שונים; פנה ל-1:1 כאשר הם תואמים ואתה רוצה קנה מידה דטרמיניסטי

שתי אפשרויות שנוספו בגרסה v2.348.0 מתקנות את ההדפסות שנראות כמעט נכונות. לכל מדפסת פיזית יש שוליים קשיחים — גבול לא ניתן להדפסה שהחומרה אינה יכולה להגיע אליו — וכברירת מחדל ראשית הצירים של העמוד נוחתת בתוכו, כך שעמוד ללא שוליים (full-bleed) מאבד רצועה משני קצוות. הגדר את IgnoreHardMargin ל-True ו-HotPDF תשאל את ה-PHYSICALOFFSETX וה-PHYSICALOFFSETY של המדפסת, ואז תסיט את ראשית הצירים של ההתקן כדי לבטל את ההיסט הזה כך שראשית הצירים של העמוד תמופה לפינת הנייר האמיתית. האפשרות השנייה, AutoRotate, מופעלת כברירת מחדל: היא קוראת את ה-MediaBox של כל עמוד, וכאשר עמוד רחב יותר מגובהו, היא הופכת את המדפסת למצב לרוחב (landscape) כך שעמוד רחב יודפס ללא קיטום במקום להיחתך למצב לאורך (portrait).‏ AutoRotate זזה הצידה ברגע שאתה מגדיר Orientation במפורש, כך שבחירת כיוון מכוונת תמיד מנצחת

כיצד מרנדרים עמוד PDF לתוך כל הקשר התקן (device context)?

RenderLoadedPageToDC(PageIndex, DC, DPI, W, H) היא פקודת היסוד שמתחת ללולאת ההדפסה, והיא שימושית בפני עצמה. PrintLoadedDocument קוראת לה פעם אחת לכל עמוד מול הקנבס של המדפסת, אך היעד יכול להיות כל HDC של Windows: הקשר התקן זיכרון מחוץ למסך (off-screen memory DC), קנבס של פקד במציג מותאם אישית, או משטח תצוגה מקדימה של הדפסה. PageIndex הוא מבוסס 0,‏ 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) מציבה עמוד אחד על לוח הגזירים של Windows, מוכן להדבקה לתוך Word,‏ PowerPoint, אימייל או עורך תמונות. דגל AsMetafile בוחר את פורמט, והבחירה חשובה יותר מכפי שהיא נראית. העבר False ו-HotPDF תציב מפת סיביות של התקן של 24 סיביות (CF_BITMAP) המרונדרת ב-DPI שציינת — גריד קבוע של פיקסלים שניתן להדביק בכל מקום אך הופך למפוקסל כאשר הנמען מגדיל אותו. העבר True והיא תציב מטא-פייל משופר (CF_ENHMETAFILE) שבו הטקסט והאמנות הווקטורית של העמוד נוסעים כפקודות ציור של GDI, כך שהתוצאה המודבקת נשארת חדה בכל זום ומתאימה את עצמה לכל גודל שמסמך היעד מעניק לה

כלל האצבע הוא פשוט. אם אדם ידביק את העמוד לתוך מסמך ועשוי לשנות את גודלו — תרשים בדוח, סעיף שהוכנס לטיוטת חוזה — השתמש במטא-פייל, מכיוון שטקסט וקטורי מודפס ועובר זום בצורה נקייה. אם היעד מבין רק נתוני רסטר, או אם אתה רוצה ספציפית תמונת מצב קפואה ברזולוציה ידועה, השתמש במפת הסיביות. שליפת המילים מתוך עמוד במקום תמונה שלהן היא עבודה שונה לחלוטין, המטופלת על ידי ממשק ה-API לחילוץ טקסט, המחזיר את התווים ומיקומיהם במקום תמונה מרונדרת

היכן שנתיב ההדפסה נעצר

כל מה שבדף זה מיועד לווינדוס בלבד, וראוי לציין זאת בבירור. PrintLoadedDocument,‏ RenderLoadedPageToDC ו-RenderLoadedPageToClipboard בנויים על TPrinter, מודל הקשר ההתקן GDI, לוח הגזירים של Windows ומטא-פיילים משופרים — כולם מבנים של Win32 ו-Win64. ביעדים אחרים שאר חלקי HotPDF עדיין יוצרים, עורכים וקוראים PDF, אך משטח הדפסה ולוח גזירים זה המגובה ב-GDI אינו זמין. המרנדר מכוון גם לתת-קבוצת המסמכים הנפוצה של טקסט, נתיבים ותמונות, כך שטיוטות עיצוב עתירות מעברי צבעים (gradients) וצבעים מנוהלי ICC יודפסו כתצוגה מקדימה נאמנה ולא כהוכחה קולורימטרית, אותו גבול שמרנדר מפת הסיביות נושא

עבור הדפסת אצווה של קבצים רבים, טען והדפס כל מסמך בתורו ושים לב לצריכת הזיכרון: PrintLoadedDocument מרנדרת עמוד אחד בכל פעם, אך גרף המסמך הטעון עצמו חי בזיכרון לאורך כל העבודה. כאשר אתה מנהל תור הדפסה של מאות קבצים גדולים, ממשק ה-Direct File API של הזרמה עבור זרימות עבודה של קובצי PDF גדולים משתלב היטב עם נתיב ההדפסה — פצל, מזג או בחר תחילה את העמודים הנחוצים לך בדיסק, ואז טען רק את התוצאה הרזה להדפסה. כך PrintLoadedDocument,‏ RenderLoadedPageToDC ו-RenderLoadedPageToClipboard מגיעות כחלק מ-HotPDF Component עבור דלפי ו-C++Builder — ספריית VCL מקורית ללא תלות ב-DLL חיצוני המכסה יצירה, עריכה, חילוץ טקסט, רינדור של PDF, וכעת גם הדפסה ישירות ל-TPrinter, לכל הקשר התקן, או ללוח הגזירים