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

跨越 Ghostscript 和 GDI+ 的桥梁

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.91/5 (30投票s)

2002年12月2日

7分钟阅读

viewsIcon

375304

downloadIcon

3168

Ghostscript DLL 的 C++ 包装器,允许直接将 PS 渲染到 GDI+ 位图。

Sample Image - ghostwrapper.png

引言

本文档提供了一个 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 接口的有两个主要类:

  • CAPIgsdll32.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 日,初始发布。
© . All rights reserved.