Skip to content

Commit

Permalink
Merge pull request #717 from jonobr1/issue-716-baseline
Browse files Browse the repository at this point in the history
Improve Text.baseline consistency across all renderers
  • Loading branch information
jonobr1 authored Feb 20, 2024
2 parents 9b471ce + 32823eb commit ce08949
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 43 deletions.
39 changes: 28 additions & 11 deletions build/two.js
Original file line number Diff line number Diff line change
Expand Up @@ -737,8 +737,8 @@ var Two = (() => {
svg: "SVGRenderer",
canvas: "CanvasRenderer"
},
Version: "v0.8.12",
PublishDate: "2023-10-16T17:55:26.551Z",
Version: "v0.8.13",
PublishDate: "2024-02-20T20:04:05.976Z",
Identifier: "two-",
Resolution: 12,
AutoCalculateImportedMatrices: true,
Expand Down Expand Up @@ -2341,6 +2341,12 @@ var Two = (() => {
middle: "center",
right: "end"
},
baselines: {
top: "top",
middle: "middle",
bottom: "bottom",
baseline: "alphabetic"
},
shim: function(elem, name) {
elem.tagName = elem.nodeName = name || "canvas";
elem.nodeType = 1;
Expand Down Expand Up @@ -2725,7 +2731,7 @@ var Two = (() => {
const isOffset = fill._renderer && fill._renderer.offset && stroke._renderer && stroke._renderer.offset;
const dashes = this.dashes;
const alignment = canvas.alignments[this._alignment] || this._alignment;
const baseline = this._baseline;
const baseline = canvas.baselines[this._baseline] || this._baseline;
let a, b, c, d, e, sx, sy, x1, y1, x2, y2;
if (!defaultMatrix) {
ctx.save();
Expand Down Expand Up @@ -2828,8 +2834,8 @@ var Two = (() => {
let scalar = 1;
switch (decoration) {
case "underline":
y1 = metrics.actualBoundingBoxAscent;
y2 = metrics.actualBoundingBoxAscent;
y1 = metrics.actualBoundingBoxDescent;
y2 = metrics.actualBoundingBoxDescent;
break;
case "strikethrough":
y1 = 0;
Expand Down Expand Up @@ -8142,6 +8148,12 @@ var Two = (() => {
center: "middle",
right: "end"
},
baselines: {
top: "hanging",
middle: "middle",
bottom: "ideographic",
baseline: "alphabetic"
},
createElement: function(name, attrs) {
const tag = name;
const elem = document.createElementNS(svg.ns, tag);
Expand Down Expand Up @@ -8584,7 +8596,7 @@ var Two = (() => {
changed["text-anchor"] = svg.alignments[this._alignment] || this._alignment;
}
if (this._flagBaseline) {
changed["alignment-baseline"] = changed["dominant-baseline"] = this._baseline;
changed["dominant-baseline"] = svg.baselines[this._baseline] || this._baseline;
}
if (this._flagStyle) {
changed["font-style"] = this._style;
Expand Down Expand Up @@ -9731,8 +9743,8 @@ var Two = (() => {
const metrics = ctx.measureText(elem.value);
switch (decoration) {
case "underline":
y1 = metrics.actualBoundingBoxAscent;
y2 = metrics.actualBoundingBoxAscent;
y1 = metrics.actualBoundingBoxDescent;
y2 = metrics.actualBoundingBoxDescent;
break;
case "strikethrough":
y1 = 0;
Expand All @@ -9754,9 +9766,10 @@ var Two = (() => {
const ctx = webgl.ctx;
ctx.font = [elem._style, elem._weight, elem._size + "px/" + elem._leading + "px", elem._family].join(" ");
ctx.textAlign = "center";
ctx.textBaseline = elem._baseline;
let width = ctx.measureText(elem._value).width * 1.25;
let height = Math.max(elem._size, elem._leading) * 1.25;
ctx.textBaseline = Renderer.Utils.baselines[elem._baseline] || elem._baseline;
const metrics = ctx.measureText(elem._value);
let width = metrics.width;
let height = 1.15 * (metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent);
if (this._linewidth && !webgl.isHidden.test(this._stroke)) {
width += this._linewidth * 2;
height += this._linewidth * 2;
Expand Down Expand Up @@ -9785,6 +9798,10 @@ var Two = (() => {
rect.top = 0;
rect.bottom = height;
break;
case "baseline":
rect.top = -h * 1.5;
rect.bottom = h * 0.5;
break;
default:
rect.top = -h;
rect.bottom = h;
Expand Down
4 changes: 2 additions & 2 deletions build/two.min.js

Large diffs are not rendered by default.

39 changes: 28 additions & 11 deletions build/two.module.js
Original file line number Diff line number Diff line change
Expand Up @@ -718,8 +718,8 @@ var Constants = {
svg: "SVGRenderer",
canvas: "CanvasRenderer"
},
Version: "v0.8.12",
PublishDate: "2023-10-16T17:55:26.551Z",
Version: "v0.8.13",
PublishDate: "2024-02-20T20:04:05.976Z",
Identifier: "two-",
Resolution: 12,
AutoCalculateImportedMatrices: true,
Expand Down Expand Up @@ -2322,6 +2322,12 @@ var canvas = {
middle: "center",
right: "end"
},
baselines: {
top: "top",
middle: "middle",
bottom: "bottom",
baseline: "alphabetic"
},
shim: function(elem, name) {
elem.tagName = elem.nodeName = name || "canvas";
elem.nodeType = 1;
Expand Down Expand Up @@ -2706,7 +2712,7 @@ var canvas = {
const isOffset = fill._renderer && fill._renderer.offset && stroke._renderer && stroke._renderer.offset;
const dashes = this.dashes;
const alignment = canvas.alignments[this._alignment] || this._alignment;
const baseline = this._baseline;
const baseline = canvas.baselines[this._baseline] || this._baseline;
let a, b, c, d, e, sx, sy, x1, y1, x2, y2;
if (!defaultMatrix) {
ctx.save();
Expand Down Expand Up @@ -2809,8 +2815,8 @@ var canvas = {
let scalar = 1;
switch (decoration) {
case "underline":
y1 = metrics.actualBoundingBoxAscent;
y2 = metrics.actualBoundingBoxAscent;
y1 = metrics.actualBoundingBoxDescent;
y2 = metrics.actualBoundingBoxDescent;
break;
case "strikethrough":
y1 = 0;
Expand Down Expand Up @@ -8123,6 +8129,12 @@ var svg = {
center: "middle",
right: "end"
},
baselines: {
top: "hanging",
middle: "middle",
bottom: "ideographic",
baseline: "alphabetic"
},
createElement: function(name, attrs) {
const tag = name;
const elem = document.createElementNS(svg.ns, tag);
Expand Down Expand Up @@ -8565,7 +8577,7 @@ var svg = {
changed["text-anchor"] = svg.alignments[this._alignment] || this._alignment;
}
if (this._flagBaseline) {
changed["alignment-baseline"] = changed["dominant-baseline"] = this._baseline;
changed["dominant-baseline"] = svg.baselines[this._baseline] || this._baseline;
}
if (this._flagStyle) {
changed["font-style"] = this._style;
Expand Down Expand Up @@ -9712,8 +9724,8 @@ var webgl = {
const metrics = ctx.measureText(elem.value);
switch (decoration) {
case "underline":
y1 = metrics.actualBoundingBoxAscent;
y2 = metrics.actualBoundingBoxAscent;
y1 = metrics.actualBoundingBoxDescent;
y2 = metrics.actualBoundingBoxDescent;
break;
case "strikethrough":
y1 = 0;
Expand All @@ -9735,9 +9747,10 @@ var webgl = {
const ctx = webgl.ctx;
ctx.font = [elem._style, elem._weight, elem._size + "px/" + elem._leading + "px", elem._family].join(" ");
ctx.textAlign = "center";
ctx.textBaseline = elem._baseline;
let width = ctx.measureText(elem._value).width * 1.25;
let height = Math.max(elem._size, elem._leading) * 1.25;
ctx.textBaseline = Renderer.Utils.baselines[elem._baseline] || elem._baseline;
const metrics = ctx.measureText(elem._value);
let width = metrics.width;
let height = 1.15 * (metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent);
if (this._linewidth && !webgl.isHidden.test(this._stroke)) {
width += this._linewidth * 2;
height += this._linewidth * 2;
Expand Down Expand Up @@ -9766,6 +9779,10 @@ var webgl = {
rect.top = 0;
rect.bottom = height;
break;
case "baseline":
rect.top = -h * 1.5;
rect.bottom = h * 0.5;
break;
default:
rect.top = -h;
rect.bottom = h;
Expand Down
13 changes: 10 additions & 3 deletions src/renderers/canvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ const canvas = {
right: 'end'
},

baselines: {
top: 'top',
middle: 'middle',
bottom: 'bottom',
baseline: 'alphabetic'
},

shim: function(elem, name) {
elem.tagName = elem.nodeName = name || 'canvas';
elem.nodeType = 1;
Expand Down Expand Up @@ -515,7 +522,7 @@ const canvas = {
&& stroke._renderer && stroke._renderer.offset;
const dashes = this.dashes;
const alignment = canvas.alignments[this._alignment] || this._alignment;
const baseline = this._baseline;
const baseline = canvas.baselines[this._baseline] || this._baseline;

let a, b, c, d, e, sx, sy, x1, y1, x2, y2;

Expand Down Expand Up @@ -638,8 +645,8 @@ const canvas = {

switch (decoration) {
case 'underline':
y1 = metrics.actualBoundingBoxAscent;
y2 = metrics.actualBoundingBoxAscent;
y1 = metrics.actualBoundingBoxDescent;
y2 = metrics.actualBoundingBoxDescent;
break;
case 'strikethrough':
y1 = 0;
Expand Down
9 changes: 8 additions & 1 deletion src/renderers/svg.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ const svg = {
right: 'end'
},

baselines: {
top: 'hanging',
middle: 'middle',
bottom: 'ideographic',
baseline: 'alphabetic'
},

// Create an svg namespaced element.
createElement: function(name, attrs) {
const tag = name;
Expand Down Expand Up @@ -684,7 +691,7 @@ const svg = {
changed['text-anchor'] = svg.alignments[this._alignment] || this._alignment;
}
if (this._flagBaseline) {
changed['alignment-baseline'] = changed['dominant-baseline'] = this._baseline;
changed['dominant-baseline'] = svg.baselines[this._baseline] || this._baseline;
}
if (this._flagStyle) {
changed['font-style'] = this._style;
Expand Down
18 changes: 12 additions & 6 deletions src/renderers/webgl.js
Original file line number Diff line number Diff line change
Expand Up @@ -999,8 +999,8 @@ const webgl = {

switch (decoration) {
case 'underline':
y1 = metrics.actualBoundingBoxAscent;
y2 = metrics.actualBoundingBoxAscent;
y1 = metrics.actualBoundingBoxDescent;
y2 = metrics.actualBoundingBoxDescent;
break;
case 'strikethrough':
y1 = 0;
Expand Down Expand Up @@ -1033,11 +1033,13 @@ const webgl = {
elem._leading + 'px', elem._family].join(' ');

ctx.textAlign = 'center';
ctx.textBaseline = elem._baseline;
ctx.textBaseline = CanvasRenderer.Utils.baselines[elem._baseline] || elem._baseline;

// TODO: Estimate this better
let width = ctx.measureText(elem._value).width * 1.25;
let height = Math.max(elem._size, elem._leading) * 1.25;
const metrics = ctx.measureText(elem._value);
let width = metrics.width;
// TODO: Why does the height need to be scaled by 15%
// in order to not cut off / mask the bitmap data.
let height = 1.15 * (metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent);

if (this._linewidth && !webgl.isHidden.test(this._stroke)) {
width += this._linewidth * 2;
Expand Down Expand Up @@ -1072,6 +1074,10 @@ const webgl = {
rect.top = 0;
rect.bottom = height;
break;
case 'baseline':
rect.top = - h * 1.5; // TODO: Improve calculation based on text metrics
rect.bottom = h * 0.5;
break;
default:
rect.top = - h;
rect.bottom = h;
Expand Down
3 changes: 1 addition & 2 deletions tests/suite/svg.js
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@

QUnit.test('Two.makeText', function(assert) {

assert.expect(17);
assert.expect(16);

var two = new Two({
width: 400,
Expand All @@ -430,7 +430,6 @@
assert.equal(elem.getAttribute('line-height'), '17', 'The line-height attribute applied properly');
assert.equal(elem.getAttribute('text-anchor'), 'middle', 'The text-anchor attribute applied properly.');
assert.equal(elem.getAttribute('dominant-baseline'), 'middle', 'The dominant-baseline attribute applied properly.');
assert.equal(elem.getAttribute('alignment-baseline'), 'middle', 'The alignment-baseline attribute applied properly.');
assert.equal(elem.getAttribute('font-style'), 'normal', 'The font-style attribute applied properly.');
assert.equal(elem.getAttribute('font-weight'), '500', 'The font-weight attribute applied properly.');
assert.equal(elem.getAttribute('text-decoration'), 'none', 'The text-decoration attribute applied properly.');
Expand Down
16 changes: 9 additions & 7 deletions wiki/changelog/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,20 @@ All notable changes to this project will be documented in this file. The format

## Nightly

+ Improved consistency of `Two.Text.baseline` rendering across all renderers

## October 16, 2023 v0.8.12

<h3 class="visible">October 16, 2023</h3><version-link v="v0.8.12" />

+ Added "no-referrer" policy to image requests
+ Added `"no-referrer"` policy to image requests
+ Updated extras to be compatible with `Two.Matrix` API changes
+ Two.Path.noFill() yields `"none"` instead of `"transparent"`
+ Two.Path.noStroke() yields `"none"` instead of `"transparent"`
+ Two.Text.noFill() yields `"none"` instead of `"transparent"`
+ Two.Text.noStroke() yields `"none"` instead of `"transparent"`
+ Two.Points.noFill() yields `"none"` instead of `"transparent"`
+ Two.Points.noStroke() yields `"none"` instead of `"transparent"`
+ `Two.Path.noFill()` yields `"none"` instead of `"transparent"`
+ `Two.Path.noStroke()` yields `"none"` instead of `"transparent"`
+ `Two.Text.noFill()` yields `"none"` instead of `"transparent"`
+ `Two.Text.noStroke()` yields `"none"` instead of `"transparent"`
+ `Two.Points.noFill()` yields `"none"` instead of `"transparent"`
+ `Two.Points.noStroke()` yields `"none"` instead of `"transparent"`

## August 7, 2023 v0.8.11

Expand Down

0 comments on commit ce08949

Please sign in to comment.