Generating Stunning PDF Reports With AWS and Python | by Alexandre Bruffa | Apr, 2022

Construct a complete PDF era system with Amazon Net Providers and Python

Illustration by Gianca Chavest

Some years in the past, I participated in a challenge supposed for small firms and entrepreneurs. It was a typical SaaS app with a consumer register and login mechanism; customers might digit merchandise, orders, and gross sales of the corporate on the platform. Then, they might visualize lots of superior charts and projections, and obtain them as PDF reviews.

The entire challenge was very standard from a technical viewpoint however the report era half was rattling attention-grabbing, I’ll share it on this article.

  • Throughout this time, I used to be working with a scalable serverless structure on Amazon, so I wanted to make the PDF era work on Lambda.
  • I had developed the entire enterprise core (calculations, projections, and so on. ) utilizing Python, so I used to be on the lookout for a Pythonic answer so far as attainable.
  • The PDF reviews needed to be just like or with the identical visible identification because the content material proven on the platform: kinds, colours, fonts, and so on.

I first discovered utilizing a PDF era library like HTML2PDF or FPDF for instance. However years in the past, these libraries had been fairly restricted: the generated PDFs had been visually poor, and the charts had been sort of ugly.

Then I questioned if I might construct an HTML template with all of the great visible issues I want, “take an image” of it, and “paste” it right into a PDF. Excellent news: that is attainable with the library Puppeteer and its Python equal Pyppeteer.

Following this concept, we are going to do the next: run a browser occasion on Lambda, go to the HTML template, populate it with consumer information, generate a PDF file, and preserve it in an S3 bucket.

This would be the closing structure of our app:

This text will give attention to the Lambda half and the PDF era; I’ll assume that we have already got the consumer information. If you wish to make an excellent integration with Cognito, API Gateway + Authorizer, and an RDS database, I invite you to learn my earlier article:

Constructing the template

First, we have to generate a great-looking HTML template. Since I’m not a designer nor a frontend developer, a superb different for me is shopping for an HTML template like a budget and exquisite Sash Template.

After some HTML, CSS, and Javascript hacks, I obtained this great dummy template with out consumer information:

HTML template with out consumer information

Tip 1: Attempt to construct a template the lighter you may, positioned on a quick internet hosting, with out massive photos or massive information to load. Additionally, the PDF file might be created after the load event is named, so keep away from asynchronous loading and cache sources.

Within the Chrome console, asynchronous loading and cache sources are proven after the purple line:

Chrome console

Tip 2: Every web page of the PDF report may be represented by a container with a set dimension. For instance, if the PDF report can have an A4 format, the container can have the next model:

Tip 3: The PDF information that we’ll create could have a coloration distortion difficulty. On this case, it’s essential to power correct colours utilizing the print-color-adjust property.

Tip 4: Keep away from chart animations. Every chart library has its technique to do it, right here is how it may be completed with the Chart.js library:

Populating the template with information

Nicely, let’s now populate our template with information. There are lots of methods to do it.

➡️ The question string technique

A straightforward technique to do it’s by sending the info by means of the URL as a query string. I selected to ship a giant JSON parameter named information and obtain it due to the URLSearchParams interface. Then the template can simply be populated with information due to native Javascript or JQuery if the template helps it.

It appears to be like great!

HTML template with consumer information

Be aware: Net browsers restrict URLs to a most size (2MB for Chrome). If you want to ship extra information, I extremely advocate you employ the localStorage technique.

➡️ The localStorage technique

localStorage permits storing information within the net browser session. We’re going to learn the info as follows:

S3

S3 is a scalable storage service for static content material, we are going to retailer there the PDF information generated by Lambda.

➡️ Bucket

We create a brand new bucket (aka repository).

➡️ Bucket coverage

We edit the bucket coverage to permit bucket content material solely to be learn.

The Lambda Layer

Lambda is a superb serverless service that enables operating code letting Amazon handle server sources for you. However earlier than writing the lambda perform, we have to create a Lambda Layer with all the mandatory sources to make the PDF era work.

➡️ The browser

We are going to use Chrome, which is the worldwide most used browser. There is no such thing as a official model of Chrome for AWS Lambda, however hopefully, there are some heroes on this world like Marco Lüthy who gave us -mere mortals- a beautiful current: a Headless Chromium model for AWS Lambda.

