Skip to content

Major: PDFs exports are BLANK! #371

Closed

Description

@Nesh108

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

DejaVuSans.ttf
DejaVuSansMono.ttf
DroidSansFallbackFull.ttf

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.

Activity

  1. chadek commented on Sep 15, 2026

    @chadek
    Collaborator

    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.

    What changed (#402):

    • 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 any AllFonts.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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions