C++字体库开发之fontconfig使用五

news2024/11/15 13:54:06
代码 


#include <cassert>
#include <algorithm>
#include "fontconfig/fontconfig.h"
#include <stdexcept>
#include <iostream>

#define HAS_FALLBACK_CONFIGURATION

enum FontStyle : uint16_t {
    Regular = 0,
    Italic = 0x01,
    Bold = 0x02,
};

enum class FontConfigSearchFlags : uint16_t {
    None = 0,
    MatchPostScriptName =
    0x01,  ///< Match postscript font name. The default is match family name. This search may be more specific
};

struct FontConfigSearchParams {
    uint16_t Style;
    uint16_t Flags = 0;
};

FcConfig* m_FcConfig;

void 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 __LINUX__
    const char* fontconf =
        R"(<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
    <dir>/usr/share/fonts</dir>
    <dir>/usr/local/share/fonts</dir>
    <dir>/usr/X11R6/lib/X11/fonts</dir>
    <dir>/usr/X11/lib/X11/fonts</dir>
    <dir>/usr/lib/X11/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>/Library/Fonts</dir>
    <dir>~/Library/Fonts</dir>
    <dir>/System/Library/Assets/com_apple_MobileAsset_Font3</dir>
    <dir>/System/Library/Assets/com_apple_MobileAsset_Font4"</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)
        throw std::runtime_error("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);
            throw std::runtime_error("Could not parse font config");
        }

        // Load fonts for the config
        if (!FcConfigBuildFonts(config)) {
            FcConfigDestroy(config);
            throw std::runtime_error("Could not load fonts in fontconfig");
        }

        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();
        assert(m_FcConfig != nullptr);

#ifdef HAS_FALLBACK_CONFIGURATION
    }
#endif
}

bool getFontInfo(FcPattern* font, std::string& fontFamily, std::string& fontPath, uint16_t& style)
{
    FcChar8* family = nullptr;
    FcChar8* path = nullptr;
    int      slant;
    int      weight;
    style = FontStyle::Regular;

    if (FcPatternGetString(font, FC_FAMILY, 0, &family) == FcResultMatch) {
        fontFamily = reinterpret_cast<char*>(family);
        if (FcPatternGetString(font, FC_FILE, 0, &path) == FcResultMatch) {
            fontPath = reinterpret_cast<char*>(path);

            if (FcPatternGetInteger(font, FC_SLANT, 0, &slant) == FcResultMatch) {
                if (slant == FC_SLANT_ITALIC || slant == FC_SLANT_OBLIQUE)
                    style |= FontStyle::Italic;

                if (FcPatternGetInteger(font, FC_WEIGHT, 0, &weight) == FcResultMatch) {
                    if (weight >= FC_WEIGHT_BOLD)
                        style |= FontStyle::Bold;

                    return true;
                }
            }
            // free( file );
        }
        // free( family );
    }

    return false;
}

std::string SearchFontPath(const std::string_view fontPattern, const FontConfigSearchParams& params, unsigned& faceIndex)
{
    FcPattern* pattern;
    FcPattern* matched;
    FcResult   result = FcResultMatch;
    FcValue    value;

    pattern = FcPatternCreate();
    if (pattern == nullptr)
        return ("FcPatternCreate returned NULL");

    // Build a pattern to search using postscript name, bold and italic
    if (params.Flags != (uint16_t)FontConfigSearchFlags::MatchPostScriptName)
        FcPatternAddString(pattern, FC_FAMILY, (const FcChar8*)fontPattern.data());
    else
        FcPatternAddString(pattern, FC_POSTSCRIPT_NAME, (const FcChar8*)fontPattern.data());

    if (params.Style) {
        bool isItalic = (params.Style & FontStyle::Italic) == FontStyle::Italic;
        bool isBold = (params.Style & FontStyle::Bold) == FontStyle::Bold;

        FcPatternAddInteger(pattern, FC_WEIGHT, (isBold ? FC_WEIGHT_BOLD : FC_WEIGHT_MEDIUM));
        FcPatternAddInteger(pattern, FC_SLANT, (isItalic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN));
    }

    // Follow fc-match procedure which proved to be more reliable
    // https://github.com/freedesktop/fontconfig/blob/e291fda7d42e5d64379555097a066d9c2c4efce3/fc-match/fc-match.c#L188
    if (!FcConfigSubstitute(m_FcConfig, pattern, FcMatchPattern)) {
        FcPatternDestroy(pattern);
        faceIndex = 0;
        return {};
    }

    FcDefaultSubstitute(pattern);

    std::string path;
    matched = FcFontMatch(m_FcConfig, pattern, &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;
    }

    FcPatternDestroy(pattern);
    FcPatternDestroy(matched);

#if _WIN32
    // Font config in Windows returns unix conventional path
    // separator. Fix it
    std::replace(path.begin(), path.end(), '/', '\\');
#endif
    return path;
}

