You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I am creating this ticket again as it is being overlooked by the developers: this is a crucial functionality that cannot be broken for over 4 years without any fix.
PDF export is the bread and butter of being able to use the document server in any capability.
Please developers, guide us on what we need to do to help you debug this.
On a normal installation running: sudo nextcloud.occ documentserver:fonts returns
Using any of those as fonts in our spreadsheets does not change anything and the export is blank. Not white fonts, just blankness and maybe table outlines if set.
Adding new fonts is possible but does not change things either.
Fixed for v0.4.0, by #402. You were right to reopen it and right that it needed a pointer to debug — so here is what it was, since the answer explains why documentserver:fonts told you nothing useful.
Your documentserver:fonts output listing three fonts was not the problem, and neither was adding more: that command lists the custom fonts in appdata, and the bundled ones were the ones failing to load. Setting any of them in a spreadsheet could not help, because no bundled font was loading at all.
What it was.server/FileConverter/bin/font_selection.bin stores an absolute path to every bundled font, written by whichever machine ran allfontsgen — the build machine, for an app store install. When one of those paths does not resolve on the machine that is running, x2t's PDF writer prints Can't load fontfile on stdout, writes a PDF with no glyphs in it, and exits 0. Nothing was logged and nothing was thrown, so from everywhere else in the system a blank PDF and a good one were the same successful conversion. That is why this stood for four years: there was no failure to find.
Same input, same binary, same exit code:
font_selection.bin holds
x2t exit
PDF size
text characters
paths that do not resolve here
0
1 937 B
0
paths pinned to ../../../core-fonts/
0
12 804 B
the text that went in
The build already pinned AllFonts.js to paths relative to x2t's working directory and deliberately left font_selection.bin alone, on a belief written into the Makefile: that its paths "are not used for loading". They are not used by the change-replay path that comment is about, which reads AllFonts.js. They are used by the PDF writer.
The paths are pinned relative at build time, so one machine's layout is no longer shipped to everybody. x2t accepts relative paths in font_selection.bin — verified first, since AllFonts.js accepting them does not prove it. The file is not sed-able (its strings are length-prefixed with a little-endian int32, so replacing one without fixing its prefix corrupts everything after it), so the rewriter is a real one, shared between the build and the runtime rebuild so both produce the same thing. Custom fonts live in appdata, outside the app directory, and keep the absolute paths they are found at.
A font rebuild that does not rebuild now says so. The old check accepted anyAllFonts.js containing /core-fonts/ — including a stale one left over from the build — so a rebuild that wrote nothing at all reported success, and it never looked at font_selection.bin at all. It now verifies that this run wrote both files, that the write succeeded, and that every pinned path resolves; and it refuses up front, naming the file and the user, when it cannot write. That case is the shape of Exception: sh: ../../tools/allfontsgen: not found #145 and Segmentation fault while rebuilding fonts #278, each of which left the user with blank PDFs and no diagnostic.
The failure is visible now. x2t's stdout is scanned for Can't load fontfile and logged, rather than discarding the one stream that mentioned it. occ documentserver:fonts also returned 1 on success and 0 on failure, so anything scripting it was told the opposite of what happened.
Proof.tests/rig/pdf_test.py is new — the rig had no PDF coverage at all, which is exactly why this survived. It asserts on extracted text rather than file size. Run against master just now, Nextcloud 34, ONLYOFFICE connector 10.1, bundled Euro-Office 9.3.4:
== a converted PDF has the text in it
ok - "PdfFontProbe" is in the 12859 byte PDF
== a font path that does not resolve reaches the log
ok - logged, and the PDF came out blank as expected
== a font rebuild that cannot write says so
ok - exit 1, and it named the file
VERDICT: conversion=ok reported=ok rebuild=ok
-- pdf: PASS in 6s
The second case deliberately breaks a font path and checks that the blank PDF is now reported, so the silent-failure half cannot come back either. It was confirmed to fail against the pre-fix font file (FAIL - "PdfFontProbe" is not in the 1937 byte PDF).
This ships in v0.4.0, and the test runs in CI on pull requests and weekly.
Activity
Pin the bundled font paths so PDF export is not blank (nextcloud#371, n…
Pin the bundled font paths so PDF export is not blank (#371, #251, #287…
chadek commentedon Sep 15, 2026
Fixed for v0.4.0, by #402. You were right to reopen it and right that it needed a pointer to debug — so here is what it was, since the answer explains why
documentserver:fontstold you nothing useful.Your
documentserver:fontsoutput listing three fonts was not the problem, and neither was adding more: that command lists the custom fonts in appdata, and the bundled ones were the ones failing to load. Setting any of them in a spreadsheet could not help, because no bundled font was loading at all.What it was.
server/FileConverter/bin/font_selection.binstores an absolute path to every bundled font, written by whichever machine ranallfontsgen— the build machine, for an app store install. When one of those paths does not resolve on the machine that is running, x2t's PDF writer printsCan't load fontfileon stdout, writes a PDF with no glyphs in it, and exits 0. Nothing was logged and nothing was thrown, so from everywhere else in the system a blank PDF and a good one were the same successful conversion. That is why this stood for four years: there was no failure to find.Same input, same binary, same exit code:
font_selection.binholds../../../core-fonts/The build already pinned
AllFonts.jsto paths relative to x2t's working directory and deliberately leftfont_selection.binalone, on a belief written into the Makefile: that its paths "are not used for loading". They are not used by the change-replay path that comment is about, which readsAllFonts.js. They are used by the PDF writer.What changed (#402):
font_selection.bin— verified first, sinceAllFonts.jsaccepting them does not prove it. The file is notsed-able (its strings are length-prefixed with a little-endian int32, so replacing one without fixing its prefix corrupts everything after it), so the rewriter is a real one, shared between the build and the runtime rebuild so both produce the same thing. Custom fonts live in appdata, outside the app directory, and keep the absolute paths they are found at.AllFonts.jscontaining/core-fonts/— including a stale one left over from the build — so a rebuild that wrote nothing at all reported success, and it never looked atfont_selection.binat all. It now verifies that this run wrote both files, that the write succeeded, and that every pinned path resolves; and it refuses up front, naming the file and the user, when it cannot write. That case is the shape of Exception: sh: ../../tools/allfontsgen: not found #145 andSegmentation faultwhile rebuilding fonts #278, each of which left the user with blank PDFs and no diagnostic.Can't load fontfileand logged, rather than discarding the one stream that mentioned it.occ documentserver:fontsalso returned 1 on success and 0 on failure, so anything scripting it was told the opposite of what happened.Proof.
tests/rig/pdf_test.pyis new — the rig had no PDF coverage at all, which is exactly why this survived. It asserts on extracted text rather than file size. Run againstmasterjust now, Nextcloud 34, ONLYOFFICE connector 10.1, bundled Euro-Office 9.3.4:The second case deliberately breaks a font path and checks that the blank PDF is now reported, so the silent-failure half cannot come back either. It was confirmed to fail against the pre-fix font file (
FAIL - "PdfFontProbe" is not in the 1937 byte PDF).This ships in v0.4.0, and the test runs in CI on pull requests and weekly.