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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/972464.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AD(Altium Designer)器件封装——立创商城导出原理图和PCB完成器件封装操作指南

1、立创商城下载原理图和PCB图 1.1 打开立创商城 官网:www.SZLCSC.COM 1.2 寻找所需器件 以芯片为例 器件类——>芯片类——>对应芯片 1.3 确定所需芯片 确定芯片——>数据手册 1.4 打开原理图和PCB图 1:原理图 2:PCB 3:打开 1.5 导出原理图 操作

Kubernetes:EKS 中 Istio Ingress Gateway 负载均衡器配置及常见问题解析

引言 在云原生时代&#xff0c;Kubernetes 已经成为容器编排的事实标准。AWS EKS (Elastic Kubernetes Service) 作为一项完全托管的 Kubernetes 服务&#xff0c;简化了在 AWS 上运行 Kubernetes 的复杂性。Istio 作为服务网格领域的佼佼者&#xff0c;为微服务提供了流量管理…

【CUDA 】第4章 全局内存——4.4 核函数可达到的带宽(3展开转置)【补图】

CUDA C编程笔记 第四章 全局内存4.4 核函数可达到的带宽4.4.2.3 展开转置【为每个线程分配更独立的任务】 待解决的问题&#xff1a; 第四章 全局内存 4.4 核函数可达到的带宽 4.4.2.3 展开转置【为每个线程分配更独立的任务】 展开&#xff1a;提高转置内存带宽的利用率&a…

后端重载和重写的区别

重载 相同的方法名&#xff0c;形参数量不同或者参数顺序不同或者参数类型不同称为方法重载 重写 方法名和形参列表相同 重写方法前提:必须存在继承关系 (1)方法重载是&#xff1a;一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法…

字节最新AI 版IDE:用Trae开发网站打包信息追踪插件,国产版Cursor表现如何?

文章首发地址&#xff1a;https://juejin.cn/post/7472684607365922850 插件背景及项目概述 在现代前端开发中&#xff0c;我们常常需要获取当前线上环境的代码构建信息&#xff0c;如项目打包人、打包时间、Git版本信息等。在持续集成/持续交付&#xff08;CI/CD&#xff09…

MySQL数据库入门到大蛇尚硅谷宋红康老师笔记 高级篇 part 1

第01章_Linux下MySQL的安装与使用 首先在vmware中下载centos7&#xff0c;实际上8更好一点&#xff0c;不过centos已经是时代的眼泪了&#xff0c;我之前已经教过了&#xff0c;不过是忘了&#xff0c;所以重新说一遍&#xff0c;看文档即可 2.开机前修改mac地址 &#xff0…

网络工程师 (47)QOS

一、概念与原理 QOS即服务质量&#xff08;Quality of Service&#xff09;是一种网络技术&#xff0c;用于管理和保证网络中不同类型的质量和性能。它通过设置优先级和带宽限制等策略&#xff0c;确保关键应用&#xff08;如视频会议、语音通信&#xff09;的数据包能够在网络…

什么是幂等性?

一.幂等性 什么是幂等性&#xff1f; 在计算机科学和数学领域中&#xff0c;” 幂等性 “虽然源于相同的概念&#xff0c;但其应用和具体含义有所不同 在数学中&#xff1a;幂等性是一个代数性质&#xff0c;描述的是一个操作或函数在多次应用后结果不变的特性 在分布式系统…

PyCharm Terminal 自动切换至虚拟环境

PyCharm 虚拟环境配置完毕后&#xff0c;打开终端&#xff0c;没有跟随虚拟环境切换&#xff0c;如图所示&#xff1a; 此时&#xff0c;需要手动将终端切换为 Command Prompt 模式 于是&#xff0c;自动切换至虚拟环境 每次手动切换&#xff0c;比较麻烦&#xff0c;可以单…

YOLOv12从入门到入土(含结构图)

论文链接&#xff1a;https://arxiv.org/abs/2502.12524 代码链接&#xff1a;https://github.com/sunsmarterjie/yolov12 文章摘要&#xff1a; 长期以来&#xff0c;增强YOLO框架的网络架构一直至关重要&#xff0c;但一直专注于基于cnn的改进&#xff0c;尽管注意力机制在建…

