利用GhostScript库和PDF转换库展示PDF文件

在本文中,将探讨如何使用GhostScript库和之前文章中描述的PDF转换库来展示PDF文件。GhostScript是一个开源的软件,用于解释和处理PostScript语言和PDF文件。将通过扩展一个图像框控件来实现PDF文件的浏览功能。

开始之前

  • Cyotek.GhostScript - 提供GhostScript集成支持的核心库
  • Cyotek.GhostScript.PdfConversion - 支持将PDF文档转换为图像的支持库
  • PdfImageBoxSample - 包含更新后的ImageBox控件和扩展的PdfImageBox的示例项目

请注意,这些下载中不包括原生的GhostScript DLL,需要从GhostScript项目页面获取。

扩展ImageBox控件

要开始扩展ImageBox,首先创建一个新类并继承ImageBox控件。还决定覆盖一些默认属性,因此添加了一个构造函数来设置新值。

public class PdfImageBox : ImageBox { public PdfImageBox() { // 覆盖一些原始ImageBox的默认值 this.GridDisplayMode = ImageBoxGridDisplayMode.None; this.BackColor = SystemColors.AppWorkspace; this.ImageBorderStyle = ImageBoxBorderStyle.FixedSingleDropShadow; // 新的PDF转换设置 this.Settings = new Pdf2ImageSettings(); } }

为了确保正确的设计器支持,添加了带有新的DefaultValue属性的属性版本。完成这些后,是时候添加将支持查看PDF文件的新属性了。这些新属性包括:

  • PdfFileName - 要查看的PDF的文件名
  • PdfPassword - 如果需要打开PDF文件的密码(注意,实际上没有测试这个功能是否有效!)
  • Settings - 使用之前讨论过的Pdf2ImageSettings类来控制转换文档的质量设置
  • PageCache - 一个内部字典,用于在页面加载后缓存页面的Bitmap

除了PageCache之外,这些属性中的每一个都有一个支持变更通知的后台事件,并且由于Pdf2ImageSettings实现了INotifyPropertyChanged,还将绑定一个事件来检测当个别设置属性被修改时。

导航支持

尽管PdfImageBox不提供用户界面来导航到不同的页面,但希望使宿主应用程序能够轻松地提供一个。为此,将添加一个新的CurrentPage属性,允许检索或设置活动页面,以及一些只读的CanMove*属性。这些属性允许宿主查询哪些导航选项适用,以便呈现正确的用户界面。

[Browsable(false)] public virtual int PageCount { get { return _converter != null ? _converter.PageCount : 0; } } [Category("Appearance"), DefaultValue(1)] public int CurrentPage { get { return _currentPage; } set { if (this.CurrentPage != value) { if (value < 1 || value > this.PageCount) throw new ArgumentException("Page number is out of bounds."); _currentPage = value; this.OnCurrentPageChanged(EventArgs.Empty); } } } [Browsable(false)] public bool CanMoveFirst { get { return this.PageCount != 0 && this.CurrentPage != 1; } } [Browsable(false)] public bool CanMoveLast { get { return this.PageCount != 0 && this.CurrentPage != this.PageCount; } } [Browsable(false)] public bool CanMoveNext { get { return this.PageCount != 0 && this.CurrentPage < this.PageCount; } } [Browsable(false)] public bool CanMovePrevious { get { return this.PageCount != 0 && this.CurrentPage > 1; } }

同样,为了使宿主更容易连接到控件,还添加了一些辅助导航方法。

public void FirstPage() { this.CurrentPage = 1; } public void LastPage() { this.CurrentPage = this.PageCount; } public void NextPage() { this.CurrentPage++; } public void PreviousPage() { this.CurrentPage--; }

最后,转换PDF文件中的页面有时可能需要几秒钟。为了允许宿主提供忙碌通知,例如设置等待光标或显示状态栏消息,将添加一对事件,这些事件将在页面转换之前和之后被调用。

public event EventHandler LoadingPage; public event EventHandler LoadedPage;

打开PDF文件

每个属性更改处理程序依次调用OpenPDF方法。此方法首先清除任何现有的图像缓存,然后根据当前PDF文件名和质量设置初始化转换类。如果指定的文件是有效的PDF,则转换第一页,将其缓存并显示。

public void OpenPDF() { this.CleanUp(); if (!this.DesignMode) { _converter = new Pdf2Image { PdfFileName = this.PdfFileName, PdfPassword = this.PdfPassword, Settings = this.Settings }; this.Image = null; this.PageCache = new Dictionary(); _currentPage = 1; if (this.PageCount != 0) { _currentPage = 0; this.CurrentPage = 1; } } }

显示图像

每次CurrentPage属性更改时,都会调用SetPageImage方法。此方法首先检查指定的页面是否在缓存中。如果没有,它将加载页面。一旦页面在缓存中,它就会在ImageBox中显示,用户可以像查看任何其他图像一样进行平移和缩放。

protected virtual void SetPageImage() { if (!this.DesignMode && this.PageCache != null) { lock (_lock) { if (!this.PageCache.ContainsKey(this.CurrentPage)) { this.OnLoadingPage(EventArgs.Empty); this.PageCache.Add(this.CurrentPage, _converter.GetImage(this.CurrentPage)); this.OnLoadedPage(EventArgs.Empty); } this.Image = this.PageCache[this.CurrentPage]; } } }

请注意,在执行此方法期间使用锁,以确保不能尝试同时加载同一页面两次。有了这个方法,控件就完成了,可以作为基本的PDF查看器使用了。为了将文章保持在合理的大小,省略了一些定义、重载和辅助方法;这些都可以在上面提供的示例下载中找到。

示例项目

示例项目演示了上述所有功能,并提供了一个设置用户界面以浏览PDF文档的示例。

目前,PdfImageBox控件一次处理一页并缓存结果。这意味着导航到已经查看过的页面会很快,但显示新页面可能不够理想。一个可能的改进是使控件多线程化,并继续在后台线程上加载页面。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485