Skip to content

Commit

Permalink
Fix some alignment issues
Browse files Browse the repository at this point in the history
  • Loading branch information
davepagurek committed Dec 8, 2024
1 parent f8ed1fe commit d918802
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 70 deletions.
9 changes: 5 additions & 4 deletions preview/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

p.setup = async function () {
// TODO: make this work without a name
f = await p.loadFont('font/BricolageGrotesque-Variable.ttf', 'Bricolage')
f = await p.loadFont('font/Lato-Black.ttf', 'Lato')
p.createCanvas(200, 200, testWebgl ? p.WEBGL : undefined);
};

Expand All @@ -34,15 +34,16 @@
if (testWebgl) p.translate(-p.width/2, -p.height/2);

p.fill('white');
p.textSize(80);
p.textAlign(p.LEFT, p.TOP)
p.textSize(60);
p.textAlign(p.RIGHT, p.CENTER)
p.textFont(f)
p.text('hello, world!', 10, 30, p.width);
p.text('hello, world!', 0, p.height/2, p.width);
};
};

new p5(sketch);
</script>
<p style="font-family: Lato">hello, world!</p>
</body>

</html>
10 changes: 10 additions & 0 deletions src/type/p5.Font.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@ function font(p5, fn) {
this.face = fontFace;
}

verticalAlign(size) {
const { sCapHeight } = this.data?.['OS/2'] || {};
const { unitsPerEm = 1000 } = this.data?.head || {};
const { ascender = 0, descender = 0 } = this.data?.hhea || {};
const current = ascender / 2;
const target = (sCapHeight || (ascender + descender)) / 2;
const offset = target - current;
return offset * size / unitsPerEm;
}

