65.9K
CodeProject 正在变化。 阅读更多。
Home

100% .NET 组件,用于渲染 PDF 文档

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.67/5 (2投票s)

2005年3月16日

CPOL

7分钟阅读

viewsIcon

403663

介绍如何使用 PDFRasterizer.NET 控件将 PDF 转换为光栅图像、在 Windows 应用程序中显示 PDF 以及静默打印 PDF 文档。

引言

PDFRasterizer.NET 是一个用于渲染 PDF 文档的组件,完全用 C# 编写。除了 .NET 框架之外,它没有任何依赖项,并且被打包成一个单一的程序集,这使得部署非常容易。该组件绘制到任何 System.Drawing.Graphics 对象。由于 Graphics 对象可以代表光栅图像、增强型图元文件、打印机或 Windows 窗体或控件的表面,因此这是我们可以选择的最通用、最强大的渲染目标。它不会引入任何特定于供应商的图像类或引入其自身特殊性并锁定您的查看控件。

本文介绍如何使用 PDFRasterizer.NET 组件来

  1. 将 PDF 文档转换为光栅图像,如 BMP、GIF、PNG 等。
  2. 在 WinForms 应用程序中显示 PDF 文档(带或不带 EMF)
  3. 以编程方式打印 PDF 文档

PDFRasterizer.NET 类概述

PDFRasterizer.NET 对象模型非常简单,仅包含 4 个类:DocumentPagesPage ExtractedImageInfoExtractedImageInfo 用于从 PDF 文档中提取图像。有关详细信息,请参阅类参考中的 Page.ExtractImages() 方法。

文档

这是表示您要渲染的 PDF 文档的顶级类。您可以这样创建它:

using ( FileStream file = new FileStream( "some.pdf", FileMode.Open, 
                                                      FileAccess.Read ) )
{
   document = new Document( new BinaryReader( file ) );
   ... now you can enumerate the pages and draw them
}

提供密码的重载可用。Document 具有只读属性,用于检索文档信息,如作者、主题等。Pages 属性返回此文档中的页面集合。

页数

这是由 Document.Pages 返回的 PDF 文档中的页面集合。它有一个属性用于检索页数以及按索引检索页面。

页面

不出所料,这个类代表单个 PDF 页面,是最有趣的类。以下代码演示了如何枚举页面:

Document document;
... see previous code for how to obtain a document object

for ( int pageIndex = 0; pageIndex < document.Pages.Count; pageIndex++ ) 
{ 
   Page page = document.Pages[ pageIndex ]; 
   ... see further for how to draw the page
}

一旦有了 Page 对象,您就可以告诉它绘制到 System.Drawing.Graphics 对象上:

Page page;
// ... see previous code for how to obtain a page object

System.Drawing.Graphics graphics;
// ... see further for different ways to obtain or create a 
// System.Drawing.Graphics object

page.Draw( graphics );

这看起来很简单,不是吗?关键部分是如何创建 Graphics 对象。这取决于您选择的渲染目标类型。在接下来的部分中,我将描述实例化 Graphics 对象的不同方法。

将 PDF 转换为光栅图像(BMP、PNG、TIFF 等)

请参阅 PDFRasterizer.NET 应用程序中包含的 ConvertToImage_cs 和 ConvertToImage_vb 示例,以获取完整的源代码。


PDF 到图像转换器示例(随 PDFRasterizer.NET 安装程序一起提供)

将 PDF 页面转换为光栅图像涉及以下步骤:

  1. 创建具有正确分辨率的 Bitmap 对象
  2. 围绕位图包装 Graphics 对象
  3. 对 Graphics 对象应用缩放变换,以考虑分辨率
  4. 将 Page 绘制到 Graphics 对象
  5. 使用任何 GDI+ 支持的图像格式保存 Bitmap
using ( FileStream file = new FileStream( "test.pdf", FileMode.Open, 
                                                      FileAccess.Read ) )
{
   // open the PDF document
   Document document = new Document( new BinaryReader( file ) ); 
  
   // get the first page
   Page page = document.Pages[0];   

   // you must apply a scale to the PDF graphics proportional to the 
   // resolution
   float dpi = 300;
   float scale =  dpi / 72f;
   
   using ( Bitmap bitmap = new Bitmap( (int) ( scale * page.Width ), 
                                       (int) ( scale * page.Height ) ) )
   {
      // wrap a graphics around the bitmap
      Graphics graphics = Graphics.FromImage( bitmap );
  
      // apply the scale that accounts for the resolution
      graphics.ScaleTransform( scale, scale );
      
      // do the actual rendering
      page.Draw( graphics ); 
  
      // save the result as a bmp - could be any format
      bitmap.Save( "test.bmp", ImageFormat.Bmp );
   }
}

