Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/vcl/unx/generic/fontmanager/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 50 kB image not shown  

Quelle  fontconfig.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <sal/config.h>

#include <iostream>
#include <memory>
#include <string_view>

#include <o3tl/lru_map.hxx>
#include <unx/fontmanager.hxx>
#include <unx/helper.hxx>
#include <comphelper/sequence.hxx>
#include <vcl/dropcache.hxx>
#include <vcl/svapp.hxx>
#include <vcl/vclenum.hxx>
#include <font/FontSelectPattern.hxx>
#include <i18nlangtag/languagetag.hxx>
#include <i18nutil/unicode.hxx>
#include <rtl/strbuf.hxx>
#include <sal/log.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <unicode/uchar.h>
#include <unicode/uscript.h>
#include <officecfg/Office/Common.hxx>
#include <org/freedesktop/PackageKit/SyncDbusSessionHelper.hpp>
#include <config_fonts.h>

#include <fontconfig/fontconfig.h>

#include <cstdio>

#include <unotools/configmgr.hxx>
#include <unotools/syslocaleoptions.hxx>

#include <osl/process.h>

#include <o3tl/hash_combine.hxx>
#include <utility>
#include <algorithm>

using namespace psp;

namespace
{

struct FontOptionsKey
{
    OUString m_sFamilyName;
    int m_nFontSize;
    FontItalic m_eItalic;
    FontWeight m_eWeight;
    FontWidth m_eWidth;
    FontPitch m_ePitch;

    bool operator==(const FontOptionsKey& rOther) const
    {
        return m_sFamilyName == rOther.m_sFamilyName &&
               m_nFontSize == rOther.m_nFontSize &&
               m_eItalic == rOther.m_eItalic &&
               m_eWeight == rOther.m_eWeight &&
               m_eWidth == rOther.m_eWidth &&
               m_ePitch == rOther.m_ePitch;
    }
};

}

namespace std
{

template <> struct hash<FontOptionsKey>
{
    std::size_t operator()(const FontOptionsKey& k) const noexcept
    {
        std::size_t seed = k.m_sFamilyName.hashCode();
        o3tl::hash_combine(seed, k.m_nFontSize);
        o3tl::hash_combine(seed, k.m_eItalic);
        o3tl::hash_combine(seed, k.m_eWeight);
        o3tl::hash_combine(seed, k.m_eWidth);
        o3tl::hash_combine(seed, k.m_ePitch);
        return seed;
    }
};

// end std namespace

namespace
{

struct FcPatternDeleter
{
    void operator()(FcPattern* pPattern) const
    {
        FcPatternDestroy(pPattern);
    }
};

typedef std::unique_ptr<FcPattern, FcPatternDeleter> FcPatternUniquePtr;

class CachedFontConfigFontOptions : public CacheOwner
{
private:
    o3tl::lru_map<FontOptionsKey, FcPatternUniquePtr> lru_options_cache;

public:
    CachedFontConfigFontOptions()
        : lru_options_cache(10) // arbitrary cache size of 10
    {
    }

    std::unique_ptr<FontConfigFontOptions> lookup(const FontOptionsKey& rKey)
    {
        auto it = lru_options_cache.find(rKey);
        if (it != lru_options_cache.end())
            return std::make_unique<FontConfigFontOptions>(FcPatternDuplicate(it->second.get()));
        return nullptr;
    }

    void cache(const FontOptionsKey& rKey, const FcPattern* pPattern)
    {
        lru_options_cache.insert(std::make_pair(rKey, FcPatternUniquePtr(FcPatternDuplicate(pPattern))));
    }

private:
    virtual void dropCaches() override
    {
        lru_options_cache.clear();
    }

    virtual void dumpState(rtl::OStringBuffer& rState) override
    {
        rState.append("\nCachedFontConfigFontOptions:\t");
        rState.append(static_cast<sal_Int32>(lru_options_cache.size()));
    }
};

typedef std::pair<FcChar8*, FcChar8*> lang_and_element;

class FontCfgWrapper
{
    FcFontSet* m_pFontSet;
    bool m_bRestrictFontSetToApplicationFonts;

    FontCfgWrapper();
    ~FontCfgWrapper();

public:
    static FontCfgWrapper& get();
    static void release();

    void addFontSet( FcSetName );

    FcFontSet* getFontSet();
    void replaceFontSet(FcFontSet* pFilteredFontSet);

    void clear();

    bool isRestrictingFontSetForTesting() const
    {
        return m_bRestrictFontSetToApplicationFonts;
    }

public:
    FcResult LocalizedElementFromPattern(FcPattern const * pPattern, FcChar8 **family,
                                         const char *elementtype, const char *elementlangtype);
//to-do, make private and add some cleaner accessor methods
    std::unordered_map< OString, OString > m_aFontNameToLocalized;
    std::unordered_map< OString, OString > m_aLocalizedToCanonical;
    CachedFontConfigFontOptions m_aCachedFontOptions;
private:
    void cacheLocalizedFontNames(const FcChar8 *origfontname, const FcChar8 *bestfontname, const std::vector< lang_and_element > &lang_and_elements);

    std::unique_ptr<LanguageTag> m_pLanguageTag;
};

}

FontCfgWrapper::FontCfgWrapper()
    : m_pFontSet(nullptr)
    , m_bRestrictFontSetToApplicationFonts(false)
{
    FcInit();
}

#ifndef FC_FONT_WRAPPER
#define FC_FONT_WRAPPER "fontwrapper"
#endif

void FontCfgWrapper::addFontSet( FcSetName eSetName )
{
    // Add only acceptable fonts to our config, for future fontconfig use.
    FcFontSet* pOrig = FcConfigGetFonts( FcConfigGetCurrent(), eSetName );
    if( !pOrig )
        return;

    // filter the font sets to remove obsolete faces
    forint i = 0; i < pOrig->nfont; ++i )
    {
        FcPattern* pPattern = pOrig->fonts[i];
        // #i115131# ignore non-scalable fonts
        // Scalable fonts are usually outline fonts, but some bitmaps fonts
        // (like Noto Color Emoji) are also scalable.
        FcBool bScalable = FcFalse;
        FcResult eScalableRes = FcPatternGetBool(pPattern, FC_SCALABLE, 0, &bScalable);
        if ((eScalableRes != FcResultMatch) || (bScalable == FcFalse))
            continue;

        // Ignore Type 1 fonts, too.
        FcChar8* pFormat = nullptr;
        FcResult eFormatRes = FcPatternGetString(pPattern, FC_FONTFORMAT, 0, &pFormat);
        if ((eFormatRes == FcResultMatch) && (strcmp(reinterpret_cast<char*>(pFormat), "Type 1") == 0))
            continue;

        // Ignore any other non-SFNT wrapper format, including WOFF and WOFF2, too.
        FcChar8* pWrapper = nullptr;
        FcResult eWrapperRes = FcPatternGetString(pPattern, FC_FONT_WRAPPER, 0, &pWrapper);
        if ((eWrapperRes == FcResultMatch) && (strcmp(reinterpret_cast<char*>(pWrapper), "SFNT") != 0))
            continue;

        FcPatternReference( pPattern );
        FcFontSetAdd( m_pFontSet, pPattern );
    }

