// 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_folderfontinfo.h" #include #include #include #include "build/build_config.h" #include "core/fxcrt/fx_codepage.h" #include "core/fxcrt/fx_extension.h" #include "core/fxcrt/fx_folder.h" #include "core/fxcrt/fx_memory_wrappers.h" #include "core/fxcrt/fx_safe_types.h" #include "core/fxcrt/fx_system.h" #include "core/fxge/cfx_fontmapper.h" #include "core/fxge/fx_font.h" #include "third_party/base/containers/contains.h" namespace { const struct { const char* m_pName; const char* m_pSubstName; } Base14Substs[] = { {"Courier", "Courier New"}, {"Courier-Bold", "Courier New Bold"}, {"Courier-BoldOblique", "Courier New Bold Italic"}, {"Courier-Oblique", "Courier New Italic"}, {"Helvetica", "Arial"}, {"Helvetica-Bold", "Arial Bold"}, {"Helvetica-BoldOblique", "Arial Bold Italic"}, {"Helvetica-Oblique", "Arial Italic"}, {"Times-Roman", "Times New Roman"}, {"Times-Bold", "Times New Roman Bold"}, {"Times-BoldItalic", "Times New Roman Bold Italic"}, {"Times-Italic", "Times New Roman Italic"}, }; // Used with std::unique_ptr to automatically call fclose(). struct FxFileCloser { inline void operator()(FILE* h) const { if (h) fclose(h); } }; bool FindFamilyNameMatch(ByteStringView family_name, const ByteString& installed_font_name) { absl::optional result = installed_font_name.Find(family_name, 0); if (!result.has_value()) return false; size_t next_index = result.value() + family_name.GetLength(); // Rule out the case that |family_name| is a substring of // |installed_font_name| but their family names are actually different words. // For example: "Univers" and "Universal" are not a match because they have // different family names, but "Univers" and "Univers Bold" are a match. if (installed_font_name.IsValidIndex(next_index) && FXSYS_IsLowerASCII(installed_font_name[next_index])) { return false; } return true; } ByteString ReadStringFromFile(FILE* pFile, uint32_t size) { ByteString result; { // Span's lifetime must end before ReleaseBuffer() below. pdfium::span buffer = result.GetBuffer(size); if (!fread(buffer.data(), size, 1, pFile)) return ByteString(); } result.ReleaseBuffer(size); return result; } ByteString LoadTableFromTT(FILE* pFile, const uint8_t* pTables, uint32_t nTables, uint32_t tag, FX_FILESIZE fileSize) { for (uint32_t i = 0; i < nTables; i++) { const uint8_t* p = pTables + i * 16; if (FXSYS_UINT32_GET_MSBFIRST(p) == tag) { uint32_t offset = FXSYS_UINT32_GET_MSBFIRST(p + 8); uint32_t size = FXSYS_UINT32_GET_MSBFIRST(p + 12); if (offset > std::numeric_limits::max() - size || static_cast(offset + size) > fileSize || fseek(pFile, offset, SEEK_SET) < 0) { return ByteString(); } return ReadStringFromFile(pFile, size); } } return ByteString(); } uint32_t GetCharset(FX_Charset charset) { switch (charset) { case FX_Charset::kShiftJIS: return CHARSET_FLAG_SHIFTJIS; case FX_Charset::kChineseSimplified: return CHARSET_FLAG_GB; case FX_Charset::kChineseTraditional: return CHARSET_FLAG_BIG5; case FX_Charset::kHangul: return CHARSET_FLAG_KOREAN; case FX_Charset::kSymbol: return CHARSET_FLAG_SYMBOL; case FX_Charset::kANSI: return CHARSET_FLAG_ANSI; default: break; } return 0; } int32_t GetSimilarValue(int weight, bool bItalic, int pitch_family, uint32_t style, bool bMatchName, size_t familyNameLength, size_t bsNameLength) { int32_t iSimilarValue = 0; if (bMatchName && (familyNameLength == bsNameLength)) iSimilarValue += 4; if (FontStyleIsForceBold(style) == (weight > 400)) iSimilarValue += 16; if (FontStyleIsItalic(style) == bItalic) iSimilarValue += 16; if (FontStyleIsSerif(style) == FontFamilyIsRoman(pitch_family)) iSimilarValue += 16; if (FontStyleIsScript(style) == FontFamilyIsScript(pitch_family)) iSimilarValue += 8; if (FontStyleIsFixedPitch(style) == FontFamilyIsFixedPitch(pitch_family)) iSimilarValue += 8; return iSimilarValue; } } // namespace CFX_FolderFontInfo::CFX_FolderFontInfo() = default; CFX_FolderFontInfo::~CFX_FolderFontInfo() = default; void CFX_FolderFontInfo::AddPath(const ByteString& path) { m_PathList.push_back(path); } bool CFX_FolderFontInfo::EnumFontList(CFX_FontMapper* pMapper) { m_pMapper = pMapper; for (const auto& path : m_PathList) ScanPath(path); return true; } void CFX_FolderFontInfo::ScanPath(const ByteString& path) { std::unique_ptr handle = FX_Folder::OpenFolder(path); if (!handle) return; ByteString filename; bool bFolder; while (handle->GetNextFile(&filename, &bFolder)) { if (bFolder) { if (filename == "." || filename == "..") continue; } else { ByteString ext = filename.Last(4); ext.MakeLower(); if (ext != ".ttf" && ext != ".ttc" && ext != ".otf") continue; } ByteString fullpath = path; #if BUILDFLAG(IS_WIN) fullpath += "\\"; #else fullpath += "/"; #endif fullpath += filename; bFolder ? ScanPath(fullpath) : ScanFile(fullpath); } } void CFX_FolderFontInfo::ScanFile(const ByteString& path) { std::unique_ptr pFile(fopen(path.c_str(), "rb")); if (!pFile) return; fseek(pFile.get(), 0, SEEK_END); FX_FILESIZE filesize = ftell(pFile.get()); uint8_t buffer[16]; fseek(pFile.get(), 0, SEEK_SET); size_t readCnt = fread(buffer, 12, 1, pFile.get()); if (readCnt != 1) return; if (FXSYS_UINT32_GET_MSBFIRST(buffer) != kTableTTCF) { ReportFace(path, pFile.get(), filesize, 0); return; } uint32_t nFaces = FXSYS_UINT32_GET_MSBFIRST(buffer + 8); FX_SAFE_SIZE_T safe_face_bytes = nFaces; safe_face_bytes *= 4; if (!safe_face_bytes.IsValid()) return; const size_t face_bytes = safe_face_bytes.ValueOrDie(); std::unique_ptr offsets( FX_Alloc(uint8_t, face_bytes)); readCnt = fread(offsets.get(), 1, face_bytes, pFile.get()); if (readCnt != face_bytes) return; auto offsets_span = pdfium::make_span(offsets.get(), face_bytes); for (uint32_t i = 0; i < nFaces; i++) { ReportFace(path, pFile.get(), filesize, FXSYS_UINT32_GET_MSBFIRST(&offsets_span[i * 4])); } } void CFX_FolderFontInfo::ReportFace(const ByteString& path, FILE* pFile, FX_FILESIZE filesize, uint32_t offset) { char buffer[16]; if (fseek(pFile, offset, SEEK_SET) < 0 || !fread(buffer, 12, 1, pFile)) return; uint32_t nTables = FXSYS_UINT16_GET_MSBFIRST(buffer + 4); ByteString tables = ReadStringFromFile(pFile, nTables * 16); if (tables.IsEmpty()) return; static constexpr uint32_t kNameTag = CFX_FontMapper::MakeTag('n', 'a', 'm', 'e'); ByteString names = LoadTableFromTT(pFile, tables.raw_str(), nTables, kNameTag, filesize); if (names.IsEmpty()) return; ByteString facename = GetNameFromTT(names.raw_span(), 1); if (facename.IsEmpty()) return; ByteString style = GetNameFromTT(names.raw_span(), 2); if (style != "Regular") facename += " " + style; if (pdfium::Contains(m_FontList, facename)) return; auto pInfo = std::make_unique(path, facename, tables, offset, filesize); static constexpr uint32_t kOs2Tag = CFX_FontMapper::MakeTag('O', 'S', '/', '2'); ByteString os2 = LoadTableFromTT(pFile, tables.raw_str(), nTables, kOs2Tag, filesize); if (os2.GetLength() >= 86) { const uint8_t* p = os2.raw_str() + 78; uint32_t codepages = FXSYS_UINT32_GET_MSBFIRST(p); if (codepages & (1U << 17)) { m_pMapper->AddInstalledFont(facename, FX_Charset::kShiftJIS); pInfo->m_Charsets |= CHARSET_FLAG_SHIFTJIS; } if (codepages & (1U << 18)) { m_pMapper->AddInstalledFont(facename, FX_Charset::kChineseSimplified); pInfo->m_Charsets |= CHARSET_FLAG_GB; } if (codepages & (1U << 20)) { m_pMapper->AddInstalledFont(facename, FX_Charset::kChineseTraditional); pInfo->m_Charsets |= CHARSET_FLAG_BIG5; } if ((codepages & (1U << 19)) || (codepages & (1U << 21))) { m_pMapper->AddInstalledFont(facename, FX_Charset::kHangul); pInfo->m_Charsets |= CHARSET_FLAG_KOREAN; } if (codepages & (1U << 31)) { m_pMapper->AddInstalledFont(facename, FX_Charset::kSymbol); pInfo->m_Charsets |= CHARSET_FLAG_SYMBOL; } } m_pMapper->AddInstalledFont(facename, FX_Charset::kANSI); pInfo->m_Charsets |= CHARSET_FLAG_ANSI; pInfo->m_Styles = 0; if (style.Contains("Bold")) pInfo->m_Styles |= FXFONT_FORCE_BOLD; if (style.Contains("Italic") || style.Contains("Oblique")) pInfo->m_Styles |= FXFONT_ITALIC; if (facename.Contains("Serif")) pInfo->m_Styles |= FXFONT_SERIF; m_FontList[facename] = std::move(pInfo); } void* CFX_FolderFontInfo::GetSubstFont(const ByteString& face) { for (size_t iBaseFont = 0; iBaseFont < std::size(Base14Substs); iBaseFont++) { if (face == Base14Substs[iBaseFont].m_pName) return GetFont(Base14Substs[iBaseFont].m_pSubstName); } return nullptr; } void* CFX_FolderFontInfo::FindFont(int weight, bool bItalic, FX_Charset charset, int pitch_family, const ByteString& family, bool bMatchName) { FontFaceInfo* pFind = nullptr; ByteStringView bsFamily = family.AsStringView(); uint32_t charset_flag = GetCharset(charset); int32_t iBestSimilar = 0; for (const auto& it : m_FontList) { const ByteString& bsName = it.first; FontFaceInfo* pFont = it.second.get(); if (!(pFont->m_Charsets & charset_flag) && charset != FX_Charset::kDefault) continue; if (bMatchName && !FindFamilyNameMatch(bsFamily, bsName)) continue; int32_t iSimilarValue = GetSimilarValue(weight, bItalic, pitch_family, pFont->m_Styles, bMatchName, bsFamily.GetLength(), bsName.GetLength()); if (iSimilarValue > iBestSimilar) { iBestSimilar = iSimilarValue; pFind = pFont; } } if (pFind) { return pFind; } if (charset == FX_Charset::kANSI && FontFamilyIsFixedPitch(pitch_family)) { auto* courier_new = GetFont("Courier New"); if (courier_new) return courier_new; } return nullptr; } void* CFX_FolderFontInfo::MapFont(int weight, bool bItalic, FX_Charset charset, int pitch_family, const ByteString& face) { return nullptr; } void* CFX_FolderFontInfo::GetFont(const ByteString& face) { auto it = m_FontList.find(face); return it != m_FontList.end() ? it->second.get() : nullptr; } size_t CFX_FolderFontInfo::GetFontData(void* hFont, uint32_t table, pdfium::span buffer) { if (!hFont) return 0; const FontFaceInfo* pFont = static_cast(hFont); uint32_t datasize = 0; uint32_t offset = 0; if (table == 0) { datasize = pFont->m_FontOffset ? 0 : pFont->m_FileSize; } else if (table == kTableTTCF) { datasize = pFont->m_FontOffset ? pFont->m_FileSize : 0; } else { size_t nTables = pFont->m_FontTables.GetLength() / 16; for (size_t i = 0; i < nTables; i++) { const uint8_t* p = pFont->m_FontTables.raw_str() + i * 16; if (FXSYS_UINT32_GET_MSBFIRST(p) == table) { offset = FXSYS_UINT32_GET_MSBFIRST(p + 8); datasize = FXSYS_UINT32_GET_MSBFIRST(p + 12); } } } if (!datasize || buffer.size() < datasize) return datasize; std::unique_ptr pFile( fopen(pFont->m_FilePath.c_str(), "rb")); if (!pFile) return 0; if (fseek(pFile.get(), offset, SEEK_SET) < 0 || fread(buffer.data(), datasize, 1, pFile.get()) != 1) { return 0; } return datasize; } void CFX_FolderFontInfo::DeleteFont(void* hFont) {} bool CFX_FolderFontInfo::GetFaceName(void* hFont, ByteString* name) { if (!hFont) return false; *name = static_cast(hFont)->m_FaceName; return true; } bool CFX_FolderFontInfo::GetFontCharset(void* hFont, FX_Charset* charset) { return false; } CFX_FolderFontInfo::FontFaceInfo::FontFaceInfo(ByteString filePath, ByteString faceName, ByteString fontTables, uint32_t fontOffset, uint32_t fileSize) : m_FilePath(filePath), m_FaceName(faceName), m_FontTables(fontTables), m_FontOffset(fontOffset), m_FileSize(fileSize) {}