void testSingleFont(FcPattern* font)
{
    std::string            fontFamily;
    std::string            fontPath;
    uint16_t               style;
    FontConfigSearchParams fcParams;

    if (getFontInfo(font, fontFamily, fontPath, style)) {
        unsigned faceIndex;
        fcParams.Style = style;
        fontPath = SearchFontPath(fontFamily, fcParams, faceIndex);
        if (fontPath.length() != 0) {
            std::cout << "fontPath:" << fontPath << ",family:" << fontFamily << ",style:" << style << std::endl;
        }
    }

    // 简单打印
    FcPatternPrint(font);

    //自定义格式打印
    const FcChar8* format = (const FcChar8*)"%{=fclist}\n";
    //const FcChar8* format = (const FcChar8*)"%{=unparse}\n";
    //const FcChar8* format = (const FcChar8*)"%{=fcmatch}\n";
    auto s = FcPatternFormat(font, format);
    std::cout << s << "\n";
    FcStrFree(s);
}

// 测试字体安装目录
void listInstallFonts() {
    // Get all installed fonts
    auto pattern = FcPatternCreate();
    auto objectSet = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_FILE, FC_SLANT, FC_WEIGHT, nullptr);
    auto fontSet = FcFontList(nullptr, pattern, objectSet);

    FcObjectSetDestroy(objectSet);
    FcPatternDestroy(pattern);

    if (fontSet == nullptr) {
        printf("Unable to search for fonts");
        return;
    }

    std::cout << "font count " << fontSet->nfont << std::endl;
    for (int i = 0; i < fontSet->nfont; i++)
        testSingleFont(fontSet->fonts[i]);

    FcFontSetDestroy(fontSet);
}


static FcStrSet* processed_dirs;