    // TODO?: FcFontSetDestroy( pOrig );
}

namespace
{
    int compareFontNames(const FcPattern *a, const FcPattern *b)
    {
        FcChar8 *pNameA=nullptr, *pNameB=nullptr;

        bool bHaveA = FcPatternGetString(a, FC_FAMILY, 0, &pNameA) == FcResultMatch;
        bool bHaveB = FcPatternGetString(b, FC_FAMILY, 0, &pNameB) == FcResultMatch;

        if (bHaveA && bHaveB)
            return strcmp(reinterpret_cast<const char*>(pNameA), reinterpret_cast<const char*>(pNameB));

        return int(bHaveA) - int(bHaveB);
    }

    //Sort fonts so that fonts with the same family name are side-by-side, with
    //those with higher version numbers first
    class SortFont
    {
    public:
        bool operator()(const FcPattern *a, const FcPattern *b)
        {
            int comp = compareFontNames(a, b);
            if (comp != 0)
                return comp < 0;

            int nVersionA=0, nVersionB=0;

            bool bHaveA = FcPatternGetInteger(a, FC_FONTVERSION, 0, &nVersionA) == FcResultMatch;
            bool bHaveB = FcPatternGetInteger(b, FC_FONTVERSION, 0, &nVersionB) == FcResultMatch;

            if (bHaveA && bHaveB)
                return nVersionA > nVersionB;

            return bHaveA > bHaveB;
        }
    };

    //See fdo#30729 for where an old opensymbol installed system-wide can
    //clobber the new opensymbol installed locally

    //See if this font is a duplicate with equal attributes which has already been
    //inserted, or if it an older version of an inserted fonts. Depends on FcFontSet
    //being sorted with SortFont
    bool isPreviouslyDuplicateOrObsoleted(FcFontSet const *pFSet, int i)
    {
        const FcPattern *a = pFSet->fonts[i];

        FcPattern* pTestPatternA = FcPatternDuplicate(a);
        FcPatternDel(pTestPatternA, FC_FILE);
        FcPatternDel(pTestPatternA, FC_CHARSET);
        FcPatternDel(pTestPatternA, FC_CAPABILITY);
        FcPatternDel(pTestPatternA, FC_FONTVERSION);
        FcPatternDel(pTestPatternA, FC_INDEX);
        FcPatternDel(pTestPatternA, FC_LANG);
        FcPatternDel(pTestPatternA, FC_POSTSCRIPT_NAME);

        bool bIsDup(false);

        // fdo#66715: loop for case of several font files for same font
        for (int j = i - 1; 0 <= j && !bIsDup; --j)
        {
            const FcPattern *b = pFSet->fonts[j];

            if (compareFontNames(a, b) != 0)
                break;

            FcPattern* pTestPatternB = FcPatternDuplicate(b);
            FcPatternDel(pTestPatternB, FC_FILE);
            FcPatternDel(pTestPatternB, FC_CHARSET);
            FcPatternDel(pTestPatternB, FC_CAPABILITY);
            FcPatternDel(pTestPatternB, FC_FONTVERSION);
            FcPatternDel(pTestPatternB, FC_INDEX);
            FcPatternDel(pTestPatternB, FC_LANG);
            FcPatternDel(pTestPatternB, FC_POSTSCRIPT_NAME);

            bIsDup = FcPatternEqual(pTestPatternA, pTestPatternB);

            FcPatternDestroy(pTestPatternB);
        }

        FcPatternDestroy(pTestPatternA);

        return bIsDup;
    }
}

FcFontSet* FontCfgWrapper::getFontSet()
{
    if( !m_pFontSet )
    {
        m_pFontSet = FcFontSetCreate();
#if HAVE_MORE_FONTS
        m_bRestrictFontSetToApplicationFonts = [] {
            return getenv("SAL_NON_APPLICATION_FONT_USE") != nullptr;
        }();
#endif
        // Add the application fonts before the system fonts.
        // tdf#157939 We will remove duplicate fonts, where the duplicate is
        // the one with a smaller version number. If the same version font is
        // available system-wide or bundled with our application, then we
        // prefer via stable-sort the first one we see. Load application fonts
        // first to prefer the one we bundle in the application in that case.
        addFontSet( FcSetApplication );
        if (!m_bRestrictFontSetToApplicationFonts)
            addFontSet( FcSetSystem );

        std::stable_sort(m_pFontSet->fonts,m_pFontSet->fonts+m_pFontSet->nfont,SortFont());
    }

    return m_pFontSet;
}

void FontCfgWrapper::replaceFontSet(FcFontSet* pFilteredFontSet)
{
    if (m_pFontSet)
        FcFontSetDestroy(m_pFontSet);
    m_pFontSet = pFilteredFontSet;
}

FontCfgWrapper::~FontCfgWrapper()
{
    clear();
    //To-Do: get gtk vclplug smoketest to pass
    //FcFini();
}

static FontCfgWrapper* pOneInstance = nullptr;

FontCfgWrapper& FontCfgWrapper::get()
{
    if( ! pOneInstance )
        pOneInstance = new FontCfgWrapper();
    return *pOneInstance;
}

void FontCfgWrapper::release()
{
    if( pOneInstance )
    {
        delete pOneInstance;
        pOneInstance = nullptr;
    }
}

namespace
{
    FcChar8* bestname(const std::vector<lang_and_element> &elements, const LanguageTag & rLangTag);

    FcChar8* bestname(const std::vector<lang_and_element> &elements, const LanguageTag & rLangTag)
    {
        FcChar8* candidate = elements.begin()->second;
        /* FIXME-BCP47: once fontconfig supports language tags this
         * language-territory stuff needs to be changed! */

        SAL_INFO_IF( !rLangTag.isIsoLocale(), "vcl.fonts""localizedsorter::bestname - not an ISO locale");
        OString sLangMatch(OUStringToOString(rLangTag.getLanguage().toAsciiLowerCase(), RTL_TEXTENCODING_UTF8));
        OString sFullMatch = sLangMatch +
            "-" +
            OUStringToOString(rLangTag.getCountry().toAsciiLowerCase(), RTL_TEXTENCODING_UTF8);

        bool alreadyclosematch = false;
        bool found_fallback_englishname = false;
        for (auto const& element : elements)
        {
            const char *pLang = reinterpret_cast<const char*>(element.first);
            if( sFullMatch == pLang)
            {
                // both language and country match
                candidate = element.second;
                break;
            }
            else if( alreadyclosematch )
            {
                // current candidate matches lang of lang-TERRITORY
                // override candidate only if there is a full match
                continue;
            }
            else if( sLangMatch == pLang)
            {
                // just the language matches
                candidate = element.second;
                alreadyclosematch = true;
            }
            else if( found_fallback_englishname )
            {
                // already found an english fallback, don't override candidate
                // unless there is a better language match
                continue;
            }
            else if( rtl_str_compare( pLang, "en") == 0)
            {
                // select a fallback candidate of the first english element
                // name
                candidate = element.second;
                found_fallback_englishname = true;
            }
        }
        return candidate;
    }
}

