diff --git a/lib/bk/bkblock.c b/lib/bk/bkblock.c index 6badd566..1b8c27a2 100644 --- a/lib/bk/bkblock.c +++ b/lib/bk/bkblock.c @@ -1,6 +1,6 @@ #include "bkblock.h" -static void bkblock_acells(caryll_bkblock *b, size_t len) { +static void bkblock_acells(caryll_bkblock *b, uint32_t len) { if (len <= b->length + b->free) { // We have enough space b->free -= len - b->length; @@ -20,8 +20,8 @@ bool bk_cell_is_ptr(bk_cell *cell) { return cell->t >= p16; } -static bk_cell *bkblock_grow(caryll_bkblock *b, size_t len) { - size_t olen = b->length; +static bk_cell *bkblock_grow(caryll_bkblock *b, uint32_t len) { + uint32_t olen = b->length; bkblock_acells(b, olen + len); return &(b->cells[olen]); } @@ -46,7 +46,7 @@ void bkblock_pushptr(caryll_bkblock *b, bk_cell_type type, caryll_bkblock *p) { static void vbkpushitems(caryll_bkblock *b, bk_cell_type type0, va_list ap) { bk_cell_type curtype = type0; while (curtype) { - if (curtype == bembed) { + if (curtype == bkcopy || curtype == bkembed) { caryll_bkblock *par = va_arg(ap, caryll_bkblock *); if (par && par->cells) { for (uint32_t j = 0; j < par->length; j++) { @@ -57,7 +57,7 @@ static void vbkpushitems(caryll_bkblock *b, bk_cell_type type0, va_list ap) { } } } - if (par) { + if (curtype == bkembed && par) { free(par->cells); free(par); } diff --git a/lib/bk/bkblock.h b/lib/bk/bkblock.h index aa96ae42..9d5c2ebb 100644 --- a/lib/bk/bkblock.h +++ b/lib/bk/bkblock.h @@ -9,13 +9,16 @@ struct __caryll_bkblock; typedef enum { - bkover = 0, // nothing - b8 = 1, // byte - b16 = 2, // short - b32 = 3, // long - p16 = 0x10, // 16-bit offset, p = pointer to block - p32 = 0x11, // 32-bit offset, p = pointer to block - bembed = 0xFF // Embed another block + bkover = 0, // nothing + b8 = 1, // byte + b16 = 2, // short + b32 = 3, // long + p16 = 0x10, // 16-bit offset, p = pointer to block + p32 = 0x11, // 32-bit offset, p = pointer to block + sp16 = 0x80, // 16-bit offset, p = pointer to block, marked as compact + sp32 = 0x81, // 32-bit offset, p = pointer to block, marked as compact + bkcopy = 0xFE, // Embed another block + bkembed = 0xFF // Embed another block } bk_cell_type; typedef enum { VISIT_WHITE, VISIT_GRAY, VISIT_BLACK } bk_cell_visit_state; @@ -31,8 +34,9 @@ typedef struct __caryll_bkblock { bk_cell_visit_state _visitstate; uint32_t _index; uint32_t _height; - size_t length; - size_t free; + uint32_t _depth; + uint32_t length; + uint32_t free; bk_cell *cells; } caryll_bkblock; diff --git a/lib/bk/bkgraph.c b/lib/bk/bkgraph.c index dcf4034c..beb067ec 100644 --- a/lib/bk/bkgraph.c +++ b/lib/bk/bkgraph.c @@ -38,17 +38,27 @@ static uint32_t dfs_insert_cells(caryll_bkblock *b, caryll_bkgraph *f, uint32_t return height; } -static int _by_order(const void *_a, const void *_b) { +static int _by_height(const void *_a, const void *_b) { const bkgraph_entry *a = _a; const bkgraph_entry *b = _b; return a->height == b->height ? a->order - b->order : b->height - a->height; } +static int _by_order(const void *_a, const void *_b) { + const bkgraph_entry *a = _a; + const bkgraph_entry *b = _b; + return a->block && b->block && a->block->_visitstate != b->block->_visitstate // Visited first + ? b->block->_visitstate - a->block->_visitstate + : a->block && b->block && a->block->_depth != b->block->_depth // By depth + ? a->block->_depth - b->block->_depth + : b->order - a->order; // By order +} + caryll_bkgraph *caryll_bkgraph_from_block(caryll_bkblock *b) { caryll_bkgraph *forest = calloc(1, sizeof(caryll_bkgraph)); uint32_t tsOrder = 0; dfs_insert_cells(b, forest, &tsOrder); - qsort(forest->entries, forest->length, sizeof(bkgraph_entry), _by_order); + qsort(forest->entries, forest->length, sizeof(bkgraph_entry), _by_height); for (uint32_t j = 0; j < forest->length; j++) { forest->entries[j].block->_index = j; forest->entries[j].alias = j; @@ -65,67 +75,6 @@ void caryll_delete_bkgraph(caryll_bkgraph *f) { } free(f->entries); } -static size_t caryll_bkblock_size(caryll_bkblock *b) { - size_t size = 0; - for (uint32_t j = 0; j < b->length; j++) - switch (b->cells[j].t) { - case b8: - size += 1; - break; - case b16: - case p16: - size += 2; - break; - case b32: - case p32: - size += 4; - break; - default: - break; - } - return size; -} - -static uint32_t getoffset(size_t *offsets, caryll_bkblock *ref, caryll_bkblock *target, uint8_t bits) { - size_t offref = offsets[ref->_index]; - size_t offtgt = offsets[target->_index]; - if (offtgt < offref || (offtgt - offref) >> bits) { - fprintf(stderr, "[otfcc-fea] Warning : Unable to fit offset %d into %d bits.\n", (int32_t)(offtgt - offref), - bits); - } - return (uint32_t)(offtgt - offref); -} -static void caryll_write_bkblock(caryll_buffer *buf, caryll_bkblock *b, size_t *offsets) { - for (uint32_t j = 0; j < b->length; j++) { - switch (b->cells[j].t) { - case b8: - bufwrite8(buf, b->cells[j].z); - break; - case b16: - bufwrite16b(buf, b->cells[j].z); - break; - case b32: - bufwrite32b(buf, b->cells[j].z); - break; - case p16: - if (b->cells[j].p) { - bufwrite16b(buf, getoffset(offsets, b, b->cells[j].p, 16)); - } else { - bufwrite16b(buf, 0); - } - break; - case p32: - if (b->cells[j].p) { - bufwrite32b(buf, getoffset(offsets, b, b->cells[j].p, 32)); - } else { - bufwrite32b(buf, 0); - } - break; - default: - break; - } - } -} static uint32_t gethash(caryll_bkblock *b) { uint32_t h = 5381; @@ -140,6 +89,8 @@ static uint32_t gethash(caryll_bkblock *b) { break; case p16: case p32: + case sp16: + case sp32: if (b->cells[j].p) { h += b->cells[j].p->_index; } break; default: @@ -163,6 +114,8 @@ static bool compareblock(caryll_bkblock *a, caryll_bkblock *b) { break; case p16: case p32: + case sp16: + case sp32: if (a->cells[j].p != b->cells[j].p) return false; break; default: @@ -181,6 +134,8 @@ static void replaceptr(caryll_bkgraph *f, caryll_bkblock *b) { switch (b->cells[j].t) { case p16: case p32: + case sp16: + case sp32: if (b->cells[j].p) { uint32_t index = b->cells[j].p->_index; while (f->entries[index].alias != index) { @@ -223,27 +178,224 @@ void caryll_minimize_bkgraph(caryll_bkgraph *f) { } } +static size_t caryll_bkblock_size(caryll_bkblock *b) { + size_t size = 0; + for (uint32_t j = 0; j < b->length; j++) + switch (b->cells[j].t) { + case b8: + size += 1; + break; + case b16: + case p16: + case sp16: + size += 2; + break; + case b32: + case p32: + case sp32: + size += 4; + break; + default: + break; + } + return size; +} + +static uint32_t getoffset(size_t *offsets, caryll_bkblock *ref, caryll_bkblock *target, uint8_t bits) { + size_t offref = offsets[ref->_index]; + size_t offtgt = offsets[target->_index]; + if (offtgt < offref || (offtgt - offref) >> bits) { + fprintf(stderr, "[otfcc-fea] Warning : Unable to fit offset %d into %d bits.\n", (int32_t)(offtgt - offref), + bits); + } + return (uint32_t)(offtgt - offref); +} +static int64_t getoffset_untangle(size_t *offsets, caryll_bkblock *ref, caryll_bkblock *target) { + size_t offref = offsets[ref->_index]; + size_t offtgt = offsets[target->_index]; + return (int64_t)(offtgt - offref); +} +static void escalate_sppointers(caryll_bkblock *b, caryll_bkgraph *f, uint32_t *order, uint32_t depth) { + if (!b) return; + for (uint32_t j = 0; j < b->length; j++) { + bk_cell *cell = &(b->cells[j]); + if (bk_cell_is_ptr(cell) && cell->p && cell->t >= sp16) { escalate_sppointers(cell->p, f, order, depth); } + } + b->_depth = depth; + *order += 1; + f->entries[b->_index].order = *order; +} +static void dfs_attract_cells(caryll_bkblock *b, caryll_bkgraph *f, uint32_t *order, uint32_t depth) { + if (!b) return; + if (b->_visitstate != VISIT_WHITE) { + if (b->_depth < depth) { b->_depth = depth; } + return; + } + b->_visitstate = VISIT_GRAY; + for (uint32_t j = 0; j < b->length; j++) { + bk_cell *cell = &(b->cells[j]); + if (bk_cell_is_ptr(cell) && cell->p) { dfs_attract_cells(cell->p, f, order, depth + 1); } + if (bk_cell_is_ptr(cell) && cell->p) { dfs_attract_cells(cell->p, f, order, depth + 1); } + } + *order += 1; + f->entries[b->_index].order = *order; + escalate_sppointers(b, f, order, depth); + b->_visitstate = VISIT_BLACK; +} + +static void attract_bkgraph(caryll_bkgraph *f) { + // Clear the visit state of all blocks + for (uint32_t j = 0; j < f->length; j++) { + f->entries[j].block->_visitstate = VISIT_WHITE; + f->entries[j].order = 0; + f->entries[j].block->_index = j; + f->entries[j].block->_depth = 0; + } + uint32_t order = 0; + dfs_attract_cells(f->entries[0].block, f, &order, 0); + qsort(f->entries, f->length, sizeof(bkgraph_entry), _by_order); + for (uint32_t j = 0; j < f->length; j++) { + f->entries[j].block->_index = j; + } +} + +static bool try_untabgle_block(caryll_bkgraph *f, caryll_bkblock *b, size_t *offsets, uint16_t passes) { + bool didCopy = false; + for (uint32_t j = 0; j < b->length; j++) { + switch (b->cells[j].t) { + case p16: + case sp16: + if (b->cells[j].p) { + int64_t offset = getoffset_untangle(offsets, b, b->cells[j].p); + if (offset < 0 || offset > 0xFFFF) { + // Once we found a long link B ------------> C -> ..., we are going to this scanerio with + // a shallow copy of C, forming a B -> C' -> ... + fprintf(stderr, "[OTFCC-fea] Untangle : Did a shallow copy of block %d to avoid offset " + "overflow for %d in pass %d\n", + b->cells[j].p->_index, (int)offset, passes); + bkgraph_entry *e = _bkgraph_grow(f); + e->order = 0; + e->alias = 0; + e->block = new_bkblock(bkcopy, b->cells[j].p, bkover); + b->cells[j].t = sp16; + b->cells[j].p = e->block; + didCopy = true; + } + } + break; + default: + break; + } + } + return didCopy; +} + +static bool try_untangle(caryll_bkgraph *f, uint16_t passes) { + size_t *offsets = calloc(f->length + 1, sizeof(size_t)); + offsets[0] = 0; + for (uint32_t j = 0; j < f->length; j++) { + if (f->entries[j].block->_visitstate == VISIT_BLACK) { + offsets[j + 1] = offsets[j] + caryll_bkblock_size(f->entries[j].block); + } else { + offsets[j + 1] = offsets[j]; + } + } + uint32_t totalBlocks = f->length; + bool didUntangle = false; + for (uint32_t j = 0; j < totalBlocks; j++) { + if (f->entries[j].block->_visitstate == VISIT_BLACK) { + bool didCopy = try_untabgle_block(f, f->entries[j].block, offsets, passes); + didUntangle = didUntangle || didCopy; + } + } + free(offsets); + return didUntangle; +} + +static void caryll_write_bkblock(caryll_buffer *buf, caryll_bkblock *b, size_t *offsets) { + for (uint32_t j = 0; j < b->length; j++) { + switch (b->cells[j].t) { + case b8: + bufwrite8(buf, b->cells[j].z); + break; + case b16: + bufwrite16b(buf, b->cells[j].z); + break; + case b32: + bufwrite32b(buf, b->cells[j].z); + break; + case p16: + case sp16: + if (b->cells[j].p) { + bufwrite16b(buf, getoffset(offsets, b, b->cells[j].p, 16)); + } else { + bufwrite16b(buf, 0); + } + break; + case p32: + case sp32: + if (b->cells[j].p) { + bufwrite32b(buf, getoffset(offsets, b, b->cells[j].p, 32)); + } else { + bufwrite32b(buf, 0); + } + break; + default: + break; + } + } +} + caryll_buffer *caryll_write_bkgraph(caryll_bkgraph *f) { caryll_buffer *buf = bufnew(); size_t *offsets = calloc(f->length + 1, sizeof(size_t)); offsets[0] = 0; - size_t validEncs = 0; for (uint32_t j = 0; j < f->length; j++) { - if (f->entries[j].alias == j) { + if (f->entries[j].block->_visitstate == VISIT_BLACK) { offsets[j + 1] = offsets[j] + caryll_bkblock_size(f->entries[j].block); - validEncs += 1; } else { offsets[j + 1] = offsets[j]; } } - for (uint32_t j = 0; j < f->length; j++) - if (f->entries[j].alias == j) { caryll_write_bkblock(buf, f->entries[j].block, offsets); } + for (uint32_t j = 0; j < f->length; j++) { + if (f->entries[j].block->_visitstate == VISIT_BLACK) { + caryll_write_bkblock(buf, f->entries[j].block, offsets); + } + } + free(offsets); return buf; } +size_t estimate_bkgraph_size(caryll_bkgraph *f) { + size_t *offsets = calloc(f->length + 1, sizeof(size_t)); + offsets[0] = 0; + for (uint32_t j = 0; j < f->length; j++) { + if (f->entries[j].block->_visitstate == VISIT_BLACK) { + offsets[j + 1] = offsets[j] + caryll_bkblock_size(f->entries[j].block); + } else { + offsets[j + 1] = offsets[j]; + } + } + size_t estimatedSize = offsets[f->length]; + free(offsets); + return estimatedSize; +} + +void caryll_untangle_bkgraph(/*BORROW*/ caryll_bkgraph *f) { + uint16_t passes = 0; + bool tangled = false; + attract_bkgraph(f); + do { + tangled = try_untangle(f, passes); + if (tangled) { attract_bkgraph(f); } + passes++; + } while (tangled && passes < 16); +} + caryll_buffer *caryll_write_bk(/*MOVE*/ caryll_bkblock *root) { caryll_bkgraph *f = caryll_bkgraph_from_block(root); caryll_minimize_bkgraph(f); + caryll_untangle_bkgraph(f); caryll_buffer *buf = caryll_write_bkgraph(f); caryll_delete_bkgraph(f); return buf; diff --git a/lib/bk/bkgraph.h b/lib/bk/bkgraph.h index 5cc1019a..7156c734 100644 --- a/lib/bk/bkgraph.h +++ b/lib/bk/bkgraph.h @@ -18,15 +18,17 @@ typedef struct { } bkgraph_entry; typedef struct { - size_t length; - size_t free; + uint32_t length; + uint32_t free; bkgraph_entry *entries; } caryll_bkgraph; caryll_bkgraph *caryll_bkgraph_from_block(caryll_bkblock *b); void caryll_delete_bkgraph(/*MOVE*/ caryll_bkgraph *f); void caryll_minimize_bkgraph(/*BORROW*/ caryll_bkgraph *f); +void caryll_untangle_bkgraph(/*BORROW*/ caryll_bkgraph *f); caryll_buffer *caryll_write_bkgraph(/*BORROW*/ caryll_bkgraph *f); caryll_buffer *caryll_write_bk(/*MOVE*/ caryll_bkblock *root); +size_t estimate_bkgraph_size(caryll_bkgraph *f); #endif diff --git a/lib/font/caryll-font.c b/lib/font/caryll-font.c index 519eae5a..eb6338e6 100644 --- a/lib/font/caryll-font.c +++ b/lib/font/caryll-font.c @@ -28,6 +28,7 @@ caryll_font *caryll_new_font() { font->GSUB = NULL; font->GPOS = NULL; font->GDEF = NULL; + font->BASE = NULL; font->VORG = NULL; return font; } @@ -53,6 +54,7 @@ void caryll_delete_font(caryll_font *font) { if (font->GSUB) caryll_delete_otl(font->GSUB); if (font->GPOS) caryll_delete_otl(font->GPOS); if (font->GDEF) caryll_delete_GDEF(font->GDEF); + if (font->BASE) caryll_delete_BASE(font->BASE); if (font->VORG) caryll_delete_VORG(font->VORG); if (font->glyph_order && *font->glyph_order) { delete_glyph_order_map(font->glyph_order); } if (font) free(font); @@ -105,6 +107,7 @@ caryll_font *caryll_read_font(caryll_sfnt *sfnt, uint32_t index) { font->GPOS = caryll_read_otl(packet, 'GPOS'); font->GDEF = caryll_read_GDEF(packet); } + font->BASE = caryll_read_BASE(packet); return font; } } @@ -133,6 +136,7 @@ json_value *caryll_font_to_json(caryll_font *font, caryll_options *options) { caryll_otl_to_json(font->GSUB, root, options, "GSUB"); caryll_otl_to_json(font->GPOS, root, options, "GPOS"); caryll_GDEF_to_json(font->GDEF, root, options); + caryll_BASE_to_json(font->BASE, root, options); return root; } @@ -169,6 +173,7 @@ caryll_font *caryll_font_from_json(json_value *root, caryll_options *options) { font->GPOS = caryll_otl_from_json(root, options, "GPOS"); font->GDEF = caryll_GDEF_from_json(root, options); } + font->BASE = caryll_BASE_from_json(root, options); return font; } @@ -217,6 +222,7 @@ caryll_buffer *caryll_write_font(caryll_font *font, caryll_options *options) { if (font->GSUB) sfnt_builder_push_table(builder, 'GSUB', caryll_write_otl(font->GSUB, options, "GSUB")); if (font->GPOS) sfnt_builder_push_table(builder, 'GPOS', caryll_write_otl(font->GPOS, options, "GPOS")); if (font->GDEF) sfnt_builder_push_table(builder, 'GDEF', caryll_write_GDEF(font->GDEF, options)); + if (font->BASE) sfnt_builder_push_table(builder, 'BASE', caryll_write_BASE(font->BASE, options)); if (options->dummy_DSIG) { caryll_buffer *dsig = bufnew(); diff --git a/lib/font/caryll-font.h b/lib/font/caryll-font.h index cb4a2e74..033e187a 100644 --- a/lib/font/caryll-font.h +++ b/lib/font/caryll-font.h @@ -31,6 +31,7 @@ typedef struct _caryll_font caryll_font; #include #include +#include #include typedef enum { FONTTYPE_TTF, FONTTYPE_CFF } caryll_font_subtype; @@ -65,6 +66,7 @@ struct _caryll_font { table_otl *GSUB; table_otl *GPOS; table_GDEF *GDEF; + table_BASE *BASE; glyph_order_hash *glyph_order; }; diff --git a/lib/support/util.h b/lib/support/util.h index a0907ad1..8bfb3446 100644 --- a/lib/support/util.h +++ b/lib/support/util.h @@ -55,6 +55,13 @@ static INLINE sds json_obj_getsds(json_value *obj, const char *key) { else return sdsnewlen(v->u.string.ptr, v->u.string.length); } +static INLINE char *json_obj_getstr_share(json_value *obj, const char *key) { + json_value *v = json_obj_get_type(obj, key, json_string); + if (!v) + return NULL; + else + return v->u.string.ptr; +} static INLINE double json_numof(json_value *cv) { if (cv && cv->type == json_integer) return cv->u.integer; if (cv && cv->type == json_double) return cv->u.dbl; @@ -338,6 +345,32 @@ static INLINE json_value *preserialize(MOVE json_value *x) { #endif } +// Tag handler +static INLINE char *tag2str(uint32_t tag) { + char *tags = (char *)malloc(sizeof(char) * 5); + tags[0] = (tag >> 24) & 0xFF; + tags[1] = (tag >> 16) & 0xFF; + tags[2] = (tag >> 8) & 0xFF; + tags[3] = tag & 0xFF; + tags[4] = 0; + return tags; +} + +static INLINE uint32_t str2tag(char *tags) { + if (!tags) return 0; + uint32_t tag = 0; + uint8_t len = 0; + while (*tags && len < 4) { + tag = (tag << 8) | (*tags), tags++, len++; + } + while (len < 4) { + tag = (tag << 8) | ' ', len++; + } + return tag; +} + +// Allocators + static INLINE void *__caryll_allocate(size_t n, unsigned long line) { if (!n) return NULL; void *p = malloc(n); diff --git a/lib/tables/otl/BASE.c b/lib/tables/otl/BASE.c new file mode 100644 index 00000000..924f6e3b --- /dev/null +++ b/lib/tables/otl/BASE.c @@ -0,0 +1,310 @@ +#include "BASE.h" + +static void deleteBaseAxis(base_axis *axis) { + if (!axis) return; + if (axis->entries) { + for (uint16_t j = 0; j < axis->scriptCount; j++) { + if (axis->entries[j].baseValues) free(axis->entries[j].baseValues); + } + free(axis->entries); + } +} + +void caryll_delete_BASE(table_BASE *base) { + deleteBaseAxis(base->horizontal); + deleteBaseAxis(base->vertical); +} + +static int16_t readBaseValue(font_file_pointer data, uint32_t tableLength, uint16_t offset) { + checkLength(offset + 4); + return read_16s(data + offset + 2); +FAIL: + return 0; +} + +static void readBaseScript(font_file_pointer data, uint32_t tableLength, uint16_t offset, base_script_entry *entry, + uint32_t *baseTagList, uint16_t nBaseTags) { + entry->baseValuesCount = 0; + entry->baseValues = NULL; + entry->defaultBaselineTag = 0; + checkLength(offset + 2); // care about base values only now + uint16_t baseValuesOffset = read_16u(data + offset); + if (baseValuesOffset) { + baseValuesOffset += offset; + checkLength(baseValuesOffset + 4); + uint16_t defaultIndex = read_16u(data + baseValuesOffset) % nBaseTags; + entry->defaultBaselineTag = baseTagList[defaultIndex]; + entry->baseValuesCount = read_16u(data + baseValuesOffset + 2); + if (entry->baseValuesCount != nBaseTags) goto FAIL; + checkLength(baseValuesOffset + 4 + 2 * entry->baseValuesCount); + NEW_N(entry->baseValues, entry->baseValuesCount); + for (uint16_t j = 0; j < entry->baseValuesCount; j++) { + entry->baseValues[j].tag = baseTagList[j]; + uint16_t _valOffset = read_16u(data + baseValuesOffset + 4 + 2 * j); + if (_valOffset) { + entry->baseValues[j].coordinate = readBaseValue(data, tableLength, baseValuesOffset + _valOffset); + } else { + entry->baseValues[j].coordinate = 0; + } + } + return; + } +FAIL: + entry->baseValuesCount = 0; + if (entry->baseValues) free(entry->baseValues); + entry->baseValues = NULL; + entry->defaultBaselineTag = 0; + return; +} + +static base_axis *readAxis(font_file_pointer data, uint32_t tableLength, uint16_t offset) { + base_axis *axis = NULL; + uint32_t *baseTagList = NULL; + checkLength(offset + 4); + + // Read BaseTagList + uint16_t baseTagListOffset = offset + read_16u(data + offset); + if (baseTagListOffset <= offset) goto FAIL; + checkLength(baseTagListOffset + 2); + uint16_t nBaseTags = read_16u(data + baseTagListOffset); + if (!nBaseTags) goto FAIL; + checkLength(baseTagListOffset + 2 + 4 * nBaseTags); + NEW_N(baseTagList, nBaseTags); + for (uint16_t j = 0; j < nBaseTags; j++) { + baseTagList[j] = read_32u(data + baseTagListOffset + 2 + j * 4); + } + + uint16_t baseScriptListOffset = offset + read_16u(data + offset + 2); + if (baseScriptListOffset <= offset) goto FAIL; + checkLength(baseScriptListOffset + 2); + uint16_t nBaseScripts = read_16u(data + baseScriptListOffset); + checkLength(baseScriptListOffset + 2 + 6 * nBaseScripts); + NEW(axis); + axis->scriptCount = nBaseScripts; + NEW_N(axis->entries, nBaseScripts); + for (uint16_t j = 0; j < nBaseScripts; j++) { + axis->entries[j].tag = read_32u(data + baseScriptListOffset + 2 + 6 * j); + uint16_t baseScriptOffset = read_16u(data + baseScriptListOffset + 2 + 6 * j + 4); + if (baseScriptOffset) { + readBaseScript(data, tableLength, baseScriptListOffset + baseScriptOffset, &(axis->entries[j]), baseTagList, + nBaseTags); + } else { + axis->entries[j].baseValuesCount = 0; + axis->entries[j].baseValues = NULL; + axis->entries[j].defaultBaselineTag = 0; + } + } + return axis; + +FAIL: + if (baseTagList) FREE(baseTagList); + DELETE(deleteBaseAxis, axis); + return axis; +} + +table_BASE *caryll_read_BASE(caryll_packet packet) { + table_BASE *base = NULL; + FOR_TABLE('BASE', table) { + font_file_pointer data = table.data; + uint32_t tableLength = table.length; + checkLength(8); + NEW_CLEAN(base); + uint16_t offsetH = read_16u(data + 4); + if (offsetH) base->horizontal = readAxis(data, tableLength, offsetH); + uint16_t offsetV = read_16u(data + 6); + if (offsetV) base->vertical = readAxis(data, tableLength, offsetV); + return base; + FAIL: + DELETE(caryll_delete_BASE, base); + } + return base; +} + +static json_value *axisToJson(base_axis *axis) { + json_value *_axis = json_object_new(axis->scriptCount); + for (uint16_t j = 0; j < axis->scriptCount; j++) { + if (!axis->entries[j].tag) continue; + json_value *_entry = json_object_new(3); + if (axis->entries[j].defaultBaselineTag) { + json_object_push(_entry, "defaultBaseline", + json_string_new_nocopy(4, tag2str(axis->entries[j].defaultBaselineTag))); + } + json_value *_values = json_object_new(axis->entries[j].baseValuesCount); + for (uint16_t k = 0; k < axis->entries[j].baseValuesCount; k++) { + if (axis->entries[j].baseValues[k].tag) + json_object_push(_values, tag2str(axis->entries[j].baseValues[k].tag), + json_integer_new(axis->entries[j].baseValues[k].coordinate)); + } + json_object_push(_entry, "baselines", _values); + json_object_push(_axis, tag2str(axis->entries[j].tag), _entry); + } + return _axis; +} + +void caryll_BASE_to_json(table_BASE *base, json_value *root, const caryll_options *options) { + if (!base) return; + if (options->verbose) fprintf(stderr, "Dumping BASE.\n"); + json_value *_base = json_object_new(2); + if (base->horizontal) json_object_push(_base, "horizontal", axisToJson(base->horizontal)); + if (base->vertical) json_object_push(_base, "vertical", axisToJson(base->vertical)); + json_object_push(root, "BASE", _base); +} + +static void baseScriptFromJson(json_value *_sr, base_script_entry *entry) { + entry->defaultBaselineTag = str2tag(json_obj_getstr_share(_sr, "defaultBaseline")); + json_value *_basevalues = json_obj_get_type(_sr, "baselines", json_object); + if (!_basevalues) { + entry->baseValuesCount = 0; + entry->baseValues = NULL; + } else { + entry->baseValuesCount = _basevalues->u.object.length; + NEW_N(entry->baseValues, entry->baseValuesCount); + for (uint16_t j = 0; j < entry->baseValuesCount; j++) { + entry->baseValues[j].tag = str2tag(_basevalues->u.object.values[j].name); + entry->baseValues[j].coordinate = json_numof(_basevalues->u.object.values[j].value); + } + } +} + +static base_axis *axisFromJson(json_value *_axis) { + if (!_axis) return NULL; + base_axis *axis; + NEW(axis); + axis->scriptCount = _axis->u.object.length; + NEW_N(axis->entries, axis->scriptCount); + uint16_t jj = 0; + for (uint16_t j = 0; j < axis->scriptCount; j++) { + if (_axis->u.object.values[j].value && _axis->u.object.values[j].value->type == json_object) { + axis->entries[jj].tag = str2tag(_axis->u.object.values[j].name); + baseScriptFromJson(_axis->u.object.values[j].value, &(axis->entries[jj])); + jj++; + } + } + axis->scriptCount = jj; + return axis; +} + +table_BASE *caryll_BASE_from_json(json_value *root, const caryll_options *options) { + table_BASE *base = NULL; + json_value *table = NULL; + if ((table = json_obj_get_type(root, "BASE", json_object))) { + if (options->verbose) fprintf(stderr, "Parsing BASE.\n"); + NEW(base); + base->horizontal = axisFromJson(json_obj_get_type(table, "horizontal", json_object)); + base->vertical = axisFromJson(json_obj_get_type(table, "vertical", json_object)); + } + return base; +} + +caryll_bkblock *axisToBk(base_axis *axis) { + if (!axis) return NULL; + struct { + uint16_t size; + uint32_t *items; + } taglist; + taglist.size = 0; + taglist.items = NULL; + + for (uint16_t j = 0; j < axis->scriptCount; j++) { + base_script_entry *entry = &(axis->entries[j]); + if (entry->defaultBaselineTag) { + bool found = false; + for (uint16_t jk = 0; jk < taglist.size; jk++) { + if (taglist.items[jk] == entry->defaultBaselineTag) { + found = true; + break; + } + } + if (!found) { + taglist.size += 1; + if (taglist.items) + taglist.items = realloc(taglist.items, taglist.size * sizeof(uint32_t)); + else + taglist.items = malloc(taglist.size * sizeof(uint32_t)); + taglist.items[taglist.size - 1] = entry->defaultBaselineTag; + } + } + for (uint16_t k = 0; k < entry->baseValuesCount; k++) { + uint32_t tag = entry->baseValues[k].tag; + bool found = false; + for (uint16_t jk = 0; jk < taglist.size; jk++) { + if (taglist.items[jk] == tag) { + found = true; + break; + } + } + if (!found) { + taglist.size += 1; + if (taglist.items) + taglist.items = realloc(taglist.items, taglist.size * sizeof(uint32_t)); + else + taglist.items = malloc(taglist.size * sizeof(uint32_t)); + taglist.items[taglist.size - 1] = tag; + } + } + } + + caryll_bkblock *baseTagList = new_bkblock(b16, taglist.size, bkover); + for (uint16_t j = 0; j < taglist.size; j++) { + bkblock_push(baseTagList, b32, taglist.items[j], bkover); + } + + caryll_bkblock *baseScriptList = new_bkblock(b16, axis->scriptCount, bkover); + for (uint16_t j = 0; j < axis->scriptCount; j++) { + base_script_entry *entry = &(axis->entries[j]); + caryll_bkblock *baseValues = new_bkblock(bkover); + { + uint16_t defaultIndex = 0; + for (uint16_t m = 0; m < taglist.size; m++) { + if (taglist.items[m] == entry->defaultBaselineTag) { + defaultIndex = m; + break; + } + } + bkblock_push(baseValues, b16, defaultIndex, bkover); + } + bkblock_push(baseValues, b16, taglist.size, bkover); + for (uint16_t m = 0; m < taglist.size; m++) { + uint16_t found = false; + uint16_t foundIndex = 0; + for (uint16_t k = 0; k < entry->baseValuesCount; k++) { + if (entry->baseValues[k].tag == taglist.items[m]) { + found = true, foundIndex = k; + break; + } + } + if (found) { + bkblock_push(baseValues, // base value + p16, new_bkblock(b16, 1, // format + b16, entry->baseValues[foundIndex].coordinate, // coordinate + bkover), + bkover); + } else { + bkblock_push(baseValues, // assign a zero value + p16, new_bkblock(b16, 1, // format + b16, 0, // coordinate + bkover), + bkover); + } + } + caryll_bkblock *scriptRecord = new_bkblock(p16, baseValues, // BaseValues + p16, NULL, // DefaultMinMax + b16, 0, // BaseLangSysCount + bkover); + bkblock_push(baseScriptList, b32, entry->tag, // BaseScriptTag + p16, scriptRecord, // BaseScript + bkover); + } + free(taglist.items); + return new_bkblock(p16, baseTagList, // BaseTagList + p16, baseScriptList, // BaseScriptList + bkover); +} + +caryll_buffer *caryll_write_BASE(table_BASE *base, const caryll_options *options) { + caryll_bkblock *root = new_bkblock(b32, 0x10000, // Version + p16, axisToBk(base->horizontal), // HorizAxis + p16, axisToBk(base->vertical), // VertAxis + bkover); + return caryll_write_bk(root); +} diff --git a/lib/tables/otl/BASE.h b/lib/tables/otl/BASE.h new file mode 100644 index 00000000..8b794756 --- /dev/null +++ b/lib/tables/otl/BASE.h @@ -0,0 +1,34 @@ +#ifndef CARYLL_TABLES_OTL_BASE_H +#define CARYLL_TABLES_OTL_BASE_H + +#include "otl.h" + +typedef struct { + uint32_t tag; + int16_t coordinate; +} base_value; + +typedef struct { + uint32_t tag; + uint32_t defaultBaselineTag; + uint16_t baseValuesCount; + base_value *baseValues; +} base_script_entry; + +typedef struct { + uint16_t scriptCount; + base_script_entry *entries; +} base_axis; + +typedef struct { + base_axis *horizontal; + base_axis *vertical; +} table_BASE; + +void caryll_delete_BASE(table_BASE *base); +table_BASE *caryll_read_BASE(caryll_packet packet); +void caryll_BASE_to_json(table_BASE *base, json_value *root, const caryll_options *options); +table_BASE *caryll_BASE_from_json(json_value *root, const caryll_options *options); +caryll_buffer *caryll_write_BASE(table_BASE *base, const caryll_options *options); + +#endif diff --git a/lib/tables/otl/gpos-pair.c b/lib/tables/otl/gpos-pair.c index 7ed9e6c9..164dea68 100644 --- a/lib/tables/otl/gpos-pair.c +++ b/lib/tables/otl/gpos-pair.c @@ -263,7 +263,7 @@ otl_subtable *caryll_gpos_pair_from_json(json_value *_subtable) { return NULL; } -caryll_buffer *caryll_write_gpos_pair_individual(otl_subtable *_subtable) { +caryll_bkblock *caryll_write_gpos_pair_individual(otl_subtable *_subtable) { subtable_gpos_pair *subtable = &(_subtable->gpos_pair); uint16_t format1 = 0; uint16_t format2 = 0; @@ -305,17 +305,17 @@ caryll_buffer *caryll_write_gpos_pair_individual(otl_subtable *_subtable) { uint16_t c2 = subtable->second->classes[k]; if (required_position_format(subtable->firstValues[c1][c2]) | required_position_format(subtable->secondValues[c1][c2])) { - bkblock_push(pairSet, b16, subtable->second->glyphs[k].index, // SecondGlyph - bembed, bk_gpos_value(subtable->firstValues[c1][c2], format1), // Value1 - bembed, bk_gpos_value(subtable->secondValues[c1][c2], format2), // Value2 + bkblock_push(pairSet, b16, subtable->second->glyphs[k].index, // SecondGlyph + bkembed, bk_gpos_value(subtable->firstValues[c1][c2], format1), // Value1 + bkembed, bk_gpos_value(subtable->secondValues[c1][c2], format2), // Value2 bkover); } } bkblock_push(root, p16, pairSet, bkover); } - return caryll_write_bk(root); + return root; } -caryll_buffer *caryll_write_gpos_pair_classes(otl_subtable *_subtable) { +caryll_bkblock *caryll_write_gpos_pair_classes(otl_subtable *_subtable) { subtable_gpos_pair *subtable = &(_subtable->gpos_pair); uint16_t format1 = 0; uint16_t format2 = 0; @@ -340,21 +340,33 @@ caryll_buffer *caryll_write_gpos_pair_classes(otl_subtable *_subtable) { bkover); for (uint16_t j = 0; j < class1Count; j++) { for (uint16_t k = 0; k < class2Count; k++) { - bkblock_push(root, bembed, bk_gpos_value(subtable->firstValues[j][k], format1), // Value1 - bembed, bk_gpos_value(subtable->secondValues[j][k], format2), // Value2 + bkblock_push(root, bkembed, bk_gpos_value(subtable->firstValues[j][k], format1), // Value1 + bkembed, bk_gpos_value(subtable->secondValues[j][k], format2), // Value2 bkover); } } - return caryll_write_bk(root); + return root; } caryll_buffer *caryll_write_gpos_pair(otl_subtable *_subtable) { - caryll_buffer *format1 = caryll_write_gpos_pair_individual(_subtable); - caryll_buffer *format2 = caryll_write_gpos_pair_classes(_subtable); - if (buflen(format1) < buflen(format2)) { - buffree(format2); - return format1; + caryll_bkblock *format1 = caryll_write_gpos_pair_individual(_subtable); + caryll_bkblock *format2 = caryll_write_gpos_pair_classes(_subtable); + caryll_bkgraph *g1 = caryll_bkgraph_from_block(format1); + caryll_bkgraph *g2 = caryll_bkgraph_from_block(format2); + caryll_minimize_bkgraph(g1); + caryll_minimize_bkgraph(g2); + if (estimate_bkgraph_size(g1) > estimate_bkgraph_size(g2)) { + // Choose pair adjustment by classes + caryll_delete_bkgraph(g1); + caryll_untangle_bkgraph(g2); + caryll_buffer *buf = caryll_write_bkgraph(g2); + caryll_delete_bkgraph(g2); + return buf; } else { - buffree(format1); - return format2; + // Choose pair adjustment by individuals + caryll_delete_bkgraph(g2); + caryll_untangle_bkgraph(g1); + caryll_buffer *buf = caryll_write_bkgraph(g1); + caryll_delete_bkgraph(g1); + return buf; } } diff --git a/lib/tables/otl/gpos-single.c b/lib/tables/otl/gpos-single.c index f1245f51..e5d9ef76 100644 --- a/lib/tables/otl/gpos-single.c +++ b/lib/tables/otl/gpos-single.c @@ -98,7 +98,7 @@ caryll_buffer *caryll_write_gpos_single(otl_subtable *_subtable) { new_bkblock(b16, 1, // Format p16, new_bkblock_from_buffer(caryll_write_coverage(subtable->coverage)), // coverage b16, format, // format - bembed, bk_gpos_value(subtable->values[0], format), // value + bkembed, bk_gpos_value(subtable->values[0], format), // value bkover)); } else { caryll_bkblock *b = @@ -108,7 +108,7 @@ caryll_buffer *caryll_write_gpos_single(otl_subtable *_subtable) { b16, subtable->coverage->numGlyphs, // quantity bkover); for (uint16_t k = 0; k < subtable->coverage->numGlyphs; k++) { - bkblock_push(b, bembed, bk_gpos_value(subtable->values[k], format), // value + bkblock_push(b, bkembed, bk_gpos_value(subtable->values[k], format), // value bkover); } return caryll_write_bk(b); diff --git a/lib/tables/otl/otl.c b/lib/tables/otl/otl.c index 9007fb92..b58912a8 100644 --- a/lib/tables/otl/otl.c +++ b/lib/tables/otl/otl.c @@ -740,6 +740,7 @@ static caryll_bkblock *writeOTLLookups(table_otl *table, const caryll_options *o NEW_N(lookupWritten, table->lookupCount); size_t lastOffset = 0; for (uint16_t j = 0; j < table->lookupCount; j++) { + if (options->verbose) { fprintf(stderr, " Writing lookup %s\n", table->lookups[j]->name); } subtables[j] = NULL; lookupWritten[j] = _write_subtable(table->lookups[j], &(subtables[j]), &lastOffset); } diff --git a/premake5.lua b/premake5.lua index c610175d..46559187 100644 --- a/premake5.lua +++ b/premake5.lua @@ -40,8 +40,8 @@ workspace "otfcc" defines { '_CARYLL_USE_PRE_SERIALIZED', 'MAIN_VER=0', - "SECONDARY_VER=1", - "PATCH_VER=0" + "SECONDARY_VER=3", + "PATCH_VER=2" } location "build"