PDFPipe

Text and fonts

Chinese, Japanese or Korean characters show as boxes

Latin text renders correctly and CJK text is a row of empty rectangles, often called tofu. Sometimes only some characters fail, which is the more confusing version.

What is actually happening

The font in use has no glyphs for those characters, and no font on the render machine does either. A minimal container image ships with almost no fonts, and none of them cover CJK. The partial-failure version happens when the fallback chain finds a font covering some ranges but not all.

Confirming it is this and not something that looks like it

Try a single common character in each script. If they all fail, no CJK font is available at all. If some render, you have partial coverage and need a font with the full range.

The fix

Declare a font that actually covers the script and load it explicitly rather than relying on whatever the machine has. Noto Sans CJK is the usual choice because its coverage is complete and its licence permits embedding. Note that Simplified Chinese, Traditional Chinese, Japanese and Korean want different regional variants for correct glyph shapes even where the code points overlap.

css
@font-face {
  font-family: "Noto Sans SC";
  src: url("https://assets.example.com/fonts/noto-sans-sc-400.woff2") format("woff2");
  font-weight: 400;
  /* Restrict to the ranges this face is for, so Latin still uses the brand font. */
  unicode-range: U+4E00-9FFF, U+3000-303F, U+FF00-FFEF;
}

.document {
  font-family: "Brand Sans", "Noto Sans SC", sans-serif;
}

/* Japanese and Korean want their own faces: the code points overlap but the
   correct glyph shapes do not. */
:lang(ja) { font-family: "Brand Sans", "Noto Sans JP", sans-serif; }
:lang(ko) { font-family: "Brand Sans", "Noto Sans KR", sans-serif; }

If that was not it

These produce the same symptom often enough to be worth ruling out before assuming the fix above did not work.

  • A lang attribute missing from the markup, so the engine cannot pick a regional variant
  • A CJK font subset that dropped the characters actually used, which is easy to do given the character count
  • line-height tuned for Latin, which crowds CJK text badly
  • Full-width punctuation, which needs the CJK font rather than the Latin one

Frequently asked

Does this happen with every rendering engine?

The behaviour behind it is not specific to one tool. The font in use has no glyphs for those characters, and no font on the render machine does either. Anything rendering HTML to a paged medium has to make the same decision, so the fix travels with you if you change how the render happens.

Will this show up as an error in my logs?

No, and that is what makes it expensive. A document that renders wrong is still a successful render as far as every log line is concerned. It is found by someone opening the file, which is usually the customer.

Related failures

Problems people arrive at from the same starting point, or mistake for this one.

Paste your markup and see the rendered document, without signing up.