当前位置: 首页 > news >正文

FontConfig封装分享

文章目录

  • 头文件
    • nullable.h
    • enum.hpp
    • EnumFlags.h
    • fontconfigwrap.h
  • 源文件
    • fontconfigwrap.cpp
    • main.cpp

头文件

nullable.h

 
/**
 * SPDX-FileCopyrightText: (C) 2022 Francesco Pretto <ceztko@gmail.com>
 * 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/
// https://gist.github.com/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.com>
 * 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.com>
 * 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
            // https://www.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 https://stackoverflow.com/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
    // https://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.com/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;
}

相关文章:

  • Token Embedding(词嵌入)和Positional Encoding(位置编码)的矩阵形状关系及转换过程
  • [grub]修改启动项选项来区分不同系统
  • fastapi sqlalchemy 日志 logging 写入异常 多进程文件写入异常
  • python-leetcode 37.翻转二叉树
  • Javascript网页设计实例:通过JS实现上传Markdown转化为脑图并下载脑图
  • 火语言RPA--Excel关闭保存文档
  • 【HarmonyOS Next】鸿蒙监听手机按键
  • 汇能感知的光谱相机/模块产品有哪些?
  • 【python】tkinter简要教程
  • oppo,汤臣倍健,康冠科技,高途教育25届春招内推
  • 记录一下windows11编译Openpose的过程
  • 使用VSCODE开发C语言程序
  • 【PLL】应用:时钟生成
  • 【项目日记】仿RabbitMQ实现消息队列 --- 模块设计
  • 【云安全】云原生-Docker(六)Docker API 未授权访问
  • unity学习49:寻路网格链接 offMeshLinks, 以及传送门效果
  • 使用FFmpeg将PCMA格式的WAV文件转换为16K采样率的PCM WAV文件
  • 基于SpringBoot实现的宠物领养系统平台功能一
  • JUC并发编程——Java线程(一)
  • 从线程池到负载均衡:高并发场景下的系统优化实战
  • 贝壳一季度收入增长42%:二手房市场活跃度维持在高位
  • 一图看懂|印巴交火后,双方基地受损多少?
  • 俄方代表团抵达土耳其,俄乌直接谈判有望于当地时间上午重启
  • 因操纵乙烯价格再遭诉讼,科莱恩等四家企业被陶氏索赔60亿
  • 美国4月CPI同比上涨2.3%低于预期,为2021年2月来最小涨幅
  • 北京今日白天超30℃晚间下冰雹,市民称“没见过这么大颗的”