| // Copyright 2016 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/cfx_fontmapper.h" | |
| #include <stdint.h> | |
| #include <algorithm> | |
| #include <memory> | |
| #include <utility> | |
| #include "build/build_config.h" | |
| #include "core/fxcrt/data_vector.h" | |
| #include "core/fxcrt/fx_codepage.h" | |
| #include "core/fxcrt/fx_extension.h" | |
| #include "core/fxcrt/fx_memory.h" | |
| #include "core/fxcrt/fx_memory_wrappers.h" | |
| #include "core/fxcrt/stl_util.h" | |
| #include "core/fxcrt/unowned_ptr_exclusion.h" | |
| #include "core/fxge/cfx_fontmgr.h" | |
| #include "core/fxge/cfx_substfont.h" | |
| #include "core/fxge/fx_font.h" | |
| #include "core/fxge/systemfontinfo_iface.h" | |
| #include "third_party/base/check_op.h" | |
| #include "third_party/base/containers/contains.h" | |
| #include "third_party/base/cxx17_backports.h" | |
| namespace { | |
| static_assert(CFX_FontMapper::kLast + 1 == CFX_FontMapper::kNumStandardFonts, | |
| "StandardFont enum count mismatch"); | |
| const char* const kBase14FontNames[CFX_FontMapper::kNumStandardFonts] = { | |
| "Courier", | |
| "Courier-Bold", | |
| "Courier-BoldOblique", | |
| "Courier-Oblique", | |
| "Helvetica", | |
| "Helvetica-Bold", | |
| "Helvetica-BoldOblique", | |
| "Helvetica-Oblique", | |
| "Times-Roman", | |
| "Times-Bold", | |
| "Times-BoldItalic", | |
| "Times-Italic", | |
| "Symbol", | |
| "ZapfDingbats", | |
| }; | |
| struct AltFontName { | |
| const char* m_pName; // Raw, POD struct. | |
| CFX_FontMapper::StandardFont m_Index; | |
| }; | |
| constexpr AltFontName kAltFontNames[] = { | |
| {"Arial", CFX_FontMapper::kHelvetica}, | |
| {"Arial,Bold", CFX_FontMapper::kHelveticaBold}, | |
| {"Arial,BoldItalic", CFX_FontMapper::kHelveticaBoldOblique}, | |
| {"Arial,Italic", CFX_FontMapper::kHelveticaOblique}, | |
| {"Arial-Bold", CFX_FontMapper::kHelveticaBold}, | |
| {"Arial-BoldItalic", CFX_FontMapper::kHelveticaBoldOblique}, | |
| {"Arial-BoldItalicMT", CFX_FontMapper::kHelveticaBoldOblique}, | |
| {"Arial-BoldMT", CFX_FontMapper::kHelveticaBold}, | |
| {"Arial-Italic", CFX_FontMapper::kHelveticaOblique}, | |
| {"Arial-ItalicMT", CFX_FontMapper::kHelveticaOblique}, | |
| {"ArialBold", CFX_FontMapper::kHelveticaBold}, | |
| {"ArialBoldItalic", CFX_FontMapper::kHelveticaBoldOblique}, | |
| {"ArialItalic", CFX_FontMapper::kHelveticaOblique}, | |
| {"ArialMT", CFX_FontMapper::kHelvetica}, | |
| {"ArialMT,Bold", CFX_FontMapper::kHelveticaBold}, | |
| {"ArialMT,BoldItalic", CFX_FontMapper::kHelveticaBoldOblique}, | |
| {"ArialMT,Italic", CFX_FontMapper::kHelveticaOblique}, | |
| {"ArialRoundedMTBold", CFX_FontMapper::kHelveticaBold}, | |
| {"Courier", CFX_FontMapper::kCourier}, | |
| {"Courier,Bold", CFX_FontMapper::kCourierBold}, | |
| {"Courier,BoldItalic", CFX_FontMapper::kCourierBoldOblique}, | |
| {"Courier,Italic", CFX_FontMapper::kCourierOblique}, | |
| {"Courier-Bold", CFX_FontMapper::kCourierBold}, | |
| {"Courier-BoldOblique", CFX_FontMapper::kCourierBoldOblique}, | |
| {"Courier-Oblique", CFX_FontMapper::kCourierOblique}, | |
| {"CourierBold", CFX_FontMapper::kCourierBold}, | |
| {"CourierBoldItalic", CFX_FontMapper::kCourierBoldOblique}, | |
| {"CourierItalic", CFX_FontMapper::kCourierOblique}, | |
| {"CourierNew", CFX_FontMapper::kCourier}, | |
| {"CourierNew,Bold", CFX_FontMapper::kCourierBold}, | |
| {"CourierNew,BoldItalic", CFX_FontMapper::kCourierBoldOblique}, | |
| {"CourierNew,Italic", CFX_FontMapper::kCourierOblique}, | |
| {"CourierNew-Bold", CFX_FontMapper::kCourierBold}, | |
| {"CourierNew-BoldItalic", CFX_FontMapper::kCourierBoldOblique}, | |
| {"CourierNew-Italic", CFX_FontMapper::kCourierOblique}, | |
| {"CourierNewBold", CFX_FontMapper::kCourierBold}, | |
| {"CourierNewBoldItalic", CFX_FontMapper::kCourierBoldOblique}, | |
| {"CourierNewItalic", CFX_FontMapper::kCourierOblique}, | |
| {"CourierNewPS-BoldItalicMT", CFX_FontMapper::kCourierBoldOblique}, | |
| {"CourierNewPS-BoldMT", CFX_FontMapper::kCourierBold}, | |
| {"CourierNewPS-ItalicMT", CFX_FontMapper::kCourierOblique}, | |
| {"CourierNewPSMT", CFX_FontMapper::kCourier}, | |
| {"CourierStd", CFX_FontMapper::kCourier}, | |
| {"CourierStd-Bold", CFX_FontMapper::kCourierBold}, | |
| {"CourierStd-BoldOblique", CFX_FontMapper::kCourierBoldOblique}, | |
| {"CourierStd-Oblique", CFX_FontMapper::kCourierOblique}, | |
| {"Helvetica", CFX_FontMapper::kHelvetica}, | |
| {"Helvetica,Bold", CFX_FontMapper::kHelveticaBold}, | |
| {"Helvetica,BoldItalic", CFX_FontMapper::kHelveticaBoldOblique}, | |
| {"Helvetica,Italic", CFX_FontMapper::kHelveticaOblique}, | |
| {"Helvetica-Bold", CFX_FontMapper::kHelveticaBold}, | |
| {"Helvetica-BoldItalic", CFX_FontMapper::kHelveticaBoldOblique}, | |
| {"Helvetica-BoldOblique", CFX_FontMapper::kHelveticaBoldOblique}, | |
| {"Helvetica-Italic", CFX_FontMapper::kHelveticaOblique}, | |
| {"Helvetica-Oblique", CFX_FontMapper::kHelveticaOblique}, | |
| {"HelveticaBold", CFX_FontMapper::kHelveticaBold}, | |
| {"HelveticaBoldItalic", CFX_FontMapper::kHelveticaBoldOblique}, | |
| {"HelveticaItalic", CFX_FontMapper::kHelveticaOblique}, | |
| {"Symbol", CFX_FontMapper::kSymbol}, | |
| {"SymbolMT", CFX_FontMapper::kSymbol}, | |
| {"Times-Bold", CFX_FontMapper::kTimesBold}, | |
| {"Times-BoldItalic", CFX_FontMapper::kTimesBoldOblique}, | |
| {"Times-Italic", CFX_FontMapper::kTimesOblique}, | |
| {"Times-Roman", CFX_FontMapper::kTimes}, | |
| {"TimesBold", CFX_FontMapper::kTimesBold}, | |
| {"TimesBoldItalic", CFX_FontMapper::kTimesBoldOblique}, | |
| {"TimesItalic", CFX_FontMapper::kTimesOblique}, | |
| {"TimesNewRoman", CFX_FontMapper::kTimes}, | |
| {"TimesNewRoman,Bold", CFX_FontMapper::kTimesBold}, | |
| {"TimesNewRoman,BoldItalic", CFX_FontMapper::kTimesBoldOblique}, | |
| {"TimesNewRoman,Italic", CFX_FontMapper::kTimesOblique}, | |
| {"TimesNewRoman-Bold", CFX_FontMapper::kTimesBold}, | |
| {"TimesNewRoman-BoldItalic", CFX_FontMapper::kTimesBoldOblique}, | |
| {"TimesNewRoman-Italic", CFX_FontMapper::kTimesOblique}, | |
| {"TimesNewRomanBold", CFX_FontMapper::kTimesBold}, | |
| {"TimesNewRomanBoldItalic", CFX_FontMapper::kTimesBoldOblique}, | |
| {"TimesNewRomanItalic", CFX_FontMapper::kTimesOblique}, | |
| {"TimesNewRomanPS", CFX_FontMapper::kTimes}, | |
| {"TimesNewRomanPS-Bold", CFX_FontMapper::kTimesBold}, | |
| {"TimesNewRomanPS-BoldItalic", CFX_FontMapper::kTimesBoldOblique}, | |
| {"TimesNewRomanPS-BoldItalicMT", CFX_FontMapper::kTimesBoldOblique}, | |
| {"TimesNewRomanPS-BoldMT", CFX_FontMapper::kTimesBold}, | |
| {"TimesNewRomanPS-Italic", CFX_FontMapper::kTimesOblique}, | |
| {"TimesNewRomanPS-ItalicMT", CFX_FontMapper::kTimesOblique}, | |
| {"TimesNewRomanPSMT", CFX_FontMapper::kTimes}, | |
| {"TimesNewRomanPSMT,Bold", CFX_FontMapper::kTimesBold}, | |
| {"TimesNewRomanPSMT,BoldItalic", CFX_FontMapper::kTimesBoldOblique}, | |
| {"TimesNewRomanPSMT,Italic", CFX_FontMapper::kTimesOblique}, | |
| {"ZapfDingbats", CFX_FontMapper::kDingbats}, | |
| }; | |
| struct AltFontFamily { | |
| const char* m_pFontName; // Raw, POD struct. | |
| const char* m_pFontFamily; // Raw, POD struct. | |
| }; | |
| constexpr AltFontFamily kAltFontFamilies[] = { | |
| {"AGaramondPro", "Adobe Garamond Pro"}, | |
| {"BankGothicBT-Medium", "BankGothic Md BT"}, | |
| {"ForteMT", "Forte"}, | |
| }; | |
| #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || defined(OS_ASMJS) | |
| const char kNarrowFamily[] = "LiberationSansNarrow"; | |
| #elif BUILDFLAG(IS_ANDROID) | |
| const char kNarrowFamily[] = "RobotoCondensed"; | |
| #else | |
| const char kNarrowFamily[] = "ArialNarrow"; | |
| #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || defined(OS_ASMJS) | |
| ByteString TT_NormalizeName(ByteString norm) { | |
| norm.Remove(' '); | |
| norm.Remove('-'); | |
| norm.Remove(','); | |
| auto pos = norm.Find('+'); | |
| if (pos.has_value() && pos.value() != 0) | |
| norm = norm.First(pos.value()); | |
| norm.MakeLower(); | |
| return norm; | |
| } | |
| void GetFontFamily(uint32_t nStyle, ByteString* fontName) { | |
| if (fontName->Contains("Script")) { | |
| if (FontStyleIsForceBold(nStyle)) | |
| *fontName = "ScriptMTBold"; | |
| else if (fontName->Contains("Palace")) | |
| *fontName = "PalaceScriptMT"; | |
| else if (fontName->Contains("French")) | |
| *fontName = "FrenchScriptMT"; | |
| else if (fontName->Contains("FreeStyle")) | |
| *fontName = "FreeStyleScript"; | |
| return; | |
| } | |
| for (const auto& alternate : kAltFontFamilies) { | |
| if (fontName->Contains(alternate.m_pFontName)) { | |
| *fontName = alternate.m_pFontFamily; | |
| return; | |
| } | |
| } | |
| } | |
| ByteString ParseStyle(const ByteString& bsStyle, size_t iStart) { | |
| ByteStringView bsRegion = bsStyle.AsStringView().Substr(iStart); | |
| size_t iIndex = bsRegion.Find(',').value_or(bsRegion.GetLength()); | |
| return ByteString(bsRegion.First(iIndex)); | |
| } | |
| struct FX_FontStyle { | |
| const char* name; | |
| size_t len; | |
| uint32_t style; | |
| }; | |
| constexpr FX_FontStyle kFontStyles[] = { | |
| {"Regular", 7, FXFONT_NORMAL}, | |
| {"Reg", 3, FXFONT_NORMAL}, | |
| {"BoldItalic", 10, FXFONT_FORCE_BOLD | FXFONT_ITALIC}, | |
| {"Italic", 6, FXFONT_ITALIC}, | |
| {"Bold", 4, FXFONT_FORCE_BOLD}, | |
| }; | |
| const FX_FontStyle* GetStyleType(ByteStringView font_name, | |
| bool reverse_search) { | |
| if (font_name.IsEmpty()) | |
| return nullptr; | |
| for (const FX_FontStyle& style : kFontStyles) { | |
| if (style.len > font_name.GetLength()) | |
| continue; | |
| ByteStringView style_view = | |
| reverse_search ? font_name.Last(style.len) : font_name.First(style.len); | |
| if (style_view == style.name) | |
| return &style; | |
| } | |
| return nullptr; | |
| } | |
| bool ParseStyles(const ByteString& style_str, | |
| bool* is_style_available, | |
| int* weight, | |
| uint32_t* style) { | |
| if (style_str.IsEmpty()) | |
| return false; | |
| size_t i = 0; | |
| bool is_first_item = true; | |
| while (i < style_str.GetLength()) { | |
| ByteString buf = ParseStyle(style_str, i); | |
| const FX_FontStyle* style_result = | |
| GetStyleType(buf.AsStringView(), /*reverse_search=*/false); | |
| if ((i && !*is_style_available) || (!i && !style_result)) | |
| return true; | |
| uint32_t parsed_style; | |
| if (style_result) { | |
| *is_style_available = true; | |
| parsed_style = style_result->style; | |
| } else { | |
| parsed_style = FXFONT_NORMAL; | |
| } | |
| if (FontStyleIsForceBold(parsed_style)) { | |
| // If we're already bold, then we're double bold, use special weight. | |
| if (FontStyleIsForceBold(*style)) { | |
| *weight = FXFONT_FW_BOLD_BOLD; | |
| } else { | |
| *weight = FXFONT_FW_BOLD; | |
| *style |= FXFONT_FORCE_BOLD; | |
| } | |
| is_first_item = false; | |
| } | |
| if (FontStyleIsItalic(parsed_style) && FontStyleIsForceBold(parsed_style)) { | |
| *style |= FXFONT_ITALIC; | |
| } else if (FontStyleIsItalic(parsed_style)) { | |
| if (!is_first_item) | |
| return true; | |
| *style |= FXFONT_ITALIC; | |
| break; | |
| } | |
| i += buf.GetLength() + 1; | |
| } | |
| return false; | |
| } | |
| bool CheckSupportThirdPartFont(const ByteString& name, int* pitch_family) { | |
| if (name != "MyriadPro") | |
| return false; | |
| *pitch_family &= ~FXFONT_FF_ROMAN; | |
| return true; | |
| } | |
| uint32_t GetStyleFromBaseFont(int base_font) { | |
| int pos = base_font % 4; | |
| uint32_t style = FXFONT_NORMAL; | |
| if (pos == 1 || pos == 2) | |
| style |= FXFONT_FORCE_BOLD; | |
| if (pos / 2) | |
| style |= FXFONT_ITALIC; | |
| return style; | |
| } | |
| int GetPitchFamilyFromBaseFont(int base_font) { | |
| if (base_font < 4) | |
| return FXFONT_FF_FIXEDPITCH; | |
| if (base_font >= 8) | |
| return FXFONT_FF_ROMAN; | |
| return 0; | |
| } | |
| int GetPitchFamilyFromFlags(uint32_t flags) { | |
| int pitch_family = 0; | |
| if (FontStyleIsSerif(flags)) | |
| pitch_family |= FXFONT_FF_ROMAN; | |
| if (FontStyleIsScript(flags)) | |
| pitch_family |= FXFONT_FF_SCRIPT; | |
| if (FontStyleIsFixedPitch(flags)) | |
| pitch_family |= FXFONT_FF_FIXEDPITCH; | |
| return pitch_family; | |
| } | |
| int AdjustBaseFontForStyle(int base_font, uint32_t style) { | |
| if (!style || (base_font % 4)) | |
| return base_font; | |
| if (FontStyleIsForceBold(style) && FontStyleIsItalic(style)) | |
| base_font += 2; | |
| else if (FontStyleIsForceBold(style)) | |
| base_font += 1; | |
| else if (FontStyleIsItalic(style)) | |
| base_font += 3; | |
| return base_font; | |
| } | |
| FX_Charset GetCharset(FX_CodePage code_page, int base_font, uint32_t flags) { | |
| if (code_page != FX_CodePage::kDefANSI) | |
| return FX_GetCharsetFromCodePage(code_page); | |
| if (FontStyleIsSymbolic(flags) && | |
| base_font == CFX_FontMapper::kNumStandardFonts) { | |
| return FX_Charset::kSymbol; | |
| } | |
| return FX_Charset::kANSI; | |
| } | |
| bool IsStrUpper(const ByteString& str) { | |
| for (size_t i = 0; i < str.GetLength(); ++i) { | |
| if (!FXSYS_IsUpperASCII(str[i])) | |
| return false; | |
| } | |
| return true; | |
| } | |
| void RemoveSubsettedFontPrefix(ByteString* subst_name) { | |
| constexpr size_t kPrefixLength = 6; | |
| if (subst_name->GetLength() > kPrefixLength && | |
| (*subst_name)[kPrefixLength] == '+' && | |
| IsStrUpper(subst_name->First(kPrefixLength))) { | |
| *subst_name = | |
| subst_name->Last(subst_name->GetLength() - (kPrefixLength + 1)); | |
| } | |
| } | |
| ByteString GetSubstName(const ByteString& name, bool is_truetype) { | |
| ByteString subst_name = name; | |
| if (is_truetype && name.Front() == '@') | |
| subst_name.Delete(0); | |
| else | |
| subst_name.Remove(' '); | |
| RemoveSubsettedFontPrefix(&subst_name); | |
| CFX_FontMapper::GetStandardFontName(&subst_name); | |
| return subst_name; | |
| } | |
| bool IsNarrowFontName(const ByteString& name) { | |
| static const char kNarrowFonts[][10] = {"Narrow", "Condensed"}; | |
| for (const char* font : kNarrowFonts) { | |
| absl::optional<size_t> pos = name.Find(font); | |
| if (pos.has_value() && pos.value() != 0) | |
| return true; | |
| } | |
| return false; | |
| } | |
| class ScopedFontDeleter { | |
| public: | |
| FX_STACK_ALLOCATED(); | |
| ScopedFontDeleter(SystemFontInfoIface* font_info, void* font) | |
| : font_info_(font_info), font_(font) {} | |
| ~ScopedFontDeleter() { font_info_->DeleteFont(font_); } | |
| private: | |
| UnownedPtr<SystemFontInfoIface> const font_info_; | |
| UNOWNED_PTR_EXCLUSION void* const font_; // void type incompatible. | |
| }; | |
| } // namespace | |
| CFX_FontMapper::CFX_FontMapper(CFX_FontMgr* mgr) : m_pFontMgr(mgr) {} | |
| CFX_FontMapper::~CFX_FontMapper() = default; | |
| void CFX_FontMapper::SetSystemFontInfo( | |
| std::unique_ptr<SystemFontInfoIface> pFontInfo) { | |
| if (!pFontInfo) | |
| return; | |
| m_bListLoaded = false; | |
| m_pFontInfo = std::move(pFontInfo); | |
| } | |
| std::unique_ptr<SystemFontInfoIface> CFX_FontMapper::TakeSystemFontInfo() { | |
| return std::move(m_pFontInfo); | |
| } | |
| uint32_t CFX_FontMapper::GetChecksumFromTT(void* font_handle) { | |
| uint32_t buffer[256]; | |
| m_pFontInfo->GetFontData( | |
| font_handle, kTableTTCF, | |
| pdfium::as_writable_bytes(pdfium::make_span(buffer))); | |
| uint32_t checksum = 0; | |
| for (auto x : buffer) | |
| checksum += x; | |
| return checksum; | |
| } | |
| ByteString CFX_FontMapper::GetPSNameFromTT(void* font_handle) { | |
| size_t size = m_pFontInfo->GetFontData(font_handle, kTableNAME, {}); | |
| if (!size) | |
| return ByteString(); | |
| DataVector<uint8_t> buffer(size); | |
| size_t bytes_read = m_pFontInfo->GetFontData(font_handle, kTableNAME, buffer); | |
| return bytes_read == size ? GetNameFromTT(buffer, 6) : ByteString(); | |
| } | |
| void CFX_FontMapper::AddInstalledFont(const ByteString& name, | |
| FX_Charset charset) { | |
| if (!m_pFontInfo) | |
| return; | |
| m_FaceArray.push_back({name, static_cast<uint32_t>(charset)}); | |
| if (name == m_LastFamily) | |
| return; | |
| bool is_localized = std::any_of(name.begin(), name.end(), [](const char& c) { | |
| return static_cast<uint8_t>(c) > 0x80; | |
| }); | |
| if (is_localized) { | |
| void* font_handle = m_pFontInfo->GetFont(name); | |
| if (!font_handle) { | |
| font_handle = | |
| m_pFontInfo->MapFont(0, false, FX_Charset::kDefault, 0, name); | |
| if (!font_handle) | |
| return; | |
| } | |
| ScopedFontDeleter scoped_font(m_pFontInfo.get(), font_handle); | |
| ByteString new_name = GetPSNameFromTT(font_handle); | |
| if (!new_name.IsEmpty()) | |
| m_LocalizedTTFonts.push_back(std::make_pair(new_name, name)); | |
| } | |
| m_InstalledTTFonts.push_back(name); | |
| m_LastFamily = name; | |
| } | |
| void CFX_FontMapper::LoadInstalledFonts() { | |
| if (!m_pFontInfo || m_bListLoaded) | |
| return; | |
| m_pFontInfo->EnumFontList(this); | |
| m_bListLoaded = true; | |
| } | |
| ByteString CFX_FontMapper::MatchInstalledFonts(const ByteString& norm_name) { | |
| LoadInstalledFonts(); | |
| int i; | |
| for (i = fxcrt::CollectionSize<int>(m_InstalledTTFonts) - 1; i >= 0; i--) { | |
| if (TT_NormalizeName(m_InstalledTTFonts[i]) == norm_name) | |
| return m_InstalledTTFonts[i]; | |
| } | |
| for (i = fxcrt::CollectionSize<int>(m_LocalizedTTFonts) - 1; i >= 0; i--) { | |
| if (TT_NormalizeName(m_LocalizedTTFonts[i].first) == norm_name) | |
| return m_LocalizedTTFonts[i].second; | |
| } | |
| return ByteString(); | |
| } | |
| RetainPtr<CFX_Face> CFX_FontMapper::UseInternalSubst( | |
| int base_font, | |
| int weight, | |
| int italic_angle, | |
| int pitch_family, | |
| CFX_SubstFont* subst_font) { | |
| if (base_font < kNumStandardFonts) { | |
| if (!m_StandardFaces[base_font]) { | |
| m_StandardFaces[base_font] = m_pFontMgr->NewFixedFace( | |
| nullptr, m_pFontMgr->GetStandardFont(base_font), 0); | |
| } | |
| return m_StandardFaces[base_font]; | |
| } | |
| subst_font->SetIsBuiltInGenericFont(); | |
| subst_font->m_ItalicAngle = italic_angle; | |
| if (weight) | |
| subst_font->m_Weight = weight; | |
| if (FontFamilyIsRoman(pitch_family)) { | |
| subst_font->UseChromeSerif(); | |
| if (!m_GenericSerifFace) { | |
| m_GenericSerifFace = m_pFontMgr->NewFixedFace( | |
| nullptr, m_pFontMgr->GetGenericSerifFont(), 0); | |
| } | |
| return m_GenericSerifFace; | |
| } | |
| subst_font->m_Family = "Chrome Sans"; | |
| if (!m_GenericSansFace) { | |
| m_GenericSansFace = | |
| m_pFontMgr->NewFixedFace(nullptr, m_pFontMgr->GetGenericSansFont(), 0); | |
| } | |
| return m_GenericSansFace; | |
| } | |
| RetainPtr<CFX_Face> CFX_FontMapper::UseExternalSubst( | |
| void* font_handle, | |
| ByteString face_name, | |
| int weight, | |
| bool is_italic, | |
| int italic_angle, | |
| FX_Charset charset, | |
| CFX_SubstFont* subst_font) { | |
| DCHECK(font_handle); | |
| ScopedFontDeleter scoped_font(m_pFontInfo.get(), font_handle); | |
| m_pFontInfo->GetFaceName(font_handle, &face_name); | |
| if (charset == FX_Charset::kDefault) | |
| m_pFontInfo->GetFontCharset(font_handle, &charset); | |
| size_t ttc_size = m_pFontInfo->GetFontData(font_handle, kTableTTCF, {}); | |
| size_t font_size = m_pFontInfo->GetFontData(font_handle, 0, {}); | |
| if (font_size == 0 && ttc_size == 0) | |
| return nullptr; | |
| RetainPtr<CFX_Face> face = | |
| ttc_size | |
| ? GetCachedTTCFace(font_handle, ttc_size, font_size) | |
| : GetCachedFace(font_handle, face_name, weight, is_italic, font_size); | |
| if (!face) | |
| return nullptr; | |
| subst_font->m_Family = face_name; | |
| subst_font->m_Charset = charset; | |
| int face_weight = | |
| FXFT_Is_Face_Bold(face->GetRec()) ? FXFONT_FW_BOLD : FXFONT_FW_NORMAL; | |
| if (weight != face_weight) | |
| subst_font->m_Weight = weight; | |
| if (is_italic && !FXFT_Is_Face_Italic(face->GetRec())) { | |
| if (italic_angle == 0) | |
| italic_angle = -12; | |
| else if (abs(italic_angle) < 5) | |
| italic_angle = 0; | |
| subst_font->m_ItalicAngle = italic_angle; | |
| } | |
| return face; | |
| } | |
| RetainPtr<CFX_Face> CFX_FontMapper::FindSubstFont(const ByteString& name, | |
| bool is_truetype, | |
| uint32_t flags, | |
| int weight, | |
| int italic_angle, | |
| FX_CodePage code_page, | |
| CFX_SubstFont* subst_font) { | |
| if (weight == 0) | |
| weight = FXFONT_FW_NORMAL; | |
| if (!(flags & FXFONT_USEEXTERNATTR)) { | |
| weight = FXFONT_FW_NORMAL; | |
| italic_angle = 0; | |
| } | |
| const ByteString subst_name = GetSubstName(name, is_truetype); | |
| if (subst_name == "Symbol" && !is_truetype) { | |
| subst_font->m_Family = "Chrome Symbol"; | |
| subst_font->m_Charset = FX_Charset::kSymbol; | |
| return UseInternalSubst(kSymbol, weight, italic_angle, 0, subst_font); | |
| } | |
| if (subst_name == "ZapfDingbats") { | |
| subst_font->m_Family = "Chrome Dingbats"; | |
| subst_font->m_Charset = FX_Charset::kSymbol; | |
| return UseInternalSubst(kDingbats, weight, italic_angle, 0, subst_font); | |
| } | |
| int base_font = 0; | |
| ByteString family; | |
| ByteString style; | |
| bool has_comma = false; | |
| bool has_hyphen = false; | |
| { | |
| absl::optional<size_t> pos = subst_name.Find(","); | |
| if (pos.has_value()) { | |
| family = subst_name.First(pos.value()); | |
| GetStandardFontName(&family); | |
| style = subst_name.Last(subst_name.GetLength() - (pos.value() + 1)); | |
| has_comma = true; | |
| } else { | |
| family = subst_name; | |
| } | |
| } | |
| for (; base_font < kSymbol; base_font++) { | |
| if (family == kBase14FontNames[base_font]) | |
| break; | |
| } | |
| int pitch_family; | |
| uint32_t nStyle; | |
| bool is_style_available = false; | |
| if (base_font < kSymbol) { | |
| nStyle = GetStyleFromBaseFont(base_font); | |
| pitch_family = GetPitchFamilyFromBaseFont(base_font); | |
| } else { | |
| base_font = kNumStandardFonts; | |
| nStyle = FXFONT_NORMAL; | |
| if (!has_comma) { | |
| absl::optional<size_t> pos = family.ReverseFind('-'); | |
| if (pos.has_value()) { | |
| style = family.Last(family.GetLength() - (pos.value() + 1)); | |
| family = family.First(pos.value()); | |
| has_hyphen = true; | |
| } | |
| } | |
| if (!has_hyphen) { | |
| size_t nLen = family.GetLength(); | |
| const FX_FontStyle* style_result = | |
| GetStyleType(family.AsStringView(), /*reverse_search=*/true); | |
| if (style_result) { | |
| family = family.First(nLen - style_result->len); | |
| nStyle |= style_result->style; | |
| } | |
| } | |
| pitch_family = GetPitchFamilyFromFlags(flags); | |
| } | |
| const int old_weight = weight; | |
| if (FontStyleIsForceBold(nStyle)) | |
| weight = FXFONT_FW_BOLD; | |
| if (ParseStyles(style, &is_style_available, &weight, &nStyle)) { | |
| family = subst_name; | |
| base_font = kNumStandardFonts; | |
| } | |
| if (!m_pFontInfo) { | |
| return UseInternalSubst(base_font, old_weight, italic_angle, pitch_family, | |
| subst_font); | |
| } | |
| const FX_Charset Charset = GetCharset(code_page, base_font, flags); | |
| const bool is_cjk = FX_CharSetIsCJK(Charset); | |
| bool is_italic = FontStyleIsItalic(nStyle); | |
| GetFontFamily(nStyle, &family); | |
| ByteString match = MatchInstalledFonts(TT_NormalizeName(family)); | |
| if (match.IsEmpty() && family != subst_name && | |
| (!has_comma && (!has_hyphen || (has_hyphen && !is_style_available)))) { | |
| match = MatchInstalledFonts(TT_NormalizeName(subst_name)); | |
| } | |
| if (match.IsEmpty() && base_font >= kNumStandardFonts) { | |
| if (!is_cjk) { | |
| if (!CheckSupportThirdPartFont(family, &pitch_family)) { | |
| is_italic = italic_angle != 0; | |
| weight = old_weight; | |
| } | |
| if (IsNarrowFontName(subst_name)) | |
| family = kNarrowFamily; | |
| } else { | |
| subst_font->m_bSubstCJK = true; | |
| if (nStyle) | |
| subst_font->m_WeightCJK = nStyle ? weight : FXFONT_FW_NORMAL; | |
| if (FontStyleIsItalic(nStyle)) | |
| subst_font->m_bItalicCJK = true; | |
| } | |
| } else { | |
| italic_angle = 0; | |
| if (nStyle == FXFONT_NORMAL) | |
| weight = FXFONT_FW_NORMAL; | |
| } | |
| if (!match.IsEmpty() || base_font < kNumStandardFonts) { | |
| if (!match.IsEmpty()) | |
| family = match; | |
| if (base_font < kNumStandardFonts) { | |
| base_font = AdjustBaseFontForStyle(base_font, nStyle); | |
| family = kBase14FontNames[base_font]; | |
| } | |
| } else if (FontStyleIsItalic(flags)) { | |
| is_italic = true; | |
| } | |
| void* font_handle = | |
| m_pFontInfo->MapFont(weight, is_italic, Charset, pitch_family, family); | |
| if (font_handle) { | |
| return UseExternalSubst(font_handle, subst_name, weight, is_italic, | |
| italic_angle, Charset, subst_font); | |
| } | |
| if (is_cjk) { | |
| is_italic = italic_angle != 0; | |
| weight = old_weight; | |
| } | |
| if (!match.IsEmpty()) { | |
| font_handle = m_pFontInfo->GetFont(match); | |
| if (!font_handle) { | |
| return UseInternalSubst(base_font, old_weight, italic_angle, pitch_family, | |
| subst_font); | |
| } | |
| return UseExternalSubst(font_handle, subst_name, weight, is_italic, | |
| italic_angle, Charset, subst_font); | |
| } | |
| if (Charset == FX_Charset::kSymbol) { | |
| #if !BUILDFLAG(IS_WIN) | |
| if (subst_name == "Symbol") { | |
| subst_font->m_Family = "Chrome Symbol"; | |
| subst_font->m_Charset = FX_Charset::kSymbol; | |
| return UseInternalSubst(kSymbol, old_weight, italic_angle, pitch_family, | |
| subst_font); | |
| } | |
| #endif | |
| return FindSubstFont(family, is_truetype, flags & ~FXFONT_SYMBOLIC, weight, | |
| italic_angle, FX_CodePage::kDefANSI, subst_font); | |
| } | |
| if (Charset == FX_Charset::kANSI) { | |
| return UseInternalSubst(base_font, old_weight, italic_angle, pitch_family, | |
| subst_font); | |
| } | |
| auto it = std::find_if( | |
| m_FaceArray.begin(), m_FaceArray.end(), [Charset](const FaceData& face) { | |
| return face.charset == static_cast<uint32_t>(Charset); | |
| }); | |
| if (it == m_FaceArray.end()) { | |
| return UseInternalSubst(base_font, old_weight, italic_angle, pitch_family, | |
| subst_font); | |
| } | |
| font_handle = m_pFontInfo->GetFont(it->name); | |
| if (!font_handle) | |
| return nullptr; | |
| return UseExternalSubst(font_handle, subst_name, weight, is_italic, | |
| italic_angle, Charset, subst_font); | |
| } | |
| size_t CFX_FontMapper::GetFaceSize() const { | |
| return m_FaceArray.size(); | |
| } | |
| ByteString CFX_FontMapper::GetFaceName(size_t index) const { | |
| CHECK_LT(index, m_FaceArray.size()); | |
| return m_FaceArray[index].name; | |
| } | |
| bool CFX_FontMapper::HasInstalledFont(ByteStringView name) const { | |
| for (const auto& font : m_InstalledTTFonts) { | |
| if (font == name) | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool CFX_FontMapper::HasLocalizedFont(ByteStringView name) const { | |
| for (const auto& fontPair : m_LocalizedTTFonts) { | |
| if (fontPair.first == name) | |
| return true; | |
| } | |
| return false; | |
| } | |
| #if BUILDFLAG(IS_WIN) | |
| absl::optional<ByteString> CFX_FontMapper::InstalledFontNameStartingWith( | |
| const ByteString& name) const { | |
| for (const auto& thisname : m_InstalledTTFonts) { | |
| if (thisname.First(name.GetLength()) == name) | |
| return thisname; | |
| } | |
| return absl::nullopt; | |
| } | |
| absl::optional<ByteString> CFX_FontMapper::LocalizedFontNameStartingWith( | |
| const ByteString& name) const { | |
| for (const auto& thispair : m_LocalizedTTFonts) { | |
| if (thispair.first.First(name.GetLength()) == name) | |
| return thispair.second; | |
| } | |
| return absl::nullopt; | |
| } | |
| #endif // BUILDFLAG(IS_WIN) | |
| #ifdef PDF_ENABLE_XFA | |
| FixedUninitDataVector<uint8_t> CFX_FontMapper::RawBytesForIndex(size_t index) { | |
| CHECK_LT(index, m_FaceArray.size()); | |
| void* font_handle = m_pFontInfo->MapFont(0, false, FX_Charset::kDefault, 0, | |
| GetFaceName(index)); | |
| if (!font_handle) | |
| return FixedUninitDataVector<uint8_t>(); | |
| ScopedFontDeleter scoped_font(m_pFontInfo.get(), font_handle); | |
| size_t required_size = m_pFontInfo->GetFontData(font_handle, 0, {}); | |
| if (required_size == 0) | |
| return FixedUninitDataVector<uint8_t>(); | |
| FixedUninitDataVector<uint8_t> result(required_size); | |
| size_t actual_size = | |
| m_pFontInfo->GetFontData(font_handle, 0, result.writable_span()); | |
| if (actual_size != required_size) | |
| return FixedUninitDataVector<uint8_t>(); | |
| return result; | |
| } | |
| #endif // PDF_ENABLE_XFA | |
| RetainPtr<CFX_Face> CFX_FontMapper::GetCachedTTCFace(void* font_handle, | |
| size_t ttc_size, | |
| size_t data_size) { | |
| CHECK_GE(ttc_size, data_size); | |
| uint32_t checksum = GetChecksumFromTT(font_handle); | |
| RetainPtr<CFX_FontMgr::FontDesc> pFontDesc = | |
| m_pFontMgr->GetCachedTTCFontDesc(ttc_size, checksum); | |
| if (!pFontDesc) { | |
| FixedUninitDataVector<uint8_t> font_data(ttc_size); | |
| size_t size = m_pFontInfo->GetFontData(font_handle, kTableTTCF, | |
| font_data.writable_span()); | |
| if (size != ttc_size) | |
| return nullptr; | |
| pFontDesc = m_pFontMgr->AddCachedTTCFontDesc(ttc_size, checksum, | |
| std::move(font_data)); | |
| } | |
| size_t font_offset = ttc_size - data_size; | |
| size_t face_index = | |
| GetTTCIndex(pFontDesc->FontData().first(ttc_size), font_offset); | |
| RetainPtr<CFX_Face> pFace(pFontDesc->GetFace(face_index)); | |
| if (pFace) | |
| return pFace; | |
| pFace = m_pFontMgr->NewFixedFace( | |
| pFontDesc, pFontDesc->FontData().first(ttc_size), face_index); | |
| if (!pFace) | |
| return nullptr; | |
| pFontDesc->SetFace(face_index, pFace.Get()); | |
| return pFace; | |
| } | |
| RetainPtr<CFX_Face> CFX_FontMapper::GetCachedFace(void* font_handle, | |
| ByteString subst_name, | |
| int weight, | |
| bool is_italic, | |
| size_t data_size) { | |
| RetainPtr<CFX_FontMgr::FontDesc> pFontDesc = | |
| m_pFontMgr->GetCachedFontDesc(subst_name, weight, is_italic); | |
| if (!pFontDesc) { | |
| FixedUninitDataVector<uint8_t> font_data(data_size); | |
| size_t size = | |
| m_pFontInfo->GetFontData(font_handle, 0, font_data.writable_span()); | |
| if (size != data_size) | |
| return nullptr; | |
| pFontDesc = m_pFontMgr->AddCachedFontDesc(subst_name, weight, is_italic, | |
| std::move(font_data)); | |
| } | |
| RetainPtr<CFX_Face> pFace(pFontDesc->GetFace(0)); | |
| if (pFace) | |
| return pFace; | |
| pFace = m_pFontMgr->NewFixedFace(pFontDesc, | |
| pFontDesc->FontData().first(data_size), 0); | |
| if (!pFace) | |
| return nullptr; | |
| pFontDesc->SetFace(0, pFace.Get()); | |
| return pFace; | |
| } | |
| // static | |
| absl::optional<CFX_FontMapper::StandardFont> | |
| CFX_FontMapper::GetStandardFontName(ByteString* name) { | |
| const auto* end = std::end(kAltFontNames); | |
| const auto* found = | |
| std::lower_bound(std::begin(kAltFontNames), end, name->c_str(), | |
| [](const AltFontName& element, const char* name) { | |
| return FXSYS_stricmp(element.m_pName, name) < 0; | |
| }); | |
| if (found == end || FXSYS_stricmp(found->m_pName, name->c_str())) | |
| return absl::nullopt; | |
| *name = kBase14FontNames[static_cast<size_t>(found->m_Index)]; | |
| return found->m_Index; | |
| } | |
| // static | |
| bool CFX_FontMapper::IsStandardFontName(const ByteString& name) { | |
| return pdfium::Contains(kBase14FontNames, name); | |
| } | |
| // static | |
| bool CFX_FontMapper::IsSymbolicFont(StandardFont font) { | |
| return font == StandardFont::kSymbol || font == StandardFont::kDingbats; | |
| } | |
| // static | |
| bool CFX_FontMapper::IsFixedFont(StandardFont font) { | |
| return font == StandardFont::kCourier || font == StandardFont::kCourierBold || | |
| font == StandardFont::kCourierBoldOblique || | |
| font == StandardFont::kCourierOblique; | |
| } |