跨越 Ghostscript 和 GDI+ 的桥梁






4.91/5 (30投票s)
2002年12月2日
7分钟阅读

375304

3168
Ghostscript DLL 的 C++ 包装器,
引言
本文档提供了一个 C++ 接口,用于调用 Ghostscript DLL。 Ghostscript[^](GS)是一个(非常)著名的 Postscript 语言[^] 的解释器,它用于将 .ps 文件渲染成各种图像格式和打印机。在大多数情况下,使用 gswin32.exe
配合命令行参数就足够了。但是,有些人可能希望将 GS 集成到他们的应用程序中,而不是直接使用 gswin32.exe
。
解决方案可能是使用 GS 提供的 DLL 函数的 C API 接口,但这对于(懒惰的)MSVC 用户来说并不合适:没有可用的项目或工作区来使用此 API,而且您必须使用 make
(一个古老的 UNIX 工具)来重新构建源代码(这简直是场噩梦)。
为了避免所有这些问题,我编写了一个 C++ 包装器,它具有以下功能:
- 动态加载
gsdll32.dll
,通过搜索路径或注册表; - 检索 DLL 中的所有 C API 方法;
- 用于初始化 Ghostscript 引擎的智能类;
- 让 Ghostscript 渲染到 GDI+。
- 兼容 UNICODE;
请注意,所有类都使用 Doxygen 进行文档记录。
许可
在开始工作之前,您可以先看一下 Ghostscript 许可政策中的这段引言:
GNU Ghostscript 不得并入禁止复制的商业产品,或者客户无法以不高于复制成本的价格获得其源代码的商业产品。尽管它可以与商业产品“聚合”;AFPL Ghostscript 根本不得并入商业产品,并且只能在极其有限的情况下进行商业分发。然而,Ghostscript 也可用于商业许可,除了并入商业产品的权利外,还包括支持、有限保证、高质量字体以及其他好处。有关 Ghostscript 商业许可的更多信息,请联系我们的商业分销合作伙伴,这是唯一合法地被授权以 GNU 或 AFPL 许可以外的任何条款分发 Ghostscript 本身的实体。
因此,如果您计划在商业应用程序中使用此包装器,请仔细阅读关于商业使用的说明。
包装器架构
用于构建 gsdll32.dll
接口的有两个主要类:
CAPI
是gsdll32.dll
的接口。它负责 DLL 的加载/卸载、方法检索和接口;CAPISettings
用于设置所有输出格式选项、标志、显示回调以及 Ghostscript 引擎的其他可能参数。一旦准备好此对象,就可以用它来启动引擎。
以下是对其他类的简要描述:
类名 | 描述 |
---|---|
CModule |
加载、卸载 Ghostscript 库。 |
CAPI |
C API 接口。 |
CAPISettings |
引擎初始化器。 |
CCallback |
显示回调的基类。 |
CGDIpCallback |
GDI+ 与 Ghostscript 的接口。 |
所有类都在 gsdll
命名空间中。从现在开始,我们将考虑以下内容:
using namespace gsdll; // ghostscript setting CAPISettings settings;
在使用此包之前,请确保您的计算机上已安装 Ghostscript(已在 AFPL Ghostscript 8.0 上进行测试)。
使用 CAPISettings 初始化 Ghostscript
CAPISettings
类用于设置 Ghostscript 引擎。
输出设备
您可以在 EDevice
枚举中选择设备。
// setting JPEG output
settings.SetDevice( CAPISettings::DeviceJPEG);
以下是所有可用输出设备中的主要几种:
DevicePNG16m
,24 位 PNG;DeviceBMP16m
,24 位 BMP;DeviceJPEG
,JPEG;DevicePDF
,Adobe PDF writer;DeviceDisplay
,自定义输出设备(更多关于此设备的信息如下)。
如上所述,用户可以向 GS 提供自定义输出设备类型。这种类型的设备用于让 GS 渲染到 GDI+ Bitmap
对象,稍后将进行讨论。
输出文件
GS 还允许您控制其输出发送到何处。对于显示设备,这并不重要,因为设备会在内部处理在屏幕上显示输出。一些专业的打印机驱动程序也以这种方式工作,但大多数设备都是通用的,需要将其定向到一个特定文件或打印机。
要将输出发送到文件,请使用 SetOutputFile 方法。例如,要将所有输出重定向到文件 ABC.xyz,请使用:
settings.SetOutputFile(_T("ABC.xyz"));
在 MS Windows 系统上打印时,输出通常直接发送到打印机 PRN。当使用 GS 作为文件栅格化器(将 PostScript 或 PDF 转换为栅格图像格式)时,您当然希望为输出指定一个命名适当的文件。
GS 还接受特殊文件名 '-',表示输出应写入标准输出(命令 shell)。
请注意,以 '%' 字符开头的文件名在 PostScript 中具有特殊含义。如果您需要指定一个实际以 '%' 开头的文件名,则必须预先显式指定 os% 文件设备。例如,要输出到名为 abc 的文件,您需要指定:
settings.SetOutputFile(_T("%%os%%%%abc"));
有关 '%' 和文件设备的更多详细信息,请参阅 GS 和 PostScript 语言以及 PostScript 语言参考手册。请注意,在 MS Windows 系统上,'%' 字符对命令处理器(shell)也有特殊含义,因此您需要将其加倍。
指定单个输出文件对于打印和栅格化图形来说效果很好,但有时您可能需要多页文档的每页图像。您可以告诉 GS 将输出的每页放入一系列命名相似的文件中。为此,请在文件名中放置一个模板 '%d',GS 将用页码替换它。
您还可以控制文件名中使用的位数:
settings.SetOutputFile(_T("ABC-%%d.png"));
产生'ABC-1.png', ... , 'ABC-10.png', ...
settings.SetOutputFile(_T("ABC-%%03d.pgm"));
产生'ABC-001.pgm', ... , 'ABC-010.pgm', ...
settings.SetOutputFile(_T("ABC_p%%04d.tiff"));
产生'ABC_p0001.tiff', ... , 'ABC_p0510.tiff', ... , 'ABC_p5238.tiff'
对于普通文档,通常 03d 是最佳选择。如上所述,在 MS Windows 系统上,您需要将 '%' 字符加倍。
杂项选项
有几个选项可以控制栅格质量:
SetResolution
,设置分辨率(dpi);SetTextAlphaBits
,控制文本子采样;SetGraphicAlphaBits
,控制图形子采样(原文此处与上一项相同,推测可能为笔误)。
如果渲染为 JPEG,您可以将输出质量从 0(低质量,高压缩)设置为 100(高质量,低压缩)。
settings.SetDevice(DeviceJPEG);
settings.SetJPEGQuality(50);
自定义选项
有许多可用的标志来控制 GS 的输出。其中一些已在 CAPISettings
类中实现,它们可以满足基本需求。但是,对于高级用户,可以使用 AddCustomArgument
设置自定义参数。
settings.AddCustomArgument(_T("-sPAPERSIZE=a4"));
启动引擎
设置完所有参数后,只需在构造函数中使用 CAPISettings
对象来构建一个 CAPI
实例即可。
// build and start GS api CAPI gsapi( settings); if (gsapi.IsInvalid()) { // the api was not properly initialized }
使用 CAPI 操作 Ghostscript 引擎
成功构建 CAPI
实例后,您必须为其提供 Postscript。您可以通过多种方式完成此操作:
- 发送整个文件
gsapi.RunFile(_T("thefiletorender.ps"));
- 发送字符串
gsapi.RunString(_T("1 2 add == flush\n"));
请注意,您可以指定要读取的字符数。 - 发送多个字符串
// prepare for receiving strings gsapi.RunStringBegin(); // sending string, the method RunStringContinue can be called several times gs.RunStringContinue(_T("more ps code")); // stop and render gsapi.RunStringEnd();
栅格化时,GS 会产生许多消息(通知或错误消息)。这些消息被重定向到 2 个字符串流(CAPI
的静态成员):
// get the message output buffer of GS TRACE(gsapi.GetOutBuffer()); // get the message error buffer of GS TRACE(gsapi.GetErrBuffer());
使用 CCallback 实现自定义输出设备
如上所述,可以实现自己的输出设备。这是通过向 GS 提供一系列函数回调来实现的。为了帮助用户,这些回调已被封装在一个虚拟基类 CCallback
中。
要编写自己的设备,您必须派生一个类,该类继承自 CCallback
并实现所有以下虚拟函数:
int Open(...) |
新设备已打开。 |
int PreClose(...) |
设备即将关闭。 |
int Close(...) |
设备已关闭。 |
int PreSize(...) |
设备即将调整大小。 |
int Size(...) |
设备已调整大小。返回的栅格指针的新值。 |
int Sync(..) |
flushpage |
int Page(...) |
Showpage 如果您想在 showpage 上暂停,请不要立即返回。 |
DWORD GetFormat() const |
返回显示格式。 |
以下是为 GS 附加自定义设备的代码片段:
// a custom display callback inherited from CCallback CMyCustomCallback callback; // attaching to GS settings.SetDisplayCallback(&callback);
我将不再详细介绍这些函数的使用。对于感兴趣的用户,自定义输出设备的创建通过 CGDIpCallback
进行说明,该类实现了输出到 GDI+ Bitmap
的功能。
跨越 GS 和 GDI+ 的桥梁:CGDIpCallback
此类实现了必要的调用,以便拥有一个输出到 GDI+ 的设备。
当调用 Size
回调时,将存储栅格缓冲区的地址,并使用适当的尺寸创建一个 Bitmap
。
之后,当调用 Page
时,栅格将被绘制到 Bitmap
中。每次调用 Page
时,该位图都会存储在一个位图列表中以供以后使用。这些位图可以通过 GetBitmapContainer
访问。
CGDIpCallback callback; // processing and rendering ... // iterating the produced bitmaps: CGDIpCallback::BitmapContainer& bc=callback.GetBitmapContainer(); CGDIpCallback::BitmapContainer::iterator it; for (it=bc.begin(); it!=bc.end();++it) { Bitmap* pBitmap=*it; // working on bitmap ... }
限制
GS DLL 在同一线程中只接受 1 个实例运行。因此,一旦启动了一个引擎,在销毁它之前,就无法成功构建其他引擎。
演示项目
演示项目是 Postscript 引擎。输入一些 postscript 代码并进行栅格化,如果成功,结果将显示在对话框窗口中。
参考文献
[1] Ghostscript 网站 |
[2] PostScript® 语言参考,第三版 |
更新历史
- 2-11-2002,
- 增加了 UNICODE 支持。
- CModule 中的 DLL 搜索改进。感谢 Russel Lang(来自 Ghostgum.com)。
- 添加了许可政策。
- 2002 年 11 月 2 日,初始发布。