Table of Contents
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.
This repo contains all the external code (C#) and the modules that can be used to accelerate the development of PDFs at ODC Applications.
To generate the External Logic package run
.\generate_upload_package.ps1
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
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
The simplest way to generate a PDF is by:
- Create an empty screen
- Add to the screen the web block
PrintLayout
(from UltimatePDF) - Build the report
- Call the server action
PrintToPDF
to generate the PDF (from UltimatePDF)
All the listed public elements are present in the library Ultimate PDF.
- 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.
- 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
- 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
- MarginSize: Common document margin sizes.
- PaperSize: Common paper sizes.
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
- Create a Flow named Print, if not present
- Add an empty screen
- Under the Authorization properties, select
Everyone
- Add an input parameter named
Token
(Data Type = Text, Is Mandatory = Yes) - Delete the web block
Layouts\LayoutTopMenu
- Fill the screen with the information to have in the PDF
- Add
On Initialize
event, and add a call to IsPDFTokenValid with theToken
as parameter - Add a if clause
IsPDFTokeValid.Valid
, and end the False branch with and exceptionPDFTokenExpired
- Add
On Ready
event, and add a call toExpireToken
with theToken
as parameter - On another screen create a button to generate the PDF
- Call the Server Action
GeneratePDFToken
- 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
fromUltimatePDF
- MarginSize - Paper margin size separated by x (eg: "2.50x3.00x2.50x3.00"). Can use the Static Entity
MarginSize
fromUltimatePDF
- 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.
- Call Download with the output parameter PDF of the Server Action
PrintToPDF_Advanced
- At the Logic table add a System Event > On Application Ready
- Add a call to
OnApplicationReady_UltimatePDF
- Create a Flow named Print, if not present
- Add an empty screen
- Under the Authorization properties, select
Everyone
- Add an input parameter named
Token
(Data Type = Text, Is Mandatory = Yes) - Delete the web block
Layouts\LayoutTopMenu
- Add the web block
PrintLayout\ScreenToPDF
- Fill the screen with the information to have in the PDF
- Add
On Initialize
event, and add a call toIsPDFTokenValid
with theToken
as parameter - Add a if clause
IsPDFTokeValid.Valid
, and end the False branch with and exceptionPDFTokenExpired
- Add a call to
ScreenToPDF_OnInitialize
- Add
On Ready
event, and add a call toExpireToken
with theToken
as parameter - On another screen create a link with an action on click
- Call the Server Action
GeneratePDFToken
- End the flow with a destination to the screen created at 2.
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.
- Create a Flow named Print, if not present
- Add an empty screen
- Under the Authorization properties, select
Everyone
- Add an input parameter named
Token
(Data Type = Text, Is Mandatory = Yes) - Delete the web block
Layouts\LayoutTopMenu
- Fill the screen with the information to have in the PDF
- Add
On Initialize
event, and add a call to IsPDFTokenValid with theToken
as parameter - Add a if clause
IsPDFTokeValid.Valid
, and end the False branch with and exceptionPDFTokenExpired
- On another screen create a button to generate the PDF
- Call the Server Action
GeneratePDFToken
- 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
More information on S3 buckets and presigned urls can be found in official AWS documentation.
- Create a Flow named Print, if not present
- Add an empty screen
- Under the Authorization properties, select
Everyone
- Add an input parameter named
Token
(Data Type = Text, Is Mandatory = Yes) - Delete the web block
Layouts\LayoutTopMenu
- Fill the screen with the information to have in the PDF
- Add
On Initialize
event, and add a call to IsPDFTokenValid with theToken
as parameter - Add a if clause
IsPDFTokeValid.Valid
, and end the False branch with and exceptionPDFTokenExpired
- On another screen create a button to generate the PDF
- Call the Server Action
GeneratePDFToken
- 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
- Create an empty screen
- Add to the screen the web block
PrintLayout
(from UltimatePDF) - Build the report
- Call the server action
ScreenshotToPNG
to generate the image (from UltimatePDF)
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.
- At the report page on the OnInitialize action add
SetDocumentFont
- If the font you need is not present on the static entity
Fonts
, call theAddFontFamilyToDocument
to the add the custom font.
BSD-3 license. See LICENSE for more information.
- 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.
Help us improve UltimatePDF-ExternalLogic
by either:
- Submitting an issue with detailed information about the problem you're having
- Sending us an email with any feedback or questions that you may have
- Do a repository Fork;
- Create a branch based in the branch master (latest & greatest release);
- Open the branch with you favorite C# code editor;
- Make your code change;
- Document your code;
- Install the external logic in your tenant and test;
- Kept the branch updated with the master branch and also synchronized with the upstream master;
- Create a PR, describing what was the (mis)behavior, what you changed and please provide a sample;
- Address any feedback in code review.
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.
Project base on OutSystems Forge component Ultimate PDF.