diff --git a/src/core/p5.Renderer.js b/src/core/p5.Renderer.js index 2e0ab55abc..541a316c86 100644 --- a/src/core/p5.Renderer.js +++ b/src/core/p5.Renderer.js @@ -233,6 +233,8 @@ p5.Renderer.prototype.text = function(str, x, y, maxWidth, maxHeight) { let chars; let shiftedY; let finalMaxHeight = Number.MAX_VALUE; + // fix for #5785 (top of bounding box) + let finalMinHeight = y; if (!(this._doFill || this._doStroke)) { return; @@ -263,29 +265,48 @@ p5.Renderer.prototype.text = function(str, x, y, maxWidth, maxHeight) { break; } - let baselineHacked = false; if (typeof maxHeight !== 'undefined') { if (this._rectMode === constants.CENTER) { y -= maxHeight / 2; } + let originalY = y; + let ascent = p.textAscent(); + switch (this._textBaseline) { case constants.BOTTOM: shiftedY = y + maxHeight; y = Math.max(shiftedY, y); + // fix for #5785 (top of bounding box) + finalMinHeight += ascent; break; case constants.CENTER: shiftedY = y + maxHeight / 2; y = Math.max(shiftedY, y); - break; - case constants.BASELINE: - baselineHacked = true; - this._textBaseline = constants.TOP; + // fix for #5785 (top of bounding box) + finalMinHeight += ascent / 2; break; } // remember the max-allowed y-position for any line (fix to #928) - finalMaxHeight = y + maxHeight - p.textAscent(); + finalMaxHeight = y + maxHeight - ascent; + + // fix for #5785 (bottom of bounding box) + if (this._textBaseline === constants.CENTER) { + finalMaxHeight = originalY + maxHeight - ascent / 2; + } + } else { + // no text-height specified, show warning for BOTTOM / CENTER + if (this._textBaseline === constants.BOTTOM) { + return console.warn( + 'textAlign(*, BOTTOM) requires x, y, width and height' + ); + } + if (this._textBaseline === constants.CENTER) { + return console.warn( + 'textAlign(*, CENTER) requires x, y, width and height' + ); + } } // Render lines of text according to settings of textWrap @@ -310,10 +331,9 @@ p5.Renderer.prototype.text = function(str, x, y, maxWidth, maxHeight) { } let offset = 0; - const vAlign = p.textAlign().vertical; - if (vAlign === constants.CENTER) { + if (this._textBaseline === constants.CENTER) { offset = (nlines.length - 1) * p.textLeading() / 2; - } else if (vAlign === constants.BOTTOM) { + } else if (this._textBaseline === constants.BOTTOM) { offset = (nlines.length - 1) * p.textLeading(); } @@ -324,18 +344,29 @@ p5.Renderer.prototype.text = function(str, x, y, maxWidth, maxHeight) { testLine = `${line + words[wordIndex]}` + ' '; testWidth = this.textWidth(testLine); if (testWidth > maxWidth && line.length > 0) { - this._renderText(p, line.trim(), x, y - offset, finalMaxHeight); + this._renderText( + p, + line.trim(), + x, + y - offset, + finalMaxHeight, + finalMinHeight + ); line = `${words[wordIndex]}` + ' '; y += p.textLeading(); } else { line = testLine; } } - this._renderText(p, line.trim(), x, y - offset, finalMaxHeight); + this._renderText( + p, + line.trim(), + x, + y - offset, + finalMaxHeight, + finalMinHeight + ); y += p.textLeading(); - if (baselineHacked) { - this._textBaseline = constants.BASELINE; - } } } else { let nlines = []; @@ -356,10 +387,9 @@ p5.Renderer.prototype.text = function(str, x, y, maxWidth, maxHeight) { nlines.push(line); let offset = 0; - const vAlign = p.textAlign().vertical; - if (vAlign === constants.CENTER) { + if (this._textBaseline === constants.CENTER) { offset = (nlines.length - 1) * p.textLeading() / 2; - } else if (vAlign === constants.BOTTOM) { + } else if (this._textBaseline === constants.BOTTOM) { offset = (nlines.length - 1) * p.textLeading(); } @@ -374,33 +404,49 @@ p5.Renderer.prototype.text = function(str, x, y, maxWidth, maxHeight) { if (testWidth <= maxWidth) { line += chars[charIndex]; } else if (testWidth > maxWidth && line.length > 0) { - this._renderText(p, line.trim(), x, y - offset, finalMaxHeight); + this._renderText( + p, + line.trim(), + x, + y - offset, + finalMaxHeight, + finalMinHeight + ); y += p.textLeading(); line = `${chars[charIndex]}`; } } } - this._renderText(p, line.trim(), x, y - offset, finalMaxHeight); + this._renderText( + p, + line.trim(), + x, + y - offset, + finalMaxHeight, + finalMinHeight + ); y += p.textLeading(); - - if (baselineHacked) { - this._textBaseline = constants.BASELINE; - } } } else { // Offset to account for vertically centering multiple lines of text - no // need to adjust anything for vertical align top or baseline let offset = 0; - const vAlign = p.textAlign().vertical; - if (vAlign === constants.CENTER) { + if (this._textBaseline === constants.CENTER) { offset = (lines.length - 1) * p.textLeading() / 2; - } else if (vAlign === constants.BOTTOM) { + } else if (this._textBaseline === constants.BOTTOM) { offset = (lines.length - 1) * p.textLeading(); } // Renders lines of text at any line breaks present in the original string for (let i = 0; i < lines.length; i++) { - this._renderText(p, lines[i], x, y - offset, finalMaxHeight); + this._renderText( + p, + lines[i], + x, + y - offset, + finalMaxHeight, + finalMinHeight + ); y += p.textLeading(); } } diff --git a/src/core/p5.Renderer2D.js b/src/core/p5.Renderer2D.js index 33279c0bc3..ff8c6dacef 100644 --- a/src/core/p5.Renderer2D.js +++ b/src/core/p5.Renderer2D.js @@ -1190,9 +1190,9 @@ p5.Renderer2D.prototype.text = function(str, x, y, maxWidth, maxHeight) { return p; }; -p5.Renderer2D.prototype._renderText = function(p, line, x, y, maxY) { - if (y >= maxY) { - return; // don't render lines beyond our maxY position +p5.Renderer2D.prototype._renderText = function(p, line, x, y, maxY, minY) { + if (y < minY || y >= maxY) { + return; // don't render lines beyond our minY/maxY bounds (see #5785) } p.push(); // fix to #803 diff --git a/test/manual-test-examples/p5.Font/textInRect/index.html b/test/manual-test-examples/p5.Font/textInRect/index.html new file mode 100644 index 0000000000..35938afb91 --- /dev/null +++ b/test/manual-test-examples/p5.Font/textInRect/index.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/test/manual-test-examples/p5.Font/textInRect/sketch.js b/test/manual-test-examples/p5.Font/textInRect/sketch.js new file mode 100644 index 0000000000..d7d5fa0f49 --- /dev/null +++ b/test/manual-test-examples/p5.Font/textInRect/sketch.js @@ -0,0 +1,81 @@ +let xpos = 50; +let ypos = 100; +let str = + 'One Two Three Four Five Six Seven Eight Nine Ten Eleven Twelve Thirteen Fourteen Fifteen Sixteen Seventeen Eighteen Nineteen Twenty Twenty-one Twenty-two Twenty-three Twenty-four Twenty-five Twenty-six Twenty-seven Twenty-eight Twenty-nine Thirty Thirty-one Thirty-two Thirty-three Thirty-four Thirty-five Thirty-six Thirty-seven Thirty-eight Thirty-nine Forty Forty-one Forty-two Forty-three Forty-four Forty-five Forty-six Forty-seven Forty-eight Forty-nine Fifty Fifty-one Fifty-two Fifty-three'; + +function setup() { + createCanvas(1050, 800); + background(245); + + let ta = textAscent(); + + textAlign(CENTER, TOP); + rect(xpos, ypos, 200, 200); + text(str, xpos, ypos, 200, 200); + xpos += 250; + + textAlign(CENTER, CENTER); + rect(xpos, ypos, 200, 200); + text(str, xpos, ypos, 200, 200); + xpos += 250; + + textAlign(CENTER, BOTTOM); + rect(xpos, ypos, 200, 200); + text(str, xpos, ypos, 200, 200); + xpos += 250; + + textAlign(CENTER, BASELINE); + rect(xpos, ypos, 200, 200); + text(str, xpos, ypos, 200, 200); + + textSize(18); + textAlign(CENTER, TOP); + text('TOP', 150, height / 2 - 40); + text('CENTER', 400, height / 2 - 40); + text('BOTTOM', 650, height / 2 - 40); + text('BASELINE', 900, height / 2 - 40); + textSize(12); + + xpos = 50; + ypos += 400; + + textAlign(CENTER, TOP); + rect(xpos, ypos, 200, 200); + text(str, xpos, ypos, 200); + xpos += 250; + + textAlign(CENTER, CENTER); + rect(xpos, ypos, 200, 200); + text(str, xpos, ypos, 200); + xpos += 250; + + textAlign(CENTER, BOTTOM); + rect(xpos, ypos, 200, 200); + text(str, xpos, ypos, 200); + xpos += 250; + + textAlign(CENTER, BASELINE); + rect(xpos, ypos, 200, 200); + text(str, xpos, ypos, 200); + + textSize(18); + textAlign(CENTER, TOP); + text('TOP', 150, height / 2 - 40); + text('CENTER', 400, height / 2 - 40); + text('BOTTOM', 650, height / 2 - 40); + text('BASELINE', 900, height / 2 - 40); + text('TOP', 150, ypos + 270); + text('CENTER', 400, ypos + 270); + text('BOTTOM', 650, ypos + 270); + text('BASELINE', 900, ypos + 270); + + fill(255); + noStroke(); + textSize(24); + + rect(0, height / 2, width, 15); + fill(0); + textAlign(LEFT, TOP); + text('text(s, x, y, w, h)', 20, 40); + text('text(s, x, y, w) [no height]', 20, height / 2 + 40); +}