Modifying XPS page on events | Java

Understanding the event-based approach in programming

The event-based approach in programming is a paradigm focused on events and their handling. In this model, the program’s flow is dictated by events, which can include user actions (such as mouse clicks or key presses), notifications generated by the system, or messages from other applications. Below are some key aspects of the event-based approach:

Overall, the event-based approach is an effective method for managing interactions and workflows within a program, making it especially suitable for applications that demand responsiveness and user interactions.

The XPS document conversion and events

When you need to modify 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 apply the changes.

Now, let’s say you need to implement repeating changes across all pages in an XPS file and subsequently convert the result to either PDF or an image format. Examples of such changes might include adding a watermark to the pages or inserting navigation hyperlinks. The straightforward method for making 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. Thus, the code to achieve this task would look as follows:

 1for (int i = 1; i <= document.getDocumentCount(); i++)
 2{
 3    document.selectActiveDocument(i);
 4    for (j = 1; j <= document.getPageCount(); j++)
 5    {
 6        document.selectActivePage(j);
 7        // Your changes ...
 8    }
 9}
10document.saveAsPdf("file-name.pdf", saveOptions);

If you also need to make some irregular changes before applying the repeating ones, this approach may lead to confusion or excessive traversals through documents and pages. Also, these loops may appear somewhat cumbersome.

When you convert an XPS document to PDF or an image, the process occurs one page at a time. As the conversion job prepares to process the next page, it triggers a “before-page” event. User can define the behavior (event handling) on such events by extending the BeforePageSavingEventHandler class, thereby leveraging some of the advantages discussed in the introductory section of this article.

In this section, we will present an example involving navigation hyperlinks. And to make the task a little more complicated, we will convert only a subset of all pages to PDF, as defined by the PdfSaveOptions.setPageNumbers() method.

The event handler class

Below is the extension of the BeforePageSavingEventHandler class:

 1/**
 2 * The class to handle the before-page event while converting an XPS document.
 3 */
 4public static class NavigationInjector extends BeforePageSavingEventHandler
 5{
 6    private final XpsFont _font;
 7    private List<Integer> _pageNumbers;
 8
 9    public NavigationInjector(XpsFont font, int[] pageNumbers)
10    {
11        _font = font;
12        if (pageNumbers == null || pageNumbers.length == 0)
13            return;
14
15        // Turn the page number array into a sorted collection of unique values.
16        SortedMap<Integer, Integer> map = new TreeMap<Integer, Integer>();
17        for (int pn : pageNumbers)
18            map.put(pn, 0);
19        _pageNumbers = new ArrayList<Integer>(map.keySet());
20    }
21
22    /**
23     * The action itself to be triggered on a before-page event.
24     * @param args The even arguments.
25     */
26    @Override
27    public void handle(BeforeSavingEventArgs<PageAPI> args)
28    {
29        PageAPI api = args.getElementAPI();
30
31        XpsGlyphs glyphs;
32        // For all pages in the output PDF except the first one...
33        if (args.getOutputPageNumber() > 1)
34        {
35          // ...insert a hyperlink to the first page...
36            glyphs = api.createGlyphs(_font, 15f, 5f, api.getHeight() - 10f, "[First]");
37            glyphs.setFill(api.createSolidColorBrush(Color.BLUE));
38            glyphs.setHyperlinkTarget(new XpsPageLinkTarget(_pageNumbers == null ? 1 : _pageNumbers.get(0)));
39            api.add(glyphs);
40
41            // ...and to the previous page.
42            glyphs = api.createGlyphs(_font, 15f, 60f, api.getHeight() - 10f, "[Prev]");
43            glyphs.setFill(api.createSolidColorBrush(Color.BLUE));
44            glyphs.setHyperlinkTarget(new XpsPageLinkTarget(
45                _pageNumbers == null ? args.getAbsolutePageNumber() - 1 : _pageNumbers.get(args.getOutputPageNumber() - 2)));
46            api.add(glyphs);
47        }
48
49        // For all pages in the output PDF except the last one...
50        if ((_pageNumbers != null && args.getOutputPageNumber() < _pageNumbers.size()) ||
51            (_pageNumbers == null && args.getOutputPageNumber() < api.getTotalPageCount()))
52        {
53          // ...insert a hyperlink to the next page...
54            glyphs = api.createGlyphs(_font, 15f, 110f, api.getHeight() - 10f, "[Next]");
55            glyphs.setFill(api.createSolidColorBrush(Color.BLUE));
56            glyphs.setHyperlinkTarget(new XpsPageLinkTarget(
57                _pageNumbers == null ? args.getAbsolutePageNumber() + 1 : _pageNumbers.get(args.getOutputPageNumber())));
58            api.add(glyphs);
59
60            // ...and to the last page.
61            glyphs = api.createGlyphs(_font, 15f, 160f, api.getHeight() - 10f, "[Last]");
62            glyphs.setFill(api.createSolidColorBrush(Color.BLUE));
63            glyphs.setHyperlinkTarget(new XpsPageLinkTarget(
64                _pageNumbers == null ? api.getTotalPageCount() : _pageNumbers.get(_pageNumbers.size() - 1)));
65            api.add(glyphs);
66        }
67
68        // Insert a page number in the bottom-right corner.
69        glyphs = api.createGlyphs(_font, 15f, api.getWidth() - 20f, api.getHeight() - 10f, Integer.toString(args.getOutputPageNumber()));
70        glyphs.setFill(api.createSolidColorBrush(Color.BLACK));
71        api.add(glyphs);
72
73        // Add an outline entry to display the links to the converted pages in the navigation pane of a PDF viewer.
74        api.addOutlineEntry(MessageFormat.format("Page {0}", args.getOutputPageNumber()), 1, args.getAbsolutePageNumber());
75    }
76}