variations() {
let vars = {};
if (this.data) {
Expand Down
192 changes: 126 additions & 66 deletions src/type/text2d.js
Original file line number Diff line number Diff line change
Expand Up @@ -762,38 +762,6 @@ function text2d(p5, fn) {
// return { x, y, w, h };
// };

/*
Position the lines of text based on their textAlign/textBaseline properties
*/
Renderer.prototype._positionLines = function (x, y, width, height, lines) {

let { textLeading, textAlign } = this.states;
let adjustedX, lineData = new Array(lines.length);
let adjustedW = typeof width === 'undefined' ? 0 : width;
let adjustedH = typeof height === 'undefined' ? 0 : height;

for (let i = 0; i < lines.length; i++) {
switch (textAlign) {
case fn.START:
throw new Error('textBounds: START not yet supported for textAlign'); // default to LEFT
case fn.LEFT:
adjustedX = x;
break;
case fn.CENTER:
adjustedX = x + adjustedW / 2;
break;
case fn.RIGHT:
adjustedX = x + adjustedW;
break;
case fn.END: // TODO: add fn.END:
throw new Error('textBounds: END not yet supported for textAlign');
}
lineData[i] = { text: lines[i], x: adjustedX, y: y + i * textLeading };
}

return this._yAlignOffset(lineData, adjustedH);
};

/*
Process the text string to handle line-breaks and text wrapping
@param {string} str - the text to process
Expand Down Expand Up @@ -860,40 +828,6 @@ function text2d(p5, fn) {
}
}

/*
Get the y-offset for text given the height, leading, line-count and textBaseline property
*/
Renderer.prototype._yAlignOffset = function (dataArr, height) {

if (typeof height === 'undefined') {
throw Error('_yAlignOffset: height is required');
}

let { textLeading, textBaseline } = this.states;
let yOff = 0, numLines = dataArr.length;
let ydiff = height - (textLeading * (numLines - 1));
switch (textBaseline) { // drawingContext ?
case fn.TOP:
break; // ??
case fn.BASELINE:
break;
case fn._CTX_MIDDLE:
yOff = ydiff / 2;
break;
case fn.BOTTOM:
yOff = ydiff;
break;
case fn.IDEOGRAPHIC:
console.warn('textBounds: IDEOGRAPHIC not yet supported for textBaseline'); // FES?
break;
case fn.HANGING:
console.warn('textBounds: HANGING not yet supported for textBaseline'); // FES?
break;
}
dataArr.forEach(ele => ele.y += yOff);
return dataArr;
}

/*
Align the bounding box based on the current rectMode setting
*/
Expand Down Expand Up @@ -1179,6 +1113,72 @@ function text2d(p5, fn) {

this.pop();
};

/*
Position the lines of text based on their textAlign/textBaseline properties
*/
p5.Renderer2D.prototype._positionLines = function (x, y, width, height, lines) {

let { textLeading, textAlign } = this.states;
let adjustedX, lineData = new Array(lines.length);
let adjustedW = typeof width === 'undefined' ? 0 : width;
let adjustedH = typeof height === 'undefined' ? 0 : height;

for (let i = 0; i < lines.length; i++) {
switch (textAlign) {
case fn.START:
throw new Error('textBounds: START not yet supported for textAlign'); // default to LEFT
case fn.LEFT:
adjustedX = x;
break;
case fn.CENTER:
adjustedX = x + adjustedW / 2;
break;
case fn.RIGHT:
adjustedX = x + adjustedW;
break;
case fn.END: // TODO: add fn.END:
throw new Error('textBounds: END not yet supported for textAlign');
}
lineData[i] = { text: lines[i], x: adjustedX, y: y + i * textLeading };
}

return this._yAlignOffset(lineData, adjustedH);
};

/*
Get the y-offset for text given the height, leading, line-count and textBaseline property
*/
p5.Renderer2D.prototype._yAlignOffset = function (dataArr, height) {

if (typeof height === 'undefined') {
throw Error('_yAlignOffset: height is required');
}

let { textLeading, textBaseline } = this.states;
let yOff = 0, numLines = dataArr.length;
let ydiff = height - (textLeading * (numLines - 1));
switch (textBaseline) { // drawingContext ?
case fn.TOP:
break; // ??
case fn.BASELINE:
break;
case fn._CTX_MIDDLE:
yOff = ydiff / 2;
break;
case fn.BOTTOM:
yOff = ydiff;
break;
case fn.IDEOGRAPHIC:
console.warn('textBounds: IDEOGRAPHIC not yet supported for textBaseline'); // FES?
break;
case fn.HANGING:
console.warn('textBounds: HANGING not yet supported for textBaseline'); // FES?
break;
}
dataArr.forEach(ele => ele.y += yOff);
return dataArr;
}
}
if (p5.RendererGL) {
p5.RendererGL.prototype.textDrawingContext = function() {
Expand All @@ -1190,6 +1190,66 @@ function text2d(p5, fn) {
}
return this._textDrawingContext;
};

p5.RendererGL.prototype._positionLines = function (x, y, width, height, lines) {

let { textLeading, textAlign } = this.states;
const widths = lines.map((line) => this._fontWidthSingle(line));
let adjustedX, lineData = new Array(lines.length);
let adjustedW = typeof width === 'undefined' ? Math.max(0, ...widths) : width;
let adjustedH = typeof height === 'undefined' ? 0 : height;

for (let i = 0; i < lines.length; i++) {
switch (textAlign) {
case fn.START:
throw new Error('textBounds: START not yet supported for textAlign'); // default to LEFT
case fn.LEFT:
adjustedX = x;
break;
case fn._CTX_MIDDLE:
adjustedX = x + (adjustedW - widths[i]) / 2;
break;
case fn.RIGHT:
adjustedX = x + adjustedW - widths[i];
break;
case fn.END: // TODO: add fn.END:
throw new Error('textBounds: END not yet supported for textAlign');
}
lineData[i] = { text: lines[i], x: adjustedX, y: y + i * textLeading };
}

return this._yAlignOffset(lineData, adjustedH);
};

p5.RendererGL.prototype._yAlignOffset = function (dataArr, height) {

if (typeof height === 'undefined') {
throw Error('_yAlignOffset: height is required');
}

let { textLeading, textBaseline, textSize, textFont } = this.states;
let yOff = 0, numLines = dataArr.length;
let totalHeight = textSize * numLines + ((textLeading - textSize) * (numLines - 1));
switch (textBaseline) { // drawingContext ?
case fn.TOP:
yOff = textSize;
break;
case fn.BASELINE:
break;
case fn._CTX_MIDDLE:
yOff = -totalHeight / 2 + textSize;
break;
case fn.BOTTOM:
yOff = -(totalHeight - textSize);
break;
default:
console.warn(`${textBaseline} is not supported in WebGL mode.`); // FES?
break;
}
yOff += this.states.textFont.font?.verticalAlign(textSize) || 0;
dataArr.forEach(ele => ele.y += yOff);
return dataArr;
}
}
}

Expand Down

0 comments on commit d918802

Please sign in to comment.