static int
scanDirs(FcStrList* list, FcConfig* config, FcBool force, FcBool really_force, FcBool verbose, FcBool error_on_no_fonts, int* changed)
{
    int		    ret = 0;
    const FcChar8* dir;
    FcStrSet* subdirs;
    FcStrList* sublist;
    FcCache* cache;
    struct stat	    statb;
    FcBool	    was_valid, was_processed = FcFalse;
    int		    i;
    const FcChar8* sysroot = FcConfigGetSysRoot(config);

    /*
     * Now scan all of the directories into separate databases
     * and write out the results
     */
    while ((dir = FcStrListNext(list)))
    {
        if (verbose)
        {
            if (sysroot)
                printf("[%s]", sysroot);
            printf("%s: ", dir);
            fflush(stdout);
        }

        if (FcStrSetMember(processed_dirs, dir))
        {
            if (verbose)
                printf(("skipping, looped directory detected\n"));
            continue;
        }

        FcChar8* rooted_dir = NULL;
        if (sysroot)
        {
            rooted_dir = FcStrPlus(sysroot, dir);
        }
        else {
            rooted_dir = FcStrCopy(dir);
        }

        if (stat((char*)rooted_dir, &statb) == -1)
        {
            switch (errno) {
            case ENOENT:
            case ENOTDIR:
                if (verbose)
                    printf(("skipping, no such directory\n"));
                break;
            default:
                fprintf(stderr, "\"%s\": ", dir);
                perror("");
                ret++;
                break;
            }
            FcStrFree(rooted_dir);
            rooted_dir = NULL;
            continue;
        }

        FcStrFree(rooted_dir);
        rooted_dir = NULL;

        if ((statb.st_mode & 0xF000) != 0x4000)
        {
            fprintf(stderr,  ("\"%s\": not a directory, skipping\n"), dir);
            continue;
        }
        was_processed = FcTrue;

        if (really_force)
        {
            FcDirCacheUnlink(dir, config);
        }

        cache = NULL;
        was_valid = FcFalse;
        if (!force) {
            //if (FcFileIsDir(arg))
            //    cache = FcDirCacheLoad(arg, config, &rooted_dir);
            //else
            //    cache = FcDirCacheLoadFile(arg, &statb);

            cache = FcDirCacheLoad(dir, config, NULL);
            if (cache)
                was_valid = FcTrue;
        }

        if (!cache)
        {
            (*changed)++;
            cache = FcDirCacheRead(dir, FcTrue, config);
            if (!cache)
            {
                fprintf(stderr,  ("\"%s\": scanning error\n"), dir);
                ret++;
                continue;
            }
        }

        if (was_valid)
        {
            if (verbose)
                printf(("skipping, existing cache is valid: %d fonts, %d dirs\n"),
                    FcCacheNumFont(cache), FcCacheNumSubdir(cache));
        }
        else
        {
            if (verbose)
                printf(("caching, new cache contents: %d fonts, %d dirs\n"),
                    FcCacheNumFont(cache), FcCacheNumSubdir(cache));

            if (!FcDirCacheValid(dir))
            {
                fprintf(stderr,  ("%s: failed to write cache\n"), dir);
                (void)FcDirCacheUnlink(dir, config);
                ret++;
            }
        }

        subdirs = FcStrSetCreate();
        if (!subdirs)
        {
            fprintf(stderr,  ("%s: Can't create subdir set\n"), dir);
            ret++;
            FcDirCacheUnload(cache);
            continue;
        }
        for (i = 0; i < FcCacheNumSubdir(cache); i++)
            FcStrSetAdd(subdirs, FcCacheSubdir(cache, i));

        FcDirCacheUnload(cache);

        sublist = FcStrListCreate(subdirs);
        FcStrSetDestroy(subdirs);
        if (!sublist)
        {
            fprintf(stderr,  ("%s: Can't create subdir list\n"), dir);
            ret++;
            continue;
        }
        FcStrSetAdd(processed_dirs, dir);
        ret += scanDirs(sublist, config, force, really_force, verbose, error_on_no_fonts, changed);
        FcStrListDone(sublist);
    }

    if (error_on_no_fonts && !was_processed)
        ret++;
    return ret;
}

static FcBool
cleanCacheDirectories(FcConfig* config, FcBool verbose)
{
    FcStrList* cache_dirs = FcConfigGetCacheDirs(config);
    FcChar8* cache_dir;
    FcBool	ret = FcTrue;

    if (!cache_dirs)
        return FcFalse;
    while ((cache_dir = FcStrListNext(cache_dirs)))
    {
        if (!FcDirCacheClean(cache_dir, verbose))
        {
            ret = FcFalse;
            break;
        }
    }
    FcStrListDone(cache_dirs);
    return ret;
}

