update some functions
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user