Skip to content

Commit 8d5498d

Browse files
authored
Merge pull request #7417 from processing/feat/webgl-fonts-2.0
Refactor 2.0 Typography code to work with WebGL
2 parents 584504d + d918802 commit 8d5498d

File tree

8 files changed

+436
-293
lines changed

8 files changed

+436
-293
lines changed

preview/index.html

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,30 +20,30 @@
2020
import p5 from '../src/app.js';
2121

2222
const sketch = function (p) {
23-
let g, f;
23+
let f;
24+
const testWebgl = true
2425

25-
p.setup = function () {
26-
p.createCanvas(200, 200);
27-
g = p.createGraphics(200, 200);
28-
f = p.createGraphics(200, 200, p.WEBGL);
26+
p.setup = async function () {
27+
// TODO: make this work without a name
28+
f = await p.loadFont('font/Lato-Black.ttf', 'Lato')
29+
p.createCanvas(200, 200, testWebgl ? p.WEBGL : undefined);
2930
};
3031

3132
p.draw = function () {
3233
p.background(0, 50, 50);
33-
p.circle(100, 100, 50);
34+
if (testWebgl) p.translate(-p.width/2, -p.height/2);
3435

3536
p.fill('white');
36-
p.textSize(30);
37-
p.text('hello', 10, 30);
38-
39-
// f.fill('red');
40-
f.sphere();
41-
p.image(f, 0, 0);
37+
p.textSize(60);
38+
p.textAlign(p.RIGHT, p.CENTER)
39+
p.textFont(f)
40+
p.text('hello, world!', 0, p.height/2, p.width);
4241
};
4342
};
4443

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

4949
</html>

src/app.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,6 @@ io(p5);
4949
import math from './math';
5050
math(p5);
5151

52-
// typography
53-
import type from './type'
54-
type(p5);
55-
5652
// utilities
5753
import utilities from './utilities';
5854
utilities(p5);
@@ -61,6 +57,10 @@ utilities(p5);
6157
import webgl from './webgl';
6258
webgl(p5);
6359

60+
// typography
61+
import type from './type'
62+
type(p5);
63+
6464
import './core/init';
6565

6666
export default p5;

src/core/p5.Renderer.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class Renderer {
3434
rectMode: constants.CORNER,
3535
ellipseMode: constants.CENTER,
3636

37-
textFont: 'sans-serif',
37+
textFont: { family: 'sans-serif' },
3838
textLeading: 15,
3939
leadingSet: false,
4040
textSize: 12,
@@ -485,8 +485,8 @@ class Renderer {
485485
/**
486486
* Helper function to check font type (system or otf)
487487
*/
488-
_isOpenType(f = this.states.textFont) {
489-
return typeof f === 'object' && f.font && f.font.supported;
488+
_isOpenType({ font: f } = this.states.textFont) {
489+
return typeof f === 'object' && f.data;
490490
}
491491

492492
_updateTextMetrics() {

src/core/p5.Renderer2D.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1405,7 +1405,7 @@ class Renderer2D extends Renderer {
14051405
return p;
14061406
}
14071407

1408-
_applyTextProperties() {
1408+
/*_applyTextProperties() {
14091409
let font;
14101410
const p = this._pInst;
14111411
@@ -1435,7 +1435,7 @@ class Renderer2D extends Renderer {
14351435
}
14361436
14371437
return p;
1438-
}
1438+
}*/
14391439

14401440
//////////////////////////////////////////////
14411441
// STRUCTURE

src/type/p5.Font.js

Lines changed: 74 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
1-
/**
1+
/**
22
* API:
33
* loadFont("https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,[email protected],200..800&display=swap")
44
* loadFont("{ font-family: "Bricolage Grotesque", serif; font-optical-sizing: auto; font-weight: <weight> font-style: normal; font-variation-settings: "wdth" 100; });
5-
* loadFont({
6-
* fontFamily: '"Bricolage Grotesque", serif';
5+
* loadFont({
6+
* fontFamily: '"Bricolage Grotesque", serif';
77
* fontOpticalSizing: 'auto';
88
* fontWeight: '<weight>';
99
* fontStyle: 'normal';
10-
* fontVariationSettings: '"wdth" 100';
10+
* fontVariationSettings: '"wdth" 100';
1111
* });
1212
* loadFont("https://fonts.gstatic.com/s/bricolagegrotesque/v1/pxiAZBhjZQIdd8jGnEotWQ.woff2");
1313
* loadFont("./path/to/localFont.ttf");
1414
* loadFont("system-font-name");
15-
*
16-
*
15+
*
16+
*
1717
* NEXT:
1818
* extract axes from font file
19-
*
20-
* TEST:
19+
*
20+
* TEST:
2121
* const font = new FontFace("Inter", "url(./fonts/inter-latin-variable-full-font.woff2)", {
2222
style: "oblique 0deg 10deg",
2323
weight: "100 900",
@@ -53,6 +53,16 @@ function font(p5, fn) {
5353
this.face = fontFace;
5454
}
5555

56+
verticalAlign(size) {
57+
const { sCapHeight } = this.data?.['OS/2'] || {};
58+
const { unitsPerEm = 1000 } = this.data?.head || {};
59+
const { ascender = 0, descender = 0 } = this.data?.hhea || {};
60+
const current = ascender / 2;
61+
const target = (sCapHeight || (ascender + descender)) / 2;
62+
const offset = target - current;
63+
return offset * size / unitsPerEm;
64+
}
65+
5666
variations() {
5767
let vars = {};
5868
if (this.data) {
@@ -254,20 +264,63 @@ function font(p5, fn) {
254264
return lines.map(coordify);
255265
}
256266

257-
_lineToGlyphs(line, scale) {
267+
_lineToGlyphs(line, scale = 1) {
258268

259269
if (!this.data) {
260270
throw Error('No font data available for "' + this.name
261271
+ '"\nTry downloading a local copy of the font file');
262272
}
263273
let glyphShapes = Typr.U.shape(this.data, line.text);
274+
line.glyphShapes = glyphShapes;
264275
line.glyphs = this._shapeToPaths(glyphShapes, line, scale);
265276

266277
return line;
267278
}
268279

269-
_shapeToPaths(glyphs, line, scale) {
280+
_positionGlyphs(text) {
281+
const glyphShapes = Typr.U.shape(this.data, text);
282+
const positionedGlyphs = [];
283+
let x = 0;
284+
for (const glyph of glyphShapes) {
285+
positionedGlyphs.push({ x, index: glyph.g, shape: glyph });
286+
x += glyph.ax;
287+
}
288+
return positionedGlyphs;
289+
}
290+
291+
_singleShapeToPath(shape, { scale = 1, x = 0, y = 0, lineX = 0, lineY = 0 } = {}) {
270292
let font = this.data;
293+
let crdIdx = 0;
294+
let { g, ax, ay, dx, dy } = shape;
295+
let { crds, cmds } = Typr.U.glyphToPath(font, g);
296+
297+
// can get simple points for each glyph here, but we don't need them ?
298+
let glyph = { /*g: line.text[i], points: [],*/ path: { commands: [] } };
299+
300+
for (let j = 0; j < cmds.length; j++) {
301+
let type = cmds[j], command = [type];
302+
if (type in pathArgCounts) {
303+
let argCount = pathArgCounts[type];
304+
for (let k = 0; k < argCount; k += 2) {
305+
let gx = crds[k + crdIdx] + x + dx;
306+
let gy = crds[k + crdIdx + 1] + y + dy;
307+
let fx = lineX + gx * scale;
308+
let fy = lineY + gy * -scale;
309+
command.push(fx);
310+
command.push(fy);
311+
/*if (k === argCount - 2) {
312+
glyph.points.push({ x: fx, y: fy });
313+
}*/
314+
}
315+
crdIdx += argCount;
316+
}
317+
glyph.path.commands.push(command);
318+
}
319+
320+
return { glyph, ax, ay };
321+
}
322+
323+
_shapeToPaths(glyphs, line, scale = 1) {
271324
let x = 0, y = 0, paths = [];
272325

273326
if (glyphs.length !== line.text.length) {
@@ -277,32 +330,14 @@ function font(p5, fn) {
277330
// iterate over the glyphs, converting each to a glyph object
278331
// with a path property containing an array of commands
279332
for (let i = 0; i < glyphs.length; i++) {
280-
let crdIdx = 0;
281-
let { g, ax, ay, dx, dy } = glyphs[i];
282-
let { crds, cmds } = Typr.U.glyphToPath(font, g);
283-
284-
// can get simple points for each glyph here, but we don't need them ?
285-
let glyph = { g: line.text[i], /*points: [],*/ path: { commands: [] } };
286-
287-
for (let j = 0; j < cmds.length; j++) {
288-
let type = cmds[j], command = [type];
289-
if (type in pathArgCounts) {
290-
let argCount = pathArgCounts[type];
291-
for (let k = 0; k < argCount; k += 2) {
292-
let gx = crds[k + crdIdx] + x + dx;
293-
let gy = crds[k + crdIdx + 1] + y + dy;
294-
let fx = line.x + gx * scale;
295-
let fy = line.y + gy * -scale;
296-
command.push(fx);
297-
command.push(fy);
298-
/*if (k === argCount - 2) {
299-
glyph.points.push({ x: fx, y: fy });
300-
}*/
301-
}
302-
crdIdx += argCount;
303-
}
304-
glyph.path.commands.push(command);
305-
}
333+
const { glyph, ax, ay } = this._singleShapeToPath(glyphs[i], {
334+
scale,
335+
x,
336+
y,
337+
lineX: line.x,
338+
lineY: line.y,
339+
});
340+
306341
paths.push(glyph);
307342
x += ax; y += ay;
308343
}
@@ -411,7 +446,7 @@ function font(p5, fn) {
411446

412447
/**
413448
* Load a font and returns a p5.Font instance. The font can be specified by its path or a url.
414-
* Optional arguments include the font name, descriptors for the FontFace object,
449+
* Optional arguments include the font name, descriptors for the FontFace object,
415450
* and callbacks for success and error.
416451
* @param {...any} args - path, name, onSuccess, onError, descriptors
417452
* @returns a Promise that resolves with a p5.Font instance
@@ -430,7 +465,7 @@ function font(p5, fn) {
430465

431466
// parse the font data
432467
let fonts = Typr.parse(result);
433-
468+
434469
if (fonts.length !== 1 || fonts[0].cmap === undefined) {
435470
throw Error(23);
436471
}
@@ -1059,4 +1094,4 @@ export default font;
10591094

10601095
if (typeof p5 !== 'undefined') {
10611096
font(p5, p5.prototype);
1062-
}
1097+
}

0 commit comments

Comments
 (0)