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

使用 Amyuni PDF Converter 打印机创建具有 Escape 调用功能的图层 PDF

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2009年7月7日

CPOL

5分钟阅读

viewsIcon

22556

以下文章展示了开发人员如何创建多层 PDF 文档。这些层可以由最终用户隐藏或显示。可以使用多个层来创建设计或建筑图纸,或者创建多语言文档(每种语言都是 PDF 中的一个单独层)。

背景信息

PDF 层在 PDF 规范中被称为“可选内容”。这是从 PDF-1.5(Acrobat 6)开始添加的一项功能。层由标识符和标题标识。只有层标题对 PDF 文档的最终用户可见。层可以构建为树状或层级结构。以下是 PDF 中可选内容对象的示例:

9 0 obj <</Type/OCG/Name(Blue Layer) >> endobj

使用此层的每个页面都应将其添加到资源列表中,例如:

6 0 obj <</Type /Page /Parent 3 0 R /MediaBox [0 0 612 792 ] /Contents [7 0 R ]
/Resources << /ProcSet [/PDF /Text] /Font <</F10 10 0 R >>
/Properties <</OC9 9 0 R >>
>> >> endobj

OC9 是层 ID(蓝色层),是层标题(PDF 中的字符串用括号括起来)。为了指示页面内容中的各种对象属于特定层,使用了以下语法:

% start the Blue Layer that contains the text: Page 1
/OC /OC9 BDC
BT /F10 5.76  Tf 1 0 0 1 24 745.56 Tm 0.026  Tc -0.067  Tw (Page 1) Tj ET

% start a sub-layer that will contain a filled rectangle
/OC /OC11 BDC
n 1 g 96 326.96 35.76 -6.6 re f*
% end of sub-layer 1
EMC

% start another sub-layer that will contain another filled rectangle
/OC /OC13 BDC
n 1 g 96 526.96 35.76 -6.6 re f*
% end of sub-layer 2
EMC

%end of blue layer
EMC

尽管代码显示了一个父层和两个嵌套层(包含在父层内),但这不足以定义层的层次结构。`Order` 字符串用于确定层的层次结构。`Order` 字符串是文档目录的一部分。上述 PDF 示例的 `Order` 字符串应如下所示:

/Order [ 9 0 R [11 0 R 13 0 R] ]

创建正确的 `Order` 字符串需要对象 ID。`Order` 字符串还可以包含一个非层但仅作为其他层容器的父节点。在上面的示例中,我们可以将所有层归到一个名为 `(Layered PDF)` 的父层下,方法是:

/Order [ (Layered PDF)  9 0 R [11 0 R 13 0 R]  ]

重要的是 `Order` 字符串应与层在页面内容中出现的顺序匹配,否则 PDF 查看器将无法正确地“显示和隐藏”带有子层的层。

注意:PDF 规范中存在关于定义其他层父节点的不一致之处。为了使 `Layered PDF` 成为层 9 的父项,我们不会像预期的那样使用 `[(Layered PDF) [9 0 R] ]`,而是使用 `[(Layered PDF) 9 0 R]`,就好像这两个节点处于同一级别一样。

创建层组

PDF 格式允许创建互斥层组,显示一个层会自动隐藏另一个层。这些被称为单选按钮组,使用 `RBGroups` PDF 关键字定义。还可以定义哪些层默认可见,哪些默认隐藏,例如:

`/RBGroups [[ 11 0 R 13 0 R]] /ON[ 11 0 R] /OFF [13 0 R]` 表示层 11 和 13 被分组,并且层 11 默认可见,而 13 不可见。

使用 Amyuni PDF Converter 动态创建层

外部开发人员只需要了解层标题。层 ID 和对象编号对开发人员不重要,因此实现的 API 将仅使用层标题来插入层和定义层层次结构。

在打印文档时,可以通过调用 Escape 序列在 PDF 文件中插入层。Escape 序列是 GDI 构件,使开发人员能够将自定义数据发送到打印机驱动程序。

检查打印机是否支持 PDF 层

开始添加层的第一步是检查打印机是否支持 Amyuni 定义的 escape 序列。需要以下调用,包括:

// check if PDF printer supports layers
#define ESCAPE_SETLAYER		248

CHAR	technology[4];
int escape = ESCAPE_SETLAYER;
ExtEscape( hDC, GETTECHNOLOGY, 0, NULL, sizeof(technology), (LPSTR)technology
);

