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>