第2章 理解插件
这一章提供一个总览,插件如何被载入,被初始化,被卸载,以及其它与插件和PDF库应用相关的概念。推荐你在开始开发插件或PDF库应用之前阅读本章节。
第1节 关于插件初始化
这部分描述Acrobat或Adobe Reader执行初始化插件的操作顺序。
插件载入和初始化:
当Acrobat或Adobe Reader启动时,自动检索/Program Files/Adobe/Acrobat/plug_ins目录,定位和载入插件。另外,Acrobat或Adobe Reader也检索包含在这个目录里的子目录。这个检索工作只有一级深度。
Acrobat或Adobe Reader在启动时在启动界面最底部一行显示一个进度消息。当每个插件被载入时,进度消息显示插件的名字。Acrobat或Adobe Reader启动时如果SHIFT键被按住,没有插件被载入。同时,如果Acrobat或Adobe Reader运行在被认证模式时,也没有第三方插件被载入。
当创建一个插件时,在思想里遵守如下规则:
1. 不要在你的插件初始化时创建对话框,或做其它可能妨碍Acrobat或Adobe Reader成功启动的工作。应用通过IAC(interapplication communication)被启动,它不会对响应你的对话框有所表现。
2. 执行一个PluginUnload程序,释放分配的内存。这个程序通过在初始化程序中返回false被调用。在正常条件下,这个程序不会被调用,直到用户关闭Acrobat或Adobe Reader。
握手和初始化:
Acrobat或Adobe Reader执行握手动作,在每一个插件被打开和载入时。在握手期间,插件说明自己的名字,几个初始化程序,和一个可选择的卸载程序。
一个插件必须执行下面的握手函数:
ACCB1 ASBool ACCB2 PIHandshake(ASUns32 handshakeVersion, void, *hsData)
在握手期间,插件接收一个hsData数据结构 (在PIVersn.h文件中定义)。Acrobat或Adobe Reader用ASCallbackCreateProto方法,把所有的被传递到这个数据结构里的函数指针转换到回调函数里。关于这个方法的信息,参考文档《Acrobat and PDF Library API Reference》。
头文件DUCallbacks.h声明了所有的回调方法,这些方法必须在你的插件中被定位。下面显示这些回调方法的函数声明:
ACCB1 ASBool ACCB2 PluginExportHFTs(void);
ACCB1 ASBool ACCB2 PluginImportReplaceAndRegister(void);
ACCB1 ASBool ACCB2 PluginInit(void);
ACCB1 ASBool ACCB2 PluginUnload(void);
所有的回调函数返回true,如果你的插件的声明成功地完成,或者回调函数是可选的并且没有被执行。如果你的插件声明失败,返回false。不管是Acrobat, Adobe Reader,还是一个插件中止握手,Acrobat或Adobe Reader都提示一个警告对话框,在载入其它插件之前显示一个解释摘要。在最少的情况下,一个插件必须执行PluginInit回调。
注意:握手函数位于PIMain.c文件中。这个文件中的源代码是官能性的并且绝对不能被修改。
导出HFTs:
HFT(Host Function Table主功能表)是一个机制,借此插件可以调用Acrobat或Adobe Reader中的方法,其它插件也一样。Acrobat完成与所有插件的握手后,它调用每个插件的PluginExportHFTs回调函数。
在PluginExportHFTs函数中,一个插件可以导出一些HFTs,打算提供给其它插件使用。这个回调函数应该只导出HFT,不要调用其它Acrobat核心API方法。相关信息请参考第164页的“Working with Host Function Tables”。
注意:这是一个插件能导出HFT的唯一机会。
导入HFTs和为notifications注册:
Acrobat或Adobe Reader完整地调用每一个插件的PluginExportHFTs回调方法后,再调用每个插件的PluginImportReplaceAndRegister回调方法。在这个方法中,插件执行三个任务:
1.导入他们使用的一些制定的HFTs(标准的Acrobat HFTs自动导入)。插件也可以在此之后,在插件运行时的任意时刻导入HFTs。
2.通过调用AVAppRegisterNotification方法,为notifications注册。插件也可以在运行时注册或取消注册。插件可以在任何时候接收到一个notification,在它已完成注册以后,甚至插件的初始化回调函数还没有被调用时。这是可能发生的,如果另一个插件首先初始化并执行了一个操作,比如创建一个PDF文档,这会引起一个notification被发送。插件必须准备好正确地抓住notifications,只要为它们注册了。
3.重置任意的Acrobat API的可重置HFT方法。相关信息请参考第174页的“Replacing HFT methods”。
注意:这是一个插件可以导入HFT或重置标准API方法的唯一机会。插件可以在此时为notifications注册,或以后的任何时候。
初始化:
Acrobat或Adobe Reader完整地调用每个插件的PluginImportReplaceAndRegister回调方法之后,继续调用每个插件的PluginInit程序。插件可以使用他们的初始化程序用来钩住Acrobat的用户接口,增加菜单条目,工具条按钮,窗口,等等。也可以在插件运行时修改Acrobat的用户接口。
如果你的插件需要在所有的插件被初始化后规划一个任务,它应该为AVAppDidInitialize通知注册。这个通知会被调用,当Acrobat已经完成初始化并且将要进入它的事件循环时。
卸载:
插件的PluginUnload程序应该释放插件分配的所有内存,并移除所有它做的用户接口改动。Acrobat调用这个程序,当它终止运行或其它任何握手回调返回false时。这个功能应该执行下列任务:
(1)移除并释放所有的菜单条目和其它用户接口元素,HFTs,以及HFTServers。
(2)释放所有内存或所有其他分配的资源。
通常,插件只有在Acrobat退出时卸载。
一个插件的生命周期摘要:
下列步骤描述了一个插件的生命周期:
1. 启动时,Adobe Reader或Acrobat搜索它的插件目录,查找插件文件。
2. 对于每个插件文件,Adobe Reader或Acrobat都尝试载入这个文件。如果插件被成功载入,Adobe Reader或Acrobat调用PIMain.c中的函数,完成握手过程。
3. Adobe Reader或Acrobat按如下顺序调用回调函数:
●PluginExportHFTs
●PluginImportReplaceAndRegister
●PluginInit
这个顺序建立了关联,在插件和Adobe Reader或Acrobat之间,也在插件和任何其它插件之间。一旦所有的插件被载入,Adobe Reader或Acrobat继续它的自载入过程和启动用户接口。它把任何插件提供的工具添加到工具条和菜单条目上,然后启动用户对话。
第2节 使用回调函数
Acrobat或Adobe Reader调用你定义的用来完成指定任务的回调函数。例如,当用户点击一个
位于工具条上的按钮时,一个回调函数就被调用了。相关信息请参考第105页的“Creating toolbar button callback functions”。
要创建一个回调函数,你可以调用ASCallbackCreateProto, ASCallbackCreateReplacement, 和ASCallbackCreateNotification方法,把函数转换成回调函数并执行类型检查。这样允许编译器决定是否为一个指定的回调函数使用了正确的原型。关于这些方法的信息,参考文档《Acrobat and PDF Library API Reference》。
类型检查只在这时发生 --- 如果宏定义DEBUG被设置为1,当你的插件被编译时。确定它在你的开发环境中被适当地设置,并且在你建立插件的注册版本时移除它。
下述代码例子说明了创建一个回调函数的句法:
AVExecuteProc ExecProcPtr = NULL;
ExecProcPtr= ASCallbackCreateProto(AVExecuteProc, &ShowMessage);
宏定义ASCallbackCreateProto返回一个指定类型的回调函数,用来调用用户自定义的函数,函数的地址作为第二个参数被传递。在这个例子中,函数ShowMessage被转换为一个回调函数,ShowMessage是一个指定动作发生时被调用的用户自定义函数。
宏定义ASCallbackCreateProto返回一个指向函数的指针,此函数能被插件调用或被Acrobat,Adobe Reader调用。如果不再需要,使用ASCallbackDestroy方法除掉回调函数。
所有的回调函数必须用Pascal调用惯例声明。为了使你的代码在平台间通用,使用ACCB1 和 ACCB2 宏定义声明你的所有的回调函数:
static ACCB1 const char* ACCB2 ShowMessage(Thing*?foo);
第3节 Notifications
Acrobat core API 提供一个通知机制,以便插件能够与Acrobat或Adobe Reader同步动作。Notifications使插件能够指出自己对一个特定的时间感兴趣(比如一个注释被更改了),并且在每次事件发生的时候,提供一个程序给Acrobat调用。相关信息请参考第151页“Registering for Event Notifications”。
第4节 Handling events
你可以使用Acrobat core API操作不同类型的事件。
(1)鼠标点击:
(2)调整光标:
(3)键按下:
第5节 使用插件前缀
第6节 修改Acrobat或Adobe Reader用户接口
第7节 获取和注册对象
第8节 调试插件
第9节 页浏览层
第10节 最小化的屏幕重画
第11节 在PDF文件中存储私人数据
第12节 从PDF文档对象中导出数据
太累了,略。
先介绍一下Acrobat的定义:
The Acrobat menu bar --- 菜单条,Windows中的主菜单条。
An Acrobat menu --- 菜单,主菜单条的下一级,就是命令的列表,每个菜单还可以包含子菜单。
A menu command --- 菜单命令,菜单的末级,点击后执行相应命令。
第6章 创建菜单和菜单命令
第2节 给菜单增加菜单命令
你可以使用Acrobat core API给一个已存在的菜单增加一个新的菜单命令。例如,你可以添加一个在Acrobat SDK中已命名的菜单命令给Adobe Reader或Acrobat菜单条中的高级菜单条目。为了给一个已存在的菜单增加一个新菜单命令,请参照下面的方法:
1. 找到Adobe Reader或Acrobat菜单条(被AVmenubar对象描述),通过调用AVAppGetMenubar方法:
AVMenubar Themenubar = AVAppGetMenubar();
2. 找到将要包含新菜单命令的菜单,通过调用AVMenubarAcquireMenuByName方法,并传递下列参数:
(1)AVmenubar对象 --- 描述菜单条.
(2)menu的名字. 例如, 对于‘File’菜单, 指定‘File’。
本方法返回一个与被指定的菜单名相应的AVMenu对象。
AVMenu FileMenu = AVmenubarAcquireMenuByName (Themenubar, "File");
如果菜单不存在,你可以写程序创建它(见下一步)。
3. 如果有必要,调用AVMenuNew方法,写程序创建一个新菜单,调用时传递下列参数:
(1)一个字符串 --- 用来描述在Adobe Reader或Acrobat中被显示的文本。
(2)将要创建的一个唯一的菜单名字(语言独立)。相关信息请参考第31页的“使用插件前缀”;
(3)ASExtension --- 用于注册菜单。与ASExtension相关的信息,请参考文档Acrobat and PDF Library API Reference。
AVMenuNew方法返回一个AVMenu的入口。
AVMenu NewMenu = AVMenuNew ("Acrobat SDK", "ADBE:Acrobat_SDK", gExtensionID);
创建一个新菜单之后,你必须把它绑定到菜单条。相关信息请参考92页的“给一个新菜单增加菜单命令”。
4. 通过调用AVMenuItemNew方法创建一个新的菜单命令,并需要传递下列参数:
(1)一个描述命令文本的字符串。只有在Windows上,你才可以通过在标题中包含一个&字符指定一个键盘快捷方式。在Acrobat或Adobe Reader中,通过&(char),在字符下面放置一个下划线(_)。这样可以允许用户按alt+<char>去选择条目。
(2)将要创建的菜单命令的名字(语言独立)。相关信息请参考第31页的“使用插件前缀”。
(3)一个AVMenu对象 --- 描述一个子菜单, 这个菜单命令是它的父亲。如果这个菜单条目没有子菜单,传递null。
(4)一个布尔值。如果为true, 只有当用户选择了整个菜单后,菜单条目可见。如果为false,菜单条目既在全菜单下可见,也在快捷菜单模式下可见(Adobe Reader或Acrobat3.0以后版本已忽略)。
(5)关键字符 --- 用于菜单命令的快捷方式(一个ASCII字符)。如果没有快捷方式,使用NO_SHORTCUT。
(6)修正的关键字符 --- 如果可能,被用于快捷方式的一部分。必须是一个修正值的OR值,除了AV_COMMAND不能指定的以外。
(7)一个AVIcon对象 --- 用于描述在菜单命令中显示的图标,如果没有图标显示,指定为null。在Windows中,一个有效的图标是一个24x24像素的单色位图HBITMAP。在Mac OS中,图标是一个标准SICN资源的句柄。创建AVIcon对象的相关信息,请参考第100页的“创建工具条按钮”。
(8)ASExtension --- 用于注册这个菜单命令。与ASExtension相关的信息,请参考文档Acrobat and PDF Library API Reference。
AVMenuItemNew方法返回一个AVMenuItem对象:
AVMenuItem menuItem = AVMenuItemNew ("Show Message", "ADBE:ExternWin", NULL, true, NO_SHORTCUT, 0, NULL, gExtensionID);
5. 通过调用AVMenuAddMenuItem方法,绑定菜单命令到一个菜单,并传递下列参数:
(1)被菜单命令绑定的AVMenu对象。
(2)被绑定的AVMenuItem对象。
(3)菜单中的位置 --- 指定命令增加在哪里。你可以指定APPEND_MENUITEM,表示把菜单命令增加在菜单的末尾。
如果方法调用成功,菜单命令被增加到指定的菜单上:
AVMenuAddMenuItem (FileMenu, menuItem, APPEND_MENUITEM);
6. 发布类型定义入口到剩余内存中。要发布AVMenu入口,需要调用AVMenuRelease方法并传递AVMenu入口参数。要发布AVMenuItem入口,需要调用AVMenuItemRelease方法并传递AVMenuItemRelease入口参数。
增加一个菜单命令到一个已存在的菜单:
下面的代码示例创建一个新的menu command,用于显示文本Show Message, 并绑定到File菜单。
Example 6.1 Adding a menu command to an existing menu
//Declare menu variables
AVMenubar Themenubar = NULL;
AVMenu FileMenu = NULL;
AVMenuItem NewMenuCommand = NULL;
//Retrieve the menu bar in Adobe Reader or Acrobat
Themenubar = AVAppGetMenubar();
//Retrieve the File menu
FileMenu = AVMenubarAcquireMenuByName(Themenubar, "File");
//Create a new menu command
NewMenuCommand = AVMenuItemNew("Show Message", "ADBE:ExternWin", NULL, true, NO_SHORTCUT, 0, NULL, gExtensionID);
if (NewMenuCommand == NULL)
{
AVAlertNote ("Unable to create the menu command");
AVMenuItemRelease(NewMenuCommand);
return;
}
//Attach the new menu command to the File menu
AVMenuAddMenuItem (FileMenu, NewMenuCommand, APPEND_MENUITEM);
//Release the typedef instances
AVMenuItemRelease(NewMenuCommand);
AVMenuRelease(FileMenu);
注意:这个代码示例创建一个新的菜单命令,它在‘File’菜单中显示 Show Message。 无论如何,在菜单命令执行一个动作之前,你必须创建一个回调菜单函数。相关信息请参考第93页的“Creating menu callback functions”。
增加一个menu command到一个新菜单:
下面的代码示例创建一个新的menu command,用于显示文本Show Message, 并绑定到一个新菜单。通过调用AVmenubarAddMenu方法,这个新菜单被绑定到菜单条。
Example 6.2 Adding a menu command to a new menu
//Declare menu variables
AVMenubar Themenubar = NULL;
AVMenu NewMenu = NULL;
AVMenuItem NewMenuCommand = NULL;
//Retrieve the menu bar in Adobe Reader or Acrobat
Themenubar = AVAppGetMenubar();
//Create a new menu
NewMenu = AVMenuNew("New Menu", "ADBE:NewMenu", gExtensionID);
if (NewMenu == NULL)
{
AVAlertNote ("Unable to create the menu");
AVMenuRelease (NewMenu);
return ;
}
//Create a new menu command
NewMenuCommand = AVMenuItemNew("Show Message", "ADBE:ExternWin", NULL, true, NO_SHORTCUT, 0, NULL, gExtensionID);
if (NewMenuCommand == NULL)
{
AVAlertNote ("Unable to create the menu command");
AVMenuItemRelease(NewMenuCommand);
return;
}
//Attach the menu item to the menu and the menu to
//the menu bar
AVMenuAddMenuItem (NewMenu, NewMenuCommand, 0);
AVMenubarAddMenu (Themenubar, NewMenu, APPEND_MENU);
//Release the typedef instances
AVMenuItemRelease(NewMenuCommand);
AVMenuRelease(NewMenu);
注意: 如果你打算给一个菜单命令增加一个子菜单,你必须在创建菜单命令之前创建子菜单。
第3节 创建菜单回调函数
当创建菜单时,你必须创建被Acrobat或Adobe Reader调用菜单回调函数。三种类型的回调函数可以被创建:
Execute:被Acrobat或Adobe Reader调用,用于响应用户选择了菜单命令。这种回调是必须的。
Compute-enabled:这种可选的回调类型被Acrobat或Adobe Reader调用,当决定是否使菜单命令能够响应的时候。
Compute-marked:这种可选的回调类型被Acrobat或Adobe Reader调用,当决定菜单命令是否应该被检查的时候。
为了达到讨论的目的,介绍一个简单的用户自定义的函数 --- ShowMessage。这个方法通过调用AVAlertNote方法显示一个消息框。下面的代码示例显示了ShowMessage的函数体:
ACCB1 void ACCB2 ShowMessage (void* data)
{
AVAlertNote ("A menu command was selected.");
}
参数data(对这个或其它回调函数)可以被用于维持菜单命令的私有数据。注意这个用户自定义函数是使用宏定义ACCB1和ACCB2声明的。相关信息,请参考第30页的“Using callback functions”。
对于你创建的每个回调函数,你需要声明指针指向被Acrobat core API定义的回调函数:
AVExecuteProc ExecProcPtr = NULL;
AVComputeEnabledProc CompEnabledProcPtr = NULL;
AVComputeMarkedProc CompMarkedProcPtr = NULL;
AVExecuteProc是一个你可以创建的回调函数,当用户选择了一个菜单条目后被Acrobat或Adobe Reader调用。AVComputeEnabledProc也是一个你可以创建的回调函数,当决定是否使菜单命令处于使能状态时,由Acrobat或Adobe Reader调用。AVComputeMarkedProc也是一个你可以创建的回调函数,当决定菜单命令是否应该被检查时,由Acrobat或Adobe Reader调用。
创建一个指针后,比如一个指向AVExecuteProc的指针,你可以调用宏定义ASCallbackCreateProto(在Acrobat core API中定义)把一个用户自定义的函数转换为一个Acrobat回调函数。例如,你可以调用ASCallbackCreateProto把ShowMessage转换成一个回调函数。宏定义ASCallbackCreateProto需要下面两个参数:
(1)回调类型。例如,你可以传递AVExecuteProc。
(2)用户自定义函数的地址。
ASCallbackCreateProto返回指定类型的回调函数。例如下面的代码:
AVExecuteProc ExecProcPtr = NULL;
ExecProcPtr = ASCallbackCreateProto(AVExecuteProc, &ShowMessage);
创建AVExecuteProc回调函数后,调用AVMenuItemSetExecuteProc方法建立菜单命令和回调函数之间的联系。这是因为,当用户选择一个指定的菜单时,Acrobat或Adobe Reader会调用用户自定义函数,其地址就是被传递给宏定义ASCallbackCreateProto的地址(老外的废话怎么这么多!!!)。AVMenuItemSetExecuteProc方法需要如下参数:
●An AVMenuItem instance --- 描述菜单命令。
●An AVExecuteProc --- 描述回调函数。
●The address of a user-defined data structure --- 可以被传递给用户自定义函数的数据结构地址。
当你调用完菜单回调函数后,你可以调用ASCallbackDestroy方法释放消耗的内存。下面的代码示例给菜单命令创建了回调函数。
Example 6.3 Creating menu callback functions
/* Display a message box */
ACCB1 void ACCB2 ShowMessage (void* data)
{
AVAlertNote ("A menu command was selected.");
}
ACCB1 ASBool ACCB2 ComputeMarkedProc (void* data)
{
ASBool expressionorcondition = true;
if (expressionorcondition)
return true;
else return false;
}
ACCB1 ASBool ACCB2 ComputeEnabledProc (void* data)
{
if (AVAppGetNumDocs() > 0)
return true;
else return false;
}
ACCB1 ASBool ACCB2 PluginInit (void)
{
//Declare menu callbacks
AVExecuteProc ExecProcPtr = NULL;
AVComputeEnabledProc CompEnabledProcPtr = NULL;
AVComputeMarkedProc CompMarkedProcPtr = NULL;
//Declare menu variables
AVMenu FileMenu = NULL;
AVMenuItem NewItem = NULL;
//Retrieve the menu bar in Adobe Reader or Acrobat
AVMenubar Themenubar = AVAppGetMenubar ();
//Create menu callbacks
ExecProcPtr = ASCallbackCreateProto (AVExecuteProc, &ShowMessage);
CompEnabledProcPtr = ASCallbackCreateProto (AVComputeEnabledProc,
&ComputeEnabledProc);
CompMarkedProcPtr = ASCallbackCreateProto (AVComputeMarkedProc,
&ComputeMarkedProc);
//Retrieve the File menu
FileMenu = AVmenubarAcquireMenuByName (Themenubar, "File");
if (FileMenu)
{
//Create a new menu item
NewItem = AVMenuItemNew ("Show Message", "ADBE:ExternWin", NULL, true, NO_SHORTCUT, 0, NULL, gExtensionID);
if (NewItem == NULL)
{
AVAlertNote ("Unable to create a menu item, not loading.");
return false;
}
AVMenuItemSetExecuteProc (NewItem, ExecProcPtr, NULL);
AVMenuItemSetComputeEnabledProc (NewItem,
CompEnabledProcPtr,NULL);
AVMenuItemSetComputeMarkedProc (NewItem,
CompMarkedProcPtr,NULL);
AVMenuAddMenuItem (FileMenu, NewItem, 1);
AVMenuRelease (FileMenu);
return true;
}
else return false;
}
ACCB1 ASBool ACCB2 PluginUnload (void)
{
ASCallbackDestroy (ExecProcPtr);
ASCallbackDestroy (CompEnabledProcPtr);
ASCallbackDestroy (CompMarkedProcPtr);
return true;
}
提示:注意应用程序的逻辑,创建菜单命令是位于PluginInit函数之内的。相关信息请参考第27页“关于插件初始化”。
第4节 决定菜单条目是否可以被执行
在Adobe Reader或Acrobat以前的版本中,一个文档在应用程序视图中执行一个菜单条目是可能的,通过使用一个被命名的动作或JavaScript方法app.execMenuItem。这两个特点,提到就像ExecMenu暴露的,对文档的所有的菜单条目,潜在地允许一个恶意文档危及用户的隐私或系统。例如,使用JavaScript方法app.execMenuItem从文档得到数据是可能的,通过创建等同于用户的菜单命令,如全部选择、复制、粘贴等命令。
Acrobat和Adobe Reader 8.0 包含一个菜单列表,可以通过使用ExecMenu被执行。没有在这个列表上的菜单条目不能被可编程地执行。
通过调用AVMenuItemIsScriptable方法和传递AVMenuItem,你可以决定菜单条目是否可以被可编程地执行。这个方法返回一个布尔值。这是因为,如果与AVMenuItem参数相符的菜单条目可以被执行,返回true;否则,返回false。
《本章完》
第14章 文档安全
第1节 关于文档安全
在PDF文件中,加密由加密字典控制。Acrobat core API 使用RC4算法加密文档资料,一个标准的私有方法加解密,通过校验用户密码决定是否一个用户被授权打开文档。
PDF文件中的每个流对象或字符串对象是分别加密的。这个级别的加密提高了性能,因为对象可以按需要被分别解密,胜过整个文件解密。所有的对象,除了加密字典(包含了安全句柄的私有资料),使用RC4算法加密,ADOBE已从RSA公司得到授权。插件不可以替代其它的RC4加密方案。
用实现了一个安全句柄的插件来加密位于加密字典中的values是可靠的,并且它可使用任何加密方案。如果安全句柄没有加密位于加密字典中的values,values以明文表示。
core API提供两个Cos层的方法用于加解密使用RC4算法的数据。这些方法是CosEncryptData 和 CosDecryptData。关于这些方法的信息,请参考Acrobat and PDF Library API Reference。
安全句柄可以使用这些方法加密数据,当他们想要放入PDF文件的加密字典,以及从字典中读取并解密的时候。
安全句柄可以更换选择以忽略这些方法,而使用他们自己的加密算法。
注意:另外,Acrobat提供其它方法保护一个PDF文档,比如公钥安全与策略,由Adobe LiveCycle Policy Server创建。相关信息,请参考Acrobat and PDF Library API Reference。
第2节 关于安全句柄
表现用户认证和设置权限的应用逻辑被称作安全句柄。Acrobat有三个内置的安全句柄:password, APS (LiveCycle Policy Server) and public key security handler.相关信息,请参考Acrobat and PDF Library API Reference。
一个安全句柄支持两个密码:
用户密码 --- 允许用户打开和阅读一个受保护的文档,无论所有者选择了什么权限。
所有者密码 --- 允许文档的所有者改变授予用户的权限。
你可以使用Acrobat core API的内置安全句柄,或者写出你自己的安全句柄实现用户的授权过程(例如,利用已有的特定硬件密钥或文件,或者利用从磁卡阅读器中读取的方法)。
安全句柄对实现下面的任务过程是可靠的:
(1)在一个文件上设置权限
(2)访问文件授权
(3)设置文件的加密和解密密钥
(4)维护包含在PDF文件中的加密字典
安全句柄在下列情况下使用:
(1)文档被打开。安全句柄决定一个用户是否已被授权允许打开文件设置用于解密PDF文件的解密密钥。
(2)文档被保存。安全句柄设置加密密钥,并把额外的安全相关信息写入到PDF文件的加密字典里。
(3)用户企图改变文档的安全设置。
一个文档可以包含0个,1个或2个与之相关的安全句柄。如果文件没有使用安全机制,文档就有0个安全句柄。当安全性被应用到文件后,或者用户为一个受保护的文件选择了其它不同的安全句柄,新选择的安全句柄并没有被立即使用。替代的方案是新的安全句柄被简单地与文档相关联。在文档被保存之前它只是一个未定的安全句柄。
新的安全句柄并没有被立即使用是因为它被可靠地用于解密文档加密字典的内容,并且只有在文档被保存的时候,这个字典以正确的格式用新的安全句柄重新加密。结果是,一个文档可能具有一个当前安全句柄和一个新的安全句柄与之相关。
一个安全句柄有两个名字:一个被放置在每个PDF文件中---这个文件是被句柄保存的,例如ADBE_Crypt,另一个名字Acrobat可以使用在任何出现安全句柄的用户接口条目中,例如,Acrobat开发人员缺省的加密方法。这与菜单条目的使用的双名方案类似:一个语言独立的名字 --- 应用逻辑可以不管用户接口的语言而提到它,另一个名字出现在用户接口中。相关信息请参考第6章第2节“给菜单增加菜单命令”。
第1小节 添加安全句柄
你可以通过下面的步骤添加安全句柄:
(1)写一系列的回调函数,实现安全相关的功能。
(2)在PDCryptHandlerRec结构里指定回调函数。
(3)注册句柄,通过把结构传递给PDRegisterCryptHandlerEx。
安全句柄数据 --- 下面描述了三种安全句柄使用的数据类型:
(1)授权数据 --- 是安全句柄需要为特定文件决定用户授权级别的数据,例如未被授权地打开文件,被授权以用户的权限访问文件,被授权以所有者的权限访问文件。密码是一种通用的授权数据类型。
(2)安全数据 --- 是安全句柄使用的任何内部数据。它包括安全信息,内部标志的值,种子值,等等。
(3)安全信息 --- 是安全数据的子系列。很明确地,它是一个标志的集合 --- 这个标志包含Acrobat用来显示当前给用户的权限的信息。这个信息包括权限和用户的授权级别(用户或所有者)。
安全句柄回调函数 --- 安全句柄必须提供实现下列功能的回调函数:
(1)决定用户是否被授权打开特定的文件,和一旦文件被打开后用户有什么权限(PDCryptAuthorizeExProc)。
(2)创建和填充授权数据结构,使用用户接口必须包含的任何东西来获取数据。例如,显示一个对话框用于用户输入密码(PDCryptGetAuthDataExProc)。
(3)创建、填充和校验一个安全数据结构(PDCryptNewSecurityDataProc)。
(4)从安全数据结构展开安全信息(PDCryptGetSecurityInfoProc)。
(5)允许用户要求不同的安全设置,通常通过显示对话框的方式(PDCryptDisplaySecurityDataProc)。
(6)设置加密密钥,用于加密文件(PDCryptNewCryptDataProc)。
(7)填充或读取PDF文件的加密字典(PDCryptFillEncryptDictProc)。
(8)显示当前文档的权限(需要PDCryptAuthorizeExProc 和 PDCryptGetAuthDataExProc)。
在Acrobat 5.0及以后版本中,一个更好的权限粒度已经被预定义,为了被PDF文档支持的对象。插件可以调用PDDocPermRequest方法去要求一个特定的操作是否被授权,在一个文档中的指定对象上执行。
为了支持PDDocPermRequest方法,这里有两个新的回调方法:
PDCryptAuthorizeExProc 和 PDCryptGetAuthDataExProc。为了批操作(在一个或更多的文件上),Acrobat 5.0及以后版本也包括可选的安全操作。一个安全句柄必须提供许多的回调函数(PDCryptBatch...)给批处理过程。这些回调函数是PDCryptBatchHandler结构的一部分。PDCryptHandlerRec结构包含一个新成员CryptBatchHandler,它指向这个结构。
为了支持批处理过程,一个安全句柄应该提供一个非空的值给CryptBatchHandler,并且实现批回调函数。Acrobat 5.0以前,加密密钥的最大长度是40位。Acrobat 5.0及以后版本,提供128位的密钥长度。这些长度限制被强制地用来适用于出口约束。
Acrobat的授权程序
Acrobat的内置授权程序按如下方式工作:
1.
Acrobat invokes the security handler’s authorize callback (which is either PDCryptAuthorizeExProc, introduced with Acrobat 5.0, or the older PDCryptAuthorizeProc) to determine whether the user is allowed to open the file. It passes NULL authorization data, to handle the case where no authorization data is needed. Acrobat also passes the following values:
●PDPermReqObjDoc and PDPermReqOprOpen when invoking PDCryptAuthorizeExProc.
●pdPermOpen when calling PDCryptAuthorizeProc.
2.
If the authorize callback returns true, the file is opened. Otherwise, the authorization procedure executes the following steps up to three times, to give the user three chances to enter a password, or whatever authorization the security handler uses.
●It calls the security handler’s get authorization data callback (PDCryptGetAuthDataExProc or the older PDCryptGetAuthDataProc). This callback should obtain the authorization data using whatever user interface (for example, a dialog box used to obtain a password) or other means necessary, and then creates and fills the authorization data structure.
●It calls the security handler’s authorize callback, passing the authorization data returned by the get authorization data callback. If the authorization succeeds, the authorize callback returns the permissions granted to the user, and the authorization procedure returns.
The authorize callback can access the encrypted PDF document, allowing it to encrypt the authorization data using a mechanism that depends on the document’s contents. By doing this, someone who knows a document’s password cannot easily find out which other documents use the same password. The authorize callback can return permissions that depend on the password as well as the permissions specified when encryption was set up. This allows, for example, more rights to be granted to someone who knows a document’s owner password than to someone who knows the document’s user password.
第2小节 打开一个受保护的文件
第3小节 保存一个受保护的文件
第3节 为文档设置安全
<完>
大约12000字。
费时两个小时 + 四个小时 + 两个小时,太累了。以后翻不翻另说了。