xref: /aosp_15_r20/external/pdfium/core/fxge/win32/cwin32_platform.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
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