Skip to content

Commit c9cf088

Browse files
authored
Merge pull request #7724 from processing/variable-text-to-points
Support variable fonts in textToPoints + WebGL rendering
2 parents f2777ed + e3b92d3 commit c9cf088

File tree

12 files changed

+364
-205
lines changed

12 files changed

+364
-205
lines changed

Diff for: src/type/p5.Font.js

+46-11
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ const invalidFontError = 'Sorry, only TTF, OTF and WOFF files are supported.'; /
5151
const fontFaceVariations = ['weight', 'stretch', 'style'];
5252

5353

54-
55-
class Font {
54+
let nextId = 0;
55+
export class Font {
5656
constructor(p, fontFace, name, path, data) {
5757
if (!(fontFace instanceof FontFace)) {
5858
throw Error('FontFace is required');
@@ -62,6 +62,7 @@ class Font {
6262
this.path = path;
6363
this.data = data;
6464
this.face = fontFace;
65+
this.id = nextId++;
6566
}
6667

6768
/**
@@ -669,14 +670,44 @@ class Font {
669670
// convert lines to paths
670671
let uPE = this.data?.head?.unitsPerEm || 1000;
671672
let scale = renderer.states.textSize / uPE;
672-
let pathsForLine = lines.map(l => this._lineToGlyphs(l, scale));
673+
674+
const axs = this._currentAxes(renderer);
675+
let pathsForLine = lines.map(l => this._lineToGlyphs(l, { scale, axs }));
673676

674677
// restore the baseline
675678
renderer.drawingContext.textBaseline = setBaseline;
676679

677680
return pathsForLine;
678681
}
679682

683+
_currentAxes(renderer) {
684+
let axs;
685+
if ((this.data?.fvar?.length ?? 0) > 0) {
686+
const fontAxes = this.data.fvar[0];
687+
axs = fontAxes.map(([tag, minVal, maxVal, defaultVal, flags, name]) => {
688+
if (!renderer) return defaultVal;
689+
if (tag === 'wght') {
690+
return renderer.states.fontWeight;
691+
} else if (tag === 'wdth') {
692+
// TODO: map from keywords (normal, ultra-condensed, etc) to values
693+
// return renderer.states.fontStretch
694+
return defaultVal;
695+
} else if (renderer.textCanvas().style.fontVariationSettings) {
696+
const match = new RegExp(`\\b${tag}\s+(\d+)`)
697+
.exec(renderer.textCanvas().style.fontVariationSettings);
698+
if (match) {
699+
return parseInt(match[1]);
700+
} else {
701+
return defaultVal;
702+
}
703+
} else {
704+
return defaultVal;
705+
}
706+
});
707+
}
708+
return axs;
709+
}
710+
680711
_textToPathPoints(str, x, y, width, height, options) {
681712

682713
({ width, height, options } = this._parseArgs(width, height, options));
@@ -760,21 +791,24 @@ class Font {
760791
return lines.map(coordify);
761792
}
762793

763-
_lineToGlyphs(line, scale = 1) {
794+
_lineToGlyphs(line, { scale = 1, axs } = {}) {
764795

765796
if (!this.data) {
766797
throw Error('No font data available for "' + this.name
767798
+ '"\nTry downloading a local copy of the font file');
768799
}
769-
let glyphShapes = Typr.U.shape(this.data, line.text);
800+
let glyphShapes = Typr.U.shape(this.data, line.text, { axs });
770801
line.glyphShapes = glyphShapes;
771-
line.glyphs = this._shapeToPaths(glyphShapes, line, scale);
802+
803+
line.glyphs = this._shapeToPaths(glyphShapes, line, { scale, axs });
772804

773805
return line;
774806
}
775807

776-
_positionGlyphs(text) {
777-
const glyphShapes = Typr.U.shape(this.data, text);
808+
_positionGlyphs(text, options) {
809+
let renderer = options?.graphics?._renderer || this._pInst._renderer;
810+
const axs = this._currentAxes(renderer);
811+
const glyphShapes = Typr.U.shape(this.data, text, { axs });
778812
const positionedGlyphs = [];
779813
let x = 0;
780814
for (const glyph of glyphShapes) {
@@ -784,11 +818,11 @@ class Font {
784818
return positionedGlyphs;
785819
}
786820

787-
_singleShapeToPath(shape, { scale = 1, x = 0, y = 0, lineX = 0, lineY = 0 } = {}) {
821+
_singleShapeToPath(shape, { scale = 1, x = 0, y = 0, lineX = 0, lineY = 0, axs } = {}) {
788822
let font = this.data;
789823
let crdIdx = 0;
790824
let { g, ax, ay, dx, dy } = shape;
791-
let { crds, cmds } = Typr.U.glyphToPath(font, g);
825+
let { crds, cmds } = Typr.U.glyphToPath(font, g, true, axs);
792826

793827
// can get simple points for each glyph here, but we don't need them ?
794828
let glyph = { /*g: line.text[i], points: [],*/ path: { commands: [] } };
@@ -816,7 +850,7 @@ class Font {
816850
return { glyph, ax, ay };
817851
}
818852

819-
_shapeToPaths(glyphs, line, scale = 1) {
853+
_shapeToPaths(glyphs, line, { scale = 1, axs } = {}) {
820854
let x = 0, y = 0, paths = [];
821855

822856
if (glyphs.length !== line.text.length) {
@@ -832,6 +866,7 @@ class Font {
832866
y,
833867
lineX: line.x,
834868
lineY: line.y,
869+
axs,
835870
});
836871

837872
paths.push(glyph);

Diff for: src/type/textCore.js

+27-9
Original file line numberDiff line numberDiff line change
@@ -1749,7 +1749,7 @@ function textCore(p5, fn) {
17491749
modified = true;
17501750
}
17511751
// does it exist in the canvas.style ?
1752-
else if (prop in this.canvas.style) {
1752+
else if (prop in this.textCanvas().style) {
17531753
this._setCanvasStyleProperty(prop, value, debug);
17541754
modified = true;
17551755
}
@@ -1913,16 +1913,16 @@ function textCore(p5, fn) {
19131913
}
19141914

19151915
// lets try to set it on the canvas style
1916-
this.canvas.style[opt] = value;
1916+
this.textCanvas().style[opt] = value;
19171917

19181918
// check if the value was set successfully
1919-
if (this.canvas.style[opt] !== value) {
1919+
if (this.textCanvas().style[opt] !== value) {
19201920

19211921
// fails on precision for floating points, also quotes and spaces
19221922

19231923
if (0) console.warn(`Unable to set '${opt}' property` // FES?
19241924
+ ' on canvas.style. It may not be supported. Expected "'
1925-
+ value + '" but got: "' + this.canvas.style[opt] + "'");
1925+
+ value + '" but got: "' + this.textCanvas().style[opt] + "'");
19261926
}
19271927
};
19281928

@@ -2075,7 +2075,7 @@ function textCore(p5, fn) {
20752075
Object.entries(props).forEach(([prop, val]) => {
20762076
ele.style[prop] = val;
20772077
});
2078-
this.canvas.appendChild(ele);
2078+
this.textCanvas().appendChild(ele);
20792079
cachedDiv = ele;
20802080
}
20812081
return cachedDiv;
@@ -2435,7 +2435,9 @@ function textCore(p5, fn) {
24352435
};
24362436

24372437
if (p5.Renderer2D) {
2438-
2438+
p5.Renderer2D.prototype.textCanvas = function () {
2439+
return this.canvas;
2440+
};
24392441
p5.Renderer2D.prototype.textDrawingContext = function () {
24402442
return this.drawingContext;
24412443
};
@@ -2535,15 +2537,31 @@ function textCore(p5, fn) {
25352537
}
25362538

25372539
if (p5.RendererGL) {
2538-
p5.RendererGL.prototype.textDrawingContext = function () {
2539-
if (!this._textDrawingContext) {
2540+
p5.RendererGL.prototype.textCanvas = function() {
2541+
if (!this._textCanvas) {
25402542
this._textCanvas = document.createElement('canvas');
25412543
this._textCanvas.width = 1;
25422544
this._textCanvas.height = 1;
2543-
this._textDrawingContext = this._textCanvas.getContext('2d');
2545+
this._textCanvas.style.display = 'none';
2546+
// Has to be added to the DOM for measureText to work properly!
2547+
this.canvas.parentElement.insertBefore(this._textCanvas, this.canvas);
2548+
}
2549+
return this._textCanvas;
2550+
};
2551+
p5.RendererGL.prototype.textDrawingContext = function() {
2552+
if (!this._textDrawingContext) {
2553+
const textCanvas = this.textCanvas();
2554+
this._textDrawingContext = textCanvas.getContext('2d');
25442555
}
25452556
return this._textDrawingContext;
25462557
};
2558+
const oldRemove = p5.RendererGL.prototype.remove;
2559+
p5.RendererGL.prototype.remove = function() {
2560+
if (this._textCanvas) {
2561+
this._textCanvas.parentElement.removeChild(this._textCanvas);
2562+
}
2563+
oldRemove.call(this);
2564+
};
25472565

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

Diff for: src/webgl/p5.RendererGL.js

+7
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,13 @@ class RendererGL extends Renderer {
457457
};
458458
}
459459

460+
remove() {
461+
this.wrappedElt.remove();
462+
this.wrappedElt = null;
463+
this.canvas = null;
464+
this.elt = null;
465+
}
466+
460467
//////////////////////////////////////////////
461468
// Geometry Building
462469
//////////////////////////////////////////////

Diff for: src/webgl/p5.Texture.js

+8
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,14 @@ class Texture {
8686
return this;
8787
}
8888

89+
remove() {
90+
if (this.glTex) {
91+
const gl = this._renderer.GL;
92+
gl.deleteTexture(this.glTex);
93+
this.glTex = undefined;
94+
}
95+
}
96+
8997
_getTextureDataFromSource () {
9098
let textureData;
9199
if (this.isFramebufferTexture) {

0 commit comments

Comments
 (0)