//Set up maps to quickly map between a fonts best UI name and all the rest of its names, and vice versa
void FontCfgWrapper::cacheLocalizedFontNames(const FcChar8 *origfontname, const FcChar8 *bestfontname,
    const std::vector< lang_and_element > &lang_and_elements)
{
    for (auto const& element : lang_and_elements)
    {
        const char *candidate = reinterpret_cast<const char*>(element.second);
        if (rtl_str_compare(candidate, reinterpret_cast<const char*>(bestfontname)) != 0)
            m_aFontNameToLocalized[OString(candidate)] = OString(reinterpret_cast<const char*>(bestfontname));
    }
    if (rtl_str_compare(reinterpret_cast<const char*>(origfontname), reinterpret_cast<const char*>(bestfontname)) != 0)
        m_aLocalizedToCanonical[OString(reinterpret_cast<const char*>(bestfontname))] = OString(reinterpret_cast<const char*>(origfontname));
}

FcResult FontCfgWrapper::LocalizedElementFromPattern(FcPattern const * pPattern, FcChar8 **element,
                                                     const char *elementtype, const char *elementlangtype)
{                                                /* e. g.:      ^ FC_FAMILY              ^ FC_FAMILYLANG */
    FcChar8 *origelement;
    FcResult eElementRes = FcPatternGetString( pPattern, elementtype, 0, &origelement );
    *element = origelement;

    if( eElementRes == FcResultMatch)
    {
        FcChar8* elementlang = nullptr;
        if (FcPatternGetString( pPattern, elementlangtype, 0, &elementlang ) == FcResultMatch)
        {
            std::vector< lang_and_element > lang_and_elements;
            lang_and_elements.emplace_back(elementlang, *element);
            int k = 1;
            while (true)
            {
                if (FcPatternGetString( pPattern, elementlangtype, k, &elementlang ) != FcResultMatch)
                    break;
                if (FcPatternGetString( pPattern, elementtype, k, element ) != FcResultMatch)
                    break;
                lang_and_elements.emplace_back(elementlang, *element);
                ++k;
            }

            if (!m_pLanguageTag)
                m_pLanguageTag.reset(new LanguageTag(SvtSysLocaleOptions().GetRealUILanguageTag()));

            // FontConfig orders Typographic Family/Subfamily before old
            // R/B/I/BI-compatible ones, but we want the later, so reverse the
            // names to match them first.
            std::reverse(lang_and_elements.begin(), lang_and_elements.end());

            *element = bestname(lang_and_elements, *m_pLanguageTag);

            //if this element is a fontname, map the other names to this best-name
            if (rtl_str_compare(elementtype, FC_FAMILY) == 0)
                cacheLocalizedFontNames(origelement, *element, lang_and_elements);
        }
    }

    return eElementRes;
}

void FontCfgWrapper::clear()
{
    m_aFontNameToLocalized.clear();
    m_aLocalizedToCanonical.clear();
    if( m_pFontSet )
    {
        FcFontSetDestroy( m_pFontSet );
        m_pFontSet = nullptr;
    }
    m_pLanguageTag.reset();
}

/*
 * PrintFontManager::initFontconfig
 */

void PrintFontManager::initFontconfig()
{
    FontCfgWrapper& rWrapper = FontCfgWrapper::get();
    rWrapper.clear();
}

namespace
{
    FontWeight convertWeight(int weight)
    {
        // set weight
        if( weight <= FC_WEIGHT_THIN )
            return WEIGHT_THIN;
        else if( weight <= FC_WEIGHT_ULTRALIGHT )
            return WEIGHT_ULTRALIGHT;
        else if( weight <= FC_WEIGHT_LIGHT )
            return WEIGHT_LIGHT;
        else if( weight <= FC_WEIGHT_BOOK )
            return WEIGHT_SEMILIGHT;
        else if( weight <= FC_WEIGHT_NORMAL )
            return WEIGHT_NORMAL;
        else if( weight <= FC_WEIGHT_MEDIUM )
            return WEIGHT_MEDIUM;
        else if( weight <= FC_WEIGHT_SEMIBOLD )
            return WEIGHT_SEMIBOLD;
        else if( weight <= FC_WEIGHT_BOLD )
            return WEIGHT_BOLD;
        else if( weight <= FC_WEIGHT_ULTRABOLD )
            return WEIGHT_ULTRABOLD;
        return WEIGHT_BLACK;
    }

    FontItalic convertSlant(int slant)
    {
        // set italic
        if( slant == FC_SLANT_ITALIC )
            return ITALIC_NORMAL;
        else if( slant == FC_SLANT_OBLIQUE )
            return ITALIC_OBLIQUE;
        return ITALIC_NONE;
    }

    FontPitch convertSpacing(int spacing)
    {
        // set pitch
        if( spacing == FC_MONO || spacing == FC_CHARCELL )
            return PITCH_FIXED;
        return PITCH_VARIABLE;
    }

    // translation: fontconfig enum -> vcl enum
    FontWidth convertWidth(int width)
    {
        if (width == FC_WIDTH_ULTRACONDENSED)
            return WIDTH_ULTRA_CONDENSED;
        else if (width == FC_WIDTH_EXTRACONDENSED)
            return WIDTH_EXTRA_CONDENSED;
        else if (width == FC_WIDTH_CONDENSED)
            return WIDTH_CONDENSED;
        else if (width == FC_WIDTH_SEMICONDENSED)
            return WIDTH_SEMI_CONDENSED;
        else if (width == FC_WIDTH_SEMIEXPANDED)
            return WIDTH_SEMI_EXPANDED;
        else if (width == FC_WIDTH_EXPANDED)
            return WIDTH_EXPANDED;
        else if (width == FC_WIDTH_EXTRAEXPANDED)
            return WIDTH_EXTRA_EXPANDED;
        else if (width == FC_WIDTH_ULTRAEXPANDED)
            return WIDTH_ULTRA_EXPANDED;
        return WIDTH_NORMAL;
    }
}

namespace
{
    // for variable fonts, FC_INDEX has been changed such that the lower half is now the
    // index of the font within the collection, and the upper half has been repurposed
    // as the index within the variations
    unsigned int GetCollectionIndex(unsigned int nEntryId)
    {
        return nEntryId & 0xFFFF;
    }

    unsigned int GetVariationIndex(unsigned int nEntryId)
    {
        return nEntryId >> 16;
    }
}