// 测试字体缓存目录
int testFontCache(const char* fontdirs) {
    //if (!FcFileIsDir((const FCChar8*)fontdirs))
    //    return 0;

        FcStrSet* dirs;
        FcStrList* list;
        FcBool    	verbose = FcFalse;
        FcBool	force = FcFalse;
        FcBool	really_force = FcFalse;
        FcBool	systemOnly = FcFalse;
        FcBool	error_on_no_fonts = FcFalse;
        FcConfig* config;
        FcChar8* sysroot = NULL;
        int		changed;
        int		ret;

        if (systemOnly)
            FcConfigEnableHome(FcFalse);
        if (sysroot)
        {
            FcConfigSetSysRoot(NULL, sysroot);
            FcStrFree(sysroot);
            config = FcConfigGetCurrent();
        }
        else
        {
            config = FcInitLoadConfig();
        }
        if (!config)
        {
            fprintf(stderr,  ("%s: Can't initialize font config library\n"), fontdirs);
            return 1;
        }
        FcConfigSetCurrent(config);

        if (fontdirs)
        {
            dirs = FcStrSetCreate();
            if (!dirs)
            {
                fprintf(stderr,  ("%s: Can't create list of directories\n"),
                    fontdirs);
                return 1;
            }

            if (!FcStrSetAddFilename(dirs, (FcChar8*)fontdirs))
            {
                fprintf(stderr,  ("%s: Can't add directory\n"), fontdirs);
                return 1;
            }
            list = FcStrListCreate(dirs);
            FcStrSetDestroy(dirs);
        }
        else
            list = FcConfigGetFontDirs(config);

        if ((processed_dirs = FcStrSetCreate()) == NULL) {
            fprintf(stderr,  ("Out of Memory\n"));
            return 1;
        }

        if (verbose)
        {
            const FcChar8* dir;

            printf("Font directories:\n");
            while ((dir = FcStrListNext(list)))
            {
                printf("\t%s\n", dir);
            }
            FcStrListFirst(list);
        }
        changed = 0;
        ret = scanDirs(list, config, force, really_force, verbose, error_on_no_fonts, &changed);
        FcStrListDone(list);

        /*
         * Try to create CACHEDIR.TAG anyway.
         * This expects the fontconfig cache directory already exists.
         * If it doesn't, it won't be simply created.
         */
        FcCacheCreateTagFile(config);

        FcStrSetDestroy(processed_dirs);

        cleanCacheDirectories(config, verbose);

        FcConfigDestroy(config);
        FcFini();
        /*
         * Now we need to sleep a second  (or two, to be extra sure), to make
         * sure that timestamps for changes after this run of fc-cache are later
         * then any timestamps we wrote.  We don't use gettimeofday() because
         * sleep(3) can't be interrupted by a signal here -- this isn't in the
         * library, and there aren't any signals flying around here.
         */
         /* the resolution of mtime on FAT is 2 seconds */
        //if (changed)
        //    sleep(2);
        if (verbose)
            printf("%s: %s\n", fontdirs, ret ? "failed" : "succeeded");
        return ret;
}

// 配置列表
void testConfigList() {
    FcConfig* config;
    FcConfigFileInfoIter iter;
    config = FcConfigGetCurrent();
    FcConfigFileInfoIterInit(config, &iter);
    do
    {
        FcChar8* name, * desc;
        FcBool enabled;

        if (FcConfigFileInfoIterGet(config, &iter, &name, &desc, &enabled))
        {
            printf("%c %s: %s\n", enabled ? '+' : '-', name, desc);
            FcStrFree(name);
            FcStrFree(desc);
        }
    } while (FcConfigFileInfoIterNext(config, &iter));

    FcFini();
}

// 字体查找
void queryFont(const FcChar8 *fontdir) {

    auto fs = FcFontSetCreate();
    // FcFreeTypeQuery
    if (!FcFreeTypeQueryAll(fontdir, 0, NULL, NULL, fs))
    {
        fprintf(stderr, "Can't query face %u of font file %s\n", -1, fontdir);
        return;
    }

    for (int i = 0; i < fs->nfont; i++)
    {
        FcPattern* pat = fs->fonts[i];
        FcPatternPrint(pat);
        //FcPatternDel(pat, FC_CHARSET);
        //FcPatternDel(pat, FC_LANG);
    }
    FcFontSetDestroy(fs);

    FcFini();
}


// 字体目录解析
void scanFont(const FcChar8* file) {

    auto fs = FcFontSetCreate();

    if (!FcFileIsDir(file))
        FcFileScan(fs, NULL, NULL, NULL, file, FcTrue);
    else
    {
        FcStrSet* dirs = FcStrSetCreate();
        FcStrList* strlist = FcStrListCreate(dirs);
        do
        {
            FcDirScan(fs, dirs, NULL, NULL, file, FcTrue);
        } while ((file = FcStrListNext(strlist)));
        FcStrListDone(strlist);
        FcStrSetDestroy(dirs);
    }

    for (int i = 0; i < fs->nfont; i++)
    {
        FcPattern* pat = fs->fonts[i];
        FcPatternPrint(pat);
        //FcPatternDel(pat, FC_CHARSET);
        //FcPatternDel(pat, FC_LANG);
    }
    FcFontSetDestroy(fs);

    FcFini();
}