// the technology should be PDF
if ( lstrcmp(technology, “PDF”) )
{
MessageBox( 0, “Not an Amyuni PDF driver”, “Error”, MB_ICONERROR );
}

// and support the SETLAYER escape
if ( !ExtEscape( hDC, QUERYESCSUPPORT, sizeof(escape), (LPCSTR)&escape, 0,
NULL ) )
{
MessageBox( 0, “Not an Amyuni PDF driver”, “Error”, MB_ICONERROR );
}

`GETTECHNOLOGY` 和 `QUERYESCSUPPORT` 是 Windows GDI 预定义的 escape 序列。`ESCAPE_SETLAYER` 是由 Amyuni 打印机处理的自定义 escape。

`SETLAYER` escape 接受一个参数,该参数是 Unicode 字符串。escape 可以包装到一个辅助函数中,例如:

void SetLayer( HDC hDC, LPWSTR LayerInfo )
{
switch ( ExtEscape( hDC, ESCAPE_SETLAYER, (int)((wcslen(LayerInfo) + 1)
* sizeof(LayerInfo[0])), (LPCSTR)LayerInfo, 0, NULL ) )
{
default:
// positive return indicates success
case 0:
// no error
return;

case -1:
// error occured, closing a layer when none was open
break;

case -2:
// memory allocation error (low memory or invalid string)
break;
}
}

相同的 escape 调用用于启动层或子层、结束层或定义层层次结构。

设置层层次结构

层的顺序或层次结构在使用 `StartDoc` 调用之前,通过 `SETLAYER` escape 发送到 PDF 打印机。实际上,只要调用是在 `StartPage`/`EndPage` 块之外,但在 `EndDoc` 调用之前进行,就可以在打印过程中随时调用。在 `StartDoc` 之前设置层次结构的优点是,它允许 PDF 打印机期望层支持并将其文件头切换到 PDF-1.5。

如果 `StartDoc` 调用之前不知道层层次结构,开发人员可以在 `StartDoc` 调用之前调用 `SetLayer( L“” )` 并传入一个空字符串,仅仅是为了指示 PDF 打印机输出 PDF-1.5 文件头。`Order` 字符串应包含所有层和它们的层次结构的标题,全部以 Unicode 格式。例如:

// set the order and hierrachy of layers
SetLayer( hDC, L“/Order[(Blue Layer)[(Blue Layer - 1)(Blue Layer - 2)](Red
Layer)(Green Layer)]” );

这将生成一个如下所示的层结构:

image001.JPG

要将层分组到单选按钮组中,应在上述调用中添加 `/RBGroups`、`/ON` 和 `/OFF` 条目。此代码示例将使红色和绿色层成为组的一部分:

// set the order and radio/button groups of layers
SetLayer( hDC, L“/Order[(Blue Layer)[(Blue Layer - 1)(Blue Layer - 2)](Red
Layer)(Green Layer)]” \
L“/RBGroups[[(Red Layer)(Green Layer)]]/OFF[(Red Layer)]/ON[(Green Layer)]”
);

在层内绘制对象

在 `StartPage`/`EndPage` 调用序列中,调用 `SetLayer` 并传入有效的层标题将启动一个新层,并将所有后续的绘图指令放置在该层内。调用 `SetLayer` 并传入一个空字符串将结束最后一个层。

如果嵌套了多个层,则需要多次调用 `SetLayer` 并传入一个空字符串来关闭所有层。如果所有层未在 `EndPage` 调用时关闭,PDF 打印机将自动关闭所有打开的层,例如:

// start parent layer
SetLayer( hDC, L“Blue Layer” );
SetTextColor( hDC, RGB(0, 0, 255) );
TextOut( hDC, 200, yPos, buf, lstrlen(buf) );
yPos += 200;

// start blue sub-layer 1
SetLayer( hDC, L“Blue Layer - 1″ );
SetTextColor( hDC, RGB(0, 0, 128) );
TextOut( hDC, 800, yPos, “Blue Layer - 1″, lstrlen(“Blue Layer - 1″) );
yPos += 200;
SetLayer( hDC, L“ ” );    // close blue sub-layer 1
SetLayer( hDC, L“ ” );    // close blue parent layer

打开和关闭层的顺序应与 `Order` 字符串一致,以便 PDF 查看器以一致的方式显示或隐藏带有子层的层。

此帖子的内容由 Dany Amiouny 提供,使用了 Amyuni PDF Converter

如需了解更多关于 Amyuni Developer Pro 工具的信息,请访问 www.amyuni.com

更多来自 Amyuni

© . All rights reserved.