void PrintFontManager::countFontconfigFonts()
{
    int nFonts = 0;
    FontCfgWrapper& rWrapper = FontCfgWrapper::get();

    FcFontSet* pFSet = rWrapper.getFontSet();
    const bool bMinimalFontset = comphelper::IsFuzzing();
    if( pFSet )
    {
        SAL_INFO("vcl.fonts""found " << pFSet->nfont << " entries in fontconfig fontset");

        FcFontSet* pFilteredSet = FcFontSetCreate();

        forint i = 0; i < pFSet->nfont; i++ )
        {
            FcChar8* file = nullptr;
            FcChar8* family = nullptr;
            FcChar8* style = nullptr;
            FcChar8* format = nullptr;
            int slant = 0;
            int weight = 0;
            int width = 0;
            int spacing = 0;
            int symbol = 0;
            int nEntryId = -1;
            FcBool scalable = false;

            FcResult eFileRes         = FcPatternGetString(pFSet->fonts[i], FC_FILE, 0, &file);
            FcResult eFamilyRes       = rWrapper.LocalizedElementFromPattern( pFSet->fonts[i], &family, FC_FAMILY, FC_FAMILYLANG );
            if (bMinimalFontset && strncmp(reinterpret_cast<char*>(family), "Liberation", strlen("Liberation")))
                continue;
            FcResult eStyleRes        = rWrapper.LocalizedElementFromPattern( pFSet->fonts[i], &style, FC_STYLE, FC_STYLELANG );
            FcResult eSlantRes        = FcPatternGetInteger(pFSet->fonts[i], FC_SLANT, 0, &slant);
            FcResult eWeightRes       = FcPatternGetInteger(pFSet->fonts[i], FC_WEIGHT, 0, &weight);
            FcResult eWidthRes        = FcPatternGetInteger(pFSet->fonts[i], FC_WIDTH, 0, &width);
            FcResult eSpacRes         = FcPatternGetInteger(pFSet->fonts[i], FC_SPACING, 0, &spacing);
            FcResult eScalableRes     = FcPatternGetBool(pFSet->fonts[i], FC_SCALABLE, 0, &scalable);
            FcResult eSymbolRes       = FcPatternGetBool(pFSet->fonts[i], FC_SYMBOL, 0, &symbol);
            FcResult eIndexRes        = FcPatternGetInteger(pFSet->fonts[i], FC_INDEX, 0, &nEntryId);
            FcResult eFormatRes       = FcPatternGetString(pFSet->fonts[i], FC_FONTFORMAT, 0, &format);

            if( eFileRes != FcResultMatch || eFamilyRes != FcResultMatch || eScalableRes != FcResultMatch || eStyleRes != FcResultMatch )
                continue;

            SAL_INFO(
                "vcl.fonts.detail",
                "found font \"" << family << "\" in file " << file << ", weight = "
                << (eWeightRes == FcResultMatch ? weight : -1) << ", slant = "
                << (eSpacRes == FcResultMatch ? slant : -1) << ", style = \""
                << (eStyleRes == FcResultMatch ? reinterpret_cast<const char*>(style) : "")
                << "\",  width = " << (eWeightRes == FcResultMatch ? width : -1) << ", spacing = "
                << (eSpacRes == FcResultMatch ? spacing : -1) << ", scalable = "
                << (eScalableRes == FcResultMatch ? scalable : -1) << ", format "
                << (eFormatRes == FcResultMatch
                    ? reinterpret_cast<const char*>(format) : "")
                << " symbol = " << (eSymbolRes == FcResultMatch ? symbol : -1));

//            OSL_ASSERT(eScalableRes != FcResultMatch || scalable);

            // We support only scalable fonts
            if( eScalableRes == FcResultMatch && ! scalable )
                continue;

            if (isPreviouslyDuplicateOrObsoleted(pFSet, i))
            {
                SAL_INFO("vcl.fonts.detail""Ditching " << file << " as duplicate/obsolete");
                continue;
            }

            OString aDir, aBase, aOrgPath( reinterpret_cast<char*>(file) );
            splitPath( aOrgPath, aDir, aBase );
            int nDirID = getDirectoryAtom( aDir );

            PrintFont aFont;
            aFont.m_nDirectory = nDirID;
            aFont.m_aFontFile = aBase;
            if (eIndexRes == FcResultMatch)
            {
                aFont.m_nCollectionEntry = GetCollectionIndex(nEntryId);
                aFont.m_nVariationEntry = GetVariationIndex(nEntryId);
            }

            auto& rFA = aFont.m_aFontAttributes;
            rFA.SetWeight(WEIGHT_NORMAL);
            rFA.SetWidthType(WIDTH_NORMAL);
            rFA.SetPitch(PITCH_VARIABLE);
            rFA.SetQuality(512);

            rFA.SetFamilyName(OStringToOUString(std::string_view(reinterpret_cast<char*>(family)), RTL_TEXTENCODING_UTF8));
            if (eStyleRes == FcResultMatch)
                rFA.SetStyleName(OStringToOUString(std::string_view(reinterpret_cast<char*>(style)), RTL_TEXTENCODING_UTF8));
            if (eWeightRes == FcResultMatch)
                rFA.SetWeight(convertWeight(weight));
            if (eWidthRes == FcResultMatch)
                rFA.SetWidthType(convertWidth(width));
            if (eSpacRes == FcResultMatch)
                rFA.SetPitch(convertSpacing(spacing));
            if (eSlantRes == FcResultMatch)
                rFA.SetItalic(convertSlant(slant));
            if (eSymbolRes == FcResultMatch)
                rFA.SetMicrosoftSymbolEncoded(bool(symbol));

            // sort into known fonts
            fontID nFontID = m_nNextFontID++;
            m_aFonts.emplace(nFontID, aFont);
            m_aFontFileToFontID[aBase].insert(nFontID);
            nFonts++;

            FcPattern* pPattern = pFSet->fonts[i];
            FcPatternReference(pPattern);
            FcFontSetAdd(pFilteredSet, pPattern);

            SAL_INFO("vcl.fonts.detail""inserted font " << family << " as fontID " << nFontID);
        }

        // tdf#157939 if we drop fonts, drop them from the FcConfig set too so they are not
        // candidates for suggestions by fontconfig
        if (pFSet->nfont != pFilteredSet->nfont)
            rWrapper.replaceFontSet(pFilteredSet);
        else
            FcFontSetDestroy(pFilteredSet);

    }

    // how does one get rid of the config ?
    SAL_INFO("vcl.fonts""inserted " << nFonts << " fonts from fontconfig");
}

void PrintFontManager::deinitFontconfig()
{
    FontCfgWrapper::release();
}

