Bài viết Kỹ thuật

In một tệp PDF đã tải trong Delphi với HotPDF và TPrinter

HotPDF in một tệp PDF đã tải ra máy in vật lý, bất kỳ ngữ cảnh thiết bị (device context) Windows nào, hoặc vào clipboard thông qua ba lệnh gọi: PrintLoadedDocument điều khiển một VCL TPrinter, RenderLoadedPageToDC vẽ một trang trực tiếp vào bất kỳ HDC nào, và RenderLoadedPageToClipboard sao chép một trang dưới dạng ảnh bitmap hoặc tệp metafile vectơ. HotPDF là một thành phần PDF VCL gốc dành cho Delphi và C++Builder, thành phần này đã bổ sung đường dẫn in ấn chất lượng cao này trong phiên bản v2.331.0 và tinh chỉnh trong phiên bản v2.348.0, nhờ đó bạn không còn phải tự tay thiết lập một vòng lặp TPrinter xung quanh một bộ kết xuất bitmap nữa. Cả ba lệnh gọi đều vẽ nội dung vectơ của trang trực tiếp tại nơi cần thiết, giúp bản in luôn sắc nét ở mức tối đa mà máy in có thể kết xuất

Tại sao nên vẽ trực tiếp trang PDF ra máy in thay vì dùng ảnh bitmap?

Một trang PDF là một chương trình chứa các toán tử vẽ vectơ — các đường dẫn, văn bản và hình ảnh được thực thi trên mô hình đồ họa ISO 32000-1 — chứ không phải một lưới các điểm ảnh (pixel). Cách hiển nhiên để in là raster hóa trang thành một ảnh bitmap rồi vẽ (blit) ảnh bitmap đó lên canvas của máy in; cách này hoạt động được, nhưng nó lại vứt bỏ điểm mạnh duy nhất của máy in. Một máy in laser hiện đại có thể xử lý độ phân giải 600 hoặc 1200 DPI; một ảnh bitmap được raster hóa ở 150 hoặc 300 DPI rồi được kéo giãn cho vừa vặn khi in ra sẽ bị mờ, với các cạnh văn bản bị răng cưa và hiện rõ các điểm nội suy. RenderLoadedPageToDC chọn hướng đi tốt hơn: nó tái hiện lại luồng nội dung của trang dưới dạng các lệnh gọi GDI được phát trực tiếp vào ngữ cảnh thiết bị đích, nhờ đó văn bản vectơ và nghệ thuật đường nét (line art) truyền tới máy in ở dạng vectơ và chỉ được raster hóa mộtครั้ง duy nhất, bởi chính máy in, tại độ phân giải riêng của máy in

Làm thế nào để in một tệp PDF đã tải ra máy in vật lý trong Delphi?

HotPDF in một tài liệu đã tải với PrintLoadedDocument(Printer, Options), nhận một VCL TPrinter đã gán và một bản ghi THPDFPrintOptions, in các trang được chọn, và trả về số lượng trang đã được gửi đi. Trước tiên, hãy đặt Printer.PrinterIndex để chọn một thiết bị, điền vào bản ghi tùy chọn — hàm lớp THPDFPrintOptions.Default cung cấp cho bạn các giá trị hợp lý — và gọi nó một lần. Thư viện sẽ mở công việc in bằng Printer.BeginDoc, duyệt qua phạm vi trang và phát một lệnh Printer.NewPage giữa các trang, rồi đóng nó bằng Printer.EndDoc, vì vậy bạn không bao giờ phải tự quản lý vòng đời công việc in bằng tay

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;

Chuỗi PageRange bắt đầu từ 1 và có định dạng giống như định dạng trong hộp thoại in: '1,3-5' in trang 1 và các trang từ 3 đến 5, còn một chuỗi trống sẽ in toàn bộ tài liệu. Copies ánh xạ trực tiếp tới Printer.Copies. Tuy nhiên, hãy lưu ý về tính năng xếp bộ (Collate): do VCL hiển thị tính năng xếp bộ không nhất quán giữa các phiên bản Delphi khác nhau, HotPDF chỉ thiết lập số lượng bản sao và nhường quyền xử lý xếp bộ cho trình điều khiển máy in (driver) mặc định, vì vậy khi bạn cần đảm bảo thứ tự xếp bộ chính xác, hãy thiết lập trực tiếp trên driver thông qua TPrinterSetupDialog thay vì tin tưởng vào cờ này

DPI và FitPage quyết định tỷ lệ in như thế nào?

FitPageDPI tương tác với nhau theo một cách đáng để tìm hiểu trước khi bạn phân phối phần mềm. Với FitPage là True — mặc định — HotPDF đọc MediaBox của mỗi trang, chia tỷ lệ của nó cho vừa Printer.PageWidthPrinter.PageHeight mà vẫn bảo toàn tỷ lệ khung hình (aspect ratio), và kết xuất ở tỷ lệ giúp trang lấp đầy giấy in. Trong chế độ đó, Options.DPI bị ghi đè một cách hiệu quả bởi tỷ lệ khớp, vì đầu ra phải tương ứng với kích thước trang vật lý của máy in. Với FitPage là False, HotPDF kết xuất mỗi trang theo tỷ lệ 1:1 chính xác tại Options.DPI, ánh xạ 72 đơn vị PDF thành các điểm ảnh thiết bị DPI, đây là điều bạn muốn khi trang nguồn và giấy in vốn đã có cùng kích thước và bạn cần một độ phân giải chính xác. Hãy tìm đến FitPage khi kích thước trang nguồn và kích thước khay giấy khác nhau; hãy tìm đến 1:1 khi chúng khớp nhau và bạn muốn chia tỷ lệ xác định

Một số tùy chọn được bổ sung trong phiên bản v2.348.0 giúp sửa các bản in vốn trông gần như chính xác. Mọi máy in vật lý đều có một lề cứng (hard margin) — một đường viền không thể in được mà phần cứng không thể tiếp cận — và theo mặc định, điểm gốc của trang nằm bên trong nó, do đó một trang in tràn lề sẽ bị mất một dải ở hai cạnh. Đặt IgnoreHardMargin thành True và HotPDF sẽ truy vấn PHYSICALOFFSETXPHYSICALOFFSETY của máy in, sau đó dịch chuyển điểm gốc của thiết bị để triệt tiêu độ lệch đó sao cho điểm gốc của trang ánh xạ khớp với góc thực của tờ giấy. Tùy chọn thứ hai, AutoRotate, được bật theo mặc định: nó đọc MediaBox của mỗi trang và khi một trang có chiều rộng lớn hơn chiều cao, nó sẽ lật máy in sang chế độ in ngang (landscape) để trang rộng được in đầy đủ không bị cắt lén thay vì bị cắt xén theo chế độ in dọc (portrait). AutoRotate sẽ nhường chỗ ngay khi bạn đặt Orientation một cách rõ ràng, vì thế một lựa chọn hướng in chủ động sẽ luôn giành ưu thế

Làm thế nào để kết xuất một trang PDF vào bất kỳ ngữ cảnh thiết bị nào?

RenderLoadedPageToDC(PageIndex, DC, DPI, W, H) là hàm cơ bản bên dưới vòng lặp in ấn, và nó rất hữu dụng khi đứng độc lập. PrintLoadedDocument gọi nó một lần cho mỗi trang đối với canvas của máy in, nhưng mục tiêu có thể là bất kỳ HDC Windows nào: một DC bộ nhớ ngoài màn hình (off-screen memory DC), canvas của một control trong một trình xem tùy chỉnh, hoặc một bề mặt xem trước khi in. PageIndex bắt đầu từ 0, WH là các đơn vị thiết bị của bề mặt mục tiêu, và bộ kết xuất sẽ duyệt qua /Contents của trang rồi phát các cuộc gọi GDI vào DC của bạn. Có một hành vi chủ ý rất đáng lưu ý: nó không vẽ màu nền. Người gọi sở hữu bề mặt, vì vậy nếu bạn muốn có giấy trắng phía sau trang, bạn phải tự mình tô màu cho DC trước khi kết xuất, đây chính là điều giúp bạn có thể ghép một trang lên trên một canvas có sẵn khi bạn mong muốn

// 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;

Khi bạn chỉ cần một TBitmap hoàn chỉnh — một hình thu nhỏ, một kết xuất hình ảnh xuất ra, hoặc một bản xem trước bạn sẽ lưu vào bộ nhớ đệm — hãy tìm đến RenderLoadedPageToBitmap thay thế, được đề cập trong bài viết đi kèm về kết xuất các trang PDF thành bitmap. Sự khác biệt nằm ở chỗ ai sở hữu bề mặt: lệnh gọi bitmap phân bổ và trả về một vùng đệm điểm ảnh, trong khi RenderLoadedPageToDC vẽ vào một ngữ cảnh thiết bị bạn đang nắm giữ, đây chính là thứ mà canvas máy in hoặc trình xử lý paint đưa cho bạn. Cả hai đều chia sẻ cùng một bộ thông dịch luồng nội dung và công cụ phông chữ nhúng, giúp văn bản in ra có các đường viền nhúng chính xác như nhau

Làm thế nào để dán một trang PDF vào Word hoặc một trình chỉnh sửa ảnh?

