A cbits/fontconfig-wrap.c => cbits/fontconfig-wrap.c +699 -0
@@ 0,0 1,699 @@
+#include "transcode.h"
+#include <fontconfig/fontconfig.h>
+#include <stdlib.h>
+#include <fontconfig/fcfreetype.h>
+
+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;
+}*/
R cbits/freetype-wrap.c => cbits/fontconfig-wrap.c~ +30 -0
@@ 3,6 3,36 @@
#include <stdlib.h>
#include <fontconfig/fcfreetype.h>
+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;
A cbits/fontconfig-wrap.h => cbits/fontconfig-wrap.h +37 -0
@@ 0,0 1,37 @@
+#include <fontconfig/fontconfig.h>
+#include <fontconfig/fcfreetype.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+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);
+
A cbits/fontconfig-wrap.h~ => cbits/fontconfig-wrap.h~ +37 -0
@@ 0,0 1,37 @@
+#include <fontconfig/fontconfig.h>
+#include <fontconfig/fcfreetype.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+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);
+
M cbits/transcode.c => cbits/transcode.c +2 -2
@@ 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);
}
M fontconfig-pure.cabal => fontconfig-pure.cabal +6 -3
@@ 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
A lib/Graphics/Text/Font/Choose/Config.hs => lib/Graphics/Text/Font/Choose/Config.hs +35 -0
@@ 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
A lib/Graphics/Text/Font/Choose/Internal/FFI.hs => lib/Graphics/Text/Font/Choose/Internal/FFI.hs +36 -0
@@ 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)
M lib/Graphics/Text/Font/Choose/Pattern.hs => lib/Graphics/Text/Font/Choose/Pattern.hs +32 -1
@@ 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
------
A lib/Graphics/Text/Font/Choose/Result.hs => lib/Graphics/Text/Font/Choose/Result.hs +20 -0
@@ 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 ()