packages feed

fontconfig-pure-0.5.1.0: cbits/fontconfig-wrap.c

#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);
    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;
}*/