mPDF is a PHP library for generating PDF files. InvoicePlan, which we adopted at my company, also uses mPDF for the PDF-output part — and naturally, we ran straight into garbled Chinese text.
Feed the keywords mPDF Chinese garbled to Google, and most of what you find is pretty terse, something like this:
$mpdf = new \Mpdf\Mpdf([
"autoScriptToLang" => true,
"autoLangToFont" => true,
]);
I tried the suggestion, and sure enough it could output Chinese — just not in the font I wanted. And when I kept googling for how to set the font, I couldn’t find much that was useful. So I paired the official docs with the source code to work out what was going on inside.
lang and font-family
What mPDF does, basically, is parse HTML code and assemble it into a PDF document. During output, mPDF decides which font to use based on two properties: the HTML lang and the CSS font-family.
Per the docs, Fonts & Languages / lang 6.x, lang affects the OTL glyph-set settings, and can also be used as a CSS lang selector. font-family, meanwhile, specifies which font a block of HTML content should be output in; for the full list of supported HTML tags, see CSS & Stylesheets / Supported CSS.
Enabling autoScriptToLang and autoLangToFont just skips those two settings and lets the system brute-force its way through your content to decide which lang and font-family to use. If you want the specifics, look at markScriptToLang() in Mpdf.php, plus Language/LanguageToFont.php and Language/ScriptToLang.php — it really is pretty brute-force. Generally, if the text contains Simplified Chinese, the language is detected as lang="und-Hans"; for Traditional Chinese it’s lang="und-Hant"; and regardless of Simplified or Traditional, the font defaults to font-family: sun-exta.
Free Adobe CJK Asian fonts
Some older sources will tell you that to get Chinese to display properly you should enable useAdobeCJK. In some ways, that option is a complete footgun. What useAdobeCJK means is this: if you have Adobe Acrobat or any related software installed on your computer, then in theory you should also have Adobe’s CJK Asian fonts. Enabling this option lets the PDF display using the fonts already installed on the machine, so you don’t have to embed the fonts into the PDF — which effectively cuts down the file size.
Given how widespread PDF and Adobe Acrobat are, the idea isn’t wrong. The problem is that times move on. The standard Adobe Traditional Chinese font that useAdobeCJK relies on is MSungStd-Light-Acro — see AddBig5Font() — and that font is an antique by now. Newer machines rarely have it installed, so turning the option on actually causes display problems instead.
According to the official docs, mPDF Variables / useAdobeCJK, the useAdobeCJK option has been supported since version 5.0. The earliest version number we can find on GitHub is v5.3.0, released on September 14, 2011… so you can imagine just how old this feature is. Also, although the docs mark useAdobeCJK as enabled by default, in my current mPDF v8.0.1 it’s already disabled by default.
Back to custom fonts
So the most reliable way to display Chinese, apart from the sun-exta font that ships with mPDF, is to add your own font. Luckily, mPDF has made this need very simple — just three steps:
- Deploy the font files
- Point to the font directory
- Link the font-family to the font files
Below, we’ll use the currently trendy Taipei Sans TC as our example.
Deploy the font files
Simply put, place the font files in a directory mPDF can access. Taipei Sans TC currently comes in three weights — Light, Regular, and Bold — and we’ll need Regular and Bold. After downloading, drop them straight into the mpdf/ttfonts/ directory.
Point to the font directory
If your custom font’s directory isn’t mpdf/ttfonts/ (for example, when mPDF only exists as a vendor package), then you need to specify the font directory separately, as in the official example:
// default font directory
$defaultConfig = (new \Mpdf\Config\ConfigVariables())->getDefaults();
$fontDirs = $defaultConfig["fontDir"];
$mpdf = new \Mpdf\Mpdf([
"fontDir" => array_merge($fontDirs, [
__DIR__ . '/custom/font/directory',
])
]);
Link the font-family to the font files
Because in HTML a font has at least four variations — regular, bold, italic, and bold-italic — your font-family setup also has to map to the different font files as needed. Here’s the fontdata configuration:
// font-family name
'freesans' => [
// Regular
'R' => 'FreeSans.ttf',
// Bold
'B' => 'FreeSansBold.ttf',
// Italic
'I' => 'FreeSansOblique.ttf',
// Bold Italic
'BI' => 'FreeSansBoldOblique.ttf',
],
For Taipei Sans TC, that’s:
// font-family name
'taipei' => [
// Regular
'R' => 'TaipeiSansTCBeta-Regular.ttf',
// Bold
'B' => 'TaipeiSansTCBeta-Bold.ttf',
],
Beyond the R B I above, font settings have plenty of other parameters — SIP-extension, useOTL, and so on. For details, see Fonts & Languages / Fonts in mPDF v7+.
Putting the fontDir and fontdata settings together
// default font directory
$defaultConfig = (new \Mpdf\Config\ConfigVariables())->getDefaults();
$fontDirs = $defaultConfig["fontDir"];
// add the custom font directory
$fontDirs = array_merge($fontDirs, [__DIR__ . '/custom/font/directory']);
// default font settings
$defaultFontConfig = (new \Mpdf\Config\FontVariables())->getDefaults();
$fontData = $defaultFontConfig["fontdata"];
// add the new font-family
$fontData = [
'taipei' => [
// Regular
'R' => 'TaipeiSansTCBeta-Regular.ttf',
// Bold
'B' => 'TaipeiSansTCBeta-Bold.ttf'
]
] + $fontData; // remember to append the original $fontData, or mPDF's built-in fonts get wiped out and stop working
$mpdf = new \Mpdf\Mpdf([
"fontDir" => $fontDirs,
"fontdata" => $fontData
]);
The HTML for testing:
<div style="font-size: 30px;">
<p style="font-family: taipei;">台北黑體 Regular:覺形創意有限公司</p>
<p style="font-family: taipei; font-weight: bold;">台北黑體 Bold:覺形創意有限公司</p>
<p style="font-family: sun-exta;">宋體 Sun-ExtA:覺形創意有限公司</p>
</div>
Once it’s output, Taipei Sans TC in regular and bold, along with the built-in Song font sun-exta, all display Chinese correctly. And that’s roughly it.


