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

使用 Xpdf 和 muPDF 库在 C# 中查看 PDF 文件,打印 PostScript。

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (37投票s)

2009年6月25日

GPL3

4分钟阅读

viewsIcon

1693799

downloadIcon

311

用 C++\CLI 编写的 C# 包装类,以及一个在 C# 中渲染 PDF 文件的示例实现。

AFPDFLib

介绍 

Xpdf 是一个在 GPL 许可下发布的开源库;它们有一个具有商业许可的 ActiveX,但很久以前,在我了解这个商业控件之前,我编写了这个包装库来在 C# 中渲染 PDF 文件。

背景

基本思路是在 C# 中创建 PDF 文件的预览。在互联网上查阅了很多地方后,我找到了这个很棒的库;唯一的问题是该库使用 XLib,而 Windows 上没有 XLib。幸运的是,Xpdf 可以将生成的 PDF 渲染到 Win32 DC。

编写包装器

C++\CLI 可以借助 IJW 技术混合托管和非托管代码,所以我想到也许可以链接 xpdf 库到包装器。问题是 xpdf 有些类也已在 .Net 中声明,解决方案是编译一个 C++ 项目,该项目只包含进行互操作所需的文件。
这个库 (AFPDFLib) 包含一个简单的类,它充当 C++ 和 C++\CLI 之间的代理,将 xpdf 对象保留在非托管堆中。
C# 包装器静态链接到 AFPDFLib,而 AFPDFLib 只包含
AFPDFDocInterop.h
OutlineItemInterop.h
SearchResultInterop.h

AFPDFDoc -> 实现需要 xpdf 的方法。
AFPDFDocInterop -> 编写需要包装到 C# 的方法
PDFWrapper -> 包装的方法

Marshal 字符串
IntPtr ptr = Marshal::StringToCoTaskMemAnsi(fileName);
char *singleByte= (char*)ptr.ToPointer();
try{
}finally{
     Marshal::FreeCoTaskMem(ptr);
}   

要释放资源,必须实现 IDisposable

!PDFWrapper()
{
   _pdfDoc->Dispose();
} 

使用代码

文件 xpdfWin-Interop.sln 包含所有必要的文件,您也可以从 http://www.foolabs.com/xpdf/ 下载最新版本并重新编译,排除需要 XLib 的文件。

构建项目顺序如下:freetype, xpdf, AFPDFLib, PDFLibNet。编译 PDFLibNet 后,就可以在 C# 代码中使用它:

OpenFileDialog dlg = new OpenFileDialog();
dlg.Filter = "Portable Document Format (*.pdf)|*.pdf";
if (dlg.ShowDialog() == DialogResult.OK)
{
    _pdfDoc = new PDFLibNet.PDFWrapper();
    _pdfDoc.LoadPDF(dlg.FileName);
    _pdfDoc.CurrentPage = 1;

   PictureBox pic =new PictureBox();
   pic.Width=800;
   pic.Height=1024;
   _pdfDoc.FitToWidth(pic.Handle);
   pic.Height = _pdfDoc.PageHeight;
   _pdfDoc.RenderPage(pic.Handle);
   
   Bitmap _backbuffer = new Bitmap(_pdfDoc.PageWidth, _pdfDoc.PageHeight);
   using (Graphics g = Graphics.FromImage(_backbuffer))
   {
       _pdfDoc.RenderHDC(g.GetHdc);
       g.ReleaseHdc();
   }
   pic.Image = _backbuffer;
}   

需要创建一个 PictureBox,因为该类只实现了一个接受 HWND 的方法,因为最初,我尝试在渲染 PDF 的同一控件中实现滚动。在包含的示例中,滚动由 Panel 容器控制。

Xpdf 可以将 PDF 导出为 PostScript 文件。对于打印,如果您有 PostScript 打印机,这是最佳选择:

PSOutputDev *psOut =new PSOutputDev((char *)fileName,m_PDFDoc->getXRef(),m_PDFDoc->getCatalog(),fromPage,toPage,psModePS);
if(psOut->isOk()){
    m_PDFDoc->displayPages(psOut,fromPage,toPage,PRINT_DPI,PRINT_DPI,0,gTrue,globalParams->getPSCrop(),gTrue);
}
delete psOut;  

文件必须以 RAW 格式发送(http://support.microsoft.com/kb/322091

JPG 导出

异步导出:

 _doc.ExportJpg(filename, 
1,        //From page
1,        //To page
150,      //Resolution in DPI
90,       //Jpg quality 
如果您需要同步操作,可以指定等待时间
 _doc.ExportJpg(filename, 
1,        //From page
1,        //To page
150,      //Resolution in DPI
90,       //Jpg quality
-1);      //Time to wait, -1 to infinite.
如果文件名不包含 %d 令牌(页码),则该过程将 .jpg 替换为 -page%d.jpg。PDFWrapper 公开两个事件 ExportJpgProgress 和 ExportJpgFinished。这两个事件都从导出线程调用,因此有必要使用 Invoke 进行安全调用,请参阅 frmExportJpg 以获取示例。

历史:

2009 年 7 月 6 日
  • 完整部署解决方案。
  • 已更新到 xpdf 3.0.2 版本。
  • FreeType 已更新到 2.3.1
  • 单击书签和搜索时,页面会滚动到正确的位置。
  • 已实现 PostScript。
  • 现在可以获取标题、作者。
2009 年 7 月 8 日
  • 已纠正一些内存泄漏
  • 在新线程中预渲染下一页
  • 页面缓存
  • 鼠标滚动
  • 鼠标导航
  • 加载页面链接(LinkURI、LinkGoTo)
2009 年 7 月 11 日
  • 使用 DIB Sections,解决了缩放问题。
  • 添加了 PageViewer 控件,现在仅渲染可见区域
  • 打开受密码保护的文件。
  • 导出到 txt
  • 导出到 jpg
2009 年 7 月 12 日
  • 添加了对书签、标题、主题、关键字等中 Unicode 的支持。
  • 添加了对命名目标的(named destinations)支持
2009 年 7 月 13 日
  • 修复了一些错误。
  • 添加了对 Unicode 搜索的支持
2009 年 7 月 20 日
  • 多线程 jpg 导出
  • 修复了其他 bug
2009 年 11 月 7 日
  • 添加了 MuPDF 作为第二个渲染器。
2010 年 11 月 26 日
  • 添加了使用 swftools 将 swf 转换为其他格式的支持(http://www.swftools.org/
  • 添加了使用 html2text 将 html 转换为其他格式的支持(http://pdftohtml.sourceforge.net/),我修改了 HtmlOuputDev,因为它最初使用 ghoscript 来渲染背景。现在它使用 SplashOutputDev。
已知问题:MuPDF 在处理透明度时存在一些问题,但比 xpdf 快。有几个内存泄漏。

重要提示:

MuPDF 使用递归来分析文档树,因此有必要将堆栈大小增加到至少 4MB,以避免某些复杂文件出现问题(C#、VB.Net exe 的 editbin)。迟早递归会导致堆栈溢出(stack overflow),因为树太大,所以在修复之前,这是最重要的问题。

待办事项:

- 应用最新的 xpdf 补丁 - 在查看器中显示多页。 - 改进用户界面。 - 为 MuPDF 实现 LoadFromStream。 xpdf 中缺少一些可以提取的功能: - 启用选择、图像提取和即时快照。 - 在非 PostScript 打印机上打印。
© . All rights reserved.