void test()
{
    //listInstallFonts();
    //testFontCache("C:\\Fonts");
    //testConfigList();
    //queryFont((const FcChar8*)"C:\\Fonts\\LiberationSans.ttc");
    //scanFont((const FcChar8*)"C:\\Fonts");
    // 待续......
    // FcConfigSetCurrent FcConfigUptoDate  FcConfigGetCache  FcConfigGetRescanInterval  FcConfigGetFonts   FcConfigAppFontAddFile   FcConfigAppFontAddDir  
    // FcConfigAppFontClear     FcDirSave   FcDirCacheRescan    FcFontSetList   FcAtomicLock    FcFontSetMatch  FcFontRenderPrepare     FcFontSetSort   
    // FcFontSetSortDestroy     FcPatternHash
}
输出


    fontPath:C:\Windows\fonts\GOTHIC.TTF,family:Century Gothic,style:0
Pattern has 5 elts (size 16)
        family: "Century Gothic"(s)
        style: "Regular"(s) "Normal"(s) "obyčejné"(s) "Standard"(s) "Κανονικά"(s) "Normaali"(s) "Normál"(s) "Normale"(s) "Standaard"(s) "Normalny"(s) "Обычный"(s) "Normálne"(s) "Navadno"(s) "Arrunta"(s)
        slant: 0(i)(s)
        weight: 80(f)(s)
        file: "C:/Windows/fonts\GOTHIC.TTF"(s)

C:/Windows/fonts\GOTHIC.TTF: Century Gothic:style=Regular,Normal,obyčejné,Standard,Κανονικά,Normaali,Normál,Normale,Standaard,Normalny,Обычный,Normálne,Navadno,Arrunta:slant=0:weight=80

Pattern has 26 elts (size 32)
    family: "Liberation Sans"(s)
    familylang: "en"(s)
    style: "Bold"(s)
    stylelang: "en"(s)
    fullname: "Liberation Sans Bold"(s)
    fullnamelang: "en"(s)
    slant: 0(i)(s)
    weight: 200(f)(s)
    width: 100(f)(s)
    foundry: "1ASC"(s)
    file: "C:\Fonts\LiberationSans-Bold.ttf"(s)
    index: 0(i)(s)
    outline: True(s)
    scalable: True(s)
    charset:
    0000: 00000000 ffffffff ffffffff 7fffffff 00000000 ffffffff ffffffff ffffffff
    0001: ffffffff ffffffff ffffffff ffffffff 00040000 00000000 00000000 fc000000
    0002: 0f000000 00000000 00000000 00000000 00000000 00000000 3f0002c0 00000000
    0003: 00000000 00000000 00000000 40000000 ffffd7f0 fffffffb 00007fff 00000000
    0004: ffffffff ffffffff ffffffff 000c0000 00030000 00000000 00000000 00000000
    001e: 00000000 00000000 00000000 00000000 0000003f 00000000 00000000 000c0000
    0020: 7fbb0000 560d0047 00000010 80000000 00000000 00001098 00000000 00000000
    0021: 00480020 00004044 78000000 00000000 003f0000 00000100 00000000 00000000
    0022: c6268044 00000a00 00000100 00000033 00000000 00000000 00000000 00000000
    0023: 00010004 00000003 00000000 00000000 00000000 00000000 00000000 00000000
    0025: 11111005 10101010 ffff0000 00001fff 000f1111 14041c03 03008c10 00000040
    0026: 00000000 1c000000 00000005 00001c69 00000000 00000000 00000000 00000000
    00f0: 00000026 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    00fb: 00000006 00000000 00000000 00000000 00000000 00000000 00000000 00000000
(s)
    lang: aa|af|av|ay|be|bg|bi|br|bs|ca|ce|ch|co|cs|cy|da|de|el|en|eo|es|et|eu|fi|fj|fo|fr|fur|fy|gd|gl|gv|ho|hr|hu|ia|id|ie|ik|io|is|it|ki|kl|kum|la|lb|lez|lt|lv|mg|mh|mk|mo|mt|nb|nds|nl|nn|no|nr|nso|ny|oc|om|os|pl|pt|rm|ro|ru|se|sel|sk|sl|sma|smj|smn|so|sq|sr|ss|st|sv|sw|tk|tl|tn|tr|ts|uk|uz|vo|vot|wa|wen|wo|xh|yap|zu|an|crh|csb|fil|hsb|ht|jv|kj|ku-tr|kwm|lg|li|ms|na|ng|pap-an|pap-aw|rn|rw|sc|sg|sn|su|za(s)
    fontversion: 70123(i)(s)
    capability: "otlayout:DFLT otlayout:cyrl otlayout:grek otlayout:latn"(s)
    fontformat: "TrueType"(s)
    decorative: False(s)
    postscriptname: "LiberationSans-Bold"(s)
    color: False(s)
    symbol: False(s)
    variable: False(s)
    fonthashint: True(s)
    order: 0(i)(s)

