From 8fd3df4c9f1998e09046bcb6da1f0053018428a4 Mon Sep 17 00:00:00 2001 From: Rasmus Andersson Date: Sun, 3 Feb 2019 14:01:11 -0800 Subject: super- and subscript brackets (note that there are no Unicode codepoints assigned to these glyphs; they are purely for substitution with sups and subs features) --- docs/lab/index.html | 363 ++++++++++++++++++++++++---------------------------- 1 file changed, 170 insertions(+), 193 deletions(-) (limited to 'docs/lab') diff --git a/docs/lab/index.html b/docs/lab/index.html index 948f33e0f..f989045c9 100644 --- a/docs/lab/index.html +++ b/docs/lab/index.html @@ -216,92 +216,64 @@ left side cascades up to 5 characters: `) -samples.set('Feature: sups', ` -Dedicated glyphs, corresponding ASCII, codepoint -\u2070 \t 0 \t U+2070 -\u00b9 \t 1 \t U+00B9 -\u00b2 \t 2 \t U+00B2 -\u00b3 \t 3 \t U+00B3 -\u2074 \t 4 \t U+2074 -\u2075 \t 5 \t U+2075 -\u2076 \t 6 \t U+2076 -\u2077 \t 7 \t U+2077 -\u2078 \t 8 \t U+2078 -\u2079 \t 9 \t U+2079 -\u207a \t + \t U+207A -\u207b \t - \t U+207B -\u207c \t = \t U+207C -\u207d \t ( \t U+207D -\u207e \t ) \t U+207E -\u1d43 \t a \t U+1D43 -\u1d47 \t b \t U+1D47 -\u1d9c \t c \t U+1D9C -\u1d48 \t d \t U+1D48 -\u1d49 \t e \t U+1D49 -\u1da0 \t f \t U+1DA0 -\u1d4d \t g \t U+1D4D -\u02b0 \t h \t U+02B0 -\u1da6 \t i \t U+1DA6 -\u02b2 \t j \t U+02B2 -\u1d4f \t k \t U+1D4F -\u02e1 \t l \t U+02E1 -\u1d50 \t m \t U+1D50 -\u207f \t n \t U+207F -\u1d52 \t o \t U+1D52 -\u1d56 \t p \t U+1D56 -\u146b \t q \t U+146B -\u02b3 \t r \t U+02B3 -\u02e2 \t s \t U+02E2 -\u1d57 \t t \t U+1D57 -\u1d58 \t u \t U+1D58 -\u1d5b \t v \t U+1D5B -\u02b7 \t w \t U+02B7 -\u02e3 \t x \t U+02E3 -\u02b8 \t y \t U+02B8 -\u1dbb \t z \t U+1DBB - -(enable sups feature to turn column 2 == column 1) +samples.set('Feature: sups & subs', ` +#!enableFeatures:tnum,sups +Superscript and subscript tests + +m⁽ˣ⁾[ˣ][³]ᵗ⁺⁴x +m(x)[x][3]t+4x + +Table of super- and subscript characters. +Enable/disable sups and subs feature to explore substitutions. + +sups \t \t \t \t | subs +——————————————————————— +0 \t \u2070 \t U+2070 \t | 0 \t \u2080 \t U+2080 +1 \t \u00b9 \t U+00B9 \t | 1 \t \u2081 \t U+2081 +2 \t \u00b2 \t U+00B2 \t | 2 \t \u2082 \t U+2082 +3 \t \u00b3 \t U+00B3 \t | 3 \t \u2083 \t U+2083 +4 \t \u2074 \t U+2074 \t | 4 \t \u2084 \t U+2084 +5 \t \u2075 \t U+2075 \t | 5 \t \u2085 \t U+2085 +6 \t \u2076 \t U+2076 \t | 6 \t \u2086 \t U+2086 +7 \t \u2077 \t U+2077 \t | 7 \t \u2087 \t U+2087 +8 \t \u2078 \t U+2078 \t | 8 \t \u2088 \t U+2088 +9 \t \u2079 \t U+2079 \t | 9 \t \u2089 \t U+2089 ++ \t \u207a \t U+207A \t | + \t \u208A \t U+208A +- \t \u207b \t U+207B \t | - \t \u208B \t U+208B += \t \u207c \t U+207C \t | = \t \u208C \t U+208C +( \t \u207d \t U+207D \t | ( \t \u208D \t U+208D +) \t \u207e \t U+207E \t | ) \t \u208E \t U+208E +[ \t \u0020 \t \t | [ \t +] \t \u0020 \t \t | ] \t +a \t \u1d43 \t U+1D43 \t | a \t \u2090 \t U+2090 +b \t \u1d47 \t U+1D47 \t | b \t +c \t \u1d9c \t U+1D9C \t | c \t +d \t \u1d48 \t U+1D48 \t | d \t +e \t \u1d49 \t U+1D49 \t | e \t \u2091 \t U+2091 +f \t \u1da0 \t U+1DA0 \t | f \t +g \t \u1d4d \t U+1D4D \t | g \t +h \t \u02b0 \t U+02B0 \t | h \t \u2095 \t U+2095 +i \t \u1da6 \t U+1DA6 \t | i \t \u1D62 \t U+1D62 +j \t \u02b2 \t U+02B2 \t | j \t \u2C7C \t U+2C7C +k \t \u1d4f \t U+1D4F \t | k \t \u2096 \t U+2096 +l \t \u02e1 \t U+02E1 \t | l \t \u2097 \t U+2097 +m \t \u1d50 \t U+1D50 \t | m \t \u2098 \t U+2098 +n \t \u207f \t U+207F \t | n \t \u2099 \t U+2099 +o \t \u1d52 \t U+1D52 \t | o \t \u2092 \t U+2092 +p \t \u1d56 \t U+1D56 \t | p \t \u209A \t U+209A +q \t \u146b \t U+146B \t | q \t +r \t \u02b3 \t U+02B3 \t | r \t \u1D63 \t U+1D63 +s \t \u02e2 \t U+02E2 \t | s \t \u209B \t U+209B +t \t \u1d57 \t U+1D57 \t | t \t \u209C \t U+209C +u \t \u1d58 \t U+1D58 \t | u \t \u1D64 \t U+1D64 +v \t \u1d5b \t U+1D5B \t | v \t \u1D65 \t U+1D65 +w \t \u02b7 \t U+02B7 \t | w \t +x \t \u02e3 \t U+02E3 \t | x \t \u2093 \t x \t U+2093 +y \t \u02b8 \t U+02B8 \t | y \t +z \t \u1dbb \t U+1DBB \t | z \t `) -samples.set('Feature: subs', ` -Dedicated glyphs, corresponding ASCII, codepoint -\u2080 \t 0 \t U+2080 -\u2081 \t 1 \t U+2081 -\u2082 \t 2 \t U+2082 -\u2083 \t 3 \t U+2083 -\u2084 \t 4 \t U+2084 -\u2085 \t 5 \t U+2085 -\u2086 \t 6 \t U+2086 -\u2087 \t 7 \t U+2087 -\u2088 \t 8 \t U+2088 -\u2089 \t 9 \t U+2089 -\u208A \t + \t U+208A -\u208B \t - \t U+208B -\u208C \t = \t U+208C -\u208D \t ( \t U+208D -\u208E \t ) \t U+208E -\u2090 \t a \t U+2090 -\u2091 \t e \t U+2091 -\u2095 \t h \t U+2095 -\u1D62 \t i \t U+1D62 -\u2C7C \t j \t U+2C7C -\u2096 \t k \t U+2096 -\u2097 \t l \t U+2097 -\u2098 \t m \t U+2098 -\u2099 \t n \t U+2099 -\u2092 \t o \t U+2092 -\u209A \t p \t U+209A -\u1D63 \t r \t U+1D63 -\u209B \t s \t U+209B -\u209C \t t \t U+209C -\u1D64 \t u \t U+1D64 -\u1D65 \t v \t U+1D65 -\u2093 \t x \t U+2093 - -(enable subs feature to turn column 2 == column 1) -`) - // From http://justanotherfoundry.com/generator samples.set('Kerning body en', @@ -1886,59 +1858,6 @@ function main() { }) } - // sample text - const samplesSelect = document.querySelector('select[name="sample"]') - for (let [k,v] of samples) { - const opt = document.createElement('option') - opt.innerText = k - if (v) { - opt.value = k - } else { - opt.disabled = true - } - samplesSelect.appendChild(opt) - } - sampleVar = vars.bind('sample', samplesSelect, (e, v) => { - let sampleText = samples.get(v) || ''+v - - if (v == 'Repertoire') { - repertoireControl.style.display = null - } else { - repertoireControl.style.display = 'none' - } - - if (typeof sampleText == 'object' && sampleText.toHTML) { - const html = sampleText.toHTML() - interUISample.innerHTML = html - secondarySample.innerHTML = html - } else { - sampleText = String(sampleText).replace(/^[\s\r\n\r]+|[\s\r\n\r]+$/g, '') - if (sampleText) { - interUISample.innerText = sampleText - secondarySample.innerText = sampleText - } - } - - if (v == 'Repertoire') { - requestAnimationFrame(() => { - if (sizeVar) { - sizeVar.refreshValue(null) - } - }) - } - }) - - vars.bind('repertoireOrder', (e, v) => { - let currOrder = repertoireOrder - if (v == 'u') { - repertoireOrder = RepertoireOrderUnicode - } else { - repertoireOrder = RepertoireOrderGlyphList - } - if (sampleVar && currOrder != repertoireOrder) { - sampleVar.refreshValue(null) - } - }) const lineHeightInput = document.querySelector('[name="lineHeight"]') let measurePending = false @@ -1982,6 +1901,67 @@ function main() { setCSSProp('-ms-' + name, value) } + + let feats = new Map() + let featVars = new Map() + let updateFeaturesStyleTimer = null + + function updateFeaturesStyle() { + let css = Array.from(feats).map(f => `"${f[0]}" ${f[1]}`).join(', ') + setCSSProp('font-feature-settings', css) + } + + function scheduleUpdateFeaturesStyle() { + if (updateFeaturesStyleTimer === null) { + updateFeaturesStyleTimer = setTimeout(() => { + updateFeaturesStyleTimer = null + updateFeaturesStyle() + }, 1) + } + } + + function setFeature(feat, val, dontUpdateVar/*=false*/) { + if (typeof val == 'boolean') { + val = val ? 1 : 0 + } + let prevVal = feats.get(feat) + if (prevVal !== val) { + feats.set(feat, val) + scheduleUpdateFeaturesStyle() + if (!dontUpdateVar) { + let vr = featVars.get(feat) + if (vr) { + vr.setValue(val) + } + } + } + } + + // sample text + const samplesSelect = document.querySelector('select[name="sample"]') + for (let [k,v] of samples) { + const opt = document.createElement('option') + opt.innerText = k + if (v) { + opt.value = k + } else { + opt.disabled = true + } + samplesSelect.appendChild(opt) + } + + vars.bind('repertoireOrder', (e, v) => { + let currOrder = repertoireOrder + if (v == 'u') { + repertoireOrder = RepertoireOrderUnicode + } else { + repertoireOrder = RepertoireOrderGlyphList + } + if (sampleVar && currOrder != repertoireOrder) { + sampleVar.refreshValue(null) + } + }) + const boxes = document.querySelector('boxes') sizeVar = vars.bind('size', (e, v) => { boxes.style.display = (v > 20) ? 'none' : null @@ -2213,73 +2193,70 @@ function main() { // setCSSProp('font-variant-numeric', e.value = v) // }) - let features = new Set() - for (let e of Array.prototype.slice.call(document.querySelectorAll('input.featopt'))) { + + for (let e of Array.from(document.querySelectorAll('input.featopt'))) { let p = e.name.replace(/^feat\:/, '').split('=') - let name = p[0], value = p[1] || '1' - vars.bind('feat-' + name, e, (e, on) => { - let val = '"' + name + '" ' + value - if (on) { - features.add(val) - } else { - features.delete(val) - } - setCSSProp('font-feature-settings', Array.from(features).join(', ')) + let name = p[0] + let valueOn = parseInt(p[1] || '1') + let valueOff = valueOn == 0 ? 1 : 0 + let vr = vars.bind('feat-' + name, e, (e, on) => { + setFeature(name, on ? valueOn : valueOff, /*dontUpdateVar=*/true) }) + featVars.set(name, vr) } - function initCanvas(canvas) { - const w = parseInt(canvas.width) - const h = parseInt(canvas.height) - const scale = window.devicePixelRatio || 1 - if (scale != 1) { - canvas.width = w * scale - canvas.height = h * scale - canvas.style.width = w + 'px' - canvas.style.height = h + 'px' - } - } + updateFeaturesStyle() - initCanvas(renderCanvas) - initCanvas(displayCanvas) - - function rasterize(text) { - const ctx = renderCanvas.getContext('2d') - const width = parseInt(renderCanvas.width) - const height = parseInt(renderCanvas.height) - - ctx.clearRect(0, 0, width, height) - - ctx.font = '22px/36px ' + fontFamilyName - ctx.fillText(text, 4, 24) - - ctx.font = '11px/18px ' + fontFamilyName - ctx.fillText(text, 4, 44) - - const zctx = displayCanvas.getContext('2d') - zctx.webkitImageSmoothingEnabled = false; - zctx.mozImageSmoothingEnabled = false; - zctx.imageSmoothingEnabled = false; - const zwidth = parseInt(displayCanvas.width) - const zheight = parseInt(displayCanvas.height) - zctx.clearRect(0, 0, zwidth, zheight) - zctx.drawImage( - renderCanvas, - 0, 0, width, height, - 0, 0, zwidth, zheight - ) - } + sampleVar = vars.bind('sample', samplesSelect, (e, v) => { + let sampleText = samples.get(v) || ''+v + + if (v == 'Repertoire') { + repertoireControl.style.display = null + } else { + repertoireControl.style.display = 'none' + } - let didSetInitialValue = false + if (typeof sampleText == 'object' && sampleText.toHTML) { + const html = sampleText.toHTML() + interUISample.innerHTML = html + secondarySample.innerHTML = html + } else { + // look for directive + // #!directive:value + sampleText = String(sampleText).replace(/^[\s\r\n\r]+|[\s\r\n\r]+$/g, '') + let m = /(?:^|\n)#\!([\w_\-]+):(.+)(?:\n|$)/.exec(sampleText) + if (m) { + // parse directive + sampleText = ( + sampleText.substring(0, m.index) + + sampleText.substr(m.index + m[0].length) + ) + let directive = m[1].toLowerCase() + console.log('dir', m[1], '=>', m[2]) + if (directive == 'enablefeatures') { + // #!enableFeatures:tnum,dlig + for (let feat of m[2].toLowerCase().split(/\s*,\s*/)) { + setFeature(feat, 1) + } + } else { + console.warn(`ignoring unknown directive ${m[0]} in sample text`) + } + } + if (sampleText) { + interUISample.innerText = sampleText + secondarySample.innerText = sampleText + } + } - let rasterizePhraseVar = vars.bind('rasterizePhrase', (e, v) => { - if (document.readyState == 'complete') { - rasterize(v) + if (v == 'Repertoire') { + requestAnimationFrame(() => { + if (sizeVar) { + sizeVar.refreshValue(null) + } + }) } }) - document.onreadystatechange = () => vars.refreshValue('rasterizePhrase') - } -- cgit v1.2.3