~alcinnz/fontconfig-pure

04e3e462ef4e7ce094848c0ea1424f1624f8bf9a — Adrian Cochrane 8 months ago d242b06
Draft C-side wrappers for various FontConfig APIs.
4 files changed, 695 insertions(+), 11 deletions(-)

A cbits/freetype-wrap.c
M cbits/transcode.c
A cbits/transcode.h
M fontconfig-pure.cabal
A cbits/freetype-wrap.c => cbits/freetype-wrap.c +651 -0
@@ 0,0 1,651 @@
#include "transcode.h"
#include <fontconfig/fontconfig.h>
#include <stdlib.h>
#include <fontconfig/fcfreetype.h>

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

M cbits/transcode.c => cbits/transcode.c +13 -11
@@ 46,26 46,28 @@ fail:
    return NULL;
}

bool encodeStrSet(cmp_ctx_t *bytes, FcStrSet *data) {
    if (bytes == NULL || data == NULL) return false;
    bool ret = false;

    FcStrList *iter = FcStrListCreate(data);
    if (iter == NULL) return false;

bool encodeStrList(cmp_ctx_t *bytes, FcStrList *iter) {
    uint32_t size = 0;
    FcStrListFirst(iter);
    while (FcStrListNext(iter) != NULL) size++;
    if (!cmp_write_array(bytes, size)) goto exit;
    if (!cmp_write_array(bytes, size)) return false;

    FcStrListFirst(iter);
    char *text = FcStrListNext(iter);
    while (text != NULL) {
        if (!cmp_write_str(bytes, text, strlen(text))) goto exit;
        if (!cmp_write_str(bytes, text, strlen(text))) return false;
        text = FcStrListNext(iter);
    }
    ret = true;
exit:
    return true;
}

bool encodeStrSet(cmp_ctx_t *bytes, FcStrSet *data) {
    if (bytes == NULL || data == NULL) return false;

    FcStrList *iter = FcStrListCreate(data);
    if (iter == NULL) return false;

    bool ret = encodeStrList(bytes, iter);
    FcStrListDone(iter);
    return ret;
}

A cbits/transcode.h => cbits/transcode.h +29 -0
@@ 0,0 1,29 @@
#include "cmp.h"
#include <fontconfig/fontconfig.h>

FcStrSet *decodeStrSet(cmp_ctx_t *bytes);
FcStrSet *decodeFileSet(cmp_ctx_t *bytes);
bool encodeStrSet(cmp_ctx_t *bytes, FcStrSet *data);
FcCharSet *decodeCharSet(cmp_ctx_t *bytes);
bool encodeStrList(cmp_ctx_t *bytes, const FcStrList *data);
bool encodeCharSet(cmp_ctx_t *bytes, const FcCharSet *data);
FcLangSet *decodeLangSet(cmp_ctx_t *bytes);
bool encodeLangSet(cmp_ctx_t *bytes, const FcLangSet *data);
FcObjectSet *decodeObjectSet(cmp_ctx_t *bytes);
FcRange *decodeRange(cmp_ctx_t *bytes);
bool encodeRange(cmp_ctx_t *bytes, const FcRange *data);
FcMatrix *decodeMatrix(cmp_ctx_t *bytes);
bool encodeMatrix(cmp_ctx_t *bytes, const FcMatrix *data);
bool decodeValue(cmp_ctx_t *bytes, FcValue *out);
bool encodeValue(cmp_ctx_t *bytes, FcValue *data);
FcPattern *decodePattern(cmp_ctx_t *bytes);
bool encodePattern(cmp_ctx_t *bytes, FcPattern *data);
FcFontSet *decodeFontSet(cmp_ctx_t *bytes);
FcFontSet **decodeFontSets(cmp_ctx_t *bytes, size_t *nsets);
bool encodeFontSet(cmp_ctx_t *bytes, FcFontSet *data);
bool encodeResult(cmp_ctx_t *bytes, FcResult res);

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);
void cmp_bytes_free(cmp_ctx_t *ctx);
uint8_t *cmp_bytes_take(cmp_ctx_t *ctx, size_t *length);

M fontconfig-pure.cabal => fontconfig-pure.cabal +2 -0
@@ 70,6 70,8 @@ library

    c-sources: cbits/cmp.c, cbits/transcode.c, cbits/freetype-wrap.c

    pkgconfig-depends: fontconfig

    -- Modules included in this library but not exported.
    -- other-modules: