#pragmaonce#include<iostream>/** Font style flags used during searches
*/enumclass PdfFontStyle :uint8_t{
None =0,
Italic =1,
Bold =2,// Alias to represent a font with regular style
Regular = None,};
/**
* SPDX-FileCopyrightText: (C) 2011 Dominik Seichter <domseichter@web.de>
* SPDX-FileCopyrightText: (C) 2020 Francesco Pretto <ceztko@gmail.com>
* SPDX-License-Identifier: LGPL-2.0-or-later
*/#ifndefPDF_FONT_CONFIG_WRAPPER_H#definePDF_FONT_CONFIG_WRAPPER_H#include"fontconfigwrap.h"#include<iostream>#include<string>#include"enum.hpp"#include"Enumflags.h"#include<algorithm>#include"nullable.h"#include<unicode/uchar.h>// 用于 UChar32(根据环境可选)#include<vector>
using namespace std;// 使用 extern "C" 声明 FontConfig 类型extern"C"{struct_FcConfig;typedefstruct_FcConfig FcConfig;}enumclass PdfFontConfigSearchFlags :uint8_t{
None =0,
SkipMatchPostScriptName =1,///< Skip matching postscript font name};structPdfFontConfigSearchParams final
{
nullable<PdfFontStyle> Style;
PdfFontConfigSearchFlags Flags = PdfFontConfigSearchFlags::None;///< A font family name specific pattern, to be alternatively used when postscript name match failed
std::string FontFamilyPattern;};/**
* This class initializes and destroys the FontConfig library.
*
* As initializing fontconfig can take a long time, you
* can create a wrapper by yourself to cache initialization of
* fontconfig.
*
* This class is reference counted. The last user of the fontconfig library
* will destroy the fontconfig handle.
*
* The fontconfig library is initialized on first used (lazy loading!)
*/
class PdfFontConfigWrapper final
{
public:/**
* Create a new FontConfigWrapper from a XML config string
*/PdfFontConfigWrapper(const std::string_view& configStr);/**
* Create a new FontConfigWrapper and initialize the fontconfig library.
*/PdfFontConfigWrapper(FcConfig* fcConfig = nullptr);~PdfFontConfigWrapper();/** Get the path of a font file on a Unix system using fontconfig
*
* This method is only available if PoDoFo was compiled with
* fontconfig support. Make sure to lock any FontConfig mutexes before
* calling this method by yourself!
*
* \param fontPattern search pattern of the requested font
* \param style font style
* \param faceIndex index of the face
* \returns the path to the fontfile or an empty string
*/
std::string SearchFontPath(const std::string_view fontPattern,unsigned& faceIndex);
std::string SearchFontPath(const std::string_view fontPattern,const PdfFontConfigSearchParams& params,unsigned& faceIndex);voidAddFontDirectory(const std::string_view& path);
FcConfig*GetFcConfig();/**
* 查找所有支持指定 Unicode 字符的字体。
*
* \param unicodeCodePoint 要查找的字符的 Unicode 码点。
* \returns 包含所有支持该字符的字体文件路径的字符串向量。
*/
std::vector<std::string>FindFontsForUnicode(unsignedint unicodeCodePoint);/**
* 查找所有支持指定 Unicode 字符的字体名称。
*
* \param unicodeCodePoint 要查找的字符的 Unicode 码点。
* \returns 包含所有支持该字符的字体名称的字符串向量。
*/
std::vector<std::string>FindFontNamesForUnicode(unsignedint unicodeCodePoint);
private:PdfFontConfigWrapper(const PdfFontConfigWrapper& rhs)= delete;const PdfFontConfigWrapper& operator=(const PdfFontConfigWrapper& rhs)= delete;voidcreateDefaultConfig();
private:
FcConfig* m_FcConfig;};ENABLE_BITMASK_OPERATORS(PdfFontConfigSearchFlags);ENABLE_BITMASK_OPERATORS(PdfFontStyle);#endif// PDF_FONT_CONFIG_WRAPPER_H
源文件
fontconfigwrap.cpp
/**
* SPDX-FileCopyrightText: (C) 2011 Dominik Seichter <domseichter@web.de>
* SPDX-FileCopyrightText: (C) 2020 Francesco Pretto <ceztko@gmail.com>
* SPDX-License-Identifier: LGPL-2.0-or-later
*/#include"fontconfigwrap.h"#include<fontconfig/fontconfig.h>usingnamespace std;#ifdefined(_WIN32)||defined(__ANDROID__)||defined(__APPLE__)// Windows, Android and Apple architectures don't primarily// use fontconfig. We can supply a fallback configuration,// if a system configuration is not found#defineHAS_FALLBACK_CONFIGURATION#endifPdfFontConfigWrapper::PdfFontConfigWrapper(const string_view& configStr):m_FcConfig(FcConfigCreate()){
m_FcConfig =FcConfigCreate();if(m_FcConfig ==nullptr){}// No system config found, supply a fallback configurationif(!FcConfigParseAndLoadFromMemory(m_FcConfig,(const FcChar8*)configStr.data(),true)){// (PdfErrorCode::InvalidFontData, "Could not parse font config");}if(!FcConfigBuildFonts(m_FcConfig)){}// (PdfErrorCode::InvalidFontData, "Could not parse font config");}PdfFontConfigWrapper::PdfFontConfigWrapper(FcConfig* fcConfig):m_FcConfig(fcConfig){if(fcConfig ==nullptr)createDefaultConfig();}PdfFontConfigWrapper::~PdfFontConfigWrapper(){FcConfigDestroy(m_FcConfig);}
string PdfFontConfigWrapper::SearchFontPath(const string_view fontPattern,unsigned& faceIndex){returnSearchFontPath(fontPattern,{}, faceIndex);}
string PdfFontConfigWrapper::SearchFontPath(const string_view fontPattern,const PdfFontConfigSearchParams& params,unsigned& faceIndex){
FcPattern* matched =nullptr;
FcResult result = FcResultNoMatch;
FcValue value;
string path;
faceIndex =0;auto cleanup =[&](){FcPatternDestroy(matched);};try{if((params.Flags & PdfFontConfigSearchFlags::SkipMatchPostScriptName)== PdfFontConfigSearchFlags::None){// Try to match postscript name only first
unique_ptr<FcPattern,decltype(&FcPatternDestroy)>pattern(FcPatternCreate(), FcPatternDestroy);if(pattern ==nullptr)// (PdfErrorCode::OutOfMemory, "FcPatternCreate returned NULL");if(!FcPatternAddString(pattern.get(), FC_POSTSCRIPT_NAME,(const FcChar8*)fontPattern.data()))// (PdfErrorCode::OutOfMemory, "FcPatternAddString");if(params.Style.has_value()){if(*params.Style == PdfFontStyle::Regular){// Ensure the font will be at least not italic/obliqueif(!FcPatternAddInteger(pattern.get(), FC_SLANT, FC_SLANT_ROMAN)){}// (PdfErrorCode::OutOfMemory, "FcPatternAddInteger");}else{bool isItalic =(*params.Style & PdfFontStyle::Italic)== PdfFontStyle::Italic;bool isBold =(*params.Style & PdfFontStyle::Bold)== PdfFontStyle::Bold;if(isBold &&!FcPatternAddInteger(pattern.get(), FC_WEIGHT, FC_WEIGHT_BOLD)){}// (PdfErrorCode::OutOfMemory, "FcPatternAddInteger");if(isItalic &&!FcPatternAddInteger(pattern.get(), FC_SLANT, FC_SLANT_ITALIC)){}// (PdfErrorCode::OutOfMemory, "FcPatternAddInteger");}}// We will enlist all fonts with the requested style. We produce font// collections that has a limited set of properties, so subsequent match// will be faster
unique_ptr<FcObjectSet,decltype(&FcObjectSetDestroy)>objectSet(FcObjectSetBuild(FC_POSTSCRIPT_NAME, FC_FILE, FC_INDEX,nullptr), FcObjectSetDestroy);
unique_ptr<FcFontSet,decltype(&FcFontSetDestroy)>fontSet(FcFontList(m_FcConfig, pattern.get(), objectSet.get()), FcFontSetDestroy);if(fontSet->nfont >0){
matched = fontSet->fonts[0];FcPatternReference(matched);
result = FcResultMatch;}}if(result == FcResultNoMatch){// Match on family name, using also styles if set
unique_ptr<FcPattern,decltype(&FcPatternDestroy)>pattern(FcPatternCreate(), FcPatternDestroy);if(pattern ==nullptr)// (PdfErrorCode::OutOfMemory, "FcPatternCreate returned NULL");if(params.FontFamilyPattern.length()==0){if(!FcPatternAddString(pattern.get(), FC_FAMILY,(const FcChar8*)fontPattern.data())){}// (PdfErrorCode::OutOfMemory, "FcPatternAddString");}else{if(!FcPatternAddString(pattern.get(), FC_FAMILY,(const FcChar8*)params.FontFamilyPattern.data())){}// (PdfErrorCode::OutOfMemory, "FcPatternAddString");}if(params.Style.has_value()){// NOTE: No need to set FC_SLANT_ROMAN, FC_WEIGHT_MEDIUM for PdfFontStyle::Regular.// It's done already by FcDefaultSubstitutebool isItalic =(*params.Style & PdfFontStyle::Italic)== PdfFontStyle::Italic;bool isBold =(*params.Style & PdfFontStyle::Bold)== PdfFontStyle::Bold;if(isBold &&!FcPatternAddInteger(pattern.get(), FC_WEIGHT, FC_WEIGHT_BOLD)){}// (PdfErrorCode::OutOfMemory, "FcPatternAddInteger");if(isItalic &&!FcPatternAddInteger(pattern.get(), FC_SLANT, FC_SLANT_ITALIC)){}// (PdfErrorCode::OutOfMemory, "FcPatternAddInteger");}// Perform recommended normalization, as documented in// https://www.freedesktop.org/software/fontconfig/fontconfig-devel/fcfontmatch.htmlFcDefaultSubstitute(pattern.get());
matched =FcFontMatch(m_FcConfig, pattern.get(),&result);}if(result != FcResultNoMatch){(void)FcPatternGet(matched, FC_FILE,0,&value);
path =reinterpret_cast<constchar*>(value.u.s);(void)FcPatternGet(matched, FC_INDEX,0,&value);
faceIndex =(unsigned)value.u.i;#if_WIN32// Font config in Windows returns unix conventional path// separator. Fix it
std::replace(path.begin(), path.end(),'/','\\');#endif}}catch(const exception& ex){// (PdfLogSeverity::Error, ex.what());}catch(...){// (PdfLogSeverity::Error, "Unknown error during FontConfig search");}cleanup();return path;}voidPdfFontConfigWrapper::AddFontDirectory(const string_view& path){if(!FcConfigAppFontAddDir(m_FcConfig,(const FcChar8*)path.data())){}// (PdfErrorCode::InvalidHandle, "Unable to add font directory");}
FcConfig*PdfFontConfigWrapper::GetFcConfig(){return m_FcConfig;}voidPdfFontConfigWrapper::createDefaultConfig(){#ifdef_WIN32constchar* fontconf =R"(<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<dir>WINDOWSFONTDIR</dir>
<dir>WINDOWSUSERFONTDIR</dir>
<dir prefix="xdg">fonts</dir>
<cachedir>LOCAL_APPDATA_FONTCONFIG_CACHE</cachedir>
<cachedir prefix="xdg">fontconfig</cachedir>
</fontconfig>
)";#elif__ANDROID__// On android fonts are located in /system/fontsconstchar* fontconf =R"(<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<dir>/system/fonts</dir>
<dir prefix="xdg">fonts</dir>
<cachedir prefix="xdg">fontconfig</cachedir>
</fontconfig>
)";#elif__APPLE__// Fonts location https://stackoverflow.com/a/2557291/213871constchar* fontconf =R"(<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<dir>/System/Library/Fonts</dir>
<dir prefix="xdg">fonts</dir>
<cachedir prefix="xdg">fontconfig</cachedir>
</fontconfig>
)";#endif#ifdefHAS_FALLBACK_CONFIGURATION// Implement the fallback as discussed in fontconfig mailing list// https://lists.freedesktop.org/archives/fontconfig/2022-February/006883.htmlauto config =FcConfigCreate();if(config ==nullptr)// (PdfErrorCode::InvalidHandle, "Could not allocate font config");// Manually try to load the config to determine// if a system configuration exists. Tell FontConfig// to not complain if it doesn't(void)FcConfigParseAndLoad(config,nullptr, FcFalse);auto configFiles =FcConfigGetConfigFiles(config);if(FcStrListNext(configFiles)==nullptr){// No system config found, supply a fallback configurationif(!FcConfigParseAndLoadFromMemory(config,(const FcChar8*)fontconf,true)){FcConfigDestroy(config);}// Load fonts for the configif(!FcConfigBuildFonts(config)){FcConfigDestroy(config);// (PdfErrorCode::InvalidFontData, "Could not parse font config");}
m_FcConfig = config;}else{// Destroy the temporary configFcStrListDone(configFiles);FcConfigDestroy(config);#endif// Default initialize a local FontConfig configuration// http://mces.blogspot.com/2015/05/how-to-use-custom-application-fonts.html
m_FcConfig =FcInitLoadConfigAndFonts();// (m_FcConfig != nullptr);#ifdefHAS_FALLBACK_CONFIGURATION}#endif}#include<unicode/uchar.h>// 用于 UChar32(根据环境可选)#include<vector>
std::vector<std::string>PdfFontConfigWrapper::FindFontsForUnicode(unsignedint unicodeCodePoint){
FcPattern* pattern =nullptr;// Fontconfig 的模式对象
std::vector<std::string> fontPaths;// 存储匹配字体路径的向量auto cleanup =[&](){if(pattern)FcPatternDestroy(pattern);// 销毁模式对象};try{// 创建一个新的模式对象
pattern =FcPatternCreate();if(!pattern)return{};// 如果无法创建模式对象,则返回空向量// 创建字符集并添加指定的 Unicode 字符
FcCharSet* charSet =FcCharSetCreate();// 创建字符集对象if(!charSet ||!FcCharSetAddChar(charSet, unicodeCodePoint))// 添加字符失败{FcCharSetDestroy(charSet);// 销毁字符集对象return{};// 返回空向量}FcPatternAddCharSet(pattern, FC_CHARSET, charSet);// 将字符集添加到模式中FcCharSetDestroy(charSet);// 销毁字符集对象// 获取所有匹配的字体列表
unique_ptr<FcFontSet,decltype(&FcFontSetDestroy)>fontSet(FcFontList(m_FcConfig, pattern,nullptr), FcFontSetDestroy);if(!fontSet || fontSet->nfont ==0)// 如果没有找到匹配字体{cleanup();// 清理资源return{};// 返回空向量}// 遍历字体列表,提取每个字体的文件路径for(int i =0; i < fontSet->nfont;++i){
FcPattern* fontPattern = fontSet->fonts[i];
FcValue value;if(FcPatternGet(fontPattern, FC_FILE,0,&value)== FcResultMatch)// 获取字体文件路径{
std::string path =reinterpret_cast<constchar*>(value.u.s);// 转换为字符串#if_WIN32// 在 Windows 平台上将路径中的斜杠替换为反斜杠
std::replace(path.begin(), path.end(),'/','\\');#endif
fontPaths.push_back(path);// 将路径添加到结果向量中}}}catch(const std::exception& ex)// 捕获标准异常{cleanup();// 清理资源return{};// 返回空向量}catch(...)// 捕获所有其他异常{cleanup();// 清理资源return{};// 返回空向量}cleanup();// 清理资源return fontPaths;// 返回字体路径集合}
std::vector<std::string>PdfFontConfigWrapper::FindFontNamesForUnicode(unsignedint unicodeCodePoint){
FcPattern* pattern =nullptr;// Fontconfig 的模式对象
std::vector<std::string> fontNames;// 存储匹配字体名称的向量auto cleanup =[&](){if(pattern)FcPatternDestroy(pattern);// 销毁模式对象};try{// 创建一个新的模式对象
pattern =FcPatternCreate();if(!pattern)return{};// 如果无法创建模式对象,则返回空向量// 创建字符集并添加指定的 Unicode 字符
FcCharSet* charSet =FcCharSetCreate();// 创建字符集对象if(!charSet ||!FcCharSetAddChar(charSet, unicodeCodePoint))// 添加字符失败{FcCharSetDestroy(charSet);// 销毁字符集对象return{};// 返回空向量}FcPatternAddCharSet(pattern, FC_CHARSET, charSet);// 将字符集添加到模式中FcCharSetDestroy(charSet);// 销毁字符集对象// 获取所有匹配的字体列表
unique_ptr<FcFontSet,decltype(&FcFontSetDestroy)>fontSet(FcFontList(m_FcConfig, pattern,nullptr), FcFontSetDestroy);if(!fontSet || fontSet->nfont ==0)// 如果没有找到匹配字体{cleanup();// 清理资源return{};// 返回空向量}// 遍历字体列表,提取每个字体的名称for(int i =0; i < fontSet->nfont;++i){
FcPattern* fontPattern = fontSet->fonts[i];
FcChar8* familyName =nullptr;if(FcPatternGetString(fontPattern, FC_FAMILY,0,&familyName)== FcResultMatch)// 获取字体家族名称{
std::string name(reinterpret_cast<constchar*>(familyName));// 转换为字符串
fontNames.push_back(name);// 将名称添加到结果向量中}}}catch(const std::exception& ex)// 捕获标准异常{cleanup();// 清理资源return{};// 返回空向量}catch(...)// 捕获所有其他异常{cleanup();// 清理资源return{};// 返回空向量}cleanup();// 清理资源return fontNames;// 返回字体名称集合}
一、概念与原理 QOS即服务质量(Quality of Service)是一种网络技术,用于管理和保证网络中不同类型的质量和性能。它通过设置优先级和带宽限制等策略,确保关键应用(如视频会议、语音通信)的数据包能够在网络…