PDF Generation Guidance

Hi there,

We are busy implementing PDF generation for an app using a cloud code task however our developer is struggling with a couple of items and needs some guidance:

1) Our developer is not sure how to correctly incorporate a Journey asset image into the header of a PDF

He has tried these paths below unsuccessfully and has found the Journey documentation a bit unclear regarding how to access Journey assets when using cloudcode.




2) Our developer is having trouble with imprinting the PDF document correctly to span multiple pages.

Please see the sample attached.

The PDF extends onto the second page without a border-header break, which needs to be resolved.

3) Our developer is having trouble with implementing dynamic inclusion/exclusion of rows onto the PDF depending on the presence of underlying data

The PDF is intended to end with 3 lines to support inclusion of 3 optional images. This should be dynamic. e.g. if all 3 images have been captured by the user, then 3 rows are added to the bottom of the PDF, but if no images have been captured then rows on the PDF for these images are not required and should be hidden.

Our developer is having some difficulty with implementing that. He has looked at the following links below that infer that this should be possible to do with Jquery. Does the journey platform support Jquery?

https://stackoverflow.com/questions/2326499/apply-css-styles-to-an-element-depending-on-its-child-elements
javascript - Hide table row if one of its columns is empty using CSS - Stack Overflow

Hi,

It is important to note that printing a PDF using the @journeyapps/pdf-reports package essentially takes provided HTML and prints it to PDF in the same manner that a browser would if the print function has been invoked.

  1. Our developer is not sure how to correctly incorporate a Journey asset image into the header of a PDF**

The app asset links mentioned here would not work since the provided HTML is not linked to the application. Any required resources need to either be embedded into the HTML via base64 encoding or referenced via external URLs for the “browser” to fetch.

CloudCode does not have access to the application code or assets folder. This unfortunately means the images would need to be duplicated and stored in the task folder or moved to a DB model attachment field.

Reading base64 from files
Upload the image files to the CloudCode task under a resources folder.
It is then possible to read the files using fs, convert to base64 and insert into the html using a template library such as Handlebars.

const fileData = 'data:image/png;base64, ' +  fs.readFileSync(__dirname + '/resources/nice_image.png').toString('base64');

data.images = {
    "nice_image": fileData
};

The Handlebars template would then reference the base64 in the data set:

<img  src="{{images.nice_image}}" />

Using DB attachments
Create a model for report resources

<model name="pdf_resource" label="PDF Resource">
    <field name="handle" label="Handle" type="text" />
    <field name="file" label="File" type="attachment" media="any" />
        
    <display>{handle}</display>
</model>

Create objects in the backend and upload the images to the file field.

In the CloudCode task you can fetch the urls from the attachments.

const imageAttachment = await DB.pdf_resource.first('handle = ?', 'nice_image');
data.images = {
    'nice_image': imageAttachment.file.url()
}

The Handlebars template would then reference the URL in the data set:

<img  src="{{images.nice_image}}" />
  1. Our developer is having trouble with imprinting the PDF document correctly to span multiple pages.

Printing html to “paper” pages in PDF format can be controlled using CSS features. A good guide is to reference this article. The section Page Breaks should relate to your question. There are many options to take when considering page breaks. The simplest solution would be to add a CSS class to the table in question:

.nice_table * {
 page-break-inside: avoid;
}
<table class="nice_table">
...etc

See the box-decoration-break property in regard to borders.

  1. Our developer is having trouble with implementing dynamic inclusion/exclusion of rows onto the PDF depending on the presence of underlying data

This can be solved using the Handlebars template library. Handlebars supports conditional rendering helper functions.

If your images are stored in the DB then you can add them to your data object

const images = await form.images.toArray();
data.images = images.map(image => {
   if (image.file.present()) {
       return {url: image.file.url()};
   } else {
     return {url: null}; 
  }
})

Then in the HTML you can include a matching number of rows from the images

<table class="nice_table">
   <tr>...</tr>
   <tr>...</tr>
   <tr>...</tr>
{{#each images}}
    {{#if url}}
     <tr><td><img src="{{url}}" /></td></tr>
    {{/if}}
{{/each}}
</table>
  1. Does the journey platform support Jquery?

The HTML provided to the printer service is rendered by a browser which does support scripting DOM manipulation. The JQuery library would have to be included via a script tag in the HTML. All DOM manipulation should be completed before the window load event is triggered - as the printer will print the DOM contents once the load event is fired.

A basic example of using JQuery is shown below

<html>
<head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script>
        window.addEventListener('DOMContentLoaded', event => {
            $('#target').html('Inserted from JQuery');
        })
    </script>
​</head>

​<body>
    <p id="target"></p>
​</body>

Please note that the use of Handlebars and CSS can often provide more eloquent reporting solutions compared to using JQuery.

As a side note:
Note that almost all the above advice (apart from the JQuery) is implemented by default or made easily possible when creating a PDF report task using the RocketPDF tool. This tool provides a quick setup for all your custom CSS (including printer classes by default), data collection and PDF development.

4 Likes

Hi @steven ,

I am having similar issue #2 Adding a margin on the second page after the table move to the next page. The footer looks fine.

  • css
    {
    table { page-break-after:auto }
    tr { page-break-inside:avoid; page-break-after:auto }
    td { page-break-inside:avoid; page-break-after:auto }
    thead { display:table-header-group }
    tfoot { display:table-footer-group }
    }

Hi @desiremuson,

If the PDF is being generated online, then you can set the margins on overflowing pages as a print option .

_generatedPdf = await pdf.generatePdf({
                html: _pdfHtml,
                print: {
                    printBackground: true,
                    marginTop: 1,
                    paperWidth: 8.25, // A4
                    paperHeight: 11.75, // A4      
                }
            });

The example above only uses the top margin since that is affected by overflowing pages. The original margin implementation depends on your current CSS setup. The normal approach is to have a padding applied to page html elements. The print margin is applied externally from CSS, so you should use a media query to cancel the padding on print.

CSS

@media print {
  page {
    padding-top: 0;
  }
}
1 Like