void PrintFontManager::addFontconfigDir( const OString& rDirName )
{
    const char* pDirName = rDirName.getStr();
    bool bDirOk = (FcConfigAppFontAddDir(FcConfigGetCurrent(), reinterpret_cast<FcChar8 const *>(pDirName) ) == FcTrue);

    SAL_INFO("vcl.fonts""FcConfigAppFontAddDir( \"" << pDirName << "\") => " << bDirOk);

    if( !bDirOk )
        return;

    // load dir-specific fc-config file too if available
    const OString aConfFileName = rDirName + "/fc_local.conf";
    FILE* pCfgFile = fopen( aConfFileName.getStr(), "rb" );
    if( pCfgFile )
    {
        fclose( pCfgFile);
        bool bCfgOk = FcConfigParseAndLoad(FcConfigGetCurrent(),
                        reinterpret_cast<FcChar8 const *>(aConfFileName.getStr()), FcTrue);

        SAL_INFO_IF(!bCfgOk,
                "vcl.fonts""FcConfigParseAndLoad( \""
                << aConfFileName << "\") => " << bCfgOk);
    } else {
        SAL_INFO("vcl.fonts""cannot open " << aConfFileName);
    }
}

void PrintFontManager::addFontconfigFile( const OString& rFileName )
{
    const char* pFileName = rFileName.getStr();
    bool bFileOk = (FcConfigAppFontAddFile(FcConfigGetCurrent(), reinterpret_cast<FcChar8 const *>(pFileName) ) == FcTrue);

    SAL_INFO("vcl.fonts""FcConfigAppFontAddFile(\"" << pFileName << "\") => " << std::boolalpha << bFileOk);

    if( !bFileOk )
        return;

    // FIXME: we want to add only the newly added font not re-add the whole
    // application font set.
    FontCfgWrapper& rWrapper = FontCfgWrapper::get();
    rWrapper.addFontSet( FcSetApplication );
}

static void addtopattern(FcPattern *pPattern,
    FontItalic eItalic, FontWeight eWeight, FontWidth eWidth, FontPitch ePitch)
{
    if( eItalic != ITALIC_DONTKNOW )
    {
        int nSlant = FC_SLANT_ROMAN;
        switch( eItalic )
        {
            case ITALIC_NORMAL:
                nSlant = FC_SLANT_ITALIC;
                break;
            case ITALIC_OBLIQUE:
                nSlant = FC_SLANT_OBLIQUE;
                break;
            default:
                break;
        }
        FcPatternAddInteger(pPattern, FC_SLANT, nSlant);
    }
    if( eWeight != WEIGHT_DONTKNOW )
    {
        int nWeight = FC_WEIGHT_NORMAL;
        switch( eWeight )
        {
            case WEIGHT_THIN:           nWeight = FC_WEIGHT_THIN;break;
            case WEIGHT_ULTRALIGHT:     nWeight = FC_WEIGHT_ULTRALIGHT;break;
            case WEIGHT_LIGHT:          nWeight = FC_WEIGHT_LIGHT;break;
            case WEIGHT_SEMILIGHT:      nWeight = FC_WEIGHT_BOOK;break;
            case WEIGHT_NORMAL:         nWeight = FC_WEIGHT_NORMAL;break;
            case WEIGHT_MEDIUM:         nWeight = FC_WEIGHT_MEDIUM;break;
            case WEIGHT_SEMIBOLD:       nWeight = FC_WEIGHT_SEMIBOLD;break;
            case WEIGHT_BOLD:           nWeight = FC_WEIGHT_BOLD;break;
            case WEIGHT_ULTRABOLD:      nWeight = FC_WEIGHT_ULTRABOLD;break;
            case WEIGHT_BLACK:          nWeight = FC_WEIGHT_BLACK;break;
            default:
                break;
        }
        FcPatternAddInteger(pPattern, FC_WEIGHT, nWeight);
    }
    if( eWidth != WIDTH_DONTKNOW )
    {
        int nWidth = FC_WIDTH_NORMAL;
        switch( eWidth )
        {
            case WIDTH_ULTRA_CONDENSED: nWidth = FC_WIDTH_ULTRACONDENSED;break;
            case WIDTH_EXTRA_CONDENSED: nWidth = FC_WIDTH_EXTRACONDENSED;break;
            case WIDTH_CONDENSED:       nWidth = FC_WIDTH_CONDENSED;break;
            case WIDTH_SEMI_CONDENSED:  nWidth = FC_WIDTH_SEMICONDENSED;break;
            case WIDTH_NORMAL:          nWidth = FC_WIDTH_NORMAL;break;
            case WIDTH_SEMI_EXPANDED:   nWidth = FC_WIDTH_SEMIEXPANDED;break;
            case WIDTH_EXPANDED:        nWidth = FC_WIDTH_EXPANDED;break;
            case WIDTH_EXTRA_EXPANDED:  nWidth = FC_WIDTH_EXTRAEXPANDED;break;
            case WIDTH_ULTRA_EXPANDED:  nWidth = FC_WIDTH_ULTRAEXPANDED;break;
            default:
                break;
        }
        FcPatternAddInteger(pPattern, FC_WIDTH, nWidth);
    }
    if( ePitch == PITCH_DONTKNOW )
        return;

    int nSpacing = FC_PROPORTIONAL;
    switch( ePitch )
    {
        case PITCH_FIXED:           nSpacing = FC_MONO;break;
        case PITCH_VARIABLE:        nSpacing = FC_PROPORTIONAL;break;
        default:
            break;
    }
    FcPatternAddInteger(pPattern, FC_SPACING, nSpacing);
    if (nSpacing == FC_MONO)
        FcPatternAddString(pPattern, FC_FAMILY, reinterpret_cast<FcChar8 const *>("monospace"));
}