The handler class needs to be aware of the pages we intend to save as PDF in order to establish the correct hyperlink targets. Consequently, the constructor should take the options’ getPageNumbers() array property as an argument. If the array of page numbers is provided, we create a sorted collection of them, excluding duplicates at the same time. Additionally, the class needs an XpsFont object that contains 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 if there is 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 simple. It analyses the getOutputPageNumber() event argument to omit some of the links where appropriate: the [First] and [Prev] links will not be added to the first page, while the [Next] and [Last] links will not appear on the last page. The logic is also designed to accommodate both scenarios, whether page numbers are specified or not.

Following the if blocks, there is code that adds a page number in the bottom-right corner of the page.

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

Code for conversion

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

 1// For complete examples and data files, please go to https://github.com/aspose-page/Aspose.Page-for-Java
 2// The path to the documents directory.
 3String dataDir = Utils.getDataDir();
 4String fontDir = dataDir + "necessary_fonts/";
 5// Open an XPS Document
 6final XpsDocument doc = new XpsDocument(dataDir + "Sample3.xps");
 7try {
 8  // Create a font
 9  final InputStream fontStream = new FileInputStream(fontDir + "arialbd.ttf");
10  try {
11    // Create options for conversion to PDF
12    PdfSaveOptions options = new PdfSaveOptions();
13    // Set the filter for the pages that need conversion
14    options.setPageNumbers(new int[] { 2, 6, 7, 13 });
15    // Add the event handler that will execute right before the conversion of each page
16    options.getBeforePageSavingEventHandlers().add(new NavigationInjector(doc.createFont(fontStream), options.getPageNumbers()));
17    // Save resultant XPS document
18    doc.saveAsPdf(dataDir + "ModifyPageOnConversion_out.pdf", options);
19  } finally {
20    if (fontStream != null)
21      fontStream.close();
22  }
23} finally {
24  if (doc != null)
25    doc.close();
26}

We open an XPS file and then create a stream object with the font data file. Next, we instantiate the PdfSaveOptions class and specify the page numbers we need to convert. The following line “connects” the “before-page” event handler to the conversion job via the getBeforePageSavingEventHandlers() collection.

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

Conclusion

In this article, we covered the fundamental aspects of the event-based approach in programming, examined the direct method for modifying pages of an XPS document, and explored a more advanced 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.

Subscribe to Aspose Product Updates

Get monthly newsletters & offers directly delivered to your mailbox.