From 15664de472b3b556e7976766b414c9d1f22b81d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Thu, 3 Oct 2024 21:50:34 +0200 Subject: [PATCH 01/38] initial --- .eslintrc.json | 2 + addons/addon-web-fonts/LICENSE | 19 ++ addons/addon-web-fonts/README.md | 137 ++++++++ addons/addon-web-fonts/package.json | 29 ++ addons/addon-web-fonts/src/WebFontsAddon.ts | 299 ++++++++++++++++++ addons/addon-web-fonts/src/tsconfig.json | 28 ++ .../test/WebLinksAddon.test.ts | 185 +++++++++++ .../addon-web-fonts/test/playwright.config.ts | 35 ++ addons/addon-web-fonts/test/tsconfig.json | 42 +++ addons/addon-web-fonts/tsconfig.json | 8 + .../typings/addon-web-fonts.d.ts | 18 ++ addons/addon-web-fonts/webpack.config.js | 33 ++ demo/client.ts | 33 +- demo/style.css | 112 +++++++ demo/tsconfig.json | 1 + tsconfig.all.json | 1 + 16 files changed, 970 insertions(+), 12 deletions(-) create mode 100644 addons/addon-web-fonts/LICENSE create mode 100644 addons/addon-web-fonts/README.md create mode 100644 addons/addon-web-fonts/package.json create mode 100644 addons/addon-web-fonts/src/WebFontsAddon.ts create mode 100644 addons/addon-web-fonts/src/tsconfig.json create mode 100644 addons/addon-web-fonts/test/WebLinksAddon.test.ts create mode 100644 addons/addon-web-fonts/test/playwright.config.ts create mode 100644 addons/addon-web-fonts/test/tsconfig.json create mode 100644 addons/addon-web-fonts/tsconfig.json create mode 100644 addons/addon-web-fonts/typings/addon-web-fonts.d.ts create mode 100644 addons/addon-web-fonts/webpack.config.js diff --git a/.eslintrc.json b/.eslintrc.json index 8c475982..3d9322d3 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -33,6 +33,8 @@ "addons/addon-unicode-graphemes/src/tsconfig.json", "addons/addon-unicode-graphemes/test/tsconfig.json", "addons/addon-unicode-graphemes/benchmark/tsconfig.json", + "addons/addon-web-fonts/src/tsconfig.json", + "addons/addon-web-fonts/test/tsconfig.json", "addons/addon-web-links/src/tsconfig.json", "addons/addon-web-links/test/tsconfig.json", "addons/addon-webgl/src/tsconfig.json", diff --git a/addons/addon-web-fonts/LICENSE b/addons/addon-web-fonts/LICENSE new file mode 100644 index 00000000..447eb79f --- /dev/null +++ b/addons/addon-web-fonts/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2024, The xterm.js authors (https://github.com/xtermjs/xterm.js) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/addons/addon-web-fonts/README.md b/addons/addon-web-fonts/README.md new file mode 100644 index 00000000..bf506cf1 --- /dev/null +++ b/addons/addon-web-fonts/README.md @@ -0,0 +1,137 @@ +## @xterm/addon-web-fonts + +Addon to use webfonts with [xterm.js](https://github.com/xtermjs/xterm.js). This addon requires xterm.js v5+. + +### Install + +```bash +npm install --save @xterm/addon-web-fonts +``` + +### Issue with Webfonts + +Webfonts are announced by CSS `font-face` rules (or its Javascript `FontFace` counterparts). Since font files tend to be quite big assets, browser engines often postpone their loading to an actual styling request of a codepoint matching a font file's `unicode-range`. In short - font files will not be loaded until really needed. + +xterm.js on the other hand heavily relies on exact measurements of character glyphs to layout its output. This is done by determining the glyph width (DOM renderer) or by creating a glyph texture (WebGl renderer) for every output character. +For performance reasons both is done in synchronous code and cached. This logic only works properly, +if a font glyph is available on its first usage, or a wrong glyph from a fallback font chosen by the browser will be used instead. + +For webfonts and xterm.js this means, that we cannot rely on the default loading strategy of the browser, but have to preload the font files before using that font in xterm.js. + + +### Static Preloading for the Rescue? + +If you dont mind higher initial loading times of the embedding document, you can tell the browser to preload the needed font files by placing the following link elements in the document's head above any other CSS/Javascript: +```html + + + ... + +``` +Downside of this approach is the much higher initial loading time showing as a white page. Browsers also will resort to system fonts, if the preloading takes too long, so with a slow connection or a very big font this solves literally nothing. + + +### Preloading with WebFontsAddon + +The webfonts addon offers several ways to deal with the loading of font assets without leaving the terminal in an unusable state. + + +Recap - normally boostrapping of a new terminal involves these basic steps: + +```typescript +import { Terminal } from '@xterm/xterm'; +import { XYAddon } from '@xterm/addon-xy'; + +// create a `Terminal` instance with some options, e.g. a custom font family +const terminal = new Terminal({fontFamily: 'monospace'}); + +// create and load all addons you want to use, e.g. fit addon +const xyAddon = new XYAddon(); +terminal.loadAddon(xyAddon); + +// finally: call `open` of the terminal instance +terminal.open(your_terminal_div_element); // <-- critical path for webfonts +// more boostrapping goes here ... +``` + +This synchronous code is guaranteed to work in all browsers, as the font `monospace` will always be available. +It will also work that way with any installed system font, but breaks horribly for webfonts. The actual culprit here is the call to `terminal.open`, which attaches the terminal to the DOM and starts the renderer with all the glyph caching mentioned above, while the webfont is not fully available yet. + +To fix that, the webfonts addon provides a waiting condition: +```typescript +import { Terminal } from '@xterm/xterm'; +import { XYAddon } from '@xterm/addon-xy'; +import { WebFontsAddon } from '@xterm/addon-web-fonts'; + +// create a `Terminal` instance, now with webfonts +const terminal = new Terminal({fontFamily: '"Web Mono 1", "Super Powerline", monospace'}); +const xyAddon = new XYAddon(); +terminal.loadAddon(xyAddon); + +const webFontsAddon = new WebFontsAddon(); +terminal.loadAddon(webFontsAddon); + +// wait for webfonts to be fully loaded +await WebFontsAddon.loadFonts(['Web Mono 1', 'Super Powerline']).then(() => { + terminal.open(your_terminal_div_element); + // more boostrapping goes here ... +}); +``` +Here `loadFonts` will look up the font face objects in `document.fonts` and load them before continuing. +For this to work, you have to make sure, that the CSS `font-face` rules for these webfonts are loaded +on the initial document load (more precise - by the time this code runs). + +Please note, that this code cannot run synchronous anymore, so you will have to split your +bootstrapping code into several stages. If thats too much of a hassle, you can also move the whole +bootstrapping under that waiting condition (`loadFonts` is actually a static method): +```typescript +import { Terminal } from '@xterm/xterm'; +import { XYAddon } from '@xterm/addon-xy'; +import { WebFontsAddon } from '@xterm/addon-web-fonts'; + +WebFontsAddon.loadFonts(['Web Mono 1', 'Super Powerline']).then(() => { + // create a `Terminal` instance, now with webfonts + const terminal = new Terminal({fontFamily: '"Web Mono 1", "Super Powerline", monospace'}); + const xyAddon = new XYAddon(); + terminal.loadAddon(xyAddon); + + const webFontsAddon = new WebFontsAddon(); + terminal.loadAddon(webFontsAddon); + + terminal.open(your_terminal_div_element); + // more boostrapping goes here ... +}); +``` + +### Webfont Loading at Runtime + +Given you have a terminal already running and want to change the font family to a different not yet loaded webfont. +That can be achieved like this: +```typescript +// either create font face objects in javascript +const ff1 = new FontFace('New Web Mono', url1, ...); +const ff2 = new FontFace('New Web Mono', url2, ...); +// and await their loading +await WebFontsAddon.loadFonts([ff1, ff2]).then(() => { + // apply new webfont to terminal + terminal.options.fontFamily = 'New Web Mono'; + // since the new font might have slighly different metrics, + // also run the fit addon here (or any other custom resize logic) + fitAddon.fit(); +}); + +// or alternatively use CSS to add new font-face rules, e.g. +document.styleSheets[0].insertRule( + "@font-face { font-family: 'New Web Mono'; src: url(newfont.woff); }", 0); +// and await the new font family name +await WebFontsAddon.loadFonts(['New Web Mono']).then(() => { + // apply new webfont to terminal + terminal.options.fontFamily = 'New Web Mono'; + // since the new font might have slighly different metrics, + // also run the fit addon here (or any other custom resize logic) + fitAddon.fit(); +}); +``` + + +See the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/addon-web-fonts/typings/addon-web-fonts.d.ts) for more advanced usage. diff --git a/addons/addon-web-fonts/package.json b/addons/addon-web-fonts/package.json new file mode 100644 index 00000000..1daac376 --- /dev/null +++ b/addons/addon-web-fonts/package.json @@ -0,0 +1,29 @@ +{ + "name": "@xterm/addon-web-fonts", + "version": "0.1.0", + "author": { + "name": "The xterm.js authors", + "url": "https://xtermjs.org/" + }, + "main": "lib/addon-web-fonts.js", + "module": "lib/addon-web-fonts.mjs", + "types": "typings/addon-web-fonts.d.ts", + "repository": "https://github.com/xtermjs/xterm.js/tree/master/addons/addon-web-fonts", + "license": "MIT", + "keywords": [ + "terminal", + "xterm", + "xterm.js" + ], + "scripts": { + "build": "../../node_modules/.bin/tsc -p .", + "prepackage": "npm run build", + "package": "../../node_modules/.bin/webpack", + "prepublishOnly": "npm run package", + "start": "node ../../demo/start" + }, + "peerDependencies": { + "@xterm/xterm": "^5.0.0" + }, + "dependencies": {} +} diff --git a/addons/addon-web-fonts/src/WebFontsAddon.ts b/addons/addon-web-fonts/src/WebFontsAddon.ts new file mode 100644 index 00000000..91800abd --- /dev/null +++ b/addons/addon-web-fonts/src/WebFontsAddon.ts @@ -0,0 +1,299 @@ +/** + * Copyright (c) 2024 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import type { Terminal, ITerminalAddon } from '@xterm/xterm'; +import type { WebFontsAddon as IWebFontsApi } from '@xterm/addon-web-fonts'; + + +/** + * Unquote family name. + */ +function unquote(s: string): string { + if (s[0] === '"' && s[s.length - 1] === '"') return s.slice(1, -1); + if (s[0] === "'" && s[s.length - 1] === "'") return s.slice(1, -1); + return s; +} + + +/** + * Quote family name. + * @see https://mathiasbynens.be/notes/unquoted-font-family + */ +function quote(s: string): string { + const pos = s.match(/([-_a-zA-Z0-9\xA0-\u{10FFFF}]+)/u); + const neg = s.match(/^(-?\d|--)/m); + if (!neg && pos && pos[1] === s) return s; + return `"${s.replace('"', '\\"')}"`; +} + + +function splitFamily(family: string | undefined): string[] { + if (!family) return []; + return family.split(',').map(e => unquote(e.trim())); +} + + +function createFamily(families: string[]): string { + return families.map(quote).join(', '); +} + + +function _loadFonts(fonts?: (string | FontFace)[]): Promise { + let ffs = Array.from(document.fonts); + if (!fonts || !fonts.length) { + return Promise.all(ffs.map(ff => ff.load())); + } + let toLoad: FontFace[] = []; + let ffsHashed = ffs.map(ff => WebFontsAddon.hashFontFace(ff)); + for (const font of fonts) { + if (font instanceof FontFace) { + const fontHashed = WebFontsAddon.hashFontFace(font); + const idx = ffsHashed.indexOf(fontHashed); + if (idx === -1) { + document.fonts.add(font); + ffs.push(font); + ffsHashed.push(fontHashed); + toLoad.push(font); + } else { + toLoad.push(ffs[idx]); + } + } else { + // string as font + const familyFiltered = ffs.filter(ff => font === unquote(ff.family)); + toLoad = toLoad.concat(familyFiltered); + if (!familyFiltered.length) { + console.warn(`font family "${font}" not registered in document.fonts`); + } + } + } + return Promise.all(toLoad.map(ff => ff.load())); +} + + + +export class WebFontsAddon implements ITerminalAddon, IWebFontsApi { + constructor(public forceInitialRelayout: boolean = true) { } + public dispose(): void { } + + public activate(terminal: Terminal): void { + if (this.forceInitialRelayout) { + document.fonts.ready.then(() => this.relayout(terminal)); + } + } + + /** + * Force a terminal re-layout by altering `options.FontFamily`. + * + * Found webfonts in `fontFamily` are temporarily removed until the webfont + * resources are fully loaded. + * + * This method is meant as a fallback fix for sloppy integrations, + * that wrongly placed a webfont at the terminal contructor options. + * It is likely to lead to terminal flickering in all browsers (FOUT). + * + * To avoid triggering this fallback in your integration, make sure to have + * the needed webfonts loaded at the time `terminal.open` is called. + */ + public relayout(terminal: Terminal): void { + const family = terminal.options.fontFamily; + const families = splitFamily(family); + const webFamilies = WebFontsAddon.getFontFamilies(); + const dirty: string[] = []; + const clean: string[] = []; + for (const fam of families) + (webFamilies.indexOf(fam) !== -1 ? dirty : clean).push(fam); + if (dirty.length) { + _loadFonts(dirty).then(() => { + terminal.options.fontFamily = clean.length ? createFamily(clean) : 'monospace'; + terminal.options.fontFamily = family; + }); + } + } + + /** + * Hash a font face from it properties. + * Used in `loadFonts` to avoid bloating + * `document.fonts` from multiple calls. + */ + public static hashFontFace(ff: FontFace): string { + return JSON.stringify([ + unquote(ff.family), + ff.stretch, + ff.style, + ff.unicodeRange, + ff.weight, + ]) + } + + /** + * Return font families known in `document.fonts`. + */ + public static getFontFamilies(): string[] { + return Array.from(new Set(Array.from(document.fonts).map(e => unquote(e.family)))); + } + + /** + * Wait for webfont resources to be loaded. + * + * Without any argument, all fonts currently listed in + * `document.fonts` will be loaded. + * For a more fine-grained loading strategy you can populate + * the `fonts` argument with: + * - font families : loads all fontfaces in `document.fonts` + * matching the family names + * - fontface objects : loads given fontfaces and adds them to + * `document.fonts` + * + * The returned promise will resolve, when all loading is done. + */ + public static loadFonts(fonts?: (string | FontFace)[]): Promise { + return document.fonts.ready.then(() => _loadFonts(fonts)); + } +} + + + + + + + + +// TODO: place into test cases +/* +(window as any).__roboto = [ + // cyrillic-ext + new FontFace( + 'Roboto Mono', + "url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3CWWoKC.woff2) format('woff2')", + { + style: 'italic', + weight: '100 700', + display: 'swap', + unicodeRange: 'U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F' + } + ), + // cyrillic + new FontFace( + 'Roboto Mono', + "url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3mWWoKC.woff2) format('woff2')", + { + style: 'italic', + weight: '100 700', + display: 'swap', + unicodeRange: 'U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116' + } + ), + // greek + new FontFace( + 'Roboto Mono', + "url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm36WWoKC.woff2) format('woff2')", + { + style: 'italic', + weight: '100 700', + display: 'swap', + unicodeRange: 'U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF' + } + ), + // vietnamese + new FontFace( + 'Roboto Mono', + "url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3KWWoKC.woff2) format('woff2')", + { + style: 'italic', + weight: '100 700', + display: 'swap', + unicodeRange: 'U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB' + } + ), + // latin-ext + new FontFace( + 'Roboto Mono', + "url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3OWWoKC.woff2) format('woff2')", + { + style: 'italic', + weight: '100 700', + display: 'swap', + unicodeRange: 'U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF' + } + ), + // latin + new FontFace( + 'Roboto Mono', + "url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm32WWg.woff2) format('woff2')", + { + style: 'italic', + weight: '100 700', + display: 'swap', + unicodeRange: 'U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD' + } + ), + // cyrillic-ext + new FontFace( + 'Roboto Mono', + "url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhGq3-OXg.woff2) format('woff2')", + { + style: 'normal', + weight: '100 700', + display: 'swap', + unicodeRange: 'U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F' + } + ), + // cyrillic + new FontFace( + 'Roboto Mono', + "url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhPq3-OXg.woff2) format('woff2')", + { + style: 'normal', + weight: '100 700', + display: 'swap', + unicodeRange: 'U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116' + } + ), + // greek + new FontFace( + 'Roboto Mono', + "url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhIq3-OXg.woff2) format('woff2')", + { + style: 'normal', + weight: '100 700', + display: 'swap', + unicodeRange: 'U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF' + } + ), + // vietnamese + new FontFace( + 'Roboto Mono', + "url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhEq3-OXg.woff2) format('woff2')", + { + style: 'normal', + weight: '100 700', + display: 'swap', + unicodeRange: 'U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB' + } + ), + // latin-ext + new FontFace( + 'Roboto Mono', + "url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhFq3-OXg.woff2) format('woff2')", + { + style: 'normal', + weight: '100 700', + display: 'swap', + unicodeRange: 'U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF' + } + ), + // latin + new FontFace( + 'Roboto Mono', + "url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhLq38.woff2) format('woff2')", + { + style: 'normal', + weight: '100 700', + display: 'swap', + unicodeRange: 'U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD' + } + ), +]; +*/ \ No newline at end of file diff --git a/addons/addon-web-fonts/src/tsconfig.json b/addons/addon-web-fonts/src/tsconfig.json new file mode 100644 index 00000000..cd297308 --- /dev/null +++ b/addons/addon-web-fonts/src/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es2021", + "lib": [ + "dom", + "es2015", + "dom.iterable" + ], + "rootDir": ".", + "outDir": "../out", + "sourceMap": true, + "removeComments": true, + "strict": true, + "types": [ + "../../../node_modules/@types/mocha" + ], + "paths": { + "@xterm/addon-web-fonts": [ + "../typings/addon-web-fonts.d.ts" + ] + } + }, + "include": [ + "./**/*", + "../../../typings/xterm.d.ts" + ] +} diff --git a/addons/addon-web-fonts/test/WebLinksAddon.test.ts b/addons/addon-web-fonts/test/WebLinksAddon.test.ts new file mode 100644 index 00000000..8682c533 --- /dev/null +++ b/addons/addon-web-fonts/test/WebLinksAddon.test.ts @@ -0,0 +1,185 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ +import test from '@playwright/test'; +import { deepStrictEqual, strictEqual } from 'assert'; +import { readFile } from 'fs'; +import { resolve } from 'path'; +import { ITestContext, createTestContext, openTerminal, pollFor, timeout } from '../../../test/playwright/TestUtils'; + +interface ILinkStateData { + uri?: string; + range?: { + start: { + x: number; + y: number; + }; + end: { + x: number; + y: number; + }; + }; +} + + +let ctx: ITestContext; +test.beforeAll(async ({ browser }) => { + ctx = await createTestContext(browser); + await openTerminal(ctx, { cols: 40 }); +}); +test.afterAll(async () => await ctx.page.close()); + +test.describe('WebLinksAddon', () => { + + test.beforeEach(async () => { + await ctx.page.evaluate(` + window.term.reset() + window._linkaddon?.dispose(); + window._linkaddon = new WebLinksAddon(); + window.term.loadAddon(window._linkaddon); + `); + }); + + const countryTlds = [ + '.ac', '.ad', '.ae', '.af', '.ag', '.ai', '.al', '.am', '.ao', '.aq', '.ar', '.as', '.at', + '.au', '.aw', '.ax', '.az', '.ba', '.bb', '.bd', '.be', '.bf', '.bg', '.bh', '.bi', '.bj', + '.bm', '.bn', '.bo', '.bq', '.br', '.bs', '.bt', '.bw', '.by', '.bz', '.ca', '.cc', '.cd', + '.cf', '.cg', '.ch', '.ci', '.ck', '.cl', '.cm', '.cn', '.co', '.cr', '.cu', '.cv', '.cw', + '.cx', '.cy', '.cz', '.de', '.dj', '.dk', '.dm', '.do', '.dz', '.ec', '.ee', '.eg', '.eh', + '.er', '.es', '.et', '.eu', '.fi', '.fj', '.fk', '.fm', '.fo', '.fr', '.ga', '.gd', '.ge', + '.gf', '.gg', '.gh', '.gi', '.gl', '.gm', '.gn', '.gp', '.gq', '.gr', '.gs', '.gt', '.gu', + '.gw', '.gy', '.hk', '.hm', '.hn', '.hr', '.ht', '.hu', '.id', '.ie', '.il', '.im', '.in', + '.io', '.iq', '.ir', '.is', '.it', '.je', '.jm', '.jo', '.jp', '.ke', '.kg', '.kh', '.ki', + '.km', '.kn', '.kp', '.kr', '.kw', '.ky', '.kz', '.la', '.lb', '.lc', '.li', '.lk', '.lr', + '.ls', '.lt', '.lu', '.lv', '.ly', '.ma', '.mc', '.md', '.me', '.mg', '.mh', '.mk', '.ml', + '.mm', '.mn', '.mo', '.mp', '.mq', '.mr', '.ms', '.mt', '.mu', '.mv', '.mw', '.mx', '.my', + '.mz', '.na', '.nc', '.ne', '.nf', '.ng', '.ni', '.nl', '.no', '.np', '.nr', '.nu', '.nz', + '.om', '.pa', '.pe', '.pf', '.pg', '.ph', '.pk', '.pl', '.pm', '.pn', '.pr', '.ps', '.pt', + '.pw', '.py', '.qa', '.re', '.ro', '.rs', '.ru', '.rw', '.sa', '.sb', '.sc', '.sd', '.se', + '.sg', '.sh', '.si', '.sk', '.sl', '.sm', '.sn', '.so', '.sr', '.ss', '.st', '.su', '.sv', + '.sx', '.sy', '.sz', '.tc', '.td', '.tf', '.tg', '.th', '.tj', '.tk', '.tl', '.tm', '.tn', + '.to', '.tr', '.tt', '.tv', '.tw', '.tz', '.ua', '.ug', '.uk', '.us', '.uy', '.uz', '.va', + '.vc', '.ve', '.vg', '.vi', '.vn', '.vu', '.wf', '.ws', '.ye', '.yt', '.za', '.zm', '.zw' + ]; + for (const tld of countryTlds) { + test(tld, async () => await testHostName(`foo${tld}`)); + } + test(`.com`, async () => await testHostName(`foo.com`)); + for (const tld of countryTlds) { + test(`.com${tld}`, async () => await testHostName(`foo.com${tld}`)); + } + + test.describe('correct buffer offsets & uri', () => { + test.beforeEach(async () => { + await ctx.page.evaluate(` + window._linkStateData = {uri:''}; + window._linkaddon._options.hover = (event, uri, range) => { window._linkStateData = { uri, range }; }; + `); + }); + test('all half width', async () => { + await ctx.proxy.write('aaa http://example.com aaa http://example.com aaa'); + await resetAndHover(5, 0); + await evalLinkStateData('http://example.com', { start: { x: 5, y: 1 }, end: { x: 22, y: 1 } }); + await resetAndHover(1, 1); + await evalLinkStateData('http://example.com', { start: { x: 28, y: 1 }, end: { x: 5, y: 2 } }); + }); + test('url after full width', async () => { + await ctx.proxy.write('¥¥¥ http://example.com ¥¥¥ http://example.com aaa'); + await resetAndHover(8, 0); + await evalLinkStateData('http://example.com', { start: { x: 8, y: 1 }, end: { x: 25, y: 1 } }); + await resetAndHover(1, 1); + await evalLinkStateData('http://example.com', { start: { x: 34, y: 1 }, end: { x: 11, y: 2 } }); + }); + test('full width within url and before', async () => { + await ctx.proxy.write('¥¥¥ https://ko.wikipedia.org/wiki/위키백과:대문 aaa https://ko.wikipedia.org/wiki/위키백과:대문 ¥¥¥'); + await resetAndHover(8, 0); + await evalLinkStateData('https://ko.wikipedia.org/wiki/위키백과:대문', { start: { x: 8, y: 1 }, end: { x: 11, y: 2 } }); + await resetAndHover(1, 1); + await evalLinkStateData('https://ko.wikipedia.org/wiki/위키백과:대문', { start: { x: 8, y: 1 }, end: { x: 11, y: 2 } }); + await resetAndHover(17, 1); + await evalLinkStateData('https://ko.wikipedia.org/wiki/위키백과:대문', { start: { x: 17, y: 2 }, end: { x: 19, y: 3 } }); + }); + test('name + password url after full width and combining', async () => { + await ctx.proxy.write('¥¥¥cafe\u0301 http://test:password@example.com/some_path'); + await resetAndHover(12, 0); + await evalLinkStateData('http://test:password@example.com/some_path', { start: { x: 12, y: 1 }, end: { x: 13, y: 2 } }); + await resetAndHover(5, 1); + await evalLinkStateData('http://test:password@example.com/some_path', { start: { x: 12, y: 1 }, end: { x: 13, y: 2 } }); + }); + test('url encoded params work properly', async () => { + await ctx.proxy.write('¥¥¥cafe\u0301 http://test:password@example.com/some_path?param=1%202%3'); + await resetAndHover(12, 0); + await evalLinkStateData('http://test:password@example.com/some_path?param=1%202%3', { start: { x: 12, y: 1 }, end: { x: 27, y: 2 } }); + await resetAndHover(5, 1); + await evalLinkStateData('http://test:password@example.com/some_path?param=1%202%3', { start: { x: 12, y: 1 }, end: { x: 27, y: 2 } }); + }); + }); + + // issue #4964 + test('uppercase in protocol and host, default ports', async () => { + await ctx.proxy.write( + ` HTTP://EXAMPLE.COM \r\n` + + ` HTTPS://Example.com \r\n` + + ` HTTP://Example.com:80 \r\n` + + ` HTTP://Example.com:80/staysUpper \r\n` + + ` HTTP://Ab:xY@abc.com:80/staysUpper \r\n` + ); + await pollForLinkAtCell(3, 0, `HTTP://EXAMPLE.COM`); + await pollForLinkAtCell(3, 1, `HTTPS://Example.com`); + await pollForLinkAtCell(3, 2, `HTTP://Example.com:80`); + await pollForLinkAtCell(3, 3, `HTTP://Example.com:80/staysUpper`); + await pollForLinkAtCell(3, 4, `HTTP://Ab:xY@abc.com:80/staysUpper`); + }); +}); + +async function testHostName(hostname: string): Promise { + await ctx.proxy.write( + ` http://${hostname} \r\n` + + ` http://${hostname}/a~b#c~d?e~f \r\n` + + ` http://${hostname}/colon:test \r\n` + + ` http://${hostname}/colon:test: \r\n` + + `"http://${hostname}/"\r\n` + + `\'http://${hostname}/\'\r\n` + + `http://${hostname}/subpath/+/id` + ); + await pollForLinkAtCell(3, 0, `http://${hostname}`); + await pollForLinkAtCell(3, 1, `http://${hostname}/a~b#c~d?e~f`); + await pollForLinkAtCell(3, 2, `http://${hostname}/colon:test`); + await pollForLinkAtCell(3, 3, `http://${hostname}/colon:test`); + await pollForLinkAtCell(2, 4, `http://${hostname}/`); + await pollForLinkAtCell(2, 5, `http://${hostname}/`); + await pollForLinkAtCell(1, 6, `http://${hostname}/subpath/+/id`); +} + +async function pollForLinkAtCell(col: number, row: number, value: string): Promise { + await ctx.page.mouse.move(...(await cellPos(col, row))); + await pollFor(ctx.page, `!!Array.from(document.querySelectorAll('.xterm-rows > :nth-child(${row+1}) > span[style]')).filter(el => el.style.textDecoration == 'underline').length`, true); + const text = await ctx.page.evaluate(`Array.from(document.querySelectorAll('.xterm-rows > :nth-child(${row+1}) > span[style]')).filter(el => el.style.textDecoration == 'underline').map(el => el.textContent).join('');`); + deepStrictEqual(text, value); +} + +async function resetAndHover(col: number, row: number): Promise { + await ctx.page.mouse.move(0, 0); + await ctx.page.evaluate(`window._linkStateData = {uri:''};`); + await new Promise(r => setTimeout(r, 200)); + await ctx.page.mouse.move(...(await cellPos(col, row))); + await pollFor(ctx.page, `!!window._linkStateData.uri.length`, true); +} + +async function evalLinkStateData(uri: string, range: any): Promise { + const data: ILinkStateData = await ctx.page.evaluate(`window._linkStateData`); + strictEqual(data.uri, uri); + deepStrictEqual(data.range, range); +} + +async function cellPos(col: number, row: number): Promise<[number, number]> { + const coords: any = await ctx.page.evaluate(` + (function() { + const rect = window.term.element.getBoundingClientRect(); + const dim = term._core._renderService.dimensions; + return {left: rect.left, top: rect.top, bottom: rect.bottom, right: rect.right, width: dim.css.cell.width, height: dim.css.cell.height}; + })(); + `); + return [col * coords.width + coords.left + 2, row * coords.height + coords.top + 2]; +} diff --git a/addons/addon-web-fonts/test/playwright.config.ts b/addons/addon-web-fonts/test/playwright.config.ts new file mode 100644 index 00000000..22834be1 --- /dev/null +++ b/addons/addon-web-fonts/test/playwright.config.ts @@ -0,0 +1,35 @@ +import { PlaywrightTestConfig } from '@playwright/test'; + +const config: PlaywrightTestConfig = { + testDir: '.', + timeout: 10000, + projects: [ + { + name: 'ChromeStable', + use: { + browserName: 'chromium', + channel: 'chrome' + } + }, + { + name: 'FirefoxStable', + use: { + browserName: 'firefox' + } + }, + { + name: 'WebKit', + use: { + browserName: 'webkit' + } + } + ], + reporter: 'list', + webServer: { + command: 'npm run start', + port: 3000, + timeout: 120000, + reuseExistingServer: !process.env.CI + } +}; +export default config; diff --git a/addons/addon-web-fonts/test/tsconfig.json b/addons/addon-web-fonts/test/tsconfig.json new file mode 100644 index 00000000..120fccdc --- /dev/null +++ b/addons/addon-web-fonts/test/tsconfig.json @@ -0,0 +1,42 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es2021", + "lib": [ + "es2021", + ], + // "downlevelIteration": true, + "rootDir": ".", + "outDir": "../out-test", + "sourceMap": true, + "removeComments": true, + "baseUrl": ".", + "paths": { + "common/*": [ + "../../../src/common/*" + ], + "browser/*": [ + "../../../src/browser/*" + ] + }, + "strict": true, + "types": [ + "../../../node_modules/@types/node" + ] + }, + "include": [ + "./**/*", + "../../../typings/xterm.d.ts" + ], + "references": [ + { + "path": "../../../src/common" + }, + { + "path": "../../../src/browser" + }, + { + "path": "../../../test/playwright" + } + ] +} diff --git a/addons/addon-web-fonts/tsconfig.json b/addons/addon-web-fonts/tsconfig.json new file mode 100644 index 00000000..2d820dd1 --- /dev/null +++ b/addons/addon-web-fonts/tsconfig.json @@ -0,0 +1,8 @@ +{ + "files": [], + "include": [], + "references": [ + { "path": "./src" }, + { "path": "./test" } + ] +} diff --git a/addons/addon-web-fonts/typings/addon-web-fonts.d.ts b/addons/addon-web-fonts/typings/addon-web-fonts.d.ts new file mode 100644 index 00000000..42cab9b6 --- /dev/null +++ b/addons/addon-web-fonts/typings/addon-web-fonts.d.ts @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2017 The xterm.js authors. All rights reserved. + * @license MIT + */ + + +import { Terminal, ITerminalAddon, IViewportRange } from '@xterm/xterm'; + +declare module '@xterm/addon-web-fonts' { + /** + * An xterm.js addon that enables web links. + */ + export class WebFontsAddon implements ITerminalAddon { + constructor(); + public activate(terminal: Terminal): void; + public dispose(): void; + } +} diff --git a/addons/addon-web-fonts/webpack.config.js b/addons/addon-web-fonts/webpack.config.js new file mode 100644 index 00000000..c73a60ba --- /dev/null +++ b/addons/addon-web-fonts/webpack.config.js @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +const path = require('path'); + +const addonName = 'WebFontsAddon'; +const mainFile = 'addon-web-fonts.js'; + +module.exports = { + entry: `./out/${addonName}.js`, + devtool: 'source-map', + module: { + rules: [ + { + test: /\.js$/, + use: ["source-map-loader"], + enforce: "pre", + exclude: /node_modules/ + } + ] + }, + output: { + filename: mainFile, + path: path.resolve('./lib'), + library: addonName, + libraryTarget: 'umd', + // Force usage of globalThis instead of global / self. (This is cross-env compatible) + globalObject: 'globalThis', + }, + mode: 'production' +}; diff --git a/demo/client.ts b/demo/client.ts index 49ba6743..d125bfee 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -23,6 +23,7 @@ import { FitAddon } from '@xterm/addon-fit'; import { LigaturesAddon } from '@xterm/addon-ligatures'; import { SearchAddon, ISearchOptions } from '@xterm/addon-search'; import { SerializeAddon } from '@xterm/addon-serialize'; +import { WebFontsAddon } from '@xterm/addon-web-fonts'; import { WebLinksAddon } from '@xterm/addon-web-links'; import { WebglAddon } from '@xterm/addon-webgl'; import { Unicode11Addon } from '@xterm/addon-unicode11'; @@ -37,6 +38,7 @@ export interface IWindowWithTerminal extends Window { ImageAddon?: typeof ImageAddon; // eslint-disable-line @typescript-eslint/naming-convention SearchAddon?: typeof SearchAddon; // eslint-disable-line @typescript-eslint/naming-convention SerializeAddon?: typeof SerializeAddon; // eslint-disable-line @typescript-eslint/naming-convention + WebFontsAddon?: typeof WebFontsAddon; // eslint-disable-line @typescript-eslint/naming-convention WebLinksAddon?: typeof WebLinksAddon; // eslint-disable-line @typescript-eslint/naming-convention WebglAddon?: typeof WebglAddon; // eslint-disable-line @typescript-eslint/naming-convention Unicode11Addon?: typeof Unicode11Addon; // eslint-disable-line @typescript-eslint/naming-convention @@ -52,7 +54,7 @@ let socket; let pid; let autoResize: boolean = true; -type AddonType = 'attach' | 'clipboard' | 'fit' | 'image' | 'search' | 'serialize' | 'unicode11' | 'unicodeGraphemes' | 'webLinks' | 'webgl' | 'ligatures'; +type AddonType = 'attach' | 'clipboard' | 'fit' | 'image' | 'search' | 'serialize' | 'unicode11' | 'unicodeGraphemes' | 'webFonts' | 'webLinks' | 'webgl' | 'ligatures'; interface IDemoAddon { name: T; @@ -65,11 +67,12 @@ interface IDemoAddon { T extends 'ligatures' ? typeof LigaturesAddon : T extends 'search' ? typeof SearchAddon : T extends 'serialize' ? typeof SerializeAddon : - T extends 'webLinks' ? typeof WebLinksAddon : - T extends 'unicode11' ? typeof Unicode11Addon : - T extends 'unicodeGraphemes' ? typeof UnicodeGraphemesAddon : - T extends 'webgl' ? typeof WebglAddon : - never + T extends 'webFonts' ? typeof WebFontsAddon : + T extends 'webLinks' ? typeof WebLinksAddon : + T extends 'unicode11' ? typeof Unicode11Addon : + T extends 'unicodeGraphemes' ? typeof UnicodeGraphemesAddon : + T extends 'webgl' ? typeof WebglAddon : + never ); instance?: ( T extends 'attach' ? AttachAddon : @@ -79,11 +82,12 @@ interface IDemoAddon { T extends 'ligatures' ? LigaturesAddon : T extends 'search' ? SearchAddon : T extends 'serialize' ? SerializeAddon : - T extends 'webLinks' ? WebLinksAddon : - T extends 'unicode11' ? Unicode11Addon : - T extends 'unicodeGraphemes' ? UnicodeGraphemesAddon : - T extends 'webgl' ? WebglAddon : - never + T extends 'webFonts' ? WebFontsAddon : + T extends 'webLinks' ? WebLinksAddon : + T extends 'unicode11' ? Unicode11Addon : + T extends 'unicodeGraphemes' ? UnicodeGraphemesAddon : + T extends 'webgl' ? WebglAddon : + never ); } @@ -94,6 +98,7 @@ const addons: { [T in AddonType]: IDemoAddon } = { image: { name: 'image', ctor: ImageAddon, canChange: true }, search: { name: 'search', ctor: SearchAddon, canChange: true }, serialize: { name: 'serialize', ctor: SerializeAddon, canChange: true }, + webFonts: { name: 'webFonts', ctor: WebFontsAddon, canChange: true }, webLinks: { name: 'webLinks', ctor: WebLinksAddon, canChange: true }, webgl: { name: 'webgl', ctor: WebglAddon, canChange: true }, unicode11: { name: 'unicode11', ctor: Unicode11Addon, canChange: true }, @@ -169,6 +174,7 @@ const disposeRecreateButtonHandler: () => void = () => { addons.unicode11.instance = undefined; addons.unicodeGraphemes.instance = undefined; addons.ligatures.instance = undefined; + addons.webFonts.instance = undefined; addons.webLinks.instance = undefined; addons.webgl.instance = undefined; document.getElementById('dispose').innerHTML = 'Recreate Terminal'; @@ -218,6 +224,7 @@ if (document.location.pathname === '/test') { window.Unicode11Addon = Unicode11Addon; window.UnicodeGraphemesAddon = UnicodeGraphemesAddon; window.LigaturesAddon = LigaturesAddon; + window.WebFontsAddon = WebFontsAddon; window.WebLinksAddon = WebLinksAddon; window.WebglAddon = WebglAddon; } else { @@ -261,7 +268,7 @@ function createTerminal(): void { backend: 'conpty', buildNumber: 22621 } : undefined, - fontFamily: '"Fira Code", courier-new, courier, monospace, "Powerline Extra Symbols"', + fontFamily: '"Roboto Mono", "Fira Code", courier-new, courier, monospace, "Powerline Extra Symbols"', theme: xtermjsTheme } as ITerminalOptions); @@ -278,12 +285,14 @@ function createTerminal(): void { } catch (e) { console.warn(e); } + addons.webFonts.instance = new WebFontsAddon(); addons.webLinks.instance = new WebLinksAddon(); typedTerm.loadAddon(addons.fit.instance); typedTerm.loadAddon(addons.image.instance); typedTerm.loadAddon(addons.search.instance); typedTerm.loadAddon(addons.serialize.instance); typedTerm.loadAddon(addons.unicodeGraphemes.instance); + typedTerm.loadAddon(addons.webFonts.instance); typedTerm.loadAddon(addons.webLinks.instance); typedTerm.loadAddon(addons.clipboard.instance); diff --git a/demo/style.css b/demo/style.css index 1fb8f7ac..3e32b42f 100644 --- a/demo/style.css +++ b/demo/style.css @@ -1,3 +1,115 @@ +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 100 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3CWWoKC.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 100 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3mWWoKC.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 100 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm36WWoKC.woff2) format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 100 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3KWWoKC.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 100 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3OWWoKC.woff2) format('woff2'); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 100 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm32WWg.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 100 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhGq3-OXg.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 100 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhPq3-OXg.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 100 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhIq3-OXg.woff2) format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 100 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhEq3-OXg.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 100 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhFq3-OXg.woff2) format('woff2'); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 100 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhLq38.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + + + + body { font-family: helvetica, sans-serif, arial; font-size: 1em; diff --git a/demo/tsconfig.json b/demo/tsconfig.json index 5569bd1d..db8939fa 100644 --- a/demo/tsconfig.json +++ b/demo/tsconfig.json @@ -12,6 +12,7 @@ "@xterm/addon-image": ["../addons/addon-image"], "@xterm/addon-search": ["../addons/addon-search"], "@xterm/addon-serialize": ["../addons/addon-serialize"], + "@xterm/addon-web-fonts": ["../addons/addon-web-fonts"], "@xterm/addon-web-links": ["../addons/addon-web-links"], "@xterm/addon-webgl": ["../addons/addon-webgl"], "@xterm/addon-unicode11": ["../addons/addon-unicode11"], diff --git a/tsconfig.all.json b/tsconfig.all.json index 7ca3b7a7..9bc8896e 100644 --- a/tsconfig.all.json +++ b/tsconfig.all.json @@ -15,6 +15,7 @@ { "path": "./addons/addon-serialize" }, { "path": "./addons/addon-unicode11" }, { "path": "./addons/addon-unicode-graphemes" }, + { "path": "./addons/addon-web-fonts" }, { "path": "./addons/addon-web-links" }, { "path": "./addons/addon-webgl" } ] From 41640d94022b21710689de95fcce65f770884b46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Thu, 3 Oct 2024 22:07:10 +0200 Subject: [PATCH 02/38] make linter happy --- addons/addon-web-fonts/src/WebFontsAddon.ts | 25 +++++++++++---------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/addons/addon-web-fonts/src/WebFontsAddon.ts b/addons/addon-web-fonts/src/WebFontsAddon.ts index 91800abd..3cce5092 100644 --- a/addons/addon-web-fonts/src/WebFontsAddon.ts +++ b/addons/addon-web-fonts/src/WebFontsAddon.ts @@ -12,7 +12,7 @@ import type { WebFontsAddon as IWebFontsApi } from '@xterm/addon-web-fonts'; */ function unquote(s: string): string { if (s[0] === '"' && s[s.length - 1] === '"') return s.slice(1, -1); - if (s[0] === "'" && s[s.length - 1] === "'") return s.slice(1, -1); + if (s[0] === '\'' && s[s.length - 1] === '\'') return s.slice(1, -1); return s; } @@ -41,12 +41,12 @@ function createFamily(families: string[]): string { function _loadFonts(fonts?: (string | FontFace)[]): Promise { - let ffs = Array.from(document.fonts); + const ffs = Array.from(document.fonts); if (!fonts || !fonts.length) { return Promise.all(ffs.map(ff => ff.load())); } let toLoad: FontFace[] = []; - let ffsHashed = ffs.map(ff => WebFontsAddon.hashFontFace(ff)); + const ffsHashed = ffs.map(ff => WebFontsAddon.hashFontFace(ff)); for (const font of fonts) { if (font instanceof FontFace) { const fontHashed = WebFontsAddon.hashFontFace(font); @@ -85,14 +85,14 @@ export class WebFontsAddon implements ITerminalAddon, IWebFontsApi { /** * Force a terminal re-layout by altering `options.FontFamily`. - * + * * Found webfonts in `fontFamily` are temporarily removed until the webfont * resources are fully loaded. - * + * * This method is meant as a fallback fix for sloppy integrations, * that wrongly placed a webfont at the terminal contructor options. * It is likely to lead to terminal flickering in all browsers (FOUT). - * + * * To avoid triggering this fallback in your integration, make sure to have * the needed webfonts loaded at the time `terminal.open` is called. */ @@ -102,8 +102,9 @@ export class WebFontsAddon implements ITerminalAddon, IWebFontsApi { const webFamilies = WebFontsAddon.getFontFamilies(); const dirty: string[] = []; const clean: string[] = []; - for (const fam of families) + for (const fam of families) { (webFamilies.indexOf(fam) !== -1 ? dirty : clean).push(fam); + } if (dirty.length) { _loadFonts(dirty).then(() => { terminal.options.fontFamily = clean.length ? createFamily(clean) : 'monospace'; @@ -123,8 +124,8 @@ export class WebFontsAddon implements ITerminalAddon, IWebFontsApi { ff.stretch, ff.style, ff.unicodeRange, - ff.weight, - ]) + ff.weight + ]); } /** @@ -145,7 +146,7 @@ export class WebFontsAddon implements ITerminalAddon, IWebFontsApi { * matching the family names * - fontface objects : loads given fontfaces and adds them to * `document.fonts` - * + * * The returned promise will resolve, when all loading is done. */ public static loadFonts(fonts?: (string | FontFace)[]): Promise { @@ -159,7 +160,7 @@ export class WebFontsAddon implements ITerminalAddon, IWebFontsApi { - +/* eslint-disable */ // TODO: place into test cases /* (window as any).__roboto = [ @@ -296,4 +297,4 @@ export class WebFontsAddon implements ITerminalAddon, IWebFontsApi { } ), ]; -*/ \ No newline at end of file +*/ From 15a63bae228eb8f17d72e5c8db43c2b819298673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Thu, 3 Oct 2024 22:10:40 +0200 Subject: [PATCH 03/38] change to global replacement --- addons/addon-web-fonts/src/WebFontsAddon.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/addon-web-fonts/src/WebFontsAddon.ts b/addons/addon-web-fonts/src/WebFontsAddon.ts index 3cce5092..e3132602 100644 --- a/addons/addon-web-fonts/src/WebFontsAddon.ts +++ b/addons/addon-web-fonts/src/WebFontsAddon.ts @@ -25,7 +25,7 @@ function quote(s: string): string { const pos = s.match(/([-_a-zA-Z0-9\xA0-\u{10FFFF}]+)/u); const neg = s.match(/^(-?\d|--)/m); if (!neg && pos && pos[1] === s) return s; - return `"${s.replace('"', '\\"')}"`; + return `"${s.replace(/"/g, '\\"')}"`; } From 9a536f758d5258ac690d5f96fa35f491759c132c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Thu, 3 Oct 2024 22:23:31 +0200 Subject: [PATCH 04/38] fix integration test runner --- bin/esbuild.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/esbuild.mjs b/bin/esbuild.mjs index 9ed69eda..95b3856e 100644 --- a/bin/esbuild.mjs +++ b/bin/esbuild.mjs @@ -138,6 +138,7 @@ if (config.addon) { "@xterm/addon-image": "./addons/addon-image/lib/addon-image.mjs", "@xterm/addon-search": "./addons/addon-search/lib/addon-search.mjs", "@xterm/addon-serialize": "./addons/addon-serialize/lib/addon-serialize.mjs", + "@xterm/addon-web-fonts": "./addons/addon-web-fonts/lib/addon-web-fonts.mjs", "@xterm/addon-web-links": "./addons/addon-web-links/lib/addon-web-links.mjs", "@xterm/addon-webgl": "./addons/addon-webgl/lib/addon-webgl.mjs", "@xterm/addon-unicode11": "./addons/addon-unicode11/lib/addon-unicode11.mjs", From 809b9aa1ad0ba1083cba9935cdc0fd73fa743574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Fri, 4 Oct 2024 00:15:49 +0200 Subject: [PATCH 05/38] fix test runner --- .github/workflows/ci.yml | 3 + .../test/WebFontsAddon.test.ts | 19 ++ .../test/WebLinksAddon.test.ts | 185 ------------------ bin/test_integration.js | 1 + 4 files changed, 23 insertions(+), 185 deletions(-) create mode 100644 addons/addon-web-fonts/test/WebFontsAddon.test.ts delete mode 100644 addons/addon-web-fonts/test/WebLinksAddon.test.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d2f39e6b..eb9066d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,6 +59,9 @@ jobs: ./addons/addon-web-links/lib/* \ ./addons/addon-web-links/out/* \ ./addons/addon-web-links/out-*/* \ + ./addons/addon-web-fonts/lib/* \ + ./addons/addon-web-fonts/out/* \ + ./addons/addon-web-fonts/out-*/* \ ./addons/addon-webgl/lib/* \ ./addons/addon-webgl/out/* \ ./addons/addon-webgl/out-*st/* diff --git a/addons/addon-web-fonts/test/WebFontsAddon.test.ts b/addons/addon-web-fonts/test/WebFontsAddon.test.ts new file mode 100644 index 00000000..09bb01a9 --- /dev/null +++ b/addons/addon-web-fonts/test/WebFontsAddon.test.ts @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2024 The xterm.js authors. All rights reserved. + * @license MIT + */ +import test from '@playwright/test'; +import { deepStrictEqual, strictEqual } from 'assert'; +import { ITestContext, createTestContext, openTerminal, pollFor, timeout } from '../../../test/playwright/TestUtils'; + + +let ctx: ITestContext; +test.beforeAll(async ({ browser }) => { + ctx = await createTestContext(browser); + await openTerminal(ctx, { cols: 40 }); +}); +test.afterAll(async () => await ctx.page.close()); + +test.describe('WebFontsAddon', () => { + test('nothing', () => {}); +}); diff --git a/addons/addon-web-fonts/test/WebLinksAddon.test.ts b/addons/addon-web-fonts/test/WebLinksAddon.test.ts deleted file mode 100644 index 8682c533..00000000 --- a/addons/addon-web-fonts/test/WebLinksAddon.test.ts +++ /dev/null @@ -1,185 +0,0 @@ -/** - * Copyright (c) 2019 The xterm.js authors. All rights reserved. - * @license MIT - */ -import test from '@playwright/test'; -import { deepStrictEqual, strictEqual } from 'assert'; -import { readFile } from 'fs'; -import { resolve } from 'path'; -import { ITestContext, createTestContext, openTerminal, pollFor, timeout } from '../../../test/playwright/TestUtils'; - -interface ILinkStateData { - uri?: string; - range?: { - start: { - x: number; - y: number; - }; - end: { - x: number; - y: number; - }; - }; -} - - -let ctx: ITestContext; -test.beforeAll(async ({ browser }) => { - ctx = await createTestContext(browser); - await openTerminal(ctx, { cols: 40 }); -}); -test.afterAll(async () => await ctx.page.close()); - -test.describe('WebLinksAddon', () => { - - test.beforeEach(async () => { - await ctx.page.evaluate(` - window.term.reset() - window._linkaddon?.dispose(); - window._linkaddon = new WebLinksAddon(); - window.term.loadAddon(window._linkaddon); - `); - }); - - const countryTlds = [ - '.ac', '.ad', '.ae', '.af', '.ag', '.ai', '.al', '.am', '.ao', '.aq', '.ar', '.as', '.at', - '.au', '.aw', '.ax', '.az', '.ba', '.bb', '.bd', '.be', '.bf', '.bg', '.bh', '.bi', '.bj', - '.bm', '.bn', '.bo', '.bq', '.br', '.bs', '.bt', '.bw', '.by', '.bz', '.ca', '.cc', '.cd', - '.cf', '.cg', '.ch', '.ci', '.ck', '.cl', '.cm', '.cn', '.co', '.cr', '.cu', '.cv', '.cw', - '.cx', '.cy', '.cz', '.de', '.dj', '.dk', '.dm', '.do', '.dz', '.ec', '.ee', '.eg', '.eh', - '.er', '.es', '.et', '.eu', '.fi', '.fj', '.fk', '.fm', '.fo', '.fr', '.ga', '.gd', '.ge', - '.gf', '.gg', '.gh', '.gi', '.gl', '.gm', '.gn', '.gp', '.gq', '.gr', '.gs', '.gt', '.gu', - '.gw', '.gy', '.hk', '.hm', '.hn', '.hr', '.ht', '.hu', '.id', '.ie', '.il', '.im', '.in', - '.io', '.iq', '.ir', '.is', '.it', '.je', '.jm', '.jo', '.jp', '.ke', '.kg', '.kh', '.ki', - '.km', '.kn', '.kp', '.kr', '.kw', '.ky', '.kz', '.la', '.lb', '.lc', '.li', '.lk', '.lr', - '.ls', '.lt', '.lu', '.lv', '.ly', '.ma', '.mc', '.md', '.me', '.mg', '.mh', '.mk', '.ml', - '.mm', '.mn', '.mo', '.mp', '.mq', '.mr', '.ms', '.mt', '.mu', '.mv', '.mw', '.mx', '.my', - '.mz', '.na', '.nc', '.ne', '.nf', '.ng', '.ni', '.nl', '.no', '.np', '.nr', '.nu', '.nz', - '.om', '.pa', '.pe', '.pf', '.pg', '.ph', '.pk', '.pl', '.pm', '.pn', '.pr', '.ps', '.pt', - '.pw', '.py', '.qa', '.re', '.ro', '.rs', '.ru', '.rw', '.sa', '.sb', '.sc', '.sd', '.se', - '.sg', '.sh', '.si', '.sk', '.sl', '.sm', '.sn', '.so', '.sr', '.ss', '.st', '.su', '.sv', - '.sx', '.sy', '.sz', '.tc', '.td', '.tf', '.tg', '.th', '.tj', '.tk', '.tl', '.tm', '.tn', - '.to', '.tr', '.tt', '.tv', '.tw', '.tz', '.ua', '.ug', '.uk', '.us', '.uy', '.uz', '.va', - '.vc', '.ve', '.vg', '.vi', '.vn', '.vu', '.wf', '.ws', '.ye', '.yt', '.za', '.zm', '.zw' - ]; - for (const tld of countryTlds) { - test(tld, async () => await testHostName(`foo${tld}`)); - } - test(`.com`, async () => await testHostName(`foo.com`)); - for (const tld of countryTlds) { - test(`.com${tld}`, async () => await testHostName(`foo.com${tld}`)); - } - - test.describe('correct buffer offsets & uri', () => { - test.beforeEach(async () => { - await ctx.page.evaluate(` - window._linkStateData = {uri:''}; - window._linkaddon._options.hover = (event, uri, range) => { window._linkStateData = { uri, range }; }; - `); - }); - test('all half width', async () => { - await ctx.proxy.write('aaa http://example.com aaa http://example.com aaa'); - await resetAndHover(5, 0); - await evalLinkStateData('http://example.com', { start: { x: 5, y: 1 }, end: { x: 22, y: 1 } }); - await resetAndHover(1, 1); - await evalLinkStateData('http://example.com', { start: { x: 28, y: 1 }, end: { x: 5, y: 2 } }); - }); - test('url after full width', async () => { - await ctx.proxy.write('¥¥¥ http://example.com ¥¥¥ http://example.com aaa'); - await resetAndHover(8, 0); - await evalLinkStateData('http://example.com', { start: { x: 8, y: 1 }, end: { x: 25, y: 1 } }); - await resetAndHover(1, 1); - await evalLinkStateData('http://example.com', { start: { x: 34, y: 1 }, end: { x: 11, y: 2 } }); - }); - test('full width within url and before', async () => { - await ctx.proxy.write('¥¥¥ https://ko.wikipedia.org/wiki/위키백과:대문 aaa https://ko.wikipedia.org/wiki/위키백과:대문 ¥¥¥'); - await resetAndHover(8, 0); - await evalLinkStateData('https://ko.wikipedia.org/wiki/위키백과:대문', { start: { x: 8, y: 1 }, end: { x: 11, y: 2 } }); - await resetAndHover(1, 1); - await evalLinkStateData('https://ko.wikipedia.org/wiki/위키백과:대문', { start: { x: 8, y: 1 }, end: { x: 11, y: 2 } }); - await resetAndHover(17, 1); - await evalLinkStateData('https://ko.wikipedia.org/wiki/위키백과:대문', { start: { x: 17, y: 2 }, end: { x: 19, y: 3 } }); - }); - test('name + password url after full width and combining', async () => { - await ctx.proxy.write('¥¥¥cafe\u0301 http://test:password@example.com/some_path'); - await resetAndHover(12, 0); - await evalLinkStateData('http://test:password@example.com/some_path', { start: { x: 12, y: 1 }, end: { x: 13, y: 2 } }); - await resetAndHover(5, 1); - await evalLinkStateData('http://test:password@example.com/some_path', { start: { x: 12, y: 1 }, end: { x: 13, y: 2 } }); - }); - test('url encoded params work properly', async () => { - await ctx.proxy.write('¥¥¥cafe\u0301 http://test:password@example.com/some_path?param=1%202%3'); - await resetAndHover(12, 0); - await evalLinkStateData('http://test:password@example.com/some_path?param=1%202%3', { start: { x: 12, y: 1 }, end: { x: 27, y: 2 } }); - await resetAndHover(5, 1); - await evalLinkStateData('http://test:password@example.com/some_path?param=1%202%3', { start: { x: 12, y: 1 }, end: { x: 27, y: 2 } }); - }); - }); - - // issue #4964 - test('uppercase in protocol and host, default ports', async () => { - await ctx.proxy.write( - ` HTTP://EXAMPLE.COM \r\n` + - ` HTTPS://Example.com \r\n` + - ` HTTP://Example.com:80 \r\n` + - ` HTTP://Example.com:80/staysUpper \r\n` + - ` HTTP://Ab:xY@abc.com:80/staysUpper \r\n` - ); - await pollForLinkAtCell(3, 0, `HTTP://EXAMPLE.COM`); - await pollForLinkAtCell(3, 1, `HTTPS://Example.com`); - await pollForLinkAtCell(3, 2, `HTTP://Example.com:80`); - await pollForLinkAtCell(3, 3, `HTTP://Example.com:80/staysUpper`); - await pollForLinkAtCell(3, 4, `HTTP://Ab:xY@abc.com:80/staysUpper`); - }); -}); - -async function testHostName(hostname: string): Promise { - await ctx.proxy.write( - ` http://${hostname} \r\n` + - ` http://${hostname}/a~b#c~d?e~f \r\n` + - ` http://${hostname}/colon:test \r\n` + - ` http://${hostname}/colon:test: \r\n` + - `"http://${hostname}/"\r\n` + - `\'http://${hostname}/\'\r\n` + - `http://${hostname}/subpath/+/id` - ); - await pollForLinkAtCell(3, 0, `http://${hostname}`); - await pollForLinkAtCell(3, 1, `http://${hostname}/a~b#c~d?e~f`); - await pollForLinkAtCell(3, 2, `http://${hostname}/colon:test`); - await pollForLinkAtCell(3, 3, `http://${hostname}/colon:test`); - await pollForLinkAtCell(2, 4, `http://${hostname}/`); - await pollForLinkAtCell(2, 5, `http://${hostname}/`); - await pollForLinkAtCell(1, 6, `http://${hostname}/subpath/+/id`); -} - -async function pollForLinkAtCell(col: number, row: number, value: string): Promise { - await ctx.page.mouse.move(...(await cellPos(col, row))); - await pollFor(ctx.page, `!!Array.from(document.querySelectorAll('.xterm-rows > :nth-child(${row+1}) > span[style]')).filter(el => el.style.textDecoration == 'underline').length`, true); - const text = await ctx.page.evaluate(`Array.from(document.querySelectorAll('.xterm-rows > :nth-child(${row+1}) > span[style]')).filter(el => el.style.textDecoration == 'underline').map(el => el.textContent).join('');`); - deepStrictEqual(text, value); -} - -async function resetAndHover(col: number, row: number): Promise { - await ctx.page.mouse.move(0, 0); - await ctx.page.evaluate(`window._linkStateData = {uri:''};`); - await new Promise(r => setTimeout(r, 200)); - await ctx.page.mouse.move(...(await cellPos(col, row))); - await pollFor(ctx.page, `!!window._linkStateData.uri.length`, true); -} - -async function evalLinkStateData(uri: string, range: any): Promise { - const data: ILinkStateData = await ctx.page.evaluate(`window._linkStateData`); - strictEqual(data.uri, uri); - deepStrictEqual(data.range, range); -} - -async function cellPos(col: number, row: number): Promise<[number, number]> { - const coords: any = await ctx.page.evaluate(` - (function() { - const rect = window.term.element.getBoundingClientRect(); - const dim = term._core._renderService.dimensions; - return {left: rect.left, top: rect.top, bottom: rect.bottom, right: rect.right, width: dim.css.cell.width, height: dim.css.cell.height}; - })(); - `); - return [col * coords.width + coords.left + 2, row * coords.height + coords.top + 2]; -} diff --git a/bin/test_integration.js b/bin/test_integration.js index 936467cc..b7773e57 100644 --- a/bin/test_integration.js +++ b/bin/test_integration.js @@ -29,6 +29,7 @@ const addons = [ 'serialize', 'unicode-graphemes', 'unicode11', + 'web-fonts', 'web-links', 'webgl', ]; From b104bcd144b2816fa1bc7b464a502c4713a119f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Fri, 4 Oct 2024 00:42:38 +0200 Subject: [PATCH 06/38] remove await remnants --- addons/addon-web-fonts/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/addon-web-fonts/README.md b/addons/addon-web-fonts/README.md index bf506cf1..bd1dedf4 100644 --- a/addons/addon-web-fonts/README.md +++ b/addons/addon-web-fonts/README.md @@ -72,7 +72,7 @@ const webFontsAddon = new WebFontsAddon(); terminal.loadAddon(webFontsAddon); // wait for webfonts to be fully loaded -await WebFontsAddon.loadFonts(['Web Mono 1', 'Super Powerline']).then(() => { +WebFontsAddon.loadFonts(['Web Mono 1', 'Super Powerline']).then(() => { terminal.open(your_terminal_div_element); // more boostrapping goes here ... }); @@ -112,7 +112,7 @@ That can be achieved like this: const ff1 = new FontFace('New Web Mono', url1, ...); const ff2 = new FontFace('New Web Mono', url2, ...); // and await their loading -await WebFontsAddon.loadFonts([ff1, ff2]).then(() => { +WebFontsAddon.loadFonts([ff1, ff2]).then(() => { // apply new webfont to terminal terminal.options.fontFamily = 'New Web Mono'; // since the new font might have slighly different metrics, @@ -124,7 +124,7 @@ await WebFontsAddon.loadFonts([ff1, ff2]).then(() => { document.styleSheets[0].insertRule( "@font-face { font-family: 'New Web Mono'; src: url(newfont.woff); }", 0); // and await the new font family name -await WebFontsAddon.loadFonts(['New Web Mono']).then(() => { +WebFontsAddon.loadFonts(['New Web Mono']).then(() => { // apply new webfont to terminal terminal.options.fontFamily = 'New Web Mono'; // since the new font might have slighly different metrics, From 23bef9913290707e65323ad1ee620ebe0c310e5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Fri, 4 Oct 2024 02:06:02 +0200 Subject: [PATCH 07/38] cleanup API --- .github/workflows/ci.yml | 2 + addons/addon-web-fonts/README.md | 12 +- addons/addon-web-fonts/src/WebFontsAddon.ts | 136 +++++++++--------- .../typings/addon-web-fonts.d.ts | 55 ++++++- 4 files changed, 127 insertions(+), 78 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb9066d9..f7616b7c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -223,6 +223,8 @@ jobs: run: yarn test-integration-${{ matrix.browser }} --workers=50% --forbid-only --suite=addon-unicode-graphemes - name: Integration tests (addon-unicode11) run: yarn test-integration-${{ matrix.browser }} --workers=50% --forbid-only --suite=addon-unicode11 + - name: Integration tests (addon-web-fonts) + run: yarn test-integration-${{ matrix.browser }} --workers=50% --forbid-only --suite=addon-web-fonts - name: Integration tests (addon-web-links) run: yarn test-integration-${{ matrix.browser }} --workers=50% --forbid-only --suite=addon-web-links - name: Integration tests (addon-webgl) diff --git a/addons/addon-web-fonts/README.md b/addons/addon-web-fonts/README.md index bd1dedf4..e910c21e 100644 --- a/addons/addon-web-fonts/README.md +++ b/addons/addon-web-fonts/README.md @@ -72,7 +72,7 @@ const webFontsAddon = new WebFontsAddon(); terminal.loadAddon(webFontsAddon); // wait for webfonts to be fully loaded -WebFontsAddon.loadFonts(['Web Mono 1', 'Super Powerline']).then(() => { +webFontsAddon.loadFonts(['Web Mono 1', 'Super Powerline']).then(() => { terminal.open(your_terminal_div_element); // more boostrapping goes here ... }); @@ -83,13 +83,13 @@ on the initial document load (more precise - by the time this code runs). Please note, that this code cannot run synchronous anymore, so you will have to split your bootstrapping code into several stages. If thats too much of a hassle, you can also move the whole -bootstrapping under that waiting condition (`loadFonts` is actually a static method): +bootstrapping under that waiting condition (import `loadFonts` for a static variant): ```typescript import { Terminal } from '@xterm/xterm'; import { XYAddon } from '@xterm/addon-xy'; -import { WebFontsAddon } from '@xterm/addon-web-fonts'; +import { WebFontsAddon, loadFonts } from '@xterm/addon-web-fonts'; -WebFontsAddon.loadFonts(['Web Mono 1', 'Super Powerline']).then(() => { +loadFonts(['Web Mono 1', 'Super Powerline']).then(() => { // create a `Terminal` instance, now with webfonts const terminal = new Terminal({fontFamily: '"Web Mono 1", "Super Powerline", monospace'}); const xyAddon = new XYAddon(); @@ -112,7 +112,7 @@ That can be achieved like this: const ff1 = new FontFace('New Web Mono', url1, ...); const ff2 = new FontFace('New Web Mono', url2, ...); // and await their loading -WebFontsAddon.loadFonts([ff1, ff2]).then(() => { +loadFonts([ff1, ff2]).then(() => { // apply new webfont to terminal terminal.options.fontFamily = 'New Web Mono'; // since the new font might have slighly different metrics, @@ -124,7 +124,7 @@ WebFontsAddon.loadFonts([ff1, ff2]).then(() => { document.styleSheets[0].insertRule( "@font-face { font-family: 'New Web Mono'; src: url(newfont.woff); }", 0); // and await the new font family name -WebFontsAddon.loadFonts(['New Web Mono']).then(() => { +loadFonts(['New Web Mono']).then(() => { // apply new webfont to terminal terminal.options.fontFamily = 'New Web Mono'; // since the new font might have slighly different metrics, diff --git a/addons/addon-web-fonts/src/WebFontsAddon.ts b/addons/addon-web-fonts/src/WebFontsAddon.ts index e3132602..219a64af 100644 --- a/addons/addon-web-fonts/src/WebFontsAddon.ts +++ b/addons/addon-web-fonts/src/WebFontsAddon.ts @@ -8,7 +8,7 @@ import type { WebFontsAddon as IWebFontsApi } from '@xterm/addon-web-fonts'; /** - * Unquote family name. + * Unquote a font family name. */ function unquote(s: string): string { if (s[0] === '"' && s[s.length - 1] === '"') return s.slice(1, -1); @@ -18,7 +18,7 @@ function unquote(s: string): string { /** - * Quote family name. + * Quote a font family name conditionally. * @see https://mathiasbynens.be/notes/unquoted-font-family */ function quote(s: string): string { @@ -40,16 +40,46 @@ function createFamily(families: string[]): string { } +/** + * Hash a font face from it properties. + * Used in `loadFonts` to avoid bloating + * `document.fonts` from multiple calls. + */ +function hashFontFace(ff: FontFace): string { + return JSON.stringify([ + unquote(ff.family), + ff.stretch, + ff.style, + ff.unicodeRange, + ff.weight + ]); +} + + +/** + * Wait for webfont resources to be loaded. + * + * Without any argument, all fonts currently listed in + * `document.fonts` will be loaded. + * For a more fine-grained loading strategy you can populate + * the `fonts` argument with: + * - font families : loads all fontfaces in `document.fonts` + * matching the family names + * - fontface objects : loads given fontfaces and adds them to + * `document.fonts` + * + * The returned promise will resolve, when all loading is done. + */ function _loadFonts(fonts?: (string | FontFace)[]): Promise { const ffs = Array.from(document.fonts); if (!fonts || !fonts.length) { return Promise.all(ffs.map(ff => ff.load())); } let toLoad: FontFace[] = []; - const ffsHashed = ffs.map(ff => WebFontsAddon.hashFontFace(ff)); + const ffsHashed = ffs.map(ff => hashFontFace(ff)); for (const font of fonts) { if (font instanceof FontFace) { - const fontHashed = WebFontsAddon.hashFontFace(font); + const fontHashed = hashFontFace(font); const idx = ffsHashed.indexOf(fontHashed); if (idx === -1) { document.fonts.add(font); @@ -72,85 +102,53 @@ function _loadFonts(fonts?: (string | FontFace)[]): Promise { } +export async function loadFonts(fonts?: (string | FontFace)[]): Promise { + await document.fonts.ready; + return _loadFonts(fonts); +} + export class WebFontsAddon implements ITerminalAddon, IWebFontsApi { - constructor(public forceInitialRelayout: boolean = true) { } - public dispose(): void { } + private _term: Terminal | undefined; - public activate(terminal: Terminal): void { + constructor(public forceInitialRelayout: boolean = true) { } + + public dispose(): void { + this._term = undefined; + } + + public activate(term: Terminal): void { + this._term = term; if (this.forceInitialRelayout) { - document.fonts.ready.then(() => this.relayout(terminal)); + document.fonts.ready.then(() => this.relayout()); } } - /** - * Force a terminal re-layout by altering `options.FontFamily`. - * - * Found webfonts in `fontFamily` are temporarily removed until the webfont - * resources are fully loaded. - * - * This method is meant as a fallback fix for sloppy integrations, - * that wrongly placed a webfont at the terminal contructor options. - * It is likely to lead to terminal flickering in all browsers (FOUT). - * - * To avoid triggering this fallback in your integration, make sure to have - * the needed webfonts loaded at the time `terminal.open` is called. - */ - public relayout(terminal: Terminal): void { - const family = terminal.options.fontFamily; + public async loadFonts(fonts?: (string | FontFace)[]): Promise { + return loadFonts(fonts); + } + + public async relayout(): Promise { + if (!this._term) { + return; + } + await document.fonts.ready; + const family = this._term.options.fontFamily; const families = splitFamily(family); - const webFamilies = WebFontsAddon.getFontFamilies(); + const webFamilies = Array.from(new Set(Array.from(document.fonts).map(e => unquote(e.family)))); const dirty: string[] = []; const clean: string[] = []; for (const fam of families) { (webFamilies.indexOf(fam) !== -1 ? dirty : clean).push(fam); } - if (dirty.length) { - _loadFonts(dirty).then(() => { - terminal.options.fontFamily = clean.length ? createFamily(clean) : 'monospace'; - terminal.options.fontFamily = family; - }); + if (!dirty.length) { + return; + } + await _loadFonts(dirty); + if (this._term) { + this._term.options.fontFamily = clean.length ? createFamily(clean) : 'monospace'; + this._term.options.fontFamily = family; } - } - - /** - * Hash a font face from it properties. - * Used in `loadFonts` to avoid bloating - * `document.fonts` from multiple calls. - */ - public static hashFontFace(ff: FontFace): string { - return JSON.stringify([ - unquote(ff.family), - ff.stretch, - ff.style, - ff.unicodeRange, - ff.weight - ]); - } - - /** - * Return font families known in `document.fonts`. - */ - public static getFontFamilies(): string[] { - return Array.from(new Set(Array.from(document.fonts).map(e => unquote(e.family)))); - } - - /** - * Wait for webfont resources to be loaded. - * - * Without any argument, all fonts currently listed in - * `document.fonts` will be loaded. - * For a more fine-grained loading strategy you can populate - * the `fonts` argument with: - * - font families : loads all fontfaces in `document.fonts` - * matching the family names - * - fontface objects : loads given fontfaces and adds them to - * `document.fonts` - * - * The returned promise will resolve, when all loading is done. - */ - public static loadFonts(fonts?: (string | FontFace)[]): Promise { - return document.fonts.ready.then(() => _loadFonts(fonts)); } } diff --git a/addons/addon-web-fonts/typings/addon-web-fonts.d.ts b/addons/addon-web-fonts/typings/addon-web-fonts.d.ts index 42cab9b6..c353a421 100644 --- a/addons/addon-web-fonts/typings/addon-web-fonts.d.ts +++ b/addons/addon-web-fonts/typings/addon-web-fonts.d.ts @@ -4,15 +4,64 @@ */ -import { Terminal, ITerminalAddon, IViewportRange } from '@xterm/xterm'; +import { Terminal, ITerminalAddon } from '@xterm/xterm'; declare module '@xterm/addon-web-fonts' { + /** - * An xterm.js addon that enables web links. + * Addon to use webfonts in xterm.js */ export class WebFontsAddon implements ITerminalAddon { - constructor(); + /** + * @param forceInitialRelayout Force an initial relayout, if a webfont was found. + */ + constructor(forceInitialRelayout?: boolean); public activate(terminal: Terminal): void; public dispose(): void; + + /** + * Wait for webfont resources to be loaded. + * + * Without any argument, all fonts currently listed in + * `document.fonts` will be loaded. + * For a more fine-grained loading strategy you can populate + * the `fonts` argument with: + * - font families : loads all fontfaces in `document.fonts` + * matching the family names + * - fontface objects : loads given fontfaces and adds them to + * `document.fonts` + * + * The returned promise will resolve, when all loading is done. + */ + public loadFonts(fonts?: (string | FontFace)[]): Promise; + + /** + * Force a terminal relayout by altering `options.FontFamily`. + * + * Found webfonts in `fontFamily` are temporarily removed until the webfont + * resources are fully loaded. + * + * Call this method, if a terminal with webfonts is stuck with broken + * glyph metrics. + * + * Returns a promise on completion. + */ + public relayout(): Promise; } + + /** + * Wait for webfont resources to be loaded. + * + * Without any argument, all fonts currently listed in + * `document.fonts` will be loaded. + * For a more fine-grained loading strategy you can populate + * the `fonts` argument with: + * - font families : loads all fontfaces in `document.fonts` + * matching the family names + * - fontface objects : loads given fontfaces and adds them to + * `document.fonts` + * + * The returned promise will resolve, when all loading is done. + */ + function loadFonts(fonts?: (string | FontFace)[]): Promise; } From 956c7cbcba5dd5e0300c84203e074f32c0c66abc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Fri, 4 Oct 2024 04:00:20 +0200 Subject: [PATCH 08/38] demo tests --- .../typings/addon-web-fonts.d.ts | 4 +- addons/addon-web-fonts/webpack.config.js | 2 +- demo/bpdots.regular.otf | Bin 0 -> 48296 bytes demo/client.ts | 23 +++- demo/index.html | 4 + demo/kongtext.regular.ttf | Bin 0 -> 10280 bytes demo/server.js | 3 + demo/style.css | 112 ------------------ 8 files changed, 31 insertions(+), 117 deletions(-) create mode 100644 demo/bpdots.regular.otf create mode 100644 demo/kongtext.regular.ttf diff --git a/addons/addon-web-fonts/typings/addon-web-fonts.d.ts b/addons/addon-web-fonts/typings/addon-web-fonts.d.ts index c353a421..9be3de21 100644 --- a/addons/addon-web-fonts/typings/addon-web-fonts.d.ts +++ b/addons/addon-web-fonts/typings/addon-web-fonts.d.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017 The xterm.js authors. All rights reserved. + * Copyright (c) 2024 The xterm.js authors. All rights reserved. * @license MIT */ @@ -13,7 +13,7 @@ declare module '@xterm/addon-web-fonts' { */ export class WebFontsAddon implements ITerminalAddon { /** - * @param forceInitialRelayout Force an initial relayout, if a webfont was found. + * @param forceInitialRelayout Force initial relayout, if a webfont was found (default true). */ constructor(forceInitialRelayout?: boolean); public activate(terminal: Terminal): void; diff --git a/addons/addon-web-fonts/webpack.config.js b/addons/addon-web-fonts/webpack.config.js index c73a60ba..f75d2d50 100644 --- a/addons/addon-web-fonts/webpack.config.js +++ b/addons/addon-web-fonts/webpack.config.js @@ -1,5 +1,5 @@ /** - * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * Copyright (c) 2024 The xterm.js authors. All rights reserved. * @license MIT */ diff --git a/demo/bpdots.regular.otf b/demo/bpdots.regular.otf new file mode 100644 index 0000000000000000000000000000000000000000..e5b5d1dcb74e06e7719aefbe81fd356e6257c1a7 GIT binary patch literal 48296 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 literal 0 HcmV?d00001 diff --git a/demo/client.ts b/demo/client.ts index d125bfee..d5e9fc08 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -23,7 +23,7 @@ import { FitAddon } from '@xterm/addon-fit'; import { LigaturesAddon } from '@xterm/addon-ligatures'; import { SearchAddon, ISearchOptions } from '@xterm/addon-search'; import { SerializeAddon } from '@xterm/addon-serialize'; -import { WebFontsAddon } from '@xterm/addon-web-fonts'; +import { WebFontsAddon, loadFonts } from '@xterm/addon-web-fonts'; import { WebLinksAddon } from '@xterm/addon-web-links'; import { WebglAddon } from '@xterm/addon-webgl'; import { Unicode11Addon } from '@xterm/addon-unicode11'; @@ -252,6 +252,7 @@ if (document.location.pathname === '/test') { addVtButtons(); initImageAddonExposed(); testEvents(); + testWebfonts(); } function createTerminal(): void { @@ -268,7 +269,7 @@ function createTerminal(): void { backend: 'conpty', buildNumber: 22621 } : undefined, - fontFamily: '"Roboto Mono", "Fira Code", courier-new, courier, monospace, "Powerline Extra Symbols"', + fontFamily: '"Fira Code", courier-new, courier, monospace, "Powerline Extra Symbols"', theme: xtermjsTheme } as ITerminalOptions); @@ -1427,3 +1428,21 @@ function testEvents(): void { document.getElementById('event-focus').addEventListener('click', ()=> term.focus()); document.getElementById('event-blur').addEventListener('click', ()=> term.blur()); } + +function testWebfonts() { + document.getElementById('webfont-kongtime').addEventListener('click', async () => { + const ff = new FontFace('Kongtext', "url(/kongtext.regular.ttf) format('truetype')"); + await loadFonts([ff]); + term.options.fontFamily = 'Kongtext'; + term.options.lineHeight = 1.3; + addons.fit.instance?.fit(); + }); + document.getElementById('webfont-bpdots').addEventListener('click', async () => { + document.styleSheets[0].insertRule("@font-face { font-family: 'BPdots'; src: url(/bpdots.regular.otf) format('opentype'); weight: 400 }", 0); + await loadFonts(['BPdots']); + term.options.fontFamily = 'BPdots'; + term.options.lineHeight = 1.3; + term.options.fontSize = 20; + addons.fit.instance?.fit(); + }); +} diff --git a/demo/index.html b/demo/index.html index 06451847..a5673481 100644 --- a/demo/index.html +++ b/demo/index.html @@ -116,6 +116,10 @@
Events Test
+ +
Webfonts
+
+
diff --git a/demo/kongtext.regular.ttf b/demo/kongtext.regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..5e4d65fd1e059a159342d62f86c374da152df7f5 GIT binary patch literal 10280 zcmZQzWME+6WoTevW(aV1adl%fVSK>Apt^#AfkDeXIMiv)_jS`47*ww?Ffio!2kRRJ z-a1gqz`z*6z`zieoSRr+E9rKgfr0e`0|Vo=^u*$V|Nj}785kHhFfcH1q~}zo-PYq( zV_@KxVPKHz$w*C1xxLWZgn@zK4g&*&Sw==`BKtk&76t}J5e5bZm5khy3T+0}1_lPs z1O^7iJvsTwi3d22{bgWaU%|k@xFa{QqJUA6L6?Dn(Sm`2K_M?OH&ySGk`+jffq_M| zAiuaI?%1zs3=C=x3=E7fzOW$8h#{We<|_j;3j+x3arqU3p#L|pFfccO+{?fO z5@lcj>0@sA-@p(9QO^PvfyzNBW{?_&+YDL^5G5d;j9@Vbhyn&i76!%%46FrF;1f;p9FgP&qC@MkRz}&z9avkFVP#l01 zG5-G#cBu?w7y|<+v>6x}yg(9QHY0-ogAY`kiGhzH4$5X`@L|YSx204abU|)cYQ)QHbvKbjT7#*N&CI)s!Pbiz2L5(pG%4T5@V61?$Ss8d4JE3eg z27bor49uYL0J)QKIh4)FAi($;%4TBVV`7D}nHhYTM4@aJ1_h>MD4Uf*f~ghCW@C_J znh$1!!Vu(lkolYp5Whp%5Whp%5Whp%5Whp%5Whp%5Wj~o6fu-Cq%wpsR5BDWq%tTl zBs1hQnq%ssS6fuE6QW#Phav2gC@)(R6EEx0{7#O_4 zYSS4?7*ZK37)lrz7=jp58PXX_8FCmB8HyMf7y=m57!ny=8B!QB8A=!w7_1l+Fmx%P z>4K;-W-wsTWiVsVg}VaNK6IT73}I-#Fl5kUFkmobP{86^G`EHnm8OPN7Nja9=jWBB z78Pga=P9gIFfuSOR!GiINzF~nGq%uU@XpUmFG;N^VF*f1FU?6TVhBh}bWO=DQLs{g zNhv_36s#1C4RpC!tWS}|l z0w|||`78`u7#J8d7!(*-7&yS?ejUSq#$d)o#tOzsOvjk+D2OOXDaa`(DX1wJC|D`1 zQB?Z>A8aTOg95_>#vsN7xJqFK2?ZGi1+Yp>s7gi#hX0TMANpVLKjpvQf75?Vf8RdX z{$R_4%?~y`SpQ(zgLw~h@9zM+N)2o(A%Y21&M~qvFfdNq1X4Snk(pt^0Z@$v;?>tx zH8ar5x<@e2qF35$q|iAzXINz2H}$tx%-DXXZescUFzY3u0f z=^Gdt8Jn1znOj&|S=-p!**iEoIlH*JxqEnedHeYK`3D3B1&4%&g-1k2MaRU(#U~^( zw6%A3Po6Pv@uDTmmM&kpdexe>>(_1AxOwZAZ9BH_*}ZokLs3dE$3tGUU`Stk~6<3@UmVa%&lqnp&rW#TghFb}}4T?Fh1`q`C7#kQGK$rn03sM2aAT^+d1ymnY6r>A;v5SIa5Ex`K z#9n085H?5-Y%9c7Xr{x=far!L!vUf}VFNBYz^x&Kd%?~Dg%FqlB0#Mr5C*9MVF()} zLn;QBG|a|KXF)A1LqSDB5c{t&0~2Ed0|Qe7GpMX(XJBAd6jT&d6jcO;(|-m=22d0D zA45X}0}~?yV*@itGcyB7ow1-YBM37zG%z+aG%z##YhY~n*TBF83TbALdD0B>3=E9M zqKc-*qQ;`iV61GaD5`9#Y-$X`;-ZSCjG!oM0E7PwV5)(Uff>qz@IfS~)!M+oz}&#x zz#t8BCCH7UiV!nJ6-^-!WUevD<%|q}8<;?b{Ac*jz|;WpKm#+#90UgW7t~Y)naIo_ z%b>`>Agm~;2=bGtG88JB8Y3XHqM%{}C_tE?hQQnl7G!RKgcei|MEzp`)i>Zw!vYF7 zNw6)NVAmUqDvBx#g2F~wR1p*w;1IE96g4d19z6@>_c!n}czp`qbF1H=;`a~m4| zfx{deN^nO&WWXc?BPgeV;|dg0ih>}+jF}i189?SRGl2Af)PRD3nSp^D>>{XZ1R?4` z3LBUqmNJ2Q5uj88HW8xQ7-FWNGAOjc&O=cRP6Hr63xh&c)Yw!}P+8PiP|;LbR8i2_ zR2k$asJB3l0r})V*l{2UNL)97(-7D;P>K{(6jU??r%F&1fT9(YOu(8L85&@|VQc`W zO^|JhrVyX}2bD)4OF-UdY+!C+0(qN3SW#55ftleiIK480e8&XJm7x9}C>4O?6l4=f z7Xu{S3qryPnl_XL!D#~=s^CxubdZ}t0t}3xtcwVHaH=r|X$FTqByBVNZ2+eUNZ5h$2?K*D!d6gj5d_78 zA}BXP?QCFT_zMY~{~#ZNqZ}0eU`xUNhL{7%Zy+Z_ePF663UNG=X$>GhfQl|qVt|+j zVu1n#WL`r9vdefusY1|LP!SXmpqPbO`=8+-q$B~QQBa0L)&)sRpwbE19pG35XZC-f zqJ)v*AEfvI1z!UwN zBNJpl$nS7BKmriz4p8|Di5Hk}AfW*&0l{et;v0}pK;NbB4=Ue4 zNga|O8^D1Dt}#G924j#tAibbk15&$yYau~JMh2#azYQP^F10}ADJVBU@*^ZJK{kMF zf|S+Drpk~+h}{-YeG4vE(83PnD^p`onE*-&paK>YQlKORqM6}E6DW}*r39!8KxH93 zcPR@RL(Bk&3KKYELJVMp7pI6g2fVn3uz1J%PIJqTMNISG^-!SM~s`rsx9 zs9*wH^dA&q2=$!coCj48R}D(84WKwfiWz9x2lbCKxc*Tzg~biTN1%2K$R}W%KnzfD zGa;8aVD||s!e9d^X@No$q#u+Z85#aEfHD~adKnF^+rjN4P&))&x$h>e_@Kq;Gn5t^Dz(aJUCGL4DhAE;xE z$WfdO;MxU}4h0qADe)gT^MLaKBxi#8$aV@cAnIvjkUv2!Gf<8+HZ=y<5TGChwY@+z zC^kVfC}Kbr52z6h4pmSY#>*fG@i(N^14=(|cQk+!52)lsI0Y0K(7cWC2bMYmTikRJsh<*+iieFxS9u0ol?Q3OfFP<@EB1xmw+)B!4YKx+ zqR zR|Cue;CdZYc7R$6pw=?DwGMJUxc>!H`w!H{PX_5TRb&-Z6a@7hK-q#3T&OTN`~f${8$eA;xK5@9 zc>5DnTZ3ycu)jfVKv2*$`~$VfL6HdZIf#as0!kr}o+_vX3n~@C{a$d|GzRx_KrsZy zkk|)V3+k~mH2eqkcMzcs%Fn3nM3iPCsGS9}7E~TF!o!TY0aCMr!x~a{fI}Kue1Pl) z6~X^NtpRW(L3-5SR3Hk<1<38n22ccnJo_Kih6ByiL25vdI*{u?`4`+y1eK7W&}U=- zcicgN3r=OwGPVKKy8-pr5xE~!T0`p>aI%AzgZ~+rLFRz`1r~vYCTN}v>_%|g58P^G zWcb(6zzi;FK)wfuIa33uEC>1B6qME!1wrk8Wl&ogl;=RHx&bu80LrTjkhgj++512rS8x(F#pl}B%M@}8w@U|d0BtSI}v@HlKdqG7aNE1og~8EhKADUVutOU;^iOh>g%x0JaZYW(yjFA*j@W*$58Yf1pGG(h5yK;G_=K z%?yfNNE=KM+=c~>p+NkNTq}W+6v)wFlR#+_>{U=CK=MCwod(MPiijHeAGiSvvc7>4 zT(cwPGDu7bgIk!OauPIl03Hv6)@Y32tO6>wK;ZcN)|T_zTOqpcWRyY|tp0BDn2r4DL^Zb1|e@ z3NjE>$AR-aDDOc11*#e#{sOfwMU71%DNxxI)Uq`Or5}(#AiBXd0Ei3H3z~%j^_4(1 zh&+Qjq}K*+WrAZ9To)J{LMm@iHEYTWO5dQ7R0h!aD5zl!F$%;3rE*YC`UfuDAPEK{ z4k_{*KsG|-j~OwN1MXXa4F`|qfcj@3UEs6_ZdZcJe@J2gwJrXFVu2ad(F3mvU}q3z zkc7G$lw;6pUt>W~NQ2B`0{3?zVF0f9p#=drWI@Iu%;aVOjcS7GBv?)|X2vj=2^0e0 z=mxcxK{gpxIg0dAFn zGZom`kYW?u+yNyjNO*$sKV&2i)aL}HNn=w`Xdp@-m`gz}134CydO)cWT&6RFMi|k; z5!7N51&xJ)`YEEurr=ZrDuzHNz;YGXMG$|3Y7UTL@VX4+p zDBeNA18PTbBaNLwT8iM%0q1#8XoJSiAb|q19ySaJiZO6_gFOKXbZC=?fdw8yAlHCK z=0GzP(6j~4&7iaZtpg!>5!5OJRo6)A3S7HD8>QejDKvjU`;VXy2F(_L#s@)c5Qe1* zSbYIWs$fTgau>)jkmtd5Gc@&c!$%FFJ_ofqQOrd6929^sE5MBykYS(*0H=Ns2RQ|U z{0_W zGAKS97(v+)WGyo&3P3GtaH0ndK7eKu7#J9M7#J9oKy5})A;nP3z{pU?0Gd5&WVpou zvH}zmpgAs3dBepZ$DqT&=;G!R!oUHUe*6#eF#`i*T2W#$c>W)x4y2C-Y(57ABZCeD z69X560s|8R8v`FhJp(g?5Q8W~9hepe+W|^xLJS}_6N3nY7|3@ZE1?)AaW-wteWiVqf zXRu(fWUykeX0TzfWw2wgXK-L}WN>0|W^iF}WpHC~XYgR~Wbk6}X7FL~W$j zWC&siW(Z*jWe8&kXNX{kWQbyjW{6>kWr$;lXGma3WLU$nn4yKCm0>T#G=>)pZ47-3 z-3)UXRx&JMWMb%IxW&-SFqz>K!)JzGhUp9)42K!+F??j0$FPdwE5jFtZw!kV)-s%A zIL@$+A&DWGVFE)6!)b<8h7$~@7|t`CVK~chj^QOk8pCCV3k(+-)-$9td|;T!aE;*# z!&Qb1hSv;l89EuV7%~~M8FCqN81fhvFyu27G8BN res.sendFile(__dirname + '/kongtext.regular.ttf')); + app.get('/bpdots.regular.otf', (req, res) => res.sendFile(__dirname + '/bpdots.regular.otf')); + app.use('/dist', express.static(__dirname + '/dist')); app.use('/src', express.static(__dirname + '/src')); diff --git a/demo/style.css b/demo/style.css index 3e32b42f..1fb8f7ac 100644 --- a/demo/style.css +++ b/demo/style.css @@ -1,115 +1,3 @@ -/* cyrillic-ext */ -@font-face { - font-family: 'Roboto Mono'; - font-style: italic; - font-weight: 100 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3CWWoKC.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Roboto Mono'; - font-style: italic; - font-weight: 100 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3mWWoKC.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek */ -@font-face { - font-family: 'Roboto Mono'; - font-style: italic; - font-weight: 100 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm36WWoKC.woff2) format('woff2'); - unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Roboto Mono'; - font-style: italic; - font-weight: 100 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3KWWoKC.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Roboto Mono'; - font-style: italic; - font-weight: 100 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3OWWoKC.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Roboto Mono'; - font-style: italic; - font-weight: 100 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm32WWg.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Roboto Mono'; - font-style: normal; - font-weight: 100 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhGq3-OXg.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Roboto Mono'; - font-style: normal; - font-weight: 100 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhPq3-OXg.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek */ -@font-face { - font-family: 'Roboto Mono'; - font-style: normal; - font-weight: 100 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhIq3-OXg.woff2) format('woff2'); - unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Roboto Mono'; - font-style: normal; - font-weight: 100 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhEq3-OXg.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Roboto Mono'; - font-style: normal; - font-weight: 100 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhFq3-OXg.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Roboto Mono'; - font-style: normal; - font-weight: 100 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhLq38.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} - - - - body { font-family: helvetica, sans-serif, arial; font-size: 1em; From 7907f13774ef6c0023f3277d5d72577d8e126717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Fri, 4 Oct 2024 04:14:10 +0200 Subject: [PATCH 09/38] add license notes for fonts --- demo/font-licenses.txt | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 demo/font-licenses.txt diff --git a/demo/font-licenses.txt b/demo/font-licenses.txt new file mode 100644 index 00000000..5b1b5ba6 --- /dev/null +++ b/demo/font-licenses.txt @@ -0,0 +1,41 @@ + +BPdots is licensed under the Creative Commons Attribution-NoDerivs License (CC BY-ND). + + +kongtext license text: + +Thanks for downloading one of codeman38's retro video game fonts, +as seen on Memepool, BoingBoing, and all around the blogosphere. + +So, you're wondering what the license is for these fonts? Pretty simple; +it's based upon that used for Bitstream's Vera font set . + +Basically, here are the key points summarized, in as little legalese as possible; +I hate reading license agreements as much as you probably do: + +With one specific exception, you have full permission to bundle these fonts in +your own free or commercial projects-- and by projects, I'm referring to not +just software but also electronic documents and print publications. + +So what's the exception? Simple: you can't re-sell these fonts +in a commercial font collection. I've seen too many font CDs for sale in stores +that are just a repackaging of thousands of freeware fonts found on the internet, +and in my mind, that's quite a bit like highway robbery. Note that this *only* +applies to products that are font collections in and of themselves; +you may freely bundle these fonts with an operating system, application program, +or the like. + +Feel free to modify these fonts and even to release the modified versions, +as long as you change the original font names (to ensure consistency among +people with the font installed) and as long as you give credit somewhere +in the font file to codeman38 or zone38.net. I may even incorporate these changes +into a later version of my fonts if you wish to send me the modifed fonts via e-mail. + +Also, feel free to mirror these fonts on your own site, as long as you make it +reasonably clear that these fonts are not your own work. I'm not asking for much; +linking to zone38.net or even just mentioning the nickname codeman38 should be enough. + +Well, that pretty much sums it up... so without further ado, +install and enjoy these fonts from the golden age of video games. + +[ codeman38 | cody@zone38.net | http://www.zone38.net/ ] From b0389a2ed173bf3b9df32a1b6f5f8c461b14d887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Fri, 4 Oct 2024 05:31:47 +0200 Subject: [PATCH 10/38] future proof demo --- demo/client.ts | 4 +++- demo/index.html | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/demo/client.ts b/demo/client.ts index d5e9fc08..732efda5 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -1430,12 +1430,14 @@ function testEvents(): void { } function testWebfonts() { - document.getElementById('webfont-kongtime').addEventListener('click', async () => { + document.getElementById('webfont-kongtext').addEventListener('click', async () => { const ff = new FontFace('Kongtext', "url(/kongtext.regular.ttf) format('truetype')"); await loadFonts([ff]); term.options.fontFamily = 'Kongtext'; term.options.lineHeight = 1.3; addons.fit.instance?.fit(); + setTimeout(() => term.write('\x1b[?12h\x1b]12;#776CF9\x07\x1b[38;2;119;108;249;48;2;21;8;150m\x1b[2J\x1b[2;5H**** COMMODORE 64 BASIC V2 ****\r\n\r\n 64K RAM SYSTEM 38911 BASIC BYTES FREE\r\n\r\nREADY.\r\nLOAD '), 1000); + setTimeout(() => {term.write('🤣\x1b[m\x1b[99;1H'); term.input('\r');}, 5000); }); document.getElementById('webfont-bpdots').addEventListener('click', async () => { document.styleSheets[0].insertRule("@font-face { font-family: 'BPdots'; src: url(/bpdots.regular.otf) format('opentype'); weight: 400 }", 0); diff --git a/demo/index.html b/demo/index.html index a5673481..691871ee 100644 --- a/demo/index.html +++ b/demo/index.html @@ -118,8 +118,8 @@
Webfonts
-
-
+
+
From c93623cb3bcd5d53fbc5c51333b2e7cac0b0e927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Fri, 4 Oct 2024 14:24:36 +0200 Subject: [PATCH 11/38] CI tests --- .../test/WebFontsAddon.test.ts | 105 +++++++++++++++++- 1 file changed, 103 insertions(+), 2 deletions(-) diff --git a/addons/addon-web-fonts/test/WebFontsAddon.test.ts b/addons/addon-web-fonts/test/WebFontsAddon.test.ts index 09bb01a9..1315b1ee 100644 --- a/addons/addon-web-fonts/test/WebFontsAddon.test.ts +++ b/addons/addon-web-fonts/test/WebFontsAddon.test.ts @@ -6,7 +6,6 @@ import test from '@playwright/test'; import { deepStrictEqual, strictEqual } from 'assert'; import { ITestContext, createTestContext, openTerminal, pollFor, timeout } from '../../../test/playwright/TestUtils'; - let ctx: ITestContext; test.beforeAll(async ({ browser }) => { ctx = await createTestContext(browser); @@ -15,5 +14,107 @@ test.beforeAll(async ({ browser }) => { test.afterAll(async () => await ctx.page.close()); test.describe('WebFontsAddon', () => { - test('nothing', () => {}); + + test.beforeEach(async () => { + // make sure that we start with no webfonts in the document + const empty = await await getDocumentFonts(); + deepStrictEqual(empty, []); + }); + test.afterEach(async () => { + // for font loading tests to work, we have to remove added rules and fonts + // to work around the quite aggressive font caching done by the browsers + await ctx.page.evaluate(` + document.styleSheets[0].deleteRule(1); + document.styleSheets[0].deleteRule(0); + document.fonts.clear(); + `); + }); + + test.describe('font loading at runtime', () => { + test('loadFonts (JS)', async () => { + await ctx.page.evaluate(` + const ff1 = new FontFace('Kongtext', "url(/kongtext.regular.ttf) format('truetype')"); + const ff2 = new FontFace('BPdots', "url(/bpdots.regular.otf) format('opentype')"); + loadFonts([ff1, ff2]); + `); + deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'loaded' }, { family: 'BPdots', status: 'loaded' }]); + }); + test('loadFonts (CSS, unquoted)', async () => { + await ctx.page.evaluate(` + document.styleSheets[0].insertRule("@font-face {font-family: Kongtext; src: url(/kongtext.regular.ttf) format('truetype')}", 0); + document.styleSheets[0].insertRule("@font-face {font-family: BPdots; src: url(/bpdots.regular.otf) format('opentype')}", 1); + loadFonts(['Kongtext', 'BPdots']); + `); + deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'loaded' }, { family: 'BPdots', status: 'loaded' }]); + }); + test('loadFonts (CSS, quoted)', async ({ browser }) => { + // NOTE: firefox preserves family quotes from CSS rules in fontface, all other browsers unquote them + await ctx.page.evaluate(` + document.styleSheets[0].insertRule("@font-face {font-family: 'Kongtext'; src: url(/kongtext.regular.ttf) format('truetype')}", 0); + document.styleSheets[0].insertRule("@font-face {font-family: 'BPdots'; src: url(/bpdots.regular.otf) format('opentype')}", 1); + loadFonts(['Kongtext', 'BPdots']); + `); + if (browser.browserType().name() === 'firefox') { + deepStrictEqual(await getDocumentFonts(), [{ family: '"Kongtext"', status: 'loaded' }, { family: '"BPdots"', status: 'loaded' }]); + } else { + deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'loaded' }, { family: 'BPdots', status: 'loaded' }]); + } + }); + test('FontFace hashing', async () => { + // multiple calls of `loadFonts` with the same objects shall not bloat document.fonts + await ctx.page.evaluate(` + const ff1 = new FontFace('Kongtext', "url(/kongtext.regular.ttf) format('truetype')"); + const ff2 = new FontFace('BPdots', "url(/bpdots.regular.otf) format('opentype')"); + loadFonts([ff1, ff2]); + loadFonts([ff1, ff2]); + loadFonts([ff1, ff2]).then(() => loadFonts([ff1, ff2])); + `); + deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'loaded' }, { family: 'BPdots', status: 'loaded' }]); + }); + + test('autoload & relayout from ctor', async ({ browser }) => { + // to make this test work, we exclude the default measurement char W (x57) by restricting unicode-range + // now the browser will postpone font loading until codepoint is hit --> wrong glyph metrics on first usage + const data = await ctx.page.evaluate(` + document.styleSheets[0].insertRule("@font-face {font-family: Kongtext; src: url(/kongtext.regular.ttf) format('truetype'); unicode-range: U+00A0-00FF}", 0); + `); + deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'unloaded' }]); + + // broken case: webfont in ctor without addon usage + await ctx.page.evaluate(` + window.helperTerm = new Terminal({fontFamily: '"Kongtext", ' + term.options.fontFamily}); + window.helperTerm.open(term.element); + `); + + // safari loads the font, firefox & chrome dont + if (browser.browserType().name() === 'webkit') { + deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'loaded' }]); + } else { + deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'unloaded' }]); + } + + // good case: addon fixes layout for webfont in ctor + // the relayout happens async, so wait a bit with a promise + await ctx.page.evaluate(` + window.helperTerm.dispose(); + window.helperTerm = new Terminal({fontFamily: '"Kongtext", ' + term.options.fontFamily}); + window._webfontsAddon = new WebFontsAddon(); + window.helperTerm.loadAddon(window._webfontsAddon); + window.helperTerm.open(term.element); + `); + await timeout(100); + deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'loaded' }]); + + // cleanup this messy test case + await ctx.page.evaluate(` + window.helperTerm.dispose(); + window._webfontsAddon.dispose(); + `); + }); + }); + }); + +async function getDocumentFonts(): Promise { + return ctx.page.evaluate(`Array.from(document.fonts).map(ff => ({family: ff.family, status: ff.status}))`); +} From 104c63d7e035c4297432081a36e9e3ae1af80be8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Fri, 4 Oct 2024 17:52:45 +0200 Subject: [PATCH 12/38] cleanup files --- addons/addon-web-fonts/src/WebFontsAddon.ts | 145 -------------------- 1 file changed, 145 deletions(-) diff --git a/addons/addon-web-fonts/src/WebFontsAddon.ts b/addons/addon-web-fonts/src/WebFontsAddon.ts index 219a64af..4a9f7e7a 100644 --- a/addons/addon-web-fonts/src/WebFontsAddon.ts +++ b/addons/addon-web-fonts/src/WebFontsAddon.ts @@ -151,148 +151,3 @@ export class WebFontsAddon implements ITerminalAddon, IWebFontsApi { } } } - - - - - - - -/* eslint-disable */ -// TODO: place into test cases -/* -(window as any).__roboto = [ - // cyrillic-ext - new FontFace( - 'Roboto Mono', - "url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3CWWoKC.woff2) format('woff2')", - { - style: 'italic', - weight: '100 700', - display: 'swap', - unicodeRange: 'U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F' - } - ), - // cyrillic - new FontFace( - 'Roboto Mono', - "url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3mWWoKC.woff2) format('woff2')", - { - style: 'italic', - weight: '100 700', - display: 'swap', - unicodeRange: 'U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116' - } - ), - // greek - new FontFace( - 'Roboto Mono', - "url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm36WWoKC.woff2) format('woff2')", - { - style: 'italic', - weight: '100 700', - display: 'swap', - unicodeRange: 'U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF' - } - ), - // vietnamese - new FontFace( - 'Roboto Mono', - "url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3KWWoKC.woff2) format('woff2')", - { - style: 'italic', - weight: '100 700', - display: 'swap', - unicodeRange: 'U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB' - } - ), - // latin-ext - new FontFace( - 'Roboto Mono', - "url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3OWWoKC.woff2) format('woff2')", - { - style: 'italic', - weight: '100 700', - display: 'swap', - unicodeRange: 'U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF' - } - ), - // latin - new FontFace( - 'Roboto Mono', - "url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm32WWg.woff2) format('woff2')", - { - style: 'italic', - weight: '100 700', - display: 'swap', - unicodeRange: 'U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD' - } - ), - // cyrillic-ext - new FontFace( - 'Roboto Mono', - "url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhGq3-OXg.woff2) format('woff2')", - { - style: 'normal', - weight: '100 700', - display: 'swap', - unicodeRange: 'U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F' - } - ), - // cyrillic - new FontFace( - 'Roboto Mono', - "url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhPq3-OXg.woff2) format('woff2')", - { - style: 'normal', - weight: '100 700', - display: 'swap', - unicodeRange: 'U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116' - } - ), - // greek - new FontFace( - 'Roboto Mono', - "url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhIq3-OXg.woff2) format('woff2')", - { - style: 'normal', - weight: '100 700', - display: 'swap', - unicodeRange: 'U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF' - } - ), - // vietnamese - new FontFace( - 'Roboto Mono', - "url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhEq3-OXg.woff2) format('woff2')", - { - style: 'normal', - weight: '100 700', - display: 'swap', - unicodeRange: 'U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB' - } - ), - // latin-ext - new FontFace( - 'Roboto Mono', - "url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhFq3-OXg.woff2) format('woff2')", - { - style: 'normal', - weight: '100 700', - display: 'swap', - unicodeRange: 'U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF' - } - ), - // latin - new FontFace( - 'Roboto Mono', - "url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhLq38.woff2) format('woff2')", - { - style: 'normal', - weight: '100 700', - display: 'swap', - unicodeRange: 'U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD' - } - ), -]; -*/ From 085a2750387f8c446b9443d2d0a9d7187e38db9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sun, 6 Oct 2024 23:53:30 +0200 Subject: [PATCH 13/38] fix promise return, reject on missing family name, doc polish --- addons/addon-web-fonts/README.md | 157 +++++++++++++----- addons/addon-web-fonts/src/WebFontsAddon.ts | 13 +- .../typings/addon-web-fonts.d.ts | 6 +- 3 files changed, 129 insertions(+), 47 deletions(-) diff --git a/addons/addon-web-fonts/README.md b/addons/addon-web-fonts/README.md index e910c21e..ce43cbe5 100644 --- a/addons/addon-web-fonts/README.md +++ b/addons/addon-web-fonts/README.md @@ -1,6 +1,7 @@ ## @xterm/addon-web-fonts -Addon to use webfonts with [xterm.js](https://github.com/xtermjs/xterm.js). This addon requires xterm.js v5+. +Addon to use webfonts with [xterm.js](https://github.com/xtermjs/xterm.js). +This addon requires xterm.js v5+. ### Install @@ -10,33 +11,43 @@ npm install --save @xterm/addon-web-fonts ### Issue with Webfonts -Webfonts are announced by CSS `font-face` rules (or its Javascript `FontFace` counterparts). Since font files tend to be quite big assets, browser engines often postpone their loading to an actual styling request of a codepoint matching a font file's `unicode-range`. In short - font files will not be loaded until really needed. +Webfonts are announced by CSS `font-face` rules (or its Javascript `FontFace` counterparts). +Since font files tend to be quite big assets, browser engines often postpone their loading +to an actual styling request of a codepoint matching a font file's `unicode-range`. +In short - font files will not be loaded until really needed. -xterm.js on the other hand heavily relies on exact measurements of character glyphs to layout its output. This is done by determining the glyph width (DOM renderer) or by creating a glyph texture (WebGl renderer) for every output character. -For performance reasons both is done in synchronous code and cached. This logic only works properly, -if a font glyph is available on its first usage, or a wrong glyph from a fallback font chosen by the browser will be used instead. +xterm.js on the other hand heavily relies on exact measurement of character glyphs +to layout its output. This is done by determining the glyph width (DOM renderer) or +by creating a glyph texture (WebGl renderer) for every output character. +For performance reasons both is done in synchronous code and cached. +This logic only works properly, if a font glyph is available on its first usage, +otherwise the browser will pick a glyph from a fallback font messing up the metrics. -For webfonts and xterm.js this means, that we cannot rely on the default loading strategy of the browser, but have to preload the font files before using that font in xterm.js. +For webfonts and xterm.js this means that we cannot rely on the default loading strategy +of the browser, but have to preload the font files before using that font in xterm.js. ### Static Preloading for the Rescue? -If you dont mind higher initial loading times of the embedding document, you can tell the browser to preload the needed font files by placing the following link elements in the document's head above any other CSS/Javascript: +If you dont mind higher initial loading times with a white page shown, +you can tell the browser to preload the needed font files by placing the following +link elements in the document's head above any other CSS/Javascript: ```html ... ``` -Downside of this approach is the much higher initial loading time showing as a white page. Browsers also will resort to system fonts, if the preloading takes too long, so with a slow connection or a very big font this solves literally nothing. +Browsers also will resort to system fonts, if the preloading takes too long. +So this solution has only a very limited scope. -### Preloading with WebFontsAddon +### Loading with WebFontsAddon -The webfonts addon offers several ways to deal with the loading of font assets without leaving the terminal in an unusable state. +The webfonts addon offers several ways to deal with font assets loading +without leaving the terminal in an unusable state. - -Recap - normally boostrapping of a new terminal involves these basic steps: +Recap - normally boostrapping of a new terminal involves these basic steps (Typescript): ```typescript import { Terminal } from '@xterm/xterm'; @@ -46,18 +57,20 @@ import { XYAddon } from '@xterm/addon-xy'; const terminal = new Terminal({fontFamily: 'monospace'}); // create and load all addons you want to use, e.g. fit addon -const xyAddon = new XYAddon(); -terminal.loadAddon(xyAddon); +const xyInstance = new XYAddon(); +terminal.loadAddon(xyInstance); // finally: call `open` of the terminal instance terminal.open(your_terminal_div_element); // <-- critical path for webfonts // more boostrapping goes here ... ``` +This code is guaranteed to work in all browsers synchronously, as the identifier `monospace` +will always be available. It will also work synchronously with any installed system font, +but breaks horribly with webfonts. The actual culprit here is the call to `terminal.open`, +which attaches the terminal to the DOM and starts the renderer with all the glyph caching +mentioned above, while the webfont is not yet fully available. -This synchronous code is guaranteed to work in all browsers, as the font `monospace` will always be available. -It will also work that way with any installed system font, but breaks horribly for webfonts. The actual culprit here is the call to `terminal.open`, which attaches the terminal to the DOM and starts the renderer with all the glyph caching mentioned above, while the webfont is not fully available yet. - -To fix that, the webfonts addon provides a waiting condition: +To fix that, the webfonts addon provides a waiting condition (Typescript): ```typescript import { Terminal } from '@xterm/xterm'; import { XYAddon } from '@xterm/addon-xy'; @@ -65,48 +78,55 @@ import { WebFontsAddon } from '@xterm/addon-web-fonts'; // create a `Terminal` instance, now with webfonts const terminal = new Terminal({fontFamily: '"Web Mono 1", "Super Powerline", monospace'}); -const xyAddon = new XYAddon(); -terminal.loadAddon(xyAddon); +const xyInstance = new XYAddon(); +terminal.loadAddon(xyInstance); -const webFontsAddon = new WebFontsAddon(); -terminal.loadAddon(webFontsAddon); +const webFontsInstance = new WebFontsAddon(); +terminal.loadAddon(webFontsInstance); // wait for webfonts to be fully loaded -webFontsAddon.loadFonts(['Web Mono 1', 'Super Powerline']).then(() => { +webFontsInstance.loadFonts(['Web Mono 1', 'Super Powerline']).then(() => { terminal.open(your_terminal_div_element); // more boostrapping goes here ... }); ``` -Here `loadFonts` will look up the font face objects in `document.fonts` and load them before continuing. -For this to work, you have to make sure, that the CSS `font-face` rules for these webfonts are loaded -on the initial document load (more precise - by the time this code runs). +Here `loadFonts` will look up the font face objects in `document.fonts` +and load them before continuing. For this to work, you have to make sure, +that the CSS `font-face` rules for these webfonts are loaded beforehand, +otherwise `loadFonts` will not find the font family names (promise will be +rejected for missing font family names). -Please note, that this code cannot run synchronous anymore, so you will have to split your -bootstrapping code into several stages. If thats too much of a hassle, you can also move the whole -bootstrapping under that waiting condition (import `loadFonts` for a static variant): +Please note, that this cannot run synchronous anymore, so you will have to split your +bootstrapping code into several stages. If that is too much of a hassle, +you can also move the whole bootstrapping under the waiting condition by using +the static loader instead (Typescript): ```typescript import { Terminal } from '@xterm/xterm'; import { XYAddon } from '@xterm/addon-xy'; -import { WebFontsAddon, loadFonts } from '@xterm/addon-web-fonts'; +// import static loader +import { loadFonts } from '@xterm/addon-web-fonts'; loadFonts(['Web Mono 1', 'Super Powerline']).then(() => { // create a `Terminal` instance, now with webfonts const terminal = new Terminal({fontFamily: '"Web Mono 1", "Super Powerline", monospace'}); - const xyAddon = new XYAddon(); - terminal.loadAddon(xyAddon); + const xyInstance = new XYAddon(); + terminal.loadAddon(xyInstance); - const webFontsAddon = new WebFontsAddon(); - terminal.loadAddon(webFontsAddon); + // optional when using static loader + const webfontsInstance = new WebFontsAddon(); + terminal.loadAddon(webfontsInstance); terminal.open(your_terminal_div_element); // more boostrapping goes here ... }); ``` +With the static loader creating and loading of the actual addon can be omitted, +as fonts are already loaded before any terminal setup happens. ### Webfont Loading at Runtime -Given you have a terminal already running and want to change the font family to a different not yet loaded webfont. -That can be achieved like this: +Given you have a terminal already running and want to change the font family +to a different not yet loaded webfont: ```typescript // either create font face objects in javascript const ff1 = new FontFace('New Web Mono', url1, ...); @@ -133,5 +153,68 @@ loadFonts(['New Web Mono']).then(() => { }); ``` +### Forced Layout Update -See the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/addon-web-fonts/typings/addon-web-fonts.d.ts) for more advanced usage. +If you have the addon loaded into your terminal, you can force the terminal to update +the layout with the method `WebFontsAddon.relayout`. This might come handy, +if the terminal shows webfont related output issue for unknown reasons: +```typescript +... +// given - terminal shows weird font issues, run: +webFontsInstance.relayout().then(() => { + // also run resize logic here, e.g. fit addon + fitAddon.fit(); +}); +``` +Note that this method is only meant as a quickfix on a running terminal to keep it +in a working condition. A production-ready integration should never rely on it, +better fix the real root cause (most likely not properly awaiting the font loader +higher up in the code). + + +### Webfonts from Fontsource + +The addon has been tested to work with webfonts from fontsource. +Javascript example for `vite` with ESM import: +```javascript +import { Terminal } from '@xterm/xterm'; +import { FitAddon } from '@xterm/addon-fit'; +import { loadFonts } from '@xterm/addon-web-fonts'; +import '@xterm/xterm/css/xterm.css'; +import '@fontsource/roboto-mono'; +import '@fontsource/roboto-mono/400.css'; +import '@fontsource/roboto-mono/400-italic.css'; +import '@fontsource/roboto-mono/700.css'; +import '@fontsource/roboto-mono/700-italic.css'; + +async function main() { + let fontFamily = '"Roboto Mono", monospace'; + try { + await loadFonts(['Roboto Mono']); + } catch (e) { + fontFamily = 'monospace'; + } + + const terminal = new Terminal({ fontFamily }); + const fitAddon = new FitAddon(); + terminal.loadAddon(fitAddon); + terminal.open(document.getElementById('your-xterm-container-div')); + fitAddon.fit(); + + // sync writing shows up in Roboto Mono w'o FOUT + // and a fallback to monospace + terminal.write('put any unicode char here'); +} + +main(); +``` +The fontsource packages download the font files to your project folder to be delivered +from there later on. For security sensitive projects this should be the preferred way, +as it brings the font files under your control. + +The example furthermore contains proper exception handling with a fallback +(skipped in all other examples for better readability). + +--- + +Also see the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/addon-web-fonts/typings/addon-web-fonts.d.ts). diff --git a/addons/addon-web-fonts/src/WebFontsAddon.ts b/addons/addon-web-fonts/src/WebFontsAddon.ts index 4a9f7e7a..cc0ccd1f 100644 --- a/addons/addon-web-fonts/src/WebFontsAddon.ts +++ b/addons/addon-web-fonts/src/WebFontsAddon.ts @@ -94,7 +94,7 @@ function _loadFonts(fonts?: (string | FontFace)[]): Promise { const familyFiltered = ffs.filter(ff => font === unquote(ff.family)); toLoad = toLoad.concat(familyFiltered); if (!familyFiltered.length) { - console.warn(`font family "${font}" not registered in document.fonts`); + return Promise.reject(`font family "${font}" not registered in document.fonts`); } } } @@ -102,16 +102,15 @@ function _loadFonts(fonts?: (string | FontFace)[]): Promise { } -export async function loadFonts(fonts?: (string | FontFace)[]): Promise { - await document.fonts.ready; - return _loadFonts(fonts); +export function loadFonts(fonts?: (string | FontFace)[]): Promise { + return document.fonts.ready.then(() => _loadFonts(fonts)); } export class WebFontsAddon implements ITerminalAddon, IWebFontsApi { private _term: Terminal | undefined; - constructor(public forceInitialRelayout: boolean = true) { } + constructor(public initialRelayout: boolean = true) { } public dispose(): void { this._term = undefined; @@ -119,12 +118,12 @@ export class WebFontsAddon implements ITerminalAddon, IWebFontsApi { public activate(term: Terminal): void { this._term = term; - if (this.forceInitialRelayout) { + if (this.initialRelayout) { document.fonts.ready.then(() => this.relayout()); } } - public async loadFonts(fonts?: (string | FontFace)[]): Promise { + public loadFonts(fonts?: (string | FontFace)[]): Promise { return loadFonts(fonts); } diff --git a/addons/addon-web-fonts/typings/addon-web-fonts.d.ts b/addons/addon-web-fonts/typings/addon-web-fonts.d.ts index 9be3de21..c7a159ed 100644 --- a/addons/addon-web-fonts/typings/addon-web-fonts.d.ts +++ b/addons/addon-web-fonts/typings/addon-web-fonts.d.ts @@ -13,9 +13,9 @@ declare module '@xterm/addon-web-fonts' { */ export class WebFontsAddon implements ITerminalAddon { /** - * @param forceInitialRelayout Force initial relayout, if a webfont was found (default true). + * @param initialRelayout Force initial relayout, if a webfont was found (default true). */ - constructor(forceInitialRelayout?: boolean); + constructor(initialRelayout?: boolean); public activate(terminal: Terminal): void; public dispose(): void; @@ -44,7 +44,7 @@ declare module '@xterm/addon-web-fonts' { * Call this method, if a terminal with webfonts is stuck with broken * glyph metrics. * - * Returns a promise on completion. + * The returned promise will resolve, when font loading and layouting are done. */ public relayout(): Promise; } From 363fc84fd27933111893199ff45a100c573cbc6d Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 3 Jan 2026 09:02:06 -0800 Subject: [PATCH 14/38] Merge font-ligatures into addon-ligatures Fixes #5568 --- addons/addon-ligatures/LICENSE | 26 +- addons/addon-ligatures/package.json | 7 +- addons/addon-ligatures/src/font.ts | 2 +- .../src/fontLigatures/flatten.ts | 35 ++ .../src/fontLigatures/index.ts | 262 ++++++++++++ .../src/fontLigatures/merge.ts | 372 ++++++++++++++++++ .../src/fontLigatures/mergeRange.ts | 64 +++ .../src/fontLigatures/processors/6-1.ts | 82 ++++ .../src/fontLigatures/processors/6-2.ts | 96 +++++ .../src/fontLigatures/processors/6-3.ts | 73 ++++ .../src/fontLigatures/processors/8-1.ts | 69 ++++ .../src/fontLigatures/processors/classDef.ts | 85 ++++ .../src/fontLigatures/processors/coverage.ts | 43 ++ .../src/fontLigatures/processors/helper.ts | 163 ++++++++ .../fontLigatures/processors/substitution.ts | 62 +++ .../src/fontLigatures/tables.ts | 112 ++++++ .../src/fontLigatures/types.ts | 86 ++++ .../addon-ligatures/src/fontLigatures/walk.ts | 67 ++++ addons/addon-ligatures/src/index.ts | 2 +- addons/addon-ligatures/test/index.test.ts | 360 +++++++++++++++++ addons/addon-ligatures/test/merge.test.ts | 226 +++++++++++ .../addon-ligatures/test/mergeRange.test.ts | 50 +++ addons/addon-ligatures/test/parse.test.ts | 2 +- package-lock.json | 74 ++-- 24 files changed, 2383 insertions(+), 37 deletions(-) create mode 100644 addons/addon-ligatures/src/fontLigatures/flatten.ts create mode 100644 addons/addon-ligatures/src/fontLigatures/index.ts create mode 100644 addons/addon-ligatures/src/fontLigatures/merge.ts create mode 100644 addons/addon-ligatures/src/fontLigatures/mergeRange.ts create mode 100644 addons/addon-ligatures/src/fontLigatures/processors/6-1.ts create mode 100644 addons/addon-ligatures/src/fontLigatures/processors/6-2.ts create mode 100644 addons/addon-ligatures/src/fontLigatures/processors/6-3.ts create mode 100644 addons/addon-ligatures/src/fontLigatures/processors/8-1.ts create mode 100644 addons/addon-ligatures/src/fontLigatures/processors/classDef.ts create mode 100644 addons/addon-ligatures/src/fontLigatures/processors/coverage.ts create mode 100644 addons/addon-ligatures/src/fontLigatures/processors/helper.ts create mode 100644 addons/addon-ligatures/src/fontLigatures/processors/substitution.ts create mode 100644 addons/addon-ligatures/src/fontLigatures/tables.ts create mode 100644 addons/addon-ligatures/src/fontLigatures/types.ts create mode 100644 addons/addon-ligatures/src/fontLigatures/walk.ts create mode 100644 addons/addon-ligatures/test/index.test.ts create mode 100644 addons/addon-ligatures/test/merge.test.ts create mode 100644 addons/addon-ligatures/test/mergeRange.test.ts diff --git a/addons/addon-ligatures/LICENSE b/addons/addon-ligatures/LICENSE index b442934b..b7ca5139 100644 --- a/addons/addon-ligatures/LICENSE +++ b/addons/addon-ligatures/LICENSE @@ -1,6 +1,30 @@ +Copyright (c) 2019, The xterm.js authors (https://github.com/xtermjs/xterm.js) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +--- + +The code that analyzes font ligatures is forked from https://github.com/princjef/font-ligatures with this license: + MIT License -Copyright (c) 2018 +Copyright (c) 2018 Jeffrey Principe Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/addons/addon-ligatures/package.json b/addons/addon-ligatures/package.json index 85a19c36..5150f383 100644 --- a/addons/addon-ligatures/package.json +++ b/addons/addon-ligatures/package.json @@ -32,11 +32,14 @@ ], "license": "MIT", "dependencies": { - "font-finder": "^1.1.0", - "font-ligatures": "^1.4.1" + "lru-cache": "^6.0.0", + "opentype.js": "^0.8.0" }, "devDependencies": { + "@types/lru-cache": "^5.1.0", + "@types/opentype.js": "^0.7.0", "axios": "^1.6.0", + "font-finder": "^1.1.0", "mkdirp": "0.5.5", "yauzl": "^2.10.0" } diff --git a/addons/addon-ligatures/src/font.ts b/addons/addon-ligatures/src/font.ts index ed7910cf..196651ac 100644 --- a/addons/addon-ligatures/src/font.ts +++ b/addons/addon-ligatures/src/font.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { Font, loadBuffer } from 'font-ligatures'; +import { Font, loadBuffer } from './fontLigatures/index'; import parse from './parse'; diff --git a/addons/addon-ligatures/src/fontLigatures/flatten.ts b/addons/addon-ligatures/src/fontLigatures/flatten.ts new file mode 100644 index 00000000..38e99c3a --- /dev/null +++ b/addons/addon-ligatures/src/fontLigatures/flatten.ts @@ -0,0 +1,35 @@ +import { LookupTree, FlattenedLookupTree, LookupTreeEntry, FlattenedLookupTreeEntry } from './types'; + +export default function flatten(tree: LookupTree): FlattenedLookupTree { + const result: FlattenedLookupTree = {}; + for (const [glyphId, entry] of Object.entries(tree.individual)) { + result[glyphId] = flattenEntry(entry); + } + + for (const { range, entry } of tree.range) { + const flattened = flattenEntry(entry); + for (let glyphId = range[0]; glyphId < range[1]; glyphId++) { + result[glyphId] = flattened; + } + } + + return result; +} + +function flattenEntry(entry: LookupTreeEntry): FlattenedLookupTreeEntry { + const result: FlattenedLookupTreeEntry = {}; + + if (entry.forward) { + result.forward = flatten(entry.forward); + } + + if (entry.reverse) { + result.reverse = flatten(entry.reverse); + } + + if (entry.lookup) { + result.lookup = entry.lookup; + } + + return result; +} diff --git a/addons/addon-ligatures/src/fontLigatures/index.ts b/addons/addon-ligatures/src/fontLigatures/index.ts new file mode 100644 index 00000000..e91eea92 --- /dev/null +++ b/addons/addon-ligatures/src/fontLigatures/index.ts @@ -0,0 +1,262 @@ +import * as opentype from 'opentype.js'; +import LRUCache = require('lru-cache'); + +import { Font, LigatureData, FlattenedLookupTree, LookupTree, Options } from './types'; +import mergeTrees from './merge'; +import walkTree from './walk'; +import mergeRange from './mergeRange'; + +import buildTreeGsubType6Format1 from './processors/6-1'; +import buildTreeGsubType6Format2 from './processors/6-2'; +import buildTreeGsubType6Format3 from './processors/6-3'; +import buildTreeGsubType8Format1 from './processors/8-1'; +import flatten from './flatten'; + +class FontImpl implements Font { + private _font: opentype.Font; + private _lookupTrees: { tree: FlattenedLookupTree; processForward: boolean; }[] = []; + private _glyphLookups: { [glyphId: string]: number[] } = {}; + private _cache?: LRUCache; + + constructor(font: opentype.Font, options: Required) { + this._font = font; + + if (options.cacheSize > 0) { + this._cache = new LRUCache({ + max: options.cacheSize, + length: ((val: LigatureData | [number, number][], key: string) => key.length) as any + }); + } + + const caltFeatures = this._font.tables.gsub && this._font.tables.gsub.features.filter((f: { tag: string }) => f.tag === 'calt') || []; + const lookupIndices: number[] = caltFeatures + .reduce((acc: number[], val: { feature: { lookupListIndexes: number[] } }) => [...acc, ...val.feature.lookupListIndexes], []); + + const allLookups = this._font.tables.gsub && this._font.tables.gsub.lookups || []; + const lookupGroups = allLookups.filter((l: unknown, i: number) => lookupIndices.some(idx => idx === i)); + + for (const [index, lookup] of lookupGroups.entries()) { + const trees: LookupTree[] = []; + switch (lookup.lookupType) { + case 6: + for (const [index, table] of lookup.subtables.entries()) { + switch (table.substFormat) { + case 1: + trees.push(buildTreeGsubType6Format1(table, allLookups, index)); + break; + case 2: + trees.push(buildTreeGsubType6Format2(table, allLookups, index)); + break; + case 3: + trees.push(buildTreeGsubType6Format3(table, allLookups, index)); + break; + } + } + break; + case 8: + for (const [index, table] of lookup.subtables.entries()) { + trees.push(buildTreeGsubType8Format1(table, index)); + } + break; + } + + const tree = flatten(mergeTrees(trees)); + + this._lookupTrees.push({ + tree, + processForward: lookup.lookupType !== 8 + }); + + for (const glyphId of Object.keys(tree)) { + if (!this._glyphLookups[glyphId]) { + this._glyphLookups[glyphId] = []; + } + + this._glyphLookups[glyphId].push(index); + } + } + } + + findLigatures(text: string): LigatureData { + const cached = this._cache && this._cache.get(text); + if (cached && !Array.isArray(cached)) { + return cached; + } + + const glyphIds: number[] = []; + for (const char of text) { + glyphIds.push(this._font.charToGlyphIndex(char)); + } + + // If there are no lookup groups, there's no point looking for + // replacements. This gives us a minor performance boost for fonts with + // no ligatures + if (this._lookupTrees.length === 0) { + return { + inputGlyphs: glyphIds, + outputGlyphs: glyphIds, + contextRanges: [] + }; + } + + const result = this._findInternal(glyphIds.slice()); + const finalResult: LigatureData = { + inputGlyphs: glyphIds, + outputGlyphs: result.sequence, + contextRanges: result.ranges + }; + if (this._cache) { + this._cache.set(text, finalResult); + } + + return finalResult; + } + + findLigatureRanges(text: string): [number, number][] { + // Short circuit the process if there are no possible ligatures in the + // font + if (this._lookupTrees.length === 0) { + return []; + } + + const cached = this._cache && this._cache.get(text); + if (cached) { + return Array.isArray(cached) ? cached : cached.contextRanges; + } + + const glyphIds: number[] = []; + for (const char of text) { + glyphIds.push(this._font.charToGlyphIndex(char)); + } + + const result = this._findInternal(glyphIds); + if (this._cache) { + this._cache.set(text, result.ranges); + } + + return result.ranges; + } + + private _findInternal(sequence: number[]): { sequence: number[]; ranges: [number, number][]; } { + const ranges: [number, number][] = []; + + let nextLookup = this._getNextLookup(sequence, 0); + while (nextLookup.index !== null) { + const lookup = this._lookupTrees[nextLookup.index]; + if (lookup.processForward) { + let lastGlyphIndex = nextLookup.last; + for (let i = nextLookup.first; i < lastGlyphIndex; i++) { + const result = walkTree(lookup.tree, sequence, i, i); + if (result) { + for (let j = 0; j < result.substitutions.length; j++) { + const sub = result.substitutions[j]; + if (sub !== null) { + sequence[i + j] = sub; + } + } + + mergeRange( + ranges, + result.contextRange[0] + i, + result.contextRange[1] + i + ); + + // Substitutions can end up extending the search range + if (i + result.length >= lastGlyphIndex) { + lastGlyphIndex = i + result.length + 1; + } + + i += result.length - 1; + } + } + } else { + // We don't need to do the lastGlyphIndex tracking here because + // reverse processing isn't allowed to replace more than one + // character at a time. + for (let i = nextLookup.last - 1; i >= nextLookup.first; i--) { + const result = walkTree(lookup.tree, sequence, i, i); + if (result) { + for (let j = 0; j < result.substitutions.length; j++) { + const sub = result.substitutions[j]; + if (sub !== null) { + sequence[i + j] = sub; + } + } + + mergeRange( + ranges, + result.contextRange[0] + i, + result.contextRange[1] + i + ); + + i -= result.length - 1; + } + } + } + + nextLookup = this._getNextLookup(sequence, nextLookup.index + 1); + } + + return { sequence, ranges }; + } + + /** + * Returns the lookup and glyph range for the first lookup that might + * contain a match. + * + * @param sequence Input glyph sequence + * @param start The first input to try + */ + private _getNextLookup(sequence: number[], start: number): { index: number | null; first: number; last: number; } { + const result: { index: number | null; first: number; last: number; } = { + index: null, + first: Infinity, + last: -1 + }; + + // Loop through each glyph and find the first valid lookup for it + for (let i = 0; i < sequence.length; i++) { + const lookups = this._glyphLookups[sequence[i]]; + if (!lookups) { + continue; + } + + for (let j = 0; j < lookups.length; j++) { + const lookupIndex = lookups[j]; + if (lookupIndex >= start) { + // Update the lookup information if it's the one we're + // storing or earlier than it. + if (result.index === null || lookupIndex <= result.index) { + result.index = lookupIndex; + + if (result.first > i) { + result.first = i; + } + + result.last = i + 1; + } + + break; + } + } + } + + return result; + } +} + +/** + * Load the font from it's binary data. The returned value can be used to find + * ligatures for the font. + * + * @param buffer ArrayBuffer of the font to load + */ +export function loadBuffer(buffer: ArrayBuffer, options?: Options): Font { + const font = opentype.parse(buffer); + return new FontImpl(font, { + cacheSize: 0, + ...options + }); +} + +export { Font, LigatureData, Options }; diff --git a/addons/addon-ligatures/src/fontLigatures/merge.ts b/addons/addon-ligatures/src/fontLigatures/merge.ts new file mode 100644 index 00000000..d0b1f23e --- /dev/null +++ b/addons/addon-ligatures/src/fontLigatures/merge.ts @@ -0,0 +1,372 @@ +import { LookupTree, LookupTreeEntry } from './types'; + +/** + * Merges the provided trees into a single lookup tree. When conflicting lookups + * are encountered between two trees, the one with the lower index, then the + * lower subindex is chosen. + * + * @param trees Array of trees to merge. Entries in earlier trees are favored + * over those in later trees when there is a choice. + */ +export default function mergeTrees(trees: LookupTree[]): LookupTree { + const result: LookupTree = { + individual: {}, + range: [] + }; + + for (const tree of trees) { + mergeSubtree(result, tree); + } + + return result; +} + +/** + * Recursively merges the data for the mergeTree into the mainTree. + * + * @param mainTree The tree where the values should be merged + * @param mergeTree The tree to be merged into the mainTree + */ +function mergeSubtree(mainTree: LookupTree, mergeTree: LookupTree): void { + // Need to fix this recursively (and handle lookups) + for (const [glyphId, value] of Object.entries(mergeTree.individual)) { + // The main tree is guaranteed to have no overlaps between the + // individual and range values, so if we match an invididual, there + // must not be a range + if (mainTree.individual[glyphId]) { + mergeTreeEntry(mainTree.individual[glyphId], value); + } else { + let matched = false; + for (const [index, { range, entry }] of mainTree.range.entries()) { + const overlap = getIndividualOverlap(Number(glyphId), range); + + // Don't overlap + if (overlap.both === null) { + continue; + } + + matched = true; + + // If they overlap, we have to split the range and then + // merge the overlap + mainTree.individual[glyphId] = value; + mergeTreeEntry(mainTree.individual[glyphId], cloneEntry(entry)); + + // When there's an overlap, we also have to fix up the range + // that we had already processed + mainTree.range.splice(index, 1); + for (const glyph of overlap.second) { + if (Array.isArray(glyph)) { + mainTree.range.push({ + range: glyph, + entry: cloneEntry(entry) + }); + } else { + mainTree.individual[glyph] = cloneEntry(entry); + } + } + } + + if (!matched) { + mainTree.individual[glyphId] = value; + } + } + } + + for (const { range, entry } of mergeTree.range) { + // Ranges are more complicated, because they can overlap with + // multiple things, individual and range alike. We start by + // eliminating ranges that are already present in another range + let remainingRanges: (number | [number, number])[] = [range]; + + for (let index = 0; index < mainTree.range.length; index++) { + const { range, entry: resultEntry } = mainTree.range[index]; + for (const [remainingIndex, remainingRange] of remainingRanges.entries()) { + if (Array.isArray(remainingRange)) { + const overlap = getRangeOverlap(remainingRange, range); + if (overlap.both === null) { + continue; + } + + mainTree.range.splice(index, 1); + index--; + + const entryToMerge: LookupTreeEntry = cloneEntry(resultEntry); + if (Array.isArray(overlap.both)) { + mainTree.range.push({ + range: overlap.both, + entry: entryToMerge + }); + } else { + mainTree.individual[overlap.both] = entryToMerge; + } + + mergeTreeEntry(entryToMerge, cloneEntry(entry)); + + for (const second of overlap.second) { + if (Array.isArray(second)) { + mainTree.range.push({ + range: second, + entry: cloneEntry(resultEntry) + }); + } else { + mainTree.individual[second] = cloneEntry(resultEntry); + } + } + + remainingRanges = overlap.first; + } else { + const overlap = getIndividualOverlap(remainingRange, range); + if (overlap.both === null) { + continue; + } + + // If they overlap, we have to split the range and then + // merge the overlap + mainTree.individual[remainingRange] = cloneEntry(entry); + mergeTreeEntry(mainTree.individual[remainingRange], cloneEntry(resultEntry)); + + // When there's an overlap, we also have to fix up the range + // that we had already processed + mainTree.range.splice(index, 1); + index--; + + for (const glyph of overlap.second) { + if (Array.isArray(glyph)) { + mainTree.range.push({ + range: glyph, + entry: cloneEntry(resultEntry) + }); + } else { + mainTree.individual[glyph] = cloneEntry(resultEntry); + } + } + + remainingRanges.splice(remainingIndex, 1, ...overlap.first); + break; + } + } + } + + // Next, we run the same against any individual glyphs + for (const glyphId of Object.keys(mainTree.individual)) { + for (const [remainingIndex, remainingRange] of remainingRanges.entries()) { + if (Array.isArray(remainingRange)) { + const overlap = getIndividualOverlap(Number(glyphId), remainingRange); + if (overlap.both === null) { + continue; + } + + // If they overlap, we have to merge the overlap + mergeTreeEntry(mainTree.individual[glyphId], cloneEntry(entry)); + + // Update the remaining ranges + remainingRanges.splice(remainingIndex, 1, ...overlap.second); + break; + } else { + if (Number(glyphId) === remainingRange) { + mergeTreeEntry(mainTree.individual[glyphId], cloneEntry(entry)); + break; + } + } + } + } + + // Any remaining ranges should just be added directly + for (const remainingRange of remainingRanges) { + if (Array.isArray(remainingRange)) { + mainTree.range.push({ + range: remainingRange, + entry: cloneEntry(entry) + }); + } else { + mainTree.individual[remainingRange] = cloneEntry(entry); + } + } + } +} + +/** + * Recursively merges the entry forr the mergeTree into the mainTree + * + * @param mainTree The entry where the values should be merged + * @param mergeTree The entry to merge into the mainTree + */ +function mergeTreeEntry(mainTree: LookupTreeEntry, mergeTree: LookupTreeEntry): void { + if ( + mergeTree.lookup && ( + !mainTree.lookup || + mainTree.lookup.index > mergeTree.lookup.index || + (mainTree.lookup.index === mergeTree.lookup.index && mainTree.lookup.subIndex > mergeTree.lookup.subIndex) + ) + ) { + mainTree.lookup = mergeTree.lookup; + } + + if (mergeTree.forward) { + if (!mainTree.forward) { + mainTree.forward = mergeTree.forward; + } else { + mergeSubtree(mainTree.forward, mergeTree.forward); + } + } + + if (mergeTree.reverse) { + if (!mainTree.reverse) { + mainTree.reverse = mergeTree.reverse; + } else { + mergeSubtree(mainTree.reverse, mergeTree.reverse); + } + } +} + +interface Overlap { + first: (number | [number, number])[]; + second: (number | [number, number])[]; + both: number | [number, number] | null; +} + +/** + * Determines the overlap (if any) between two ranges. Returns the distinct + * ranges for each range and the overlap (if any). + * + * @param first First range + * @param second Second range + */ +function getRangeOverlap(first: [number, number], second: [number, number]): Overlap { + const result: Overlap = { + first: [], + second: [], + both: null + }; + + // Both + if (first[0] < second[1] && second[0] < first[1]) { + const start = Math.max(first[0], second[0]); + const end = Math.min(first[1], second[1]); + result.both = rangeOrIndividual(start, end); + } + + // Before + if (first[0] < second[0]) { + const start = first[0]; + const end = Math.min(second[0], first[1]); + result.first.push(rangeOrIndividual(start, end)); + } else if (second[0] < first[0]) { + const start = second[0]; + const end = Math.min(second[1], first[0]); + result.second.push(rangeOrIndividual(start, end)); + } + + // After + if (first[1] > second[1]) { + const start = Math.max(first[0], second[1]); + const end = first[1]; + result.first.push(rangeOrIndividual(start, end)); + } else if (second[1] > first[1]) { + const start = Math.max(first[1], second[0]); + const end = second[1]; + result.second.push(rangeOrIndividual(start, end)); + } + + return result; +} + +/** + * Determines the overlap (if any) between the individual glyph and the range + * provided. Returns the glyphs and/or ranges that are unique to each provided + * and the overlap (if any). + * + * @param first Individual glyph + * @param second Range + */ +function getIndividualOverlap(first: number, second: [number, number]): Overlap { + // Disjoint + if (first < second[0] || first > second[1]) { + return { + first: [first], + second: [second], + both: null + }; + } + + const result: Overlap = { + first: [], + second: [], + both: first + }; + + if (second[0] < first) { + result.second.push(rangeOrIndividual(second[0], first)); + } + + if (second[1] > first) { + result.second.push(rangeOrIndividual(first + 1, second[1])); + } + + return result; +} + +/** + * Returns an individual glyph if the range is of size one or a range if it is + * larger. + * + * @param start Beginning of the range (inclusive) + * @param end End of the range (exclusive) + */ +function rangeOrIndividual(start: number, end: number): number | [number, number] { + if (end - start === 1) { + return start; + } else { + return [start, end]; + } +} + +/** + * Clones an individual lookup tree entry. + * + * @param entry Lookup tree entry to clone + */ +function cloneEntry(entry: LookupTreeEntry): LookupTreeEntry { + const result: LookupTreeEntry = {}; + + if (entry.forward) { + result.forward = cloneTree(entry.forward); + } + + if (entry.reverse) { + result.reverse = cloneTree(entry.reverse); + } + + if (entry.lookup) { + result.lookup = { + contextRange: entry.lookup.contextRange.slice() as [number, number], + index: entry.lookup.index, + length: entry.lookup.length, + subIndex: entry.lookup.subIndex, + substitutions: entry.lookup.substitutions.slice() + }; + } + + return result; +} + +/** + * Clones a lookup tree. + * + * @param tree Lookup tree to clone + */ +function cloneTree(tree: LookupTree): LookupTree { + const individual: { [glyphId: string]: LookupTreeEntry; } = {}; + for (const [glyphId, entry] of Object.entries(tree.individual)) { + individual[glyphId] = cloneEntry(entry); + } + + return { + individual, + range: tree.range.map(({ range, entry }) => ({ + range: range.slice() as [number, number], + entry: cloneEntry(entry) + })) + }; +} diff --git a/addons/addon-ligatures/src/fontLigatures/mergeRange.ts b/addons/addon-ligatures/src/fontLigatures/mergeRange.ts new file mode 100644 index 00000000..7c872a8a --- /dev/null +++ b/addons/addon-ligatures/src/fontLigatures/mergeRange.ts @@ -0,0 +1,64 @@ +/** + * Merges the range defined by the provided start and end into the list of + * existing ranges. The merge is done in place on the existing range for + * performance and is also returned. + * + * @param ranges Existing range list + * @param newRangeStart Start position of the range to merge, inclusive + * @param newRangeEnd End position of range to merge, exclusive + */ +export default function mergeRange(ranges: [number, number][], newRangeStart: number, newRangeEnd: number): [number, number][] { + let inRange = false; + for (let i = 0; i < ranges.length; i++) { + const range = ranges[i]; + if (!inRange) { + if (newRangeEnd <= range[0]) { + // Case 1: New range is before the search range + ranges.splice(i, 0, [newRangeStart, newRangeEnd]); + return ranges; + } else if (newRangeEnd <= range[1]) { + // Case 2: New range is either wholly contained within the + // search range or overlaps with the front of it + range[0] = Math.min(newRangeStart, range[0]); + return ranges; + } else if (newRangeStart < range[1]) { + // Case 3: New range either wholly contains the search range + // or overlaps with the end of it + range[0] = Math.min(newRangeStart, range[0]); + inRange = true; + } else { + // Case 4: New range starts after the search range + continue; + } + } else { + if (newRangeEnd <= range[0]) { + // Case 5: New range extends from previous range but doesn't + // reach the current one + ranges[i - 1][1] = newRangeEnd; + return ranges; + } else if (newRangeEnd <= range[1]) { + // Case 6: New range extends from prvious range into the + // current range + ranges[i - 1][1] = Math.max(newRangeEnd, range[1]); + ranges.splice(i, 1); + inRange = false; + return ranges; + } else { + // Case 7: New range extends from previous range past the + // end of the current range + ranges.splice(i, 1); + i--; + } + } + } + + if (inRange) { + // Case 8: New range extends past the last existing range + ranges[ranges.length - 1][1] = newRangeEnd; + } else { + // Case 9: New range starts after the last existing range + ranges.push([newRangeStart, newRangeEnd]); + } + + return ranges; +} diff --git a/addons/addon-ligatures/src/fontLigatures/processors/6-1.ts b/addons/addon-ligatures/src/fontLigatures/processors/6-1.ts new file mode 100644 index 00000000..2f0dad18 --- /dev/null +++ b/addons/addon-ligatures/src/fontLigatures/processors/6-1.ts @@ -0,0 +1,82 @@ +import { ChainingContextualSubstitutionTable, Lookup } from '../tables'; +import { LookupTree } from '../types'; + +import { listGlyphsByIndex } from './coverage'; +import { processInputPosition, processLookaheadPosition, processBacktrackPosition, getInputTree, EntryMeta } from './helper'; + +/** + * Build lookup tree for GSUB lookup table 6, format 1. + * https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#61-chaining-context-substitution-format-1-simple-glyph-contexts + * + * @param table JSON representation of the table + * @param lookups List of lookup tables + * @param tableIndex Index of this table in the overall lookup + */ +export default function buildTree(table: ChainingContextualSubstitutionTable.Format1, lookups: Lookup[], tableIndex: number): LookupTree { + const result: LookupTree = { + individual: {}, + range: [] + }; + + const firstGlyphs = listGlyphsByIndex(table.coverage); + + for (const { glyphId, index } of firstGlyphs) { + const chainRuleSet = table.chainRuleSets[index]; + + // If the chain rule set is null there's nothing to do with this table. + if (!chainRuleSet) { + continue; + } + + for (const [subIndex, subTable] of chainRuleSet.entries()) { + let currentEntries: EntryMeta[] = getInputTree( + result, + subTable.lookupRecords, + lookups, + 0, + glyphId + ).map(({ entry, substitution }) => ({ entry, substitutions: [substitution] })); + + // We walk forward, then backward + for (const [index, glyph] of subTable.input.entries()) { + currentEntries = processInputPosition( + [glyph], + index + 1, + currentEntries, + subTable.lookupRecords, + lookups + ); + } + + for (const glyph of subTable.lookahead) { + currentEntries = processLookaheadPosition( + [glyph], + currentEntries + ); + } + + for (const glyph of subTable.backtrack) { + currentEntries = processBacktrackPosition( + [glyph], + currentEntries + ); + } + + // When we get to the end, insert the lookup information + for (const { entry, substitutions } of currentEntries) { + entry.lookup = { + substitutions, + length: subTable.input.length + 1, + index: tableIndex, + subIndex, + contextRange: [ + -1 * subTable.backtrack.length, + 1 + subTable.input.length + subTable.lookahead.length + ] + }; + } + } + } + + return result; +} diff --git a/addons/addon-ligatures/src/fontLigatures/processors/6-2.ts b/addons/addon-ligatures/src/fontLigatures/processors/6-2.ts new file mode 100644 index 00000000..9cd8eaf3 --- /dev/null +++ b/addons/addon-ligatures/src/fontLigatures/processors/6-2.ts @@ -0,0 +1,96 @@ +import { ChainingContextualSubstitutionTable, Lookup } from '../tables'; +import { LookupTree } from '../types'; +import mergeTrees from '../merge'; + +import { listGlyphsByIndex } from './coverage'; +import getGlyphClass, { listClassGlyphs } from './classDef'; +import { processInputPosition, processLookaheadPosition, processBacktrackPosition, getInputTree, EntryMeta } from './helper'; + +/** + * Build lookup tree for GSUB lookup table 6, format 2. + * https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#62-chaining-context-substitution-format-2-class-based-glyph-contexts + * + * @param table JSON representation of the table + * @param lookups List of lookup tables + * @param tableIndex Index of this table in the overall lookup + */ +export default function buildTree(table: ChainingContextualSubstitutionTable.Format2, lookups: Lookup[], tableIndex: number): LookupTree { + const results: LookupTree[] = []; + + const firstGlyphs = listGlyphsByIndex(table.coverage); + + for (const { glyphId } of firstGlyphs) { + const firstInputClass = getGlyphClass(table.inputClassDef, glyphId); + for (const [glyphId, inputClass] of firstInputClass.entries()) { + // istanbul ignore next - invalid font + if (inputClass === null) { + continue; + } + + const classSet = table.chainClassSet[inputClass]; + + // If the class set is null there's nothing to do with this table. + if (!classSet) { + continue; + } + + for (const [subIndex, subTable] of classSet.entries()) { + const result: LookupTree = { + individual: {}, + range: [] + }; + + let currentEntries: EntryMeta[] = getInputTree( + result, + subTable.lookupRecords, + lookups, + 0, + glyphId + ).map(({ entry, substitution }) => ({ entry, substitutions: [substitution] })); + + for (const [index, classNum] of subTable.input.entries()) { + currentEntries = processInputPosition( + listClassGlyphs(table.inputClassDef, classNum), + index + 1, + currentEntries, + subTable.lookupRecords, + lookups + ); + } + + for (const classNum of subTable.lookahead) { + currentEntries = processLookaheadPosition( + listClassGlyphs(table.lookaheadClassDef, classNum), + currentEntries + ); + } + + for (const classNum of subTable.backtrack) { + currentEntries = processBacktrackPosition( + listClassGlyphs(table.backtrackClassDef, classNum), + currentEntries + ); + } + + // When we get to the end, all of the entries we've accumulated + // should have a lookup defined + for (const { entry, substitutions } of currentEntries) { + entry.lookup = { + substitutions, + index: tableIndex, + subIndex, + length: subTable.input.length + 1, + contextRange: [ + -1 * subTable.backtrack.length, + 1 + subTable.input.length + subTable.lookahead.length + ] + }; + } + + results.push(result); + } + } + } + + return mergeTrees(results); +} diff --git a/addons/addon-ligatures/src/fontLigatures/processors/6-3.ts b/addons/addon-ligatures/src/fontLigatures/processors/6-3.ts new file mode 100644 index 00000000..fc671e7b --- /dev/null +++ b/addons/addon-ligatures/src/fontLigatures/processors/6-3.ts @@ -0,0 +1,73 @@ +import { ChainingContextualSubstitutionTable, Lookup } from '../tables'; +import { LookupTree } from '../types'; + +import { listGlyphsByIndex } from './coverage'; +import { processInputPosition, processLookaheadPosition, processBacktrackPosition, getInputTree, EntryMeta } from './helper'; + +/** + * Build lookup tree for GSUB lookup table 6, format 3. + * https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#63-chaining-context-substitution-format-3-coverage-based-glyph-contexts + * + * @param table JSON representation of the table + * @param lookups List of lookup tables + * @param tableIndex Index of this table in the overall lookup + */ +export default function buildTree(table: ChainingContextualSubstitutionTable.Format3, lookups: Lookup[], tableIndex: number): LookupTree { + const result: LookupTree = { + individual: {}, + range: [] + }; + + const firstGlyphs = listGlyphsByIndex(table.inputCoverage[0]); + + for (const { glyphId } of firstGlyphs) { + let currentEntries: EntryMeta[] = getInputTree( + result, + table.lookupRecords, + lookups, + 0, + glyphId + ).map(({ entry, substitution }) => ({ entry, substitutions: [substitution] })); + + for (const [index, coverage] of table.inputCoverage.slice(1).entries()) { + currentEntries = processInputPosition( + listGlyphsByIndex(coverage).map(glyph => glyph.glyphId), + index + 1, + currentEntries, + table.lookupRecords, + lookups + ); + } + + for (const coverage of table.lookaheadCoverage) { + currentEntries = processLookaheadPosition( + listGlyphsByIndex(coverage).map(glyph => glyph.glyphId), + currentEntries + ); + } + + for (const coverage of table.backtrackCoverage) { + currentEntries = processBacktrackPosition( + listGlyphsByIndex(coverage).map(glyph => glyph.glyphId), + currentEntries + ); + } + + // When we get to the end, all of the entries we've accumulated + // should have a lookup defined + for (const { entry, substitutions } of currentEntries) { + entry.lookup = { + substitutions, + index: tableIndex, + subIndex: 0, + length: table.inputCoverage.length, + contextRange: [ + -1 * table.backtrackCoverage.length, + table.inputCoverage.length + table.lookaheadCoverage.length + ] + }; + } + } + + return result; +} diff --git a/addons/addon-ligatures/src/fontLigatures/processors/8-1.ts b/addons/addon-ligatures/src/fontLigatures/processors/8-1.ts new file mode 100644 index 00000000..620d2e20 --- /dev/null +++ b/addons/addon-ligatures/src/fontLigatures/processors/8-1.ts @@ -0,0 +1,69 @@ +import { ReverseChainingContextualSingleSubstitutionTable } from '../tables'; +import { LookupTree, LookupTreeEntry } from '../types'; + +import { listGlyphsByIndex } from './coverage'; +import { processLookaheadPosition, processBacktrackPosition, EntryMeta } from './helper'; + +/** + * Build lookup tree for GSUB lookup table 8, format 1. + * https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#81-reverse-chaining-contextual-single-substitution-format-1-coverage-based-glyph-contexts + * + * @param table JSON representation of the table + * @param tableIndex Index of this table in the overall lookup + */ +export default function buildTree(table: ReverseChainingContextualSingleSubstitutionTable, tableIndex: number): LookupTree { + const result: LookupTree = { + individual: {}, + range: [] + }; + + const glyphs = listGlyphsByIndex(table.coverage); + + for (const { glyphId, index } of glyphs) { + const initialEntry: LookupTreeEntry = {}; + if (Array.isArray(glyphId)) { + result.range.push({ + entry: initialEntry, + range: glyphId + }); + } else { + result.individual[glyphId] = initialEntry; + } + + let currentEntries: EntryMeta[] = [{ + entry: initialEntry, + substitutions: [table.substitutes[index]] + }]; + + // We walk forward, then backward + for (const coverage of table.lookaheadCoverage) { + currentEntries = processLookaheadPosition( + listGlyphsByIndex(coverage).map(glyph => glyph.glyphId), + currentEntries + ); + } + + for (const coverage of table.backtrackCoverage) { + currentEntries = processBacktrackPosition( + listGlyphsByIndex(coverage).map(glyph => glyph.glyphId), + currentEntries + ); + } + + // When we get to the end, insert the lookup information + for (const { entry, substitutions } of currentEntries) { + entry.lookup = { + substitutions, + index: tableIndex, + subIndex: 0, + length: 1, + contextRange: [ + -1 * table.backtrackCoverage.length, + 1 + table.lookaheadCoverage.length + ] + }; + } + } + + return result; +} diff --git a/addons/addon-ligatures/src/fontLigatures/processors/classDef.ts b/addons/addon-ligatures/src/fontLigatures/processors/classDef.ts new file mode 100644 index 00000000..3f2dc21e --- /dev/null +++ b/addons/addon-ligatures/src/fontLigatures/processors/classDef.ts @@ -0,0 +1,85 @@ +import { ClassDefTable } from '../tables'; + +/** + * Get the number of the class to which the glyph belongs, or null if it doesn't + * belong to any of them. + * + * @param table JSON representation of the class def table + * @param glyphId Index of the glyph to look for + */ +export default function getGlyphClass(table: ClassDefTable, glyphId: number | [number, number]): Map { + switch (table.format) { + // https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table-format-2 + case 2: + if (Array.isArray(glyphId)) { + return getRangeGlyphClass(table, glyphId); + } else { + return new Map([[ + glyphId, + getIndividualGlyphClass(table, glyphId) + ]]); + } + // https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table-format-1 + default: + return new Map([[glyphId, null]]); + } +} + +function getRangeGlyphClass(table: ClassDefTable.Format2, glyphId: [number, number]): Map { + let classStart: number = glyphId[0]; + let currentClass: number | null = getIndividualGlyphClass(table, classStart); + let search: number = glyphId[0] + 1; + + const result = new Map<[number, number] | number, number | null>(); + + while (search < glyphId[1]) { + const clazz = getIndividualGlyphClass(table, search); + if (clazz !== currentClass) { + if (search - classStart <= 1) { + result.set(classStart, currentClass); + } else { + result.set([classStart, search], currentClass); + } + } + search++; + } + + if (search - classStart <= 1) { + result.set(classStart, currentClass); + } else { + result.set([classStart, search], currentClass); + } + + return result; +} + +function getIndividualGlyphClass(table: ClassDefTable.Format2, glyphId: number): number | null { + for (const range of table.ranges) { + if (range.start <= glyphId && range.end >= glyphId) { + return range.classId; + } + } + + return null; +} + +export function listClassGlyphs(table: ClassDefTable, index: number): (number | [number, number])[] { + switch (table.format) { + case 2: + const results: (number | [number, number])[] = []; + for (const range of table.ranges) { + if (range.classId !== index) { + continue; + } + + if (range.end === range.start) { + results.push(range.start); + } else { + results.push([range.start, range.end + 1]); + } + } + return results; + default: + return []; + } +} diff --git a/addons/addon-ligatures/src/fontLigatures/processors/coverage.ts b/addons/addon-ligatures/src/fontLigatures/processors/coverage.ts new file mode 100644 index 00000000..0ee3f42d --- /dev/null +++ b/addons/addon-ligatures/src/fontLigatures/processors/coverage.ts @@ -0,0 +1,43 @@ +import { CoverageTable } from '../tables'; + +/** + * Get the index of the given glyph in the coverage table, or null if it is not + * present in the table. + * + * @param table JSON representation of the coverage table + * @param glyphId Index of the glyph to look for + */ +export default function getCoverageGlyphIndex(table: CoverageTable, glyphId: number): number | null { + switch (table.format) { + // https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-format-1 + case 1: + const index = table.glyphs.indexOf(glyphId); + return index !== -1 + ? index + : null; + // https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-format-2 + case 2: + const range = table.ranges + .find(range => range.start <= glyphId && range.end >= glyphId); + return range + ? range.index + : null; + } +} + +export function listGlyphsByIndex(table: CoverageTable): { glyphId: number | [number, number]; index: number; }[] { + switch (table.format) { + case 1: + return table.glyphs.map((glyphId, index) => ({ glyphId, index })); + case 2: + let results: { glyphId: number | [number, number]; index: number; }[] = []; + for (const [index, range] of table.ranges.entries()) { + if (range.end === range.start) { + results.push({ glyphId: range.start, index }); + } else { + results.push({ glyphId: [range.start, range.end + 1], index }); + } + } + return results; + } +} diff --git a/addons/addon-ligatures/src/fontLigatures/processors/helper.ts b/addons/addon-ligatures/src/fontLigatures/processors/helper.ts new file mode 100644 index 00000000..7186a22d --- /dev/null +++ b/addons/addon-ligatures/src/fontLigatures/processors/helper.ts @@ -0,0 +1,163 @@ +import { LookupTreeEntry, LookupTree } from '../types'; +import { SubstitutionLookupRecord, Lookup } from '../tables'; + +import { getIndividualSubstitutionGlyph, getRangeSubstitutionGlyphs } from './substitution'; + +export interface EntryMeta { + entry: LookupTreeEntry; + substitutions: (number | null)[]; +} + +export function processInputPosition( + glyphs: (number | [number, number])[], + position: number, + currentEntries: EntryMeta[], + lookupRecords: SubstitutionLookupRecord[], + lookups: Lookup[] +): EntryMeta[] { + const nextEntries: EntryMeta[] = []; + for (const currentEntry of currentEntries) { + currentEntry.entry.forward = { + individual: {}, + range: [] + }; + for (const glyph of glyphs) { + nextEntries.push(...getInputTree( + currentEntry.entry.forward, + lookupRecords, + lookups, + position, + glyph + ).map(({ entry, substitution }) => ({ + entry, + substitutions: [...currentEntry.substitutions, substitution] + }))); + } + } + + return nextEntries; +} + +export function processLookaheadPosition( + glyphs: (number | [number, number])[], + currentEntries: EntryMeta[] +): EntryMeta[] { + const nextEntries: EntryMeta[] = []; + for (const currentEntry of currentEntries) { + for (const glyph of glyphs) { + const entry: LookupTreeEntry = {}; + if (!currentEntry.entry.forward) { + currentEntry.entry.forward = { + individual: {}, + range: [] + }; + } + nextEntries.push({ + entry, + substitutions: currentEntry.substitutions + }); + + if (Array.isArray(glyph)) { + currentEntry.entry.forward.range.push({ + entry, + range: glyph + }); + } else { + currentEntry.entry.forward.individual[glyph] = entry; + } + } + } + + return nextEntries; +} + +export function processBacktrackPosition( + glyphs: (number | [number, number])[], + currentEntries: EntryMeta[] +): EntryMeta[] { + const nextEntries: EntryMeta[] = []; + for (const currentEntry of currentEntries) { + for (const glyph of glyphs) { + const entry: LookupTreeEntry = {}; + if (!currentEntry.entry.reverse) { + currentEntry.entry.reverse = { + individual: {}, + range: [] + }; + } + nextEntries.push({ + entry, + substitutions: currentEntry.substitutions + }); + + if (Array.isArray(glyph)) { + currentEntry.entry.reverse.range.push({ + entry, + range: glyph + }); + } else { + currentEntry.entry.reverse.individual[glyph] = entry; + } + } + } + + return nextEntries; +} + +export function getInputTree(tree: LookupTree, substitutions: SubstitutionLookupRecord[], lookups: Lookup[], inputIndex: number, glyphId: number | [number, number]): { entry: LookupTreeEntry; substitution: number | null; }[] { + const result: { entry: LookupTreeEntry; substitution: number | null; }[] = []; + if (!Array.isArray(glyphId)) { + tree.individual[glyphId] = {}; + result.push({ + entry: tree.individual[glyphId], + substitution: getSubstitutionAtPosition(substitutions, lookups, inputIndex, glyphId) + }); + } else { + const subs = getSubstitutionAtPositionRange(substitutions, lookups, inputIndex, glyphId); + for (const [range, substitution] of subs) { + const entry: LookupTreeEntry = {}; + if (Array.isArray(range)) { + tree.range.push({ range, entry }); + } else { + tree.individual[range] = {}; + } + result.push({ entry, substitution }); + } + } + + return result; +} + +function getSubstitutionAtPositionRange(substitutions: SubstitutionLookupRecord[], lookups: Lookup[], index: number, range: [number, number]): Map { + for (const substitution of substitutions.filter(s => s.sequenceIndex === index)) { + for (const substitutionTable of (lookups[substitution.lookupListIndex] as Lookup.Type1).subtables) { + const sub = getRangeSubstitutionGlyphs( + substitutionTable, + range + ); + + if (!Array.from(sub.values()).every(val => val !== null)) { + return sub; + } + } + } + + return new Map([[range, null]]); +} + +function getSubstitutionAtPosition(substitutions: SubstitutionLookupRecord[], lookups: Lookup[], index: number, glyphId: number): number | null { + for (const substitution of substitutions.filter(s => s.sequenceIndex === index)) { + for (const substitutionTable of (lookups[substitution.lookupListIndex] as Lookup.Type1).subtables) { + const sub = getIndividualSubstitutionGlyph( + substitutionTable, + glyphId + ); + + if (sub !== null) { + return sub; + } + } + } + + return null; +} diff --git a/addons/addon-ligatures/src/fontLigatures/processors/substitution.ts b/addons/addon-ligatures/src/fontLigatures/processors/substitution.ts new file mode 100644 index 00000000..975d7a25 --- /dev/null +++ b/addons/addon-ligatures/src/fontLigatures/processors/substitution.ts @@ -0,0 +1,62 @@ +import { SubstitutionTable } from '../tables'; + +import getCoverageGlyphIndex from './coverage'; + +/** + * Get the substitution glyph for the givne glyph, or null if the glyph was not + * found in the table. + * + * @param table JSON representation of the substitution table + * @param glyphId The index of the glpyh to find substitutions for + */ +export function getRangeSubstitutionGlyphs(table: SubstitutionTable, glyphId: [number, number]): Map<[number, number] | number, number | null> { + let replacementStart: number = glyphId[0]; + let currentReplacement: number | null = getIndividualSubstitutionGlyph(table, replacementStart); + let search: number = glyphId[0] + 1; + + const result = new Map<[number, number] | number, number | null>(); + + while (search < glyphId[1]) { + const sub = getIndividualSubstitutionGlyph(table, search); + if (sub !== currentReplacement) { + if (search - replacementStart <= 1) { + result.set(replacementStart, currentReplacement); + } else { + result.set([replacementStart, search], currentReplacement); + } + } + + search++; + } + + if (search - replacementStart <= 1) { + result.set(replacementStart, currentReplacement); + } else { + result.set([replacementStart, search], currentReplacement); + } + + return result; +} + +export function getIndividualSubstitutionGlyph(table: SubstitutionTable, glyphId: number): number | null { + const coverageIndex = getCoverageGlyphIndex(table.coverage, glyphId); + + // istanbul ignore next - invalid font + if (coverageIndex === null) { + return null; + } + + switch (table.substFormat) { + // https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#11-single-substitution-format-1 + case 1: + // TODO: determine if there's a rhyme or reason to the 16-bit + // wraparound and if it can ever be a different number + return (glyphId + table.deltaGlyphId) % (2 ** 16); + // https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#12-single-substitution-format-2 + case 2: + // tslint:disable-next-line + return table.substitute[coverageIndex] != null + ? table.substitute[coverageIndex] + : null; + } +} diff --git a/addons/addon-ligatures/src/fontLigatures/tables.ts b/addons/addon-ligatures/src/fontLigatures/tables.ts new file mode 100644 index 00000000..d3eccd08 --- /dev/null +++ b/addons/addon-ligatures/src/fontLigatures/tables.ts @@ -0,0 +1,112 @@ +export type SubstitutionTable = SubstitutionTable.Format1 | SubstitutionTable.Format2; +export namespace SubstitutionTable { + export interface Format1 { + substFormat: 1; + coverage: CoverageTable; + deltaGlyphId: number; + } + + export interface Format2 { + substFormat: 2; + coverage: CoverageTable; + substitute: number[]; + } +} + +export type CoverageTable = CoverageTable.Format1 | CoverageTable.Format2; +export namespace CoverageTable { + export interface Format1 { + format: 1; + glyphs: number[]; + } + + export interface Format2 { + format: 2; + ranges: { + start: number; + end: number; + index: number; + }[]; + } +} + +export type ChainingContextualSubstitutionTable = ChainingContextualSubstitutionTable.Format1 | + ChainingContextualSubstitutionTable.Format2 | ChainingContextualSubstitutionTable.Format3; +export namespace ChainingContextualSubstitutionTable { + export interface Format1 { + substFormat: 1; + coverage: CoverageTable; + chainRuleSets: ChainSubRuleTable[][]; + } + + export interface Format2 { + substFormat: 2; + coverage: CoverageTable; + backtrackClassDef: ClassDefTable; + inputClassDef: ClassDefTable; + lookaheadClassDef: ClassDefTable; + chainClassSet: (null | ChainSubClassRuleTable[])[]; + } + + export interface Format3 { + substFormat: 3; + backtrackCoverage: CoverageTable[]; + inputCoverage: CoverageTable[]; + lookaheadCoverage: CoverageTable[]; + lookupRecords: SubstitutionLookupRecord[]; + } +} + +export interface ReverseChainingContextualSingleSubstitutionTable { + substFormat: 1; + coverage: CoverageTable; + backtrackCoverage: CoverageTable[]; + lookaheadCoverage: CoverageTable[]; + substitutes: number[]; +} + +export type ClassDefTable = ClassDefTable.Format2; +export namespace ClassDefTable { + export interface Format2 { + format: 2; + ranges: { + start: number; + end: number; + classId: number; + }[]; + } +} + +export interface SubstitutionLookupRecord { + sequenceIndex: number; + lookupListIndex: number; +} + +export type ChainSubRuleTable = ChainSubClassRuleTable; +export interface ChainSubClassRuleTable { + backtrack: number[]; + input: number[]; + lookahead: number[]; + lookupRecords: SubstitutionLookupRecord[]; +} + +export type Lookup = Lookup.Type1 | Lookup.Type6 | Lookup.Type8; +export namespace Lookup { + export interface Type1 { + lookupType: 1; + lookupFlag: number; + subtables: SubstitutionTable[]; + } + + export interface Type6 { + lookupType: 6; + lookupFlag: number; + subtables: ChainingContextualSubstitutionTable[]; + } + + export interface Type8 { + lookupType: 8; + lookupFlag: number; + subtables: ReverseChainingContextualSingleSubstitutionTable[]; + } +} diff --git a/addons/addon-ligatures/src/fontLigatures/types.ts b/addons/addon-ligatures/src/fontLigatures/types.ts new file mode 100644 index 00000000..9364c75c --- /dev/null +++ b/addons/addon-ligatures/src/fontLigatures/types.ts @@ -0,0 +1,86 @@ +export interface SubstitutionResult { + index: number; + contextRange: [number, number]; +} + +/** + * Information about ligatures found in a sequence of text + */ +export interface LigatureData { + /** + * The list of font glyphs in the input text. + */ + inputGlyphs: number[]; + + /** + * The list of font glyphs after performing replacements for font ligatures. + */ + outputGlyphs: number[]; + + /** + * Sorted array of ranges that must be rendered together to produce the + * ligatures in the output sequence. The ranges are inclusive on the left and + * exclusive on the right. + */ + contextRanges: [number, number][]; +} + +export interface Font { + /** + * Scans the provided text for font ligatures, returning an object with + * metadata about the text and any ligatures found. + * + * @param text String to search for ligatures + */ + findLigatures(text: string): LigatureData; + + /** + * Scans the provided text for font ligatures, returning an array of ranges + * where ligatures are located. + * + * @param text String to search for ligatures + */ + findLigatureRanges(text: string): [number, number][]; +} + +export interface Options { + /** + * Optional size of previous results to store, measured in total number of + * characters from input strings. Defaults to no cache (0) + */ + cacheSize?: number; +} + +export interface LookupTree { + individual: { + [glyphId: string]: LookupTreeEntry; + }; + range: { + range: [number, number]; + entry: LookupTreeEntry; + }[]; +} + +export interface LookupTreeEntry { + lookup?: LookupResult; + forward?: LookupTree; + reverse?: LookupTree; +} + +export interface LookupResult { + substitutions: (number | null)[]; + length: number; + index: number; + subIndex: number; + contextRange: [number, number]; +} + +export interface FlattenedLookupTree { + [glyphId: string]: FlattenedLookupTreeEntry; +} + +export interface FlattenedLookupTreeEntry { + lookup?: LookupResult; + forward?: FlattenedLookupTree; + reverse?: FlattenedLookupTree; +} diff --git a/addons/addon-ligatures/src/fontLigatures/walk.ts b/addons/addon-ligatures/src/fontLigatures/walk.ts new file mode 100644 index 00000000..56d6eac1 --- /dev/null +++ b/addons/addon-ligatures/src/fontLigatures/walk.ts @@ -0,0 +1,67 @@ +import { FlattenedLookupTree, LookupResult } from './types'; + +export default function walkTree(tree: FlattenedLookupTree, sequence: number[], startIndex: number, index: number): LookupResult | undefined { + const glyphId = sequence[index]; + let subtree = tree[glyphId]; + if (!subtree) { + return undefined; + } + + let lookup = subtree.lookup; + if (subtree.reverse) { + const reverseLookup = walkReverse(subtree.reverse, sequence, startIndex); + + if ( + (!lookup && reverseLookup) || + ( + reverseLookup && lookup && ( + lookup.index > reverseLookup.index || + (lookup.index === reverseLookup.index && lookup.subIndex > reverseLookup.subIndex) + ) + ) + ) { + lookup = reverseLookup; + } + } + + if (++index >= sequence.length || !subtree.forward) { + return lookup; + } + + const forwardLookup = walkTree(subtree.forward, sequence, startIndex, index); + + if ( + (!lookup && forwardLookup) || + ( + forwardLookup && lookup && ( + lookup.index > forwardLookup.index || + (lookup.index === forwardLookup.index && lookup.subIndex > forwardLookup.subIndex) + ) + ) + ) { + lookup = forwardLookup; + } + + return lookup; +} + +function walkReverse(tree: FlattenedLookupTree, sequence: number[], index: number): LookupResult | undefined { + let subtree = tree[sequence[--index]]; + let lookup: LookupResult | undefined = subtree && subtree.lookup; + while (subtree) { + if ( + (!lookup && subtree.lookup) || + (subtree.lookup && lookup && lookup.index > subtree.lookup.index) + ) { + lookup = subtree.lookup; + } + + if (--index < 0 || !subtree.reverse) { + break; + } + + subtree = subtree.reverse[sequence[index]]; + } + + return lookup; +} diff --git a/addons/addon-ligatures/src/index.ts b/addons/addon-ligatures/src/index.ts index bd8ff215..0c67f510 100644 --- a/addons/addon-ligatures/src/index.ts +++ b/addons/addon-ligatures/src/index.ts @@ -4,7 +4,7 @@ */ import type { Terminal } from '@xterm/xterm'; -import { Font } from 'font-ligatures'; +import { Font } from './fontLigatures/index'; import load from './font'; diff --git a/addons/addon-ligatures/test/index.test.ts b/addons/addon-ligatures/test/index.test.ts new file mode 100644 index 00000000..ecef7661 --- /dev/null +++ b/addons/addon-ligatures/test/index.test.ts @@ -0,0 +1,360 @@ +/** + * Copyright (c) 2018 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import * as path from 'path'; +import * as fs from 'fs'; +import { assert } from 'chai'; + +const fontLigatures = require('../out-esbuild/fontLigatures/index'); +const loadBuffer: (buffer: ArrayBuffer, options?: { cacheSize?: number }) => Font = fontLigatures.loadBuffer; + +interface Font { + findLigatures(text: string): { outputGlyphs: number[]; contextRanges: [number, number][] }; + findLigatureRanges(text: string): [number, number][]; +} + +interface TestCase { + font: string; + input: string; + glyphs: number[]; + ranges: [number, number][]; +} + +const fira = (input: string, glyphs: number[], ranges: [number, number][]): TestCase => + ({ font: 'Fira Code', input, glyphs, ranges }); + +const iosevka = (input: string, glyphs: number[], ranges: [number, number][]): TestCase => + ({ font: 'Iosevka', input, glyphs, ranges }); + +const monoid = (input: string, glyphs: number[], ranges: [number, number][]): TestCase => + ({ font: 'Monoid', input, glyphs, ranges }); + +const ubuntu = (input: string, glyphs: number[], ranges: [number, number][]): TestCase => + ({ font: 'Ubuntu Mono', input, glyphs, ranges }); + +const firaCases: TestCase[] = [ + fira('abc', [133, 145, 146], []), + fira('.=', [1614, 1081], [[0, 2]]), + fira('..=', [1614, 1614, 1083], [[0, 3]]), + fira('.-', [1614, 1080], [[0, 2]]), + fira(':=', [1614, 1055], [[0, 2]]), + fira('=:=', [1614, 1614, 1483], [[0, 3]]), + fira('=!=', [1614, 1614, 1484], [[0, 3]]), + fira('__', [1614, 1099], [[0, 2]]), + fira('==', [1614, 1485], [[0, 2]]), + fira('!=', [1614, 1058], [[0, 2]]), + fira('===', [1614, 1614, 1486], [[0, 3]]), + fira('!==', [1614, 1614, 1059], [[0, 3]]), + fira('=/=', [1614, 1614, 1491], [[0, 3]]), + fira('<-<', [1614, 1614, 1513], [[0, 3]]), + fira('<<-', [1614, 1614, 1522], [[0, 3]]), + fira('<--', [1614, 1614, 1511], [[0, 3]]), + fira('<-', [1614, 1510], [[0, 2]]), + fira('<->', [1614, 1614, 1512], [[0, 3]]), + fira('->', [1614, 1064], [[0, 2]]), + fira('-->', [1614, 1614, 1063], [[0, 3]]), + fira('->>', [1614, 1614, 1065], [[0, 3]]), + fira('>->', [1614, 1614, 1493], [[0, 3]]), + fira('<=<', [1614, 1614, 1519], [[0, 3]]), + fira('<<=', [1614, 1614, 1523], [[0, 3]]), + fira('<==', [1614, 1614, 1517], [[0, 3]]), + fira('<=>', [1614, 1614, 1518], [[0, 3]]), + fira('=>', [1614, 1488], [[0, 2]]), + fira('==>', [1614, 1614, 1487], [[0, 3]]), + fira('=>>', [1614, 1614, 1489], [[0, 3]]), + fira('>=>', [1614, 1614, 1495], [[0, 3]]), + fira('>>=', [1614, 1614, 1498], [[0, 3]]), + fira('>>-', [1614, 1614, 1497], [[0, 3]]), + fira('>-', [1614, 1492], [[0, 2]]), + fira('<~>', [1614, 1614, 1526], [[0, 3]]), + fira('-<', [1614, 1066], [[0, 2]]), + fira('-<<', [1614, 1614, 1067], [[0, 3]]), + fira('=<<', [1614, 1614, 1490], [[0, 3]]), + fira('<~~', [1614, 1614, 1527], [[0, 3]]), + fira('<~', [1614, 1525], [[0, 2]]), + fira('~~', [1614, 1534], [[0, 2]]), + fira('~>', [1614, 1533], [[0, 2]]), + fira('~~>', [1614, 1614, 1535], [[0, 3]]), + fira('<<<', [1614, 1614, 1524], [[0, 3]]), + fira('<<', [1614, 1521], [[0, 2]]), + fira('<=', [1614, 1516], [[0, 2]]), + fira('<>', [1614, 1520], [[0, 2]]), + fira('>=', [1614, 1494], [[0, 2]]), + fira('>>', [1614, 1496], [[0, 2]]), + fira('>>>', [1614, 1614, 1499], [[0, 3]]), + fira('{.', [1001, 977], [[0, 2]]), + fira('{|', [1614, 1049], [[0, 2]]), + fira('[|', [1614, 1050], [[0, 2]]), + fira('<:', [1614, 1506], [[0, 2]]), + fira(':>', [1614, 1056], [[0, 2]]), + fira('|]', [1614, 1474], [[0, 2]]), + fira('|}', [1614, 1473], [[0, 2]]), + fira('.}', [977, 1002], [[0, 2]]), + fira('<|||', [1614, 1614, 1614, 1504], [[0, 4]]), + fira('<||', [1614, 1614, 1503], [[0, 3]]), + fira('<|', [1614, 1502], [[0, 2]]), + fira('<|>', [1614, 1614, 1505], [[0, 3]]), + fira('|>', [1614, 1477], [[0, 2]]), + fira('||>', [1614, 1614, 1472], [[0, 3]]), + fira('|||>', [1614, 1614, 1614, 1470], [[0, 4]]), + fira('<$', [1614, 1507], [[0, 2]]), + fira('<$>', [1614, 1614, 1508], [[0, 3]]), + fira('$>', [1614, 1479], [[0, 2]]), + fira('<+', [1614, 1514], [[0, 2]]), + fira('<+>', [1614, 1614, 1515], [[0, 3]]), + fira('+>', [1614, 1482], [[0, 2]]), + fira('<*', [1614, 1500], [[0, 2]]), + fira('<*>', [1614, 1614, 1501], [[0, 3]]), + fira('*>', [1614, 1047], [[0, 2]]), + fira('/*', [1614, 1092], [[0, 2]]), + fira('*/', [1614, 1048], [[0, 2]]), + fira('///', [1614, 1614, 1097], [[0, 3]]), + fira('//', [1614, 1096], [[0, 2]]), + fira('', [1614, 1614, 1529], [[0, 3]]), + fira('/>', [1614, 1095], [[0, 2]]), + fira('0xff', [895, 270, 166, 166], [[0, 3]]), + fira('10x10', [896, 895, 270, 896, 895], [[1, 4]]), + fira('9:45', [904, 998, 899, 900], [[0, 2]]), + fira('[:]', [1003, 998, 1004], [[0, 2]]), + fira(';;', [1614, 1091], [[0, 2]]), + fira('::', [1614, 1052], [[0, 2]]), + fira(':::', [1614, 1614, 1053], [[0, 3]]), + fira('..', [1614, 1082], [[0, 2]]), + fira('...', [1614, 1614, 1085], [[0, 3]]), + fira('..<', [1614, 1614, 1084], [[0, 3]]), + fira('!!', [1614, 1057], [[0, 2]]), + fira('??', [1614, 1090], [[0, 2]]), + fira('%%', [1614, 1536], [[0, 2]]), + fira('&&', [1614, 1468], [[0, 2]]), + fira('||', [1614, 1469], [[0, 2]]), + fira('?.', [1614, 1089], [[0, 2]]), + fira('?:', [1614, 1087], [[0, 2]]), + fira('++', [1614, 1480], [[0, 2]]), + fira('+++', [1614, 1614, 1481], [[0, 3]]), + fira('--', [1614, 1061], [[0, 2]]), + fira('---', [1614, 1614, 1062], [[0, 3]]), + fira('**', [1614, 1045], [[0, 2]]), + fira('***', [1614, 1614, 1046], [[0, 3]]), + fira('~=', [1614, 1532], [[0, 2]]), + fira('~-', [1614, 1531], [[0, 2]]), + fira('www', [1614, 1614, 271], [[0, 3]]), + fira('-~', [1614, 1068], [[0, 2]]), + fira('~@', [1614, 1530], [[0, 2]]), + fira('^=', [1614, 1478], [[0, 2]]), + fira('?=', [1614, 1088], [[0, 2]]), + fira('/=', [1614, 1093], [[0, 2]]), + fira('/==', [1614, 1614, 1094], [[0, 3]]), + fira('-|', [1614, 1060], [[0, 2]]), + fira('_|_', [1614, 1614, 1098], [[0, 3]]), + fira('|-', [1614, 1475], [[0, 2]]), + fira('|=', [1614, 1476], [[0, 2]]), + fira('||=', [1614, 1614, 1471], [[0, 3]]), + fira('#!', [1614, 1071], [[0, 2]]), + fira('#=', [1614, 1075], [[0, 2]]), + fira('##', [1614, 1072], [[0, 2]]), + fira('###', [1614, 1614, 1073], [[0, 3]]), + fira('####', [1614, 1614, 1614, 1074], [[0, 4]]), + fira('#{', [1614, 1069], [[0, 2]]), + fira('#[', [1614, 1070], [[0, 2]]), + fira(']#', [1614, 1051], [[0, 2]]), + fira('#(', [1614, 1076], [[0, 2]]), + fira('#?', [1614, 1077], [[0, 2]]), + fira('#_', [1614, 1078], [[0, 2]]), + fira('#_(', [1614, 1614, 1079], [[0, 3]]), + fira('::=', [1614, 1614, 1054], [[0, 3]]), + fira('.?', [1614, 1086], [[0, 2]]), + fira('===>', [1614, 1614, 1486, 1148], [[0, 4]]) +]; + +const iosevkaCases: TestCase[] = [ + iosevka('<-', [31, 3127], [[0, 2]]), + iosevka('<--', [31, 3129, 3139], [[0, 3]]), + iosevka('<---', [31, 3129, 3150, 3139], [[0, 4]]), + iosevka('<-----', [31, 3129, 3150, 3139, 3151, 3151], [[0, 6]]), + iosevka('->', [3126, 33], [[0, 2]]), + iosevka('-->', [3140, 3128, 33], [[0, 3]]), + iosevka('--->', [3140, 3150, 3128, 33], [[0, 4]]), + iosevka('----->', [3153, 3153, 3140, 3150, 3128, 33], [[0, 6]]), + iosevka('<->', [31, 3149, 33], [[0, 3]]), + iosevka('<-->', [31, 3129, 3128, 33], [[0, 4]]), + iosevka('<--->', [31, 3129, 3150, 3128, 33], [[0, 5]]), + iosevka('<----->', [31, 3129, 3150, 3150, 3150, 3128, 33], [[0, 7]]), + iosevka('<=', [3094, 3095], [[0, 2]]), + iosevka('<==', [31, 3158, 3168], [[0, 3]]), + iosevka('<===', [31, 3158, 3179, 3168], [[0, 4]]), + iosevka('<=====', [31, 3158, 3179, 3168, 3180, 3180], [[0, 6]]), + iosevka('=>', [3155, 33], [[0, 2]]), + iosevka('==>', [3169, 3157, 33], [[0, 3]]), + iosevka('===>', [3169, 3179, 3157, 33], [[0, 4]]), + iosevka('=====>', [3182, 3182, 3169, 3179, 3157, 33], [[0, 6]]), + iosevka('<=>', [31, 3178, 33], [[0, 3]]), + iosevka('<==>', [31, 3158, 3157, 33], [[0, 4]]), + iosevka('<===>', [31, 3158, 3179, 3157, 33], [[0, 5]]), + iosevka('<=====>', [31, 3158, 3179, 3179, 3179, 3157, 33], [[0, 7]]), + iosevka('', [779, 779, 628], [[0, 3]]), + monoid('<--', [776, 776, 627], [[0, 3]]), + monoid('->>', [780, 780, 626], [[0, 3]]), + monoid('<<-', [777, 777, 625], [[0, 3]]), + monoid('->', [781, 623], [[0, 2]]), + monoid('<-', [778, 624], [[0, 2]]), + monoid('=>', [793, 666], [[0, 2]]), + monoid('<=>', [785, 785, 760], [[0, 3]]), + monoid('<==>', [786, 786, 786, 771], [[0, 4]]), + monoid('==>', [787, 787, 672], [[0, 3]]), + monoid('<==', [788, 788, 671], [[0, 3]]), + monoid('>>=', [791, 791, 758], [[0, 3]]), + monoid('=<<', [792, 792, 759], [[0, 3]]), + monoid('--', [667, 667], [[0, 2]]), + monoid(':=', [29, 761], [[0, 2]]), + monoid('=:=', [789, 789, 665], [[0, 3]]), + monoid('==', [794, 641], [[0, 2]]), + monoid('!==', [782, 782, 646], [[0, 3]]), + monoid('!=', [783, 629], [[0, 2]]), + monoid('<=', [790, 630], [[0, 2]]), + monoid('>=', [792, 631], [[0, 2]]), + monoid('//', [621, 664], [[0, 2]]), + monoid('/**', [18, 753, 753], [[0, 3]]), + monoid('/*', [18, 753], [[0, 2]]), + monoid('*/', [754, 18], [[0, 2]]), + monoid('&&', [633, 775], [[0, 2]]), + monoid('.&', [17, 755], [[0, 2]]), + monoid('||', [634, 635], [[0, 2]]), + monoid('!!', [769, 770], [[0, 2]]), + monoid('::', [772, 773], [[0, 2]]), + monoid('>>', [637, 638], [[0, 2]]), + monoid('<<', [639, 640], [[0, 2]]), + monoid('¯\\_(ツ)_/¯', [113, 765, 66, 767, 613, 768, 66, 766, 113], [[0, 3], [3, 6], [6, 9]]), + monoid('__', [763, 764], [[0, 2]]) +]; + +const ubuntuCases: TestCase[] = [ + ubuntu('==>', [32, 32, 33], []) +]; + +const fontPaths: Record = { + 'Fira Code': path.join(__dirname, '../fonts/FiraCode-Regular.otf'), + 'Iosevka': path.join(__dirname, '../fonts/iosevka-regular.ttf'), + 'Monoid': path.join(__dirname, '../fonts/Monoid-Regular.ttf'), + 'Ubuntu Mono': path.join(__dirname, '../fonts/UbuntuMono-Regular.ttf') +}; + +const fontCache: Map = new Map(); + +function loadFont(fontName: string): Font { + let font = fontCache.get(fontName); + if (!font) { + const fontPath = fontPaths[fontName]; + const buffer = fs.readFileSync(fontPath); + font = loadBuffer(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength)); + fontCache.set(fontName, font); + } + return font; +} + +describe('addon-ligatures - index', () => { + describe('findLigatures', () => { + for (const { font: fontName, input, glyphs, ranges } of [...firaCases, ...iosevkaCases, ...monoidCases, ...ubuntuCases]) { + it(`${fontName}: '${input}'`, () => { + const font = loadFont(fontName); + const result = font.findLigatures(input); + assert.deepEqual(result.outputGlyphs, glyphs); + assert.deepEqual(result.contextRanges, ranges); + }); + } + }); + + describe('findLigatureRanges', () => { + for (const { font: fontName, input, ranges } of [...firaCases, ...iosevkaCases, ...monoidCases, ...ubuntuCases]) { + it(`${fontName}: '${input}'`, () => { + const font = loadFont(fontName); + const result = font.findLigatureRanges(input); + assert.deepEqual(result, ranges); + }); + } + }); + + describe('caching', () => { + it('findLigatures caches successive calls correctly', () => { + const fontPath = fontPaths['Fira Code']; + const buffer = fs.readFileSync(fontPath); + const font = loadBuffer(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength), { cacheSize: 100 }); + const result1 = font.findLigatures('in --> out'); + const result2 = font.findLigatures('in --> out'); + assert.deepEqual(result1, result2); + }); + + it('findLigatureRanges caches successive calls correctly', () => { + const fontPath = fontPaths['Fira Code']; + const buffer = fs.readFileSync(fontPath); + const font = loadBuffer(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength), { cacheSize: 100 }); + const result1 = font.findLigatureRanges('in --> out'); + const result2 = font.findLigatureRanges('in --> out'); + assert.deepEqual(result1, result2); + }); + + it('caches calls to findLigatures after findLigatureRanges correctly', () => { + const fontPath = fontPaths['Fira Code']; + const buffer = fs.readFileSync(fontPath); + + const uncached = loadBuffer(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength)); + const uncachedResult1 = uncached.findLigatureRanges('in --> out'); + const uncachedResult2 = uncached.findLigatures('in --> out'); + + const font = loadBuffer(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength), { cacheSize: 100 }); + const result1 = font.findLigatureRanges('in --> out'); + const result2 = font.findLigatures('in --> out'); + + assert.deepEqual(result1, uncachedResult1); + assert.deepEqual(result2, uncachedResult2); + assert.deepEqual(result1, result2.contextRanges); + }); + + it('caches calls to findLigatureRanges after findLigatures correctly', () => { + const fontPath = fontPaths['Fira Code']; + const buffer = fs.readFileSync(fontPath); + + const uncached = loadBuffer(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength)); + const uncachedResult1 = uncached.findLigatures('in --> out'); + const uncachedResult2 = uncached.findLigatureRanges('in --> out'); + + const font = loadBuffer(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength), { cacheSize: 100 }); + const result1 = font.findLigatures('in --> out'); + const result2 = font.findLigatureRanges('in --> out'); + + assert.deepEqual(result1, uncachedResult1); + assert.deepEqual(result2, uncachedResult2); + assert.deepEqual(result1.contextRanges, result2); + }); + }); +}); \ No newline at end of file diff --git a/addons/addon-ligatures/test/merge.test.ts b/addons/addon-ligatures/test/merge.test.ts new file mode 100644 index 00000000..0dc157f2 --- /dev/null +++ b/addons/addon-ligatures/test/merge.test.ts @@ -0,0 +1,226 @@ +/** + * Copyright (c) 2018 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { assert } from 'chai'; + +const mergeTrees = require('../out-esbuild/fontLigatures/merge').default; + +interface LookupResult { + contextRange: [number, number]; + index: number; + subIndex: number; + length: number; + substitutions: number[]; +} + +function lookup(substitutionGlyph: number, index?: number, subIndex?: number): LookupResult { + return { + contextRange: [0, 1], + index: index || 0, + subIndex: subIndex || 0, + length: 1, + substitutions: [substitutionGlyph] + }; +} + +describe('addon-ligatures - merge', () => { + describe('mergeTrees', () => { + it('combines disjoint trees', () => { + const result = mergeTrees([ + { + individual: { + '1': { lookup: lookup(1) } + }, + range: [] + }, + { + individual: {}, + range: [{ + entry: { lookup: lookup(2) }, + range: [2, 4] + }] + }, + { + individual: { + '5': { lookup: lookup(3) } + }, + range: [] + }, + { + individual: {}, + range: [{ + entry: { lookup: lookup(4) }, + range: [8, 10] + }] + } + ]); + + assert.deepEqual(result, { + individual: { + '1': { lookup: lookup(1) }, + '5': { lookup: lookup(3) } + }, + range: [{ + entry: { lookup: lookup(2) }, + range: [2, 4] + }, { + entry: { lookup: lookup(4) }, + range: [8, 10] + }] + }); + }); + + it('merges matching individual glyphs', () => { + const result = mergeTrees([ + { + individual: { + '1': { lookup: lookup(1, 1) } + }, + range: [] + }, + { + individual: { + '1': { lookup: lookup(2, 0) } + }, + range: [] + }, + { + individual: { + '1': { lookup: lookup(3, 2) } + }, + range: [] + } + ]); + + assert.deepEqual(result, { + individual: { + '1': { lookup: lookup(2, 0) } + }, + range: [] + }); + }); + + it('merges range glyphs overlapping individual glyphs', () => { + const result = mergeTrees([ + { + individual: { + '1': { lookup: lookup(1, 0) } + }, + range: [] + }, + { + individual: {}, + range: [{ + entry: { lookup: lookup(2, 1) }, + range: [0, 4] + }] + } + ]); + + assert.deepEqual(result, { + individual: { + '0': { lookup: lookup(2, 1) }, + '1': { lookup: lookup(1, 0) } + }, + range: [{ + entry: { lookup: lookup(2, 1) }, + range: [2, 4] + }] + }); + }); + + it('merges individual glyphs overlapping range glyphs', () => { + const result = mergeTrees([ + { + individual: {}, + range: [{ + entry: { lookup: lookup(2, 1) }, + range: [0, 4] + }] + }, + { + individual: { + '1': { lookup: lookup(1, 0) } + }, + range: [] + } + ]); + + assert.deepEqual(result, { + individual: { + '0': { lookup: lookup(2, 1) }, + '1': { lookup: lookup(1, 0) } + }, + range: [{ + entry: { lookup: lookup(2, 1) }, + range: [2, 4] + }] + }); + }); + + it('merges multiple overlapping ranges', () => { + const result = mergeTrees([ + { + individual: {}, + range: [{ + entry: { lookup: lookup(1, 2) }, + range: [0, 3] + }, { + entry: { lookup: lookup(2, 1) }, + range: [6, 12] + }, { + entry: { lookup: lookup(5, 3) }, + range: [15, 20] + }, { + entry: { lookup: lookup(7, 4) }, + range: [20, 22] + }] + }, + { + individual: {}, + range: [{ + entry: { lookup: lookup(3, 0) }, + range: [2, 8] + }, { + entry: { lookup: lookup(4, 0) }, + range: [10, 13] + }, { + entry: { lookup: lookup(6, 0) }, + range: [16, 21] + }] + } + ]); + + assert.deepEqual(result, { + individual: { + '2': { lookup: lookup(3, 0) }, + '12': { lookup: lookup(4, 0) }, + '15': { lookup: lookup(5, 3) }, + '20': { lookup: lookup(6, 0) }, + '21': { lookup: lookup(7, 4) } + }, + range: [{ + entry: { lookup: lookup(1, 2) }, + range: [0, 2] + }, { + entry: { lookup: lookup(3, 0) }, + range: [6, 8] + }, { + entry: { lookup: lookup(3, 0) }, + range: [3, 6] + }, { + entry: { lookup: lookup(2, 1) }, + range: [8, 10] + }, { + entry: { lookup: lookup(4, 0) }, + range: [10, 12] + }, { + entry: { lookup: lookup(6, 0) }, + range: [16, 20] + }] + }); + }); + }); +}); \ No newline at end of file diff --git a/addons/addon-ligatures/test/mergeRange.test.ts b/addons/addon-ligatures/test/mergeRange.test.ts new file mode 100644 index 00000000..91642e8d --- /dev/null +++ b/addons/addon-ligatures/test/mergeRange.test.ts @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2018 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { assert } from 'chai'; + +const mergeRange = require('../out-esbuild/fontLigatures/mergeRange').default; + +describe('addon-ligatures - mergeRange', () => { + it('inserts a new range before the existing ones', () => { + const result = mergeRange([[1, 2], [2, 3]], 0, 1); + assert.deepEqual(result, [[0, 1], [1, 2], [2, 3]]); + }); + + it('inserts in between two ranges', () => { + const result = mergeRange([[0, 2], [4, 6]], 2, 4); + assert.deepEqual(result, [[0, 2], [2, 4], [4, 6]]); + }); + + it('inserts after the last range', () => { + const result = mergeRange([[0, 2], [4, 6]], 6, 8); + assert.deepEqual(result, [[0, 2], [4, 6], [6, 8]]); + }); + + it('extends the beginning of a range', () => { + const result = mergeRange([[0, 2], [4, 6]], 3, 5); + assert.deepEqual(result, [[0, 2], [3, 6]]); + }); + + it('extends the end of a range', () => { + const result = mergeRange([[0, 2], [4, 6]], 1, 4); + assert.deepEqual(result, [[0, 4], [4, 6]]); + }); + + it('extends the last range', () => { + const result = mergeRange([[0, 2], [4, 6]], 5, 7); + assert.deepEqual(result, [[0, 2], [4, 7]]); + }); + + it('connects two ranges', () => { + const result = mergeRange([[0, 2], [4, 6]], 1, 5); + assert.deepEqual(result, [[0, 6]]); + }); + + it('connects more than two ranges', () => { + const result = mergeRange([[0, 2], [4, 6], [8, 10], [12, 14]], 1, 10); + assert.deepEqual(result, [[0, 10], [12, 14]]); + }); +}); diff --git a/addons/addon-ligatures/test/parse.test.ts b/addons/addon-ligatures/test/parse.test.ts index d6c56d2c..de846f4a 100644 --- a/addons/addon-ligatures/test/parse.test.ts +++ b/addons/addon-ligatures/test/parse.test.ts @@ -8,7 +8,7 @@ import { assert } from 'chai'; const parse = require('../out-esbuild/parse').default; // TODO: integrate tests from http://test.csswg.org/suites/css-fonts-4_dev/nightly-unstable/ -describe('parse', () => { +describe('addon-ligatures - parse', () => { it('parses individual families', () => { assert.deepEqual(parse('monospace'), ['monospace']); }); diff --git a/package-lock.json b/package-lock.json index 71dff295..e48e512c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -87,11 +87,14 @@ "version": "0.10.0", "license": "MIT", "dependencies": { - "font-finder": "^1.1.0", - "font-ligatures": "^1.4.1" + "lru-cache": "^6.0.0", + "opentype.js": "^0.8.0" }, "devDependencies": { + "@types/lru-cache": "^5.1.0", + "@types/opentype.js": "^0.7.0", "axios": "^1.6.0", + "font-finder": "^1.1.0", "mkdirp": "0.5.5", "yauzl": "^2.10.0" }, @@ -99,6 +102,24 @@ "node": ">8.0.0" } }, + "addons/addon-ligatures/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "addons/addon-ligatures/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "addons/addon-progress": { "name": "@xterm/addon-progress", "version": "0.2.0", @@ -1701,6 +1722,13 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, + "node_modules/@types/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/mathjs": { "version": "6.0.12", "resolved": "https://registry.npmjs.org/@types/mathjs/-/mathjs-6.0.12.tgz", @@ -1737,6 +1765,13 @@ "undici-types": "~6.21.0" } }, + "node_modules/@types/opentype.js": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@types/opentype.js/-/opentype.js-0.7.2.tgz", + "integrity": "sha512-Riz6WyBUBEFs7YqSsJya3SbDHJZ6BmMkY7bzNoue6rtwj+RNilLc+mgOX/eJ0Y0asq16FSU6DatBeOg8ZMy2UQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/puppeteer": { "version": "5.4.7", "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-5.4.7.tgz", @@ -4228,6 +4263,8 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/font-finder/-/font-finder-1.1.0.tgz", "integrity": "sha512-wpCL2uIbi6GurJbU7ZlQ3nGd61Ho+dSU6U83/xJT5UPFfN35EeCW/rOtS+5k+IuEZu2SYmHzDIPL9eA5tSYRAw==", + "dev": true, + "license": "MIT", "dependencies": { "get-system-fonts": "^2.0.0", "promise-stream-reader": "^1.0.1" @@ -4236,35 +4273,6 @@ "node": ">8.0.0" } }, - "node_modules/font-ligatures": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/font-ligatures/-/font-ligatures-1.4.1.tgz", - "integrity": "sha512-7W6zlfyhvCqShZ5ReUWqmSd9vBaUudW0Hxis+tqUjtHhsPU+L3Grf8mcZAtCiXHTzorhwdRTId2WeH/88gdFkw==", - "dependencies": { - "font-finder": "^1.0.3", - "lru-cache": "^6.0.0", - "opentype.js": "^0.8.0" - }, - "engines": { - "node": ">8.0.0" - } - }, - "node_modules/font-ligatures/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/font-ligatures/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -4490,6 +4498,8 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/get-system-fonts/-/get-system-fonts-2.0.2.tgz", "integrity": "sha512-zzlgaYnHMIEgHRrfC7x0Qp0Ylhw/sHpM6MHXeVBTYIsvGf5GpbnClB+Q6rAPdn+0gd2oZZIo6Tj3EaWrt4VhDQ==", + "dev": true, + "license": "MIT", "engines": { "node": ">8.0.0" } @@ -6700,6 +6710,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-stream-reader/-/promise-stream-reader-1.0.1.tgz", "integrity": "sha512-Tnxit5trUjBAqqZCGWwjyxhmgMN4hGrtpW3Oc/tRI4bpm/O2+ej72BB08l6JBnGQgVDGCLvHFGjGgQS6vzhwXg==", + "dev": true, + "license": "MIT", "engines": { "node": ">8.0.0" } From 21b8f77afb5fac4142d1c1167c47611d7f1cf69f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 3 Jan 2026 09:16:58 -0800 Subject: [PATCH 15/38] Move unit tests to right folder, push font fixtures --- addons/addon-ligatures/.gitignore | 1 - .../addon-ligatures/fonts/FiraCode-Regular.otf | Bin 0 -> 159496 bytes .../addon-ligatures/fonts/Monoid-Regular.ttf | Bin 0 -> 79248 bytes .../fonts/UbuntuMono-Regular.ttf | Bin 0 -> 205748 bytes .../addon-ligatures/fonts/iosevka-regular.ttf | Bin 0 -> 399128 bytes .../{test => src/fontLigatures}/index.test.ts | 12 +++++------- .../{test => src/fontLigatures}/merge.test.ts | 3 +-- .../fontLigatures}/mergeRange.test.ts | 3 +-- .../{test => src}/parse.test.ts | 3 +-- addons/addon-ligatures/src/tsconfig.json | 4 +++- 10 files changed, 11 insertions(+), 15 deletions(-) create mode 100644 addons/addon-ligatures/fonts/FiraCode-Regular.otf create mode 100644 addons/addon-ligatures/fonts/Monoid-Regular.ttf create mode 100644 addons/addon-ligatures/fonts/UbuntuMono-Regular.ttf create mode 100644 addons/addon-ligatures/fonts/iosevka-regular.ttf rename addons/addon-ligatures/{test => src/fontLigatures}/index.test.ts (96%) rename addons/addon-ligatures/{test => src/fontLigatures}/merge.test.ts (98%) rename addons/addon-ligatures/{test => src/fontLigatures}/mergeRange.test.ts (94%) rename addons/addon-ligatures/{test => src}/parse.test.ts (97%) diff --git a/addons/addon-ligatures/.gitignore b/addons/addon-ligatures/.gitignore index f172c3a1..7b48eed8 100644 --- a/addons/addon-ligatures/.gitignore +++ b/addons/addon-ligatures/.gitignore @@ -3,7 +3,6 @@ node_modules/ coverage/ lib/ -fonts/ .env .vscode/ diff --git a/addons/addon-ligatures/fonts/FiraCode-Regular.otf b/addons/addon-ligatures/fonts/FiraCode-Regular.otf new file mode 100644 index 0000000000000000000000000000000000000000..e7a9fda69dad82c508e6374a053e6d3f1b82f6c0 GIT binary patch literal 159496 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

MPVd<+J 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%0DG? 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%0Aopr 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 VE_#YS1aU4pF@TRPy4-d8F94u@-NOI? literal 0 HcmV?d00001 diff --git a/addons/addon-ligatures/fonts/UbuntuMono-Regular.ttf b/addons/addon-ligatures/fonts/UbuntuMono-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..fdd309d716629f4e5339d5e5508225ed857a3ede GIT binary patch literal 205748 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 L<|2NP7a14;dV*d7 literal 0 HcmV?d00001 diff --git a/addons/addon-ligatures/fonts/iosevka-regular.ttf b/addons/addon-ligatures/fonts/iosevka-regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..963cbe2a654a8e5ab284a622575c57464e8f1b35 GIT binary patch literal 399128 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

ifyxeEG&qLyw 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