diff options
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.c | 320 |
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; +} |