Skip to content
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
6 changes: 6 additions & 0 deletions src/core/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -1072,6 +1072,12 @@ export const CHAR = 'CHAR';
* @final
*/
export const WORD = 'WORD';
/**
* @typedef {'JUSTIFIED'} JUSTIFIED
* @property {JUSTIFIED} JUSTIFIED
* @final
*/
export const JUSTIFIED = 'JUSTIFIED';

// TYPOGRAPHY-INTERNAL
export const _DEFAULT_TEXT_FILL = '#000000';
Expand Down
50 changes: 48 additions & 2 deletions src/type/textCore.js
Original file line number Diff line number Diff line change
Expand Up @@ -1501,7 +1501,10 @@ function textCore(p5, fn) {
lines = this._positionLines(x, y, width, height, lines);

// render each line at the adjusted position
lines.forEach(line => this._renderText(line.text, line.x, line.y));
lines.forEach((line, index) => {
const isLastLine = index === lines.length - 1;
this._renderText(line.text, line.x, line.y, Infinity, -Infinity, width, isLastLine);
});

this.textDrawingContext().textBaseline = setBaseline; // restore baseline
};
Expand Down Expand Up @@ -2526,7 +2529,7 @@ function textCore(p5, fn) {
return this.drawingContext;
};

p5.Renderer2D.prototype._renderText = function (text, x, y, maxY, minY) {
p5.Renderer2D.prototype._renderText = function (text, x, y, maxY, minY, width, isLastLine) {
let states = this.states;
let context = this.textDrawingContext();

Expand All @@ -2536,6 +2539,46 @@ function textCore(p5, fn) {

this.push();

// Handle JUSTIFIED alignment
if (states.textAlign === fn.JUSTIFIED && typeof width !== 'undefined' && !isLastLine) {
const words = text.split(' ');
if (words.length > 1) {
const textWidth = this._textWidthSingle(text);
const spaceWidth = this._textWidthSingle(' ');
const totalGaps = words.length - 1;
const extraSpace = width - textWidth;
const spacePerGap = spaceWidth + (extraSpace / totalGaps);

// Render each word with adjusted spacing
let currentX = x;
for (let i = 0; i < words.length; i++) {
const word = words[i];

// no stroke unless specified by user
if (states.strokeColor && states.strokeSet) {
context.strokeText(word, currentX, y);
}

if (!this._clipping && states.fillColor) {
// if fill hasn't been set by user, use default text fill
if (!states.fillSet) {
this._setFill(DefaultFill);
}
context.fillText(word, currentX, y);
}

currentX += this._textWidthSingle(word);
if (i < words.length - 1) {
currentX += spacePerGap;
}
}

this.pop();
return;
}
}

// Default rendering for non-JUSTIFIED or last line
// no stroke unless specified by user
if (states.strokeColor && states.strokeSet) {
context.strokeText(text, x, y);
Expand Down Expand Up @@ -2580,6 +2623,9 @@ function textCore(p5, fn) {
case fn.RIGHT:
adjustedX = x + adjustedW;
break;
case fn.JUSTIFIED:
adjustedX = x; // JUSTIFIED starts from left
break;
case textCoreConstants.END:
throw new Error('textBounds: END not yet supported for textAlign');
}
Expand Down
61 changes: 61 additions & 0 deletions test/manual-test-examples/type/justified-sketch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
function setup() {
createCanvas(800, 600);
background(255);

let sampleText = 'This is a sample text that will be justified. The spacing between words will be adjusted to align both left and right edges.';
let boxWidth = 300;
let boxHeight = 150;

// Test 1: JUSTIFIED with WORD wrap
fill(0);
textSize(16);
textAlign(JUSTIFIED, TOP);
textWrap(WORD);

stroke(200);
noFill();
rect(50, 50, boxWidth, boxHeight);

fill(0);
noStroke();
text('JUSTIFIED + WORD wrap:', 50, 30);
text(sampleText, 50, 50, boxWidth, boxHeight);

// Test 2: JUSTIFIED with CHAR wrap
stroke(200);
noFill();
rect(450, 50, boxWidth, boxHeight);

fill(0);
noStroke();
textWrap(CHAR);
text('JUSTIFIED + CHAR wrap:', 450, 30);
text(sampleText, 450, 50, boxWidth, boxHeight);

// Test 3: LEFT alignment for comparison
stroke(200);
noFill();
rect(50, 250, boxWidth, boxHeight);

fill(0);
noStroke();
textAlign(LEFT, TOP);
textWrap(WORD);
text('LEFT + WORD wrap (comparison):', 50, 230);
text(sampleText, 50, 250, boxWidth, boxHeight);

// Test 4: Show last line is ragged
let multiLineText = 'First line will be justified. Second line will also be justified. But the last line stays ragged.';

stroke(200);
noFill();
rect(450, 250, boxWidth, boxHeight);

fill(0);
noStroke();
textAlign(JUSTIFIED, TOP);
text('JUSTIFIED - last line ragged:', 450, 230);
text(multiLineText, 450, 250, boxWidth, boxHeight);

noLoop();
}
10 changes: 10 additions & 0 deletions test/manual-test-examples/type/justified-test.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<title>JUSTIFIED Alignment Test</title>
<script src="../../../dist/p5.js"></script>
<script src="justified-sketch.js"></script>
</head>
<body>
</body>
</html>