@nrkn/text-layout
Version:
Wrapping and fitting styled runs of text
81 lines (64 loc) • 1.92 kB
text/typescript
import { groupWords, measuredRun, splitRunsOnSpaces } from './runs.js'
import {
Block, Line, MeasureMetrics, MeasureRunWidth, TextRun, Word
} from './types.js'
// split the runs on spaces and generate words (unbreakable runs)
export const runsToWords = (
measureText: MeasureRunWidth | MeasureMetrics
) => {
const mr = measuredRun(measureText)
const rtw = (runs: TextRun[]) => {
const withSpaces = splitRunsOnSpaces(runs)
const wordGroups = groupWords(withSpaces)
const words: Word[] = []
for (const wg of wordGroups) {
const measuredRuns = wg.map(mr)
const word: Word = {
runs: measuredRuns,
width: 0,
advanceX: 0,
height: 0
}
for (let i = 0; i < measuredRuns.length; i++) {
const run = measuredRuns[i]
word.width += run.width
word.advanceX += run.advanceX
word.height = Math.max(word.height, run.height)
if (i === 0 && run.actualBoundingBoxLeft !== undefined) {
word.opticalLeft = run.actualBoundingBoxLeft
}
if (
i === measuredRuns.length - 1 &&
run.actualBoundingBoxRight !== undefined
) {
word.opticalRight = run.actualBoundingBoxRight
}
}
words.push(word)
}
return words
}
return rtw
}
export const emptyWord = (): Word => ({
runs: [],
width: 0,
advanceX: 0,
height: 0
})
// find the longest word in a line
export const longestWordInLine = (line: Line) =>
line.words.reduce(
(longest, word) =>
word.width > longest.width ? word : longest,
line.words[0] || emptyWord()
)
// find the longest word in a block
export const longestWordInBlock = (block: Block) =>
block.lines.reduce(
(longest, line) => {
const lw = longestWordInLine(line)
return lw.width > longest.width ? lw : longest
},
block.lines[0].words[0] || emptyWord()
)