namespace
{
    //Someday fontconfig will hopefully use bcp47, see:
    //https://gitlab.freedesktop.org/fontconfig/fontconfig/-/issues/50
    //In the meantime try something that will fit to workaround, see:
    //https://gitlab.freedesktop.org/fontconfig/fontconfig/-/issues/30
    OString mapToFontConfigLangTag(const LanguageTag &rLangTag)
    {
        std::shared_ptr<FcStrSet> xLangSet(FcGetLangs(), FcStrSetDestroy);
        OString sLangAttrib;

        sLangAttrib = OUStringToOString(rLangTag.getBcp47(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
        if (FcStrSetMember(xLangSet.get(), reinterpret_cast<const FcChar8*>(sLangAttrib.getStr())))
        {
            return sLangAttrib;
        }

        sLangAttrib = OUStringToOString(rLangTag.getLanguageAndScript(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
        if (FcStrSetMember(xLangSet.get(), reinterpret_cast<const FcChar8*>(sLangAttrib.getStr())))
        {
            return sLangAttrib;
        }

        OString sLang = OUStringToOString(rLangTag.getLanguage(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
        OString sRegion = OUStringToOString(rLangTag.getCountry(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();

        if (!sRegion.isEmpty())
        {
            sLangAttrib = sLang + "-" + sRegion;
            if (FcStrSetMember(xLangSet.get(), reinterpret_cast<const FcChar8*>(sLangAttrib.getStr())))
            {
                return sLangAttrib;
            }
        }

        if (FcStrSetMember(xLangSet.get(), reinterpret_cast<const FcChar8*>(sLang.getStr())))
        {
            return sLang;
        }

        return OString();
    }

    bool isEmoji(sal_uInt32 nCurrentChar)
    {
        return u_hasBinaryProperty(nCurrentChar, UCHAR_EMOJI);
    }

    //returns true if the given code-point couldn't possibly be in rLangTag.
    bool isImpossibleCodePointForLang(const LanguageTag &rLangTag, sal_uInt32 currentChar)
    {
        //a non-default script is set, let's believe it
        if (rLangTag.hasScript())
            return false;

        int32_t script = u_getIntPropertyValue(currentChar, UCHAR_SCRIPT);
        UScriptCode eScript = static_cast<UScriptCode>(script);
        bool bIsImpossible = false;
        OUString sLang = rLangTag.getLanguage();
        switch (eScript)
        {
            //http://en.wiktionary.org/wiki/Category:Oriya_script_languages
            case USCRIPT_ORIYA:
                bIsImpossible =
                    sLang != "or" &&
                    sLang != "kxv";
                break;
            //http://en.wiktionary.org/wiki/Category:Telugu_script_languages
            case USCRIPT_TELUGU:
                bIsImpossible =
                    sLang != "te" &&
                    sLang != "gon" &&
                    sLang != "kfc";
                break;
            //http://en.wiktionary.org/wiki/Category:Bengali_script_languages
            case USCRIPT_BENGALI:
                bIsImpossible =
                    sLang != "bn" &&
                    sLang != "as" &&
                    sLang != "bpy" &&
                    sLang != "ctg" &&
                    sLang != "sa";
                break;
            default:
                break;
        }
        SAL_WARN_IF(bIsImpossible, "vcl.fonts""In glyph fallback throwing away the language property of "
            << sLang << " because the detected script for '0x"
            << OUString::number(currentChar, 16)
            << "' is " << uscript_getName(eScript)
            << " and that language doesn't make sense. Autodetecting instead.");
        return bIsImpossible;
    }

    OUString getExemplarLangTagForCodePoint(sal_uInt32 currentChar)
    {
        if (isEmoji(currentChar))
            return u"und-zsye"_ustr;
        int32_t script = u_getIntPropertyValue(currentChar, UCHAR_SCRIPT);
        UScriptCode eScript = static_cast<UScriptCode>(script);
        OStringBuffer aBuf(unicode::getExemplarLanguageForUScriptCode(eScript));
        if (const char* pScriptCode = uscript_getShortName(eScript))
            aBuf.append(OStringChar('-') + pScriptCode);
        return OStringToOUString(aBuf, RTL_TEXTENCODING_UTF8);
    }
}

IMPL_LINK_NOARG(PrintFontManager, autoInstallFontLangSupport, Timer *, void)
{
    try
    {
        using namespace org::freedesktop::PackageKit;
        css::uno::Reference<XSyncDbusSessionHelper> xSyncDbusSessionHelper(SyncDbusSessionHelper::create(comphelper::getProcessComponentContext()));
        xSyncDbusSessionHelper->InstallFontconfigResources(comphelper::containerToSequence(m_aCurrentRequests), u"hide-finished"_ustr);
    }
    catch (const css::uno::Exception&)
    {
        TOOLS_INFO_EXCEPTION("vcl.fonts""InstallFontconfigResources problem");
        // Disable this method from now on. It's simply not available on some systems
        // and leads to an error dialog being shown each time this is called tdf#104883
        std::shared_ptr<comphelper::ConfigurationChanges> batch( comphelper::ConfigurationChanges::create() );
        officecfg::Office::Common::PackageKit::EnableFontInstallation::set(false, batch);
        batch->commit();
    }

    m_aCurrentRequests.clear();
}

void PrintFontManager::Substitute(vcl::font::FontSelectPattern &rPattern, OUString& rMissingCodes)
{
    FontCfgWrapper& rWrapper = FontCfgWrapper::get();

    // build pattern argument for fontconfig query
    FcPattern* pPattern = FcPatternCreate();

    // Prefer scalable fonts
    FcPatternAddBool(pPattern, FC_SCALABLE, FcTrue);

    const OString aTargetName = OUStringToOString( rPattern.maTargetName, RTL_TEXTENCODING_UTF8 );
    const FcChar8* pTargetNameUtf8 = reinterpret_cast<FcChar8 const *>(aTargetName.getStr());
    FcPatternAddString(pPattern, FC_FAMILY, pTargetNameUtf8);

    // Try to map tools FontFamily to fontconfig FC_FAMILY. Note that FcPatternAddString() appends
    // to a list, so it won't overwrite the previous FcPatternAddString(FC_FAMILY), this way we can
    // express that we wanted a certain font, and otherwise a given family style.
    FontFamily eFamilyType = rPattern.GetFamilyType();
    switch (eFamilyType)
    {
        case FAMILY_ROMAN:
            FcPatternAddString(pPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>("serif"));
            break;
        case FAMILY_SWISS:
            FcPatternAddString(pPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>("sans"));
            break;
        default:
            break;
    }

    LanguageTag aLangTag(rPattern.meLanguage);
    OString aLangAttrib = mapToFontConfigLangTag(aLangTag);

    bool bMissingJustBullet = false;

    // Add required Unicode characters, if any
    if ( !rMissingCodes.isEmpty() )
    {
        FcCharSet *codePoints = FcCharSetCreate();
        bMissingJustBullet = rMissingCodes.getLength() == 1 && rMissingCodes[0] == 0xb7;
        for( sal_Int32 nStrIndex = 0; nStrIndex < rMissingCodes.getLength(); )
        {
            // also handle unicode surrogates
            const sal_uInt32 nCode = rMissingCodes.iterateCodePoints( &nStrIndex );
            FcCharSetAddChar( codePoints, nCode );
            //if the codepoint is impossible for this lang tag, then clear it
            //and autodetect something useful
            if (!aLangAttrib.isEmpty() && (isImpossibleCodePointForLang(aLangTag, nCode) || isEmoji(nCode)))
                aLangAttrib.clear();
            //#i105784#/rhbz#527719  improve selection of fallback font
            if (aLangAttrib.isEmpty())
            {
                aLangTag.reset(getExemplarLangTagForCodePoint(nCode));
                aLangAttrib = mapToFontConfigLangTag(aLangTag);
            }
        }
        FcPatternAddCharSet(pPattern, FC_CHARSET, codePoints);
        FcCharSetDestroy(codePoints);
    }

    if (!aLangAttrib.isEmpty())
        FcPatternAddString(pPattern, FC_LANG, reinterpret_cast<FcChar8 const *>(aLangAttrib.getStr()));

    // bodge: testTdf153440 wants a fallback to an emoji font it adds as a temp
    // testing font which has the required glyphs, but that emoji font is not
    // seen as a "color" font, while it is possible that OpenDyslexic can be
    // bundled, which *is* a "color" font. The default rules (See in Fedora 38
    // at least) then prefer a color font *without* the glyphs over a non-color
    // font *with* the glyphs, which seems like a bug to me.
    // Maybe this is an attempt to prefer color emoji fonts over non-color emoji
    // containing fonts like Symbola which has gone awry?
    // For testing purposes (isRestrictingFontSetForTesting is true) force a
    // preference for non-color fonts.
    if (rWrapper.isRestrictingFontSetForTesting())
        FcPatternAddBool(pPattern, FC_COLOR, FcFalse);

    addtopattern(pPattern, rPattern.GetItalic(), rPattern.GetWeight(),
        rPattern.GetWidthType(), rPattern.GetPitch());

    // query fontconfig for a substitute
    FcConfigSubstitute(FcConfigGetCurrent(), pPattern, FcMatchPattern);
    FcDefaultSubstitute(pPattern);

    // process the result of the fontconfig query
    FcResult eResult = FcResultNoMatch;
    FcFontSet* pFontSet = rWrapper.getFontSet();
    FcPattern* pResult = FcFontSetMatch(FcConfigGetCurrent(), &pFontSet, 1, pPattern, &eResult);
    FcPatternDestroy( pPattern );

    FcFontSet*  pSet = nullptr;
    if( pResult )
    {
        pSet = FcFontSetCreate();
        // info: destroying the pSet destroys pResult implicitly
        // since pResult was "added" to pSet
        FcFontSetAdd( pSet, pResult );
    }

    if( pSet )
    {
        if( pSet->nfont > 0 )
        {
            bool bRet = false;

            //extract the closest match
            FcChar8* file = nullptr;
            FcResult eFileRes = FcPatternGetString(pSet->fonts[0], FC_FILE, 0, &file);
            int nEntryId = 0;
            FcResult eIndexRes = FcPatternGetInteger(pSet->fonts[0], FC_INDEX, 0, &nEntryId);
            if (eIndexRes != FcResultMatch)
                nEntryId = 0;
            if( eFileRes == FcResultMatch )
            {
                OString aDir, aBase, aOrgPath( reinterpret_cast<char*>(file) );
                splitPath( aOrgPath, aDir, aBase );
                int nDirID = getDirectoryAtom( aDir );
                fontID nFontID = findFontFileID(nDirID, aBase, GetCollectionIndex(nEntryId), GetVariationIndex(nEntryId));
                auto const* pFont = getFont(nFontID);
                if (pFont)
                {
                    rPattern.maSearchName = pFont->m_aFontAttributes.GetFamilyName();
                    bRet = true;
                }
            }

            SAL_WARN_IF(!bRet, "vcl.fonts""no FC_FILE found, falling back to name search");

            if (!bRet)
            {
                FcChar8* family = nullptr;
                FcResult eFamilyRes = FcPatternGetString( pSet->fonts[0], FC_FAMILY, 0, &family );

                // get the family name
                if( eFamilyRes == FcResultMatch )
                {
                    OString sFamily(reinterpret_cast<char*>(family));
                    std::unordered_map< OString, OString >::const_iterator aI =
                        rWrapper.m_aFontNameToLocalized.find(sFamily);
                    if (aI != rWrapper.m_aFontNameToLocalized.end())
                        sFamily = aI->second;
                    rPattern.maSearchName = OStringToOUString( sFamily, RTL_TEXTENCODING_UTF8 );
                    bRet = true;
                }
            }

            if (bRet)
            {
                int val = 0;
                if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_WEIGHT, 0, &val))
                    rPattern.SetWeight( convertWeight(val) );
                if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_SLANT, 0, &val))
                    rPattern.SetItalic( convertSlant(val) );
                if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_SPACING, 0, &val))
                    rPattern.SetPitch ( convertSpacing(val) );
                if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_WIDTH, 0, &val))
                    rPattern.SetWidthType ( convertWidth(val) );
                FcBool bEmbolden;
                if (FcResultMatch == FcPatternGetBool(pSet->fonts[0], FC_EMBOLDEN, 0, &bEmbolden))
                    rPattern.mbEmbolden = bEmbolden;
                FcMatrix *pMatrix = nullptr;
                if (FcResultMatch == FcPatternGetMatrix(pSet->fonts[0], FC_MATRIX, 0, &pMatrix))
                {
                    rPattern.maItalicMatrix.xx = pMatrix->xx;
                    rPattern.maItalicMatrix.xy = pMatrix->xy;
                    rPattern.maItalicMatrix.yx = pMatrix->yx;
                    rPattern.maItalicMatrix.yy = pMatrix->yy;
                }
            }

            // update rMissingCodes by removing resolved code points
            if( !rMissingCodes.isEmpty() )
            {
                std::unique_ptr<sal_uInt32[]> const pRemainingCodes(new sal_uInt32[rMissingCodes.getLength()]);
                int nRemainingLen = 0;
                FcCharSet* codePoints;
                if (!FcPatternGetCharSet(pSet->fonts[0], FC_CHARSET, 0, &codePoints))
                {
                    for( sal_Int32 nStrIndex = 0; nStrIndex < rMissingCodes.getLength(); )
                    {
                        // also handle surrogates
                        const sal_uInt32 nCode = rMissingCodes.iterateCodePoints( &nStrIndex );
                        if (FcCharSetHasChar(codePoints, nCode) != FcTrue)
                            pRemainingCodes[ nRemainingLen++ ] = nCode;
                    }
                }
                OUString sStillMissing(pRemainingCodes.get(), nRemainingLen);
                if (!Application::IsHeadlessModeEnabled() && officecfg::Office::Common::PackageKit::EnableFontInstallation::get())
                {
                    if (sStillMissing == rMissingCodes) //replaced nothing
                    {
                        //It'd be better if we could ask packagekit using the
                        //missing codepoints or some such rather than using
                        //"language" as a proxy to how fontconfig considers
                        //scripts to default to a given language.
                        for (sal_Int32 i = 0; i < nRemainingLen; ++i)
                        {
                            LanguageTag aOurTag(getExemplarLangTagForCodePoint(pRemainingCodes[i]));
                            OString sTag = OUStringToOString(aOurTag.getBcp47(), RTL_TEXTENCODING_UTF8);
                            if (!m_aPreviousLangSupportRequests.insert(sTag).second)
                                continue;
                            sTag = mapToFontConfigLangTag(aOurTag);
                            if (!sTag.isEmpty() && m_aPreviousLangSupportRequests.find(sTag) == m_aPreviousLangSupportRequests.end())
                            {
                                OString sReq = OString::Concat(":lang=") + sTag;
                                m_aCurrentRequests.push_back(OUString::fromUtf8(sReq));
                                m_aPreviousLangSupportRequests.insert(sTag);
                            }
                        }
                    }
                    if (!m_aCurrentRequests.empty())
                        m_aFontInstallerTimer.Start();
                }
                rMissingCodes = sStillMissing;
            }
        }

        FcFontSetDestroy( pSet );
    }

