Forum How do I...?

kerning issue on alpine linux

Saybrian
I'm trying to get an alpine linux-based installation of prince to render output in the same way as our previous ubuntu version did, but letter spacing is wrong.

salient section of docker file:
`RUN apk add --no-cache llibxml2 pixman tiff giflib libpng libcrypto1.1 lcms2 libjpeg-turbo libgomp libavif aom libcurl msttcorefonts-installer && \
update-ms-fonts fc-cache -f && \
wget -O /tmp/prince-15-alpine3.17-x86_64.tar.gz https:.../Prince/prince-15-alpine3.17-x86_64.tar.gz && \
tar -xf /tmp/prince-15-alpine3.17-x86_64.tar.gz -C /tmp && \
cd /tmp/prince-15-alpine3.17-x86_64 && \
./install.sh && \
rm -rf /tmp/prince-15-alpine3.17-x86_64 && \
rm /tmp/prince-15-alpine3.17-x86_64.tar.gz && \
wget -O /usr/local/lib/prince/license/license.dat https:.../Prince/Licences/license5.dat`

equivalent section in ubuntu based docker file:
`RUN apt install -y libtiff5 libgif7 libfontconfig1 fonts-freefont-ttf texlive-fonts-extra libgomp-plugin-nvptx1 && \
wget -O /tmp/prince-13.1-ubuntu18.04-amd64.tar.gz https:/.../Prince/prince-13.1-ubuntu18.04-amd64.tar.gz && \
tar -xf /tmp/prince-13.1-ubuntu18.04-amd64.tar.gz -C /tmp && \
cd /tmp/prince-13.1-ubuntu18.04-amd64 && \
./install.sh && \
rm -rf /tmp/prince-13.1-ubuntu18.04-amd64 && \
rm /tmp/prince-13.1-ubuntu18.04-amd64.tar.gz && \
wget -O /usr/local/lib/prince/license/license.dat https:.../Prince/Licences/license5.dat && \
rm -rf /var/lib/apt/lists/*`

i've attached two images showing the difference in character spacing/kerning. any insights would be greatly appreciated.
in the attached screenshots prince is defaulting to `/usr/share/fonts/truetype/freefont/FreeSerif.ttf` which it seems to require to be there, but the same problem is occurring with Roboto font as well (not with Merriweather though)

  1. AlpineVUbuntu.mp4922.5 kB
  2. alpine.PNG27.9 kB
    pdf output on alpine imahe
  3. ubuntu.PNG28.3 kB
    Pdf output on ubuntu image

Edited by Saybrian

howcome
Could you upload the pdf files as well?
Saybrian
I managed to resolve the issue in the previous example by ensuring the font files matched. there is still an issue with Roboto font though (as seen in the attached pdfs) - I've confirmed that the exact same font files are being resolved and used by prince by running with --debug option.
  1. AlpineVUbuntu.mp4922.5 kB
    screen recording showing issue
  2. alpine.pdf36.0 kB
    Alpine version
  3. ubuntu.pdf35.4 kB
    Ubuntu version
howcome
Indeed, having access to the same font files is key to achieving the same results.

In the PDF viewers on my machine (evince, acroread), I cannot see any difference in kerning in the two PDF files you provided. I attach a screenshot of evince renderings.

  1. foo.png52.9 kB

Edited by howcome

