资讯详情

资讯详情

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

代码#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 version1.0? !DOCTYPE fontconfig SYSTEM fonts.dtd fontconfig dirWINDOWSFONTDIR/dir dirWINDOWSUSERFONTDIR/dir dir prefixxdgfonts/dir cachedirLOCAL_APPDATA_FONTCONFIG_CACHE/cachedir cachedir prefixxdgfontconfig/cachedir /fontconfig ); #elif __ANDROID__ // On android fonts are located in /system/fonts const char* fontconf R(?xml version1.0? !DOCTYPE fontconfig SYSTEM fonts.dtd fontconfig dir/system/fonts/dir dir prefixxdgfonts/dir cachedir prefixxdgfontconfig/cachedir /fontconfig ); #elif __LINUX__ const char* fontconf R(?xml version1.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 prefixxdgfonts/dir cachedir prefixxdgfontconfig/cachedir /fontconfig ); #elif __APPLE__ // Fonts location https://stackoverflow.com/a/2557291/213871 const char* fontconf R(?xml version1.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 prefixxdgfonts/dir cachedir prefixxdgfontconfig/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 doesnt (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_castchar*(family); if (FcPatternGetString(font, FC_FILE, 0, path) FcResultMatch) { fontPath reinterpret_castchar*(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_castconst 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: Cant 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: Cant 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: Cant initialize font config library\n), fontdirs); return 1; } FcConfigSetCurrent(config); if (fontdirs) { dirs FcStrSetCreate(); if (!dirs) { fprintf(stderr, (%s: Cant create list of directories\n), fontdirs); return 1; } if (!FcStrSetAddFilename(dirs, (FcChar8*)fontdirs)) { fprintf(stderr, (%s: Cant 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 doesnt, it wont 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 dont use gettimeofday() because * sleep(3) cant be interrupted by a signal here -- this isnt in the * library, and there arent 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, Cant 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:0Pattern 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:styleRegular,Normal,obyčejné,Standard,Κανονικά,Normaali,Normál,Normale,Standaard,Normalny,Обычный,Normálne,Navadno,Arrunta:slant0:weight80Pattern 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 ffffffff0001: ffffffff ffffffff ffffffff ffffffff 00040000 00000000 00000000 fc0000000002: 0f000000 00000000 00000000 00000000 00000000 00000000 3f0002c0 000000000003: 00000000 00000000 00000000 40000000 ffffd7f0 fffffffb 00007fff 000000000004: ffffffff ffffffff ffffffff 000c0000 00030000 00000000 00000000 00000000001e: 00000000 00000000 00000000 00000000 0000003f 00000000 00000000 000c00000020: 7fbb0000 560d0047 00000010 80000000 00000000 00001098 00000000 000000000021: 00480020 00004044 78000000 00000000 003f0000 00000100 00000000 000000000022: c6268044 00000a00 00000100 00000033 00000000 00000000 00000000 000000000023: 00010004 00000003 00000000 00000000 00000000 00000000 00000000 000000000025: 11111005 10101010 ffff0000 00001fff 000f1111 14041c03 03008c10 000000400026: 00000000 1c000000 00000005 00001c69 00000000 00000000 00000000 0000000000f0: 00000026 00000000 00000000 00000000 00000000 00000000 00000000 0000000000fb: 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/fontconfighttps://github.com/ShiftMediaProject/fontconfighttps://github.com/benoitkugler/textlayouthttps://github.com/benoitkugler/textprocessinghttps://github.com/go-text/typesetting创作不易小小的支持一下吧
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →