diff options
Diffstat (limited to 'core/fxge/win32/cwin32_platform.cpp')
-rw-r--r-- | core/fxge/win32/cwin32_platform.cpp | 490 |
1 files changed, 490 insertions, 0 deletions
diff --git a/core/fxge/win32/cwin32_platform.cpp b/core/fxge/win32/cwin32_platform.cpp new file mode 100644 index 000000000..5794e2ed8 --- /dev/null +++ b/core/fxge/win32/cwin32_platform.cpp @@ -0,0 +1,490 @@ +// Copyright 2020 The PDFium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxge/win32/cwin32_platform.h" + +#include <iterator> +#include <memory> +#include <utility> + +#include "core/fxcrt/fx_codepage.h" +#include "core/fxge/cfx_folderfontinfo.h" +#include "core/fxge/cfx_gemodule.h" +#include "third_party/base/check.h" +#include "third_party/base/numerics/safe_conversions.h" +#include "third_party/base/span.h" +#include "third_party/base/win/scoped_select_object.h" +#include "third_party/base/win/win_util.h" + +namespace { + +using ScopedSelectObject = pdfium::base::win::ScopedSelectObject; + +struct Variant { + const char* m_pFaceName; + const char* m_pVariantName; // Note: UTF-16LE terminator required. +}; + +constexpr Variant kVariantNames[] = { + {"DFKai-SB", "\x19\x6A\x77\x69\xD4\x9A\x00\x00"}, +}; + +struct Substs { + const char* m_pName; + const char* m_pWinName; + bool m_bBold; + bool m_bItalic; +}; + +constexpr Substs kBase14Substs[] = { + {"Courier", "Courier New", false, false}, + {"Courier-Bold", "Courier New", true, false}, + {"Courier-BoldOblique", "Courier New", true, true}, + {"Courier-Oblique", "Courier New", false, true}, + {"Helvetica", "Arial", false, false}, + {"Helvetica-Bold", "Arial", true, false}, + {"Helvetica-BoldOblique", "Arial", true, true}, + {"Helvetica-Oblique", "Arial", false, true}, + {"Times-Roman", "Times New Roman", false, false}, + {"Times-Bold", "Times New Roman", true, false}, + {"Times-BoldItalic", "Times New Roman", true, true}, + {"Times-Italic", "Times New Roman", false, true}, +}; + +struct FontNameMap { + const char* m_pSubFontName; + const char* m_pSrcFontName; +}; + +constexpr FontNameMap kJpFontNameMap[] = { + {"MS Mincho", "Heiseimin-W3"}, + {"MS Gothic", "Jun101-Light"}, +}; + +bool GetSubFontName(ByteString* name) { + for (size_t i = 0; i < std::size(kJpFontNameMap); ++i) { + if (!FXSYS_stricmp(name->c_str(), kJpFontNameMap[i].m_pSrcFontName)) { + *name = kJpFontNameMap[i].m_pSubFontName; + return true; + } + } + return false; +} + +// Wraps CreateFontA() so callers don't have to specify all the arguments. +HFONT Win32CreateFont(int weight, + bool italic, + FX_Charset charset, + int pitch_family, + const char* face) { + return ::CreateFontA(-10, 0, 0, 0, weight, italic, 0, 0, + static_cast<int>(charset), OUT_TT_ONLY_PRECIS, 0, 0, + pitch_family, face); +} + +class CFX_Win32FallbackFontInfo final : public CFX_FolderFontInfo { + public: + CFX_Win32FallbackFontInfo() = default; + ~CFX_Win32FallbackFontInfo() override = default; + + // CFX_FolderFontInfo: + void* MapFont(int weight, + bool bItalic, + FX_Charset charset, + int pitch_family, + const ByteString& face) override; +}; + +class CFX_Win32FontInfo final : public SystemFontInfoIface { + public: + CFX_Win32FontInfo(); + ~CFX_Win32FontInfo() override; + + // SystemFontInfoIface: + bool EnumFontList(CFX_FontMapper* pMapper) override; + void* MapFont(int weight, + bool bItalic, + FX_Charset charset, + int pitch_family, + const ByteString& face) override; + void* GetFont(const ByteString& face) override { return nullptr; } + size_t GetFontData(void* hFont, + uint32_t table, + pdfium::span<uint8_t> buffer) override; + bool GetFaceName(void* hFont, ByteString* name) override; + bool GetFontCharset(void* hFont, FX_Charset* charset) override; + void DeleteFont(void* hFont) override; + + void AddInstalledFont(const LOGFONTA* plf, uint32_t font_type); + + private: + bool IsSupportedFont(const LOGFONTA* plf); + void GetGBPreference(ByteString& face, int weight, int pitch_family); + void GetJapanesePreference(ByteString& face, int weight, int pitch_family); + ByteString FindFont(const ByteString& name); + void* GetFontFromList(int weight, + bool italic, + FX_Charset charset, + int pitch_family, + pdfium::span<const char* const> font_faces); + + const HDC m_hDC; + UnownedPtr<CFX_FontMapper> m_pMapper; + ByteString m_LastFamily; + ByteString m_KaiTi; + ByteString m_FangSong; +}; + +int CALLBACK FontEnumProc(const LOGFONTA* plf, + const TEXTMETRICA* lpntme, + uint32_t font_type, + LPARAM lParam) { + CFX_Win32FontInfo* pFontInfo = reinterpret_cast<CFX_Win32FontInfo*>(lParam); + pFontInfo->AddInstalledFont(plf, font_type); + return 1; +} + +CFX_Win32FontInfo::CFX_Win32FontInfo() : m_hDC(CreateCompatibleDC(nullptr)) {} + +CFX_Win32FontInfo::~CFX_Win32FontInfo() { + DeleteDC(m_hDC); +} + +bool CFX_Win32FontInfo::IsSupportedFont(const LOGFONTA* plf) { + HFONT hFont = CreateFontIndirectA(plf); + bool ret = false; + size_t font_size = GetFontData(hFont, 0, {}); + if (font_size != GDI_ERROR && font_size >= sizeof(uint32_t)) { + uint32_t header; + auto span = pdfium::as_writable_bytes(pdfium::make_span(&header, 1)); + GetFontData(hFont, 0, span); + header = FXSYS_UINT32_GET_MSBFIRST(span); + ret = header == FXBSTR_ID('O', 'T', 'T', 'O') || + header == FXBSTR_ID('t', 't', 'c', 'f') || + header == FXBSTR_ID('t', 'r', 'u', 'e') || header == 0x00010000 || + header == 0x00020000 || + (header & 0xFFFF0000) == FXBSTR_ID(0x80, 0x01, 0x00, 0x00) || + (header & 0xFFFF0000) == FXBSTR_ID('%', '!', 0, 0); + } + DeleteFont(hFont); + return ret; +} + +void CFX_Win32FontInfo::AddInstalledFont(const LOGFONTA* plf, + uint32_t font_type) { + ByteString name(plf->lfFaceName); + if (name.GetLength() > 0 && name[0] == '@') + return; + + if (name == m_LastFamily) { + m_pMapper->AddInstalledFont(name, FX_GetCharsetFromInt(plf->lfCharSet)); + return; + } + if (!(font_type & TRUETYPE_FONTTYPE)) { + if (!(font_type & DEVICE_FONTTYPE) || !IsSupportedFont(plf)) + return; + } + + m_pMapper->AddInstalledFont(name, FX_GetCharsetFromInt(plf->lfCharSet)); + m_LastFamily = name; +} + +bool CFX_Win32FontInfo::EnumFontList(CFX_FontMapper* pMapper) { + m_pMapper = pMapper; + LOGFONTA lf; + memset(&lf, 0, sizeof(LOGFONTA)); + lf.lfCharSet = static_cast<int>(FX_Charset::kDefault); + lf.lfFaceName[0] = 0; + lf.lfPitchAndFamily = 0; + EnumFontFamiliesExA(m_hDC, &lf, reinterpret_cast<FONTENUMPROCA>(FontEnumProc), + reinterpret_cast<uintptr_t>(this), 0); + return true; +} + +ByteString CFX_Win32FontInfo::FindFont(const ByteString& name) { + if (!m_pMapper) + return name; + + absl::optional<ByteString> maybe_installed = + m_pMapper->InstalledFontNameStartingWith(name); + if (maybe_installed.has_value()) + return maybe_installed.value(); + + absl::optional<ByteString> maybe_localized = + m_pMapper->LocalizedFontNameStartingWith(name); + if (maybe_localized.has_value()) + return maybe_localized.value(); + + return ByteString(); +} + +void* CFX_Win32FontInfo::GetFontFromList( + int weight, + bool italic, + FX_Charset charset, + int pitch_family, + pdfium::span<const char* const> font_faces) { + DCHECK(!font_faces.empty()); + + // Initialization not needed because of DCHECK() above and the assignment in + // the for-loop below. + HFONT font; + for (const char* face : font_faces) { + font = Win32CreateFont(weight, italic, charset, pitch_family, face); + ByteString actual_face; + if (GetFaceName(font, &actual_face) && actual_face.EqualNoCase(face)) + break; + } + return font; +} + +void* CFX_Win32FallbackFontInfo::MapFont(int weight, + bool bItalic, + FX_Charset charset, + int pitch_family, + const ByteString& face) { + void* font = GetSubstFont(face); + if (font) + return font; + + bool bCJK = true; + switch (charset) { + case FX_Charset::kShiftJIS: + case FX_Charset::kChineseSimplified: + case FX_Charset::kChineseTraditional: + case FX_Charset::kHangul: + break; + default: + bCJK = false; + break; + } + return FindFont(weight, bItalic, charset, pitch_family, face, !bCJK); +} + +void CFX_Win32FontInfo::GetGBPreference(ByteString& face, + int weight, + int pitch_family) { + if (face.Contains("KaiTi") || face.Contains("\xbf\xac")) { + if (m_KaiTi.IsEmpty()) { + m_KaiTi = FindFont("KaiTi"); + if (m_KaiTi.IsEmpty()) { + m_KaiTi = "SimSun"; + } + } + face = m_KaiTi; + } else if (face.Contains("FangSong") || face.Contains("\xb7\xc2\xcb\xce")) { + if (m_FangSong.IsEmpty()) { + m_FangSong = FindFont("FangSong"); + if (m_FangSong.IsEmpty()) { + m_FangSong = "SimSun"; + } + } + face = m_FangSong; + } else if (face.Contains("SimSun") || face.Contains("\xcb\xce")) { + face = "SimSun"; + } else if (face.Contains("SimHei") || face.Contains("\xba\xda")) { + face = "SimHei"; + } else if (!(pitch_family & FF_ROMAN) && weight > 550) { + face = "SimHei"; + } else { + face = "SimSun"; + } +} + +void CFX_Win32FontInfo::GetJapanesePreference(ByteString& face, + int weight, + int pitch_family) { + if (face.Contains("Gothic") || + face.Contains("\x83\x53\x83\x56\x83\x62\x83\x4e")) { + if (face.Contains("PGothic") || + face.Contains("\x82\x6f\x83\x53\x83\x56\x83\x62\x83\x4e")) { + face = "MS PGothic"; + } else if (face.Contains("UI Gothic")) { + face = "MS UI Gothic"; + } else { + if (face.Contains("HGSGothicM") || face.Contains("HGMaruGothicMPRO")) { + face = "MS PGothic"; + } else { + face = "MS Gothic"; + } + } + return; + } + if (face.Contains("Mincho") || face.Contains("\x96\xbe\x92\xa9")) { + if (face.Contains("PMincho") || face.Contains("\x82\x6f\x96\xbe\x92\xa9")) { + face = "MS PMincho"; + } else { + face = "MS Mincho"; + } + return; + } + if (GetSubFontName(&face)) + return; + + if (!(pitch_family & FF_ROMAN) && weight > 400) { + face = "MS PGothic"; + } else { + face = "MS PMincho"; + } +} + +void* CFX_Win32FontInfo::MapFont(int weight, + bool bItalic, + FX_Charset charset, + int pitch_family, + const ByteString& face) { + ByteString new_face = face; + for (int iBaseFont = 0; iBaseFont < 12; iBaseFont++) { + if (new_face == ByteStringView(kBase14Substs[iBaseFont].m_pName)) { + new_face = kBase14Substs[iBaseFont].m_pWinName; + weight = kBase14Substs[iBaseFont].m_bBold ? FW_BOLD : FW_NORMAL; + bItalic = kBase14Substs[iBaseFont].m_bItalic; + break; + } + } + if (charset == FX_Charset::kANSI || charset == FX_Charset::kSymbol) + charset = FX_Charset::kDefault; + + int subst_pitch_family; + switch (charset) { + case FX_Charset::kShiftJIS: + subst_pitch_family = FF_ROMAN; + break; + case FX_Charset::kChineseTraditional: + case FX_Charset::kHangul: + case FX_Charset::kChineseSimplified: + subst_pitch_family = 0; + break; + default: + subst_pitch_family = pitch_family; + break; + } + HFONT hFont = Win32CreateFont(weight, bItalic, charset, subst_pitch_family, + new_face.c_str()); + ByteString actual_new_face; + if (GetFaceName(hFont, &actual_new_face) && + new_face.EqualNoCase(actual_new_face.AsStringView())) { + return hFont; + } + + WideString wsFace = WideString::FromDefANSI(actual_new_face.AsStringView()); + for (const Variant& variant : kVariantNames) { + if (new_face != variant.m_pFaceName) + continue; + + const auto* pName = + reinterpret_cast<const unsigned short*>(variant.m_pVariantName); + size_t len = WideString::WStringLength(pName); + WideString wsName = WideString::FromUTF16LE(pName, len); + if (wsFace == wsName) + return hFont; + } + ::DeleteObject(hFont); + if (charset == FX_Charset::kDefault) + return nullptr; + + switch (charset) { + case FX_Charset::kShiftJIS: + GetJapanesePreference(new_face, weight, pitch_family); + break; + case FX_Charset::kChineseSimplified: + GetGBPreference(new_face, weight, pitch_family); + break; + case FX_Charset::kHangul: + new_face = "Gulim"; + break; + case FX_Charset::kChineseTraditional: { + static const char* const kMonospaceFonts[] = {"Microsoft YaHei", + "MingLiU"}; + static const char* const kProportionalFonts[] = {"Microsoft JHengHei", + "PMingLiU"}; + pdfium::span<const char* const> candidate_fonts = + new_face.Contains("MSung") ? kMonospaceFonts : kProportionalFonts; + return GetFontFromList(weight, bItalic, charset, subst_pitch_family, + candidate_fonts); + } + default: + break; + } + return Win32CreateFont(weight, bItalic, charset, subst_pitch_family, + new_face.c_str()); +} + +void CFX_Win32FontInfo::DeleteFont(void* hFont) { + ::DeleteObject(hFont); +} + +size_t CFX_Win32FontInfo::GetFontData(void* hFont, + uint32_t table, + pdfium::span<uint8_t> buffer) { + ScopedSelectObject select_object(m_hDC, static_cast<HFONT>(hFont)); + table = FXSYS_UINT32_GET_MSBFIRST(reinterpret_cast<uint8_t*>(&table)); + size_t size = ::GetFontData(m_hDC, table, 0, buffer.data(), + pdfium::base::checked_cast<DWORD>(buffer.size())); + return size != GDI_ERROR ? size : 0; +} + +bool CFX_Win32FontInfo::GetFaceName(void* hFont, ByteString* name) { + ScopedSelectObject select_object(m_hDC, static_cast<HFONT>(hFont)); + char facebuf[100]; + if (::GetTextFaceA(m_hDC, std::size(facebuf), facebuf) == 0) + return false; + + *name = facebuf; + return true; +} + +bool CFX_Win32FontInfo::GetFontCharset(void* hFont, FX_Charset* charset) { + ScopedSelectObject select_object(m_hDC, static_cast<HFONT>(hFont)); + TEXTMETRIC tm; + ::GetTextMetrics(m_hDC, &tm); + *charset = FX_GetCharsetFromInt(tm.tmCharSet); + return true; +} + +} // namespace + +CWin32Platform::CWin32Platform() = default; + +CWin32Platform::~CWin32Platform() = default; + +void CWin32Platform::Init() { + if (pdfium::base::win::IsUser32AndGdi32Available()) + m_GdiplusExt.Load(); +} + +std::unique_ptr<SystemFontInfoIface> +CWin32Platform::CreateDefaultSystemFontInfo() { + auto** user_paths = CFX_GEModule::Get()->GetUserFontPaths(); + if (user_paths) { + auto font_info = std::make_unique<CFX_Win32FallbackFontInfo>(); + for (; *user_paths; user_paths++) + font_info->AddPath(*user_paths); + return std::move(font_info); + } + + if (pdfium::base::win::IsUser32AndGdi32Available()) + return std::make_unique<CFX_Win32FontInfo>(); + + // Select the fallback font information class if GDI is disabled. + auto fallback_info = std::make_unique<CFX_Win32FallbackFontInfo>(); + // Construct the font path manually, SHGetKnownFolderPath won't work under + // a restrictive sandbox. + CHAR windows_path[MAX_PATH] = {}; + DWORD path_len = ::GetWindowsDirectoryA(windows_path, MAX_PATH); + if (path_len > 0 && path_len < MAX_PATH) { + ByteString fonts_path(windows_path); + fonts_path += "\\Fonts"; + fallback_info->AddPath(fonts_path); + } + return fallback_info; +} + +// static +std::unique_ptr<CFX_GEModule::PlatformIface> +CFX_GEModule::PlatformIface::Create() { + return std::make_unique<CWin32Platform>(); +} |