mPDF는 PHP로 PDF 파일을 생성하는 라이브러리예요. 우리 회사에서 도입한 InvoicePlan도 PDF 출력 부분은 mPDF로 처리하고 있는데, 아니나 다를까 중국어 글자 깨짐 문제에 부딪혔어요.
mPDF 중국어 깨짐이라는 키워드로 Google을 검색하면, 나오는 정보 대부분이 꽤 간략해서, 대개 이런 식이에요:
$mpdf = new \Mpdf\Mpdf([
"autoScriptToLang" => true,
"autoLangToFont" => true,
]);
시키는 대로 해 보면, 확실히 중국어는 출력돼요. 다만 폰트가 제가 원하는 게 아니에요. 폰트 설정 쪽은 Google로 계속 찾아봐도 쓸 만한 정보가 잘 안 나오고요. 그래서 큰맘 먹고, 공식 문서와 함께 소스 코드를 보면서 안에서 무슨 일이 일어나는지 살펴봤어요.
lang과 font-family
mPDF가 하는 일은, 기본적으로 HTML 코드를 파싱해서 하나의 PDF 문서로 조립해 출력하는 거예요. 출력 과정에서 mPDF는 HTML의 lang과 CSS의 font-family라는 두 속성으로 어떤 폰트를 쓸지 판단해요.
문서 Fonts & Languages / lang 6.x에 따르면, lang은 OTL 글리프셋 설정에 영향을 주고, CSS의 lang selector로도 쓸 수 있어요. font-family 쪽은 HTML 블록의 내용을 어떤 폰트로 출력할지 지정하고요. 지원하는 HTML 태그의 자세한 목록은 CSS & Stylesheets / Supported CSS를 참고해 보세요.
autoScriptToLang과 autoLangToFont를 켠다는 건, 이 두 설정을 생략하고 시스템에 내용을 억지로 뜯어서 어떤 lang과 font-family를 쓸지 판단하게 하는 거예요. 구체적으로는 Mpdf.php의 markScriptToLang()이나 Language/LanguageToFont.php, Language/ScriptToLang.php 언저리 파일을 봐 보세요…… 정말 꽤 힘으로 밀어붙이거든요. 보통 본문에 간체자가 들어 있으면 언어는 lang="und-Hans"로 판정되고, 번체자면 lang="und-Hant", 폰트는 번체·간체 가리지 않고 자동으로 font-family: sun-exta가 선택돼요.
Free Adobe CJK Asian fonts
오래된 정보 중에는, 중국어를 제대로 표시하려면 useAdobeCJK를 켜라고 쓰여 있는 것들이 있어요. 이 옵션은 어떤 의미로는 완전히 지뢰예요. useAdobeCJK의 의미는 이래요. 만약 PC에 Adobe Acrobat이나 관련 소프트웨어가 깔려 있으면, 이론상으로는 Adobe의 CJK Asian 폰트도 깔려 있을 거예요. 이 옵션을 켜면 PC에 깔린 폰트를 그대로 써서 표시할 수 있어서, 폰트를 PDF에 심을 필요가 없어지고, 그만큼 파일 크기를 효과적으로 줄일 수 있어요.
PDF와 Adobe Acrobat의 보급률을 생각하면, 이 발상은 틀리지 않았어요. 문제는 시대가 앞으로 가고 있다는 거예요. useAdobeCJK가 쓰는 Adobe 표준 번체자 폰트는 MSungStd-Light-Acro —— AddBig5Font() 참조 —— 인데, 이건 이미 골동품 폰트예요. 요즘 PC에는 거의 안 깔려 있어서, 옵션을 켜면 오히려 표시 문제를 일으켜요.
공식 문서 mPDF Variables / useAdobeCJK에 따르면, useAdobeCJK는 5.0 버전부터 지원돼요. GitHub에서 따라갈 수 있는 가장 오래된 버전은 2011년 9월 14일 릴리스된 v5.3.0…… 이 기능이 얼마나 오래됐는지 짐작이 가죠. 게다가 공식은 useAdobeCJK의 기본값을 켜짐으로 써 뒀지만, 제가 지금 쓰는 mPDF v8.0.1에서는 기본값이 이미 꺼짐이에요.
커스텀 폰트로 돌아가기
그런 이유로, 가장 확실한 중국어 표시 방법은 mPDF에 딸려 오는 sun-exta 말고는, 직접 폰트를 추가하는 거예요. 다행히 이 작업은 mPDF가 아주 간단하게 해 놔서, 순서는 다음 세 가지뿐이에요:
- 폰트 파일을 배치한다
- 폰트 디렉터리를 지정한다
- font-family와 폰트 파일을 연결한다
아래에서는 요즘 인기 있는 台北黑體(Taipei Sans TC)를 예로 쓸게요.
폰트 파일 배치하기
간단히 말하면, 폰트 파일을 mPDF가 접근할 수 있는 디렉터리에 두기만 하면 돼요. 台北黑體에는 지금 Light·Regular·Bold 세 종류가 있고, 필요한 건 Regular와 Bold예요. 다운로드했으면 그대로 mpdf/ttfonts/ 디렉터리에 두세요.
폰트 디렉터리 지정하기
커스텀 폰트 디렉터리가 mpdf/ttfonts/가 아닌 경우(예를 들어 mPDF가 vendor로만 놓여 있는 경우)에는, 폰트 디렉터리를 따로 지정해야 해요. 공식 예제는 이런 식이에요:
// 기본 폰트 디렉터리
$defaultConfig = (new \Mpdf\Config\ConfigVariables())->getDefaults();
$fontDirs = $defaultConfig["fontDir"];
$mpdf = new \Mpdf\Mpdf([
"fontDir" => array_merge($fontDirs, [
__DIR__ . '/custom/font/directory',
])
]);
font-family와 폰트 파일 연결하기
HTML에서는 폰트에 적어도 표준·굵게·기울임·굵은 기울임 네 가지 변형이 있어요. 그래서 font-family 설정에서도 필요에 따라 각각의 폰트 파일에 대응시켜요. 아래가 fontdata 설정이에요:
// font-family 이름
'freesans' => [
// 표준(Regular)
'R' => 'FreeSans.ttf',
// 굵게(Bold)
'B' => 'FreeSansBold.ttf',
// 기울임(Italic)
'I' => 'FreeSansOblique.ttf',
// 굵은 기울임(Bold Italic)
'BI' => 'FreeSansBoldOblique.ttf',
],
台北黑體라면 이렇게 돼요:
// font-family 이름
'taipei' => [
// 표준(Regular)
'R' => 'TaipeiSansTCBeta-Regular.ttf',
// 굵게(Bold)
'B' => 'TaipeiSansTCBeta-Bold.ttf',
],
위의 R B I 말고도, 폰트 설정에는 SIP-extension, useOTL 등 많은 파라미터가 있어요. 자세한 건 Fonts & Languages / Fonts in mPDF v7+를 참고해 보세요.
fontDir·fontdata 설정 합치기
// 기본 폰트 디렉터리
$defaultConfig = (new \Mpdf\Config\ConfigVariables())->getDefaults();
$fontDirs = $defaultConfig["fontDir"];
// 커스텀 폰트 디렉터리 추가
$fontDirs = array_merge($fontDirs, [__DIR__ . '/custom/font/directory']);
// 기본 폰트 설정
$defaultFontConfig = (new \Mpdf\Config\FontVariables())->getDefaults();
$fontData = $defaultFontConfig["fontdata"];
// 폰트(font-family) 추가
$fontData = [
'taipei' => [
// 표준(Regular)
'R' => 'TaipeiSansTCBeta-Regular.ttf',
// 굵게(Bold)
'B' => 'TaipeiSansTCBeta-Bold.ttf'
]
] + $fontData; // 원래 $fontData를 꼭 더할 것. 안 그러면 mPDF 내장 폰트가 사라져 못 쓰게 된다
$mpdf = new \Mpdf\Mpdf([
"fontDir" => $fontDirs,
"fontdata" => $fontData
]);
테스트용 HTML:
<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>
출력하면, 台北黑體의 표준과 굵게, 그리고 내장 송체 sun-exta도 모두 중국어를 제대로 표시할 수 있어요. 대략 이런 정도예요.