参考

C++字体库开发之字符显示四-CSDN博客

https://github.com/freedesktop/fontconfig

https://github.com/ShiftMediaProject/fontconfig

https://github.com/benoitkugler/textlayout

https://github.com/benoitkugler/textprocessing

https://github.com/go-text/typesetting


创作不易,小小的支持一下吧!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何在 Mac 上恢复丢失或删除的文件

也许您放错了 Mac 上的某个文件&#xff0c;或者永久删除了现在需要的文件。根据您丢失或删除文件的时间&#xff0c;有多种方法可以恢复 MacOS 上的项目。 我们从最快、最简单的方法开始&#xff0c;然后逐渐采用更耗时或更昂贵的方法来恢复 Mac 上的文件、文件夹和其他项目。…

MySQL添加索引时会锁表吗?

目录 简介Online DDL概念Online DDL用法总结 简介 在MySQL5.5以及之前的版本&#xff0c;通常更改数据表结构操作&#xff08;DDL&#xff09;会阻塞对表数据的增删改操作&#xff08;DML&#xff09;。 MySQL5.6提供Online DDL之后可支持DDL与DML操作同时执行&#xff0c;降低…

CentOS 7 yum官方源失效

问题 2024年7月&#xff0c;官方对centos 7停止了维护&#xff0c;yum的源网址mirror.centos.org也已经无法访问。 在此情况下&#xff0c;无法正常使用yum进行安装和更新工具。 在尝试了更换阿里源之后&#xff0c;仍然有部分工具库无法访问。 通用解决方案 1. 打开/etc/y…

react页面指定dom转pdf导出

1. 使用jsPDFhtml2canvas将页面转成图片然后导出2. 自定义创建iframe调用iframe.print()进行页面打印转pdf导出3. 使用react-to-print插件打印pdf4. 利用media print样式打印页面局部元素 1. 使用jsPDFhtml2canvas将页面转成图片然后导出 缺点&#xff1a;页面过长可能会导出失…

SwarmBrain: 通过大模型玩实时战略游戏《星际争霸II》

人工智能咨询培训老师叶梓 转载标明出处 实时战略&#xff08;RTS&#xff09;游戏如《星际争霸II》一直被视为测试和提升AI能力的绝佳平台。尽管基于强化学习&#xff08;RL&#xff09;的AI代理在《星际争霸II》中取得了显著进展&#xff0c;但它们在处理复杂环境时仍面临挑战…

SpringBoot、SpringCloud、SpringCloud Alibaba版本对应关系

1. 概述 随着SpringBoot和SpringCloud的发展&#xff0c;有些服务进入停更运维状态&#xff0c;一些新的框架出现&#xff0c;在开发微服务的时候不同组件的版本对应关系也是不可忽视的问题&#xff0c;如果版本不对应&#xff0c;可能会出现很多莫名的错误&#xff0c;给开发…

5G mmWave PAAM 开发平台

Avnet-Fujikura-AMD 5G 毫米波相控阵天线模块开发平台 Avnet 和 Fujikura 为毫米波频段创建了一个领先的 5G FR2 相控阵天线开发平台。该平台使开发人员能够使用 AMD Xilinx 的 Zynq UltraScale™ RFSoC Gen3 和 Fujikura 的 FutureAcess™ 相控阵天线模块 (PAAM) 快速创建和制…

AI周报(7.14-7.20)

AI应用-本届欧洲杯的AI技术应用 卢卡库在两场比赛中共有三次庆祝进球的情况&#xff0c;但遗憾的是这三次庆祝均未能转化为有效的进球。这种“吐饼王”的表现也让他成为了本届欧洲杯的一个另类焦点人物。虽然稍显悲情&#xff0c;但有了AI的深度应用&#xff0c;即使这样极端的…

震惊,搜索二叉树告诉我们不要生二胎?本篇(带图)让你轻松拿下

