summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/mfluadir/otfcc/lib/table/otl/build.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/web2c/mfluadir/otfcc/lib/table/otl/build.c')
-rw-r--r--Build/source/texk/web2c/mfluadir/otfcc/lib/table/otl/build.c320
1 files changed, 320 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/mfluadir/otfcc/lib/table/otl/build.c b/Build/source/texk/web2c/mfluadir/otfcc/lib/table/otl/build.c
new file mode 100644
index 00000000000..91739f11589
--- /dev/null
+++ b/Build/source/texk/web2c/mfluadir/otfcc/lib/table/otl/build.c
@@ -0,0 +1,320 @@
+#include "private.h"
+
+#define LARGE_SUBTABLE_LIMIT 4096
+
+static uint32_t featureNameToTag(const sds name) {
+ uint32_t tag = 0;
+ if (sdslen(name) > 0) {
+ tag |= ((uint8_t)name[0]) << 24;
+ } else {
+ tag |= ((uint8_t)' ') << 24;
+ }
+ if (sdslen(name) > 1) {
+ tag |= ((uint8_t)name[1]) << 16;
+ } else {
+ tag |= ((uint8_t)' ') << 16;
+ }
+ if (sdslen(name) > 2) {
+ tag |= ((uint8_t)name[2]) << 8;
+ } else {
+ tag |= ((uint8_t)' ') << 8;
+ }
+ if (sdslen(name) > 3) {
+ tag |= ((uint8_t)name[3]) << 0;
+ } else {
+ tag |= ((uint8_t)' ') << 0;
+ }
+ return tag;
+}
+
+typedef caryll_Buffer *(*_otl_Builder)(const otl_Subtable *_subtable,
+ otl_BuildHeuristics heuristics);
+
+static tableid_t _declare_lookup_writer(otl_LookupType type, _otl_Builder fn,
+ const otl_Lookup *lookup, caryll_Buffer ***subtables,
+ size_t *lastOffset, bool *preferExtensionForThisLUT,
+ otl_BuildHeuristics heuristics) {
+ if (lookup->type == type) {
+ NEW(*subtables, lookup->subtables.length);
+ size_t totalBufSizeShort = 0;
+ size_t totalBufSizeExt = 0;
+ for (tableid_t j = 0; j < lookup->subtables.length; j++) {
+ caryll_Buffer *buf = fn(lookup->subtables.items[j], heuristics);
+ (*subtables)[j] = buf;
+ totalBufSizeShort += buf->size;
+ totalBufSizeExt += 8;
+ }
+ if (totalBufSizeShort > LARGE_SUBTABLE_LIMIT) {
+ *lastOffset += totalBufSizeExt;
+ *preferExtensionForThisLUT = true;
+ } else {
+ *lastOffset += totalBufSizeShort;
+ *preferExtensionForThisLUT = false;
+ }
+ return lookup->subtables.length;
+ }
+ return 0;
+}
+
+#define LOOKUP_WRITER(type, fn) \
+ if (!written) \
+ written = _declare_lookup_writer(type, fn, lookup, subtables, lastOffset, \
+ preferExtensionForThisLUT, heuristics);
+
+static tableid_t _build_lookup(const otl_Lookup *lookup, caryll_Buffer ***subtables,
+ size_t *lastOffset, bool *preferExtensionForThisLUT,
+ otl_BuildHeuristics heuristics) {
+ if (lookup->type == otl_type_gpos_chaining || lookup->type == otl_type_gsub_chaining) {
+ return otfcc_classifiedBuildChaining(lookup, subtables, lastOffset);
+ }
+
+ tableid_t written = 0;
+ LOOKUP_WRITER(otl_type_gsub_single, otfcc_build_gsub_single_subtable);
+ LOOKUP_WRITER(otl_type_gsub_multiple, otfcc_build_gsub_multi_subtable);
+ LOOKUP_WRITER(otl_type_gsub_alternate, otfcc_build_gsub_multi_subtable);
+ LOOKUP_WRITER(otl_type_gsub_ligature, otfcc_build_gsub_ligature_subtable);
+ LOOKUP_WRITER(otl_type_gsub_reverse, otfcc_build_gsub_reverse);
+ LOOKUP_WRITER(otl_type_gpos_single, otfcc_build_gpos_single);
+ LOOKUP_WRITER(otl_type_gpos_pair, otfcc_build_gpos_pair);
+ LOOKUP_WRITER(otl_type_gpos_cursive, otfcc_build_gpos_cursive);
+ LOOKUP_WRITER(otl_type_gpos_markToBase, otfcc_build_gpos_markToSingle);
+ LOOKUP_WRITER(otl_type_gpos_markToMark, otfcc_build_gpos_markToSingle);
+ LOOKUP_WRITER(otl_type_gpos_markToLigature, otfcc_build_gpos_markToLigature);
+ return written;
+}
+
+static otl_BuildHeuristics getLookupHeuristics(const table_OTL *table, const otl_Lookup *lut) {
+ otl_BuildHeuristics heu = OTL_BH_NORMAL;
+ // GSUB VERT heuristics
+ // GDI have some restrictions on the internal format of the lookup inisde a VERT feature
+ if (lut->type == otl_type_gsub_single) {
+ for (tableid_t j = 0; j < table->features.length; j++) {
+ const otl_Feature *fea = table->features.items[j];
+ if (featureNameToTag(fea->name) != OTFCC_CHR('v','e','r','t')) continue;
+ for (tableid_t k = 0; k < fea->lookups.length; k++) {
+ if (fea->lookups.items[k] == lut) heu |= OTL_BH_GSUB_VERT;
+ }
+ }
+ }
+ return heu;
+}
+
+// When writing lookups, otfcc will try to maintain everything correctly.
+// That is, we will use extended layout lookups automatically when the
+// offsets are too large.
+static bk_Block *writeOTLLookups(const table_OTL *table, const otfcc_Options *options,
+ const char *tag) {
+ caryll_Buffer ***subtables;
+ NEW(subtables, table->lookups.length);
+ bool *preferExtForThisLut;
+ tableid_t *subtableQuantity;
+ NEW(subtableQuantity, table->lookups.length);
+ NEW(preferExtForThisLut, table->lookups.length);
+
+ size_t lastOffset = 0;
+ for (tableid_t j = 0; j < table->lookups.length; j++) {
+ otl_Lookup *lookup = table->lookups.items[j];
+ otl_BuildHeuristics heu = getLookupHeuristics(table, lookup);
+ logProgress("Building lookup %s (%u/%u)\n", lookup->name, j,
+ (uint32_t)table->lookups.length);
+ subtableQuantity[j] =
+ _build_lookup(lookup, &(subtables[j]), &lastOffset, &(preferExtForThisLut[j]), heu);
+ }
+
+ size_t headerSize = 2 + 2 * table->lookups.length;
+ for (tableid_t j = 0; j < table->lookups.length; j++) {
+ if (subtableQuantity[j]) { headerSize += 6 + 2 * subtableQuantity[j]; }
+ }
+ bool useExtended = lastOffset >= 0xFF00 - headerSize;
+
+ bk_Block *root = bk_new_Block(b16, table->lookups.length, // LookupCount
+ bkover);
+ for (tableid_t j = 0; j < table->lookups.length; j++) {
+ if (!subtableQuantity[j]) {
+ logNotice("Lookup %s is empty.\n", table->lookups.items[j]->name);
+ }
+ otl_Lookup *lookup = table->lookups.items[j];
+ const bool canBeContextual = otfcc_chainingLookupIsContextualLookup(lookup);
+ const bool useExtendedForIt = useExtended || preferExtForThisLut[j];
+ if (useExtendedForIt) {
+ logNotice("[OTFCC-fea] Using extended OpenType table layout for %s/%s.\n", tag,
+ lookup->name);
+ }
+ uint16_t lookupType =
+ useExtendedForIt
+ ? (lookup->type > otl_type_gpos_unknown
+ ? otl_type_gpos_extend - otl_type_gpos_unknown
+ : lookup->type > otl_type_gsub_unknown
+ ? otl_type_gsub_extend - otl_type_gsub_unknown
+ : 0)
+ : (lookup->type > otl_type_gpos_unknown
+ ? lookup->type - otl_type_gpos_unknown
+ : lookup->type > otl_type_gsub_unknown ? lookup->type - otl_type_gsub_unknown
+ : 0) -
+ (canBeContextual ? 1 : 0);
+
+ bk_Block *blk = bk_new_Block(b16, lookupType, // LookupType
+ b16, lookup->flags, // LookupFlag
+ b16, subtableQuantity[j], // SubTableCount
+ bkover);
+
+ for (tableid_t k = 0; k < subtableQuantity[j]; k++) {
+ if (useExtendedForIt) {
+ uint16_t extensionLookupType = (lookup->type > otl_type_gpos_unknown
+ ? lookup->type - otl_type_gpos_unknown
+ : lookup->type > otl_type_gsub_unknown
+ ? lookup->type - otl_type_gsub_unknown
+ : 0) -
+ (canBeContextual ? 1 : 0);
+
+ bk_Block *stub =
+ bk_new_Block(b16, 1, // format
+ b16, extensionLookupType, // ExtensionLookupType
+ p32, bk_newBlockFromBuffer(subtables[j][k]), // ExtensionOffset
+ bkover);
+ bk_push(blk, p16, stub, bkover);
+ } else {
+ bk_push(blk, p16, bk_newBlockFromBuffer(subtables[j][k]), bkover);
+ }
+ }
+ bk_push(blk, b16, 0, // MarkFilteringSet
+ bkover);
+ bk_push(root, p16, blk, bkover);
+ FREE(subtables[j]);
+ }
+ FREE(subtables);
+ FREE(subtableQuantity);
+ FREE(preferExtForThisLut);
+ return root;
+}
+
+static bk_Block *writeOTLFeatures(const table_OTL *table, const otfcc_Options *options) {
+ bk_Block *root = bk_new_Block(b16, table->features.length, bkover);
+ for (tableid_t j = 0; j < table->features.length; j++) {
+ bk_Block *fea = bk_new_Block(p16, NULL, // FeatureParams
+ b16, table->features.items[j]->lookups.length, // LookupCount
+ bkover);
+ for (tableid_t k = 0; k < table->features.items[j]->lookups.length; k++) {
+ // reverse lookup
+ for (tableid_t l = 0; l < table->lookups.length; l++) {
+ if (table->features.items[j]->lookups.items[k] == table->lookups.items[l]) {
+ bk_push(fea, b16, l, bkover);
+ break;
+ }
+ }
+ }
+ bk_push(root, b32, featureNameToTag(table->features.items[j]->name), // FeatureTag
+ p16, fea, // Feature
+ bkover);
+ }
+ return root;
+}
+
+// script hash
+typedef struct {
+ sds tag;
+ uint16_t lc;
+ otl_LanguageSystem *dl;
+ otl_LanguageSystem **ll;
+ UT_hash_handle hh;
+} script_stat_hash;
+
+static tableid_t featureIndex(const otl_Feature *feature, const table_OTL *table) {
+ for (tableid_t j = 0; j < table->features.length; j++)
+ if (table->features.items[j] == feature) { return j; }
+ return 0xFFFF;
+}
+static bk_Block *writeLanguage(otl_LanguageSystem *lang, const table_OTL *table) {
+ if (!lang) return NULL;
+ bk_Block *root =
+ bk_new_Block(p16, NULL, // LookupOrder
+ b16, featureIndex(lang->requiredFeature, table), // ReqFeatureIndex
+ b16, lang->features.length, // FeatureCount
+ bkover);
+ for (tableid_t k = 0; k < lang->features.length; k++) {
+ bk_push(root, b16, featureIndex(lang->features.items[k], table), bkover);
+ }
+ return root;
+}
+
+static bk_Block *writeScript(script_stat_hash *script, const table_OTL *table) {
+ bk_Block *root = bk_new_Block(p16, writeLanguage(script->dl, table), // DefaultLangSys
+ b16, script->lc, // LangSysCount
+ bkover);
+
+ for (tableid_t j = 0; j < script->lc; j++) {
+ sds tag = sdsnewlen(script->ll[j]->name + 5, 4);
+
+ bk_push(root, b32, featureNameToTag(tag), // LangSysTag
+ p16, writeLanguage(script->ll[j], table), // LangSys
+ bkover);
+ sdsfree(tag);
+ }
+ return root;
+}
+static bk_Block *writeOTLScriptAndLanguages(const table_OTL *table, const otfcc_Options *options) {
+ script_stat_hash *h = NULL;
+ for (tableid_t j = 0; j < table->languages.length; j++) {
+ otl_LanguageSystem *language = table->languages.items[j];
+ sds scriptTag = sdsnewlen(language->name, 4);
+ bool isDefault = strncmp(language->name + 5, "DFLT", 4) == 0 ||
+ strncmp(language->name + 5, "dflt", 4) == 0;
+
+ script_stat_hash *s = NULL;
+ HASH_FIND_STR(h, scriptTag, s);
+ if (s) {
+ if (isDefault) {
+ s->dl = language;
+ } else {
+ s->lc += 1;
+ s->ll[s->lc - 1] = language;
+ }
+ sdsfree(scriptTag);
+ } else {
+ NEW(s);
+ s->tag = scriptTag;
+ s->dl = NULL;
+ NEW(s->ll, table->languages.length);
+ if (isDefault) {
+ s->dl = language;
+ s->lc = 0;
+ } else {
+ s->lc = 1;
+ s->ll[s->lc - 1] = language;
+ }
+ HASH_ADD_STR(h, tag, s);
+ }
+ }
+
+ bk_Block *root = bk_new_Block(b16, HASH_COUNT(h), bkover);
+
+ script_stat_hash *s, *tmp;
+ HASH_ITER(hh, h, s, tmp) {
+ bk_push(root, b32, featureNameToTag(s->tag), // ScriptTag
+ p16, writeScript(s, table), // Script
+ bkover);
+ HASH_DEL(h, s);
+ sdsfree(s->tag);
+ FREE(s->ll);
+ FREE(s);
+ }
+ return root;
+}
+
+caryll_Buffer *otfcc_buildOtl(const table_OTL *table, const otfcc_Options *options,
+ const char *tag) {
+ if (!table) return NULL;
+ caryll_Buffer *buf;
+ loggedStep("%s", tag) {
+ bk_Block *lookups = writeOTLLookups(table, options, tag);
+ bk_Block *features = writeOTLFeatures(table, options);
+ bk_Block *languages = writeOTLScriptAndLanguages(table, options);
+ bk_Block *root = bk_new_Block(b32, 0x10000, // Version
+ p16, languages, // ScriptList
+ p16, features, // FeatureList
+ p16, lookups, // LookupList
+ bkover);
+ buf = bk_build_Block(root);
+ }
+ return buf;
+}