get-char-advances.js (2121B)
1 'use strict'; 2 3 /** 4 * Returns an array of advances for all characters in the descendants 5 * of the specified element. 6 * 7 * Technically speaking, advances and glyph bounding boxes are different things, 8 * and advances are not exposed. This function computes approximate values from 9 * bounding boxes. 10 */ 11 function getCharAdvances(element) { 12 const style = getComputedStyle(element); 13 const is_vertical = style.writingMode.startsWith('vertical'); 14 const range = document.createRange(); 15 const all_bounds = [] 16 17 function walk(element) { 18 for (const node of element.childNodes) { 19 const nodeType = node.nodeType; 20 if (nodeType === Node.TEXT_NODE) { 21 const text = node.nodeValue; 22 for (let i = 0; i < text.length; ++i) { 23 range.setStart(node, i); 24 range.setEnd(node, i + 1); 25 let bounds = range.getBoundingClientRect(); 26 // Transpose if it's in vertical flow. Guarantee that top < bottom 27 // and left < right are always true. 28 if (is_vertical) { 29 bounds = { 30 left: bounds.top, 31 top: bounds.left, 32 right: bounds.bottom, 33 bottom: bounds.right 34 }; 35 } 36 all_bounds.push(bounds); 37 } 38 } else if (nodeType === Node.ELEMENT_NODE) { 39 walk(node); 40 } 41 } 42 } 43 walk(element); 44 all_bounds.sort(function(bound_a, bound_b) { 45 if (bound_a.bottom <= bound_b.top) { 46 return -1; 47 } 48 if (bound_b.bottom <= bound_a.top) { 49 return 1; 50 } 51 return bound_a.left - bound_b.left; 52 }); 53 let origin = undefined; 54 let blockEnd = -1; 55 const advances = []; 56 for (let bounds of all_bounds) { 57 // Check if this is on the same line. 58 if (bounds.top >= blockEnd) { 59 origin = undefined; 60 blockEnd = bounds.bottom; 61 } 62 // For the first character, the left bound is closest to the origin. 63 if (origin === undefined) 64 origin = bounds.left; 65 // The right bound is a good approximation of the next origin. 66 advances.push(bounds.right - origin); 67 origin = bounds.right; 68 } 69 return advances; 70 }