#include "pdf/pdfium/pdfium_font_linux.h"
#include <stddef.h>
#include <stdint.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <algorithm>
#include <limits>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include "base/check_op.h"
#include "base/containers/heap_array.h"
#include "base/containers/span.h"
#include "base/feature_list.h"
#include "base/files/file.h"
#include "base/i18n/encoding_detection.h"
#include "base/i18n/icu_string_conversions.h"
#include "base/no_destructor.h"
#include "base/numerics/byte_conversions.h"
#include "base/numerics/safe_conversions.h"
#include "base/posix/eintr_wrapper.h"
#include "base/sequence_checker.h"
#include "components/services/font/public/cpp/font_loader.h"
#include "pdf/pdf_features.h"
#include "pdf/pdfium/pdfium_engine.h"
#include "pdf/pdfium/pdfium_font_helpers.h"
#include "third_party/blink/public/platform/web_font_description.h"
#include "third_party/pdfium/public/fpdf_sysfontinfo.h"
namespace chrome_pdf {
namespace {
BASE_FEATURE(kPdfEnumerateAllSystemFonts, base::FEATURE_ENABLED_BY_DEFAULT);
bool GetFontTable(int fd,
uint32_t table_tag,
uint8_t* output,
size_t* output_length) {
size_t data_length = 0u;
off_t data_offset = 0;
if (table_tag == 0) {
struct stat st;
if (fstat(fd, &st) < 0) {
return false;
}
data_length = base::checked_cast<size_t>(st.st_size);
} else {
uint8_t bytes[2];
ssize_t n = HANDLE_EINTR(
pread(fd, bytes, sizeof(bytes), 4 ));
if (n != sizeof(bytes)) {
return false;
}
uint16_t num_tables = base::U16FromBigEndian(bytes);
static const size_t kTableEntrySize = 16u;
auto table_entries =
base::HeapArray<uint8_t>::WithSize(num_tables * kTableEntrySize);
n = HANDLE_EINTR(pread(fd, table_entries.data(), table_entries.size(),
12 ));
if (n != base::checked_cast<ssize_t>(table_entries.size())) {
return false;
}
for (uint16_t i = 0u; i < num_tables; ++i) {
auto entry = table_entries.subspan(i * kTableEntrySize, kTableEntrySize);
auto tag = base::U32FromNativeEndian(entry.first<4u>());
if (tag == table_tag) {
data_offset = base::U32FromBigEndian(entry.subspan<8u, 4u>());
data_length = base::U32FromBigEndian(entry.subspan<12u, 4u>());
break;
}
}
}
if (!data_length) {
return false;
}
if (output) {
data_length = std::min(data_length, *output_length);
ssize_t n = HANDLE_EINTR(pread(fd, output, data_length, data_offset));
if (n != base::checked_cast<ssize_t>(data_length)) {
return false;
}
}
*output_length = data_length;
return true;
}
class BlinkFontMapper {
public:
using FontId = void*;
BlinkFontMapper() = default;
~BlinkFontMapper() = delete;
FontId MapFont(const blink::WebFontDescription& desc, int charset) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
sk_sp<SkFontConfigInterface> fci = SkFontConfigInterface::RefGlobal();
if (fci.get() == SkFontConfigInterface::GetSingletonDirectInterface()) {
return nullptr;
}
auto font_file = std::make_unique<base::File>();
auto* font_loader = reinterpret_cast<font_service::FontLoader*>(fci.get());
font_loader->MatchFontWithFallback(
desc.family.Utf8(),
desc.weight >= blink::WebFontDescription::kWeightBold, desc.italic,
charset, desc.generic_family, font_file.get());
if (!font_file->IsValid()) {
return nullptr;
}
return font_file.release();
}
void DeleteFont(FontId font_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
delete FileFromFontId(font_id);
}
unsigned long GetFontData(FontId font_id,
unsigned int table_tag,
unsigned char* buffer,
unsigned long buf_size) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::PlatformFile platform_file =
FileFromFontId(font_id)->GetPlatformFile();
size_t size = buf_size;
if (!GetFontTable(platform_file, table_tag, buffer, &size)) {
return 0;
}
return size;
}
void EnumFonts(FPDF_SYSFONTINFO* sysfontinfo, void* mapper) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(
!base::FeatureList::IsEnabled(features::kPdfiumPerRequestFontMatching));
if (!base::FeatureList::IsEnabled(kPdfEnumerateAllSystemFonts)) {
FPDF_AddInstalledFont(mapper, "Arial", FXFONT_DEFAULT_CHARSET);
size_t count = FPDF_GetDefaultTTFMapCount();
for (size_t i = 0; i < count; ++i) {
const FPDF_CharsetFontMap* font_map = FPDF_GetDefaultTTFMapEntry(i);
if (font_map) {
FPDF_AddInstalledFont(mapper, font_map->fontname, font_map->charset);
}
}
return;
}
std::set<std::pair<std::string, int>> seen_fonts;
if (sk_sp<SkFontConfigInterface> fci = SkFontConfigInterface::RefGlobal();
fci &&
fci.get() != SkFontConfigInterface::GetSingletonDirectInterface()) {
auto* font_loader = static_cast<font_service::FontLoader*>(fci.get());
for (const std::string& family_name : font_loader->ListFamilies()) {
CHECK(!family_name.empty());
if (bool inserted =
seen_fonts.emplace(family_name, FXFONT_DEFAULT_CHARSET).second;
inserted) {
FPDF_AddInstalledFont(mapper, family_name.c_str(),
FXFONT_DEFAULT_CHARSET);
}
}
}
size_t count = FPDF_GetDefaultTTFMapCount();
for (size_t i = 0; i < count; ++i) {
const FPDF_CharsetFontMap* font_map = FPDF_GetDefaultTTFMapEntry(i);
if (font_map) {
if (bool inserted =
seen_fonts.emplace(font_map->fontname, font_map->charset)
.second;
inserted) {
FPDF_AddInstalledFont(mapper, font_map->fontname, font_map->charset);
}
}
}
}
private:
static base::File* FileFromFontId(FontId font_id) {
return reinterpret_cast<base::File*>(font_id);
}
SEQUENCE_CHECKER(sequence_checker_);
};
BlinkFontMapper& GetBlinkFontMapper() {
static base::NoDestructor<BlinkFontMapper> mapper;
return *mapper;
}
void EnumFonts(FPDF_SYSFONTINFO* sysfontinfo, void* mapper) {
if (PDFiumEngine::GetFontMappingMode() != FontMappingMode::kBlink) {
CHECK_EQ(PDFiumEngine::GetFontMappingMode(), FontMappingMode::kNoMapping);
return;
}
GetBlinkFontMapper().EnumFonts(sysfontinfo, mapper);
}
void* MapFont(FPDF_SYSFONTINFO*,
int weight,
int italic,
int charset,
int pitch_family,
const char* face,
int* exact) {
if (PDFiumEngine::GetFontMappingMode() != FontMappingMode::kBlink) {
DCHECK_EQ(PDFiumEngine::GetFontMappingMode(), FontMappingMode::kNoMapping);
return nullptr;
}
std::optional<blink::WebFontDescription> desc =
PdfFontToBlinkFontMapping(weight, italic, charset, pitch_family, face);
if (!desc.has_value()) {
return nullptr;
}
return GetBlinkFontMapper().MapFont(desc.value(), charset);
}
unsigned long GetFontData(FPDF_SYSFONTINFO*,
void* font_id,
unsigned int table,
unsigned char* buffer,
unsigned long buf_size) {
DCHECK_EQ(PDFiumEngine::GetFontMappingMode(), FontMappingMode::kBlink);
return GetBlinkFontMapper().GetFontData(font_id, table, buffer, buf_size);
}
void DeleteFont(FPDF_SYSFONTINFO*, void* font_id) {
DCHECK_EQ(PDFiumEngine::GetFontMappingMode(), FontMappingMode::kBlink);
GetBlinkFontMapper().DeleteFont(font_id);
}
FPDF_SYSFONTINFO g_font_info = {.version = 1,
.Release = nullptr,
.EnumFonts = EnumFonts,
.MapFont = MapFont,
.GetFont = nullptr,
.GetFontData = GetFontData,
.GetFaceName = nullptr,
.GetFontCharset = nullptr,
.DeleteFont = DeleteFont};
}
void InitializeLinuxFontMapper() {
if (base::FeatureList::IsEnabled(features::kPdfiumPerRequestFontMatching)) {
g_font_info.version = 2;
} else {
g_font_info.version = 1;
}
FPDF_SetSystemFontInfo(&g_font_info);
}
}