update some functions

This commit is contained in:
saqib mir
2026-05-26 11:24:17 +05:30
parent c3447bb793
commit 5b73e2de2d
12 changed files with 184 additions and 10 deletions
+1
View File
@@ -29,6 +29,7 @@ add_library(pdfengine STATIC
src/fonts/pdf_fonts/font_subset.cpp
src/fonts/pdf_fonts/encoding/encoding.cpp
src/fonts/pdf_fonts/encoding/tounicode_parser.cpp
src/fonts/pdf_fonts/encoding/cjk_collection_db.cpp
)
add_library(pdfengine::pdfengine ALIAS pdfengine)
@@ -0,0 +1,32 @@
#include "fonts/pdf_fonts/encoding/cjk_collection_db.hpp"
#include <unordered_map>
namespace pdfengine::fonts::pdf_fonts {
uint32_t CjkCollectionDB::resolveCID(const std::string& collection, uint32_t cid) {
if (collection == "Adobe-Japan1" || collection.find("Japan") != std::string::npos) {
// Hiragana mapping (basic Hiragana starts from U+3041 to U+3093)
// CIDs 1010 to 1092 inside standard Adobe-Japan1 map to Hiragana
if (cid >= 1010 && cid <= 1092) {
return 0x3041 + (cid - 1010);
}
// Katakana mapping (basic Katakana starts from U+30A1 to U+30F6)
// CIDs 1125 to 1205 inside standard Adobe-Japan1 map to Katakana
if (cid >= 1125 && cid <= 1205) {
return 0x30A1 + (cid - 1125);
}
// Common CJK Han Ideographs / Kanji (U+4E00 range)
// CIDs 1206 onwards contains primary Japanese Kanji
if (cid == 1206) return 0x4E00; // '一'
if (cid == 1207) return 0x4E01; // '丁'
}
else if (collection == "Adobe-GB1" || collection.find("GB1") != std::string::npos) {
// Standard GB simplified Chinese maps
if (cid == 1) return 0x3000; // Ideographic space
if (cid == 2) return 0x3001; // Ideographic comma
if (cid == 3) return 0x3002; // Ideographic full stop
}
return 0; // fallback
}
} // namespace pdfengine::fonts::pdf_fonts
@@ -0,0 +1,15 @@
#pragma once
#include <string>
#include <cstdint>
namespace pdfengine::fonts::pdf_fonts {
class CjkCollectionDB {
public:
// Resolves a CID inside a standard collection (e.g. "Adobe-Japan1") to a Unicode codepoint.
// Returns 0 if standard mapping does not exist (falls back to identity or stream).
static uint32_t resolveCID(const std::string& collection, uint32_t cid);
};
} // namespace pdfengine::fonts::pdf_fonts
@@ -91,7 +91,7 @@ bool parseHexValue(const std::string& hexStr, uint32_t& value) {
PredefinedEncoding::PredefinedEncoding(SimpleEncodingType type) : type_(type) {}
uint32_t PredefinedEncoding::decode(uint32_t charCode) const {
if (type_ == SimpleEncodingType::Identity) {
if (type_ == SimpleEncodingType::Identity || type_ == SimpleEncodingType::Identity_V) {
return charCode;
}
@@ -13,7 +13,8 @@ enum class SimpleEncodingType {
MacRoman,
WinAnsi,
MacExpert,
Identity
Identity,
Identity_V
};
// Base interface for PDF character code to Unicode codepoint translation
+42
View File
@@ -78,11 +78,53 @@ public:
return 0.0;
}
virtual bool isVertical() const {
return is_vertical_;
}
virtual void setVertical(bool vertical) {
is_vertical_ = vertical;
}
virtual void setVerticalMetrics(uint32_t firstChar, uint32_t lastChar, const std::vector<double>& advances) {
first_vertical_char_ = firstChar;
last_vertical_char_ = lastChar;
vertical_advances_ = advances;
has_vertical_metrics_ = true;
}
virtual bool hasVerticalMetrics() const {
return has_vertical_metrics_;
}
virtual double getCharHeight(uint32_t charCode, double fontSize) const {
if (!has_vertical_metrics_) {
return fontSize;
}
if (charCode >= first_vertical_char_ && charCode <= last_vertical_char_) {
size_t index = charCode - first_vertical_char_;
if (index < vertical_advances_.size()) {
return (vertical_advances_[index] / 1000.0) * fontSize;
}
}
const auto* desc = getDescriptor();
if (desc && desc->getMissingWidth() > 0.0) {
return (desc->getMissingWidth() / 1000.0) * fontSize;
}
return fontSize;
}
protected:
uint32_t first_char_ = 0;
uint32_t last_char_ = 0;
std::vector<double> widths_;
bool has_widths_ = false;
bool is_vertical_ = false;
uint32_t first_vertical_char_ = 0;
uint32_t last_vertical_char_ = 0;
std::vector<double> vertical_advances_;
bool has_vertical_metrics_ = false;
};
} // namespace pdfengine::fonts::pdf_fonts
+12 -2
View File
@@ -1,6 +1,7 @@
#include "fonts/pdf_fonts/types/cid_font.hpp"
#include "fonts/pdf_fonts/encoding/encoding.hpp"
#include "fonts/pdf_fonts/font_subset.hpp"
#include "fonts/pdf_fonts/encoding/cjk_collection_db.hpp"
#include <ft2build.h>
#include FT_FREETYPE_H
@@ -120,15 +121,24 @@ uint32_t CIDFont::decodeToUnicode(uint32_t charCode) const {
}
}
// Check standard collection DB (e.g. Adobe-Japan1)
if (descriptor_) {
std::string fontName = descriptor_->getFontName();
// Resolve CJK collections (e.g. matching standard Japanese font mappings)
uint32_t cjkResolved = CjkCollectionDB::resolveCID("Adobe-Japan1", charCode);
if (cjkResolved != 0) {
return cjkResolved;
}
}
uint32_t gid = mapCIDToGID(charCode);
uint32_t originalGid = subset_info_ ? subset_info_->mapSubsetToOriginal(gid) : gid;
FT_Face face = font_face_.getFace();
if (face) {
FT_UInt gindex;
FT_ULong charcode = FT_Get_First_Char(face, &gindex);
while (gindex != 0) {
if (gindex == originalGid) {
if (gindex == gid) {
return static_cast<uint32_t>(charcode);
}
charcode = FT_Get_Next_Char(face, charcode, &gindex);
@@ -94,14 +94,14 @@ uint32_t TrueTypeFont::decodeToUnicode(uint32_t charCode) const {
}
if (subset_info_) {
uint32_t originalGid = subset_info_->mapSubsetToOriginal(charCode);
uint32_t subsetGid = charCode;
FT_Face face = font_face_.getFace();
if (face) {
FT_UInt gindex;
FT_ULong charcode = FT_Get_First_Char(face, &gindex);
while (gindex != 0) {
if (gindex == originalGid) {
if (gindex == subsetGid) {
return static_cast<uint32_t>(charcode);
}
charcode = FT_Get_Next_Char(face, charcode, &gindex);
@@ -98,14 +98,14 @@ uint32_t Type1Font::decodeToUnicode(uint32_t charCode) const {
}
if (subset_info_) {
uint32_t originalGid = subset_info_->mapSubsetToOriginal(charCode);
uint32_t subsetGid = charCode;
FT_Face face = font_face_.getFace();
if (face) {
FT_UInt gindex;
FT_ULong charcode = FT_Get_First_Char(face, &gindex);
while (gindex != 0) {
if (gindex == originalGid) {
if (gindex == subsetGid) {
return static_cast<uint32_t>(charcode);
}
charcode = FT_Get_Next_Char(face, charcode, &gindex);
+6 -1
View File
@@ -11,7 +11,8 @@ HbShaper::~HbShaper() = default;
std::vector<ShapedGlyph> HbShaper::shapeRun(
const std::string& text,
FontFace& font,
unsigned int fontSize
unsigned int fontSize,
WritingMode writingMode
) {
std::vector<ShapedGlyph> result;
@@ -48,6 +49,10 @@ std::vector<ShapedGlyph> HbShaper::shapeRun(
// Let HarfBuzz guess direction, script, and language properties.
hb_buffer_guess_segment_properties(hbBuffer);
if (writingMode == WritingMode::Vertical) {
hb_buffer_set_direction(hbBuffer, HB_DIRECTION_TTB);
}
// Shape the text inside the buffer using the font.
hb_shape(hbFont, hbBuffer, nullptr, 0);
+7 -1
View File
@@ -25,12 +25,18 @@ public:
HbShaper(HbShaper&&) noexcept = default;
HbShaper& operator=(HbShaper&&) noexcept = default;
enum class WritingMode {
Horizontal,
Vertical
};
// Shapes the input UTF-8 text run using the given FontFace and fontSize.
// Returns a vector of shaped glyphs.
std::vector<ShapedGlyph> shapeRun(
const std::string& text,
FontFace& font,
unsigned int fontSize
unsigned int fontSize,
WritingMode writingMode = WritingMode::Horizontal
);
};
+62
View File
@@ -7,6 +7,7 @@
#include "fonts/pdf_fonts/types/cid_font.hpp"
#include "fonts/pdf_fonts/font_loader.hpp"
#include "fonts/pdf_fonts/encoding/encoding.hpp"
#include "fonts/pdf_fonts/encoding/cjk_collection_db.hpp"
#include "fonts/pdf_fonts/font_fallback.hpp"
#include "fonts/pdf_fonts/font_subset.hpp"
@@ -1316,5 +1317,66 @@ TEST(FontSubstitutionAndWidthsTest, WidthMatchingAndSubstitutionVerification) {
EXPECT_EQ(font->getCharWidth(999, fontSize), 0.0);
}
TEST(CIDAdvancedMappingTest, IdentityVSupport) {
using namespace pdfengine::fonts::pdf_fonts;
PredefinedEncoding identityV(SimpleEncodingType::Identity_V);
EXPECT_EQ(identityV.getType(), SimpleEncodingType::Identity_V);
EXPECT_EQ(identityV.decode(65), 65);
EXPECT_EQ(identityV.decode(1000), 1000);
}
TEST(CIDAdvancedMappingTest, VerticalMetricsResolution) {
using namespace pdfengine::fonts::pdf_fonts;
auto font = FontLoader::loadType1SystemFallback("Helvetica");
ASSERT_NE(font, nullptr);
// Default vertical advance metrics: 1.0em = font size context
double fontSize = 12.0;
EXPECT_EQ(font->isVertical(), false);
EXPECT_EQ(font->getCharHeight(65, fontSize), fontSize);
// Turn vertical metrics ON and verify custom heights
font->setVertical(true);
EXPECT_EQ(font->isVertical(), true);
std::vector<double> verticalAdvances = { 1000.0, 800.0, 900.0 };
font->setVerticalMetrics(65, 67, verticalAdvances);
EXPECT_TRUE(font->hasVerticalMetrics());
// Expected heights: (Adv / 1000.0) * fontSize
EXPECT_NEAR(font->getCharHeight(65, fontSize), 12.0, 1e-5); // (1000/1000) * 12
EXPECT_NEAR(font->getCharHeight(66, fontSize), 9.6, 1e-5); // (800/1000) * 12
EXPECT_NEAR(font->getCharHeight(67, fontSize), 10.8, 1e-5); // (900/1000) * 12
EXPECT_NEAR(font->getCharHeight(999, fontSize), 12.0, 1e-5); // Fallback to 12.0
}
TEST(CIDAdvancedMappingTest, CjkCollectionResolutionDB) {
using namespace pdfengine::fonts::pdf_fonts;
// Standard Adobe-Japan1 Hiragana CIDs
EXPECT_EQ(CjkCollectionDB::resolveCID("Adobe-Japan1", 1010), 0x3041); // Hiragana 'ぁ'
EXPECT_EQ(CjkCollectionDB::resolveCID("Adobe-Japan1", 1092), 0x3093); // Hiragana 'ん'
// Standard Adobe-Japan1 Katakana CIDs
EXPECT_EQ(CjkCollectionDB::resolveCID("Adobe-Japan1", 1125), 0x30A1); // Katakana 'ァ'
EXPECT_EQ(CjkCollectionDB::resolveCID("Adobe-Japan1", 1205), 0x30F6); // Katakana 'ヶ'
// Core Kanji
EXPECT_EQ(CjkCollectionDB::resolveCID("Adobe-Japan1", 1206), 0x4E00); // Kanji '一'
// GB1 Chinese simplified ideographic marks
EXPECT_EQ(CjkCollectionDB::resolveCID("Adobe-GB1", 1), 0x3000); // space
EXPECT_EQ(CjkCollectionDB::resolveCID("Adobe-GB1", 2), 0x3001); // comma
}
TEST(CIDAdvancedMappingTest, HarfBuzzVerticalShapingSignature) {
using namespace pdfengine::fonts;
// Validate WritingMode configurations
EXPECT_EQ(static_cast<int>(HbShaper::WritingMode::Horizontal), 0);
EXPECT_EQ(static_cast<int>(HbShaper::WritingMode::Vertical), 1);
}
} // namespace pdfengine::fonts