#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); if (res == NULL) goto fail_list; 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); fail_list: 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! Fold it into this C call, safer & *might* save overhead. FcPattern *res = NULL; if (!FcConfigSubstitute(config, pattern, FcMatchPattern)) goto fail2; FcDefaultSubstitute(pattern); FcResult err; res = FcFontSetMatch(config, fontsets, nsets, pattern, &err); if (res == NULL) goto fail2; 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! Fold into this C call, safer & *might* save overhead. if (!FcConfigSubstitute(config, pattern, FcMatchPattern)) goto fail2; FcDefaultSubstitute(pattern); FcResult err; FcCharSet *charset = NULL; FcFontSet *res = FcFontSetSort(config, fontsets, nsets, pattern, trim, &charset, &err); if (res == NULL) goto fail2; 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 { ok = ok || encodeRenderableFontSet(&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 fcLangSetCompare(uint8_t *langset, size_t length) { cmp_ctx_t in; if (!cmp_bytes_init(&in, langset, length)) return -1; uint32_t size = 0; if (!cmp_read_array(&in, &size) || size != 2) {cmp_bytes_take(&in, NULL); return -1;} FcLangSet *a = decodeLangSet(&in); if (a == NULL) {cmp_bytes_take(&in, NULL); return -1;} FcLangSet *b = decodeLangSet(&in); cmp_bytes_take(&in, NULL); if (b == NULL) return -1; FcLangResult ret = FcLangSetCompare(a, b); FcLangSetDestroy(a); FcLangSetDestroy(b); switch (ret) { case FcLangDifferentLang: return 0; case FcLangEqual: return 1; case FcLangDifferentTerritory: return 2; default: return -2; } } 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) { // NOTE: We shouldn't free results! FcFontSet *res = FcConfigGetFonts(conf, system ? FcSetSystem : FcSetApplication); if (res == NULL) return NULL; cmp_ctx_t out; if (!cmp_bytes_alloc(&out, 1024)) return NULL; if (!encodeFontSet(&out, res)) { cmp_bytes_free(&out); return NULL; } 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, 2)) goto fail3; if (!encodeFontSet(&out, res)) goto fail3; if (!cmp_write_array(&out, 0)) 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; }*/