基于事件的 XPS 页面修改 | C++
什么是基于事件的编程方法?
基于事件的编程方法是一种围绕事件和事件处理概念的范式。在此模型中,程序的流程由事件决定,这些事件可以是用户操作(例如鼠标点击或键盘按下)、系统生成的通知或来自其他应用程序的消息。以下是基于事件的方法的一些关键方面:
事件:事件表示程序中发生的重大事件。这些事件可以包括用户交互、数据更改或来自系统其他部分的消息。例如,按钮点击或文件加载都可能触发事件。
事件监听器:为了响应事件,程序员使用事件监听器(或事件处理程序)。这些是定义为在特定事件发生时执行的函数或方法。例如,可以设置事件监听器以在用户点击按钮时执行某个函数。
异步执行:基于事件的模型通常支持异步编程,允许程序在等待事件发生时保持响应。例如,Web 应用程序可以在等待服务器数据的同时继续运行。
解耦:基于事件的方法促进了程序不同部分之间的解耦。组件可以通过事件进行通信,而无需了解彼此的实现细节,从而使代码更加模块化,更易于维护。
常见用例:事件驱动编程广泛应用于图形用户界面 (GUI)、Web 应用程序以及需要实时交互的系统。Node.js、React 等框架和库都采用了事件驱动模式。
状态管理:在事件驱动系统中,状态管理至关重要,因为应用程序可能会根据用户交互或事件处于不同的状态。通常采用适当的状态管理策略来确保应用程序按预期运行。
总而言之,基于事件的方法是管理程序内交互和工作流的有效方法,对于需要响应能力和用户交互的应用程序尤其有效。
XPS 文档转换期间发生的事件
当您需要使用 Aspose.Page API 更改 XPS 文档的特定页面时,通常需要选择活动文档(如果 XPS 文件中有多个文档),然后选择活动页面,然后自行进行更改。
现在,假设您需要对 XPS 文件中的所有页面进行重复更改,然后将结果转换为 PDF 或图像格式。此类更改的一些示例包括在页面上添加水印或添加导航超链接。进行此类更改的直接方法是遍历 XPS 包中的文档,遍历当前活动文档中的页面,最后应用更改。因此,完成此任务的代码如下所示:
1for (uint32_t i = 1; i <= document->DocumentCount(); i++)
2{
3 document->SelectActiveDocument(i);
4 for (uint32_t j = 1; j <= document->PageCount(); j++)
5 {
6 document->SelectActivePage(j);
7 // Your changes ...
8 }
9}
10document->SaveAsPdf(u"file-name.pdf", saveOptions);
如果您还需要在重复的更改之前进行一些不规则的更改,这种方法可能会导致一些混乱或过度遍历文档和页面,更不用说这些循环看起来有点繁琐。
将 XPS 文档转换为 PDF 或图像时,该过程会逐页进行。当转换作业准备好处理下一页时,它会触发“before-page”事件。用户可以通过扩展 BeforePageSavingEventHandler 类来定义此类事件的处理,从而利用本文简介部分概述的一些优势。
添加导航超链接示例
在这里,我们将提供一个与导航超链接相关的示例。为了使任务稍微复杂一些,我们将仅将所有页面的子集转换为 PDF,如 PdfSaveOptions.PageNumbers
属性所指定。
事件处理程序类
以下是 BeforePageSavingEventHandler
类的扩展:
1 /// <summary>
2 /// The class to handle the before-page event while converting an XPS document.
3 /// </summary>
4 class NavigationInjector : public Aspose::Page::XPS::Features::EventBasedModifications::BeforePageSavingEventHandler
5 {
6 typedef NavigationInjector ThisType;
7 typedef Aspose::Page::XPS::Features::EventBasedModifications::BeforePageSavingEventHandler BaseType;
8
9 typedef ::System::BaseTypesInfo<BaseType> ThisTypeBaseTypesInfo;
10 RTTI_INFO_DECL();
11
12 public:
13
14 NavigationInjector(System::SharedPtr<Aspose::Page::XPS::XpsModel::XpsFont> font, System::ArrayPtr<int32_t> pageNumbers);
15
16 /// <summary>
17 /// The action itself to be triggered on a before-page event.
18 /// </summary>
19 /// <param name="args">The event arguments.</param>
20 void Handle(System::SharedPtr<Aspose::Page::XPS::Features::EventBasedModifications::BeforeSavingEventArgs<System::SharedPtr<Aspose::Page::XPS::Features::EventBasedModifications::PageAPI>>> args) override;
21
22 private:
23
24 System::SharedPtr<Aspose::Page::XPS::XpsModel::XpsFont> _font;
25 System::SharedPtr<System::Collections::Generic::SortedList<int32_t, int32_t>> _pageNumbers;
26
27 };
1ModifyXpsPageOnConversion::NavigationInjector::NavigationInjector(System::SharedPtr<Aspose::Page::XPS::XpsModel::XpsFont> font, System::ArrayPtr<int32_t> pageNumbers)
2{
3 _font = font;
4 if (pageNumbers == nullptr)
5 {
6 return;
7 }
8
9 // Turn the page number array into a sorted collection of unique values.
10 _pageNumbers = System::MakeObject<System::Collections::Generic::SortedList<int32_t, int32_t>>();
11 for (int32_t pn : pageNumbers)
12 {
13 _pageNumbers->idx_set(pn, 0);
14 }
15
16}
17
18void ModifyXpsPageOnConversion::NavigationInjector::Handle(System::SharedPtr<Aspose::Page::XPS::Features::EventBasedModifications::BeforeSavingEventArgs<System::SharedPtr<Aspose::Page::XPS::Features::EventBasedModifications::PageAPI>>> args)
19{
20 System::SharedPtr<PageAPI> api = args->get_ElementAPI();
21
22 System::SharedPtr<XpsGlyphs> glyphs;
23 // For all pages in the output PDF except the first one...
24 if (args->get_OutputPageNumber() > 1)
25 {
26 // ...insert a hyperlink to the first page...
27 glyphs = api->CreateGlyphs(_font, 15.f, 5.f, api->get_Height() - 10.f, u"[First]");
28 glyphs->set_Fill(api->CreateSolidColorBrush(System::Drawing::Color::get_Blue()));
29 glyphs->set_HyperlinkTarget(System::MakeObject<XpsPageLinkTarget>(_pageNumbers == nullptr ? 1 : _pageNumbers->get_Keys()->idx_get(0)));
30 api->Add<System::SharedPtr<XpsGlyphs>>(glyphs);
31
32 // ...and to the previous page.
33 glyphs = api->CreateGlyphs(_font, 15.f, 60.f, api->get_Height() - 10.f, u"[Prev]");
34 glyphs->set_Fill(api->CreateSolidColorBrush(System::Drawing::Color::get_Blue()));
35 glyphs->set_HyperlinkTarget(System::MakeObject<XpsPageLinkTarget>(_pageNumbers == nullptr ? args->get_AbsolutePageNumber() - 1 : _pageNumbers->get_Keys()->idx_get(args->get_OutputPageNumber() - 2)));
36 api->Add<System::SharedPtr<XpsGlyphs>>(glyphs);
37 }
38
39 // For all pages in the output PDF except the last one...
40 if ((_pageNumbers != nullptr && args->get_OutputPageNumber() < _pageNumbers->get_Count()) || (_pageNumbers == nullptr && args->get_OutputPageNumber() < api->get_TotalPageCount()))
41 {
42 // ...insert a hyperlink to the next page...
43 glyphs = api->CreateGlyphs(_font, 15.f, 110.f, api->get_Height() - 10.f, u"[Next]");
44 glyphs->set_Fill(api->CreateSolidColorBrush(System::Drawing::Color::get_Blue()));
45 glyphs->set_HyperlinkTarget(System::MakeObject<XpsPageLinkTarget>(_pageNumbers == nullptr ? args->get_AbsolutePageNumber() + 1 : _pageNumbers->get_Keys()->idx_get(args->get_OutputPageNumber())));
46 api->Add<System::SharedPtr<XpsGlyphs>>(glyphs);
47
48 // ...and to the last page.
49 glyphs = api->CreateGlyphs(_font, 15.f, 160.f, api->get_Height() - 10.f, u"[Last]");
50 glyphs->set_Fill(api->CreateSolidColorBrush(System::Drawing::Color::get_Blue()));
51 glyphs->set_HyperlinkTarget(System::MakeObject<XpsPageLinkTarget>(_pageNumbers == nullptr ? api->get_TotalPageCount() : _pageNumbers->get_Keys()->idx_get(_pageNumbers->get_Keys()->get_Count() - 1)));
52 api->Add<System::SharedPtr<XpsGlyphs>>(glyphs);
53 }
54
55 // Insert a page number in the bottom-right corner.
56 glyphs = api->CreateGlyphs(_font, 15.f, api->get_Width() - 20.f, api->get_Height() - 10.f, System::Convert::ToString(args->get_OutputPageNumber()));
57 glyphs->set_Fill(api->CreateSolidColorBrush(System::Drawing::Color::get_Black()));
58 api->Add<System::SharedPtr<XpsGlyphs>>(glyphs);
59
60 // Add an outline entry to display the links to the converted pages in the navigation pane of a PDF viewer.
61 api->AddOutlineEntry(System::String::Format(u"Page {0}", args->get_OutputPageNumber()), 1, args->get_AbsolutePageNumber());
62}
为了创建正确的超链接目标,处理程序类应该知道我们想要保存为 PDF 的页面。因此,构造函数应该将 options 的数组属性作为参数。如果指定了页码数组,我们会创建一个已排序的页码集合,同时避免重复。(顺便说一句,这个解决方案并不完全准确。你能找出导致输出不一致的原因吗?)我们还需要一个 XpsFont
对象,其中包含超链接文本的字体数据。
所有操作都发生在重写的 Handle()
方法中。该方法的参数是一个对象,其中包含当前页面的修改 API、XPS 包中的文档编号、所有文档的 绝对页码、当前文档中的 相对页码(如果包中只有一个文档,则等于之前的页码)以及 输出页码(当我们转换整个包时,它等于绝对页码)。
以下两个 if
语句块的逻辑非常简单。它基于对 OutputPageNumber
事件参数的分析,在适当的情况下省略一些链接:[First]
和 [Prev]
链接将添加到除第一页之外的所有输出页面,而 [Next]
和 [Last]
链接将出现在除最后一页之外的所有页面上。无论是否指定页码,该逻辑都针对这两种情况进行了定制。
在 if
代码块之后,是用于在页面右下角添加页码的代码。
最后一行添加了页面的大纲条目,该条目将显示在 PDF 查看器的导航窗格中(如果支持)。
转换代码
现在“before-page”事件处理程序已经定义,我们可以编写转换文档的代码了:
我们打开一个 XPS 文件,然后使用字体数据文件实例化一个流对象。接下来,我们创建 PdfSaveOptions
类的实例,并指定需要转换的页数。下一行代码中,“before-page”事件处理程序通过 BeforePageSavingEventHandlers
集合选项“连接”到转换作业。
接下来只需使用文档的 SaveAsPdf()
方法运行 PDF 转换即可。
结论
在本文中,我们探讨了基于事件的编程方法的关键点,研究了直接修改 XPS 文档页面的方法,并学习了一种更高级、更复杂的技术,用于在转换过程中对所有输出页面进行重复更改,并以插入导航超链接为例。
如需查看完整示例,请探索我们的 示例项目。