FontConfig封装分享
- 互联网
- 2025-08-28 05:45:02

文章目录 头文件nullable.henum.hppEnumFlags.hfontconfigwrap.h 源文件fontconfigwrap.cppmain.cpp 头文件 nullable.h /** * SPDX-FileCopyrightText: (C) 2022 Francesco Pretto <ceztko@gmail > * SPDX-License-Identifier: LGPL-2.0-or-later * SPDX-License-Identifier: MPL-2.0 */ #ifndef AUX_NULLABLE_H #define AUX_NULLABLE_H #include <cstddef> #include <stdexcept> #include <type_traits> class bad_nullable_access : public std::runtime_error { public: bad_nullable_access() : std::runtime_error("nullable object doesn't have a value") {} }; /** * Alternative to std::optional that supports reference (but not pointer) types */ template <typename T, typename = std::enable_if_t<!std::is_pointer_v<T>>> class nullable final { public: nullable() : m_value{ }, m_hasValue(false) { } nullable(T value) : m_value(std::move(value)), m_hasValue(true) { } nullable(std::nullptr_t) : m_value{ }, m_hasValue(false) { } nullable(const nullable& value) = default; nullable& operator=(const nullable& value) = default; nullable& operator=(T value) { m_hasValue = true; m_value = std::move(value); return *this; } nullable& operator=(std::nullptr_t) { m_hasValue = false; m_value = { }; return *this; } const T& value() const { if (!m_hasValue) throw bad_nullable_access(); return m_value; } bool has_value() const { return m_hasValue; } const T* operator->() const { return &m_value; } const T& operator*() const { return m_value; } public: template <typename T2> friend bool operator==(const nullable<T2>& lhs, const nullable<T2>& rhs); template <typename T2> friend bool operator!=(const nullable<T2>& lhs, const nullable<T2>& rhs); template <typename T2> friend bool operator==(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs); template <typename T2> friend bool operator!=(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs); template <typename T2> friend bool operator==(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs); template <typename T2> friend bool operator!=(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs); template <typename T2> friend bool operator==(const nullable<T2>& lhs, const T2& rhs); template <typename T2> friend bool operator==(const T2& lhs, const nullable<T2>& rhs); template <typename T2> friend bool operator!=(const nullable<T2>& lhs, const T2& rhs); template <typename T2> friend bool operator!=(const T2& lhs, const nullable<T2>& rhs); template <typename T2> friend std::enable_if_t<!std::is_reference_v<T2>, bool> operator==(const nullable<T2>& lhs, std::nullptr_t); template <typename T2> friend std::enable_if_t<!std::is_reference_v<T2>, bool> operator==(std::nullptr_t, const nullable<T2>& rhs); template <typename T2> friend std::enable_if_t<!std::is_reference_v<T2>, bool> operator!=(const nullable<T2>& lhs, std::nullptr_t); template <typename T2> friend std::enable_if_t<!std::is_reference_v<T2>, bool> operator!=(std::nullptr_t, const nullable<T2>& rhs); private: T m_value; bool m_hasValue; }; // Template specialization for references template <typename TRef> class nullable<TRef, std::enable_if_t<std::is_reference_v<TRef>>> final { using T = std::remove_reference_t<TRef>; public: nullable() : m_value{ } { } nullable(T& value) : m_value(&value) { } nullable(T* value) : m_value(value) { } nullable(std::nullptr_t) : m_value{ } { } // Allow nullable<const T&>::nullable(const nullable<T&>&) template <typename T2, std::enable_if_t<std::is_convertible_v<std::add_pointer_t<std::remove_reference_t<T2>>, std::add_pointer_t<std::remove_reference_t<T>>>, int> = 0> nullable(const nullable<T2&>& value) : m_value(reinterpret_cast<const nullable&>(value).m_value) { } nullable(const nullable& value) = default; nullable& operator=(const nullable& value) = default; T& value() { if (m_value == nullptr) throw bad_nullable_access(); return *m_value; } bool has_value() const { return m_value != nullptr; } T* operator->() const { return m_value; } T& operator*() const { return *m_value; } public: template <typename T2> friend bool operator==(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs); template <typename T2> friend bool operator==(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs); template <typename T2> friend bool operator!=(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs); template <typename T2> friend bool operator!=(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs); template <typename T2> friend bool operator==(const nullable<T2&>& lhs, const nullable<T2&>& rhs); template <typename T2> friend bool operator==(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs); template <typename T2> friend bool operator==(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs); template <typename T2> friend bool operator!=(const nullable<T2&>& lhs, const nullable<T2&>& rhs); template <typename T2> friend bool operator!=(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs); template <typename T2> friend bool operator!=(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs); template <typename T2> friend std::enable_if_t<std::is_reference_v<T2>, bool> operator==(const nullable<T2>& lhs, std::nullptr_t); template <typename T2> friend std::enable_if_t<std::is_reference_v<T2>, bool> operator==(std::nullptr_t, const nullable<T2>& rhs); template <typename T2> friend std::enable_if_t<std::is_reference_v<T2>, bool> operator!=(const nullable<T2>& lhs, std::nullptr_t); template <typename T2> friend std::enable_if_t<std::is_reference_v<T2>, bool> operator!=(std::nullptr_t, const nullable<T2>& rhs); template <typename T2> friend bool operator==(const nullable<T2&>& lhs, const T2* rhs); template <typename T2> friend bool operator==(const T2* lhs, const nullable<T2&>& rhs); template <typename T2> friend bool operator!=(const nullable<T2&>& lhs, const T2* rhs); template <typename T2> friend bool operator!=(const T2* lhs, const nullable<T2&>& rhs); private: T* m_value; }; template <typename T2> bool operator==(const nullable<T2>& lhs, const nullable<T2>& rhs) { if (lhs.m_hasValue != rhs.m_hasValue) return false; if (lhs.m_hasValue) return lhs.m_value == rhs.m_value; else return true; } template <typename T2> bool operator!=(const nullable<T2>& lhs, const nullable<T2>& rhs) { if (lhs.m_hasValue != rhs.m_hasValue) return true; if (lhs.m_hasValue) return lhs.m_value != rhs.m_value; else return false; } template <typename T2> bool operator==(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs) { if (lhs.m_hasValue != rhs.has_value()) return false; if (lhs.m_hasValue) return lhs.m_value == *rhs.m_value; else return true; } template <typename T2> bool operator!=(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs) { if (lhs.m_hasValue != rhs.has_value()) return true; if (lhs.m_hasValue) return lhs.m_value != *rhs.m_value; else return false; } template <typename T2> bool operator==(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs) { if (lhs.has_value() != rhs.m_hasValue) return false; if (lhs.has_value()) return *lhs.m_value == rhs.m_value; else return true; } template <typename T2> bool operator!=(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs) { if (lhs.has_value() != rhs.m_hasValue) return true; if (lhs.has_value()) return *lhs.m_value != rhs.m_value; else return false; } template <typename T2> bool operator==(const nullable<T2&>& lhs, const nullable<T2&>& rhs) { if (lhs.has_value() != rhs.has_value()) return false; if (lhs.has_value()) return *lhs.m_value == *rhs.m_value; else return true; } template <typename T2> bool operator!=(const nullable<T2&>& lhs, const nullable<T2&>& rhs) { if (lhs.has_value() != rhs.has_value()) return true; if (lhs.has_value()) return *lhs.m_value != *rhs.m_value; else return false; } template <typename T2> bool operator==(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs) { if (!lhs.has_value()) return false; return *lhs.m_value == rhs; } template <typename T2> bool operator!=(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs) { if (!lhs.has_value()) return true; return *lhs.m_value != rhs; } template <typename T2> bool operator==(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs) { if (!rhs.has_value()) return false; return lhs == *rhs.m_value; } template <typename T2> bool operator!=(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs) { if (!rhs.has_value()) return true; return lhs != *rhs.m_value; } template <typename T2> bool operator==(const nullable<T2>& lhs, const T2& rhs) { if (!lhs.m_hasValue) return false; return lhs.m_value == rhs; } template <typename T2> bool operator!=(const nullable<T2>& lhs, const T2& rhs) { if (!lhs.m_hasValue) return true; return lhs.m_value != rhs; } template <typename T2> bool operator==(const T2& lhs, const nullable<T2>& rhs) { if (!rhs.m_hasValue) return false; return lhs == rhs.m_value; } template <typename T2> bool operator!=(const T2& lhs, const nullable<T2>& rhs) { if (!rhs.m_hasValue) return true; return lhs != rhs.m_value; } template <typename T2> std::enable_if_t<!std::is_reference_v<T2>, bool> operator==(const nullable<T2>& lhs, std::nullptr_t) { return !lhs.m_hasValue; } template <typename T2> std::enable_if_t<!std::is_reference_v<T2>, bool> operator!=(const nullable<T2>& lhs, std::nullptr_t) { return lhs.m_hasValue; } template <typename T2> std::enable_if_t<!std::is_reference_v<T2>, bool> operator==(std::nullptr_t, const nullable<T2>& rhs) { return !rhs.m_hasValue; } template <typename T2> std::enable_if_t<!std::is_reference_v<T2>, bool> operator!=(std::nullptr_t, const nullable<T2>& rhs) { return rhs.m_hasValue; } template <typename T2> std::enable_if_t<std::is_reference_v<T2>, bool> operator==(const nullable<T2>& lhs, std::nullptr_t) { return lhs.m_value == nullptr; } template <typename T2> std::enable_if_t<std::is_reference_v<T2>, bool> operator==(std::nullptr_t, const nullable<T2>& rhs) { return rhs.m_value == nullptr; } template <typename T2> std::enable_if_t<std::is_reference_v<T2>, bool> operator!=(const nullable<T2>& lhs, std::nullptr_t) { return lhs.m_value != nullptr; } template <typename T2> std::enable_if_t<std::is_reference_v<T2>, bool> operator!=(std::nullptr_t, const nullable<T2>& rhs) { return rhs.m_value != nullptr; } template<typename T2> bool operator==(const nullable<T2&>& lhs, const T2* rhs) { return lhs.m_value == rhs; } template<typename T2> bool operator==(const T2* lhs, const nullable<T2&>& rhs) { return lhs == rhs.m_value; } template<typename T2> bool operator!=(const nullable<T2&>& lhs, const T2* rhs) { return lhs.m_value != rhs; } template<typename T2> bool operator!=(const T2* lhs, const nullable<T2&>& rhs) { return lhs != rhs.m_value; } #endif // AUX_NULLABLE_H enum.hpp #pragma once #include<iostream> /** Font style flags used during searches */ enum class PdfFontStyle : uint8_t { None = 0, Italic = 1, Bold = 2, // Alias to represent a font with regular style Regular = None, }; EnumFlags.h #ifndef AUX_ENUM_FLAGS_H #define AUX_ENUM_FLAGS_H #include <type_traits> // http://blog.bitwigglers.org/using-enum-classes-as-type-safe-bitmasks/ // gist.github /derofim/0188769131c62c8aff5e1da5740b3574 template<typename Enum> struct EnableBitMaskOperators { static const bool enable = false; }; #define ENABLE_BITMASK_OPERATORS(x) \ template<> \ struct EnableBitMaskOperators<x> \ { \ static const bool enable = true; \ }; template<typename Enum> constexpr typename std::enable_if<EnableBitMaskOperators<Enum>::enable, Enum>::type operator &(Enum lhs, Enum rhs) noexcept { using underlying = typename std::underlying_type<Enum>::type; return static_cast<Enum> ( static_cast<underlying>(lhs) & static_cast<underlying>(rhs) ); } template<typename Enum> constexpr typename std::enable_if<EnableBitMaskOperators<Enum>::enable, Enum>::type operator ^(Enum lhs, Enum rhs) noexcept { using underlying = typename std::underlying_type<Enum>::type; return static_cast<Enum> ( static_cast<underlying>(lhs) ^ static_cast<underlying>(rhs) ); } template<typename Enum> constexpr typename std::enable_if<EnableBitMaskOperators<Enum>::enable, Enum>::type operator ~(Enum rhs) noexcept { using underlying = typename std::underlying_type<Enum>::type; return static_cast<Enum> ( ~static_cast<underlying>(rhs) ); } template<typename Enum> constexpr typename std::enable_if<EnableBitMaskOperators<Enum>::enable, Enum>::type operator |(Enum lhs, Enum rhs) noexcept { using underlying = typename std::underlying_type<Enum>::type; return static_cast<Enum> ( static_cast<underlying>(lhs) | static_cast<underlying>(rhs) ); } template<typename Enum> constexpr typename std::enable_if<EnableBitMaskOperators<Enum>::enable, Enum>::type& operator &=(Enum& lhs, Enum rhs) noexcept { using underlying = typename std::underlying_type<Enum>::type; lhs = static_cast<Enum> ( static_cast<underlying>(lhs) & static_cast<underlying>(rhs) ); return lhs; } template<typename Enum> constexpr typename std::enable_if<EnableBitMaskOperators<Enum>::enable, Enum>::type& operator ^=(Enum& lhs, Enum rhs) noexcept { using underlying = typename std::underlying_type<Enum>::type; lhs = static_cast<Enum> ( static_cast<underlying>(lhs) ^ static_cast<underlying>(rhs) ); return lhs; } template<typename Enum> constexpr typename std::enable_if<EnableBitMaskOperators<Enum>::enable, Enum&>::type operator |=(Enum& lhs, Enum rhs) noexcept { using underlying = typename std::underlying_type<Enum>::type; lhs = static_cast<Enum> ( static_cast<underlying>(lhs) | static_cast<underlying>(rhs) ); return lhs; } #endif // AUX_ENUM_FLAGS_H fontconfigwrap.h /** * SPDX-FileCopyrightText: (C) 2011 Dominik Seichter <domseichter@web.de> * SPDX-FileCopyrightText: (C) 2020 Francesco Pretto <ceztko@gmail > * SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef PDF_FONT_CONFIG_WRAPPER_H #define PDF_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; typedef struct _FcConfig FcConfig; } enum class PdfFontConfigSearchFlags : uint8_t { None = 0, SkipMatchPostScriptName = 1, ///< Skip matching postscript font name }; struct PdfFontConfigSearchParams 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); void AddFontDirectory(const std::string_view& path); FcConfig* GetFcConfig(); /** * 查找所有支持指定 Unicode 字符的字体。 * * \param unicodeCodePoint 要查找的字符的 Unicode 码点。 * \returns 包含所有支持该字符的字体文件路径的字符串向量。 */ std::vector<std::string> FindFontsForUnicode(unsigned int unicodeCodePoint); /** * 查找所有支持指定 Unicode 字符的字体名称。 * * \param unicodeCodePoint 要查找的字符的 Unicode 码点。 * \returns 包含所有支持该字符的字体名称的字符串向量。 */ std::vector<std::string> FindFontNamesForUnicode(unsigned int unicodeCodePoint); private: PdfFontConfigWrapper(const PdfFontConfigWrapper& rhs) = delete; const PdfFontConfigWrapper& operator=(const PdfFontConfigWrapper& rhs) = delete; void createDefaultConfig(); 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 > * SPDX-License-Identifier: LGPL-2.0-or-later */ #include"fontconfigwrap.h" #include <fontconfig/fontconfig.h> using namespace std; #if defined(_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 #define HAS_FALLBACK_CONFIGURATION #endif PdfFontConfigWrapper::PdfFontConfigWrapper(const string_view& configStr) : m_FcConfig(FcConfigCreate()) { m_FcConfig = FcConfigCreate(); if (m_FcConfig == nullptr) { } // No system config found, supply a fallback configuration if (!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) { return SearchFontPath(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/oblique if (!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 FcDefaultSubstitute 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"); } // Perform recommended normalization, as documented in // .freedesktop.org/software/fontconfig/fontconfig-devel/fcfontmatch.html FcDefaultSubstitute(pattern.get()); matched = FcFontMatch(m_FcConfig, pattern.get(), &result); } if (result != FcResultNoMatch) { (void)FcPatternGet(matched, FC_FILE, 0, &value); path = reinterpret_cast<const char*>(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; } void PdfFontConfigWrapper::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; } void PdfFontConfigWrapper::createDefaultConfig() { #ifdef _WIN32 const char* 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/fonts const char* 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 stackoverflow /a/2557291/213871 const char* 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 #ifdef HAS_FALLBACK_CONFIGURATION // Implement the fallback as discussed in fontconfig mailing list // lists.freedesktop.org/archives/fontconfig/2022-February/006883.html auto 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 configuration if (!FcConfigParseAndLoadFromMemory(config, (const FcChar8*)fontconf, true)) { FcConfigDestroy(config); } // Load fonts for the config if (!FcConfigBuildFonts(config)) { FcConfigDestroy(config); // (PdfErrorCode::InvalidFontData, "Could not parse font config"); } m_FcConfig = config; } else { // Destroy the temporary config FcStrListDone(configFiles); FcConfigDestroy(config); #endif // Default initialize a local FontConfig configuration // http://mces.blogspot /2015/05/how-to-use-custom-application-fonts.html m_FcConfig = FcInitLoadConfigAndFonts(); // (m_FcConfig != nullptr); #ifdef HAS_FALLBACK_CONFIGURATION } #endif } #include <unicode/uchar.h> // 用于 UChar32(根据环境可选) #include <vector> std::vector<std::string> PdfFontConfigWrapper::FindFontsForUnicode(unsigned int 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<const char*>(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(unsigned int 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<const char*>(familyName)); // 转换为字符串 fontNames.push_back(name); // 将名称添加到结果向量中 } } } catch (const std::exception& ex) // 捕获标准异常 { cleanup(); // 清理资源 return {}; // 返回空向量 } catch (...) // 捕获所有其他异常 { cleanup(); // 清理资源 return {}; // 返回空向量 } cleanup(); // 清理资源 return fontNames; // 返回字体名称集合 } main.cpp #include "fontconfigwrap.h" #include <iostream> #include <string> int main() { #if 0 // 创建 PdfFontConfigWrapper 对象 PdfFontConfigWrapper wrapper; // 添加自定义字体目录 wrapper.AddFontDirectory("/usr/share/fonts"); // 简单搜索字体路径 unsigned faceIndex = 0; std::string fontPath = wrapper.SearchFontPath("Arial", faceIndex); if (!fontPath.empty()) { std::cout << "Font Path (Simple): " << fontPath << ", Face Index: " << faceIndex << std::endl; } else { std::cout << "Font not found (Simple)!" << std::endl; } // 高级搜索 PdfFontConfigSearchParams params; params.Style = PdfFontStyle::Bold | PdfFontStyle::Italic; params.Flags = PdfFontConfigSearchFlags::SkipMatchPostScriptName; params.FontFamilyPattern = "Sans"; std::string advancedFontPath = wrapper.SearchFontPath("Arial", params, faceIndex); if (!advancedFontPath.empty()) { std::cout << "Font Path (Advanced): " << advancedFontPath << ", Face Index: " << faceIndex << std::endl; } else { std::cout << "Font not found (Advanced)!" << std::endl; } #else PdfFontConfigWrapper fontConfig; // 创建 FontConfig 包装器对象 unsigned int unicodeCodePoint = 0x4E2D; // 中文字符 "中" 的 Unicode 码点 std::vector<std::string> fontPaths = fontConfig.FindFontsForUnicode(unicodeCodePoint); // 查找支持该字符的所有字体 if (!fontPaths.empty()) // 如果找到了字体 { std::cout << "支持 Unicode 字符的所有字体: " << std::endl; for (const auto& path : fontPaths) // 遍历所有字体路径 { std::cout << path << std::endl; } } else { std::cout << "未找到支持指定 Unicode 字符的字体。" << std::endl; } std::vector<std::string> fontNames = fontConfig.FindFontNamesForUnicode(unicodeCodePoint); // 查找支持该字符的所有字体名称 if (!fontNames.empty()) // 如果找到了字体 { std::cout << "支持 Unicode 字符的所有字体名称: " << std::endl; for (const auto& name : fontNames) // 遍历所有字体名称 { std::cout << name << std::endl; } } else { std::cout << "未找到支持指定 Unicode 字符的字体。" << std::endl; } #endif return 0; }
FontConfig封装分享由讯客互联互联网栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“FontConfig封装分享”