// Copyright 2016 PDFium Authors. All rights reserved. // 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 <limits> #include <utility> #include "core/fxcrt/fx_codepage.h" #include "core/fxcrt/fx_stream.h" #include "core/fxge/cfx_fontmapper.h" #include "core/fxge/fx_font.h" #include "third_party/base/ptr_util.h" #include "third_party/base/stl_util.h" #define CHARSET_FLAG_ANSI (1 << 0) #define CHARSET_FLAG_SYMBOL (1 << 1) #define CHARSET_FLAG_SHIFTJIS (1 << 2) #define CHARSET_FLAG_BIG5 (1 << 3) #define CHARSET_FLAG_GB (1 << 4) #define CHARSET_FLAG_KOREAN (1 << 5) 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"}, }; ByteString FPDF_ReadStringFromFile(FILE* pFile, uint32_t size) { ByteString buffer; if (!fread(buffer.GetBuffer(size), size, 1, pFile)) return ByteString(); buffer.ReleaseBuffer(size); return buffer; } ByteString FPDF_LoadTableFromTT(FILE* pFile, const uint8_t* pTables, uint32_t nTables, uint32_t tag, uint32_t fileSize) { for (uint32_t i = 0; i < nTables; i++) { const uint8_t* p = pTables + i * 16; if (GET_TT_LONG(p) == tag) { uint32_t offset = GET_TT_LONG(p + 8); uint32_t size = GET_TT_LONG(p + 12); if (offset > std::numeric_limits<uint32_t>::max() - size || offset + size > fileSize || fseek(pFile, offset, SEEK_SET) < 0) { return ByteString(); } return FPDF_ReadStringFromFile(pFile, size); } } return ByteString(); } uint32_t GetCharset(int charset) { switch (charset) { case FX_CHARSET_ShiftJIS: return CHARSET_FLAG_SHIFTJIS; case FX_CHARSET_ChineseSimplified: return CHARSET_FLAG_GB; case FX_CHARSET_ChineseTraditional: return CHARSET_FLAG_BIG5; case FX_CHARSET_Hangul: return CHARSET_FLAG_KOREAN; case FX_CHARSET_Symbol: return CHARSET_FLAG_SYMBOL; case FX_CHARSET_ANSI: return CHARSET_FLAG_ANSI; default: break; } return 0; } int32_t GetSimilarValue(int weight, bool bItalic, int pitch_family, uint32_t style) { int32_t iSimilarValue = 0; if (FontStyleIsBold(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() {} CFX_FolderFontInfo::~CFX_FolderFontInfo() {} 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) { FX_FileHandle* handle = FX_OpenFolder(path.c_str()); if (!handle) return; ByteString filename; bool bFolder; while (FX_GetNextFile(handle, &filename, &bFolder)) { if (bFolder) { if (filename == "." || filename == "..") continue; } else { ByteString ext = filename.Right(4); ext.MakeUpper(); if (ext != ".TTF" && ext != ".OTF" && ext != ".TTC") continue; } ByteString fullpath = path; #if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_ fullpath += "\\"; #else fullpath += "/"; #endif fullpath += filename; bFolder ? ScanPath(fullpath) : ScanFile(fullpath); } FX_CloseFolder(handle); } void CFX_FolderFontInfo::ScanFile(const ByteString& path) { FILE* pFile = fopen(path.c_str(), "rb"); if (!pFile) return; fseek(pFile, 0, SEEK_END); uint32_t filesize = ftell(pFile); uint8_t buffer[16]; fseek(pFile, 0, SEEK_SET); size_t readCnt = fread(buffer, 12, 1, pFile); if (readCnt != 1) { fclose(pFile); return; } if (GET_TT_LONG(buffer) == kTableTTCF) { uint32_t nFaces = GET_TT_LONG(buffer + 8); if (nFaces > std::numeric_limits<uint32_t>::max() / 4) { fclose(pFile); return; } uint32_t face_bytes = nFaces * 4; uint8_t* offsets = FX_Alloc(uint8_t, face_bytes); readCnt = fread(offsets, 1, face_bytes, pFile); if (readCnt != face_bytes) { FX_Free(offsets); fclose(pFile); return; } for (uint32_t i = 0; i < nFaces; i++) { uint8_t* p = offsets + i * 4; ReportFace(path, pFile, filesize, GET_TT_LONG(p)); } FX_Free(offsets); } else { ReportFace(path, pFile, filesize, 0); } fclose(pFile); } void CFX_FolderFontInfo::ReportFace(const ByteString& path, FILE* pFile, uint32_t filesize, uint32_t offset) { char buffer[16]; if (fseek(pFile, offset, SEEK_SET) < 0 || !fread(buffer, 12, 1, pFile)) return; uint32_t nTables = GET_TT_SHORT(buffer + 4); ByteString tables = FPDF_ReadStringFromFile(pFile, nTables * 16); if (tables.IsEmpty()) return; ByteString names = FPDF_LoadTableFromTT(pFile, tables.raw_str(), nTables, 0x6e616d65, filesize); if (names.IsEmpty()) return; ByteString facename = GetNameFromTT(names.raw_str(), names.GetLength(), 1); if (facename.IsEmpty()) return; ByteString style = GetNameFromTT(names.raw_str(), names.GetLength(), 2); if (style != "Regular") facename += " " + style; if (pdfium::ContainsKey(m_FontList, facename)) return; auto pInfo = pdfium::MakeUnique<FontFaceInfo>(path, facename, tables, offset, filesize); ByteString os2 = FPDF_LoadTableFromTT(pFile, tables.raw_str(), nTables, 0x4f532f32, filesize); if (os2.GetLength() >= 86) { const uint8_t* p = os2.raw_str() + 78; uint32_t codepages = GET_TT_LONG(p); if (codepages & (1 << 17)) { m_pMapper->AddInstalledFont(facename, FX_CHARSET_ShiftJIS); pInfo->m_Charsets |= CHARSET_FLAG_SHIFTJIS; } if (codepages & (1 << 18)) { m_pMapper->AddInstalledFont(facename, FX_CHARSET_ChineseSimplified); pInfo->m_Charsets |= CHARSET_FLAG_GB; } if (codepages & (1 << 20)) { m_pMapper->AddInstalledFont(facename, FX_CHARSET_ChineseTraditional); pInfo->m_Charsets |= CHARSET_FLAG_BIG5; } if ((codepages & (1 << 19)) || (codepages & (1 << 21))) { m_pMapper->AddInstalledFont(facename, FX_CHARSET_Hangul); pInfo->m_Charsets |= CHARSET_FLAG_KOREAN; } if (codepages & (1 << 31)) { m_pMapper->AddInstalledFont(facename, FX_CHARSET_Symbol); pInfo->m_Charsets |= CHARSET_FLAG_SYMBOL; } } m_pMapper->AddInstalledFont(facename, FX_CHARSET_ANSI); pInfo->m_Charsets |= CHARSET_FLAG_ANSI; pInfo->m_Styles = 0; if (style.Contains("Bold")) pInfo->m_Styles |= FXFONT_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 < FX_ArraySize(Base14Substs); iBaseFont++) { if (face == Base14Substs[iBaseFont].m_pName) return GetFont(Base14Substs[iBaseFont].m_pSubstName); } return nullptr; } void* CFX_FolderFontInfo::FindFont(int weight, bool bItalic, int charset, int pitch_family, const char* family, bool bMatchName) { FontFaceInfo* pFind = nullptr; if (charset == FX_CHARSET_ANSI && FontFamilyIsFixedPitch(pitch_family)) return GetFont("Courier New"); 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_Default) continue; if (bMatchName && !bsName.Contains(family)) continue; int32_t iSimilarValue = GetSimilarValue(weight, bItalic, pitch_family, pFont->m_Styles); if (iSimilarValue > iBestSimilar) { iBestSimilar = iSimilarValue; pFind = pFont; } } return pFind; } void* CFX_FolderFontInfo::MapFont(int weight, bool bItalic, int charset, int pitch_family, const char* family) { return nullptr; } #ifdef PDF_ENABLE_XFA void* CFX_FolderFontInfo::MapFontByUnicode(uint32_t dwUnicode, int weight, bool bItalic, int pitch_family) { return nullptr; } #endif // PDF_ENABLE_XFA void* CFX_FolderFontInfo::GetFont(const char* face) { auto it = m_FontList.find(face); return it != m_FontList.end() ? it->second.get() : nullptr; } uint32_t CFX_FolderFontInfo::GetFontData(void* hFont, uint32_t table, uint8_t* buffer, uint32_t size) { if (!hFont) return 0; const FontFaceInfo* pFont = static_cast<FontFaceInfo*>(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 { uint32_t nTables = pFont->m_FontTables.GetLength() / 16; for (uint32_t i = 0; i < nTables; i++) { const uint8_t* p = pFont->m_FontTables.raw_str() + i * 16; if (GET_TT_LONG(p) == table) { offset = GET_TT_LONG(p + 8); datasize = GET_TT_LONG(p + 12); } } } if (!datasize || size < datasize) return datasize; FILE* pFile = fopen(pFont->m_FilePath.c_str(), "rb"); if (!pFile) return 0; if (fseek(pFile, offset, SEEK_SET) < 0 || fread(buffer, datasize, 1, pFile) != 1) { datasize = 0; } fclose(pFile); return datasize; } void CFX_FolderFontInfo::DeleteFont(void* hFont) {} bool CFX_FolderFontInfo::GetFaceName(void* hFont, ByteString* name) { if (!hFont) return false; *name = static_cast<FontFaceInfo*>(hFont)->m_FaceName; return true; } bool CFX_FolderFontInfo::GetFontCharset(void* hFont, int* 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), m_Styles(0), m_Charsets(0) {}