Воспроизвести ограничивающую рамку текста в браузерах
При использовании SVG в браузере браузер имеет getBBox
функция, чтобы дать вам ограничительную рамку различных элементов. Но когда дело дошло до текстовых элементов, меня очень смутило, как рассчитывается эта коробка. Я знаю, что размер шрифта основан на em-Box, который указан в файле шрифта. Однако мои тесты показывают, что ни один из них не дает таких же результатов, как в FF или Chrome (которые отличаются только на несколько пикселей на fontsize 1000):
fontSize != bbox-height
(ascender-descender)/unitsPerEm * fontSize != bbox-height
(unitsPerEm-descender)/unitsPerEm * fontSize != bbox-height
...maybe adding a fixed amount to ascender for accents? Like Ć
Так в чем же секрет высоты текста bbox в браузерах?
Я даже пытался заглянуть в исходный код FF и Chrome, но найти правильное место, где основаны расчеты, - это сложная задача.
// РЕДАКТИРОВАТЬ: В ответ на комментарий: я хочу рассчитать bbox текста SVG, как это сделано в браузере (повторить поведение). Мне нужно знать метрики шрифта, которые необходимы для правильного расчета bbox и формуляра, который используется для вычисления (ширина и высота достаточны)
1 ответ
После множества исследований и поисков и ошибок я нашел возможное решение, по крайней мере, для объяснения поведения цветов в текстовых измерениях bbox.
Высота BBox
Прежде всего я использовал пакет npm fontkit
загрузить и разобрать файл шрифта. fontkit
дать вам несколько метрик для шрифта в целом, который включает в себя:
- font.ascent
- font.descent
- font.lineGap
- font.unitsPerEm
Итак, чтобы рассчитать высоту bbox, я рассчитал следующее:
bboxHeight = (font.ascent - font.descent + font.lineGap) / unitsPerEm * fontSize
Однако, это приводит к ошибкам, когда шрифт больше, чем поле em (font.ascent - font.descent > unitsPerEm
). В этом особом случае bboxHeight
является font.ascent - font.descent
,
Это приводит к следующему коду для высоты:
var fontHeight = font.ascent - font.descent
var lineHeight = fontHeight > font.unitsPerEm ? fontHeight : fontHeight + font.lineGap
var height = lineHeight/font.unitsPerEm * fontSize
Ширина BBox
чтобы вычислить ширину текста, который я использовал layout
особенность fontkit
, layout
дает вам доступ к глифам, из которых взят текст, а также доступ к метрикам глифа. Метрика нам нужна advanceWidth
который включает поля для других глифов рядом с текущим glpyh. Суммируя все advanceWidth
S и масштабируя их соответственно, я в конечном итоге с bboxWidth
:
var width = font.layout(text).glyphs.reduce((last, curr) => last + curr.advanceWidth, 0)
width = width / font.unitsPerEm * fontSize
BBox y position
Проблема не останавливается здесь, мы все еще должны вычислить y-позицию bbox. Вот довольно простая формула:
var bboxY = y-font.ascent/font.unitsPerEm * fontSize
Где у теоретическая позиция, которую вы бы вытянуть из дома (y
а также dy
атрибут)
BBox x position
Это просто фигура, которую вы вытаскиваете из дома (x
а также dx
)
Вся коробка:
var box = {
x:x,
y: y-font.ascent/font.unitsPerEm * fontSize,
width: width
height: height
}
Надеюсь, это поможет кому-то еще!