Saybrian
I've attached both the diff file generated by jest-image-snapshot (pdf first converted to image using pdf2pic) and an explanatory video where I've overlayed screen grab of the alpine pdf over ubuntu. the difference is subtle (I couldn't see it with the naked eye) but clearly there
  1. RobotoPdfDiff.mp43.4 MB
  2. account-direct-client-1-diff.png1.2 MB
    the diff file
howcome
Could it be that the small differences you are seeing are artefacts created by the onscreen pdf viewer?

Here's an attempt to render the pdf files without using an onscreen application:
howcome@ff:/ph/2023/kerning$ ls -l *pdf
-rw-rw-r-- 1 howcome howcome 36050 Jan 18 17:18 alpine.pdf
-rw-rw-r-- 1 howcome howcome 35365 Jan 18 17:18 ubuntu.pdf
howcome@ff:/ph/2023/kerning$ pdftoppm alpine.pdf | pnmtopng > alpine.png
howcome@ff:/ph/2023/kerning$ pdftoppm ubuntu.pdf | pnmtopng > ubuntu.png
howcome@ff:/ph/2023/kerning$ ls -l *png
-rw-rw-r-- 1 howcome howcome 405340 Jan 18 17:19 alpine.png
-rw-rw-r-- 1 howcome howcome 405340 Jan 18 17:19 ubuntu.png
howcome@ff:/ph/2023/kerning$ diff alpine.png ubuntu.png 
howcome@ff:/ph/2023/kerning$ 


So, pdftoppm creates identical images from the two PDF files.

(The PDF files are not identical, though, as can also be seen above. I'm guessing that the differences are due to the two builds using different versions of third-party libraries like Fontconfig or FreeType.)

https://www.princexml.com/doc/acknowledgements/

Edited by howcome

Saybrian
apologies if this is shifting the goal posts but here is another difference between the alpine and ubuntu versions. this one is evident on the attached pdf files, so not a matter of the viewer being used. specifically, look at the font being resolved for Local and offshore labels above the top bar chart. I notice in the debug output that given font-family:Roboto and font-weight:500, the ubuntu version resolves to this file:

`prince: used font: Roboto, Bold
prince: debug: font request: 500 Roboto
prince: loading font: /usr/share/fonts/truetype/roboto/unhinted/RobotoTTF/Roboto-Medium.ttf
prince: debug: loaded resource: /usr/share/fonts/truetype/roboto/unhinted/RobotoTTF/Roboto-Medium.ttf`

whereas alpine resolves another version of the same font file:
`prince: debug: font request: 500 Roboto
prince: loading font: /usr/share/fonts/Roboto/Roboto-Medium.ttf
prince: debug: loaded resource: /usr/share/fonts/Roboto/Roboto-Medium.ttf
prince: debug: loaded resource: type: no
prince: used font: Roboto, Medium`

the ubuntu version also has this same file at the same location and resolves it for the sub-header, but for some reason when the font weight is requested it looks for this unhinted file - is there a reason for that?

it should be noted that the alpine version also had Prince v15 installed whereas the ubuntu one has v13 so maybe there has been a change in font resolution behaviour between the two versions.

on your suggestion about Fontconfig and Freetype versions - it's only feasible to use latest versions of these packages on alpine and I have checked that the ubuntu image is on the latest available builds of these packages for that distro.

Also notice the difference in line height and kerning in the headers (merriweather), sub-header (Roboto-Medium) and preamble (Roboto regular). Can you offer any insights into why these would differ given that they are resolving the exact same font files (I've checked)?

  1. alpineassetallocation.pdf41.9 kB
  2. ubuntuassetallocation.pdf39.5 kB

Edited by Saybrian

howcome
Could you post the HTML source?
Saybrian
there you go
  1. assetallocation.html162.3 kB
howcome
It seems you are running Prince 13.1 on Ubuntu and Prince 15 on Alpine. Could you upgrade Prince to run the same version on both systems?

Edited by howcome

Saybrian
there you go
  1. assetallocation_ubuntu_prince15.pdf40.7 kB
howcome
Here's an analysis of the heading set in Merriweather ("Investment review"):

$ pdf2txt.py -t xml alpineassetallocation.pdf | grep '783.199' > alpine-boxes
$ pdf2txt.py -t xml assetallocation_ubuntu_prince15.pdf | grep '783.199' > ubuntu-boxes
$ ls -l alpine-boxes ubuntu-boxes 
-rw-rw-r-- 1 howcome howcome 1731 Jan 23 12:27 alpine-boxes
-rw-rw-r-- 1 howcome howcome 1731 Jan 23 12:28 ubuntu-boxes
$ diff alpine-boxes ubuntu-boxes 
$


pdf2text.py analyses the pdf files, grep filters out all but that heading, and the result is stored in a file.

Given that the two files are identical, I believe Prince 15 renders the headline identically on the two platforms. Are you still seeing differences on your screen?
  1. alpine-boxes1.7 kB
  2. ubuntu-boxes1.7 kB

Edited by howcome

Saybrian
yes - the header does now look identical. so we can put that down to a Prince change between 13.1 and 15.

The roboto regular line height and weight are still a bit of a mystery to me
howcome
Could you list a sentence (or a few consecutive words) where you see a difference in the two Prince15-generated files?
Saybrian
font resolution output (ubuntu):
prince: loading font: /usr/share/fonts/Roboto/Roboto-Medium.ttf
prince: debug: loaded resource: /usr/share/fonts/Roboto/Roboto-Medium.ttf
prince: debug: loaded resource: type: no
prince: used font: Roboto, Medium
prince: debug: font request: serif
prince: debug: font request: Roboto
prince: debug: font scan: roboto
prince: debug: found font: roboto Italic
prince: debug: found font: roboto Italic
prince: debug: found font: roboto Regular
prince: debug: found font: roboto Regular
prince: debug: found font: roboto Regular
prince: debug: found font: roboto Italic
prince: debug: duplicate font: roboto Italic
prince: debug: found font: roboto Regular
prince: debug: found font: roboto Italic
prince: debug: found font: roboto Italic
prince: debug: duplicate font: roboto Regular
prince: debug: found font: roboto Regular
prince: debug: duplicate font: roboto Regular
prince: debug: duplicate font: roboto Regular
prince: debug: duplicate font: roboto Italic
prince: debug: duplicate font: roboto Italic
prince: debug: duplicate font: roboto Regular
prince: debug: duplicate font: roboto Regular
prince: debug: duplicate font: roboto Italic
prince: debug: duplicate font: roboto Regular
prince: debug: duplicate font: roboto Regular
prince: debug: duplicate font: roboto Italic
prince: debug: font scan: roboto, 22 matches
prince: loading font: /usr/share/fonts/Roboto/Roboto-Regular.ttf
prince: debug: loaded resource: /usr/share/fonts/Roboto/Roboto-Regular.ttf
prince: debug: loaded resource: type: no
prince: used font: Roboto, Regular
prince: debug: font request: bold Roboto
prince: loading font: /usr/share/fonts/truetype/roboto/unhinted/RobotoTTF/Roboto-Bold.ttf
prince: debug: loaded resource: /usr/share/fonts/truetype/roboto/unhinted/RobotoTTF/Roboto-Bold.ttf
prince: debug: loaded resource: type: no
prince: used font: Roboto, Bold
prince: debug: font request: 500 Roboto
prince: loading font: /usr/share/fonts/truetype/roboto/unhinted/Roboto-Medium.ttf
prince: debug: loaded resource: /usr/share/fonts/truetype/roboto/unhinted/Roboto-Medium.ttf
prince: debug: loaded resource: type: no
prince: used font: Roboto, Medium
prince: loading font: /usr/share/fonts/truetype/freefont/FreeSerif.ttf
prince: debug: loaded resource: /usr/share/fonts/truetype/freefont/FreeSerif.ttf
prince: debug: loaded resource: type: no
prince: debug: font metrics: using inconsistent OS/2 typo ascender
prince: used font: FreeSerif, Regular
prince: Resolving cross-references...
prince: debug: pack
prince: debug: writing PDF to file: assetallocation.pdf
prince: Finished: success


font resolution output (alpine):
prince: loading font: /usr/share/fonts/Roboto/Roboto-Medium.ttf
prince: debug: loaded resource: /usr/share/fonts/Roboto/Roboto-Medium.ttf
prince: debug: loaded resource: type: no
prince: used font: Roboto, Medium
prince: debug: font request: serif
prince: debug: font request: Roboto
prince: debug: font scan: roboto
prince: debug: found font: roboto Italic
prince: debug: found font: roboto Italic
prince: debug: found font: roboto Regular
prince: debug: found font: roboto Regular
prince: debug: found font: roboto Italic
prince: debug: found font: roboto Regular
prince: debug: found font: roboto Italic
prince: debug: found font: roboto Italic
prince: debug: found font: roboto Regular
prince: debug: found font: roboto Regular
prince: debug: duplicate font: roboto Regular
prince: debug: duplicate font: roboto Regular
prince: debug: duplicate font: roboto Regular
prince: debug: duplicate font: roboto Regular
prince: debug: font scan: roboto, 14 matches
prince: loading font: /usr/share/fonts/Roboto/Roboto-Regular.ttf
prince: debug: loaded resource: /usr/share/fonts/Roboto/Roboto-Regular.ttf
prince: debug: loaded resource: type: no
prince: used font: Roboto, Regular
prince: debug: font request: bold Roboto
prince: loading font: /usr/share/fonts/truetype/roboto/unhinted/RobotoTTF/Roboto-Bold.ttf
prince: debug: loaded resource: /usr/share/fonts/truetype/roboto/unhinted/RobotoTTF/Roboto-Bold.ttf
prince: debug: loaded resource: type: no
prince: used font: Roboto, Bold
prince: debug: font request: 500 Roboto
prince: loading font: /usr/share/fonts/Roboto/Roboto-Medium.ttf
prince: debug: loaded resource: /usr/share/fonts/Roboto/Roboto-Medium.ttf
prince: debug: loaded resource: type: no
prince: used font: Roboto, Medium
prince: loading font: /usr/share/fonts/truetype/freefont/FreeSerif.ttf
prince: debug: loaded resource: /usr/share/fonts/truetype/freefont/FreeSerif.ttf
prince: debug: loaded resource: type: no
prince: debug: font metrics: using inconsistent OS/2 typo ascender
prince: used font: FreeSerif, Regular
prince: Resolving cross-references...
prince: debug: pack
prince: debug: writing PDF to file: assetallocation.pdf
prince: Finished: success
  1. alpinev15snippet.PNG19.4 kB
  2. ubuntuv15snippet.PNG12.7 kB
Saybrian
the font files of the same name are identical between alpine and ubuntu (I've made sure of that). the only difference is ubuntu is resolving /usr/share/fonts/truetype/roboto/unhinted/Roboto-Medium.ttf while alpine resolves: /usr/share/fonts/Roboto/Roboto-Medium.ttf but for Roboto-Regular the font files are the same
  1. fontoutputdiff.PNG122.1 kB
howcome
Where in the document do the differences occur on your screen? Could you list a few consecutive words where you see a difference?

Edited by howcome

Saybrian
this blurb under Asset allocation heading:
"The asset allocation shows how your investment is split between assets as a result of your unit trust allocation. Asset allocation information is
provided by ProfileData and may differ from the latest fund factsheets.
Your growth assets, comprising net equity and property, total 75.37%"
Saybrian
see demo video
  1. AlpinevUbuntuRobotRegular.mp42.0 MB
howcome
On my screen, using two different pdf viewers (evince and mupdf on Linux Mint 20.2) I cannot see any difference in the rendering of that segment. Which pdf viewer are you using, on what platform?

To try narrow down the test case, I've created a simplistic document. Could you try format it on your two systems and post the resulting pdf files?
  1. test.html0.6 kB
Saybrian
i'm using adobe acrobat reader on my windows 10 pc
Saybrian
as requested. still different in adobe acrobat reader on my pc
  1. test(alpine).pdf6.8 kB
  2. test(ubuntu).pdf8.5 kB
Saybrian
video showing what I'm seeing on Adobe acrobat reader,
  1. AlpinevUbuntuTest.mp42.1 MB
    movie showing diff on adobe acrobat

Edited by Saybrian

howcome
Thanks. It seems that the ttf files embedded in the two pdf files are slightly different:
$ mutool extract test\(ubuntu\).pdf 
extracting font Roboto-Regular-0009.ttf
$ otfinfo -t Roboto-Regular-0009.ttf 
    274 cmap
     84 cvt
    444 fpgm
   3424 glyf
     54 head
     36 hhea
     92 hmtx
     96 loca
     32 maxp
   1203 name
     32 post
    329 prep
$ mutool extract test\(alpine\).pdf 
extracting font Roboto-Regular-0009.ttf
$ otfinfo -t Roboto-Regular-0009.ttf 
    274 cmap
   1597 glyf
     54 head
     36 hhea
     92 hmtx
     96 loca
     32 maxp
    722 name
     32 post

This could be due to different versions of fontconfig. Could you try run this on both systems?
fc-list --version

Or, it could be that the originating font files are different on the two systems. Try run:
prince -v test.html

and then run md5sum (or similar) on the font files that are listed, e.g.:
$ md5sum /usr/share/fonts/truetype/msttcorefonts/times.ttf
4f97f4d6ba74767259ccfb242ce0e3f7  /usr/share/fonts/truetype/msttcorefonts/times.ttf

Edited by howcome

Saybrian
fc-list --version
- ubuntu: fontconfig version 2.13.1
- alpine: fontconfig version 2.14.1

md5sum
- ubuntu: 8f793587dcf03f31c551c5b60d175fc2 /usr/share/fonts/Roboto/Roboto-Regular.ttf
- alpine: 86da78cb59576328483a11c6ef74bc2b /usr/share/fonts/Roboto/Roboto-Regular.ttf

Edited by Saybrian

Saybrian
ok so it turns out the font files *didn't* actually match - i got things confused with all of the copying and reverting i've done - so now the test.pdfs looks identical - thanks!

the last thing causing differences in the assetallocation.html test case is that the ubuntu image uses these font files for the graph headers ("Total local/offshore split" and "Asset allocation breakdown"):
/usr/share/fonts/truetype/roboto/unhinted/RobotoTTF/Roboto-Bold.ttf
/usr/share/fonts/truetype/roboto/unhinted/Roboto-Medium.ttf

whereas alpine uses these files:
/usr/share/fonts/Roboto/Roboto-Bold.ttf
/usr/share/fonts/Roboto/Roboto-Medium.ttf

i've tried copying the unhinted ubuntu files to the same locations on alpine but it still uses the ones listed above. is this a result of fontconfig differences? (i see the latest available ubuntu version is 2.13.1: https://repology.org/project/fontconfig/versions

any idea how i might get the alpine version to resolve the same font files?

Edited by Saybrian

howcome
Thanks, we're making progress!

The simplest way to have both systems use exactly the same font files for a given font family is to only provide one version. For example, you wouldn't have both hinted and unhinted versions of Roboto available.

A more complex solution is to tune fontconfig. Here's an extensive guide:

https://www.linuxfromscratch.org/blfs/view/svn/x/tuning-fontconfig.html

Edited by howcome

Saybrian
ok I finally have the alpine version producing the same results as the ubuntu one. in case someone has a similar requirement and stumbles across this thread, these were the things to look out for to ensure consistent rendering behaviour:
1. Prince version. this is fairly obvious but amidst all the other variables (OS, fontconfig, freetype versions) it wasn't the first place I looked (having looked at the release notes between 13 and 15 nothing stood out as affecting font kerning/rendering). In my case, Prince 15 renders fonts differently to Prince 13.6 on the same operating system, even when the same font files are being resolved.
2. Font file versions - in my case, the ubuntu image version I was trying to match was resolving both unhinted versions of font files and those I was explicitly copying to /usr/share/fonts. What made all the difference was running `prince --debug` with test html files and comparing the debug output which very clearly (in retrospect) indicates which font file is used. Using this output, I copied the font files from the ubuntu image and ensured that I copied them to the exact same locations on the alpine image.

@howcome - thanks very much for your time and investment in helping me debug this

Edited by Saybrian

howcome
Glad it worked out, thanks for your summary!