Modifiche basate su eventi nelle pagine XPS | .NET
Cos’è l’approccio basato sugli eventi nella programmazione?
L’approccio basato sugli eventi nella programmazione è un paradigma che ruota attorno al concetto di eventi e alla loro gestione. In questo modello, il flusso del programma è determinato dagli eventi, che possono essere azioni dell’utente (come clic del mouse o pressione di tasti), notifiche generate dal sistema o messaggi provenienti da altre applicazioni. Ecco alcuni aspetti chiave dell’approccio basato sugli eventi:
Eventi: Gli eventi rappresentano eventi significativi in un programma. Possono includere interazioni dell’utente, modifiche ai dati o messaggi provenienti da altre parti del sistema. Ad esempio, il clic di un pulsante o il caricamento di un file possono attivare eventi.
Listener di eventi: Per rispondere agli eventi, i programmatori utilizzano i listener di eventi (o gestori). Si tratta di funzioni o metodi definiti per essere eseguiti al verificarsi di un evento specifico. Ad esempio, un listener di eventi può essere configurato per eseguire una funzione quando un utente fa clic su un pulsante.
Esecuzione asincrona: Il modello basato su eventi supporta spesso la programmazione asincrona, consentendo ai programmi di rimanere reattivi in attesa del verificarsi di eventi. Ad esempio, un’applicazione web può continuare a funzionare in attesa di dati da un server.
Disaccoppiamento: L’approccio basato su eventi promuove il disaccoppiamento tra le diverse parti di un programma. I componenti possono comunicare tramite eventi senza dover conoscere i dettagli dell’implementazione reciproca, rendendo il codice più modulare e più facile da manutenere.
Casi d’uso comuni: La programmazione basata su eventi è ampiamente utilizzata nelle interfacce utente grafiche (GUI), nelle applicazioni web e nei sistemi che richiedono interazioni in tempo reale. Framework e librerie come Node.js, React e molti altri utilizzano pattern basati su eventi.
Gestione dello stato: In un sistema basato su eventi, la gestione dello stato può essere cruciale poiché l’applicazione può trovarsi in stati diversi a seconda delle interazioni dell’utente o degli eventi. Spesso vengono impiegate strategie di gestione dello stato appropriate per garantire che l’applicazione si comporti come previsto.
Nel complesso, l’approccio basato sugli eventi è un modo potente per gestire interazioni e flussi di lavoro all’interno di un programma, rendendolo particolarmente efficace per le applicazioni che richiedono reattività e interazione con l’utente.
Eventi che si verificano durante la conversione di un documento XPS
Quando è necessario apportare modifiche a una pagina specifica di un documento XPS utilizzando l’API Aspose.Page, in genere si seleziona il documento attivo (se il file XPS contiene più documenti), si seleziona la pagina attiva e quindi si apportano le modifiche.
Ora, supponiamo di dover apportare modifiche ripetute a tutte le pagine di un file XPS e quindi convertire il risultato in PDF o in un formato immagine. Alcuni esempi di tali modifiche includono l’inserimento di una filigrana sulle pagine o l’aggiunta di collegamenti ipertestuali di navigazione. Il modo diretto per apportare tali modifiche consiste nell’attraversare i documenti nel pacchetto XPS, le pagine del documento attivo corrente e infine applicare le modifiche. Pertanto, il codice per eseguire questa operazione sarebbe il seguente:
1for (int i = 1; i <= document.DocumentCount; i++)
2{
3 document.SelectActiveDocument(i);
4 for (j = 1; j <= document.PageCount; j++)
5 {
6 document.SelectActivePage(j);
7 // Your changes ...
8 }
9}
10document.SaveAsPdf("file-name.pdf", saveOptions);
Se è necessario apportare modifiche irregolari prima di quelle ricorrenti, questo approccio potrebbe generare confusione o richiedere un’eccessiva esplorazione di documenti e pagine, per non parlare del fatto che questi cicli potrebbero apparire un po’ macchinosi.
Quando si converte un documento XPS in PDF o in un’immagine, il processo avviene pagina per pagina. Quando il processo di conversione è pronto per elaborare la pagina successiva, attiva un evento “before-page”. L’utente può definire la gestione di tali eventi estendendo la classe BeforePageSavingEventHandler, sfruttando così alcuni dei vantaggi descritti nella sezione introduttiva di questo articolo.
Esempio di aggiunta di collegamenti ipertestuali di navigazione
Qui forniremo un esempio relativo al caso dei collegamenti ipertestuali di navigazione. Per rendere il compito un po’ più complicato, convertiremo solo un sottoinsieme di tutte le pagine in PDF, come specificato dalla proprietà PdfSaveOptions.PageNumbers
.
La classe gestore eventi
Di seguito è riportata l’estensione della classe BeforePageSavingEventHandler
:
1// The class to handle the before-page event while converting an XPS document.
2class NavigationInjector : BeforePageSavingEventHandler
3{
4 // The font in which navigation hyperlinks and page numbers will be displayed.
5 private readonly XpsFont _font;
6 // The page numbers to convert.
7 private readonly SortedList<int, int> _pageNumbers;
8
9 public NavigationInjector(XpsFont font, int[] pageNumbers)
10 {
11 _font = font;
12 if (pageNumbers == null)
13 return;
14
15 // Turn the page number array into a sorted collection of unique values.
16 _pageNumbers = new SortedList<int, int>();
17 foreach (int pn in pageNumbers)
18 _pageNumbers[pn] = 0;
19 }
20
21 /// <summary>
22 /// The action itself to be triggered on a before-page event.
23 /// </summary>
24 /// <param name="args">The event arguments.</param>
25 public override void Handle(BeforeSavingEventArgs<PageAPI> args)
26 {
27 PageAPI api = args.ElementAPI;
28
29 XpsGlyphs glyphs;
30 // For all pages in the output PDF except the first one...
31 if (args.OutputPageNumber > 1)
32 {
33 // ...insert a hyperlink to the first page...
34 glyphs = api.CreateGlyphs(_font, 15f, 5f, api.Height - 10f, "[First]");
35 glyphs.Fill = api.CreateSolidColorBrush(Color.Blue);
36 glyphs.HyperlinkTarget = new XpsPageLinkTarget(_pageNumbers == null ? 1 : _pageNumbers.Keys[0]);
37 api.Add(glyphs);
38
39 // ...and to the previous page.
40 glyphs = api.CreateGlyphs(_font, 15f, 60f, api.Height - 10f, "[Prev]");
41 glyphs.Fill = api.CreateSolidColorBrush(Color.Blue);
42 glyphs.HyperlinkTarget = new XpsPageLinkTarget(
43 _pageNumbers == null ? args.AbsolutePageNumber - 1 : _pageNumbers.Keys[args.OutputPageNumber - 2]);
44 api.Add(glyphs);
45 }
46
47 // For all pages in the output PDF except the last one...
48 if ((_pageNumbers != null && args.OutputPageNumber < _pageNumbers.Count) ||
49 (_pageNumbers == null && args.OutputPageNumber < api.TotalPageCount))
50 {
51 // ...insert a hyperlink to the next page...
52 glyphs = api.CreateGlyphs(_font, 15f, 110f, api.Height - 10f, "[Next]");
53 glyphs.Fill = api.CreateSolidColorBrush(Color.Blue);
54 glyphs.HyperlinkTarget = new XpsPageLinkTarget(
55 _pageNumbers == null ? args.AbsolutePageNumber + 1 : _pageNumbers.Keys[args.OutputPageNumber]);
56 api.Add(glyphs);
57
58 // ...and to the last page.
59 glyphs = api.CreateGlyphs(_font, 15f, 160f, api.Height - 10f, "[Last]");
60 glyphs.Fill = api.CreateSolidColorBrush(Color.Blue);
61 glyphs.HyperlinkTarget = new XpsPageLinkTarget(
62 _pageNumbers == null ? api.TotalPageCount : _pageNumbers.Keys[_pageNumbers.Keys.Count - 1]);
63 api.Add(glyphs);
64 }
65
66 // Insert a page number in the bottom-right corner.
67 glyphs = api.CreateGlyphs(_font, 15f, api.Width - 20f, api.Height - 10f, args.OutputPageNumber.ToString());
68 glyphs.Fill = api.CreateSolidColorBrush(Color.Black);
69 api.Add(glyphs);
70
71 // Add an outline entry to display the links to the converted pages in the navigation pane of a PDF viewer.
72 api.AddOutlineEntry(string.Format("Page {0}", args.OutputPageNumber), 1, args.AbsolutePageNumber);
73 }
74}
La classe gestore deve essere a conoscenza delle pagine che desideriamo salvare in PDF per creare i target corretti per i collegamenti ipertestuali. Pertanto, il costruttore deve accettare la proprietà array delle opzioni come argomento. Se viene specificato l’array dei numeri di pagina, ne creiamo una raccolta ordinata, evitando allo stesso tempo i duplicati. (A proposito, questa soluzione non è del tutto accurata. Riesci a capire cosa potrebbe causare incongruenze nell’output?) Avremo anche bisogno di un oggetto XpsFont
contenente i dati del font per il testo del collegamento ipertestuale.
Il metodo Handle()
sovrascritto è dove avviene tutto. L’argomento del metodo è un oggetto che contiene l’API di modifica per la pagina corrente, il numero del documento all’interno del pacchetto XPS, il numero di pagina assoluto di tutti i documenti, il numero di pagina relativo all’interno del documento corrente (che è uguale al numero precedente nel caso di un solo documento nel pacchetto) e il numero di pagina di output (che è uguale al numero di pagina assoluto quando convertiamo l’intero pacchetto).
La logica dei due blocchi if
seguenti è piuttosto semplice. Si basa sull’analisi dell’argomento dell’evento OutputPageNumber
per omettere alcuni link ove appropriato: i link [First]
e [Prev]
verranno aggiunti a tutte le pagine di output tranne la prima, mentre i link [Next]
e [Last]
appariranno su tutte le pagine tranne l’ultima. La logica è inoltre adattata per entrambi i casi, indipendentemente dal fatto che i numeri di pagina siano specificati o meno.
Dopo i blocchi if
, è presente il codice per aggiungere un numero di pagina nell’angolo in basso a destra della pagina.
L’ultima riga aggiunge la voce di struttura della pagina, l’elemento che verrà visualizzato nel riquadro di navigazione di un visualizzatore PDF (se supportato).
Il codice di conversione
Ora che il gestore eventi “before-page” è definito, possiamo scrivere il codice che converte il documento:
1// Modify page on conversion event.
2
3// Open an XPS document
4using (XpsDocument doc = new XpsDocument(DataDir + "Sample3.xps"))
5// Create a font
6using (Stream fontStream = File.OpenRead(DataDir + "arialbd.ttf"))
7{
8 // Create options for conversion to PDF
9 PdfSaveOptions options = new PdfSaveOptions();
10 // Set the filter for the pages that need conversion
11 options.PageNumbers = new int[] { 2, 6, 7, 13 };
12 // Add the event handler that will execute right before the conversion of each page
13 options.BeforePageSavingEventHandlers.Add(new NavigationInjector(doc.CreateFont(fontStream), options.PageNumbers));
14 // Save resultant XPS document
15 doc.SaveAsPdf(OutputDir + "ModifyPageOnConversion_out.pdf", options);
16}
Apriamo un file XPS e quindi istanziamo un oggetto stream con il file di dati del font. Successivamente, creiamo un’istanza della classe PdfSaveOptions
e specifichiamo il numero di pagine da convertire. La riga successiva è quella in cui il gestore eventi “before-page” viene “connesso” al processo di conversione tramite l’opzione di raccolta BeforePageSavingEventHandlers
.
Non resta che eseguire la conversione in PDF utilizzando il metodo SaveAsPdf()
del documento.
Conclusione
In questo articolo abbiamo esplorato i punti chiave dell’approccio basato su eventi nella programmazione, esaminato il metodo diretto per modificare le pagine di un documento XPS e appreso una tecnica più avanzata e sofisticata per apportare modifiche ripetute a tutte le pagine di output durante il processo di conversione, utilizzando come esempio l’inserimento di collegamenti ipertestuali di navigazione.
Per gli esempi completi, esplora il nostro Progetto di esempio.