[BUG] Print in Browser — Custom large fonts (Arabic/CJK) render blank on first print after page refresh

Stimulsoft Reports.JS discussion
Post Reply
User avatar
noob
Posts: 78
Joined: Sat Feb 05, 2022 9:03 am
Location: Iraq, Erbil

[BUG] Print in Browser — Custom large fonts (Arabic/CJK) render blank on first print after page refresh

Post by noob »

Bug: Custom fonts (large @font-face base64) render blank on first "Print in Browser" after page refresh

Product: Stimulsoft Reports.Blazor
Version: 2026.2.2 (reproducible across multiple versions)
Platform: Blazor Server, ASP.NET Core
Browser: Chrome, Edge (any Chromium-based)

───────────────────────────────────────────────────────────────

Description

When using StiPrintDestination.Direct (Print Without Preview / Print in Browser), any report component that uses a custom font loaded via AllowLoadingCustomFontsToClientSide = true + StiFontCollection.AddFontBase64() renders as blank/invisible text on the first print attempt after a full page refresh.
  • First click on Print → browser print dialog opens → affected text is blank
  • Close the dialog, click Print again → everything renders correctly
  • Refresh the page → problem returns on the first print
The issue is specific to large fonts (Arabic, CJK, etc. — 150KB+). Small Latin fonts are not affected.

───────────────────────────────────────────────────────────────

Steps to Reproduce
  1. Register a large custom font (e.g. Noto Naskh Arabic, ~217KB) using StiFontCollection.AddFontBase64() on the server
  2. Set Options.Server.AllowLoadingCustomFontsToClientSide = true
  3. Set Options.Toolbar.PrintDestination = StiPrintDestination.Direct
  4. Design a report with at least one text component using that font
  5. Open the page in the browser (hard refresh, Ctrl+Shift+R)
  6. Click the Print button → select "Print Without Preview"
Expected: Text using the custom font renders correctly in the print dialog.
Actual: Text is blank/invisible. Closing and re-printing immediately works fine.

───────────────────────────────────────────────────────────────

Root Cause (identified)

By inspecting the embedded resource Stimulsoft.Report.Web.Viewer.Scripts.Base.Actions.js inside the DLL, the printAsHtml function contains this sequence:

Code: Select all

StiJsViewer.prototype.printAsHtml = function (text, jsObject) {
    // ...
    printFrame.onload = function () {
        jsObject.addCustomFontStyles(
            jsObject.options.customOpenTypeFonts,
            printFrame.contentWindow.document.getElementsByTagName("head")[0]
        );
        printFrame.contentWindow.focus();
        printFrame.contentWindow.print(); // ← called immediately after font injection
    }

    printFrame.contentWindow.document.open();
    printFrame.contentWindow.document.write(text);
    printFrame.contentWindow.document.close();
}
addCustomFontStyles injects a @font-face CSS rule with the full base64 font data into the iframe <head>:

Code: Select all

var cssText = "@font-face {\n" +
    "font-family: '" + font.originalFontFamily + "';\n" +
    "src: url(" + font.contentForCss + ");\n }";
style.innerHTML = cssText;
head.appendChild(style);
The browser parses base64 font data asynchronously in a background thread. For a 217KB Arabic font this takes ~20–80ms. Since print() is called on the very next line — synchronously — the font has not finished parsing when the print dialog opens. The browser falls back to a system font (or renders nothing), producing blank text.

The second print succeeds because the decoded font is already in the browser's internal font cache.

This is a standard browser behavior — the Font Loading API (document.fonts.ready) exists specifically to detect this condition.

───────────────────────────────────────────────────────────────

Suggested Fix

In printAsHtml (and similarly in printAsPopup), replace the immediate print() call with a document.fonts.ready wait:

Code: Select all

// BEFORE
printFrame.onload = function () {
    jsObject.addCustomFontStyles(..., head);
    printFrame.contentWindow.focus();
    printFrame.contentWindow.print();
}

// AFTER
printFrame.onload = function () {
    jsObject.addCustomFontStyles(..., head);
    printFrame.contentWindow.document.fonts.ready.then(function () {
        printFrame.contentWindow.focus();
        printFrame.contentWindow.print();
    });
}
document.fonts.ready is a Promise that resolves only when all pending @font-face resources have finished loading and parsing. For system fonts or already-cached fonts it resolves instantly, so there is zero performance impact in the normal case.
MDN reference: https://developer.mozilla.org/en-US/doc ... eSet/ready
"The ready read-only property of the FontFaceSet interface returns a Promise that resolves when font loading and layout operations have completed and the document is ready to be printed."
───────────────────────────────────────────────────────────────