缩放变换值得解释一下。假设您想渲染一个页面大小为 Letter 的 PDF 页面。该格式的大小为 8.5 x 11 英寸(宽度 x 高度),相当于 612 x 792 点,因为一英寸有 72 点。如果您想以 72 点/英寸(DPI)的分辨率绘制此页面,那么位图将有 612(8.5 x 72)列和 792(11 x 72)行像素。现在假设您想将分辨率加倍到 144 dpi。现在位图应该有 8.5 x 144 = 1224 列和 11 x 144 = 1584 行像素。因此,比例会应用于 Bitmap 构造函数的宽度和高度参数。

Page.Draw 执行时,PDFRasterizer.NET 将执行一系列对 Graphics 对象的调用。它以自己的坐标系执行此操作,在该坐标系中,一个单位对应一个点(1/72 英寸)。为了确保 PDF 坐标空间中的坐标(612、792)在 144 dpi 位图坐标空间中最终位于(1224、1594),在 PDF 页面绘制到 Graphics 对象之前,必须对 Graphics 对象应用缩放变换。因此,在绘制页面之前调用 graphics.ScaleTransform()。

在 Windows 控件上绘制 PDF 页面

因为您可以将 PDF 页面绘制到任何 Graphics 对象,所以您也可以将其绘制到 Windows 控件的表面上。最明显的地方是 Control.Paint 事件处理程序。将 PDF 页面绘制到控件表面的典型代码如下所示:

Page page;
Panel viewerPanel;

...

// this method is a member of a System.Windows.Forms.Form derived class
// this method handles the Paint event of a control member called viewerPanel
// page is a member of type Page that has been initialized
private void viewerPanel_Paint(object sender, 
                               System.Windows.Forms.PaintEventArgs e)
{
   e.Graphics.Clear( Color.White );
   if ( null != page )
   {
      page.Draw( e.Graphics );
      e.Graphics.DrawRectangle( new Pen( Color.Gray ), 0, 0, 
                                (float) page.Width, (float) page.Height );
   }
}

请注意,通常您会在调用 page.Draw() 之前调用 e.Graphics.TranslateTransfom()e.Graphics.ScaleTransform() 来定位和调整 PDF 页面的大小。

这种简单方法的缺点是,每次触发 Paint 事件时都会绘制 PDF 页面。对于复杂的 PDF 页面,这可能非常耗时。更好的方法是将 PDF 页面一次性渲染到增强型图元文件 (EMF),然后在 Paint 事件处理程序中反复播放该 EMF 文件。这将在下一节中讨论。

使用 EMF 录制和播放 PDF 页面


PDF 查看器示例(随 PDFRasterizer.NET 安装程序一起提供)

请参阅 PDFRasterizer.NET 应用程序中包含的 ViewPDF_cs 和 ViewPDF_vb 示例,以获取完整的源代码。

增强型图元文件 (EMF) 可以被看作是对 Graphics 对象的方法调用的“录制”。录制后,您可以随时将这些方法调用播放到 Graphics 对象。与 BMP 或 PNG 等光栅格式不同,EMF 保留了矢量图形。这使其成为以不同缩放级别重复绘制 PDF 页面的绝佳格式。您只需根据缩放级别应用 ScaleTransfrom,然后播放 EMF 即可。

创建 EMF 文件

以下方法从 Page 对象创建 System.Drawing.Imaging.Metafile。

private Metafile createMetafile( Page page ) 
{ 
   Metafile metafile = null; 
   
   // create a Metafile object that is compatible with the surface of this 
   // form
   using ( Graphics graphics = this.CreateGraphics() )
   { 
      System.IntPtr hdc = graphics.GetHdc(); 
      metafile = new Metafile( hdc, new Rectangle( 0, 0, 
           (int) page.Width, (int) page.Height ), MetafileFrameUnit.Point ); 
      graphics.ReleaseHdc( hdc );
   }

   // draw to the metafile
   using ( Graphics metafileGraphics = Graphics.FromImage( metafile ) )
   {
      metafileGraphics.SmoothingMode = SmoothingMode.AntiAlias; // smooth the 
                                                                // output
      page.Draw( metafileGraphics );
   }

   return metafile;
}

播放 EMF 文件

当 Paint 事件处理程序需要一个图元文件来绘制相应的页面时,就会调用 createMetafile 方法。该图元文件在首次需要时创建(延迟加载)并缓存。以下代码显示了 Paint 事件处理程序。metafiles 变量是一个类型为 Metafile[] 的成员,在打开 PDF 文档时已初始化为 null 引用数组。

Document document; // initialized when user selects PDF document
Metafile[] metafiles[]; // initialized when user selects PDF document

...

