Skip to content

OutSystems/UltimatePDF-ExternalLogic

Repository files navigation


Logo

Ultimate PDF

Generate PDF reports by using modern web technologies.

Table of Contents
  1. About The Project
  2. Getting Started
  3. Usage
  4. Advance Scenarios
  5. License
  6. Known Limitations
  7. Get in touch
  8. Contributing
  9. Testing
  10. Acknowledgments

About The Project

Project that enables ODC customers to generate PDFs using modern web technologies.

This component uses the same rendering engine as Chromium (an open-source version of Google Chrome) to transform web pages into PDF documents.

This component is based on the O11 version of Ultimate PDF.

THIS CODE IS NOT SUPPORTED BY OUTSYSTEMS.

(back to top)

Getting Started

This repo contains all the external code (C#) and the modules that can be used to accelerate the development of PDFs at ODC Applications.

Prerequisites

To generate the External Logic package run

.\generate_upload_package.ps1

Installation

This component is published at forge. So the best way to install it on your ODC tenant is by searching for:

  • UltimatePDF_ExternalLogic
  • Ultimate PDF
  • (Optional) Template_UltimatePDF

From the repo

The code will generate the file UltimatePDF_ExternalLogic.zip that can be uploaded to the ODC Portal as external logic (documentation).

Use ODC Studio to publish the modules

  • Ultimate PDF.oml - Library with accelerators to use the code from the External Logic actions
  • Template_UltimatePDF.oml - Application template. Use this template to create an application that is ready to use the Ultimate PDF component

(back to top)

Usage

The simplest way to generate a PDF is by:

  1. Create an empty screen
  2. Add to the screen the web block PrintLayout (from UltimatePDF)
  3. Build the report
  4. Call the server action PrintToPDF to generate the PDF (from UltimatePDF)

(back to top)

Public Elements

All the listed public elements are present in the library Ultimate PDF.

Block

  • PageCount: Displays the total number of pages. Use inside the header of footer of PrintLayout. See also: PrintLayout.
  • PageNumber: Displays the current page number. Use inside the header of footer of PrintLayout. See also: PrintLayout.
  • PageBreak: Force a page break.
  • SectionBreak: Breaks a section between two PrintLayout blocks, and allows to control how pagination continues on the new section.
  • PrintLayout: Creates a page layout, including header and footer placeholders that are repeated on every page, and a page background that can be used for watermarks and other full-bleed design elements. See also: ScreenToPDF.
  • ScreenToPDF: Automatically converts a screen into PDF, such that any navigation to the screen automatically downloads it as a PDF.
  • HideOnPrint: Content will not be shown on print media.
  • ShowOnPrint: Content will only be shown on print media.

Client actions

  • OnApplicationReady_UltimatePDF: Loads support for Ultimate PDF on reactive applications. This action must be invoked during the OnApplicationReady event. Some features of Ultimate PDF may not work otherwise.
  • ScreenToPDF_OnInitialize: Initializes a screen that is using ScreenToPDF block. This action must be invoked during the OnInitialize event of the screen. The ScreenToPDF block will not work otherwise.
  • CurrEnvironment: Current Environment information.
  • GetDefaultViewport: Defines a default viewport size of 1366x768

Server actions

  • PrintToPDF: Generates a PDF from a given URL, using the paper size and margin size from the print stylesheet.
  • PrintToPDF_Advanced: Generates a PDF from a given URL, specifying paper size and margin size.
  • ScreenshotToPNG: Generates a screenshot (PNG) from a given URL, using the paper size and margin size from the print stylesheet.
  • ScreenshotToPNG_Advanced: Generates a screenshot (PNG) from a given URL, specifying paper size and margin size.
  • GetDefaultViewport: Defines a default viewport size of 1366x768

Static Entities

  • MarginSize: Common document margin sizes.
  • PaperSize: Common paper sizes.

Advance Scenarios

Prerequisites

In the instructions bellow we will assume that the application that is generating the PDFs was created based on Template_UltimatePDF. This Template creates an application that contains:

  • REST API pdf - that implements an API to store the PDF on the application
  • Entity GeneratePDF - contains information for the pdf and the token authentication for the the pages
  • Entity GeneratedPDF_Files - entity where the REST API saves the PDF files
  • Entity GeneratedPDF_Logs - entity where the REST API saves the Log files

Advance PDF Generation

  1. Create a Flow named Print, if not present
  2. Add an empty screen
  3. Under the Authorization properties, select Everyone
  4. Add an input parameter named Token (Data Type = Text, Is Mandatory = Yes)
  5. Delete the web block Layouts\LayoutTopMenu
  6. Fill the screen with the information to have in the PDF
  7. Add On Initialize event, and add a call to IsPDFTokenValid with the Token as parameter
  8. Add a if clause IsPDFTokeValid.Valid, and end the False branch with and exception PDFTokenExpired
  9. Add On Ready event, and add a call to ExpireToken with the Token as parameter
  10. On another screen create a button to generate the PDF
  11. Call the Server Action GeneratePDFToken
  12. Call the Server Action PrintToPDF_Advanced
  • URL - url for the page to be generated. In this example, the screen was created at 2.
  • Environment - information of the environment where the browser will be launched. Can use the output of the Client Action CurrEnvironment
  • PaperSize - Paper size measures separated by x (eg: "21.00x29.70"). Can use the Static Entity PaperSize from UltimatePDF
  • MarginSize - Paper margin size separated by x (eg: "2.50x3.00x2.50x3.00"). Can use the Static Entity MarginSize from UltimatePDF
  • CollectLogs - If the execution of the external logic collects logs. If True, the output parameter LogsZipFile has the logs, it's empty otherwise.
  • Cookies - Cookie values to be used in the browser that will be launched.
  • TimeoutSeconds - Timeout in seconds the browser will wait to render and generate the PDF
  • RestCaller - REST API information for the external logic to store the PDF and the LogsZipFile.
  • PDF (Output parameter) - The PDF file binary data. Empty if RestCaller is passed.
  • LogsZipFile (Output parameter) - The logs of the external logic execution. Empty if RestCaller is passed.
  1. Call Download with the output parameter PDF of the Server Action PrintToPDF_Advanced

Screen to PDF

  1. At the Logic table add a System Event > On Application Ready
  2. Add a call to OnApplicationReady_UltimatePDF
  3. Create a Flow named Print, if not present
  4. Add an empty screen
  5. Under the Authorization properties, select Everyone
  6. Add an input parameter named Token (Data Type = Text, Is Mandatory = Yes)
  7. Delete the web block Layouts\LayoutTopMenu
  8. Add the web block PrintLayout\ScreenToPDF
  9. Fill the screen with the information to have in the PDF
  10. Add On Initialize event, and add a call to IsPDFTokenValid with the Token as parameter
  11. Add a if clause IsPDFTokeValid.Valid, and end the False branch with and exception PDFTokenExpired
  12. Add a call to ScreenToPDF_OnInitialize
  13. Add On Ready event, and add a call to ExpireToken with the Token as parameter
  14. On another screen create a link with an action on click
  15. Call the Server Action GeneratePDFToken
  16. End the flow with a destination to the screen created at 2.

External Logic call Rest API to store the PDF

The Template_UltimatePDF already creates a REST API named pdf with two methods Store and StoreLogs. The external logic expects the REST API to be implemented as POST methods with binary data as the body of the request. The API call uses the Token parameter as an authorization header.

  1. Create a Flow named Print, if not present
  2. Add an empty screen
  3. Under the Authorization properties, select Everyone
  4. Add an input parameter named Token (Data Type = Text, Is Mandatory = Yes)
  5. Delete the web block Layouts\LayoutTopMenu
  6. Fill the screen with the information to have in the PDF
  7. Add On Initialize event, and add a call to IsPDFTokenValid with the Token as parameter
  8. Add a if clause IsPDFTokeValid.Valid, and end the False branch with and exception PDFTokenExpired
  9. On another screen create a button to generate the PDF
  10. Call the Server Action GeneratePDFToken
  11. Call the Server Action PrintToPDF_Advanced_ToRest, fill the RestCaller parameter
  • Token - The token of the printable page, we use it for REST API authentication.
  • BaseUrl - base tenant url, eg: https://tenant.outsystems.com
  • Module - Name of the module that implements the REST API, can use GetOwnerURLPath()
  • StorePath - Rest method URL Path to store the PDF, eg: /rest/pdf/Store
  • LogPath - Rest method URL Path to store the logs, eg: /rest/pdf/StoreLogs
  • The PDF will be stored at the entity GeneratedPDF_Files
  • The Logs will be stored at the entity GeneratedPDF_Logs, if requested

External Logic call S3 Bucket to store the PDF

More information on S3 buckets and presigned urls can be found in official AWS documentation.

  1. Create a Flow named Print, if not present
  2. Add an empty screen
  3. Under the Authorization properties, select Everyone
  4. Add an input parameter named Token (Data Type = Text, Is Mandatory = Yes)
  5. Delete the web block Layouts\LayoutTopMenu
  6. Fill the screen with the information to have in the PDF
  7. Add On Initialize event, and add a call to IsPDFTokenValid with the Token as parameter
  8. Add a if clause IsPDFTokeValid.Valid, and end the False branch with and exception PDFTokenExpired
  9. On another screen create a button to generate the PDF
  10. Call the Server Action GeneratePDFToken
  11. Call the Server Action PrintToPDF_Advanced_ToS3, fill the S3Endpoints parameter
  • PdfPreSignedUrl - Presigned url to a S3 Bucket to store the resulting PDF
  • LogsPreSignedUrl - Presigned url to a S3 Bucket to store the resulting logs

Basic page screenshot

  1. Create an empty screen
  2. Add to the screen the web block PrintLayout (from UltimatePDF)
  3. Build the report
  4. Call the server action ScreenshotToPNG to generate the image (from UltimatePDF)

Add Fonts to the Report

The library uses Google Fonts to download the fonts for the PDF generation. Fonts added using these methods need to be available in the Google Fonts.

  1. At the report page on the OnInitialize action add SetDocumentFont
  2. If the font you need is not present on the static entity Fonts, call the AddFontFamilyToDocument to the add the custom font.

License

BSD-3 license. See LICENSE for more information.

(back to top)

Known Limitations

  • Ultimate PDF doesn’t support the IP Filtering feature. Enabling this feature in a stage will result in the reports returning a 403 error page. According to OutSystems documentation, the external libraries run on “an external service” outside the realm of the tenant applications. Because of this, Ultimate PDF fails to call the report page to render the PDF.
  • The screens to print cannot be protected by authentication. We recommend the screens to be protected by tokens. See the usage of GeneratePDFToken on this documentation for examples.
  • The input and output payload of the external logic cannot be greater than 5.5MB. Workaround use the REST API Store functionality.
  • The version of chromium bundle with the forge component only has Open Sans font installed meaning it only supports a subset of languages. As a workaround the customer can add the needed fonts as css (https://developers.google.com/fonts/docs/getting_started).
  • ODC has a time limit for running external logic (custom code). The current configuration is 95 seconds (documentation). Any call to this library that takes more than 95 seconds will fail with a timeout. If you are getting a 95 seconds timeout please consider: 1) Reviewing the report (screen) logic to try and improve its performance; 2) using a different library that does not rely on rendering the page on demand to generate the PDF.
    • To check if you are affected by this check the Traces to see if you have a message like "OS-BERT-ELR-61302 - Running this action's custom code has exceeded the timeout limit of 95 seconds.".
  • The first execution of the UltimatePDF external logic normally is slower than 10 seconds. This results in the request timing out. The main reason for this slowness is the fact that we need to prepare a chromium browser to run the requests, and then the render of the page. In an interval of 10 to 15 minutes this penalty is reduced since the external logic infrastructure is reused. The best workaround for this limitation is to set a Server Request Timeout greater than 10 seconds.
    • To check if you are affected by this check the Traces to see if you have a timeout.

(back to top)

Get in touch

Help us improve UltimatePDF-ExternalLogic by either:

(back to top)

Contributing

  1. Do a repository Fork;
  2. Create a branch based in the branch master (latest & greatest release);
  3. Open the branch with you favorite C# code editor;
  4. Make your code change;
  5. Document your code;
  6. Install the external logic in your tenant and test;
  7. Kept the branch updated with the master branch and also synchronized with the upstream master;
  8. Create a PR, describing what was the (mis)behavior, what you changed and please provide a sample;
  9. Address any feedback in code review.

(back to top)

Testing

At the oml folder, there is an application named Ultimate PDF Tests that contains multiple examples and tests of the component. All the pull requests will be tested against the test application scenarios. Use this application to test your changes before sending the PR.

(back to top)

Acknowledgments

Project base on OutSystems Forge component Ultimate PDF.

(back to top)