引子&#xff1a; 二叉树作为一种常见的一种数据结构,我们也经常使用&#xff0c;有以下几种类型&#xff1a;满二叉树&#xff08;Full Binary Tree&#xff09;&#xff1a;所有节点都恰好有两个子节点或没有子节点。完全二叉树&#xff08;Complete Binary Tree&#xff09…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第三十六章 Linux驱动初探

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

Python list comprehension (列表推导式 - 列表解析式 - 列表生成式)

Python list comprehension {列表推导式 - 列表解析式 - 列表生成式} 1. Python list comprehension (列表推导式 - 列表解析式 - 列表生成式)2. Example3. ExampleReferences Python 中的列表解析式并不是用来解决全新的问题&#xff0c;只是为解决已有问题提供新的语法。 列…

易我数据恢复怎么样?这款四款也很好用

虽然我已经很小心了&#xff0c;但是生活中总有一些意外情况导致数据丢失&#xff0c;这时&#xff0c;数据恢复软件便成为了我们的救命稻草。不知道大家用过易我数据恢复没。我用过&#xff0c;但是目前更喜欢下面的四款数据恢复工具&#xff0c;更符合我的使用习惯&#xff0…

【ai】学习笔记:电影推荐1:协同过滤 TF-DF 余弦相似性

2020年之前都是用协同过滤2020年以后用深度学习、人工智能视频收费的,不完整,里面是电影推荐 这里有个视频讲解2016年大神分析了电影推荐 :MovieRecommendation github地址 看起来是基于用户的相似性和物品的相似性,向用户推荐物品: 大神的介绍: 大神的介绍: 基于Pytho…

Windows tasklist命令详解,Windows查看进程

「作者简介」&#xff1a;冬奥会网络安全中国代表队&#xff0c;CSDN Top100&#xff0c;就职奇安信多年&#xff0c;以实战工作为基础著作 《网络安全自学教程》&#xff0c;适合基础薄弱的同学系统化的学习网络安全&#xff0c;用最短的时间掌握最核心的技术。 tasklist 可以…

Chapter14 非真实感渲染 NPR——Shader入门精要

Chapter14 非真实感渲染 NPR 一、卡通风格渲染1.渲染轮廓线2.添加高光3.ToonShadingMat 二、素描风格渲染 一、卡通风格渲染 特点&#xff1a;物体被黑色线条描边&#xff0c;分明的明暗变化等方法&#xff1a;其中之一就是 基于色调的着色技术 1.渲染轮廓线 方法 基于观察角…

零基础入门鸿蒙开发 HarmonyOS NEXT星河版开发学习

今天开始带大家零基础入门鸿蒙开发&#xff0c;也就是你没有任何编程基础的情况下就可以跟着石头哥零基础学习鸿蒙开发。 目录 一&#xff0c;为什么要学习鸿蒙 1-1&#xff0c;鸿蒙介绍 1-2&#xff0c;为什么要学习鸿蒙 1-3&#xff0c;鸿蒙各个版本介绍 1-4&#xff0…

MYSQL2

1.建库以及建表&#xff1a; mysql> create database mydb8_woker; mysql> use mydb8_woker; mysql> create table t_worker( -> department_id int(11) not null comment 部门号, -> worker_id int(11) primary key not null comment 职工号, -&…

Java中的Heap(堆)(如果想知道Java中有关堆的知识点,那么只看这一篇就足够了!)

前言&#xff1a;&#xff08;Heap&#xff09;是一种特殊的完全二叉树&#xff0c;它在诸多算法中有着广泛的应用&#xff0c;本文将详细介绍Java中的堆。 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解更多内容可以访问我的主页秋刀鱼不做梦-CSDN博客 先让我们看一下本文大…

《Milvus Cloud向量数据库指南》——开源许可证的开放度:塑造AI开发合作与创新的双刃剑

在人工智能(AI)技术日新月异的今天,开源软件作为推动技术创新的重要力量,其许可证的开放度成为了影响AI开发合作、创新模式乃至整个行业生态的关键因素。不同的开源许可证模型,以其各自独特的开放程度,不仅决定了软件项目的可访问性和可定制性,还深刻影响着AI领域内的合…

springboot 实体类加注解校验入参数据

导入的是springboot自身的依赖包 import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.validation.Valid;