    SAL_INFO("vcl.fonts""PrintFontManager::Substitute: replacing missing font: '"
                              << rPattern.maTargetName << "' with '" << rPattern.maSearchName
                              << "'");

    static const bool bAbortOnFontSubstitute = [] {
        const char* pEnv = getenv("SAL_NON_APPLICATION_FONT_USE");
        return pEnv && strcmp(pEnv, "abort") == 0;
    }();
    if (bAbortOnFontSubstitute && rPattern.maTargetName != rPattern.maSearchName)
    {
        if (bMissingJustBullet)
        {
            // Some fonts exist in "more_fonts", but have no U+00B7 MIDDLE DOT
            // so will always glyph fallback on measuring mnBulletOffset in
            // FontMetricData::ImplInitTextLineSize
            return;
        }
        if (rPattern.maTargetName == "Linux Libertine G" && rPattern.maSearchName == "Linux Libertine O")
            return;
        SAL_WARN("vcl.fonts""PrintFontManager::Substitute: missing font: '" << rPattern.maTargetName <<
                              "' try: " << rPattern.maSearchName << " instead");
        std::cerr << "terminating test due to missing font: " << rPattern.maTargetName << std::endl;
        std::abort();
    }
}

FontConfigFontOptions::~FontConfigFontOptions()
{
    FcPatternDestroy(mpPattern);
}

