chromium/third_party/blink/web_tests/external/wpt/css/css-text/support/get-char-advances.js

'use strict';

/**
 * Returns an array of advances for all characters in the descendants
 * of the specified element.
 *
 * Technically speaking, advances and glyph bounding boxes are different things,
 * and advances are not exposed. This function computes approximate values from
 * bounding boxes.
 */
function getCharAdvances(element) {
  const style = getComputedStyle(element);
  const is_vertical = style.writingMode.startsWith('vertical');
  const range = document.createRange();
  const all_bounds = []

  function walk(element) {
    for (const node of element.childNodes) {
      const nodeType = node.nodeType;
      if (nodeType === Node.TEXT_NODE) {
        const text = node.nodeValue;
        for (let i = 0; i < text.length; ++i) {
          range.setStart(node, i);
          range.setEnd(node, i + 1);
          let bounds = range.getBoundingClientRect();
          // Transpose if it's in vertical flow. Guarantee that top < bottom
          // and left < right are always true.
          if (is_vertical) {
            bounds = {
              left: bounds.top,
              top: bounds.left,
              right: bounds.bottom,
              bottom: bounds.right
            };
          }
          all_bounds.push(bounds);
        }
      } else if (nodeType === Node.ELEMENT_NODE) {
        walk(node);
      }
    }
  }
  walk(element);
  all_bounds.sort(function(bound_a, bound_b) {
    if (bound_a.bottom <= bound_b.top) {
      return -1;
    }
    if (bound_b.bottom <= bound_a.top) {
      return 1;
    }
    return bound_a.left - bound_b.left;
  });
  let origin = undefined;
  let blockEnd = -1;
  const advances = [];
  for (let bounds of all_bounds) {
    // Check if this is on the same line.
    if (bounds.top >= blockEnd) {
      origin = undefined;
      blockEnd = bounds.bottom;
    }
    // For the first character, the left bound is closest to the origin.
    if (origin === undefined)
      origin = bounds.left;
    // The right bound is a good approximation of the next origin.
    advances.push(bounds.right - origin);
    origin = bounds.right;
  }
  return advances;
}