Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add backward-compatible wordBreak option that follows css word-break rules #9

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ Creates a new layout with the given options.
- `text` (string) the text to layout. Newline characters (`\n`) will cause line breaks
- `width` (number, optional) the desired width of the text box, causes word-wrapping and clipping in `"pre"` mode. Leave as undefined to remove word-wrapping (default behaviour)
- `mode` (string) a mode for [word-wrapper](https://www.npmjs.com/package/word-wrapper); can be 'pre' (maintain spacing), or 'nowrap' (collapse whitespace but only break on newline characters), otherwise assumes normal word-wrap behaviour (collapse whitespace, break at width or newlines)
- `wordBreak` (string) can be `"normal"`, `"break-all"`, `"keep-all"`, `"break-word"` (default: `break-all` for backwrad-compatability). Refer to [CSS word-break](https://developer.mozilla.org/en-US/docs/Web/CSS/word-break) prop for how this would work.
- `align` (string) can be `"left"`, `"center"` or `"right"` (default: left)
- `letterSpacing` (number) the letter spacing in pixels (default: 0)
- `lineHeight` (number) the line height in pixels (default to `font.common.lineHeight`)
Expand Down
46 changes: 40 additions & 6 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,9 @@ TextLayout.prototype.computeMetrics = function(text, start, end, width) {
var count = 0
var glyph
var lastGlyph

// Add an option to allow users to disable word clipping when
// wrapping. Not adding it by default for backward compatability.
var wordBreak = this._opt.wordBreak || 'break-all';
if (!font.chars || font.chars.length === 0) {
return {
start: start,
Expand All @@ -167,8 +169,12 @@ TextLayout.prototype.computeMetrics = function(text, start, end, width) {
}
}

// If the container is too small, let at least one word bleed outside the edges.
var minWordsPerLine = 1;
var lineWordsCount = 0;
var glyphsInCurrentWord = 0;
end = Math.min(text.length, end)
for (var i=start; i < end; i++) {
for (var i = start; i < end; i++) {
var id = text.charCodeAt(i)
var glyph = this.getGlyph(font, id)

Expand All @@ -180,16 +186,44 @@ TextLayout.prototype.computeMetrics = function(text, start, end, width) {

var nextPen = curPen + glyph.xadvance + letterSpacing
var nextWidth = curPen + glyph.width

//we've hit our limit; we can't move onto the next glyph
if (nextWidth >= width || nextPen >= width)
break
// We've hit the width limit. Check wordBreak option and decide what to do.
if ((nextWidth >= width || nextPen >= width)) {
// That's it. Break everything. Previous behavior befoer adding wordBreak.
// To keep backword compatability wordBreak defaults to 'break-all' if
// users don't pass it.
// See https://developer.mozilla.org/en-US/docs/Web/CSS/word-break for reference.
if (wordBreak === 'break-all') break;
else if (wordBreak === 'normal') {
if (lineWordsCount >= minWordsPerLine) {
count -= glyphsInCurrentWord;
break;
}
}
else if (wordBreak === 'break-word') {
if (lineWordsCount > minWordsPerLine) {
// If we already have words in the line just go to a new line.
count -= glyphsInCurrentWord;
} else {
// If this is the first word in that line, then let's break it.
break;
}
} else if (wordBreak === 'keep-all') {
// Just never break.
}
}

//otherwise continue along our line
curPen = nextPen
curWidth = nextWidth
lastGlyph = glyph
}
// If we're at an end of the word, reset current glyphs in word count.
if (id === SPACE_ID || id === TAB_ID) {
glyphsInCurrentWord = 0;
lineWordsCount++;
}
// Otherwise count it as part of the current word.
else glyphsInCurrentWord++;
count++
}

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "layout-bmfont-text",
"version": "1.3.4",
"version": "1.4.0",
"description": "word-wraps and lays out text glyphs",
"main": "index.js",
"license": "MIT",
Expand All @@ -22,6 +22,7 @@
"canvas-testbed": "^1.0.3",
"clamp": "^1.0.1",
"img": "^1.0.0",
"indexof-property": "^1.1.1",
"lerp": "^1.0.3",
"smoothstep": "^1.0.1",
"tape": "^3.5.0",
Expand Down
60 changes: 60 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,65 @@ test('should export API', function(t) {
t.deepEqual(layout.glyphs.map(function (x) {
return x.index
}), [ 0, 1, 3, 4 ], 'provides indices')

// Test limited width and test wordBreak options
// Default behavior of wordBreak;
layout = createLayout({
text: 'hello\nworld',
font: font,
width: 50,
})
t.deepEqual(layout.glyphs.map(function (x) {
return x.line
}), [ 0, 0, 0, 1, 1, 2, 2, 3, 3, 3 ], 'breaks all words by default')

// wordBreak: break-all (also default behavior)
layout = createLayout({
text: 'hello\nworld',
font: font,
width: 50,
wordBreak: 'break-all',
})
t.deepEqual(layout.glyphs.map(function (x) {
return x.line
}), [ 0, 0, 0, 1, 1, 2, 2, 3, 3, 3 ], 'wordBreak: break-all')


// wordBreak: normal
layout = createLayout({
text: 'line1\nline2 thiswrapsbutstaysone',
font: font,
width: 50,
wordBreak: 'normal',
})

t.deepEqual(layout.glyphs.map(function (x) {
return x.line
}), [ 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ], 'wordBreak: normal')

// wordBreak: keep-all
layout = createLayout({
text: 'hello line1\nworld line2',
font: font,
width: 50,
wordBreak: 'keep-all',
})

t.deepEqual(layout.glyphs.map(function (x) {
return x.line
}), [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ], 'wordBreak: keep-all')

// wordBreak: break-word
layout = createLayout({
text: 'hel by a superlong',
font: font,
width: 50,
wordBreak: 'break-word',
})

t.deepEqual(layout.glyphs.map(function (x) {
return x.line
}), [0, 0, 0, 1, 1, 2, 3, 3, 4, 4, 4, 5, 5, 5, 6 ], 'wordBreak: break-word')

t.end()
})