FcPattern *FontConfigFontOptions::GetPattern() const
{
    return mpPattern;
}

void FontConfigFontOptions::SyncPattern(const OString& rFileName, sal_uInt32 nIndex, sal_uInt32 nVariation, bool bEmbolden)
{
    FcPatternDel(mpPattern, FC_FILE);
    FcPatternAddString(mpPattern, FC_FILE, reinterpret_cast<FcChar8 const *>(rFileName.getStr()));
    FcPatternDel(mpPattern, FC_INDEX);
    sal_uInt32 nFcIndex = (nVariation << 16) | nIndex;
    FcPatternAddInteger(mpPattern, FC_INDEX, nFcIndex);
    FcPatternDel(mpPattern, FC_EMBOLDEN);
    FcPatternAddBool(mpPattern, FC_EMBOLDEN, bEmbolden ? FcTrue : FcFalse);
}

std::unique_ptr<FontConfigFontOptions> PrintFontManager::getFontOptions(const FontAttributes& rInfo, int nSize)
{
    FontOptionsKey aKey{ rInfo.GetFamilyName(), nSize, rInfo.GetItalic(),
                         rInfo.GetWeight(), rInfo.GetWidthType(), rInfo.GetPitch() };

    FontCfgWrapper& rWrapper = FontCfgWrapper::get();

    std::unique_ptr<FontConfigFontOptions> pOptions = rWrapper.m_aCachedFontOptions.lookup(aKey);
    if (pOptions)
        return pOptions;

    FcConfig* pConfig = FcConfigGetCurrent();
    FcPattern* pPattern = FcPatternCreate();

    OString sFamily = OUStringToOString(aKey.m_sFamilyName, RTL_TEXTENCODING_UTF8);

    std::unordered_map< OString, OString >::const_iterator aI = rWrapper.m_aLocalizedToCanonical.find(sFamily);
    if (aI != rWrapper.m_aLocalizedToCanonical.end())
        sFamily = aI->second;
    if( !sFamily.isEmpty() )
        FcPatternAddString(pPattern, FC_FAMILY, reinterpret_cast<FcChar8 const *>(sFamily.getStr()));

    addtopattern(pPattern, aKey.m_eItalic, aKey.m_eWeight, aKey.m_eWidth, aKey.m_ePitch);
    FcPatternAddDouble(pPattern, FC_PIXEL_SIZE, nSize);

    FcConfigSubstitute(pConfig, pPattern, FcMatchPattern);
    FontConfigFontOptions::cairo_font_options_substitute(pPattern);
    FcDefaultSubstitute(pPattern);

    FcResult eResult = FcResultNoMatch;
    FcFontSet* pFontSet = rWrapper.getFontSet();
    if (FcPattern* pResult = FcFontSetMatch(pConfig, &pFontSet, 1, pPattern, &eResult))
    {
        rWrapper.m_aCachedFontOptions.cache(aKey, pResult);
        pOptions.reset(new FontConfigFontOptions(pResult));
    }

    // cleanup
    FcPatternDestroy( pPattern );

    return pOptions;
}


bool PrintFontManager::matchFont(FontAttributes& rDFA, const css::lang::Locale&&nbsp;rLocale)
{
    bool bFound = false;
    FontCfgWrapper& rWrapper = FontCfgWrapper::get();

    FcConfig* pConfig = FcConfigGetCurrent();
    FcPattern* pPattern = FcPatternCreate();

    // populate pattern with font characteristics
    const LanguageTag aLangTag(rLocale);
    const OString aLangAttrib = mapToFontConfigLangTag(aLangTag);
    if (!aLangAttrib.isEmpty())
        FcPatternAddString(pPattern, FC_LANG, reinterpret_cast<FcChar8 const *>(aLangAttrib.getStr()));

    OString aFamily = OUStringToOString(rDFA.GetFamilyName(), RTL_TEXTENCODING_UTF8);
    if( !aFamily.isEmpty() )
        FcPatternAddString(pPattern, FC_FAMILY, reinterpret_cast<FcChar8 const *>(aFamily.getStr()));

    addtopattern(pPattern, rDFA.GetItalic(), rDFA.GetWeight(), rDFA.GetWidthType(), rDFA.GetPitch());

    FcConfigSubstitute(pConfig, pPattern, FcMatchPattern);
    FcDefaultSubstitute(pPattern);
    FcResult eResult = FcResultNoMatch;
    FcFontSet *pFontSet = rWrapper.getFontSet();
    FcPattern* pResult = FcFontSetMatch(pConfig, &pFontSet, 1, pPattern, &eResult);
    if( pResult )
    {
        FcFontSet* pSet = FcFontSetCreate();
        FcFontSetAdd( pSet, pResult );
        if( pSet->nfont > 0 )
        {
            //extract the closest match
            FcChar8* file = nullptr;
            FcResult eFileRes = FcPatternGetString(pSet->fonts[0], FC_FILE, 0, &file);
            int nEntryId = 0;
            FcResult eIndexRes = FcPatternGetInteger(pSet->fonts[0], FC_INDEX, 0, &nEntryId);
            if (eIndexRes != FcResultMatch)
                nEntryId = 0;
            if( eFileRes == FcResultMatch )
            {
                OString aDir, aBase, aOrgPath( reinterpret_cast<char*>(file) );
                splitPath( aOrgPath, aDir, aBase );
                int nDirID = getDirectoryAtom( aDir );
                fontID nFontID = findFontFileID(nDirID, aBase,
                                              GetCollectionIndex(nEntryId),
                                              GetVariationIndex(nEntryId));
                auto const* pFont = getFont(nFontID);
                if (pFont)
                {
                    rDFA = pFont->m_aFontAttributes;
                    bFound = true;
                }
            }
        }
        // info: destroying the pSet destroys pResult implicitly
        // since pResult was "added" to pSet
        FcFontSetDestroy( pSet );
    }

    // cleanup
    FcPatternDestroy( pPattern );

    return bFound;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=92 H=98 G=94

¤ Dauer der Verarbeitung: 0.23 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.