XPS page event-based modifications | C++

What is the event-based approach in programming?

The event-based approach in programming is a paradigm that revolves around the concept of events and event handling. In this model, the flow of the program is determined by events, which can be user actions (like mouse clicks or key presses), system-generated notifications, or messages from other applications. Here are some key aspects of the event-based approach:

Overall, the event-based approach is a powerful way to manage interactions and workflows within a program, making it particularly effective for applications that require responsiveness and user interaction.

Events that occur during the XPS document conversion

When you need to make changes to a specific page of an XPS document using the Aspose.Page API, you typically select the active document (if there are multiple documents in the XPS file), select the active page, and then make the changes themselves.

Now, suppose you need to make repeating changes to all pages in an XPS file and then convert the result to PDF or an image format. Some examples of such changes include placing a watermark over the pages or adding navigation hyperlinks. The direct way to make such changes involves traversing through the documents in the XPS package, traversing through the pages in the current active document, and then, finally, applying your changes. Therefore, the code to accomplish this task would look as follows:

 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);

If you also need to make some irregular changes before the repeating ones, this approach may lead to some confusion or excessive traversals through documents and pages, let alone these loops may seem a little cumbersome.

When you convert an XPS document to PDF or an image, the process occurs page by page. When the conversion job is ready to process the next page, it triggers a “before-page” event. The user can define the handling of such events by extending the BeforePageSavingEventHandler class, thereby taking advantage of some benefits outlined in the introductory section of this article.

Here, we will provide an example related to the case of navigation hyperlinks. And to make the task a little more complicated, we will convert only a subset of all pages to PDF, as specified by the PdfSaveOptions.PageNumbers property.

The event handler class

Below is the extension of the BeforePageSavingEventHandler class:

 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}

The handler class should be aware of the pages we want to save as PDF in order to create the correct hyperlink targets. Therefore, the constructor should take the options’ array property as an argument. If the array of page numbers is specified, we create a sorted collection of them, avoiding duplicates at the same time. (By the way, this solution is not entirely accurate. Can you figure out what might cause inconsistency in the output?) We will also need an XpsFont object containing the font data for the hyperlink text.

The overridden Handle() method is where it all happens. The method’s argument is an object that contains the modification API for the current page, the document number within the XPS package, the absolute page number across all documents, the relative page number within the current document (which is equal to the previous number in case of only one document in the package), and the output page number (which is equal to the absolute page number when we convert the entire package).

The logic of the following two if blocks is quite straightforward. It is based on the analysis of the OutputPageNumber event argument to omit some of the links where appropriate: the [First] and [Prev] links will be added to all output pages except the first one, while the [Next] and [Last] links will appear on all pages except the last one. The logic is also tailored for both cases, whether page numbers are specified or not.

After the if blocks, there is code for adding a page number in the bottom-right corner of the page.

The last line adds the page’s outline entry, the item that will be displayed in the navigation pane of a PDF viewer (if supported).

The conversion code

Now that the “before-page” event handler is defined, we can write the code that converts the document:

We open an XPS file and then instantiate a stream object with the font data file. Next, we create an instance of the PdfSaveOptions class and specify the numbers of pages we need to convert. The next line is where the “before-page” event handler becomes “connected” to the conversion job via the BeforePageSavingEventHandlers collection option.

All that’s left to do is run the conversion to PDF using the document’s SaveAsPdf() method.

Conclusion

In this article, we explored the key points of the event-based approach in programming, examined the direct method for modifying pages of an XPS document, and learned a more advanced and sophisticated technique for making repeating changes to all output pages during the conversion process, using the insertion of navigation hyperlinks as an example.

For the complete examples, explore our Example project.

Have any questions about Aspose.Page?



Subscribe to Aspose Product Updates

Get monthly newsletters & offers directly delivered to your mailbox.