RenderLoadedPageToClipboard(PageIndex, DPI, AsMetafile) đưa một trang vào clipboard Windows, sẵn sàng để dán vào Word, PowerPoint, email, hoặc một trình chỉnh sửa ảnh. Cờ AsMetafile lựa chọn định dạng, và lựa chọn này quan trọng hơn so với vẻ ngoài của nó. Truyền False và HotPDF sẽ đặt một ảnh bitmap thiết bị 24-bit (CF_BITMAP) được kết xuất ở độ phân giải DPI của bạn — một lưới các điểm ảnh cố định có thể dán vào bất kỳ đâu nhưng sẽ bị vỡ hình khi người nhận phóng to nó lên. Truyền True và nó sẽ đặt một tệp enhanced metafile (CF_ENHMETAFILE) trong đó văn bản và nghệ thuật vectơ của trang di chuyển dưới dạng các lệnh vẽ GDI, giúp kết quả dán luôn sắc nét ở bất kỳ mức thu phóng nào và tự động thay đổi kích thước theo bất kỳ kích thước nào mà tài liệu đích cung cấp

// Copy page 2 as a scalable vector metafile for pasting into Word
Pdf.RenderLoadedPageToClipboard(1, 300, True);

// Copy the same page as a fixed 300-DPI bitmap instead
Pdf.RenderLoadedPageToClipboard(1, 300, False);

Quy tắc ngón tay cái rất đơn giản. Nếu một người sẽ dán trang vào một tài liệu và có khả năng thay đổi kích thước của nó — chẳng hạn một hình minh họa trong một báo cáo, một điều khoản được đưa vào một bản thảo hợp đồng — hãy sử dụng metafile, bởi vì văn bản vectơ in ấn và thu phóng rất sạch sẽ. Nếu đích đến chỉ hiểu dữ liệu raster, hoặc bạn đặc biệt muốn có một ảnh chụp đóng băng tại một độ phân giải đã biết, hãy sử dụng bitmap. Đối với việc kéo các từ ra khỏi một trang thay vì chụp một bức ảnh về chúng, đó là một công việc hoàn toàn khác, được xử lý bởi API trích xuất văn bản, vốn trả về các ký tự và vị trí của chúng thay vì một hình ảnh kết xuất

Nơi đường dẫn in ấn dừng lại

Mọi thứ trên trang này chỉ dành cho Windows, và điều này đáng được nêu rõ một cách thẳng thắn. PrintLoadedDocument, RenderLoadedPageToDCRenderLoadedPageToClipboard được xây dựng trên TPrinter, mô hình device-context GDI, clipboard Windows và enhanced metafiles — tất cả đều là các cấu trúc Win32 và Win64. Trên các nền tảng đích khác, phần còn lại của HotPDF vẫn thực hiện tạo, chỉnh sửa và đọc tệp PDF, nhưng bề mặt in ấn và clipboard hỗ trợ bởi GDI này sẽ không khả dụng. Bộ kết xuất cũng hướng tới tập hợp con tài liệu phổ biến gồm văn bản, đường dẫn và hình ảnh, do đó các bản thiết kế thử nghiệm nhiều dải màu chuyển sắc và màu sắc quản lý bởi ICC sẽ được in dưới dạng bản xem trước đáng tin cậy chứ không phải là bản in thử nghiệm so màu, cùng giới hạn mà bộ kết xuất bitmap mang theo

Đối với việc in hàng loạt trên nhiều tệp, hãy tải và in lần lượt từng tài liệu rồi theo dõi chi phí bộ nhớ: PrintLoadedDocument kết xuất từng trang một tại một thời điểm, nhưng biểu đồ tài liệu đã tải sẽ nằm trong bộ nhớ trong toàn bộ quá trình thực thi công việc. Khi bạn đang điều khiển một hàng đợi in ấn trên hàng trăm tệp lớn, Direct File API dạng luồng cho quy trình công việc PDF lớn sẽ kết hợp tốt với đường dẫn in — phân tách, hợp nhất hoặc chọn các trang bạn cần trên đĩa trước, sau đó chỉ tải kết quả tinh gọn để in. Đó là cách mà PrintLoadedDocument, RenderLoadedPageToDCRenderLoadedPageToClipboard đi kèm như một phần của HotPDF Component dành cho Delphi và C++Builder — một thư viện VCL gốc không có sự phụ thuộc vào DLL bên ngoài, bao trùm việc tạo, chỉnh sửa, trích xuất văn bản, kết xuất PDF, và giờ đây là in trực tiếp ra TPrinter, bất kỳ ngữ cảnh thiết bị nào, hoặc clipboard