We obtain a steady launch here and we unzip it.

➡️ Pyppeteer

That is the spicy half: we have to do some hacks to make Pyppeteer work on Lambda. We obtain the final accessible model of Pyppeteer on Pypi and we open the __init__.py file.

For an unknown motive, the library model makes Pyppeteer crash on Lambda, so we remark the entire corresponding block and exchange it with a hardcoded model quantity:

The one writable listing on a lambda perform is the tmp directory, so we modify it by hardcoding the house listing of Pyppeteer:

Be aware: we might use the userDataDir choice of the Pyppeteer launcher to set the best listing, however doing this manner we take away the AppDirs dependency and different embedded dependencies.

➡️ Dependencies

Pyppeteer has the next dependencies: pyee, tqdm, and websockets. We obtain these libraries and place them in the identical folder because the headless chromium and Pyppeteer.

➡️ Zip file

The configuration we’re constructing solely works with a Python model ≤ 3.7, so our Layer and performance might be Python 3.7 suitable.

We place the headless chromium and the libraries in a listing containing the next subdirectories: lib > python3.7 > site-packages and we lastly zip it.

➡️ Importing to the Layer

Nice! We lastly can create a brand new Lambda layer: we add the zip we’ve got created beforehand and we choose Python 3.7 as suitable runtime.

Our layer is prepared!!

The Lambda perform

➡️ Creating the perform

We create a brand new Lambda perform with Python 3.7 as runtime.

➡️ Including the Pyppeteer layer to the perform

Earlier than coding, we’ve got so as to add the layer we’ve got created beforehand to the perform:

➡️ Coding the perform 🚀

Now essentially the most thrilling half, let’s code the perform!

Notes:

  • We assume that we have already got the information and user_id values. You may test my previous article if you wish to make an excellent integration with different AWS providers.
  • We create a singular PDF file identify, concatenation of consumer id, and precise date.
  • We use the quote perform of the urllib library to encode the info after which may be a part of the URL.
  • We use the asyncio library as specified within the Pyppeteer documentation.
  • Lambda saved the information we uploaded within the /decide listing, so we launch the Headless Chromium calling /decide/python/lib/python3.7/site-packages/headless-chromium due to the executablePath choice of the launch perform.
  • We use the goto perform to go to the HTML template and the pdf perform to generate the PDF content material.
  • We use the put_object perform of the boto3 library to retailer our PDF file within the S3 bucket we’ve got created.

➡️ Perform suggestions

I allow you to some great suggestions you should utilize within the asynchronous perform:

  • If you wish to retrieve the message displayed by the browser console, use the console event and the ConsoleMessage class,
  • If you happen to see some pixelated content material in your generated PDFs, use the deviceScaleFactor parameter of the setViewport function. The default worth is 1.0.
  • If you wish to use the localStorage technique, go to the web page, use the evaluate function to insert the info worth in localStorage and go to the web page once more.
  • By default, the PDF file generated has a letter format. You may change due to the format choice.

➡️ Perform position

We go to the position related to the perform and we add the AmazonS3FullAccess coverage in order that our perform can write the PDF file generated in a bucket.

➡️ Perform settings

Our Lambda perform will run a Chromium occasion so it might take just a few seconds and use consequent reminiscence.

I discovered that with 2048Mb of reminiscence allotted, a PDF file is generated and written in a bucket in roughly 3.5 seconds, so we modify the perform timeout worth to five seconds.

This is an example of a generated PDF from the template hosted on https://alexandrebruffa.com/pdfreport/. It has all we’d like: consumer information on gorgeous charts, customized fonts, colours, and photos. It has additionally selectable textual content and embedded hyperlinks.

If you happen to attempt the PDF era following this text, please present me your PDF information within the feedback, I might be happy to see them!

This text confirmed you find out how to generate superior PDF information from an HTML template on AWS Lambda. We discovered about AWS Lambda Layers, Pyppeteer library, and a few curiosities we discovered on the way in which.

Throughout my analysis, I discovered this Medium article written by Rafael Brand, it could be helpful as a complement to my article.

All ids and tokens proven on this article are faux or expired, in the event you attempt to use them, you won’t be able to ascertain any connections.

A particular due to Gianca Chavest for designing the superior illustration.

More Posts