at-font-face-font-matching.html (11592B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>Testing @font-face font matching logic introduced in CSS Fonts level 4</title> 5 <link rel="help" href="https://www.w3.org/TR/css-fonts-4/#font-matching-algorithm" /> 6 <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/9389#issuecomment-2625314278"> 7 <meta name="timeout" content="long"> 8 <script src="/resources/testharness.js"></script> 9 <script src="/resources/testharnessreport.js"></script> 10 <style> 11 .test 12 { 13 float:left; 14 border:1px solid red; 15 font-size:24pt; 16 white-space: nowrap; 17 clear:both; 18 } 19 20 @font-face { font-family: W100; src: url('./resources/csstest-weights-100-kerned.ttf'); } 21 @font-face { font-family: W200; src: url('./resources/csstest-weights-200-kerned.ttf'); } 22 @font-face { font-family: W300; src: url('./resources/csstest-weights-300-kerned.ttf'); } 23 24 25 @font-face { font-family: descriptorPriorityTest; src: url('./resources/csstest-weights-100-kerned.ttf'); font-stretch: 125%; } 26 @font-face { font-family: descriptorPriorityTest; src: url('./resources/csstest-weights-200-kerned.ttf'); font-style: italic; } 27 @font-face { font-family: descriptorPriorityTest; src: url('./resources/csstest-weights-300-kerned.ttf'); font-weight: 350; } 28 </style> 29 <style id="dynamicStyles"> 30 </style> 31 </head> 32 <body> 33 34 <span style="position: absolute; top: -100vh;"> 35 <span style="font-family: 'W100';">A</span> 36 <span style="font-family: 'W200';">A</span> 37 <span style="font-family: 'W300';">A</span> 38 <span style="font-family: 'descriptorPriorityTest'; font-stretch: 125%;">A</span> 39 <span style="font-family: 'descriptorPriorityTest'; font-style: italic;">A</span> 40 <span style="font-family: 'descriptorPriorityTest'; font-weight: 350;">A</span> 41 </span> 42 43 <div id="master" class="test">A1 A2 A2 A3 A3 A3</div> 44 <div id="test" class="test">A1 A2 A2 A3 A3 A3</div> 45 <div style="clear:both"></div> 46 <script> 47 48 // wait for the fonts to load 49 // -- this should not be necessary if the fonts are installed as required 50 // -- but if they are not, the test is otherwise unstable 51 var once_fonts_are_ready = (document.fonts ? document.fonts.ready : new Promise(function(ready) { window.onload = time => [...document.querySelectorAll('body > span:nth-child(1) > span')].every(e => e.offsetWidth > 20) ? ready() : requestAnimationFrame(window.onload) })); 52 53 var masterElement = document.getElementById("master"); 54 var testElement = document.getElementById("test"); 55 var dynamicStyles = document.getElementById("dynamicStyles"); 56 57 function verifyFont(testFamily, testWeight, testStyle, testStretch, expectedFamily) { 58 59 testElement.style.fontWeight = "normal"; 60 testElement.style.fontStyle = "normal"; 61 testElement.style.fontStretch = "normal"; 62 63 masterElement.style.fontFamily = expectedFamily; 64 let masterWidth = masterElement.offsetWidth; 65 66 testElement.style.fontFamily = expectedFamily; 67 assert_equals(masterWidth, testElement.offsetWidth, "Sanity test: same family name gets same width" + dynamicStyles.innerHTML); 68 69 testElement.style.fontFamily = "serif"; 70 assert_not_equals(masterWidth, testElement.offsetWidth, "Sanity test: different family get different width"); 71 72 testElement.style.fontWeight = testWeight; 73 testElement.style.fontStyle = testStyle; 74 testElement.style.fontStretch = testStretch; 75 testElement.style.fontFamily = testFamily; 76 77 assert_equals(masterWidth, testElement.offsetWidth, "Unexpected font on test element"); 78 } 79 80 var descriptorPriorityCases = [ 81 { weight: "normal", style: "italic", stretch: "125%", expectedFamily: "'W100'", description: "Stretch has higher priority than style"}, 82 { weight: "350", style: "normal", stretch: "125%", expectedFamily: "'W100'", description: "Stretch has higher priority than weight"}, 83 { weight: "350", style: "italic", stretch: "normal", expectedFamily: "'W200'", description: "Style has higher priority than weight"} 84 ]; 85 86 descriptorPriorityCases.forEach(function (testCase) { 87 promise_test(() => { 88 return once_fonts_are_ready 89 .then(() => verifyFont("descriptorPriorityTest", testCase.weight, testCase.style, testCase.stretch, testCase.expectedFamily)); 90 }, 91 "Descriptor matching priority: " + testCase.description 92 ); 93 }); 94 95 function load(family, name, value) { 96 const el1 = document.createElement("span"); 97 const el2 = document.createElement("span"); 98 el1.innerText = "A"; 99 el2.innerText = "A"; 100 let value1, value2; 101 if (value.indexOf("deg") > 0) { 102 value1 = "oblique " + value.split(" ")[1]; 103 value2 = "oblique " + (value.split(" ")[2] || value.split(" ")[1]); 104 } else { 105 value1 = value.split(" ")[0]; 106 value2 = value.split(" ")[1] || value1; 107 } 108 el1.style[name] = value1; 109 el2.style[name] = value2; 110 document.body.appendChild(el1); 111 document.body.appendChild(el2); 112 const initialWidth1 = el1.offsetWidth; 113 const initialWidth2 = el2.offsetWidth; 114 return new Promise((resolve) => { 115 el1.style.fontFamily = family; 116 el2.style.fontFamily = family; 117 (function check() { 118 if (el1.offsetWidth !== initialWidth1 && el2.offsetWidth !== initialWidth2) { 119 el1.remove(); 120 el2.remove(); 121 resolve(); 122 } else { 123 requestAnimationFrame(check); 124 } 125 }()); 126 }); 127 } 128 function createFontFaceRules(fontFaceFamily, descriptorName, expectedMatch, unexpectedMatch) { 129 dynamicStyles.innerHTML = 130 "@font-face { font-family: " + fontFaceFamily + "; src: url('./resources/csstest-weights-100-kerned.ttf'); " + descriptorName + ": " + expectedMatch + "; }" + 131 "@font-face { font-family: " + fontFaceFamily + "; src: url('./resources/csstest-weights-200-kerned.ttf'); " + descriptorName + ": " + unexpectedMatch + "; }"; 132 133 return Promise.all([ 134 load(fontFaceFamily, descriptorName, expectedMatch), 135 load(fontFaceFamily, descriptorName, unexpectedMatch) 136 ]); 137 } 138 139 let familyId = 0; 140 141 function testDescriptor(descriptorName, testCases) { 142 testCases.forEach(function (testCase) { 143 // Go though test cases, checking each descriptor has higher priority than next in the list 144 for(let i = 0; i < testCase.testDescriptors.length - 1; i++) { 145 let expectedMatch = testCase.testDescriptors[i]; 146 let unexpectedMatch = testCase.testDescriptors[i + 1]; 147 familyId += 1; 148 const family = "MatchTestFamily" + familyId; 149 150 promise_test( 151 () => { 152 return createFontFaceRules(family, descriptorName, expectedMatch, unexpectedMatch) 153 .then(() => { 154 let testWeight = (descriptorName == "font-weight") ? testCase.value : "normal"; 155 let testStyle = (descriptorName == "font-style") ? testCase.value : "normal"; 156 let testStretch = (descriptorName == "font-stretch") ? testCase.value : "normal"; 157 158 verifyFont(family, testWeight, testStyle, testStretch, "'W100'"); 159 }); 160 }, 161 "Matching " + descriptorName + ": '" + testCase.value + "' should prefer '" + expectedMatch + "' over '" + unexpectedMatch + "'"); 162 } 163 }); 164 } 165 166 // Each case defines property value being tested and set of descriptor values in order of matching priority from highest to lowest 167 168 testDescriptor("font-weight", [ 169 { value: "400", testDescriptors: ["400", "450 460", "500", "350 399", "351 398", "501 550", "502 560"] }, 170 { value: "430", testDescriptors: ["420 440", "450 460", "500", "400 425", "350 399", "340 398", "501 550", "502 560"] }, 171 { value: "500", testDescriptors: ["500", "450 460", "400", "350 399", "351 398", "501 550", "502 560"] }, 172 { value: "501", testDescriptors: ["501", "502 510", "503 520", "500", "450 460", "390 410", "300 350"] }, 173 { value: "399", testDescriptors: ["350 399", "340 360", "200 300", "400", "450 460", "500 501", "502 510"] } 174 ]); 175 176 testDescriptor("font-stretch", [ 177 { value: "100%", testDescriptors: ["100%", "110% 120%", "115% 116%"] }, 178 { value: "110%", testDescriptors: ["110% 120%", "115% 116%", "105%", "100%", "50% 80%", "60% 70%"] }, 179 { value: "90%", testDescriptors: ["90% 100%", "50% 80%", "60% 70%", "110% 140%", "120% 130%"] }, 180 ]); 181 182 testDescriptor("font-style", [ 183 { value: "normal", testDescriptors: ["normal", "oblique 10deg 40deg", "oblique 20deg 30deg", "oblique -50deg -20deg", "oblique -40deg -30deg" ] }, 184 { value: "italic", testDescriptors: ["italic", "oblique 20deg", "oblique 30deg 60deg", "oblique 40deg 50deg", "oblique 5deg 10deg", "oblique 5deg", "normal", "oblique -60deg -30deg", "oblique -50deg -40deg" ] }, 185 { value: "oblique 20deg", testDescriptors: ["oblique 20deg", "oblique 30deg 60deg", "oblique 40deg 50deg", "oblique 10deg", "oblique 0deg", "oblique -50deg -20deg", "oblique -40deg -30deg", "italic" ] }, 186 { value: "oblique 21deg", testDescriptors: ["oblique 21deg", "oblique 30deg 60deg", "oblique 40deg 50deg", "oblique 20deg", "oblique 10deg", "oblique 0deg", "oblique -50deg -20deg", "oblique -40deg -30deg", "italic" ] }, 187 { value: "oblique 10deg", testDescriptors: ["oblique 10deg", "oblique 5deg", "oblique 15deg 20deg", "oblique 30deg 60deg", "oblique 40deg 50deg", "oblique 0deg", "oblique -50deg -20deg", "oblique -40deg -30deg", "italic" ] }, 188 { value: "oblique 0deg", testDescriptors: ["oblique 0deg", "oblique 5deg", "oblique 15deg 20deg", "oblique 30deg 60deg", "oblique 40deg 50deg", "oblique -50deg -20deg", "oblique -40deg -30deg", "italic" ] }, 189 { value: "oblique -10deg", testDescriptors: ["oblique -10deg", "oblique -5deg", "oblique -1deg 0deg", "oblique -20deg -15deg", "oblique -60deg -30deg", "oblique -50deg -40deg", "oblique 0deg 10deg", "oblique 40deg 50deg", "italic" ] }, 190 { value: "oblique -20deg", testDescriptors: ["oblique -20deg", "oblique -60deg -40deg", "oblique -10deg", "oblique 0deg", "oblique 30deg 60deg", "oblique 40deg 50deg", "italic"] }, 191 { value: "oblique -21deg", testDescriptors: ["oblique -21deg", "oblique -60deg -40deg", "oblique -10deg", "oblique 0deg", "oblique 30deg 60deg", "oblique 40deg 50deg", "italic"] }, 192 ]); 193 194 </script> 195 </body> 196 </html>