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:
Events: Events signify important occurrences within a program. These can encompass user interactions, data changes, or messages from different components of a system. For instance, a button click or the loading of a file can trigger events.
Event Listeners: To handle events, programmers employ event listeners (or handlers). These are functions or methods designated to run when a particular event occurs. For example, an event listener can be configured to execute a function when a user clicks a button.
Asynchronous Execution: The event-based model typically facilitates asynchronous programming, enabling programs to maintain responsiveness while awaiting events. For instance, a web application can keep functioning as it waits for data from a server.
Decoupling: The event-based approach encourages decoupling among various components of a program. These components can interact through events without requiring knowledge of each other’s implementation details, resulting in more modular and maintainable code.
Common Use Cases: Event-driven programming is commonly employed in graphical user interfaces (GUIs), web applications, and systems that necessitate real-time interactions. Frameworks and libraries such as Node.js, React, and many others leverage event-driven patterns.
State Management: In an event-driven system, effective state management is essential, as the application can exist in various states depending on user interactions or events. Appropriate state management strategies are typically implemented to ensure that the application functions as intended.
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.
Example of adding navigation hyperlinks
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.