freetype封装
文章目录
- 头文件
- 源文件
- 简单使用
参考podofo
头文件
/**
* SPDX-FileCopyrightText: (C) 2022 Francesco Pretto <ceztko@gmail.com>
* SPDX-License-Identifier: LGPL-2.0-or-later
* SPDX-License-Identifier: MPL-2.0
*/
#ifndef PODOFO_FREETYPE_PRIVATE_H
#define PODOFO_FREETYPE_PRIVATE_H
#define _RPCNDR_H_BYTE_REDEFINED
#include <windows.h>
// Old freetype versions requires <ft2build.h> to be included first
#include <ft2build.h>
#include FT_FREETYPE_H
//#include <podofo/main/PdfDeclarations.h>
#include<string>
#include<vector>
#include<set>
using namespace std;
#define CHECK_FT_RC(rc, func) if (rc != 0)\
PODOFO_RAISE_ERROR_INFO(PdfErrorCode::FreeTypeError, "Function " #func " failed")
enum class PdfFontFileType : uint8_t
{
// Table 126 – Embedded font organization for various font types
Unknown = 0,
Type1,
Type1CFF, ///< Compact Font representation for a Type1 font, as described by Adobe Technical Note #5176 "The Compact Font Format Specification"
CIDKeyedCFF, ///< A Compact Font representation of a CID keyed font, as described by Adobe Technical Note #5176 "The Compact Font Format Specification"
Type3,
TrueType, ///< A TrueType/OpenType font that has a "glyf" table
OpenTypeCFF ///< OpenType font with a "CFF"/"CFF2" table, as described in ISO/IEC 14496-22
};
namespace FT
{
FT_Library GetLibrary();
FT_Face CreateFaceFromFile(const std::string_view& filepath, unsigned faceIndex,
vector<uint8_t>& buffer);
FT_Face CreateFaceFromBuffer(const vector<uint8_t>& view, unsigned faceIndex,
vector<uint8_t>& buffer);
// Extract a CFF table from a OpenType CFF font
FT_Face ExtractCFFFont(FT_Face face, vector<uint8_t>& buffer);
// No check for TTC fonts
FT_Face CreateFaceFromBuffer(const vector<uint8_t>& view);
vector<uint8_t> GetDataFromFace(FT_Face face);
bool TryGetFontFileFormat(FT_Face face, PdfFontFileType& format);
bool IsPdfSupported(FT_Face face);
}
// Other legacy TrueType tables defined in Apple documentation
// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6.html
#define TTAG_acnt FT_MAKE_TAG('a', 'c', 'n', 't')
#define TTAG_ankr FT_MAKE_TAG('a', 'n', 'k', 'r')
#define TTAG_kerx FT_MAKE_TAG('k', 'e', 'r', 'x')
#define TTAG_fdsc FT_MAKE_TAG('f', 'd', 's', 'c')
#define TTAG_fmtx FT_MAKE_TAG('f', 'm', 't', 'x')
#define TTAG_fond FT_MAKE_TAG('f', 'o', 'n', 'd')
#define TTAG_gcid FT_MAKE_TAG('g', 'c', 'i', 'd')
#define TTAG_ltag FT_MAKE_TAG('l', 't', 'a', 'g')
#define TTAG_meta FT_MAKE_TAG('m', 'e', 't', 'a')
#define TTAG_xref FT_MAKE_TAG('x', 'r', 'e', 'f')
#define TTAG_Zapf FT_MAKE_TAG('Z', 'a', 'p', 'f')
#endif // PODOFO_FREETYPE_PRIVATE_H
源文件
#include <vector>
#include <cstdint>
#include <string>
#include <memory>
#include <ft2build.h>
#include"freetype_wrap.h"
#include FT_TRUETYPE_TAGS_H
#include FT_TRUETYPE_TABLES_H
#include FT_FONT_FORMATS_H
#include FT_CID_H
using namespace std;
#include<windows.h>
#include <fstream>
#include <string>
#include <vector>
#include <cstdint>
#include <ios>
#include <codecvt>
#include <locale>
constexpr unsigned TableDirectoryFixedSize = 12;
namespace {
struct TTCF_Header {
uint32_t ttcTag;
uint16_t majorVersion;
uint16_t minorVersion;
uint32_t numFonts;
};
struct TT_TableHeader {
uint32_t tableTag;
uint32_t checksum;
uint32_t offset;
uint32_t length;
};
struct TableInfo {
FT_ULong Tag;
FT_ULong Size;
};
}
// 定义 ReadTo 函数,从文件读取数据到 buffer
void ReadTo(vector<uint8_t>& buffer, const string_view& filepath, size_t maxReadSize = 0) {
// 将 string_view 转换为 string,以确保兼容性
ifstream file(string(filepath), ios_base::binary); // 打开文件,二进制模式
if (!file.is_open()) {
throw runtime_error("Failed to open file: " + string(filepath));
}
// 获取文件大小
file.seekg(0, ios::end);
streampos fileSize = file.tellg();
if (fileSize == -1) {
throw runtime_error("Error reading file size");
}
file.seekg(0, ios::beg);
// 如果指定了最大读取大小,则限制读取大小
size_t readSize = maxReadSize > 0 ? min((size_t)fileSize, maxReadSize) : (size_t)fileSize;
// 调整缓冲区大小
buffer.resize(readSize);
// 读取数据到缓冲区
file.read(reinterpret_cast<char*>(buffer.data()), buffer.size());
if (file.fail() && !file.eof()) {
throw runtime_error("Error reading from file");
}
}
static PdfFontFileType determineFormatCFF(FT_Face face);
static unsigned determineFaceSize(FT_Face face, vector<TableInfo>& tables, unsigned& tableDirSize);
static FT_Face createFaceFromBuffer(const vector<uint8_t>& view, unsigned faceIndex);
static bool isTTCFont(FT_Face face);
static bool isTTCFont(const vector<uint8_t>& face);
static bool tryExtractDataFromTTC(FT_Face face, vector<uint8_t>& buffer);
static void getDataFromFace(FT_Face face, vector<uint8_t>& buffer);
FT_Library FT::GetLibrary() {
struct Init {
Init() : Library(nullptr) {
if (FT_Init_FreeType(&Library))
{
//PODOFO_RAISE_ERROR(PdfErrorCode::FreeTypeError);
}
}
~Init() {
FT_Done_FreeType(Library);
}
FT_Library Library;
};
thread_local Init init;
return init.Library;
}
FT_Face FT::CreateFaceFromBuffer(const vector<uint8_t>& view, unsigned faceIndex, vector<uint8_t>& buffer) {
if (isTTCFont(view)) {
auto face = createFaceFromBuffer(view, faceIndex);
unique_ptr<struct FT_FaceRec_, decltype(&FT_Done_Face)> face_(face, FT_Done_Face);
if (!tryExtractDataFromTTC(face, buffer))
buffer = view;
return createFaceFromBuffer(buffer, 0);
}
else {
buffer = view;
return createFaceFromBuffer(buffer, 0);
}
}
FT_Face FT::ExtractCFFFont(FT_Face face, vector<uint8_t>& buffer) {
FT_ULong size = 0;
FT_Error rc = FT_Load_Sfnt_Table(face, TTAG_CFF, 0, nullptr, &size);
//CHECK_FT_RC(rc, FT_Load_Sfnt_Table);
buffer.resize(size);
rc = FT_Load_Sfnt_Table(face, TTAG_CFF, 0, reinterpret_cast<FT_Byte*>(buffer.data()), &size);
return createFaceFromBuffer(buffer, 0);
}
FT_Face FT::CreateFaceFromBuffer(const vector<uint8_t>& view) {
return createFaceFromBuffer(view, 0);
}
FT_Face FT::CreateFaceFromFile(const string_view& filepath, unsigned faceIndex, vector<uint8_t>& buffer) {
ReadTo(buffer, filepath, sizeof(TTAG_ttcf));
if (isTTCFont(buffer)) {
FT_Error rc;
FT_Face face;
rc = FT_New_Face(FT::GetLibrary(), filepath.data(), faceIndex, &face);
if (rc != 0)
return nullptr;
unique_ptr<struct FT_FaceRec_, decltype(&FT_Done_Face)> face_(face, FT_Done_Face);
if (tryExtractDataFromTTC(face, buffer))
return createFaceFromBuffer(buffer, 0);
}
ReadTo(buffer, filepath);
return createFaceFromBuffer(buffer, 0);
}
vector<uint8_t> FT::GetDataFromFace(FT_Face face) {
vector<uint8_t> buffer;
if (!isTTCFont(face) || !tryExtractDataFromTTC(face, buffer))
getDataFromFace(face, buffer);
return buffer;
}
bool FT::TryGetFontFileFormat(FT_Face face, PdfFontFileType& format) {
string_view formatstr = FT_Get_Font_Format(face);
if (formatstr == "TrueType") {
format = PdfFontFileType::TrueType;
}
else if (formatstr == "Type 1") {
format = PdfFontFileType::Type1;
}
else if (formatstr == "CID Type 1") {
format = PdfFontFileType::Unknown;
return false;
}
else if (formatstr == "CFF") {
format = determineFormatCFF(face);
}
else {
format = PdfFontFileType::Unknown;
return false;
}
return true;
}
bool FT::IsPdfSupported(FT_Face face) {
PdfFontFileType format;
if (!FT::TryGetFontFileFormat(face, format))
return false;
return true;
}
FT_Face createFaceFromBuffer(const vector<uint8_t>& view, unsigned faceIndex) {
FT_Error rc;
FT_Open_Args openArgs{ };
openArgs.flags = FT_OPEN_MEMORY;
openArgs.memory_base = const_cast<FT_Byte*>(reinterpret_cast<const FT_Byte*>(view.data()));
openArgs.memory_size = static_cast<FT_Long>(view.size());
FT_Face face;
rc = FT_Open_Face(FT::GetLibrary(), &openArgs, faceIndex, &face);
if (rc != 0)
return nullptr;
return face;
}
bool isTTCFont(FT_Face face) {
FT_Error rc;
FT_ULong size;
uint32_t tag;
size = sizeof(uint32_t);
rc = FT_Load_Sfnt_Table(face, 0, 0, reinterpret_cast<FT_Byte*>(&tag), &size);
if (rc == 0 && _byteswap_ulong(tag) == TTAG_ttcf)
return true;
return false;
}
bool isTTCFont(const vector<uint8_t>& face) {
uint32_t tag;
if (face.size() < sizeof(tag))
return false;
memcpy(&tag, face.data(), sizeof(tag));
if (_byteswap_ulong(tag) == TTAG_ttcf)
return true;
return false;
}
bool tryExtractDataFromTTC(FT_Face face, vector<uint8_t>& buffer) {
FT_Error rc;
FT_ULong size;
TTCF_Header header;
size = sizeof(TTCF_Header);
rc = FT_Load_Sfnt_Table(face, 0, 0, reinterpret_cast<FT_Byte*>(&header), &size);
//CHECK_FT_RC(rc, FT_Load_Sfnt_Table);
unsigned numFonts = _byteswap_ulong(header.numFonts);
vector<uint32_t> offsets(numFonts);
size = numFonts * sizeof(uint32_t);
rc = FT_Load_Sfnt_Table(face, 0, sizeof(TTCF_Header), reinterpret_cast<FT_Byte*>(offsets.data()), &size);
//CHECK_FT_RC(rc, FT_Load_Sfnt_Table);
if (face->face_index < 0 || static_cast<size_t>(face->face_index) >= offsets.size())
return false;
uint32_t faceOffset = _byteswap_ulong(offsets[face->face_index]);
vector<TableInfo> tables;
unsigned tableDirSize;
buffer.resize(determineFaceSize(face, tables, tableDirSize));
size = tableDirSize;
rc = FT_Load_Sfnt_Table(face, 0, faceOffset, reinterpret_cast<FT_Byte*>(buffer.data()), &size);
//CHECK_FT_RC(rc, FT_Load_Sfnt_Table);
auto tableRecords = reinterpret_cast<TT_TableHeader*>(buffer.data() + TableDirectoryFixedSize);
unsigned offset = tableDirSize;
for (FT_ULong i = 0; i < tables.size(); i++) {
auto& table = tables[i];
size = table.Size;
rc = FT_Load_Sfnt_Table(face, table.Tag, 0, reinterpret_cast<FT_Byte*>(buffer.data()) + offset, &size);
//CHECK_FT_RC(rc, FT_Load_Sfnt_Table);
tableRecords[i].offset = _byteswap_ulong(offset);
offset += table.Size;
}
return true;
}
void getDataFromFace(FT_Face face, vector<uint8_t>& buffer) {
FT_Error rc;
FT_ULong size = 0;
rc = FT_Load_Sfnt_Table(face, 0, 0, nullptr, &size);
//CHECK_FT_RC(rc, FT_Load_Sfnt_Table);
buffer.resize(size);
rc = FT_Load_Sfnt_Table(face, 0, 0, reinterpret_cast<FT_Byte*>(buffer.data()), &size);
//CHECK_FT_RC(rc, FT_Load_Sfnt_Table);
}
PdfFontFileType determineFormatCFF(FT_Face face) {
FT_Error rc;
FT_ULong size;
rc = FT_Sfnt_Table_Info(face, 0, nullptr, &size);
if (rc == 0) {
return PdfFontFileType::OpenTypeCFF;
}
else {
FT_Bool isCid = 0;
(void)FT_Get_CID_Is_Internally_CID_Keyed(face, &isCid);
if (isCid == 1)
return PdfFontFileType::CIDKeyedCFF;
else
return PdfFontFileType::Type1CFF;
}
}
unsigned determineFaceSize(FT_Face face, vector<TableInfo>& tables, unsigned& tableDirSize) {
FT_Error rc;
FT_ULong size;
rc = FT_Sfnt_Table_Info(face, 0, nullptr, &size);
//CHECK_FT_RC(rc, FT_Sfnt_Table_Info);
unsigned faceSize = TableDirectoryFixedSize + (sizeof(TT_TableHeader) * size);
tableDirSize = faceSize;
tables.resize(size);
for (FT_ULong i = 0; i < size; i++) {
auto& table = tables[i];
rc = FT_Sfnt_Table_Info(face, i, &table.Tag, &table.Size);
//CHECK_FT_RC(rc, FT_Sfnt_Table_Info);
faceSize += table.Size;
}
return faceSize;
}
简单使用
#include <iostream>
#include <vector>
#include <string>
#include "freetype_wrap.h" // 假设这是你的头文件路径
using namespace std;
int main() {
try {
// 字体文件路径
//string fontFilePath = "C:/Windows/Fonts/simsun.ttc"; // 替换为实际字体文件路径
string fontFilePath = "C:/Windows/Fonts/simsunb.ttf";
// 缓冲区用于存储字体数据
vector<uint8_t> buffer;
// 创建 FT_Face 对象
FT_Face face = FT::CreateFaceFromFile(fontFilePath, 0, buffer);
if (!face) {
throw runtime_error("Failed to create FT_Face from file.");
}
// 获取字体格式
PdfFontFileType format;
if (!FT::TryGetFontFileFormat(face, format)) {
throw runtime_error("Unsupported or unknown font format.");
}
// 输出字体格式
cout << "Font Format: ";
switch (format) {
case PdfFontFileType::TrueType:
cout << "TrueType" << endl;
break;
case PdfFontFileType::Type1:
cout << "Type 1" << endl;
break;
case PdfFontFileType::OpenTypeCFF:
cout << "OpenType CFF" << endl;
break;
case PdfFontFileType::CIDKeyedCFF:
cout << "CID-Keyed CFF" << endl;
break;
case PdfFontFileType::Type1CFF:
cout << "Type 1 CFF" << endl;
break;
default:
cout << "Unknown" << endl;
break;
}
// 提取字体数据
vector<uint8_t> extractedData = FT::GetDataFromFace(face);
// 输出提取的数据大小
cout << "Extracted Font Data Size: " << extractedData.size() << " bytes" << endl;
// 清理资源
FT_Done_Face(face);
}
catch (const exception& e) {
cerr << "Error: " << e.what() << endl;
}
return 0;
}