From bea597e765a8a1b1098fa7aba31d109aa8d6e507 Mon Sep 17 00:00:00 2001 From: Adrian Cochrane Date: Fri, 5 Apr 2024 17:41:17 +1300 Subject: [PATCH] Write init/finalize & pattern language bindings, haskell-side! --- cbits/fontconfig-wrap.c | 699 ++++++++++++++++++ cbits/{freetype-wrap.c => fontconfig-wrap.c~} | 30 + cbits/fontconfig-wrap.h | 37 + cbits/fontconfig-wrap.h~ | 37 + cbits/transcode.c | 4 +- fontconfig-pure.cabal | 9 +- lib/Graphics/Text/Font/Choose/Config.hs | 35 + lib/Graphics/Text/Font/Choose/Internal/FFI.hs | 36 + lib/Graphics/Text/Font/Choose/Pattern.hs | 33 +- lib/Graphics/Text/Font/Choose/Result.hs | 20 + 10 files changed, 934 insertions(+), 6 deletions(-) create mode 100644 cbits/fontconfig-wrap.c rename cbits/{freetype-wrap.c => fontconfig-wrap.c~} (96%) create mode 100644 cbits/fontconfig-wrap.h create mode 100644 cbits/fontconfig-wrap.h~ create mode 100644 lib/Graphics/Text/Font/Choose/Config.hs create mode 100644 lib/Graphics/Text/Font/Choose/Internal/FFI.hs create mode 100644 lib/Graphics/Text/Font/Choose/Result.hs diff --git a/cbits/fontconfig-wrap.c b/cbits/fontconfig-wrap.c new file mode 100644 index 0000000..b000b0a --- /dev/null +++ b/cbits/fontconfig-wrap.c @@ -0,0 +1,699 @@ +#include "transcode.h" +#include +#include +#include + +int fcPatternEqualSubset(uint8_t *data, size_t length) { + cmp_ctx_t in; + if (!cmp_bytes_init(&in, data, length)) return -1; + uint32_t size = 0; + if (!cmp_read_array(&in, &size) || size != 3) { + cmp_bytes_take(&in, NULL); + return -1; + } + FcPattern *pa = decodePattern(&in); + if (pa == NULL) {cmp_bytes_take(&in, NULL); return -1;} + FcPattern *pb = decodePattern(&in); + if (pb == NULL) { + cmp_bytes_take(&in, NULL); + FcPatternDestroy(pa); + return -1; + } + FcObjectSet *os = decodeObjectSet(&in); + cmp_bytes_take(&in, NULL); + + int ret = -1; + if (os != NULL) ret = FcPatternEqualSubset(pa, pb, os) ? 1 : 0; + + FcPatternDestroy(pa); + FcPatternDestroy(pb); + FcObjectSetDestroy(os); + return ret; +} + +uint8_t *fcDefaultSubstitute(uint8_t *data, size_t in_length, size_t *length) { + cmp_ctx_t in; + if (!cmp_bytes_init(&in, data, in_length)) return NULL; + FcPattern *pat = decodePattern(&in); + cmp_bytes_take(&in, NULL); + if (pat == NULL) return NULL; + + FcDefaultSubstitute(pat); + + cmp_ctx_t out; + if (!cmp_bytes_alloc(&out, 1024)) {FcPatternDestroy(pat); return NULL;} + if (!encodePattern(&out, pat)) { + cmp_bytes_free(&out); + FcPatternDestroy(pat); + return NULL; + } + FcPatternDestroy(pat); + return cmp_bytes_take(&out, length); +} + +uint8_t *fcNameParse(char *name, size_t *length) { + FcPattern *pat = FcNameParse(name); + if (pat == NULL) return NULL; + + cmp_ctx_t out; + if (!cmp_bytes_alloc(&out, 1024)) {FcPatternDestroy(pat); return NULL;} + if (!encodePattern(&out, pat)) { + FcPatternDestroy(pat); cmp_bytes_free(&out); return NULL; + } + FcPatternDestroy(pat); + return cmp_bytes_take(&out, length); +} + +char *fcNameUnparse(uint8_t *data, size_t length) { + cmp_ctx_t in; + if (!cmp_bytes_init(&in, data, length)) return NULL; + FcPattern *pat = decodePattern(&in); + cmp_bytes_take(&in, NULL); // Caller frees `data`! + if (pat == NULL) return NULL; + + char *ret = FcNameUnparse(pat); + FcPatternDestroy(pat); + return ret; +} + +char *fcNameFormat(uint8_t *data, size_t length, char *format) { + cmp_ctx_t in; + if (!cmp_bytes_init(&in, data, length)) return NULL; + FcPattern *pat = decodePattern(&in); + cmp_bytes_take(&in, NULL); // Caller frees `data`! + if (pat == NULL) return NULL; + + char *ret = FcPatternFormat(pat, format); + FcPatternDestroy(pat); + return ret; +} + +uint8_t *fcFontSetList(FcConfig *config, uint8_t *sets, size_t sets_length, + uint8_t *pat, size_t pat_length, uint8_t *objects, size_t objs_length, size_t *length) { + cmp_ctx_t in; + uint8_t *ret = NULL; + + if (!cmp_bytes_init(&in, sets, sets_length)) return NULL; + size_t nsets; + FcFontSet **fontsets = decodeFontSets(&in, &nsets); + cmp_bytes_take(&in, NULL); + if (fontsets == NULL) return NULL; + + if (!cmp_bytes_init(&in, pat, pat_length)) goto fail_pat; + FcPattern *pattern = decodePattern(&in); + cmp_bytes_take(&in, NULL); + if (pattern == NULL) goto fail_pat; + + if (!cmp_bytes_init(&in, objects, objs_length)) goto fail_objs; + FcObjectSet *objs = decodeObjectSet(&in); + cmp_bytes_take(&in, NULL); + if (objs == NULL) goto fail_objs; + + FcFontSet *res = FcFontSetList(config, fontsets, nsets, pattern, objs); + + cmp_ctx_t out; + if (!cmp_bytes_alloc(&out, 1024)) goto fail; + bool ok = encodeFontSet(&out, res); + ret = cmp_bytes_take(&out, length); + if (!ok && ret != NULL) { free(ret); ret = NULL;} + +fail: + FcFontSetDestroy(res); + FcObjectSetDestroy(objs); +fail_objs: + FcPatternDestroy(pattern); +fail_pat: + for (size_t i = 0; i < nsets; i++) FcFontSetDestroy(fontsets[i]); + free(fontsets); + return ret; +} + + +// FcResultMatch, FcResultNoMatch, FcResultTypeMismatch, FcResultNoId, FcResultOutOfMemory + +uint8_t *fcFontSetMatch(FcConfig *config, uint8_t *sets, size_t sets_length, + uint8_t *pat, size_t pat_length, size_t *length) { + cmp_ctx_t in; + uint8_t *ret = NULL; + + if (!cmp_bytes_init(&in, sets, sets_length)) return NULL; + size_t nsets; + FcFontSet **fontsets = decodeFontSets(&in, &nsets); + cmp_bytes_take(&in, NULL); + if (fontsets == NULL) return NULL; + + if (!cmp_bytes_init(&in, pat, pat_length)) goto fail; + FcPattern *pattern = decodePattern(&in); + cmp_bytes_take(&in, NULL); + if (pattern == NULL) goto fail; + + // Necessary preprocessing! + FcPattern *res = NULL; + if (!FcConfigSubstitute(config, pattern, FcMatchPattern)) goto fail2; + FcDefaultSubstitute(pattern); + + FcResult err; + res = FcFontSetMatch(config, fontsets, nsets, pattern, &err); + + cmp_ctx_t out; + bool ok; + if (err == FcResultMatch) { + if (!cmp_bytes_alloc(&out, 1024)) goto fail2; + ok = encodePattern(&out, res); + ret = cmp_bytes_take(&out, length); + if (!ok && ret != NULL) { free(ret); ret = NULL; } + } else { + if (!cmp_bytes_alloc(&out, 32)) goto fail2; + ok = encodeResult(&out, err); + ret = cmp_bytes_take(&out, length); + if (!ok && ret != NULL) { free(ret); ret = NULL; } + } + +fail2: + if (res != NULL) FcPatternDestroy(res); + FcPatternDestroy(pattern); +fail: + for (size_t i = 0; i < nsets; i++) FcFontSetDestroy(fontsets[i]); + free(fontsets); + return ret; +} + +uint8_t *fcFontSetSort(FcConfig *config, uint8_t *sets, size_t sets_length, + uint8_t *pat, size_t pat_length, bool trim, size_t *length) { + cmp_ctx_t in; + uint8_t *ret = NULL; + + if (!cmp_bytes_init(&in, sets, sets_length)) return NULL; + size_t nsets; + FcFontSet **fontsets = decodeFontSets(&in, &nsets); + cmp_bytes_take(&in, NULL); + if (fontsets == NULL) return NULL; + + if (!cmp_bytes_init(&in, pat, pat_length)) goto fail; + FcPattern *pattern = decodePattern(&in); + cmp_bytes_take(&in, NULL); + if (pattern == NULL) goto fail; + + // Necessary preprocessing! + if (!FcConfigSubstitute(config, pattern, FcMatchPattern)) goto fail2; + FcDefaultSubstitute(pattern); + + FcResult err; + FcCharSet *charset = NULL; + FcFontSet *res = FcFontSetSort(config, fontsets, nsets, pattern, trim, &charset, &err); + + cmp_ctx_t out; + bool ok = true; + switch (err) { + case FcResultMatch: + if (!cmp_bytes_alloc(&out, 1024*res->nfont)) goto fail3; + ok = ok || cmp_write_array(&out, 2); + if (res == NULL) cmp_write_nil(&out); + else { + // FIXME: Postprocess each font! Rather than call encodeFontSet! + ok = ok || encodeFontSet(&out, res); + } + if (charset == NULL) cmp_write_nil(&out); + else ok = ok || encodeCharSet(&out, charset); + break; + case FcResultNoMatch: + if (!cmp_bytes_alloc(&out, 1024)) goto fail3; + ok = ok || cmp_write_array(&out, 2); + ok = ok || cmp_write_array(&out, 0); + if (charset == NULL) cmp_write_nil(&out); + else ok = ok || encodeCharSet(&out, charset); + break; + default: + if (!cmp_bytes_alloc(&out, 32)) goto fail3; + ok = ok || encodeResult(&out, err); + } + ret = cmp_bytes_take(&out, length); + if (!ok && ret != NULL) { free(ret); ret = NULL; } + +fail3: + FcFontSetDestroy(res); + if (charset != NULL) FcCharSetDestroy(charset); +fail2: + FcPatternDestroy(pattern); +fail: + for (size_t i = 0; i < nsets; i++) FcFontSetDestroy(fontsets[i]); + free(fontsets); + return ret; +} + +unsigned int fcFreeTypeCharIndex(FT_Face *face, uint32_t ucs4) { + return FcFreeTypeCharIndex(*face, ucs4); +} + +uint8_t *fcFreeTypeCharSet(FT_Face *face, size_t *length) { + FcCharSet *res = FcFreeTypeCharSet(*face, NULL); + + if (res == NULL) return NULL; + cmp_ctx_t out; + if (!cmp_bytes_alloc(&out, 1024)) { + FcCharSetDestroy(res); + return NULL; + } + if (!encodeCharSet(&out, res)) { + FcCharSetDestroy(res); + cmp_bytes_free(&out); + return NULL; + } + FcCharSetDestroy(res); + return cmp_bytes_take(&out, length);; +} + +uint8_t *fcFreeTypeCharSetAndSpacing(FT_Face *face, size_t *length) { + int spacing; + FcCharSet *res = FcFreeTypeCharSetAndSpacing(*face, NULL, &spacing); + if (res == NULL) return NULL; + + cmp_ctx_t out; + if (!cmp_bytes_alloc(&out, 1024)) { + FcCharSetDestroy(res); + return NULL; + } + if (!cmp_write_array(&out, 2)) goto fail; + switch (spacing) { // This branches ensures a consistant ABI for Haskell to bind against. + case FC_MONO: + if (!cmp_write_integer(&out, 0)) goto fail; + break; + case FC_DUAL: + if (!cmp_write_integer(&out, 1)) goto fail; + break; + case FC_PROPORTIONAL: + if (!cmp_write_integer(&out, 2)) goto fail; + break; + default: + if (!cmp_write_integer(&out, 3)) goto fail; + break; + } + if (!encodeCharSet(&out, res)) goto fail; + FcCharSetDestroy(res); + return cmp_bytes_take(&out, length);; + +fail: + FcCharSetDestroy(res); + cmp_bytes_free(&out); + return NULL; +} + +uint8_t *fcFreeTypeQuery(char *file, int id, size_t *length) { + int count; + FcPattern *res = FcFreeTypeQuery(file, id, NULL, &count); + if (res == NULL) return NULL; + + cmp_ctx_t out; + if (!cmp_bytes_alloc(&out, 1024)) { + FcPatternDestroy(res); + return NULL; + } + if (!cmp_write_array(&out, 2)) goto fail; + if (!cmp_write_integer(&out, count)) goto fail; + if (!encodePattern(&out, res)) goto fail; + FcPatternDestroy(res); + return cmp_bytes_take(&out, length);; + +fail: + FcPatternDestroy(res); + cmp_bytes_free(&out); + return NULL; +} + +uint8_t *fcFreeTypeQueryAll(char *file, size_t *length) { + int count; + FcFontSet *fontset = FcFontSetCreate(); + if (fontset == NULL) return NULL; + unsigned int npatterns = FcFreeTypeQueryAll(file, -1, NULL, &count, fontset); + + cmp_ctx_t out; + if (!cmp_bytes_alloc(&out, 1024)) { + FcFontSetDestroy(fontset); + return NULL; + } + if (!cmp_write_array(&out, 3)) goto fail; + if (!cmp_write_integer(&out, npatterns)) goto fail; + if (!cmp_write_integer(&out, count)) goto fail; + if (!encodeFontSet(&out, fontset)) goto fail; + uint8_t *ret = cmp_bytes_take(&out, length); + FcFontSetDestroy(fontset); + return ret; + +fail: + FcFontSetDestroy(fontset); + cmp_bytes_free(&out); + return NULL; +} + +uint8_t *fcFreeTypeQueryFace(FT_Face *face, char *file, int id, size_t *length) { + FcPattern *res = FcFreeTypeQueryFace(*face, file, id, NULL); + if (res == NULL) return NULL; + + cmp_ctx_t out; + if (!cmp_bytes_alloc(&out, 1024)) { + FcPatternDestroy(res); + return NULL; + } + if (!encodePattern(&out, res)) { + FcPatternDestroy(res); + cmp_bytes_free(&out); + } + uint8_t *ret = cmp_bytes_take(&out, length); + FcPatternDestroy(res); + return ret; +} + +int fcLangSetHasLang(uint8_t *langset, size_t length, const char *lang) { + cmp_ctx_t in; + if (!cmp_bytes_init(&in, langset, length)) return -1; + FcLangSet *ls = decodeLangSet(&in); + cmp_bytes_take(&in, NULL); // Caller frees `langset` + if (ls == NULL) return -1; + + FcLangResult ret = FcLangSetHasLang(ls, lang); + FcLangSetDestroy(ls); + switch (ret) { + case FcLangDifferentLang: + return 0; + case FcLangEqual: + return 1; + case FcLangDifferentTerritory: + return 2; + default: + return -2; + } +} + +uint8_t *fcGetDefaultLangs(size_t *length) { + FcStrSet *res = FcGetDefaultLangs(); + if (res == NULL) return NULL; + + cmp_ctx_t out; + if (!cmp_bytes_alloc(&out, 1024)) { + FcStrSetDestroy(res); + return NULL; + } + if (!encodeStrSet(&out, res)) { + cmp_bytes_free(&out); + FcStrSetDestroy(res); + return NULL; + } + FcStrSetDestroy(res); + return cmp_bytes_take(&out, length); +} + +uint8_t *fcGetLangs(size_t *length) { + FcStrSet *res = FcGetLangs(); + if (res == NULL) return NULL; + + cmp_ctx_t out; + if (!cmp_bytes_alloc(&out, 1024)) { + FcStrSetDestroy(res); + return NULL; + } + if (!encodeStrSet(&out, res)) { + cmp_bytes_free(&out); + FcStrSetDestroy(res); + return NULL; + } + FcStrSetDestroy(res); + return cmp_bytes_take(&out, length); +} + +char *fcLangNormalize(char *lang) {return FcLangNormalize(lang);} + +uint8_t *fcLangGetCharSet(const char *lang, size_t *length) { + const FcCharSet *res = FcLangGetCharSet(lang); + if (res == NULL) return NULL; + + cmp_ctx_t out; + if (!cmp_bytes_alloc(&out, 1024)) return NULL; + if (!encodeCharSet(&out, res)) { + cmp_bytes_free(&out); + return NULL; + } + return cmp_bytes_take(&out, length); +} + +uint8_t *fcConfigGetConfigDirs(FcConfig *conf, size_t *length) { + FcStrList *res = FcConfigGetConfigDirs(conf); + if (res == NULL) return NULL; + + cmp_ctx_t out; + if (!cmp_bytes_alloc(&out, 1024)) { + FcStrListDone(res); + return NULL; + } + if (!encodeStrList(&out, res)) { + cmp_bytes_free(&out); + FcStrListDone(res); + return NULL; + } + FcStrListDone(res); + return cmp_bytes_take(&out, length); +} + +uint8_t *fcConfigGetFontDirs(FcConfig *conf, size_t *length) { + FcStrList *res = FcConfigGetFontDirs(conf); + if (res == NULL) return NULL; + + cmp_ctx_t out; + if (!cmp_bytes_alloc(&out, 1024)) { + FcStrListDone(res); + return NULL; + } + if (!encodeStrList(&out, res)) { + cmp_bytes_free(&out); + FcStrListDone(res); + return NULL; + } + FcStrListDone(res); + return cmp_bytes_take(&out, length); +} + +uint8_t *fcConfigGetConfigFiles(FcConfig *conf, size_t *length) { + FcStrList *res = FcConfigGetConfigFiles(conf); + if (res == NULL) return NULL; + + cmp_ctx_t out; + if (!cmp_bytes_alloc(&out, 1024)) { + FcStrListDone(res); + return NULL; + } + if (!encodeStrList(&out, res)) { + cmp_bytes_free(&out); + FcStrListDone(res); + return NULL; + } + FcStrListDone(res); + return cmp_bytes_take(&out, length); +} + +uint8_t *fcConfigGetCacheDirs(FcConfig *conf, size_t *length) { + FcStrList *res = FcConfigGetCacheDirs(conf); + if (res == NULL) return NULL; + + cmp_ctx_t out; + if (!cmp_bytes_alloc(&out, 1024)) { + FcStrListDone(res); + return NULL; + } + if (!encodeStrList(&out, res)) { + cmp_bytes_free(&out); + FcStrListDone(res); + return NULL; + } + FcStrListDone(res); + return cmp_bytes_take(&out, length); +} + +uint8_t *fcConfigGetFonts(FcConfig *conf, bool system, size_t *length) { + FcFontSet *res = FcConfigGetFonts(conf, system ? FcSetSystem : FcSetApplication); + if (res == NULL) return NULL; + + cmp_ctx_t out; + if (!cmp_bytes_alloc(&out, 1024)) { + FcFontSetDestroy(res); + return NULL; + } + if (!encodeFontSet(&out, res)) { + cmp_bytes_free(&out); + FcFontSetDestroy(res); + return NULL; + } + FcFontSetDestroy(res); + return cmp_bytes_take(&out, length); +} + +uint8_t *fcConfigSubstituteWithPat(FcConfig *conf, uint8_t *data, size_t in_length, bool isFont, size_t *length) { + cmp_ctx_t in; + if (!cmp_bytes_init(&in, data, in_length)) return NULL; + + uint32_t size = 0; + if (!cmp_read_array(&in, &size) || size < 1 || size > 2) return NULL; + FcPattern *p = decodePattern(&in); + if (p == NULL) {cmp_bytes_take(&in, NULL); return NULL;} + FcPattern *p_pat = NULL; + if (size == 2) { + p_pat = decodePattern(&in); + if (p_pat == NULL) {cmp_bytes_take(&in, NULL); goto fail;} + } + cmp_bytes_take(&in, NULL); + + if (!FcConfigSubstituteWithPat(conf, p, p_pat, isFont ? FcMatchFont : FcMatchPattern)) goto fail; + + cmp_ctx_t out; + if (!cmp_bytes_alloc(&out, 1024)) goto fail; + if (!encodePattern(&out, p)) goto fail; + FcPatternDestroy(p); + if (p_pat != NULL) FcPatternDestroy(p_pat); + return cmp_bytes_take(&out, length); + +fail: + FcPatternDestroy(p); + if (p_pat != NULL) FcPatternDestroy(p_pat); + return NULL; +} + +uint8_t *fcFontMatch(FcConfig *conf, uint8_t *data, size_t in_length, size_t *length) { + cmp_ctx_t in; + if (!cmp_bytes_init(&in, data, in_length)) return NULL; + FcPattern *p = decodePattern(&in); + cmp_bytes_take(&in, NULL); + if (p == NULL) return NULL; + + if (!FcConfigSubstitute(conf, p, FcMatchPattern)) goto fail; + FcDefaultSubstitute(p); + + FcResult err; + FcPattern *res = FcFontMatch(conf, p, &err); + if (res == NULL) goto fail; + + cmp_ctx_t out; + if (!cmp_bytes_alloc(&out, 1024)) goto fail; + if (err != FcResultMatch) { + if (!encodeResult(&out, err)) goto fail2; + } else { + if (!encodePattern(&out, res)) goto fail2; + } + FcPatternDestroy(p); + return cmp_bytes_take(&out, length);; + +fail2: + cmp_bytes_free(&out); +fail: + FcPatternDestroy(p); + return NULL; +} + +uint8_t *fcFontSort(FcConfig *conf, uint8_t *data, size_t in_length, bool trim, size_t *length) { + cmp_ctx_t in; + if (!cmp_bytes_init(&in, data, in_length)) return NULL; + FcPattern *p = decodePattern(&in); + cmp_bytes_take(&in, NULL); + if (p == NULL) return NULL; + + if (!FcConfigSubstitute(conf, p, FcMatchPattern)) goto fail; + FcDefaultSubstitute(p); + + FcResult err; + FcCharSet *csp; + FcFontSet *res = FcFontSort(conf, p, trim, &csp, &err); + if (res == NULL) goto fail2; + + cmp_ctx_t out; + if (!cmp_bytes_alloc(&out, 1024)) goto fail2; + if (err != FcResultMatch) { + if (!encodeResult(&out, err)) goto fail3; + } else if (csp != NULL) { + if (!cmp_write_array(&out, 2)) goto fail3; + if (!encodeFontSet(&out, res)) goto fail3; + if (!encodeCharSet(&out, csp)) goto fail3; + } else { + if (!cmp_write_array(&out, 1)) goto fail3; + if (!encodeFontSet(&out, res)) goto fail3; + } + FcPatternDestroy(p); + if (csp != NULL) FcCharSetDestroy(csp); + return cmp_bytes_take(&out, length); + +fail3: + cmp_bytes_free(&out); +fail2: + if (csp != NULL) FcCharSetDestroy(csp); +fail: + FcPatternDestroy(p); + return NULL; +} + +uint8_t *fcFontRenderPrepare(FcConfig *conf, uint8_t *data, size_t in_length, size_t *length) { + cmp_ctx_t in; + if (!cmp_bytes_init(&in, data, in_length)) return NULL; + uint32_t size = 0; + if (!cmp_read_array(&in, &size) || size != 2) return NULL; + FcPattern *pat = decodePattern(&in); + if (pat == NULL) {cmp_bytes_take(&in, NULL); return NULL;} + FcPattern *font = decodePattern(&in); + cmp_bytes_take(&in, NULL); + if (font == NULL) {FcPatternDestroy(pat); return NULL; } + + FcPattern *res = FcFontRenderPrepare(conf, pat, font); + if (res == NULL) goto fail0; + + cmp_ctx_t out; + if (!cmp_bytes_alloc(&out, 1024)) goto fail; + if (!encodePattern(&out, res)) {cmp_bytes_free(&out); goto fail;} + FcPatternDestroy(pat); + FcPatternDestroy(font); + FcPatternDestroy(res); + return cmp_bytes_take(&out, length); + +fail: + FcPatternDestroy(res); +fail0: + FcPatternDestroy(pat); + FcPatternDestroy(font); + return NULL; +} + +uint8_t *fcFontList(FcConfig *conf, uint8_t *data, size_t in_length, size_t *length) { + cmp_ctx_t in; + if (!cmp_bytes_init(&in, data, in_length)) return NULL; + uint32_t size = 0; + if (!cmp_read_array(&in, &size) || size != 2) { + cmp_bytes_take(&in, NULL); + return NULL; + } + FcPattern *pat = decodePattern(&in); + if (pat == NULL) {cmp_bytes_take(&in, NULL); return NULL;} + FcObjectSet *os = decodeObjectSet(&in); + cmp_bytes_take(&in, NULL); + if (os == NULL) {FcPatternDestroy(pat); return NULL;} + + FcFontSet *res = FcFontList(conf, pat, os); + if (res == NULL) goto fail0; + + cmp_ctx_t out; + if (!cmp_bytes_alloc(&out, 1024)) goto fail; + if (!encodeFontSet(&out, res)) { cmp_bytes_free(&out); goto fail;} + FcFontSetDestroy(res); + return cmp_bytes_take(&out, length); + +fail: + FcFontSetDestroy(res); +fail0: + FcPatternDestroy(pat); + FcObjectSetDestroy(os); + return NULL; +} + +/*int fcConfigAcceptFont(FcConfig *conf, uint8_t *data, size_t length) { + cmp_ctx_t in; + if (!cmp_bytes_init(&in, data, length)) return -1; + FcPattern *pat = decodePattern(&in); + if (pat == NULL) return -1; + + FcBool ret = FcConfigAcceptFont(conf, pat); + FcPatternDestroy(pat); + return ret ? 1 : 0; +}*/ diff --git a/cbits/freetype-wrap.c b/cbits/fontconfig-wrap.c~ similarity index 96% rename from cbits/freetype-wrap.c rename to cbits/fontconfig-wrap.c~ index 41e9038..5679109 100644 --- a/cbits/freetype-wrap.c +++ b/cbits/fontconfig-wrap.c~ @@ -3,6 +3,36 @@ #include #include +int fcPatternEqualSubset(uint8_t *data, size_t length) { + cmp_ctx_t in; + if (!cmp_bytes_init(&in, data, length)) return -1; + uint32_t size = 0; + if (!cmp_read_array(&in, &size) || size != 3) { + cmp_bytes_take(&in, NULL); + return -1; + } + FcPattern *pa = decodePattern(&in); + if (pa == NULL) {cmp_bytes_take(&in, NULL); return -1;} + FcPattern *pb = decodePattern(&in); + if (pb == NULL) { + cmp_bytes_take(&in, NULL); + FcPatternDestroy(pa); + return -1; + } + FcObjectSet *os = decodeObjectSet(&in); + cmp_bytes_take(&in, NULL); + + int ret = -1; + if (os != NULL) ret = FcPatternEqualSubset(pa, pb, os) ? 1 : 0; + + FcPatternDestroy(pa); + FcPatternDestroy(pb); + FcObjectSetDestroy(os); + return ret; +} + + + uint8_t *fcNameParse(char *name, size_t *length) { FcPattern *pat = FcNameParse(name); if (pat == NULL) return NULL; diff --git a/cbits/fontconfig-wrap.h b/cbits/fontconfig-wrap.h new file mode 100644 index 0000000..e4c6970 --- /dev/null +++ b/cbits/fontconfig-wrap.h @@ -0,0 +1,37 @@ +#include +#include +#include +#include + +int fcPatternEqualSubset(uint8_t *data, size_t length); +uint8_t *fcDefaultSubstitute(uint8_t *data, size_t in_length, size_t *length); +uint8_t *fcNameParse(char *name, size_t *length); +char *fcNameUnparse(uint8_t *data, size_t length); +char *fcNameFormat(uint8_t *data, size_t length, char *format); +uint8_t *fcFontSetList(FcConfig *config, uint8_t *sets, size_t sets_length, + uint8_t *pat, size_t pat_length, uint8_t *objects, size_t objs_length, size_t *length); +uint8_t *fcFontSetMatch(FcConfig *config, uint8_t *sets, size_t sets_length, + uint8_t *pat, size_t pat_length, size_t *length); +uint8_t *fcFontSetSort(FcConfig *config, uint8_t *sets, size_t sets_length, + uint8_t *pat, size_t pat_length, bool trim, size_t *length); +unsigned int fcFreeTypeCharIndex(FT_Face *face, uint32_t ucs4); +uint8_t *fcFreeTypeCharSet(FT_Face *face, size_t *length); +uint8_t *fcFreeTypeCharSetAndSpacing(FT_Face *face, size_t *length); +uint8_t *fcFreeTypeQuery(char *file, int id, size_t *length); +uint8_t *fcFreeTypeQueryAll(char *file, size_t *length); +uint8_t *fcFreeTypeQueryFace(FT_Face *face, char *file, int id, size_t *length); +int fcLangSetHasLang(uint8_t *langset, size_t length, const char *lang); +uint8_t *fcGetDefaultLangs(size_t *length); +uint8_t *fcGetLangs(size_t *length); +char *fcLangNormalize(char *lang); +uint8_t *fcConfigGetConfigDirs(FcConfig *conf, size_t *length); +uint8_t *fcConfigGetFontDirs(FcConfig *conf, size_t *length); +uint8_t *fcConfigGetConfigFiles(FcConfig *conf, size_t *length); +uint8_t *fcConfigGetCacheDirs(FcConfig *conf, size_t *length); +uint8_t *fcConfigGetFonts(FcConfig *conf, bool system, size_t *length); +uint8_t *fcConfigSubstituteWithPat(FcConfig *conf, uint8_t *data, size_t in_length, bool isFont, size_t *length); +uint8_t *fcFontMatch(FcConfig *conf, uint8_t *data, size_t in_length, size_t *length); +uint8_t *fcFontSort(FcConfig *conf, uint8_t *data, size_t in_length, bool trim, size_t *length); +uint8_t *fcFontRenderPrepare(FcConfig *conf, uint8_t *data, size_t in_length, size_t *length); +uint8_t *fcFontList(FcConfig *conf, uint8_t *data, size_t in_length, size_t *length); + diff --git a/cbits/fontconfig-wrap.h~ b/cbits/fontconfig-wrap.h~ new file mode 100644 index 0000000..5ae535e --- /dev/null +++ b/cbits/fontconfig-wrap.h~ @@ -0,0 +1,37 @@ +#include +#include +#include +#include + +int fcPatternEqualSubset(uint8_t *data, size_t length); +uint8_t *fcDefaultSubstitute(uint8_t *data, size_t in_length, size_t *) +uint8_t *fcNameParse(char *name, size_t *length); +char *fcNameUnparse(uint8_t *data, size_t length); +char *fcNameFormat(uint8_t *data, size_t length, char *format); +uint8_t *fcFontSetList(FcConfig *config, uint8_t *sets, size_t sets_length, + uint8_t *pat, size_t pat_length, uint8_t *objects, size_t objs_length, size_t *length); +uint8_t *fcFontSetMatch(FcConfig *config, uint8_t *sets, size_t sets_length, + uint8_t *pat, size_t pat_length, size_t *length); +uint8_t *fcFontSetSort(FcConfig *config, uint8_t *sets, size_t sets_length, + uint8_t *pat, size_t pat_length, bool trim, size_t *length); +unsigned int fcFreeTypeCharIndex(FT_Face *face, uint32_t ucs4); +uint8_t *fcFreeTypeCharSet(FT_Face *face, size_t *length); +uint8_t *fcFreeTypeCharSetAndSpacing(FT_Face *face, size_t *length); +uint8_t *fcFreeTypeQuery(char *file, int id, size_t *length); +uint8_t *fcFreeTypeQueryAll(char *file, size_t *length); +uint8_t *fcFreeTypeQueryFace(FT_Face *face, char *file, int id, size_t *length); +int fcLangSetHasLang(uint8_t *langset, size_t length, const char *lang); +uint8_t *fcGetDefaultLangs(size_t *length); +uint8_t *fcGetLangs(size_t *length); +char *fcLangNormalize(char *lang); +uint8_t *fcConfigGetConfigDirs(FcConfig *conf, size_t *length); +uint8_t *fcConfigGetFontDirs(FcConfig *conf, size_t *length); +uint8_t *fcConfigGetConfigFiles(FcConfig *conf, size_t *length); +uint8_t *fcConfigGetCacheDirs(FcConfig *conf, size_t *length); +uint8_t *fcConfigGetFonts(FcConfig *conf, bool system, size_t *length); +uint8_t *fcConfigSubstituteWithPat(FcConfig *conf, uint8_t *data, size_t in_length, bool isFont, size_t *length); +uint8_t *fcFontMatch(FcConfig *conf, uint8_t *data, size_t in_length, size_t *length); +uint8_t *fcFontSort(FcConfig *conf, uint8_t *data, size_t in_length, bool trim, size_t *length); +uint8_t *fcFontRenderPrepare(FcConfig *conf, uint8_t *data, size_t in_length, size_t *length); +uint8_t *fcFontList(FcConfig *conf, uint8_t *data, size_t in_length, size_t *length); + diff --git a/cbits/transcode.c b/cbits/transcode.c index 1106aac..b73145d 100644 --- a/cbits/transcode.c +++ b/cbits/transcode.c @@ -380,7 +380,7 @@ FcFontSet **decodeFontSets(cmp_ctx_t *bytes, size_t *nsets) { uint32_t size; if (!cmp_read_array(bytes, &size)) return NULL; - if (nsets != NULL) nsets = size; + if (nsets != NULL) *nsets = size; FcFontSet **ret = calloc(sizeof(FcFontSet *), size); uint32_t i; @@ -467,7 +467,7 @@ bool cmp_bytes_init(cmp_ctx_t *ctx, uint8_t *buf, size_t length) { } bool cmp_bytes_alloc(cmp_ctx_t *ctx, size_t length) { - struct uint8_t *bytes = malloc(length); + uint8_t *bytes = malloc(length); if (bytes == NULL) return false; return cmp_bytes_init(ctx, bytes, length); } diff --git a/fontconfig-pure.cabal b/fontconfig-pure.cabal index 888a5b9..22f60f1 100644 --- a/fontconfig-pure.cabal +++ b/fontconfig-pure.cabal @@ -66,9 +66,12 @@ library exposed-modules: Graphics.Text.Font.Choose.CharSet, Graphics.Text.Font.Choose.LangSet, Graphics.Text.Font.Choose.ObjectSet, Graphics.Text.Font.Choose.Range, Graphics.Text.Font.Choose.StrSet, Graphics.Text.Font.Choose.Value, - Graphics.Text.Font.Choose.Pattern, Graphics.Text.Font.Choose.FontSet + Graphics.Text.Font.Choose.Pattern, Graphics.Text.Font.Choose.FontSet, + Graphics.Text.Font.Choose.Config, Graphics.Text.Font.Choose.Result, + Graphics.Text.Font.Choose.Internal.FFI - c-sources: cbits/cmp.c, cbits/transcode.c, cbits/freetype-wrap.c + c-sources: cbits/cmp.c, cbits/transcode.c, cbits/fontconfig-wrap.c + include-dirs: cbits pkgconfig-depends: fontconfig @@ -82,7 +85,7 @@ library build-depends: base >=4.12 && <5, containers >=0.1 && <1, css-syntax, freetype2 >=0.2 && <0.3, hashable >=1.3 && <2, linear >=1.0.1 && <2, scientific, stylist-traits >=0.1.1 && <1, text, msgpack >= 1.0 && <2, - vector >= 0.13 && <1 + vector >= 0.13 && <1, bytestring -- Directories containing source files. hs-source-dirs: lib diff --git a/lib/Graphics/Text/Font/Choose/Config.hs b/lib/Graphics/Text/Font/Choose/Config.hs new file mode 100644 index 0000000..56ae462 --- /dev/null +++ b/lib/Graphics/Text/Font/Choose/Config.hs @@ -0,0 +1,35 @@ +{-# LANGUAGE CApiFFI #-} +module Graphics.Text.Font.Choose.Config where + +import Foreign.Ptr (Ptr, FunPtr) +import Foreign.ForeignPtr (ForeignPtr, newForeignPtr) + +import Graphics.Text.Font.Choose.Result (throwBool, throwNull) + +data Config' +type Config = ForeignPtr Config' + + + +initLoadConfig :: IO Config +initLoadConfig = newForeignPtr fcConfigDestroy =<< throwNull =<< fcInitLoadConfig -- FIXME: What's proper memory-management here? + +initLoadConfigAndFonts :: IO Config +initLoadConfigAndFonts = newForeignPtr fcConfigDestroy =<< throwNull =<< fcInitLoadConfigAndFonts -- FIXME: What's proper memory-management here? + +init :: IO () +init = throwBool =<< fcInit +foreign import capi "fontconfig/fontconfig.h FcFini" fini :: IO () +foreign import capi "fontconfig/fontconfig.h FcGetVersion" version :: Int +reinit :: IO () +reinit = throwBool =<< fcInitReinitialize +bringUptoDate :: IO () +bringUptoDate = throwBool =<< fcInitBringUptoDate + +foreign import capi "fontconfig/fontconfig.h FcInitLoadConfig" fcInitLoadConfig :: IO (Ptr Config') +foreign import capi "fontconfig/fontconfig.h FcInitLoadConfigAndFonts" fcInitLoadConfigAndFonts :: IO (Ptr Config') +foreign import capi "fontconfig/fontconfig.h FcInit" fcInit :: IO Bool +foreign import capi "fontconfig/fontconfig.h &FcConfigDestroy" fcConfigDestroy :: FunPtr (Ptr Config' -> IO ()) + +foreign import capi "fontconfig/fontconfig.h FcInitReinitialize" fcInitReinitialize :: IO Bool +foreign import capi "fontconfig/fontconfig.h FcInitBringUptoDate" fcInitBringUptoDate :: IO Bool diff --git a/lib/Graphics/Text/Font/Choose/Internal/FFI.hs b/lib/Graphics/Text/Font/Choose/Internal/FFI.hs new file mode 100644 index 0000000..e63ca35 --- /dev/null +++ b/lib/Graphics/Text/Font/Choose/Internal/FFI.hs @@ -0,0 +1,36 @@ +module Graphics.Text.Font.Choose.Internal.FFI where + +import Data.MessagePack (MessagePack, pack, unpack) +import Foreign.C.String (CString, withCString) +import Foreign.Ptr (Ptr) +import Foreign.Storable (Storable(..)) +import Foreign.Marshal.Alloc (alloca) +import Data.Tuple (swap) +import Graphics.Text.Font.Choose.Result (throwNull) +import Data.Maybe (fromJust) + +import Data.ByteString.Unsafe (unsafeUseAsCStringLen, unsafePackMallocCStringLen) +import Data.ByteString.Lazy (toStrict, fromStrict) +import System.IO.Unsafe (unsafePerformIO) + +withMessage :: MessagePack a => (CString -> Int -> b) -> a -> b +withMessage inner arg = unsafePerformIO $ + unsafeUseAsCStringLen (toStrict $ pack arg) (return . uncurry inner) + +fromMessage :: MessagePack a => (Ptr Int -> CString) -> Maybe a +fromMessage inner = unpack $ fromStrict $ unsafePerformIO $ do + unsafePackMallocCStringLen . swap =<< withPtr (throwNull . inner) + +fromMessage0 :: MessagePack a => (Ptr Int -> CString) -> a +fromMessage0 = fromJust . fromMessage + +withCString' :: (CString -> a) -> String -> a +withCString' inner = unsafePerformIO . flip withCString (return . inner) + +-- I don't want to pull in all of inline-c for this util! +withPtr :: (Storable a) => (Ptr a -> IO b) -> IO (a, b) +withPtr f = do + alloca $ \ptr -> do + x <- f ptr + y <- peek ptr + return (y, x) diff --git a/lib/Graphics/Text/Font/Choose/Pattern.hs b/lib/Graphics/Text/Font/Choose/Pattern.hs index 261deda..fda2265 100644 --- a/lib/Graphics/Text/Font/Choose/Pattern.hs +++ b/lib/Graphics/Text/Font/Choose/Pattern.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DeriveGeneric, CApiFFI #-} module Graphics.Text.Font.Choose.Pattern where import Data.Map as M @@ -6,7 +6,15 @@ import Data.MessagePack (MessagePack(..), Object(..)) import Data.Hashable (Hashable(..)) import GHC.Generics (Generic) +import Foreign.C.String (CString, peekCString) +import Foreign.Ptr (Ptr) +import Control.Exception (throw) +import Graphics.Text.Font.Choose.Internal.FFI (withMessage, fromMessage0, withCString') +import System.IO.Unsafe (unsafePerformIO) + import Graphics.Text.Font.Choose.Value +import Graphics.Text.Font.Choose.ObjectSet +import Graphics.Text.Font.Choose.Result type Pattern = Map String [(Binding, Value)] data Pattern' = Pattern' { unPattern :: Pattern } @@ -23,6 +31,29 @@ instance MessagePack Binding where toObject Weak = ObjectBool False toObject Same = ObjectNil +equalSubset :: Pattern -> Pattern -> ObjectSet -> Bool +equalSubset a b os = case withMessage fcPatternEqualSubset [toObject a, toObject b, toObject os] of + 0 -> False + 1 -> True + _ -> throw ErrOOM + +foreign import capi "fontconfig-wrap.h" fcPatternEqualSubset :: CString -> Int -> Int + +defaultSubstitute :: Pattern -> Pattern +defaultSubstitute = fromMessage0 . withMessage fcDefaultSubstitute + +foreign import capi "fontconfig-wrap.h" fcDefaultSubstitute :: CString -> Int -> Ptr Int -> CString + +nameParse :: String -> Pattern +nameParse = fromMessage0 . withCString' fcNameParse + +foreign import capi "fontconfig-wrap.h" fcNameParse :: CString -> Ptr Int -> CString + +nameUnparse :: Pattern -> String +nameUnparse = unsafePerformIO . peekCString . withMessage fcNameUnparse + +foreign import capi "fontconfig-wrap.h" fcNameUnparse :: CString -> Int -> CString + ------ --- CSS ------ diff --git a/lib/Graphics/Text/Font/Choose/Result.hs b/lib/Graphics/Text/Font/Choose/Result.hs new file mode 100644 index 0000000..c8de008 --- /dev/null +++ b/lib/Graphics/Text/Font/Choose/Result.hs @@ -0,0 +1,20 @@ +module Graphics.Text.Font.Choose.Result (FcException(..), throwBool, throwNull, throwString) where + +import Foreign.Ptr (Ptr, nullPtr) +import Text.Read (readMaybe) +import Control.Exception (Exception, throwIO) + +data FcException = ErrType | ErrNoId | ErrOOM | ErrOther deriving (Read, Show, Eq, Enum) +instance Exception FcException + +throwBool :: Bool -> IO () +throwBool False = throwIO ErrOOM +throwBool True = return () + +throwNull :: Ptr a -> IO (Ptr a) +throwNull a | a == nullPtr = throwIO ErrOOM + | otherwise = return a + +throwString :: String -> IO () +throwString a | Just b <- readMaybe a :: Maybe FcException = throwIO b + | otherwise = return () -- 2.30.2