UNPKG

opentype.features.js

Version:

Library to list opentype fonts optional features.

338 lines (337 loc) 22.4 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.FeatureType = void 0; var index_js_1 = __importDefault(require("./opentype/src/index.js")); var FeatureType; (function (FeatureType) { FeatureType[FeatureType["None"] = 0] = "None"; FeatureType[FeatureType["Hidden"] = 1] = "Hidden"; // << 1 - for future use // << 2 - for future use // << 3 - for future use FeatureType[FeatureType["DefaultOff"] = 16] = "DefaultOff"; FeatureType[FeatureType["DefaultOn"] = 32] = "DefaultOn"; })(FeatureType = exports.FeatureType || (exports.FeatureType = {})); var Features = /** @class */ (function () { function Features() { } Features.getFeaturesFromFile = function (file) { return __awaiter(this, void 0, void 0, function () { var font, _a; return __generator(this, function (_b) { switch (_b.label) { case 0: _b.trys.push([0, 2, , 3]); return [4 /*yield*/, index_js_1.default.load(file, undefined, { isUrl: false })]; case 1: // @ts-ignore font = _b.sent(); return [3 /*break*/, 3]; case 2: _a = _b.sent(); return [2 /*return*/, []]; case 3: return [2 /*return*/, this._getFeaturesFromFont(font)]; } }); }); }; Features.getFeaturesFromUrl = function (url) { return __awaiter(this, void 0, void 0, function () { var font, _a; return __generator(this, function (_b) { switch (_b.label) { case 0: _b.trys.push([0, 2, , 3]); return [4 /*yield*/, index_js_1.default.load(url, undefined, { isUrl: true })]; case 1: // @ts-ignore font = _b.sent(); return [3 /*break*/, 3]; case 2: _a = _b.sent(); return [2 /*return*/, null]; case 3: return [2 /*return*/, this._getFeaturesFromFont(font)]; } }); }); }; Features.getFeaturesFromArrayBuffer = function (buffer) { return __awaiter(this, void 0, void 0, function () { var font, _a; return __generator(this, function (_b) { switch (_b.label) { case 0: _b.trys.push([0, 2, , 3]); return [4 /*yield*/, index_js_1.default.parseBuffer(buffer, {})]; case 1: font = _b.sent(); return [3 /*break*/, 3]; case 2: _a = _b.sent(); return [2 /*return*/, []]; case 3: return [2 /*return*/, this._getFeaturesFromFont(font)]; } }); }); }; Features._getFeaturesFromFont = function (font) { return __awaiter(this, void 0, void 0, function () { var ret, _i, _a, n, params, description, numNamedParameters, firstParam, i, key, obj, value, result, lookupIndex, lookup, glyphArray, item; return __generator(this, function (_b) { if (!font.tables.gsub) return [2 /*return*/, []]; ret = []; for (_i = 0, _a = font.tables.gsub.features; _i < _a.length; _i++) { n = _a[_i]; params = []; description = null; if (n.feature.featureParams) { description = font.names[n.feature.featureParams.featUiTooltipTextNameId]; numNamedParameters = n.feature.featureParams.numNamedParameters; firstParam = n.feature.featureParams.firstParamUiLabelNameId; for (i = 0; i < numNamedParameters; i++) { key = (firstParam + i); obj = font.names[key]; value = obj && obj['en']; if (value) params.push(value); } result = []; lookupIndex = n.feature.lookupListIndexes[0]; lookup = font.tables.gsub.lookups[lookupIndex]; // LookupType 3 is the Alternate substitution. (Replace one glyph with one of many glyphs) if (lookup.lookupType === 3) { glyphArray = lookup.subtables[0].coverage.glyphs; result = this._lookupCodePoints(font.tables.cmap.glyphIndexMap, glyphArray); } item = { tag: n.tag, description: (description && description.en) || this.lookupRegisteredTags(n.tag), options: params, alternateCharacters: result, type: this.lookupTypeFlags(n.tag) }; ret.push(item); } } return [2 /*return*/, ret]; }); }); }; Features._lookupCodePoints = function (glyphIndexMap, lookupListIndexes) { return lookupListIndexes === null || lookupListIndexes === void 0 ? void 0 : lookupListIndexes.map(function (glyphIndex) { var key = Object.keys(glyphIndexMap).find(function (x) { return glyphIndexMap[x] === glyphIndex; }); if (!key) return undefined; return String.fromCharCode(parseInt(key)); }).filter(function (value, index, self) { return self.indexOf(value) === index; }).filter(function (x) { return x; }); }; Features.lookupTypeFlags = function (tag) { var _a; return (_a = this._lookupData.find(function (x) { return x.tag === tag; })) === null || _a === void 0 ? void 0 : _a.type; }; Features.lookupRegisteredTags = function (tag) { var _a; return (_a = this._lookupData.find(function (x) { return x.tag === tag; })) === null || _a === void 0 ? void 0 : _a.description; }; // Data from : // https://docs.microsoft.com/en-us/typography/opentype/spec/features_ae // https://docs.microsoft.com/en-us/typography/opentype/spec/features_fj // https://docs.microsoft.com/en-us/typography/opentype/spec/features_ko // https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt // https://docs.microsoft.com/en-us/typography/opentype/spec/features_uz // And by consulting font forge. (https://fontforge.org) Features._lookupData = [ // a-e { tag: 'aalt', description: 'Access All Alternates' }, { tag: 'abvf', description: 'Above Base Forms', type: FeatureType.Hidden }, { tag: 'abvm', description: 'Above Base Mark', type: FeatureType.Hidden }, { tag: 'abvs', description: 'Above Base Substitutions', type: FeatureType.Hidden }, { tag: 'afrc', description: 'Vertical Fractions', type: FeatureType.DefaultOff }, { tag: 'akhn', description: 'Akhand', type: FeatureType.Hidden }, { tag: 'alig', description: 'Ancient Ligatures' }, { tag: 'blwf', description: 'Below Base Forms', type: FeatureType.Hidden }, { tag: 'blwm', description: 'Below Base Mark', type: FeatureType.Hidden }, { tag: 'blws', description: 'Below Base Substitutions', type: FeatureType.Hidden }, { tag: 'c2pc', description: 'Capitals to Petite Capitals', type: FeatureType.DefaultOff }, { tag: 'c2sc', description: 'Capitals to Small Capitals', type: FeatureType.DefaultOff }, { tag: 'calt', description: 'Contextual Alternates', type: FeatureType.DefaultOn }, { tag: 'case', description: 'Case-Sensitive Forms' }, { tag: 'ccmp', description: 'Glyph Composition/Decomposition', type: FeatureType.Hidden }, { tag: 'cfar', description: 'Conjunct Form After Ro', type: FeatureType.Hidden }, { tag: 'chws', description: 'Contextual Half-width Spacing', type: FeatureType.DefaultOn }, { tag: 'cjct', description: 'Conjunct Forms', type: FeatureType.Hidden }, { tag: 'clig', description: 'Contextual Ligatures', type: FeatureType.DefaultOn }, { tag: 'cpct', description: 'Centered CJK Punctuation', type: FeatureType.DefaultOff }, { tag: 'cpsp', description: 'Capital Spacing', type: FeatureType.DefaultOn }, { tag: 'cswh', description: 'Contextual Swash', type: FeatureType.DefaultOff }, { tag: 'curs', description: 'Cursive Attachment', type: FeatureType.Hidden }, { tag: 'dcap', description: 'Drop Caps' }, { tag: 'dist', description: 'Distance', type: FeatureType.Hidden }, { tag: 'dlig', description: 'Discretionary Ligatures', type: FeatureType.Hidden }, { tag: 'dnom', description: 'Denominators', type: FeatureType.Hidden }, { tag: 'dpng', description: 'Dipthongs (Obsolete)', type: FeatureType.Hidden }, { tag: 'dtls', description: 'Dotless Forms', type: FeatureType.Hidden }, { tag: 'expt', description: 'Expert Forms' }, // f - j { tag: 'falt', description: 'Final Glyph On Line' }, { tag: 'fin2', description: 'Terminal Forms #2', type: FeatureType.Hidden }, { tag: 'fin3', description: 'Terminal Forms #3', type: FeatureType.Hidden }, { tag: 'fina', description: 'Terminal Forms', type: FeatureType.Hidden }, { tag: 'flac', description: 'Flattened Accents over Capitals', type: FeatureType.Hidden }, { tag: 'frac', description: 'Diagonal Fractions', type: FeatureType.DefaultOff }, { tag: 'fwid', description: 'Full Widths', type: FeatureType.DefaultOff }, { tag: 'half', description: 'Half Forms', type: FeatureType.Hidden }, { tag: 'haln', description: 'Halant Forms', type: FeatureType.Hidden }, { tag: 'halt', description: 'Alternative Half Widths', type: FeatureType.DefaultOn }, { tag: 'hist', description: 'Historical Forms', type: FeatureType.DefaultOff }, { tag: 'hkna', description: 'Horizontal Kana Alternatives', type: FeatureType.DefaultOff }, { tag: 'hlig', description: 'Historic Ligatures', type: FeatureType.DefaultOff }, { tag: 'hngl', description: 'Hanja to Hangul', type: FeatureType.DefaultOff | FeatureType.Hidden }, { tag: 'hojo', description: 'Hojo (JIS X 0212-1990) Kanji Forms', type: FeatureType.DefaultOff }, { tag: 'hwid', description: 'Half Widths', type: FeatureType.DefaultOff }, { tag: 'init', description: 'Initial Forms', type: FeatureType.Hidden }, { tag: 'isol', description: 'Isolated Forms', type: FeatureType.Hidden }, { tag: 'ital', description: 'Italics' }, { tag: 'jalt', description: 'Justification Alternatives' }, { tag: 'jajp', description: 'Japanese Forms (Obsolete)', type: FeatureType.Hidden }, { tag: 'jp04', description: 'JIS2004 Forms', type: FeatureType.DefaultOff }, { tag: 'jp78', description: 'JIS78 Forms', type: FeatureType.DefaultOff }, { tag: 'jp83', description: 'JIS83 Forms', type: FeatureType.DefaultOff }, { tag: 'jp90', description: 'JIS90 Forms', type: FeatureType.DefaultOff }, // k - o { tag: 'kern', description: 'Horizontal Kerning' }, { tag: 'lfbd', description: 'Left Bounds', type: FeatureType.DefaultOff }, { tag: 'liga', description: 'Standard Ligatures', type: FeatureType.DefaultOn }, { tag: 'ljmo', description: 'Leading Jamo Forms', type: FeatureType.Hidden }, { tag: 'lnum', description: 'Lining Figures', type: FeatureType.DefaultOff }, { tag: 'locl', description: 'Localized Forms', type: FeatureType.Hidden }, { tag: 'ltra', description: 'Left-to-right glyph alternates', type: FeatureType.Hidden }, { tag: 'ltrm', description: 'Left-to-right mirrored forms', type: FeatureType.Hidden }, { tag: 'mark', description: 'Mark Positioning', type: FeatureType.Hidden }, { tag: 'med2', description: 'Medial Forms 2', type: FeatureType.Hidden }, { tag: 'medi', description: 'Medial Forms', type: FeatureType.Hidden }, { tag: 'mgrk', description: 'Mathematical Greek', type: FeatureType.DefaultOff }, { tag: 'mkmk', description: 'Mark to Mark', type: FeatureType.Hidden }, { tag: 'mset', description: 'Mark Positioning via Substitution' }, { tag: 'nalt', description: 'Alternate Annotation Forms', type: FeatureType.DefaultOff }, { tag: 'nlck', description: 'NLC Kanji Forms', type: FeatureType.DefaultOff }, { tag: 'nukt', description: 'Nukta Forms', type: FeatureType.Hidden }, { tag: 'numr', description: 'Numerators', type: FeatureType.Hidden | FeatureType.DefaultOn }, { tag: 'onum', description: 'Oldstyle Figures', type: FeatureType.DefaultOff }, { tag: 'opbd', description: 'Optical Bounds', type: FeatureType.Hidden | FeatureType.DefaultOn }, { tag: 'ordn', description: 'Ordinals', type: FeatureType.DefaultOff }, { tag: 'ornm', description: 'Ornaments', type: FeatureType.DefaultOff }, // p - t { tag: 'palt', description: 'Proportional Alternate Metrics', type: FeatureType.DefaultOff }, { tag: 'pcap', description: 'Lowercase to Petite Capitals', type: FeatureType.DefaultOff }, { tag: 'pkna', description: 'Proportional Kana', type: FeatureType.DefaultOff }, { tag: 'pnum', description: 'Proportional Numbers', type: FeatureType.DefaultOff }, { tag: 'pref', description: 'Pre Base Forms', type: FeatureType.Hidden }, { tag: 'pres', description: 'Pre Base Substitutions', type: FeatureType.Hidden }, { tag: 'pstf', description: 'Post Base Forms', type: FeatureType.Hidden }, { tag: 'psts', description: 'Post Base Substitutions', type: FeatureType.Hidden }, { tag: 'pwid', description: 'Proportional Width' }, { tag: 'qwid', description: 'Quarter Widths', type: FeatureType.DefaultOff }, { tag: 'rand', description: 'Randomize', type: FeatureType.DefaultOn }, { tag: 'rclt', description: 'Required Contextual Alternates', type: FeatureType.Hidden }, { tag: 'rkrf', description: 'Rakar Forms', type: FeatureType.Hidden }, { tag: 'rlig', description: 'Required Ligatures', type: FeatureType.Hidden }, { tag: 'rphf', description: 'Reph Form', type: FeatureType.Hidden }, { tag: 'rtbd', description: 'Right Bounds', type: FeatureType.DefaultOff }, { tag: 'rtla', description: 'Right to Left Alternates', type: FeatureType.Hidden }, { tag: 'rtlm', description: 'Right to Left mirrored forms', type: FeatureType.Hidden }, { tag: 'ruby', description: 'Ruby Notational Forms', type: FeatureType.DefaultOff }, { tag: 'rvrn', description: 'Required Variation Alternates', type: FeatureType.DefaultOn | FeatureType.Hidden }, { tag: 'salt', description: 'Stylistic Alternatives', type: FeatureType.DefaultOff }, { tag: 'sinf', description: 'Scientific Inferiors', type: FeatureType.DefaultOff }, { tag: 'smcp', description: 'Lowercase to Small Capitals', type: FeatureType.DefaultOff }, { tag: 'size', description: 'Optical size', type: FeatureType.DefaultOn }, { tag: 'smpl', description: 'Simplified Forms', type: FeatureType.DefaultOff }, { tag: 'ss01', description: 'Style Set 1', type: FeatureType.DefaultOff }, { tag: 'ss02', description: 'Style Set 2', type: FeatureType.DefaultOff }, { tag: 'ss03', description: 'Style Set 3', type: FeatureType.DefaultOff }, { tag: 'ss04', description: 'Style Set 4', type: FeatureType.DefaultOff }, { tag: 'ss05', description: 'Style Set 5', type: FeatureType.DefaultOff }, { tag: 'ss06', description: 'Style Set 6', type: FeatureType.DefaultOff }, { tag: 'ss07', description: 'Style Set 7', type: FeatureType.DefaultOff }, { tag: 'ss08', description: 'Style Set 8', type: FeatureType.DefaultOff }, { tag: 'ss09', description: 'Style Set 9', type: FeatureType.DefaultOff }, { tag: 'ss10', description: 'Style Set 10', type: FeatureType.DefaultOff }, { tag: 'ss11', description: 'Style Set 11', type: FeatureType.DefaultOff }, { tag: 'ss12', description: 'Style Set 12', type: FeatureType.DefaultOff }, { tag: 'ss13', description: 'Style Set 13', type: FeatureType.DefaultOff }, { tag: 'ss14', description: 'Style Set 14', type: FeatureType.DefaultOff }, { tag: 'ss15', description: 'Style Set 15', type: FeatureType.DefaultOff }, { tag: 'ss16', description: 'Style Set 16', type: FeatureType.DefaultOff }, { tag: 'ss17', description: 'Style Set 17', type: FeatureType.DefaultOff }, { tag: 'ss18', description: 'Style Set 18', type: FeatureType.DefaultOff }, { tag: 'ss19', description: 'Style Set 19', type: FeatureType.DefaultOff }, { tag: 'ss20', description: 'Style Set 20', type: FeatureType.DefaultOff }, { tag: 'ssty', description: 'Math script style alternates', type: FeatureType.Hidden }, { tag: 'stchc', description: 'Stretching Glyph Decomposition', type: FeatureType.Hidden }, { tag: 'subs', description: 'Subscript', type: FeatureType.DefaultOff }, { tag: 'sups', description: 'Superscript', type: FeatureType.DefaultOff }, { tag: 'swsh', description: 'Swash', type: FeatureType.DefaultOff }, { tag: 'titl', description: 'Titling', type: FeatureType.DefaultOff }, { tag: 'tjmo', description: 'Trailing Jamo Forms', type: FeatureType.Hidden }, { tag: 'tnam', description: 'Traditional Name Forms', type: FeatureType.DefaultOff }, { tag: 'tnum', description: 'Tabular Numbers', type: FeatureType.DefaultOff }, { tag: 'trad', description: 'Traditional Forms', type: FeatureType.DefaultOff }, { tag: 'twid', description: 'Third Widths', type: FeatureType.DefaultOff }, // u - z { tag: 'unic', description: 'Unicase', type: FeatureType.DefaultOff }, { tag: 'valt', description: 'Alternate Vertical Metrics' }, { tag: 'vatu', description: 'Vattu Variants', type: FeatureType.Hidden }, { tag: 'vchw', description: 'Vertical Contextual Half-width Spacing' }, { tag: 'vert', description: 'Vertical Alternates (obs)' }, { tag: 'vhal', description: 'Alternate Vertical Half Metrics', type: FeatureType.DefaultOff }, { tag: 'vjmo', description: 'Vowel Jamo Forms', type: FeatureType.Hidden }, { tag: 'vkna', description: 'Vertical Kana Alternates', type: FeatureType.DefaultOff }, { tag: 'vkrn', description: 'Vertical Kerning' }, { tag: 'vpal', description: 'Proportional Alternate Vertical Metrics', type: FeatureType.DefaultOff }, { tag: 'vrt2', description: 'Vertical Rotation & Alternates' }, { tag: 'zero', description: 'Slashed Zero', type: FeatureType.DefaultOff }, ]; return Features; }()); exports.default = Features;