summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/mfluadir/otfcc/lib/table/otl/subtables/gpos-pair.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/web2c/mfluadir/otfcc/lib/table/otl/subtables/gpos-pair.c')
-rw-r--r--Build/source/texk/web2c/mfluadir/otfcc/lib/table/otl/subtables/gpos-pair.c406
1 files changed, 406 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/mfluadir/otfcc/lib/table/otl/subtables/gpos-pair.c b/Build/source/texk/web2c/mfluadir/otfcc/lib/table/otl/subtables/gpos-pair.c
new file mode 100644
index 00000000000..87a113096d2
--- /dev/null
+++ b/Build/source/texk/web2c/mfluadir/otfcc/lib/table/otl/subtables/gpos-pair.c
@@ -0,0 +1,406 @@
+#include "gpos-pair.h"
+#include "gpos-common.h"
+
+static INLINE void initGposPair(subtable_gpos_pair *subtable) {
+ subtable->first = NULL;
+ subtable->second = NULL;
+ subtable->firstValues = NULL;
+ subtable->secondValues = NULL;
+}
+static INLINE void disposeGposPair(subtable_gpos_pair *subtable) {
+ if (subtable->firstValues) {
+ for (glyphclass_t j = 0; j <= subtable->first->maxclass; j++) {
+ FREE(subtable->firstValues[j]);
+ }
+ FREE(subtable->firstValues);
+ }
+ if (subtable->secondValues) {
+ for (glyphclass_t j = 0; j <= subtable->first->maxclass; j++) {
+ FREE(subtable->secondValues[j]);
+ }
+ FREE(subtable->secondValues);
+ }
+ DELETE(ClassDef.free, subtable->first);
+ DELETE(ClassDef.free, subtable->second);
+}
+
+caryll_standardRefType(subtable_gpos_pair, iSubtable_gpos_pair, initGposPair, disposeGposPair);
+
+typedef struct {
+ int gid;
+ int cid;
+ UT_hash_handle hh;
+} pair_classifier_hash;
+
+otl_Subtable *otl_read_gpos_pair(const font_file_pointer data, uint32_t tableLength,
+ uint32_t offset, const glyphid_t maxGlyphs,
+ const otfcc_Options *options) {
+ subtable_gpos_pair *subtable = iSubtable_gpos_pair.create();
+
+ checkLength(offset + 2);
+ uint16_t subtableFormat = read_16u(data + offset);
+ if (subtableFormat == 1) {
+ // pair adjustment by individuals
+ otl_Coverage *cov = Coverage.read(data, tableLength, offset + read_16u(data + offset + 2));
+ NEW(subtable->first);
+ subtable->first->numGlyphs = cov->numGlyphs;
+ subtable->first->maxclass = cov->numGlyphs - 1;
+ subtable->first->glyphs = cov->glyphs;
+ NEW(subtable->first->classes, cov->numGlyphs);
+ for (glyphid_t j = 0; j < cov->numGlyphs; j++) {
+ subtable->first->classes[j] = j;
+ }
+ FREE(cov);
+
+ uint16_t format1 = read_16u(data + offset + 4);
+ uint16_t format2 = read_16u(data + offset + 6);
+ uint8_t len1 = position_format_length(format1);
+ uint8_t len2 = position_format_length(format2);
+
+ glyphid_t pairSetCount = read_16u(data + offset + 8);
+ if (pairSetCount != subtable->first->numGlyphs) goto FAIL;
+ checkLength(offset + 10 + 2 * pairSetCount);
+
+ // pass 1: check length
+ for (glyphid_t j = 0; j < pairSetCount; j++) {
+ uint32_t pairSetOffset = offset + read_16u(data + offset + 10 + 2 * j);
+ checkLength(pairSetOffset + 2);
+ glyphid_t pairCount = read_16u(data + pairSetOffset);
+ checkLength(pairSetOffset + 2 + (2 + len1 + len2) * pairCount);
+ }
+ // pass 2: make hashtable about second glyphs to construct a classdec
+ pair_classifier_hash *h = NULL;
+ for (glyphid_t j = 0; j < pairSetCount; j++) {
+ uint32_t pairSetOffset = offset + read_16u(data + offset + 10 + 2 * j);
+ glyphid_t pairCount = read_16u(data + pairSetOffset);
+ for (glyphid_t k = 0; k < pairCount; k++) {
+ int second = read_16u(data + pairSetOffset + 2 + (2 + len1 + len2) * k);
+ pair_classifier_hash *s;
+ HASH_FIND_INT(h, &second, s);
+ if (!s) {
+ NEW(s);
+ s->gid = second;
+ s->cid = HASH_COUNT(h) + 1;
+ HASH_ADD_INT(h, gid, s);
+ }
+ }
+ }
+ // pass 3: make the matrix
+ NEW(subtable->second);
+ subtable->second->numGlyphs = HASH_COUNT(h);
+ subtable->second->maxclass = HASH_COUNT(h);
+ NEW(subtable->second->classes, subtable->second->numGlyphs);
+ NEW(subtable->second->glyphs, subtable->second->numGlyphs);
+ glyphclass_t class2Count = subtable->second->maxclass + 1;
+
+ NEW(subtable->firstValues, subtable->first->maxclass + 1);
+ NEW(subtable->secondValues, subtable->first->maxclass + 1);
+ for (glyphclass_t j = 0; j <= subtable->first->maxclass; j++) {
+ NEW(subtable->firstValues[j], class2Count);
+ NEW(subtable->secondValues[j], class2Count);
+ for (glyphclass_t k = 0; k < class2Count; k++) {
+ subtable->firstValues[j][k] = position_zero();
+ subtable->secondValues[j][k] = position_zero();
+ }
+ };
+ for (glyphclass_t j = 0; j <= subtable->first->maxclass; j++) {
+ uint32_t pairSetOffset = offset + read_16u(data + offset + 10 + 2 * j);
+ glyphid_t pairCount = read_16u(data + pairSetOffset);
+ for (glyphid_t k = 0; k < pairCount; k++) {
+ int second = read_16u(data + pairSetOffset + 2 + (2 + len1 + len2) * k);
+ pair_classifier_hash *s;
+ HASH_FIND_INT(h, &second, s);
+ if (s) {
+ subtable->firstValues[j][s->cid] = read_gpos_value(
+ data, tableLength, pairSetOffset + 2 + (2 + len1 + len2) * k + 2, format1);
+ subtable->secondValues[j][s->cid] = read_gpos_value(
+ data, tableLength, pairSetOffset + 2 + (2 + len1 + len2) * k + 2 + len1,
+ format2);
+ }
+ }
+ }
+ // pass 4: return
+ pair_classifier_hash *s, *tmp;
+ glyphid_t jj = 0;
+ HASH_ITER(hh, h, s, tmp) {
+ subtable->second->glyphs[jj] = Handle.fromIndex(s->gid);
+ subtable->second->classes[jj] = s->cid;
+ jj++;
+ HASH_DEL(h, s);
+ FREE(s);
+ }
+ return (otl_Subtable *)subtable;
+ } else if (subtableFormat == 2) {
+ // pair adjustment by classes
+ checkLength(offset + 16);
+ uint16_t format1 = read_16u(data + offset + 4);
+ uint16_t format2 = read_16u(data + offset + 6);
+ uint8_t len1 = position_format_length(format1);
+ uint8_t len2 = position_format_length(format2);
+ otl_Coverage *cov = Coverage.read(data, tableLength, offset + read_16u(data + offset + 2));
+ subtable->first = ClassDef.read(data, tableLength, offset + read_16u(data + offset + 8));
+ subtable->first = ClassDef.expand(cov, subtable->first);
+ DELETE(Coverage.free, cov);
+ subtable->second = ClassDef.read(data, tableLength, offset + read_16u(data + offset + 10));
+ if (!subtable->first || !subtable->second) goto FAIL;
+ glyphclass_t class1Count = read_16u(data + offset + 12);
+ glyphclass_t class2Count = read_16u(data + offset + 14);
+ if (subtable->first->maxclass + 1 != class1Count) goto FAIL;
+ if (subtable->second->maxclass + 1 != class2Count) goto FAIL;
+
+ checkLength(offset + 16 + class1Count * class2Count * (len1 + len2));
+ // read the matrix
+ NEW(subtable->firstValues, class1Count);
+ NEW(subtable->secondValues, class1Count);
+
+ for (glyphclass_t j = 0; j < class1Count; j++) {
+ NEW(subtable->firstValues[j], class2Count);
+ NEW(subtable->secondValues[j], class2Count);
+ for (glyphclass_t k = 0; k < class2Count; k++) {
+ subtable->firstValues[j][k] =
+ read_gpos_value(data, tableLength,
+ offset + 16 + (j * class2Count + k) * (len1 + len2), format1);
+ subtable->secondValues[j][k] = read_gpos_value(
+ data, tableLength, offset + 16 + (j * class2Count + k) * (len1 + len2) + len1,
+ format2);
+ }
+ }
+
+ return (otl_Subtable *)subtable;
+ }
+FAIL:
+ iSubtable_gpos_pair.free(subtable);
+ return NULL;
+}
+
+json_value *otl_gpos_dump_pair(const otl_Subtable *_subtable) {
+ const subtable_gpos_pair *subtable = &(_subtable->gpos_pair);
+ json_value *st = json_object_new(3);
+ json_object_push(st, "first", ClassDef.dump(subtable->first));
+ json_object_push(st, "second", ClassDef.dump(subtable->second));
+
+ json_value *mat = json_array_new(subtable->first->maxclass + 1);
+ for (glyphclass_t j = 0; j <= subtable->first->maxclass; j++) {
+ json_value *row = json_array_new(subtable->second->maxclass + 1);
+ for (glyphclass_t k = 0; k <= subtable->second->maxclass; k++) {
+ uint8_t f1 = required_position_format(subtable->firstValues[j][k]);
+ uint8_t f2 = required_position_format(subtable->secondValues[j][k]);
+ if (f1 | f2) {
+ if (f1 == FORMAT_DWIDTH && f2 == 0) {
+ // simple kerning
+ json_array_push(row, json_new_position(subtable->firstValues[j][k].dWidth));
+ } else {
+ json_value *pair = json_object_new(2);
+ if (f1) {
+ json_object_push(pair, "first",
+ gpos_dump_value(subtable->firstValues[j][k]));
+ }
+ if (f2) {
+ json_object_push(pair, "second",
+ gpos_dump_value(subtable->secondValues[j][k]));
+ }
+ json_array_push(row, pair);
+ }
+ } else {
+ json_array_push(row, json_new_position(0));
+ }
+ }
+ json_array_push(mat, preserialize(row));
+ }
+ json_object_push(st, "matrix", mat);
+ return st;
+}
+otl_Subtable *otl_gpos_parse_pair(const json_value *_subtable, const otfcc_Options *options) {
+ subtable_gpos_pair *subtable = iSubtable_gpos_pair.create();
+
+ json_value *_mat = json_obj_get_type(_subtable, "matrix", json_array);
+ subtable->first = ClassDef.parse(json_obj_get_type(_subtable, "first", json_object));
+ subtable->second = ClassDef.parse(json_obj_get_type(_subtable, "second", json_object));
+ if (!_mat || !subtable->first || !subtable->second) goto FAIL;
+
+ glyphclass_t class1Count = subtable->first->maxclass + 1;
+ glyphclass_t class2Count = subtable->second->maxclass + 1;
+
+ NEW(subtable->firstValues, class1Count);
+ NEW(subtable->secondValues, class1Count);
+
+ for (glyphclass_t j = 0; j < class1Count; j++) {
+ NEW(subtable->firstValues[j], class2Count);
+ NEW(subtable->secondValues[j], class2Count);
+ for (glyphclass_t k = 0; k < class2Count; k++) {
+ subtable->firstValues[j][k] = position_zero();
+ subtable->secondValues[j][k] = position_zero();
+ }
+ }
+ for (glyphclass_t j = 0; j < class1Count && j < _mat->u.array.length; j++) {
+ json_value *_row = _mat->u.array.values[j];
+ if (!_row || _row->type != json_array) continue;
+ for (glyphclass_t k = 0; k < class2Count && k < _row->u.array.length; k++) {
+ json_value *_item = _row->u.array.values[k];
+ if (_item->type == json_integer) {
+ subtable->firstValues[j][k].dWidth = _item->u.integer;
+ } else if (_item->type == json_double) {
+ subtable->firstValues[j][k].dWidth = _item->u.dbl;
+ } else if (_item->type == json_object) {
+ subtable->firstValues[j][k] = gpos_parse_value(json_obj_get(_item, "first"));
+ subtable->secondValues[j][k] = gpos_parse_value(json_obj_get(_item, "second"));
+ }
+ }
+ }
+ return (otl_Subtable *)subtable;
+FAIL:
+ iSubtable_gpos_pair.free(subtable);
+ return NULL;
+}
+static otl_Coverage *covFromCD(otl_ClassDef *cd) {
+ otl_Coverage *cov;
+ NEW(cov);
+ cov->numGlyphs = cd->numGlyphs;
+ NEW(cov->glyphs, cd->numGlyphs);
+ for (glyphid_t j = 0; j < cd->numGlyphs; j++) {
+ cov->glyphs[j] = Handle.dup(cd->glyphs[j]);
+ }
+ return cov;
+}
+
+typedef struct {
+ glyphid_t gid;
+ otl_PositionValue *fv;
+ otl_PositionValue *sv;
+} IndividualGposPair;
+
+static int by_pairSecondGlyph(const void *a, const void *b) {
+ return ((const IndividualGposPair *)a)->gid - ((const IndividualGposPair *)b)->gid;
+}
+
+static bk_Block *otfcc_build_gpos_pair_individual(const otl_Subtable *_subtable) {
+ const subtable_gpos_pair *subtable = &(_subtable->gpos_pair);
+ uint16_t format1 = 0;
+ uint16_t format2 = 0;
+ glyphclass_t class1Count = subtable->first->maxclass + 1;
+ glyphclass_t class2Count = subtable->second->maxclass + 1;
+
+ for (glyphclass_t j = 0; j < class1Count; j++) {
+ for (glyphclass_t k = 0; k < class2Count; k++) {
+ format1 |= required_position_format(subtable->firstValues[j][k]);
+ format2 |= required_position_format(subtable->secondValues[j][k]);
+ }
+ }
+ glyphid_t *pairCounts;
+ NEW(pairCounts, subtable->first->numGlyphs);
+ for (glyphid_t j = 0; j < subtable->first->numGlyphs; j++) {
+ pairCounts[j] = 0;
+ for (glyphid_t k = 0; k < subtable->second->numGlyphs; k++) {
+ glyphclass_t c1 = subtable->first->classes[j];
+ glyphclass_t c2 = subtable->second->classes[k];
+ if (required_position_format(subtable->firstValues[c1][c2]) |
+ required_position_format(subtable->secondValues[c1][c2])) {
+ pairCounts[j] += 1;
+ }
+ }
+ }
+ otl_Coverage *cov = covFromCD(subtable->first);
+ Coverage.shrink(cov, true);
+ bk_Block *root = bk_new_Block(b16, 1, // PosFormat
+ p16, bk_newBlockFromBuffer(Coverage.build(cov)), // Coverage
+ b16, format1, // ValueFormat1
+ b16, format2, // ValueFormat2
+ b16, subtable->first->numGlyphs, // PairSetCount
+ bkover);
+
+ for (glyphid_t j = 0; j < cov->numGlyphs; j++) {
+ tableid_t currentPairCount = 0;
+ glyphclass_t c1 = 0;
+ for (glyphid_t k = 0; k < subtable->first->numGlyphs; k++) {
+ if (subtable->first->glyphs[k].index == cov->glyphs[j].index) {
+ // The coverage is sorted, not direct correspondence with subtable->first.
+ c1 = subtable->first->classes[k];
+ currentPairCount = pairCounts[k];
+ }
+ }
+ bk_Block *pairSet = bk_new_Block(b16, currentPairCount, // PairValueCount
+ bkover);
+ IndividualGposPair *pairs;
+ NEW_CLEAN_N(pairs, currentPairCount);
+ size_t n = 0;
+ for (glyphid_t k = 0; k < subtable->second->numGlyphs; k++) {
+ glyphclass_t c2 = subtable->second->classes[k];
+ if (required_position_format(subtable->firstValues[c1][c2]) |
+ required_position_format(subtable->secondValues[c1][c2])) {
+ pairs[n].gid = subtable->second->glyphs[k].index;
+ pairs[n].fv = &subtable->firstValues[c1][c2];
+ pairs[n].sv = &subtable->secondValues[c1][c2];
+ n++;
+ }
+ }
+ qsort(pairs, currentPairCount, sizeof(IndividualGposPair), by_pairSecondGlyph);
+ for (size_t n = 0; n < currentPairCount; n++) {
+ bk_push(pairSet, b16, pairs[n].gid, // SecondGlyph
+ bkembed, bk_gpos_value(*(pairs[n].fv), format1), // Value1
+ bkembed, bk_gpos_value(*(pairs[n].sv), format2), // Value2
+ bkover);
+ }
+ FREE(pairs);
+ bk_push(root, p16, pairSet, bkover);
+ }
+ DELETE(Coverage.free, cov);
+ FREE(pairCounts);
+ return root;
+}
+static bk_Block *otfcc_build_gpos_pair_classes(const otl_Subtable *_subtable) {
+ const subtable_gpos_pair *subtable = &(_subtable->gpos_pair);
+ uint16_t format1 = 0;
+ uint16_t format2 = 0;
+ glyphclass_t class1Count = subtable->first->maxclass + 1;
+ glyphclass_t class2Count = subtable->second->maxclass + 1;
+
+ for (glyphclass_t j = 0; j < class1Count; j++) {
+ for (glyphclass_t k = 0; k < class2Count; k++) {
+ format1 |= required_position_format(subtable->firstValues[j][k]);
+ format2 |= required_position_format(subtable->secondValues[j][k]);
+ }
+ }
+ otl_Coverage *cov = covFromCD(subtable->first);
+ bk_Block *root =
+ bk_new_Block(b16, 2, // PosFormat
+ p16, bk_newBlockFromBuffer(Coverage.build(cov)), // Coverage
+ b16, format1, // ValueFormat1
+ b16, format2, // ValueFormat2
+ p16, bk_newBlockFromBuffer(ClassDef.build(subtable->first)), // ClassDef1
+ p16, bk_newBlockFromBuffer(ClassDef.build(subtable->second)), // ClassDef2
+ b16, class1Count, // Class1Count
+ b16, class2Count, // Class2Count
+ bkover);
+ for (glyphclass_t j = 0; j < class1Count; j++) {
+ for (glyphclass_t k = 0; k < class2Count; k++) {
+ bk_push(root, bkembed, bk_gpos_value(subtable->firstValues[j][k], format1), // Value1
+ bkembed, bk_gpos_value(subtable->secondValues[j][k], format2), // Value2
+ bkover);
+ }
+ }
+ DELETE(Coverage.free, cov);
+ return root;
+}
+caryll_Buffer *otfcc_build_gpos_pair(const otl_Subtable *_subtable, otl_BuildHeuristics heuristics) {
+ bk_Block *format1 = otfcc_build_gpos_pair_individual(_subtable);
+ bk_Block *format2 = otfcc_build_gpos_pair_classes(_subtable);
+ bk_Graph *g1 = bk_newGraphFromRootBlock(format1);
+ bk_Graph *g2 = bk_newGraphFromRootBlock(format2);
+ bk_minimizeGraph(g1);
+ bk_minimizeGraph(g2);
+ if (bk_estimateSizeOfGraph(g1) > bk_estimateSizeOfGraph(g2)) {
+ // Choose pair adjustment by classes
+ bk_delete_Graph(g1);
+ bk_untangleGraph(g2);
+ caryll_Buffer *buf = bk_build_Graph(g2);
+ bk_delete_Graph(g2);
+ return buf;
+ } else {
+ // Choose pair adjustment by individuals
+ bk_delete_Graph(g2);
+ bk_untangleGraph(g1);
+ caryll_Buffer *buf = bk_build_Graph(g1);
+ bk_delete_Graph(g1);
+ return buf;
+ }
+}