Skip to content

Commit 05aa09a

Browse files
committed
feat(typography): add JUSTIFIED alignment for bounded text in 2D renderer
- Justifies non-final lines in bounded text() - Keeps final line ragged - Maps Canvas2D textAlign to LEFT when JUSTIFIED is set - WEBGL remains left-aligned for now Refs #7712
1 parent 1d5fdb2 commit 05aa09a

File tree

3 files changed

+52
-2
lines changed

3 files changed

+52
-2
lines changed

src/core/constants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,7 @@ export const WORD = 'WORD';
10581058
export const _DEFAULT_TEXT_FILL = '#000000';
10591059
export const _DEFAULT_LEADMULT = 1.25;
10601060
export const _CTX_MIDDLE = 'middle';
1061+
export const JUSTIFIED = 'justified';
10611062

10621063
// VERTICES
10631064
/**

src/core/p5.Renderer.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ class Renderer extends p5.Element {
4949
this._textAlign = constants.LEFT;
5050
this._textBaseline = constants.BASELINE;
5151
this._textWrap = constants.WORD;
52+
this._justifyActive = false;
53+
this._justifyWidth = 0;
54+
this._justifyIsLastLine = false;
5255

5356
this._rectMode = constants.CORNER;
5457
this._ellipseMode = constants.CENTER;
@@ -351,6 +354,9 @@ class Renderer extends p5.Element {
351354
testLine = `${line + words[wordIndex]}` + ' ';
352355
testWidth = this.textWidth(testLine);
353356
if (testWidth > maxWidth && line.length > 0) {
357+
this._justifyActive = this._textAlign === constants.JUSTIFIED;
358+
this._justifyWidth = maxWidth;
359+
this._justifyIsLastLine = false;
354360
this._renderText(
355361
p,
356362
line.trim(),
@@ -359,12 +365,16 @@ class Renderer extends p5.Element {
359365
finalMaxHeight,
360366
finalMinHeight
361367
);
368+
this._justifyActive = false;
362369
line = `${words[wordIndex]}` + ' ';
363370
y += p.textLeading();
364371
} else {
365372
line = testLine;
366373
}
367374
}
375+
this._justifyActive = this._textAlign === constants.JUSTIFIED;
376+
this._justifyWidth = maxWidth;
377+
this._justifyIsLastLine = true;
368378
this._renderText(
369379
p,
370380
line.trim(),
@@ -373,6 +383,7 @@ class Renderer extends p5.Element {
373383
finalMaxHeight,
374384
finalMinHeight
375385
);
386+
this._justifyActive = false;
376387
y += p.textLeading();
377388
}
378389
} else {
@@ -446,6 +457,7 @@ class Renderer extends p5.Element {
446457

447458
// Renders lines of text at any line breaks present in the original string
448459
for (let i = 0; i < lines.length; i++) {
460+
this._justifyActive = false;
449461
this._renderText(
450462
p,
451463
lines[i],

src/core/p5.Renderer2D.js

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,7 +1217,41 @@ class Renderer2D extends p5.Renderer {
12171217
p.push(); // fix to #803
12181218

12191219
if (!this._isOpenType()) {
1220-
// a system/browser font
1220+
if (
1221+
this._textAlign === constants.JUSTIFIED &&
1222+
this._justifyActive &&
1223+
!this._justifyIsLastLine &&
1224+
this._justifyWidth > 0
1225+
) {
1226+
const words = line.split(/\s+/).filter(s => s.length > 0);
1227+
if (words.length > 1) {
1228+
const widths = words.map(s => this.textWidth(s));
1229+
const sum = widths.reduce((a, b) => a + b, 0);
1230+
const extra = this._justifyWidth - sum;
1231+
if (extra > 0) {
1232+
const gap = extra / (words.length - 1);
1233+
let cx = x;
1234+
if (this._doStroke && this._strokeSet) {
1235+
for (let i = 0; i < words.length; i++) {
1236+
this.drawingContext.strokeText(words[i], cx, y);
1237+
cx += widths[i] + (i < words.length - 1 ? gap : 0);
1238+
}
1239+
}
1240+
cx = x;
1241+
if (!this._clipping && this._doFill) {
1242+
if (!this._fillSet) {
1243+
this._setFill(constants._DEFAULT_TEXT_FILL);
1244+
}
1245+
for (let i = 0; i < words.length; i++) {
1246+
this.drawingContext.fillText(words[i], cx, y);
1247+
cx += widths[i] + (i < words.length - 1 ? gap : 0);
1248+
}
1249+
}
1250+
p.pop();
1251+
return p;
1252+
}
1253+
}
1254+
}
12211255

12221256
// no stroke unless specified by user
12231257
if (this._doStroke && this._strokeSet) {
@@ -1272,7 +1306,10 @@ class Renderer2D extends p5.Renderer {
12721306
this.drawingContext.font = `${this._textStyle || 'normal'} ${this._textSize ||
12731307
12}px ${fontNameString}`;
12741308

1275-
this.drawingContext.textAlign = this._textAlign;
1309+
const _ta = this._textAlign === constants.JUSTIFIED
1310+
? constants.LEFT
1311+
: this._textAlign;
1312+
this.drawingContext.textAlign = _ta;
12761313
if (this._textBaseline === constants.CENTER) {
12771314
this.drawingContext.textBaseline = constants._CTX_MIDDLE;
12781315
} else {

0 commit comments

Comments
 (0)