1 // Copyright 2020 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "core/fxge/win32/cwin32_platform.h"
8
9 #include <iterator>
10 #include <memory>
11 #include <utility>
12
13 #include "core/fxcrt/fx_codepage.h"
14 #include "core/fxge/cfx_folderfontinfo.h"
15 #include "core/fxge/cfx_gemodule.h"
16 #include "third_party/base/check.h"
17 #include "third_party/base/containers/span.h"
18 #include "third_party/base/numerics/safe_conversions.h"
19 #include "third_party/base/win/scoped_select_object.h"
20 #include "third_party/base/win/win_util.h"
21
22 namespace {
23
24 using ScopedSelectObject = pdfium::base::win::ScopedSelectObject;
25
26 struct Variant {
27 const char* m_pFaceName;
28 const char* m_pVariantName; // Note: UTF-16LE terminator required.
29 };
30
31 constexpr Variant kVariantNames[] = {
32 {"DFKai-SB", "\x19\x6A\x77\x69\xD4\x9A\x00\x00"},
33 };
34
35 struct Substs {
36 const char* m_pName;
37 const char* m_pWinName;
38 bool m_bBold;
39 bool m_bItalic;
40 };
41
42 constexpr Substs kBase14Substs[] = {
43 {"Courier", "Courier New", false, false},
44 {"Courier-Bold", "Courier New", true, false},
45 {"Courier-BoldOblique", "Courier New", true, true},
46 {"Courier-Oblique", "Courier New", false, true},
47 {"Helvetica", "Arial", false, false},
48 {"Helvetica-Bold", "Arial", true, false},
49 {"Helvetica-BoldOblique", "Arial", true, true},
50 {"Helvetica-Oblique", "Arial", false, true},
51 {"Times-Roman", "Times New Roman", false, false},
52 {"Times-Bold", "Times New Roman", true, false},
53 {"Times-BoldItalic", "Times New Roman", true, true},
54 {"Times-Italic", "Times New Roman", false, true},
55 };
56
57 struct FontNameMap {
58 const char* m_pSubFontName;
59 const char* m_pSrcFontName;
60 };
61
62 constexpr FontNameMap kJpFontNameMap[] = {
63 {"MS Mincho", "Heiseimin-W3"},
64 {"MS Gothic", "Jun101-Light"},
65 };
66
GetSubFontName(ByteString * name)67 bool GetSubFontName(ByteString* name) {
68 for (size_t i = 0; i < std::size(kJpFontNameMap); ++i) {
69 if (!FXSYS_stricmp(name->c_str(), kJpFontNameMap[i].m_pSrcFontName)) {
70 *name = kJpFontNameMap[i].m_pSubFontName;
71 return true;
72 }
73 }
74 return false;
75 }
76
77 // Wraps CreateFontA() so callers don't have to specify all the arguments.
Win32CreateFont(int weight,bool italic,FX_Charset charset,int pitch_family,const char * face)78 HFONT Win32CreateFont(int weight,
79 bool italic,
80 FX_Charset charset,
81 int pitch_family,
82 const char* face) {
83 return ::CreateFontA(-10, 0, 0, 0, weight, italic, 0, 0,
84 static_cast<int>(charset), OUT_TT_ONLY_PRECIS, 0, 0,
85 pitch_family, face);
86 }
87
88 class CFX_Win32FallbackFontInfo final : public CFX_FolderFontInfo {
89 public:
90 CFX_Win32FallbackFontInfo() = default;
91 ~CFX_Win32FallbackFontInfo() override = default;
92
93 // CFX_FolderFontInfo:
94 void* MapFont(int weight,
95 bool bItalic,
96 FX_Charset charset,
97 int pitch_family,
98 const ByteString& face) override;
99 };
100
101 class CFX_Win32FontInfo final : public SystemFontInfoIface {
102 public:
103 CFX_Win32FontInfo();
104 ~CFX_Win32FontInfo() override;
105
106 // SystemFontInfoIface:
107 bool EnumFontList(CFX_FontMapper* pMapper) override;
108 void* MapFont(int weight,
109 bool bItalic,
110 FX_Charset charset,
111 int pitch_family,
112 const ByteString& face) override;
GetFont(const ByteString & face)113 void* GetFont(const ByteString& face) override { return nullptr; }
114 size_t GetFontData(void* hFont,
115 uint32_t table,
116 pdfium::span<uint8_t> buffer) override;
117 bool GetFaceName(void* hFont, ByteString* name) override;
118 bool GetFontCharset(void* hFont, FX_Charset* charset) override;
119 void DeleteFont(void* hFont) override;
120
121 void AddInstalledFont(const LOGFONTA* plf, uint32_t font_type);
122
123 private:
124 bool IsSupportedFont(const LOGFONTA* plf);
125 void GetGBPreference(ByteString& face, int weight, int pitch_family);
126 void GetJapanesePreference(ByteString& face, int weight, int pitch_family);
127 ByteString FindFont(const ByteString& name);
128 void* GetFontFromList(int weight,
129 bool italic,
130 FX_Charset charset,
131 int pitch_family,
132 pdfium::span<const char* const> font_faces);
133
134 const HDC m_hDC;
135 UnownedPtr<CFX_FontMapper> m_pMapper;
136 ByteString m_LastFamily;
137 ByteString m_KaiTi;
138 ByteString m_FangSong;
139 };
140
FontEnumProc(const LOGFONTA * plf,const TEXTMETRICA * lpntme,uint32_t font_type,LPARAM lParam)141 int CALLBACK FontEnumProc(const LOGFONTA* plf,
142 const TEXTMETRICA* lpntme,
143 uint32_t font_type,
144 LPARAM lParam) {
145 CFX_Win32FontInfo* pFontInfo = reinterpret_cast<CFX_Win32FontInfo*>(lParam);
146 pFontInfo->AddInstalledFont(plf, font_type);
147 return 1;
148 }
149
CFX_Win32FontInfo()150 CFX_Win32FontInfo::CFX_Win32FontInfo() : m_hDC(CreateCompatibleDC(nullptr)) {}
151
~CFX_Win32FontInfo()152 CFX_Win32FontInfo::~CFX_Win32FontInfo() {
153 DeleteDC(m_hDC);
154 }
155
IsSupportedFont(const LOGFONTA * plf)156 bool CFX_Win32FontInfo::IsSupportedFont(const LOGFONTA* plf) {
157 HFONT hFont = CreateFontIndirectA(plf);
158 bool ret = false;
159 size_t font_size = GetFontData(hFont, 0, {});
160 if (font_size != GDI_ERROR && font_size >= sizeof(uint32_t)) {
161 uint32_t header;
162 auto span = pdfium::as_writable_bytes(pdfium::make_span(&header, 1));
163 GetFontData(hFont, 0, span);
164 header = FXSYS_UINT32_GET_MSBFIRST(span);
165 ret = header == FXBSTR_ID('O', 'T', 'T', 'O') ||
166 header == FXBSTR_ID('t', 't', 'c', 'f') ||
167 header == FXBSTR_ID('t', 'r', 'u', 'e') || header == 0x00010000 ||
168 header == 0x00020000 ||
169 (header & 0xFFFF0000) == FXBSTR_ID(0x80, 0x01, 0x00, 0x00) ||
170 (header & 0xFFFF0000) == FXBSTR_ID('%', '!', 0, 0);
171 }
172 DeleteFont(hFont);
173 return ret;
174 }
175
AddInstalledFont(const LOGFONTA * plf,uint32_t font_type)176 void CFX_Win32FontInfo::AddInstalledFont(const LOGFONTA* plf,
177 uint32_t font_type) {
178 ByteString name(plf->lfFaceName);
179 if (name.GetLength() > 0 && name[0] == '@')
180 return;
181
182 if (name == m_LastFamily) {
183 m_pMapper->AddInstalledFont(name, FX_GetCharsetFromInt(plf->lfCharSet));
184 return;
185 }
186 if (!(font_type & TRUETYPE_FONTTYPE)) {
187 if (!(font_type & DEVICE_FONTTYPE) || !IsSupportedFont(plf))
188 return;
189 }
190
191 m_pMapper->AddInstalledFont(name, FX_GetCharsetFromInt(plf->lfCharSet));
192 m_LastFamily = name;
193 }
194
EnumFontList(CFX_FontMapper * pMapper)195 bool CFX_Win32FontInfo::EnumFontList(CFX_FontMapper* pMapper) {
196 m_pMapper = pMapper;
197 LOGFONTA lf;
198 memset(&lf, 0, sizeof(LOGFONTA));
199 lf.lfCharSet = static_cast<int>(FX_Charset::kDefault);
200 lf.lfFaceName[0] = 0;
201 lf.lfPitchAndFamily = 0;
202 EnumFontFamiliesExA(m_hDC, &lf, reinterpret_cast<FONTENUMPROCA>(FontEnumProc),
203 reinterpret_cast<uintptr_t>(this), 0);
204 return true;
205 }
206
FindFont(const ByteString & name)207 ByteString CFX_Win32FontInfo::FindFont(const ByteString& name) {
208 if (!m_pMapper)
209 return name;
210
211 absl::optional<ByteString> maybe_installed =
212 m_pMapper->InstalledFontNameStartingWith(name);
213 if (maybe_installed.has_value())
214 return maybe_installed.value();
215
216 absl::optional<ByteString> maybe_localized =
217 m_pMapper->LocalizedFontNameStartingWith(name);
218 if (maybe_localized.has_value())
219 return maybe_localized.value();
220
221 return ByteString();
222 }
223
GetFontFromList(int weight,bool italic,FX_Charset charset,int pitch_family,pdfium::span<const char * const> font_faces)224 void* CFX_Win32FontInfo::GetFontFromList(
225 int weight,
226 bool italic,
227 FX_Charset charset,
228 int pitch_family,
229 pdfium::span<const char* const> font_faces) {
230 DCHECK(!font_faces.empty());
231
232 // Initialization not needed because of DCHECK() above and the assignment in
233 // the for-loop below.
234 HFONT font;
235 for (const char* face : font_faces) {
236 font = Win32CreateFont(weight, italic, charset, pitch_family, face);
237 ByteString actual_face;
238 if (GetFaceName(font, &actual_face) && actual_face.EqualNoCase(face))
239 break;
240 }
241 return font;
242 }
243
MapFont(int weight,bool bItalic,FX_Charset charset,int pitch_family,const ByteString & face)244 void* CFX_Win32FallbackFontInfo::MapFont(int weight,
245 bool bItalic,
246 FX_Charset charset,
247 int pitch_family,
248 const ByteString& face) {
249 void* font = GetSubstFont(face);
250 if (font)
251 return font;
252
253 bool bCJK = true;
254 switch (charset) {
255 case FX_Charset::kShiftJIS:
256 case FX_Charset::kChineseSimplified:
257 case FX_Charset::kChineseTraditional:
258 case FX_Charset::kHangul:
259 break;
260 default:
261 bCJK = false;
262 break;
263 }
264 return FindFont(weight, bItalic, charset, pitch_family, face, !bCJK);
265 }
266
GetGBPreference(ByteString & face,int weight,int pitch_family)267 void CFX_Win32FontInfo::GetGBPreference(ByteString& face,
268 int weight,
269 int pitch_family) {
270 if (face.Contains("KaiTi") || face.Contains("\xbf\xac")) {
271 if (m_KaiTi.IsEmpty()) {
272 m_KaiTi = FindFont("KaiTi");
273 if (m_KaiTi.IsEmpty()) {
274 m_KaiTi = "SimSun";
275 }
276 }
277 face = m_KaiTi;
278 } else if (face.Contains("FangSong") || face.Contains("\xb7\xc2\xcb\xce")) {
279 if (m_FangSong.IsEmpty()) {
280 m_FangSong = FindFont("FangSong");
281 if (m_FangSong.IsEmpty()) {
282 m_FangSong = "SimSun";
283 }
284 }
285 face = m_FangSong;
286 } else if (face.Contains("SimSun") || face.Contains("\xcb\xce")) {
287 face = "SimSun";
288 } else if (face.Contains("SimHei") || face.Contains("\xba\xda")) {
289 face = "SimHei";
290 } else if (!(pitch_family & FF_ROMAN) && weight > 550) {
291 face = "SimHei";
292 } else {
293 face = "SimSun";
294 }
295 }
296
GetJapanesePreference(ByteString & face,int weight,int pitch_family)297 void CFX_Win32FontInfo::GetJapanesePreference(ByteString& face,
298 int weight,
299 int pitch_family) {
300 if (face.Contains("Gothic") ||
301 face.Contains("\x83\x53\x83\x56\x83\x62\x83\x4e")) {
302 if (face.Contains("PGothic") ||
303 face.Contains("\x82\x6f\x83\x53\x83\x56\x83\x62\x83\x4e")) {
304 face = "MS PGothic";
305 } else if (face.Contains("UI Gothic")) {
306 face = "MS UI Gothic";
307 } else {
308 if (face.Contains("HGSGothicM") || face.Contains("HGMaruGothicMPRO")) {
309 face = "MS PGothic";
310 } else {
311 face = "MS Gothic";
312 }
313 }
314 return;
315 }
316 if (face.Contains("Mincho") || face.Contains("\x96\xbe\x92\xa9")) {
317 if (face.Contains("PMincho") || face.Contains("\x82\x6f\x96\xbe\x92\xa9")) {
318 face = "MS PMincho";
319 } else {
320 face = "MS Mincho";
321 }
322 return;
323 }
324 if (GetSubFontName(&face))
325 return;
326
327 if (!(pitch_family & FF_ROMAN) && weight > 400) {
328 face = "MS PGothic";
329 } else {
330 face = "MS PMincho";
331 }
332 }
333
MapFont(int weight,bool bItalic,FX_Charset charset,int pitch_family,const ByteString & face)334 void* CFX_Win32FontInfo::MapFont(int weight,
335 bool bItalic,
336 FX_Charset charset,
337 int pitch_family,
338 const ByteString& face) {
339 ByteString new_face = face;
340 for (int iBaseFont = 0; iBaseFont < 12; iBaseFont++) {
341 if (new_face == ByteStringView(kBase14Substs[iBaseFont].m_pName)) {
342 new_face = kBase14Substs[iBaseFont].m_pWinName;
343 weight = kBase14Substs[iBaseFont].m_bBold ? FW_BOLD : FW_NORMAL;
344 bItalic = kBase14Substs[iBaseFont].m_bItalic;
345 break;
346 }
347 }
348 if (charset == FX_Charset::kANSI || charset == FX_Charset::kSymbol)
349 charset = FX_Charset::kDefault;
350
351 int subst_pitch_family;
352 switch (charset) {
353 case FX_Charset::kShiftJIS:
354 subst_pitch_family = FF_ROMAN;
355 break;
356 case FX_Charset::kChineseTraditional:
357 case FX_Charset::kHangul:
358 case FX_Charset::kChineseSimplified:
359 subst_pitch_family = 0;
360 break;
361 default:
362 subst_pitch_family = pitch_family;
363 break;
364 }
365 HFONT hFont = Win32CreateFont(weight, bItalic, charset, subst_pitch_family,
366 new_face.c_str());
367 ByteString actual_new_face;
368 if (GetFaceName(hFont, &actual_new_face) &&
369 new_face.EqualNoCase(actual_new_face.AsStringView())) {
370 return hFont;
371 }
372
373 WideString wsFace = WideString::FromDefANSI(actual_new_face.AsStringView());
374 for (const Variant& variant : kVariantNames) {
375 if (new_face != variant.m_pFaceName)
376 continue;
377
378 const auto* pName =
379 reinterpret_cast<const unsigned short*>(variant.m_pVariantName);
380 size_t len = WideString::WStringLength(pName);
381 WideString wsName = WideString::FromUTF16LE(pName, len);
382 if (wsFace == wsName)
383 return hFont;
384 }
385 ::DeleteObject(hFont);
386 if (charset == FX_Charset::kDefault)
387 return nullptr;
388
389 switch (charset) {
390 case FX_Charset::kShiftJIS:
391 GetJapanesePreference(new_face, weight, pitch_family);
392 break;
393 case FX_Charset::kChineseSimplified:
394 GetGBPreference(new_face, weight, pitch_family);
395 break;
396 case FX_Charset::kHangul:
397 new_face = "Gulim";
398 break;
399 case FX_Charset::kChineseTraditional: {
400 static const char* const kMonospaceFonts[] = {"Microsoft YaHei",
401 "MingLiU"};
402 static const char* const kProportionalFonts[] = {"Microsoft JHengHei",
403 "PMingLiU"};
404 pdfium::span<const char* const> candidate_fonts =
405 new_face.Contains("MSung") ? kMonospaceFonts : kProportionalFonts;
406 return GetFontFromList(weight, bItalic, charset, subst_pitch_family,
407 candidate_fonts);
408 }
409 default:
410 break;
411 }
412 return Win32CreateFont(weight, bItalic, charset, subst_pitch_family,
413 new_face.c_str());
414 }
415
DeleteFont(void * hFont)416 void CFX_Win32FontInfo::DeleteFont(void* hFont) {
417 ::DeleteObject(hFont);
418 }
419
GetFontData(void * hFont,uint32_t table,pdfium::span<uint8_t> buffer)420 size_t CFX_Win32FontInfo::GetFontData(void* hFont,
421 uint32_t table,
422 pdfium::span<uint8_t> buffer) {
423 ScopedSelectObject select_object(m_hDC, static_cast<HFONT>(hFont));
424 table = FXSYS_UINT32_GET_MSBFIRST(reinterpret_cast<uint8_t*>(&table));
425 size_t size = ::GetFontData(m_hDC, table, 0, buffer.data(),
426 pdfium::base::checked_cast<DWORD>(buffer.size()));
427 return size != GDI_ERROR ? size : 0;
428 }
429
GetFaceName(void * hFont,ByteString * name)430 bool CFX_Win32FontInfo::GetFaceName(void* hFont, ByteString* name) {
431 ScopedSelectObject select_object(m_hDC, static_cast<HFONT>(hFont));
432 char facebuf[100];
433 if (::GetTextFaceA(m_hDC, std::size(facebuf), facebuf) == 0)
434 return false;
435
436 *name = facebuf;
437 return true;
438 }
439
GetFontCharset(void * hFont,FX_Charset * charset)440 bool CFX_Win32FontInfo::GetFontCharset(void* hFont, FX_Charset* charset) {
441 ScopedSelectObject select_object(m_hDC, static_cast<HFONT>(hFont));
442 TEXTMETRIC tm;
443 ::GetTextMetrics(m_hDC, &tm);
444 *charset = FX_GetCharsetFromInt(tm.tmCharSet);
445 return true;
446 }
447
448 } // namespace
449
450 CWin32Platform::CWin32Platform() = default;
451
452 CWin32Platform::~CWin32Platform() = default;
453
Init()454 void CWin32Platform::Init() {
455 if (pdfium::base::win::IsUser32AndGdi32Available())
456 m_GdiplusExt.Load();
457 }
458
459 std::unique_ptr<SystemFontInfoIface>
CreateDefaultSystemFontInfo()460 CWin32Platform::CreateDefaultSystemFontInfo() {
461 auto** user_paths = CFX_GEModule::Get()->GetUserFontPaths();
462 if (user_paths) {
463 auto font_info = std::make_unique<CFX_Win32FallbackFontInfo>();
464 for (; *user_paths; user_paths++)
465 font_info->AddPath(*user_paths);
466 return std::move(font_info);
467 }
468
469 if (pdfium::base::win::IsUser32AndGdi32Available())
470 return std::make_unique<CFX_Win32FontInfo>();
471
472 // Select the fallback font information class if GDI is disabled.
473 auto fallback_info = std::make_unique<CFX_Win32FallbackFontInfo>();
474 // Construct the font path manually, SHGetKnownFolderPath won't work under
475 // a restrictive sandbox.
476 CHAR windows_path[MAX_PATH] = {};
477 DWORD path_len = ::GetWindowsDirectoryA(windows_path, MAX_PATH);
478 if (path_len > 0 && path_len < MAX_PATH) {
479 ByteString fonts_path(windows_path);
480 fonts_path += "\\Fonts";
481 fallback_info->AddPath(fonts_path);
482 }
483 return fallback_info;
484 }
485
486 // static
487 std::unique_ptr<CFX_GEModule::PlatformIface>
Create()488 CFX_GEModule::PlatformIface::Create() {
489 return std::make_unique<CWin32Platform>();
490 }
491