我用AI做数据分析之数据清洗

我用AI做数据分析之数据清洗 AI与数据分析的融合效果怎样&#xff1f; 这里描述自己在使用AI进行数据分析&#xff08;数据清洗&#xff09;过程中的几个小故事&#xff1a; 1. 变量名的翻译 有一个项目是某医生自己收集的数据&#xff0c;变量名使用的是中文&#xff0c;分…

解锁机器学习核心算法 | K-平均:揭开K-平均算法的神秘面纱

一、引言 机器学习算法种类繁多&#xff0c;它们各自有着独特的优势和应用场景。前面我们学习了线性回归算法、逻辑回归算法、决策树算法。而今天&#xff0c;我们要深入探讨的是其中一种经典且广泛应用的聚类算法 —— K - 平均算法&#xff08;K-Means Algorithm&#xff09…

Bigemap Pro如何设置经纬网出图网格设置

第一步&#xff1a;打开bigemap pro软件&#xff0c;单击顶部网格选项第二栏&#xff0c;弹出经纬网设置对话框&#xff0c;如下图&#xff1a; 按作图需求自定义设置后&#xff0c;点击应用如下图&#xff1a; 第二步&#xff1a;设置好经纬网之后&#xff0c;进行作图&#x…

K8s 之端口暴露(The Port of K8s is Exposed)

K8s 之端口暴露 Kubernetes 是一个用于管理容器化应用程序的流行工具。然而&#xff0c;关于它的工作原理存在一些误解。最常见的误解之一是关于 Kubernetes Pod 中的端口暴露。本文将解释 Kubernetes 中端口暴露的真相。 1 误解 像许多 Kubernetes 新手一样&#xff0c;我最…

操作系统2.4

一、死锁&#xff0c;饥饿&#xff0c;死循环 死锁&#xff1a;各进程互相等待对方手里的资源&#xff0c;导致各进程都阻塞&#xff0c;无法向前推进的现象 饥饿&#xff1a;由于长期得不到想要的资源&#xff0c;某进程无法向前推进的现象&#xff0c;例如&#xff1a;短进…

解决DeepSeek服务器繁忙问题的实用指南

目录 简述 1. 关于服务器繁忙 1.1 服务器负载与资源限制 1.2 会话管理与连接机制 1.3 客户端配置与网络问题 2. 关于DeepSeek服务的备用选项 2.1 纳米AI搜索 2.2 硅基流动 2.3 秘塔AI搜索 2.4 字节跳动火山引擎 2.5 百度云千帆 2.6 英伟达NIM 2.7 Groq 2.8 Firew…

c++进阶———继承

1.引言 在一些大的项目中&#xff0c;我们可能要重复定义一些类&#xff0c;但是很麻烦&#xff0c;应该怎么办呢&#xff1f;举个简单的例子&#xff0c;我要做一个全校师生统计表&#xff0c;统计学号&#xff0c;教师编号&#xff0c;姓名&#xff0c;年龄&#xff0c;电话…

Android 平台GB28181设备接入实战指南

一、引言 随着视频监控技术的不断发展&#xff0c;国标 GB28181 协议在安防监控领域得到了广泛应用。该协议为不同厂家的视频监控设备之间的互联互通提供了统一的规范&#xff0c;使得设备的接入与管理变得更加简单和高效。在 Android 平台上实现 GB28181 设备接入&#xff0c…

细说Java 引用(强、软、弱、虚)和 GC 流程(一)

一、引用概览 1.1 引用简介 JDK1.2中引入了 Reference 抽象类及其子类&#xff0c;来满足不同场景的 JVM 垃圾回收工作&#xff1a; SoftReference 内存不足&#xff0c;GC发生时&#xff0c;引用的对象&#xff08;没有强引用时&#xff09;会被清理&#xff1b;高速缓存使用…

基于图像处理的裂缝检测与特征提取

一、引言 裂缝检测是基础设施监测中至关重要的一项任务,尤其是在土木工程和建筑工程领域。随着自动化技术的发展,传统的人工巡检方法逐渐被基于图像分析的自动化检测系统所取代。通过计算机视觉和图像处理技术,能够高效、精确地提取裂缝的几何特征,如长度、宽度、方向、面…