tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

text-spacing-trim-combinations-001.html (4478B)


      1 <!DOCTYPE html>
      2 <meta charset="utf-8">
      3 <link rel="help" href="https://drafts.csswg.org/css-text-4/#text-spacing-trim-property">
      4 <!--
      5  Test patterns are from "Fullwidth Punctuation Collapsing":
      6  https://drafts.csswg.org/css-text-4/#fullwidth-collapsing
      7 -->
      8 <meta name="variant" content="?class=htb&test=CC:HF">
      9 <meta name="variant" content="?class=htb&test=CM:HF">
     10 <meta name="variant" content="?class=htb&test=CO:FH">
     11 <meta name="variant" content="?class=htb&test=MO:FH">
     12 <meta name="variant" content="?class=htb&test=OO:FH">
     13 <meta name="variant" content="?class=vrl&test=CC:HF">
     14 <meta name="variant" content="?class=vrl&test=CM:HF">
     15 <meta name="variant" content="?class=vrl&test=CO:FH">
     16 <meta name="variant" content="?class=vrl&test=MO:FH">
     17 <meta name="variant" content="?class=vrl&test=OO:FH">
     18 <script src="/resources/testharness.js"></script>
     19 <script src="/resources/testharnessreport.js"></script>
     20 <script src="support/variant-class.js"></script>
     21 <script src="../support/get-char-advances.js"></script>
     22 <style>
     23 @font-face {
     24  font-family: halt-font;
     25  src: url('/fonts/noto/cjk/NotoSansCJKjp-Regular-subset-halt.otf');
     26 }
     27 #container {
     28  font-family: halt-font;
     29  font-size: 20px;
     30 }
     31 .vrl #container {
     32  writing-mode: vertical-rl;
     33 }
     34 </style>
     35 <div id="log"></div>
     36 <div id="container"></div>
     37 <script>
     38 const classes = {
     39  // Open. Noto CJK doesn't have these glyphs or `halt`/`valt`:
     40  // \u3008\u301A\u3010\uFF3B
     41  'O': '\u300A\u300C\u300E\u3014\u3016\u3018\u301D' +
     42       '\uFF08\uFF5B\uFF5F',
     43  // Close. Noto CJK doesn't have these glyphs or `halt`/`valt`:
     44  // \u3009\u301B\u3011\u301E\uFF3D
     45  'C': '\u300B\u300D\u300F\u3015\u3017\u3019\u301F' +
     46       '\uFF09\uFF5D\uFF60',
     47  // Open Quotes.
     48  'Q': '\u2018\u201C',
     49  // Close quotes.
     50  'R': '\u2019\u201D',
     51  // Middle and ideographic space.
     52  'M': '\u30FB\u3000',
     53  // Dot (comma and full stops.)
     54  'D': '\u3001\u3002\uFF0C\uFF0E',
     55  // Colon.
     56  'L': '\uFF1A',
     57  // Semicolon. Colon and Semicolon may be different in vertical flow.
     58  'S': '\uFF1B',
     59  // Exclamation marks.
     60  'E': '\uFF01\uFF1F',
     61 };
     62 const container = document.getElementById('container');
     63 const em = parseInt(getComputedStyle(container).fontSize);
     64 const threshold = em * .8;
     65 
     66 // Generate a list of text from a pattern.
     67 // For example, a pattern 'OC' generates all combinations from
     68 // characters in `classes['O']` and `classes['C']`.
     69 function* textFromPattern(pattern, prefix = '') {
     70  const key = pattern[0];
     71  const rest = pattern.substr(1);
     72  for (const ch of classes[key]) {
     73    if (!rest) {
     74      yield prefix + ch;
     75      continue;
     76    }
     77    yield *textFromPattern(rest, prefix + ch);
     78  }
     79 }
     80 
     81 class TestData {
     82  constructor(text, expect) {
     83    const element = document.createElement('div');
     84    element.textContent = text;
     85    this.text = text;
     86    this.element = element;
     87    this.expect = expect;
     88  }
     89 
     90  static all = [];
     91 
     92  static generate(pattern, expect, container) {
     93    for (const text of textFromPattern(pattern)) {
     94      const data = new TestData(text, expect);
     95      container.appendChild(data.element);
     96      TestData.all.push(data);
     97    }
     98  }
     99 
    100  static runAll(indices) {
    101    if (indices && indices.length) {
    102      for (const index of indices) {
    103        TestData.all[index].run(index);
    104      }
    105      return;
    106    }
    107    TestData.all.forEach((data, i) => {
    108      data.run(i);
    109    })
    110  }
    111 
    112  run(index) {
    113    const advances = getCharAdvances(this.element);
    114    const results = advances.map(advance => advance >= threshold ? 'F' : 'H');
    115    const result = results.join('');
    116    test(() => {
    117      assert_equals(result, this.expect);
    118    }, `${index}: ${this.text}`);
    119  }
    120 }
    121 
    122 setup(() => {
    123  assert_implements(CSS.supports('text-spacing-trim', 'initial'));
    124 }, {explicit_done: true});
    125 (async function () {
    126  const params = new URLSearchParams(window.location.search);
    127  const style= getComputedStyle(container);
    128  const is_vertical = style.writingMode.startsWith('vertical');
    129 
    130  // Assign dots and colons using the Noto's Japanese convention.
    131  classes['C'] += classes['D'];
    132  classes['M'] += classes['L'];
    133  if (!is_vertical) classes['M'] += classes['S'];
    134 
    135  const args = params.getAll('test').flatMap(i => i.split(','));
    136  for (const arg of args) {
    137    const [pattern, expect] = arg.split(':');
    138    TestData.generate(pattern, expect, container);
    139  }
    140 
    141  await document.fonts.ready;
    142  const indices = params.getAll('i').flatMap(i => i.split(','))
    143                        .map(i => parseInt(i));
    144  TestData.runAll(indices);
    145  done();
    146 })();
    147 </script>