133 lines
3.7 KiB
C++
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
|