在本文中,将探讨如何使用GhostScript库和之前文章中描述的PDF转换库来展示PDF文件。GhostScript是一个开源的软件,用于解释和处理PostScript语言和PDF文件。将通过扩展一个图像框控件来实现PDF文件的浏览功能。
请注意,这些下载中不包括原生的GhostScript DLL,需要从GhostScript项目页面获取。
要开始扩展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文件的新属性了。这些新属性包括:
除了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;
每个属性更改处理程序依次调用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控件一次处理一页并缓存结果。这意味着导航到已经查看过的页面会很快,但显示新页面可能不够理想。一个可能的改进是使控件多线程化,并继续在后台线程上加载页面。