#include "cmp.h"
#include <fontconfig/fontconfig.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
FcStrSet *decodeStrSet(cmp_ctx_t *bytes) {
if (bytes == NULL) return NULL;
uint32_t size;
if (!cmp_read_array(bytes, &size)) return NULL;
FcStrSet *ret = FcStrSetCreate();
if (ret == NULL) return NULL;
for (uint32_t i = 0; i < size; i++) {
char text[512]; // What's the appropriate size here?
uint32_t str_size = 512;
if (!cmp_read_str(bytes, text, &str_size)) goto fail;
if (!FcStrSetAdd(ret, text)) goto fail;
}
return ret;
fail:
FcStrSetDestroy(ret);
return NULL;
}
FcStrSet *decodeFileSet(cmp_ctx_t *bytes) {
if (bytes == NULL) return NULL;
uint32_t size;
if (!cmp_read_array(bytes, &size)) return NULL;
FcStrSet *ret = FcStrSetCreate();
if (ret == NULL) return NULL;
for (uint32_t i = 0; i < size; i++) {
char text[512]; // What's the appropriate size here?
uint32_t str_size = 512;
if (!cmp_read_str(bytes, text, &str_size)) goto fail;
if (!FcStrSetAddFilename(ret, text)) goto fail;
}
return ret;
fail:
FcStrSetDestroy(ret);
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;
uint32_t size = 0;
FcStrListFirst(iter);
while (FcStrListNext(iter) != NULL) size++;
if (!cmp_write_array(bytes, size)) goto exit;
FcStrListFirst(iter);
char *text = FcStrListNext(iter);
while (text != NULL) {
if (!cmp_write_str(bytes, text, strlen(text))) goto exit;
text = FcStrListNext(iter);
}
ret = true;
exit:
FcStrListDone(iter);
return ret;
}
FcCharSet *decodeCharSet(cmp_ctx_t *bytes) {
if (bytes == NULL) return NULL;
uint32_t size;
if (!cmp_read_array(bytes, &size)) return NULL;
FcCharSet *ret = FcCharSetCreate();
if (ret == NULL) return NULL;
FcChar32 prev = 0;
for (uint32_t i = 0; i < size; i++) {
uint32_t x;
if (!cmp_read_uint(bytes, &x)) goto fail;
prev += x;
if (!FcCharSetAddChar(ret, prev)) goto fail;
}
return ret;
fail:
FcCharSetDestroy(ret);
return NULL;
}
bool encodeCharSet(cmp_ctx_t *bytes, FcCharSet *data) {
if (bytes == NULL || data == NULL) return false;
FcChar32 size = FcCharSetCount(data);
FcChar32 count = 0; // For validation
if (!cmp_write_array(bytes, size)) return false;
FcChar32 map[FC_CHARSET_MAP_SIZE];
FcChar32 next;
FcChar32 c = FcCharSetFirstPage(data, map, &next);
FcChar32 prev = 0;
while (c != FC_CHARSET_DONE) {
if (!cmp_write_uinteger(bytes, c - prev)) return false;
prev = c;
count++;
c = FcCharSetNextPage(data, map, &next);
}
assert(size == count);
return true;
}
FcLangSet *decodeLangSet(cmp_ctx_t *bytes) {
if (bytes == NULL) return NULL;
uint32_t size;
if (!cmp_read_array(bytes, &size)) return NULL;
FcLangSet *ret = FcLangSetCreate();
if (ret == NULL) return NULL;
for (uint32_t i = 0; i < size; i++) {
char lang[10];
uint32_t str_size = 10;
if (!cmp_read_str(bytes, lang, &str_size)) goto fail;
char *lang2 = FcLangNormalize(lang);
if (lang2 == NULL) goto fail;
if (!FcLangSetAdd(ret, lang2)) goto fail;
free(lang2);
}
return ret;
fail:
FcLangSetDestroy(ret);
return NULL;
}
bool encodeLangSet(cmp_ctx_t *bytes, FcLangSet *data) {
if (bytes == NULL || data == NULL) return false;
FcStrSet *langs = FcLangSetGetLangs(data);
if (langs == NULL) return false;
bool ret = encodeStrSet(bytes, langs);
FcStrSetDestroy(langs);
return ret;
}
FcObjectSet *decodeObjectSet(cmp_ctx_t *bytes) {
if (bytes == NULL) return NULL;
uint32_t size;
if (!cmp_read_array(bytes, &size)) return NULL;
FcObjectSet *ret = FcObjectSetCreate();
for (uint32_t i = 0; i < size; i++) {
char object[20];
uint32_t o_size = 20;
if (!cmp_read_str(bytes, lang, &str_size)) goto fail;
if (!FcObjectSetAdd(ret, object)) goto fail;
}
return ret;
fail:
FcObjectSetDestroy(ret);
return NULL;
}
// No corresponding objectset encoder.
FcRange *decodeRange(cmp_ctx_t *bytes) {
if (bytes == NULL) return NULL;
uint32_t size;
if (!cmp_read_map(bytes, &size)) return NULL;
double range[2];
for (uint32_t i = 0; i < size; i++) {
uint32_t j;
if (!cmp_read_uint(bytes, &j)) return NULL;
if (!cmp_read_double(bytes, &range[j])) return NULL;
}
return FcCreateDouble(range[0], range[1]);
}
bool encodeRange(cmp_ctx_t *bytes, FcRange *data) {
if (bytes == NULL || data == NULL) return false;
double begin, end;
if (!FcRangeGetDouble(data, &begin, &end)) return false;
if (!cmp_write_map(bytes, 2)) return false;
if (!cmp_write_uint(bytes, 0)) return false;
if (!cmp_write_double(bytes, begin)) return false;
if (!cmp_write_uint(bytes, 1)) return false;
if (!cmp_write_double(bytes, end)) return false;
return true;
}
FcMatrix *decodeMatrix(cmp_ctx_t *bytes) {
if (bytes == NULL) return NULL;
uint32_t size;
if (!cmp_read_array(bytes, &size) || size != 4) return NULL;
FcMatrix *ret = malloc(sizeof(FcMatrix));
if (!cmp_read_double(bytes, &ret->xx)) goto fail;
if (!cmp_read_double(bytes, &ret->xy)) goto fail;
if (!cmp_read_double(bytes, &ret->yx)) goto fail;
if (!cmp_read_double(bytes, &ret->yy)) goto fail;
return ret;
fail:
free(ret);
return NULL;
}
bool encodeMatrix(cmp_ctx_t *bytes, FcMatrix *data) {
if (bytes == NULL || data == NULL) return NULL;
if (!cmp_write_array(bytes, 4)) return false;
if (!cmp_write_double(bytes, data->xx)) return false;
if (!cmp_write_double(bytes, data->xy)) return false;
if (!cmp_write_double(bytes, data->yx)) return false;
if (!cmp_write_double(bytes, data->yy)) return false;
return true;
}
bool decodeValue(cmp_ctx_t *bytes, FcValue *out) {
if (bytes == NULL || out == NULL) return false;
if (cmp_read_nil(bytes)) out->type = FcTypeVoid;
else if (cmp_read_int(bytes, &out->u.i)) out->type = FcTypeInteger;
else if (cmp_read_double(bytes, &out->u.d)) out->type = FcTypeDouble;
else if (cmp_read_string(bytes, &out->u.s, NULL)) out->type = FcTypeString;
else if (cmp_read_bool(bytes, &out->u.b)) out->type = FcTypeBool;
else if ((out->u.m = decodeMatrix(bytes)) != NULL) out->type = FcTypeMatrix;
else if ((out->u.c = decodeCharSet(bytes)) != NULL) out->type = FcTypeCharSet;
// Not supporting FcTypeFcFace
else if ((out->u.l = decodeLangSet(bytes)) != NULL) out->type = FcTypeLangSet;
else if ((out->u.r = decodeRange(bytes)) != NULL) out->type = FcTypeRange;
else return false;
return true;
}
bool encodeValue(cmp_ctx_t *bytes, FcValue *data) {
if (bytes == NULL || data == NULL) return false;
switch (data->type) {
case FcTypeVoid:
return cmp_write_nil(bytes);
case FcTypeInteger:
return cmp_write_int(bytes, data->u.i);
case FcTypeDouble:
return cmp_write_double(bytes, data->u.d);
case FcTypeString:
return cmp_write_string(bytes, data->u.s, strlen(data->u.s));
case FcTypeBool:
return cmp_write_bool(bytes, data->u.b);
case FcTypeMatrix:
return encodeMatrix(bytes, data->u.m);
case FcTypeCharSet:
return encodeCharSet(bytes, data->u.c);
case FcTypeFcFace:
return true; // Not supporting this yet...
case FcTypeLangSet:
return encodeLangSet(bytes, data->u.l);
case FcTypeRange:
return encodeRange(bytes, data->u.r);
default:
return false;
}
}
FcPattern *decodePattern(cmp_ctx_t *bytes) {
if (bytes == NULL) return NULL;
uint32_t size;
if (!cmp_read_map(bytes, &size)) return NULL;
FcPattern *ret = FcPatternCreate();
for (uint32_t i = 0; i < size; i++) {
char object[20];
uint32_t osize = 20;
if (!cmp_read_string(bytes, object, &osize)) goto fail;
uint32_t vsize;
if (!cmp_read_array(bytes, &vsize)) goto fail;
for (uint32 j = 0; j < vsize; j++) {
uint32_t tsize;
if (!cmp_read_array(bytes, &tsize) || tsize != 2) goto fail;
bool is_strong = false;
if (cmp_read_bool(bytes, &is_weak)) {}
else if (cmp_read_nil(bytes)) {}
else goto fail;
FcValue val;
if (!decodeValue(bytes, &val)) goto fail;
if (is_strong) {
if (!FcPatternAdd(bytes, object, value, true)) goto fail;
} else {
if (!FcPatternAddWeak(bytes, object, value, true)) goto fail;
}
}
}
return ret;
fail:
FcPatternDestroy(ret);
return NULL;
}
bool encodePattern(cmp_ctx_t *bytes, FcPattern *data) {
if (bytes == NULL || data == NULL) return false;
int size = FcPatternObjectCount(data);
if (!cmp_write_map(bytes, &size)) return false;
FcPatternIter iter;
FcPatternIterStart(data, &iter);
int count = 0;
while (FcPatternIterNext(data, &iter)) {
count++;
char *obj = FcPatternIterGetObject(data, &iter);
if (!cmp_write_string(bytes, obj, strlen(obj))) return false;
int nvalues = FcPatternIterValueCount(data, &iter);
if (!cmp_write_array(bytes, nvalues)) return false;
for (int j = 0; j < nvalues; j++) {
FcValue val;
FcValueBinding weight;
if (FcPatternIterGetValue(data, &iter, j, &val, &weight) != FcResultMatch)
return false;
if (!cmp_write_array(bytes, 2)) return false;
switch (weight) {
case FcValueBindingWeak:
if (!cmp_write_bool(bytes, false)) return false;
break;
case FcValueBindingStrong:
if (!cmp_write_bool(bytes, true)) return false;
break;
case FcValueBindingSame:
if (!cmp_write_nil(bytes)) return false;
break;
default:
return false;
}
if (!encodeValue(bytes, &val)) return false;
}
}
assert(size == count);
return true;
}
FcFontSet *decodeFontSet(cmp_ctx_t *bytes) {
if (bytes == NULL) return NULL;
uint32_t size;
if (!cmp_read_array(bytes, &size)) return NULL;
FcFontSet *ret = FcFontSetCreate();
if (ret == NULL) return NULL;
for (uint32_t i = 0; i < size; i++) {
FcPattern *font = decodePattern(bytes);
if (font == NULL) goto fail;
if (!FcFontSetAdd(ret, font)) goto fail;
}
return ret;
fail:
FcFontSetDestroy(ret);
return NULL;
}
bool encodeFontSet(cmp_ctx_t *bytes, FcFontSet *data) {
if (bytes == NULL || data == NULL) return NULL;
if (!cmp_write_array(bytes, data->nfont)) return false;
for (int i = 0; i < data->nfont; i++) {
if (!encodePattern(bytes, data->fonts[i])) return false;
}
return true;
}