Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tests for font weight + fix Google Font loading #7426

Merged
merged 3 commits into from
Dec 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 54 additions & 2 deletions src/type/p5.Font.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* API:
* loadFont("https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,[email protected],200..800&display=swap")
* loadFont("{ font-family: "Bricolage Grotesque", serif; font-optical-sizing: auto; font-weight: <weight> font-style: normal; font-variation-settings: "wdth" 100; });
* loadFont("@font-face { font-family: "Bricolage Grotesque", serif; font-optical-sizing: auto; font-weight: <weight> font-style: normal; font-variation-settings: "wdth" 100; });
* loadFont({
* fontFamily: '"Bricolage Grotesque", serif';
* fontOpticalSizing: 'auto';
Expand Down Expand Up @@ -31,6 +31,14 @@
*/
import Typr from './lib/Typr.js';

function unquote(name) {
// Unquote name from CSS
if ((name.startsWith('"') || name.startsWith("'")) && name.at(0) === name.at(-1)) {
return name.slice(1, -1).replace(/\/(['"])/g, '$1');
}
return name;
}

function font(p5, fn) {

const pathArgCounts = { M: 2, L: 2, C: 6, Q: 4 };
Expand Down Expand Up @@ -455,6 +463,43 @@ function font(p5, fn) {

let { path, name, success, error, descriptors } = parseCreateArgs(...args);

let isCSS = path.includes('@font-face');

if (!isCSS) {
const info = await fetch(path, { method: 'HEAD' });
const isCSSFile = info.headers.get('content-type')?.startsWith('text/css');
if (isCSSFile) {
isCSS = true;
path = await fetch(path).then((res) => res.text());
}
}

if (isCSS) {
const stylesheet = new CSSStyleSheet();
await stylesheet.replace(path);
const fontPromises = [];
for (const rule of stylesheet.cssRules) {
if (rule instanceof CSSFontFaceRule) {
const style = rule.style;
let name = unquote(style.getPropertyValue('font-family'));
const src = style.getPropertyValue('src');
const fontDescriptors = { ...(descriptors || {}) };
for (const key of style) {
if (key === 'font-family' || key === 'src') continue;
const camelCaseKey = key
.replace(/^font-/, '')
.split('-')
.map((v, i) => i === 0 ? v : `${v[0].toUpperCase()}${v.slice(1)}`)
.join('');
fontDescriptors[camelCaseKey] = style.getPropertyValue(key);
}
fontPromises.push(create(this, name, src, fontDescriptors));
}
}
const fonts = await Promise.all(fontPromises);
return fonts[0]; // TODO: handle multiple faces?
}

let pfont;
try {
// load the raw font bytes
Expand All @@ -465,13 +510,20 @@ function font(p5, fn) {

// parse the font data
let fonts = Typr.parse(result);
console.log(fonts[0])
// TODO: generate descriptors from font in the future

if (fonts.length !== 1 || fonts[0].cmap === undefined) {
throw Error(23);
}

// make sure we have a valid name
name = name || extractFontName(fonts[0], path);
if (!name) {
name = extractFontName(fonts[0], path);
if (name.includes(' ')) {
name = name.replace(/ /g, '_');
}
}

// create a FontFace object and pass it to the p5.Font constructor
pfont = await create(this, name, path, descriptors, fonts[0]);
Expand Down
14 changes: 10 additions & 4 deletions src/type/text2d.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,10 @@ function text2d(p5, fn) {
};
};

Renderer.prototype._currentTextFont = function() {
return this.states.textFont.font || this.states.textFont.family;
}

/**
* Set the font and [size] and [options] for rendering text
* @param {p5.Font | string} font - the font to use for rendering text
Expand All @@ -269,7 +273,7 @@ function text2d(p5, fn) {
Renderer.prototype.textFont = function (font, size, options) {

if (arguments.length === 0) {
return this.states.textFont;
return this._currentTextFont();
}

let family = font;
Expand All @@ -296,7 +300,7 @@ function text2d(p5, fn) {
}

// check for font-string with size in first arg
if (typeof size === 'undefined' && /[.0-9]+(%|em|p[xt])/.test(family)) {
if (typeof size === 'undefined' && /^[.0-9]+(%|em|p[xt])/.test(family)) {
({ family, size } = this._directSetFontString(family));
}

Expand Down Expand Up @@ -351,7 +355,9 @@ function text2d(p5, fn) {
// the setter
if (typeof weight === 'number') {
this.states.fontWeight = weight;
return this._applyTextProperties();
this._applyTextProperties();
this._setCanvasStyleProperty('font-variation-settings', `"wght" ${weight}`);
return;
}
// the getter
return this.states.fontWeight;
Expand Down Expand Up @@ -605,7 +611,7 @@ function text2d(p5, fn) {
case 'wght':
if (debug) console.log('setting font-weight=' + val);
// manually set the font-weight via the font string
this.textWeight(val);
if (this.states.fontWeight !== val) this.textWeight(val);
return val;
case 'wdth':
if (0) { // attempt to map font-stretch to allowed keywords
Expand Down
7 changes: 5 additions & 2 deletions src/webgl/text.js
Original file line number Diff line number Diff line change
Expand Up @@ -733,8 +733,11 @@ function text(p5, fn){
sh.setUniform('uStrokeImageSize', [strokeImageWidth, strokeImageHeight]);
sh.setUniform('uGridSize', [charGridWidth, charGridHeight]);
}

const curFillColor = this.states.fillSet ? this.states.curFillColor : [0, 0, 0, 255];

this._setGlobalUniforms(sh);
this._applyColorBlend(this.states.curFillColor);
this._applyColorBlend(curFillColor);

let g = this.geometryBufferCache.getGeometryByID('glyph');
if (!g) {
Expand All @@ -759,7 +762,7 @@ function text(p5, fn){
this._bindBuffer(this.geometryBufferCache.cache.glyph.indexBuffer, gl.ELEMENT_ARRAY_BUFFER);

// this will have to do for now...
sh.setUniform('uMaterialColor', this.states.curFillColor);
sh.setUniform('uMaterialColor', curFillColor);
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);

try {
Expand Down
Binary file added test/unit/assets/BricolageGrotesque-Variable.ttf
Binary file not shown.
90 changes: 90 additions & 0 deletions test/unit/visual/cases/typography.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,96 @@ visualSuite("Typography", function () {
p5.text("test", 0, 0);
screenshot();
});

visualTest('with a Google Font URL', async function(p5, screenshot) {
p5.createCanvas(100, 100);
const font = await p5.loadFont(
'https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,[email protected],200..800&display=swap'
);
p5.textFont(font);
p5.textAlign(p5.LEFT, p5.TOP);
p5.textSize(35);
p5.text('p5*js', 0, 10, p5.width);
screenshot();
});

visualTest('with a font file', async function(p5, screenshot) {
p5.createCanvas(100, 100);
const font = await p5.loadFont(
'/unit/assets/Inconsolata-Bold.ttf'
);
p5.textFont(font);
p5.textAlign(p5.LEFT, p5.TOP);
p5.textSize(35);
p5.text('p5*js', 0, 10, p5.width);
screenshot();
});

visualTest('with a font file in WebGL', async function(p5, screenshot) {
p5.createCanvas(100, 100, p5.WEBGL);
const font = await p5.loadFont(
'/unit/assets/Inconsolata-Bold.ttf'
);
p5.textFont(font);
p5.textAlign(p5.LEFT, p5.TOP);
p5.textSize(35);
p5.text('p5*js', -p5.width/2, -p5.height/2 + 10, p5.width);
screenshot();
});
});

visualSuite('textWeight', function() {
visualTest('can control non-variable fonts', async function (p5, screenshot) {
p5.createCanvas(100, 100);
const font = await p5.loadFont(
'https://fonts.googleapis.com/css2?family=Sniglet:wght@400;800&display=swap'
);

for (const weight of [400, 800]) {
p5.background(255);
p5.textFont(font);
p5.textAlign(p5.LEFT, p5.TOP);
p5.textSize(35);
p5.textWeight(weight);
p5.text('p5*js', 0, 10, p5.width);
screenshot();
}
});

visualTest('can control variable fonts', async function (p5, screenshot) {
p5.createCanvas(100, 100);
const font = await p5.loadFont(
'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap'
);

for (let weight = 400; weight <= 800; weight += 100) {
p5.background(255);
p5.textFont(font);
p5.textAlign(p5.LEFT, p5.TOP);
p5.textSize(35);
p5.textWeight(weight);
p5.text('p5*js', 0, 10, p5.width);
screenshot();
}
});

visualTest('can control variable fonts from files', async function (p5, screenshot) {
p5.createCanvas(100, 100);
const font = await p5.loadFont(
'/unit/assets/BricolageGrotesque-Variable.ttf',
{ weight: '200 800' }
);

for (let weight = 400; weight <= 800; weight += 100) {
p5.background(255);
p5.textFont(font);
p5.textAlign(p5.LEFT, p5.TOP);
p5.textSize(35);
p5.textWeight(weight);
p5.text('p5*js', 0, 10, p5.width);
screenshot();
}
});
});

visualSuite("textAlign", function () { // TEMPORARY SKIP
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"numScreenshots": 1
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"numScreenshots": 1
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"numScreenshots": 1
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"numScreenshots": 2
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"numScreenshots": 5
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"numScreenshots": 5
}
Loading