// this method is a member of a System.Windows.Forms.Form derived class
// this method handles the Paint event of a control member called viewerPanel
private void viewerPanel_Paint(object sender, 
                               System.Windows.Forms.PaintEventArgs e)
{
   if ( null == document ) return;
         
   e.Graphics.Clear( Color.White );

   // create a metafile for the selected page if not already
   if ( null == metafiles[ selectedPage ] )
   {
      metafiles[ selectedPage ] = createMetafile( 
                                           document.Pages[ selectedPage ] );
   }
       
   // scale to fit the page  
   Page page = document.Pages[ selectedPage ];
   float scale = (float) Math.Min( viewerPanel.Width / page.Width, 
                                   viewerPanel.Height / page.Height );
   e.Graphics.ScaleTransform( scale, scale );
   
   // play the metafile to the surface of the control
   e.Graphics.EnumerateMetafile( metafiles[ selectedPage ], 
                  new Point( 0, 0 ), 
                  new Graphics.EnumerateMetafileProc( MetafileCallback ) );
}

MatefileCallback 函数是样板代码,与 PDFRasterizer.NET 的工作无关。有关其实现,请参阅示例项目 ViewPDF_EMF_cs 和 ViewPDF_EMF_vb。

打印 PDF 页面

请参阅 PDFRasterizer.NET 应用程序中包含的 PrintPDF_cs 和 PrintPDF_vb 示例,以获取完整的源代码。


打印 PDF 示例(随 PDFRasterizer.NET 安装程序一起提供)

打印涉及以下步骤:

  1. 创建 PrintDocument 实例
  2. 设置 PrintDocument 的 PrinterSetting 和 PageSettings 属性(例如,通过显示 PageSetupDialog)
  3. 将事件处理程序分配给 PrintDocument.PrintPage 事件
  4. 调用 PrintDocument.Print 方法开始打印
  5. 通过在提供的 Graphics 对象上绘图来处理 PrintDocument.PrintPage 事件

由于在第 5 步中,应用程序会获得一个 Graphics 对象以实际绘制到打印机,因此我们可以将此 Graphics 对象传递给 Page.Draw 方法。请注意,这将保留 PDF 页面上的所有矢量图形。扫描转换由打印机完成。

当用户从查看器应用程序中选择“打印”时,将调用以下方法。变量 pagesList 是一个 ListBox ,用于显示所有页面。变量 selectedPages 是一个 IEnumerator,将用于从上到下打印每个选定的页面。

ListBox pagesList;         // holds the pages in the select PDF document
IEnumerator selectedPages; // used to enumerate the selected pages in the 
                           // listbox

...

// this method is a member of a System.Windows.Forms.Form derived class
void print()
{
   if ( null == document ) return;

   // get the selected pages
   selectedPages = pagesList.SelectedIndices.GetEnumerator();
   selectedPages.Reset();

   // move enumerator to the first selected page
   if ( selectedPages.MoveNext() )
   {
      // create a print document with the same name as the PDF document
      PrintDocument printDocument = new PrintDocument();
      printDocument.DocumentName = document.Title;
         
      // query the user for printer and page settings
      PageSetupDialog setupDialog = new PageSetupDialog();
      setupDialog.Document = printDocument;
      if ( DialogResult.OK == setupDialog.ShowDialog() )
      {
         printDocument.DefaultPageSettings = setupDialog.PageSettings;
         printDocument.PrinterSettings = setupDialog.PrinterSettings;

         // setup event handler and start printing
         printDocument.PrintPage += new PrintPageEventHandler( printPage );
         printDocument.Print();
      }
   }
}

调用 printDocument.Print() 方法后,将为要打印的每个页面调用 PrintPage 事件处理程序。此事件处理程序实现如下:

// this is the PrintDocument.PrintPage event handler
void printPage( object sender, PrintPageEventArgs e )
{
   e.Graphics.PageUnit = GraphicsUnit.Point;
   
   // get the current index from the enumerator     
   if ( null != selectedPages.Current )
   {
      int pageIndex = (int) selectedPages.Current;
      Page page = document.Pages[ pageIndex++ ];
      
      // draw the page to the printer
      page.Draw( e.Graphics );
   }

   // more pages?
   e.HasMorePages = selectedPages.MoveNext();
}

类型为 IEnumerator selectedPages 返回下一个索引。此索引用于从 Pages 集合中选择相应的 Page 。接下来,此页面将绘制到代表打印机的 Graphics 对象上。最后,事件处理程序会报告是否还有更多页面。这是通过尝试移动到下一个选定的页面并返回结果来完成的。

请发送渲染不正确的 PDF 给我们

PDF 是一种非常丰富的格式,有许多不同的方法可以实现相同的输出。此外,还有许多生成器,它们各自对规范有自己的解释。因此,您可能会遇到我们无法正确渲染的文档。请将它们发送至 support@tallcomponents.com

TallComponents

您可以访问我们的网站 http://www.tallcomponents.com?ref=34。您可以在此处下载我们所有与 PDF 相关的 .NET 组件的评估版本并获得支持。

© . All rights reserved.