diff --git a/src/type/p5.Font.js b/src/type/p5.Font.js index b039cd84f2..9b42b4e763 100644 --- a/src/type/p5.Font.js +++ b/src/type/p5.Font.js @@ -1,7 +1,7 @@ /** * API: * loadFont("https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,200..800&display=swap") - * loadFont("{ font-family: "Bricolage Grotesque", serif; font-optical-sizing: auto; font-weight: font-style: normal; font-variation-settings: "wdth" 100; }); + * loadFont("@font-face { font-family: "Bricolage Grotesque", serif; font-optical-sizing: auto; font-weight: font-style: normal; font-variation-settings: "wdth" 100; }); * loadFont({ * fontFamily: '"Bricolage Grotesque", serif'; * fontOpticalSizing: 'auto'; @@ -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 }; @@ -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 @@ -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]); diff --git a/src/type/text2d.js b/src/type/text2d.js index 446bf496b4..f8636916f5 100644 --- a/src/type/text2d.js +++ b/src/type/text2d.js @@ -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 @@ -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; @@ -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)); } @@ -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; @@ -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 diff --git a/src/webgl/text.js b/src/webgl/text.js index fbe377fe1d..2e418b82c8 100644 --- a/src/webgl/text.js +++ b/src/webgl/text.js @@ -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) { @@ -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 { diff --git a/test/unit/assets/BricolageGrotesque-Variable.ttf b/test/unit/assets/BricolageGrotesque-Variable.ttf new file mode 100644 index 0000000000..1c7c35e602 Binary files /dev/null and b/test/unit/assets/BricolageGrotesque-Variable.ttf differ diff --git a/test/unit/visual/cases/typography.js b/test/unit/visual/cases/typography.js index 826325d644..93014a4ed6 100644 --- a/test/unit/visual/cases/typography.js +++ b/test/unit/visual/cases/typography.js @@ -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,wght@12..96,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 diff --git a/test/unit/visual/screenshots/Typography/textFont/with a Google Font URL/000.png b/test/unit/visual/screenshots/Typography/textFont/with a Google Font URL/000.png new file mode 100644 index 0000000000..f3c32fe1eb Binary files /dev/null and b/test/unit/visual/screenshots/Typography/textFont/with a Google Font URL/000.png differ diff --git a/test/unit/visual/screenshots/Typography/textFont/with a Google Font URL/metadata.json b/test/unit/visual/screenshots/Typography/textFont/with a Google Font URL/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/Typography/textFont/with a Google Font URL/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/Typography/textFont/with a font file in WebGL/000.png b/test/unit/visual/screenshots/Typography/textFont/with a font file in WebGL/000.png new file mode 100644 index 0000000000..b1807c2d3e Binary files /dev/null and b/test/unit/visual/screenshots/Typography/textFont/with a font file in WebGL/000.png differ diff --git a/test/unit/visual/screenshots/Typography/textFont/with a font file in WebGL/metadata.json b/test/unit/visual/screenshots/Typography/textFont/with a font file in WebGL/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/Typography/textFont/with a font file in WebGL/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/Typography/textFont/with a font file/000.png b/test/unit/visual/screenshots/Typography/textFont/with a font file/000.png new file mode 100644 index 0000000000..cfb3d88363 Binary files /dev/null and b/test/unit/visual/screenshots/Typography/textFont/with a font file/000.png differ diff --git a/test/unit/visual/screenshots/Typography/textFont/with a font file/metadata.json b/test/unit/visual/screenshots/Typography/textFont/with a font file/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/Typography/textFont/with a font file/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/Typography/textWeight/can control non-variable fonts/000.png b/test/unit/visual/screenshots/Typography/textWeight/can control non-variable fonts/000.png new file mode 100644 index 0000000000..87dcda79df Binary files /dev/null and b/test/unit/visual/screenshots/Typography/textWeight/can control non-variable fonts/000.png differ diff --git a/test/unit/visual/screenshots/Typography/textWeight/can control non-variable fonts/001.png b/test/unit/visual/screenshots/Typography/textWeight/can control non-variable fonts/001.png new file mode 100644 index 0000000000..786f2a8b71 Binary files /dev/null and b/test/unit/visual/screenshots/Typography/textWeight/can control non-variable fonts/001.png differ diff --git a/test/unit/visual/screenshots/Typography/textWeight/can control non-variable fonts/metadata.json b/test/unit/visual/screenshots/Typography/textWeight/can control non-variable fonts/metadata.json new file mode 100644 index 0000000000..ebf58a6cb0 --- /dev/null +++ b/test/unit/visual/screenshots/Typography/textWeight/can control non-variable fonts/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 2 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts from files/000.png b/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts from files/000.png new file mode 100644 index 0000000000..a0a4df3428 Binary files /dev/null and b/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts from files/000.png differ diff --git a/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts from files/001.png b/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts from files/001.png new file mode 100644 index 0000000000..a0a4df3428 Binary files /dev/null and b/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts from files/001.png differ diff --git a/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts from files/002.png b/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts from files/002.png new file mode 100644 index 0000000000..f8284ba852 Binary files /dev/null and b/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts from files/002.png differ diff --git a/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts from files/003.png b/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts from files/003.png new file mode 100644 index 0000000000..5e8e4be2f2 Binary files /dev/null and b/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts from files/003.png differ diff --git a/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts from files/004.png b/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts from files/004.png new file mode 100644 index 0000000000..3d79f196a4 Binary files /dev/null and b/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts from files/004.png differ diff --git a/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts from files/metadata.json b/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts from files/metadata.json new file mode 100644 index 0000000000..01dd8f26ca --- /dev/null +++ b/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts from files/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 5 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts/000.png b/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts/000.png new file mode 100644 index 0000000000..2e1331d994 Binary files /dev/null and b/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts/000.png differ diff --git a/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts/001.png b/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts/001.png new file mode 100644 index 0000000000..2e1331d994 Binary files /dev/null and b/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts/001.png differ diff --git a/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts/002.png b/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts/002.png new file mode 100644 index 0000000000..0ce9cff826 Binary files /dev/null and b/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts/002.png differ diff --git a/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts/003.png b/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts/003.png new file mode 100644 index 0000000000..5ba14b1f2c Binary files /dev/null and b/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts/003.png differ diff --git a/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts/004.png b/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts/004.png new file mode 100644 index 0000000000..0b18dff4af Binary files /dev/null and b/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts/004.png differ diff --git a/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts/metadata.json b/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts/metadata.json new file mode 100644 index 0000000000..01dd8f26ca --- /dev/null +++ b/test/unit/visual/screenshots/Typography/textWeight/can control variable fonts/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 5 +} \ No newline at end of file