Modificações baseadas em eventos de página XPS | C++
O que é a abordagem baseada em eventos na programação?
A abordagem baseada em eventos na programação é um paradigma que gira em torno do conceito de eventos e tratamento de eventos. Neste modelo, o fluxo do programa é determinado por eventos, que podem ser ações do utilizador (como cliques do rato ou pressionamentos de teclas), notificações geradas pelo sistema ou mensagens de outras aplicações. Eis alguns aspetos-chave da abordagem baseada em eventos:
Eventos: Os eventos representam ocorrências significativas num programa. Podem incluir interações do utilizador, alterações de dados ou mensagens de outras partes do sistema. Por exemplo, um clique num botão ou o carregamento de um ficheiro podem desencadear eventos.
Event Ouvintes: Para responder a eventos, os programadores utilizam event listeners (ou manipuladores). São funções ou métodos definidos para serem executados quando ocorre um evento específico. Por exemplo, um ouvinte de eventos pode ser configurado para executar uma função quando um utilizador clica num botão.
Execução Assíncrona: O modelo baseado em eventos suporta frequentemente a programação assíncrona, permitindo que os programas permaneçam responsivos enquanto aguardam a ocorrência de eventos. Por exemplo, uma aplicação web pode continuar a operar enquanto aguarda dados de um servidor.
Desacoplamento: A abordagem baseada em eventos promove o desacoplamento entre diferentes partes de um programa. Os componentes podem comunicar através de eventos sem a necessidade de conhecer os detalhes da implementação uns dos outros, tornando o código mais modular e fácil de manter.
Casos de Utilização Comuns: A programação orientada a eventos é amplamente utilizada em interfaces gráficas de utilizador (GUIs), aplicações web e sistemas que requerem interações em tempo real. Frameworks e bibliotecas como Node.js, React e muitas outras utilizam standards orientados a eventos.
Gestão do Estado: Num sistema orientado a eventos, a gestão do estado pode ser crucial, uma vez que a aplicação pode estar em diferentes estados dependendo das interações do utilizador ou dos eventos. Estratégias adequadas de gestão do estado são frequentemente empregues para garantir que a aplicação se comporta conforme o esperado.
No geral, a abordagem baseada em eventos é uma forma poderosa de gerir interações e fluxos de trabalho dentro de um programa, tornando-a particularmente eficaz para aplicações que exigem capacidade de resposta e interação do utilizador.
Eventos que ocorrem durante a conversão de documentos XPS
Quando necessita de efetuar alterações numa página específica de um documento XPS utilizando a API Aspose.Page, normalmente seleciona o documento ativo (se existirem vários documentos no ficheiro XPS), seleciona a página ativa e, em seguida, efetua as alterações.
Agora, suponha que precisa de fazer alterações repetidas em todas as páginas de um ficheiro XPS e, em seguida, converter o resultado para PDF ou um formato de imagem. Alguns exemplos destas alterações incluem a inserção de uma marca de água sobre as páginas ou a adição de hiperligações de navegação. A forma direta de fazer estas alterações implica percorrer os documentos no pacote XPS, percorrer as páginas do documento ativo atual e, finalmente, aplicar as alterações. Assim sendo, o código para realizar esta tarefa seria o seguinte:
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);
Se também precisar de fazer algumas alterações irregulares antes das repetidas, esta abordagem pode causar confusão ou percorrer documentos e páginas excessivamente, e estes loops podem parecer um pouco trabalhosos.
Ao converter um documento XPS em PDF ou imagem, o processo ocorre página a página. Quando a tarefa de conversão estiver pronta para processar a página seguinte, irá desencadear um evento “before-page”. O utilizador pode definir o tratamento destes eventos estendendo a classe BeforePageSavingEventHandler, aproveitando assim alguns benefícios descritos na secção introdutória deste artigo.
Exemplo de adição de hiperligações de navegação
Aqui, iremos fornecer um exemplo relacionado com o caso de hiperligações de navegação. E para complicar um pouco a tarefa, apenas converteremos um subconjunto de todas as páginas para PDF, conforme especificado pela propriedade PdfSaveOptions.PageNumbers
.
A classe manipuladora de eventos
Abaixo está a extensão da classe 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}
A classe manipuladora deve estar ciente das páginas que queremos guardar como PDF para criar os destinos de hiperligação corretos. Assim sendo, o construtor deve receber como argumento a propriedade array das opções. Se o array de números de página for especificado, criamos uma coleção ordenada dos mesmos, evitando duplicados ao mesmo tempo. (A propósito, esta solução não é totalmente precisa. Consegue descobrir o que pode causar inconsistência na saída?) Também precisaremos de um objeto XpsFont
contendo os dados da fonte para o texto da hiperligação.
O método Handle()
sobrescrito é onde tudo acontece. O argumento do método é um objeto que contém a API de modificação para a página atual, o número do documento dentro do pacote XPS, o número absoluto da página em todos os documentos, o número relativo da página dentro do documento atual (que é igual ao número anterior no caso de existir apenas um documento no pacote) e o número da página de saída (que é igual ao número absoluto da página quando convertemos o pacote inteiro).
A lógica dos dois blocos if
seguintes é bastante direta. Baseia-se na análise do argumento do evento OutputPageNumber
para omitir alguns links quando apropriado: os links [First]
e [Prev]
serão adicionados a todas as páginas de saída, exceto a primeira, enquanto os links [Next]
e [Last]
aparecerão em todas as páginas, exceto na última. A lógica também é adaptada para ambos os casos, independentemente de os números de página serem ou não especificados.
Após os blocos if
, existe um código para adicionar um número de página no canto inferior direito da página.
A última linha adiciona a entrada de estrutura de tópicos da página, o item que será apresentado no painel de navegação de um visualizador de PDF (se compatível).
O código de conversão
Agora que o manipulador de eventos “before-page” está definido, podemos escrever o código que converte o documento:
1 // ExStart:ModifyingXpsPageOnConversion
2 // The path to the documents directory.
3 System::String dataDir = RunExamples::GetDataDir_WorkingWithPages();
4 // Open an XPS document
5 {
6 System::SharedPtr<XpsDocument> doc = System::MakeObject<XpsDocument>(dataDir + u"Sample3.xps");
7 // Clearing resources under 'using' statement
8 System::Details::DisposeGuard<1> __dispose_guard_1({ doc});
9 // ------------------------------------------
10
11 try{
12 System::SharedPtr<System::IO::Stream> fontStream = System::IO::File::OpenRead(dataDir + u"arialbd.ttf");
13 // Clearing resources under 'using' statement
14 System::Details::DisposeGuard<1> __dispose_guard_0({ fontStream});
15 // ------------------------------------------
16
17 try
18 {
19 // Create options for conversion to PDF
20 System::SharedPtr<PdfSaveOptions> options = System::MakeObject<PdfSaveOptions>();
21 // Set the filter for the pages that need conversion
22 options->set_PageNumbers(System::MakeArray<int32_t>({2, 6, 7, 13}));
23 // Add the event handler that will execute right before the conversion of each page
24 options->get_BeforePageSavingEventHandlers()->Add(System::MakeObject<ModifyXpsPageOnConversion::NavigationInjector>(doc->CreateFont(fontStream), options->get_PageNumbers()));
25 // Save resultant XPS document
26 doc->SaveAsPdf(dataDir + u"ModifyPageOnConversion_out.pdf", options);
27 }
28 catch(...)
29 {
30 __dispose_guard_0.SetCurrentException(std::current_exception());
31 }
32 }
33 catch(...)
34 {
35 __dispose_guard_1.SetCurrentException(std::current_exception());
36 }
37 }
38 // ExEnd:ModifyingXpsPageOnConversion
Abrimos um ficheiro XPS e instanciamos um objeto de fluxo com o ficheiro de dados da fonte. De seguida, criamos uma instância da classe PdfSaveOptions
e especificamos o número de páginas que precisamos de converter. A linha seguinte é onde o manipulador de eventos “before-page” se “liga” à tarefa de conversão através da opção de coleção BeforePageSavingEventHandlers
.
Falta apenas executar a conversão para PDF utilizando o método SaveAsPdf()
do documento.
Conclusão
Neste artigo, exploramos os pontos-chave da abordagem baseada em eventos na programação, examinamos o método direto para modificar páginas de um documento XPS e aprendemos uma técnica mais avançada e sofisticada para fazer alterações repetidas em todas as páginas de saída durante o processo de conversão, utilizando a inserção de hiperligações de navegação como exemplo.
Para os exemplos completos, explore o nosso projeto de exemplo.