blob: 3a4f41b5c2a8561c406f192963a4fd08cb2dfd9d [file] [log] [blame]
// 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 <iterator>
#include <limits>
#include <utility>
#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) {
std::optional<size_t> 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<char> 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<uint32_t>::max() - size ||
static_cast<FX_FILESIZE>(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<FX_Folder> 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<FILE, FxFileCloser> 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<uint8_t, FxFreeDeleter> 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<FontFaceInfo>(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<uint8_t> buffer) {
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 {
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<FILE, FxFileCloser> 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<FontFaceInfo*>(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) {}