Files
pdf/engine/src/fonts/pdf_fonts/font_subset.cpp
T
2026-06-08 11:23:27 +05:30

133 lines
3.7 KiB
C++

#include "fonts/pdf_fonts/font_subset.hpp"
#include <algorithm>
#include <cctype>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <hb.h>
#include <hb-subset.h>
namespace pdfengine::fonts::pdf_fonts {
void FontSubset::populateFromFace(FontSubset& subset, void* ftFace) {
if (!ftFace) return;
FT_Face face = static_cast<FT_Face>(ftFace);
FT_UInt gindex;
FT_ULong charcode = FT_Get_First_Char(face, &gindex);
while (gindex != 0) {
// In embedded subsets, the TrueType cmap typically maps the
// Original GID or CID (charcode) to the new Subset GID (gindex).
subset.addGlyphMapping(gindex, static_cast<uint32_t>(charcode));
charcode = FT_Get_Next_Char(face, charcode, &gindex);
}
}
std::vector<uint8_t> FontSubset::buildSubset(const std::vector<uint8_t>& originalStream, const std::vector<uint32_t>& glyphIdsToKeep) {
std::vector<uint8_t> result;
if (originalStream.empty() || glyphIdsToKeep.empty()) {
return result;
}
hb_blob_t* blob = hb_blob_create(
reinterpret_cast<const char*>(originalStream.data()),
static_cast<unsigned int>(originalStream.size()),
HB_MEMORY_MODE_READONLY,
nullptr,
nullptr
);
hb_face_t* face = hb_face_create(blob, 0);
hb_blob_destroy(blob);
if (!face) {
return result;
}
hb_subset_input_t* input = hb_subset_input_create_or_fail();
if (!input) {
hb_face_destroy(face);
return result;
}
hb_set_t* glyph_set = hb_subset_input_glyph_set(input);
for (uint32_t gid : glyphIdsToKeep) {
hb_set_add(glyph_set, gid);
}
// Always keep GID 0 (the .notdef glyph)
hb_set_add(glyph_set, 0);
// Retain layout tables for complex text shaping
hb_subset_input_set_flags(input, HB_SUBSET_FLAGS_RETAIN_GIDS);
hb_face_t* subset_face = hb_subset_or_fail(face, input);
hb_subset_input_destroy(input);
hb_face_destroy(face);
if (subset_face) {
hb_blob_t* result_blob = hb_face_reference_blob(subset_face);
if (result_blob) {
unsigned int length = 0;
const char* data = hb_blob_get_data(result_blob, &length);
if (data && length > 0) {
result.assign(data, data + length);
}
hb_blob_destroy(result_blob);
}
hb_face_destroy(subset_face);
}
return result;
}
bool FontSubset::hasSubsetPrefix(const std::string& fontName) {
if (fontName.length() < 8) {
return false;
}
if (fontName[6] != '+') {
return false;
}
for (int i = 0; i < 6; ++i) {
if (!std::isupper(static_cast<unsigned char>(fontName[i]))) {
return false;
}
}
return true;
}
std::string FontSubset::stripSubsetPrefix(const std::string& fontName) {
if (hasSubsetPrefix(fontName)) {
return fontName.substr(7);
}
return fontName;
}
std::string FontSubset::getSubsetPrefix(const std::string& fontName) {
if (hasSubsetPrefix(fontName)) {
return fontName.substr(0, 6);
}
return "";
}
FontSubset::FontSubset(const std::string& fontName)
: full_name_(fontName),
is_subset_(hasSubsetPrefix(fontName)) {}
void FontSubset::addGlyphMapping(uint32_t subsetGid, uint32_t originalGid) {
subset_to_original_map_[subsetGid] = originalGid;
}
uint32_t FontSubset::mapSubsetToOriginal(uint32_t subsetGid) const {
auto it = subset_to_original_map_.find(subsetGid);
if (it != subset_to_original_map_.end()) {
return it->second;
}
return subsetGid;
}
bool FontSubset::hasGlyphMapping(uint32_t subsetGid) const {
return subset_to_original_map_.find(subsetGid) != subset_to_original_map_.end();
}
} // namespace pdfengine::fonts::pdf_fonts