Workaround (client-side, until an official fix is released)

Patch HTMLIFrameElement.prototype.contentWindow in your main JS file before Stimulsoft initializes, to transparently wrap every iframe's print() with a fonts.ready wait:

Code: Select all

(function () {
    var iframeDesc = Object.getOwnPropertyDescriptor(
        HTMLIFrameElement.prototype, 'contentWindow'
    );
    if (!iframeDesc || !iframeDesc.get) return;

    var _origGet = iframeDesc.get;
    Object.defineProperty(HTMLIFrameElement.prototype, 'contentWindow', {
        get: function () {
            var win = _origGet.call(this);
            if (win && !win.__fontPrintPatched) {
                try {
                    var _origPrint = win.print.bind(win);
                    win.print = function () {
                        var ready = win.document && win.document.fonts
                            ? win.document.fonts.ready
                            : Promise.resolve();
                        ready.then(function () { _origPrint(); });
                    };
                    win.__fontPrintPatched = true;
                } catch (e) { /* cross-origin iframe — skip */ }
            }
            return win;
        },
        configurable: true,
        enumerable: true
    });
})();
This does not modify any Stimulsoft code and has no side effects on other iframe usage.

───────────────────────────────────────────────────────────────

Environment
  • Stimulsoft.Reports.Blazor: 2026.2.2
  • Framework: .NET 8, Blazor Server
  • Browser: Chrome 124+, Edge 124+
  • Font triggering the bug: Noto Naskh Arabic Regular (217KB TTF, loaded via StiFontCollection.AddFontBase64)
  • Does NOT happen with: Small Latin fonts, system fonts, or StiPrintDestination.Pdf
Max Shamanov
Posts: 1132
Joined: Tue Sep 07, 2021 10:11 am

Re: [BUG] Print in Browser — Custom large fonts (Arabic/CJK) render blank on first print after page refresh

Post by Max Shamanov »

Hello,

Please clarify whether you load the font as a base64 string or as a font file?
Also, could you please send us a sample project that reproduces the issue?

Thank you.
User avatar
noob
Posts: 78
Joined: Sat Feb 05, 2022 9:03 am
Location: Iraq, Erbil

Re: [BUG] Print in Browser — Custom large fonts (Arabic/CJK) render blank on first print after page refresh

Post by noob »

Hello Max,

1- I load the font as a base64 string.
2- this issue won't happen in new very small project (my project that has this issue is very HUGE) because:
A clean project uses only system fonts (Arial, Times New Roman, etc.) — fonts the OS already has installed. When Stimulsoft's viewer runs in a browser, those fonts are already available in the browser's font engine before print() is ever called. There is nothing to parse, so the race condition has zero window of time to occur.
User avatar
noob
Posts: 78
Joined: Sat Feb 05, 2022 9:03 am
Location: Iraq, Erbil

Re: [BUG] Print in Browser — Custom large fonts (Arabic/CJK) render blank on first print after page refresh

Post by noob »

additional information:

The race only manifests when all three conditions are true simultaneously:
Screenshot1.png
Screenshot1.png (10.98 KiB) Viewed 1380 times
System fonts bypass FontFaceSet entirely — document.fonts.ready resolves instantly because there are no pending font loads. With a 217KB Arabic font injected as a raw base64 data URI at onload time, the parse takes tens of milliseconds, which is longer than the zero milliseconds between addCustomFontStyles() and print().
Max Shamanov
Posts: 1132
Joined: Tue Sep 07, 2021 10:11 am

Re: [BUG] Print in Browser — Custom large fonts (Arabic/CJK) render blank on first print after page refresh

Post by Max Shamanov »

Hello,

Unfortunately, we were unable to reproduce the issue; we tested your issue using our test cases:
https://github.com/stimulsoft/Samples-R ... /NET%208.0

We added code to load the font using both the font file and Base64, and it worked in both cases.
We also added code to display the font in the viewer and for printing, and we didn’t encounter any issues.

Thank you.
User avatar
noob
Posts: 78
Joined: Sat Feb 05, 2022 9:03 am
Location: Iraq, Erbil

Re: [BUG] Print in Browser — Custom large fonts (Arabic/CJK) render blank on first print after page refresh

Post by noob »

Hello Max,

Interestingly, the workaround I previously provided resolved my issue, so it may be beneficial for your development team to review and analyze the workaround further for additional investigation.

Thanks.
Max Shamanov
Posts: 1132
Joined: Tue Sep 07, 2021 10:11 am

Re: [BUG] Print in Browser — Custom large fonts (Arabic/CJK) render blank on first print after page refresh

Post by Max Shamanov »

Hello,

Thank you for the information.
Post Reply