Background
I am using Puppeteer in an express application that is running in a Docker image. It is necessary for us to run in Docker because of needed dependencies that Debian needs which we do not have access to install. Using Docker allows us to install what we need.
We have seen a lot of people have problems getting their fonts to render in the PDFs correctly and in every case I have seen, installing the font in a way close to this is always the answer,
apt-get install -yq --allow-unauthenticated ttf-mscorefonts-installer
In that case they are installing specific fonts that there happens to be an apt-get
for. I have seen others install the default Puppeteer fonts with RUN apt-get install -yyq fonts-liberation
as well.
Code Example
const browser = await puppeteer.launch({
args: ['--no-sandbox', '--disable-setuid-sandbox'],
ignoreHTTPSErrors: true,
dumpio: false,
});
const page = await browser.newPage();
await page.goto(
`http://localhost:3000/${template}?data=${JSON.stringify(req.body)}`,
);
const pdfBuffer = await page.pdf({
format: 'A4',
margin: {
top: '20px',
left: '20px',
right: '20px',
bottom: '20px',
},
});
await browser.close();
Problem
We need to install about 10 fonts that different parts of are application will use on different occasions. We have the ttf
and the woff
files for this. We decided to add them to the system in the way that appears to be what apt-get
is doing with the other fonts we have seen people install. We did this by adding our fonts to the Debian directory,
/usr/local/share/fonts
We can see the fonts are setup correctly in the system by running,
fc-list
When we add the fonts like this, they do not render. Instead we get weird symbols where these fonts should be.
Example
We are adding our fonts like this using the Dockerfile,
RUN apt-get install -yyq fonts-liberation
COPY /fonts/*.ttf /usr/local/share/fonts/
COPY /fonts/*.woff /usr/local/share/fonts/
Question
Since we have a bunch of ttf
and woff
font files that need to be rendered in PDFs using Puppeteer, what is the correct way to add them to our Debian image that is running in Docker so that Puppeteer will use them as expected?
This is a sample script which goes and captures screenshot and pdf on website. Both serves same purpose on this question, to show the fonts works.
(async()=>{
const puppeteer = require('puppeteer')
const browser = await puppeteer.launch({
headless: true,
args: ["--no-sandbox", "--disable-setuid-sandbox"]
});
const page = await browser.newPage()
await page.goto('https://jp.quora.com/')
await page.screenshot({path: `/shared/_${Date.now()}.png`})
const pdfBuffer = await page.pdf({path: `/shared/_${Date.now()}.pdf`});
await browser.close()
})()
Here is the minimal dockerfile, this is very minimal, it doesn't include anything extra like dumb-init and various cleanup hacks as it's not required here.
FROM node:8
# Install dependencies
RUN apt-get update && \
apt-get -yq install libatk1.0-0 libgtk2.0-0 libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 libasound2 xauth xvfb
# Cd into /app
WORKDIR /app
# Copy package.json into app folder
COPY package.json /app
# Install dependencies
RUN npm install
COPY . /app
# Start script on Xvfb
CMD ["xvfb-run","npm","start"]
Upon running, here is how Japanese Quora is showing on puppeteer, it is showing like this because fonts are missing.
.
Since it is based on jessie, and we can install fonts using few different commands.
The below will install some fonts with Japanese characters in it.
RUN apt-get -yq install xfonts-utils fonts-droid xfonts-intl-asian
If I have the fonts inside fonts directory, then the command is,
COPY fonts/*.* /usr/share/fonts/truetype/
That's really straightforward. However the fonts will still not work because the font cache is not updated that fast enough. Adding the following will make sure it's updated.
RUN mkfontscale && mkfontdir && fc-cache
That's it! Let us run the script again.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With