From ae922ec07e560ca3640bc51afe9d8f4b2f3f6326 Mon Sep 17 00:00:00 2001 From: dhowe Date: Fri, 1 Oct 2021 21:04:24 +0800 Subject: [PATCH] fix horizontal/vertical alignment for multiline text --- src/core/p5.Renderer.js | 71 ++++++++++++++++--- .../p5.Font/custom/sketch.js | 16 ++--- .../p5.Font/system/sketch.js | 20 +++--- 3 files changed, 81 insertions(+), 26 deletions(-) diff --git a/src/core/p5.Renderer.js b/src/core/p5.Renderer.js index 5d70f7218b..2e0ab55abc 100644 --- a/src/core/p5.Renderer.js +++ b/src/core/p5.Renderer.js @@ -224,6 +224,7 @@ p5.Renderer.prototype.textWrap = function(wrapStyle) { p5.Renderer.prototype.text = function(str, x, y, maxWidth, maxHeight) { const p = this._pInst; const textWrapStyle = this._textWrap; + let lines; let line; let testLine; @@ -243,7 +244,8 @@ p5.Renderer.prototype.text = function(str, x, y, maxWidth, maxHeight) { str = str.toString(); } - // Replaces tabs with double-spaces and splits string at any line breaks present in the original string + // Replaces tabs with double-spaces and splits string on any line + // breaks present in the original string str = str.replace(/(\t)/g, ' '); lines = str.split('\n'); @@ -287,8 +289,34 @@ p5.Renderer.prototype.text = function(str, x, y, maxWidth, maxHeight) { } // Render lines of text according to settings of textWrap - // Splits lines at spaces, for loop adds one word + space at a time and tests length with next word added + // Splits lines at spaces, for loop adds one word + space + // at a time and tests length with next word added if (textWrapStyle === constants.WORD) { + let nlines = []; + for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { + line = ''; + words = lines[lineIndex].split(' '); + for (let wordIndex = 0; wordIndex < words.length; wordIndex++) { + testLine = `${line + words[wordIndex]}` + ' '; + testWidth = this.textWidth(testLine); + if (testWidth > maxWidth && line.length > 0) { + nlines.push(line); + line = `${words[wordIndex]}` + ' '; + } else { + line = testLine; + } + } + nlines.push(line); + } + + let offset = 0; + const vAlign = p.textAlign().vertical; + if (vAlign === constants.CENTER) { + offset = (nlines.length - 1) * p.textLeading() / 2; + } else if (vAlign === constants.BOTTOM) { + offset = (nlines.length - 1) * p.textLeading(); + } + for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { line = ''; words = lines[lineIndex].split(' '); @@ -296,21 +324,21 @@ 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, x, y, finalMaxHeight); + this._renderText(p, line.trim(), x, y - offset, finalMaxHeight); line = `${words[wordIndex]}` + ' '; y += p.textLeading(); } else { line = testLine; } } - this._renderText(p, line, x, y, finalMaxHeight); + this._renderText(p, line.trim(), x, y - offset, finalMaxHeight); y += p.textLeading(); if (baselineHacked) { this._textBaseline = constants.BASELINE; } } } else { - // Splits lines at characters, for loop adds one char at a time and tests length with next char added + let nlines = []; for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { line = ''; chars = lines[lineIndex].split(''); @@ -320,13 +348,39 @@ 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, x, y, finalMaxHeight); + nlines.push(line); + line = `${chars[charIndex]}`; + } + } + } + + nlines.push(line); + let offset = 0; + const vAlign = p.textAlign().vertical; + if (vAlign === constants.CENTER) { + offset = (nlines.length - 1) * p.textLeading() / 2; + } else if (vAlign === constants.BOTTOM) { + offset = (nlines.length - 1) * p.textLeading(); + } + + // Splits lines at characters, for loop adds one char at a time + // and tests length with next char added + for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { + line = ''; + chars = lines[lineIndex].split(''); + for (let charIndex = 0; charIndex < chars.length; charIndex++) { + testLine = `${line + chars[charIndex]}`; + testWidth = this.textWidth(testLine); + if (testWidth <= maxWidth) { + line += chars[charIndex]; + } else if (testWidth > maxWidth && line.length > 0) { + this._renderText(p, line.trim(), x, y - offset, finalMaxHeight); y += p.textLeading(); line = `${chars[charIndex]}`; } } } - this._renderText(p, line, x, y, finalMaxHeight); + this._renderText(p, line.trim(), x, y - offset, finalMaxHeight); y += p.textLeading(); if (baselineHacked) { @@ -337,7 +391,6 @@ p5.Renderer.prototype.text = function(str, x, y, maxWidth, maxHeight) { // 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) { offset = (lines.length - 1) * p.textLeading() / 2; @@ -360,7 +413,7 @@ p5.Renderer.prototype._applyDefaults = function() { }; /** - * Helper fxn to check font type (system or otf) + * Helper function to check font type (system or otf) */ p5.Renderer.prototype._isOpenType = function(f = this._textFont) { return typeof f === 'object' && f.font && f.font.supported; diff --git a/test/manual-test-examples/p5.Font/custom/sketch.js b/test/manual-test-examples/p5.Font/custom/sketch.js index fbd75b3939..b7243e0d64 100755 --- a/test/manual-test-examples/p5.Font/custom/sketch.js +++ b/test/manual-test-examples/p5.Font/custom/sketch.js @@ -1,22 +1,22 @@ var textSketch = function(p) { var font, font2; p.preload = function() { - font = p.loadFont('../acmesa.ttf'); + //font = p.loadFont('../acmesa.ttf'); font2 = p.loadFont('../SourceSansPro-Regular.otf'); }; p.setup = function() { p.createCanvas(240, 160); //p.ellipse(20,20,50,70); - p.textFont(font); + //p.textFont(font); + //p.text('Default Text', 10, 30); p.textSize(18); - p.text('Default Text', 10, 30); p.textFont(font2); p.noStroke(); p.fill(0, 102, 153); p.text('Blue No Stroke Text', 10, 60); - p.stroke(0, 200, 0); - p.strokeWeight(0.5); - p.text('Blue with Green Stroked Text', 10, 90); + //p.stroke(0, 200, 0); + //p.strokeWeight(0.5); + //p.text('Blue with Green Stroked Text', 10, 90); p.noStroke(); p.textSize(12); p.fill(120); @@ -469,7 +469,7 @@ var textAlignmentSketch = function(p) { var textString = 'Hello p5'; var padding = 10; p.preload = function() { - font1 = p.loadFont('../SourceSansPro-Regular.otf'); + font1 = p.loadFont('../SourceSansPro-Regular.otf'); // different font2 = p.loadFont('../FiraSans-Book.otf'); font3 = p.loadFont('../Inconsolata-Bold.ttf'); font4 = p.loadFont('../PlayfairDisplay-Regular.ttf'); @@ -523,7 +523,7 @@ var textVertAlignmentSketch = function(p) { 'Merriweather-LightItalic.ttf', 'Montserrat-Regular.ttf', 'OpenSans-Regular.ttf', - 'SourceSansPro-Regular.otf' + 'SourceSansPro-Regular.otf' // different ]; var fonts = []; var vAligns = [p.TOP, p.CENTER, p.BASELINE, p.BOTTOM]; diff --git a/test/manual-test-examples/p5.Font/system/sketch.js b/test/manual-test-examples/p5.Font/system/sketch.js index 91a5ba4681..726abfc2bf 100755 --- a/test/manual-test-examples/p5.Font/system/sketch.js +++ b/test/manual-test-examples/p5.Font/system/sketch.js @@ -1,23 +1,25 @@ var textSketch = function(p) { p.setup = function() { p.createCanvas(240, 160); - p.textSize(18); + p.background(204); + p.textSize(19.5); + p.fill(255); p.text('Default Text', 10, 30); p.noStroke(); - p.fill(0, 102, 153); - p.text('Blue No Stroke Text', 10, 60); - p.textStyle(p.ITALIC); - p.text('Blue No Stroke Text Italic', 10, 80); - p.textStyle(p.BOLD); - p.text('Blue No Stroke Text Bold', 10, 100); + p.fill(57, 112, 155); + p.text('Black No Stroke Text', 10, 60); + //p.textStyle(p.ITALIC); + //p.text('Blue No Stroke Text Italic', 10, 80); + //p.textStyle(p.BOLD); + //p.text('Blue No Stroke Text Bold', 10, 100); p .fill(120) .textStyle(p.NORMAL) - .textSize(12) + .textSize(13.5) .text( 'Simple long Text: Lorem Ipsum is simply dummy text of the printing and typesetting industry. ', 10, - 110, + 92, 220, 60 );