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

OpenType Features and Variable Fonts for Canvas #3571

Open
drott opened this issue Mar 15, 2018 · 22 comments
Open

OpenType Features and Variable Fonts for Canvas #3571

drott opened this issue Mar 15, 2018 · 22 comments
Labels
addition/proposal New features or enhancements needs concrete proposal Moving the issue forward requires someone to figure out a detailed plan needs implementer interest Moving the issue forward requires implementers to express interest topic: canvas

Comments

@drott
Copy link

drott commented Mar 15, 2018

Font styling for the 2D Context of Canvas is currently limited to the font state of the context object.

I would like to start a discussion on extending Canvas' capabilities for font styling and OpenType feature activation. The reason being that a number of advancements in typographic quality on the web are not accessible through the font property of 2D context.

In regular CSS activating OpenType features such as ligatures, small caps, contextual alternates, additional number/fraction/ordinal forms is performed through using font-variant-* properties, and font-feature-settings.

The second limitation is the usage of Variable Fonts (Introducing Variable Fonts, Introduction to Variable Fonts on the Web, Variable Fonts Exploration).

In regular CSS, controlling parameters for the rendering of variable fonts is done through the font-weight, font-stretch, and font-style properties, which can be specified as part of the font property/shorthand form on canvas. However, activating variation axes outside these three canonical axes is not possible, preventing a whole set of more artistic and creative fonts to be used on canvas. Control of the additional axes is done through the font-optical-sizing property as well as the low level font-variation-settings.

I believe it would be beneficial to to find a way for all of the tools and control that the CSS Fonts Specification offers to be applicable to the 2D context of Canvas.

@drott
Copy link
Author

drott commented Mar 15, 2018

Perhaps one approach would be to expose a style-like object on the 2D context and define a subset of style properties relevant for font-styling to be permitted, such as font-family, font-(stretch|style|weight), font-variant-*, font-feature-settings, font-variation-settings etc.

@drott drott changed the title OpenType features and Variable Fonts for Canvas OpenType Features and Variable Fonts for Canvas Mar 15, 2018
@annevk annevk added addition/proposal New features or enhancements needs implementer interest Moving the issue forward requires implementers to express interest topic: canvas labels Mar 15, 2018
@annevk
Copy link
Member

annevk commented Mar 15, 2018

Could we use https://drafts.csswg.org/css-font-loading/#fontface-interface somehow? Abstracting fonts into a reusable/mutable object rather than exposing various properties to poke at it seems more reasonable.

cc @tabatkins

@drott
Copy link
Author

drott commented Mar 15, 2018

While the FontFace interface is getting close, I find it confusing to use FontFace in a dual purpose: In one sense as a representation of a @font-face rule that style rules are being matched against, and in a second sense as a collection of style attributes covering font styling, meaning one concrete particular instance of a font.

Especially so for variable fonts, where a @font-face can mean a spectrum of available font instances, e.g. weight ranging from 100 to 700, stretch ranging from 100% to 150%, slant ranging from 0 deg to 20 deg.

Also, @litherum commented in a separate thread that accessing/assigning only one such instance to the 2D context does not provide a means of specifying a cascade for fallback, as we would usually find it in the font-family stack of fonts.

@annevk
Copy link
Member

annevk commented Mar 15, 2018

We could make it a sequence of FontFace objects, but we could also support a CSSFontFaceRule object, if that matches things better. It just seems that at some point we should have an underlying primitive here rather than keep adding properties to the 2D context.

@kojiishi
Copy link

/cc @jfkthame @FremyCompany

@litherum
Copy link

We could make it a sequence of FontFace objects, but we could also support a CSSFontFaceRule object, if that matches things better. It just seems that at some point we should have an underlying primitive here rather than keep adding properties to the 2D context.

The underlying primitive for font selection is CSS properties.

  1. In the past, canvas's interaction with fonts has maintained the invariant that drawing text in a canvas has the same visual result as the browser drawing native text. Matching native rendering is desirable for sites like Flipboard which implemented their entire site using canvas.
  2. Keep in mind that this has to be implementable - all browsers already have code to select fonts using CSS properties and draw the result. Requiring an entirely new font selection mechanism would likely limit implementor's interest in implementing (it would certainly limit our interest).

Perhaps one approach would be to expose a style-like object on the 2D context and define a subset of style properties relevant for font-styling to be permitted

I think @drott's suggestion is a good one. Making the canvas API match the existing font selection facilities is best for developers, users, and implementors. Indeed, this is already how the existing canvas API was designed, and we should not deviate from that design.

@litherum
Copy link

Oh, a point I missed before - this issue is bringing up a valuable request. The ability to use great fonts should be ubiquitous, and included in every subsystem that interacts with text.

@tabatkins
Copy link
Contributor

Note that there are two very distinct possibilities for a font-related interface (the confusion of which resulted in a very mixed-up TypedOM section that I eventually had to remove):

  1. Explicitly providing font faces to something for use - these should use FontFace. (CSSFontFaceRule is roughly equivalent, but it's tied more to the stylesheet and its loading behavior; it shouldn't be used directly by APIs.)
  2. Activating the system's font-selection capability - this is what canvas currently does with its font attribute.

The latter is currently only exposed thru CSS properties; at some point in a future level of TypedOM there will be an object representing a font-selection query (for 'font' to reify to), but that doesn't exist yet.

You probably want to continue canvas's current approach, and continue just doing font queries, rather than explicitly providing faces.

@FremyCompany
Copy link

+1 to what @tabatkins said

@litherum
Copy link

litherum commented Apr 8, 2019

Yes, @tabatkins is right.

@darshanbib
Copy link

+1 this would be a great feature to add.

@rsheeter
Copy link

Pardon my density but I'm a little fuzzy on how this issue can be moved forward? - in addition to ot features and variations I'd love to be able to use font-palette and font-palette-values (https://www.w3.org/TR/css-fonts-4/#color-font-support) on text in a canvas, and of course to have a setup where the default for new font capabilities is to work on canvas.

@litherum
Copy link

I think we're just missing a concrete proposal.

@annevk annevk added the needs concrete proposal Moving the issue forward requires someone to figure out a detailed plan label May 19, 2022
@macnmm
Copy link

macnmm commented Oct 17, 2022

using measuretext to measure text on canvas using a variable font is among the concrete needs people have...

@RoelN
Copy link

RoelN commented Jan 24, 2023

using measuretext to measure text on canvas using a variable font is among the concrete needs people have...

Indeed. If a font has a opsz axis, the canvas will apply the optical size matching the font size, with no way to override it. So measureText.width for 160px will not be 10x the value of measureText.width for 16px.

@nornagon
Copy link

Based on @tabatkins' comment from 2018, it seems like the way forward is to add additional properties to CanvasRenderingContext2D mirroring the CSS font-{weight,stretch,style,size,variant-*,kerning,palette} properties (and probably more that I've missed in this list). It looks like this has already happened for fontKerning, fontStretch and fontVariantCaps, so adding the rest would be consistent.

@nornagon
Copy link

Looking a little deeper into the above, let me parse out each individual font-* CSS property and comment on them separately.

  • font-size, font-weight and font-style are already settable in CanvasRenderingContext2D using the font attribute. I think it would make sense to have these be settable independently (with e.g. ctx.font = "sans-serif"; ctx.fontWeight = "bold" being equivalent to ctx.font = "bold sans-serif"), but it seems low priority as the functionality is already available.
  • font-stretch and font-kerning are already present in CanvasRenderingContext2D as fontStretch and fontKerning.
  • font-variant is partially available in that the font property allows setting either normal or small-caps as a font variant, and further, the fontVariantCaps property on CanvasRenderingContext2D allows setting other caps variants. However, the various other constituent properties of font-variant aren't accessible. There could be a generic attribute DOMString fontVariant to allow setting all of the above at once, again parsed like a CSS <'font-variant'> value. This could supersede the fontVariantCaps property.
  • font-synthesis, font-feature-settings, font-language-override, font-optical-sizing, font-palette, font-size-adjust, and font-variation-settings are all unavailable in the CanvasRenderingContext2D context and would be good to expose similarly, as attribute DOMString parsed as CSS values.

If all these were added, we'd have a bunch of new DOMString attributes on CanvasRenderingContext2D to specify font settings. Some of these would interact with the value of the font attribute (in the way that e.g. setting fontVariantCaps = "small-caps" causes the font attribute to have the value 'small-caps 10px sans-serif', or setting font = "small-caps 10px sans-serif" causes the value of fontVariantCaps to be 'small-caps'), while others would not. Specifically, this would add:

attribute DOMString fontWeight;
attribute DOMString fontStyle;
attribute DOMString fontSize;
attribute DOMString fontVariant;
attribute DOMString fontSynthesis;
attribute DOMString fontFeatureSettings;
attribute DOMString fontVariationSettings;
attribute DOMString fontLanguageOverride;
attribute DOMString fontOpticalSizing;
attribute DOMString fontPalette;
attribute DOMString fontSizeAdjust;

to the CanvasRenderingContext2D interface. This would bring the full set of font control features from CSS Fonts Level 4 to <canvas>.

@tabatkins
Copy link
Contributor

I'd suggest mirroring everything that can be selected by 'font', so that the .font attribute can behave entirely like a shorthand property in CSS (and akin to the .style.font attribute in CSSOM). So that would imply adding .fontFamily too, at least. (I'm not checking the full set of 'font' sub-properties right now.)

This would allow you to do useful things like just changing the family while leaving the other settings intact; right now you'd have to set .font and then re-set all the sub-settings.

@nornagon
Copy link

Yeah, that makes sense. I guess this would almost exactly mirror the .style.font* in CSSOM, with the exception that inherit, smaller and similar values that imply an element tree to refer to would be excluded from allowable values.

@annevk
Copy link
Member

annevk commented May 8, 2023

They could use the canvas element itself to resolve those values. That's what we do elsewhere. (For OffscreenCanvas we would have to define what happens in the absence of an element.)

@Lorp
Copy link

Lorp commented Jun 14, 2023

Ideally we’d be able to style fonts for canvas just as we style them for HTML: the canvas would somehow “import” the font instance definition from an element defined with CSS (potentially the <canvas> element itself). This would make it very convenient for authors to switch freely between rendering text as HTML elements or on the canvas, which seems a very desirable goal. In documents that use both models, authors don’t want to worry whether they’ve correctly matched font instance definitions.

Here’s some JavaScript showing current and imagined ways of cloning an HTML element’s font style to a canvas, which may be useful for discussion:

The current method is how we do things now, where only a limited number of font properties can be replicated in canvas.

The future-A method imagines expanding the font shorthand property to include variations (and a string syntax for that), being able to retrieve complete shorthand property values from getComputedStyle(), and the canvas context able to ingest that.

The future-B method imagines a currentFont (psuedo-)property, a simpler way of getting the font shorthand property, and the canvas context able to ingest that.

The future-C method imagines a new Window.getComputedFont() method, which returns an object that a canvas context can ingest.

// set up div
let mydiv = document.getElementById("mydiv");
mydiv.style.fontFamily = "Amstelvar";
mydiv.style.fontSize = "48px";
mydiv.style.fontVariationSettings = `"XOPQ" 130, "YOPQ" 120, "XTRA" 420`;
let computedStyle = window.getComputedStyle(mydiv);

// set up canvas
let canvas = document.getElementById("mycanvas");
let ctx = canvas.getContext("2d");

// choose font selection tech for canvas
let tech = "current";
switch (tech) {

  case "current": 
    ctx.font = `${computedStyle.fontSize} ${computedStyle.fontFamily}`;
    break;
  
  case "future-A":
    ctx.font = computedStyle.font;
    break;
  
  case "future-B":
    ctx.font = mydiv.style.currentFont;
    break;

  case "future-C":
    ctx.font = window.getComputedFont(mydiv.style);
    break;
    
}

ctx.fillText("Test variable font instance in <canvas>", 0, 100);

@Lorp
Copy link

Lorp commented Jun 14, 2023

@tabatkins:

I'd suggest mirroring everything that can be selected by 'font', so that the .font attribute can behave entirely like a shorthand property in CSS…

Don’t forget that font-variation-settings, font-feature-settings, font-palette, font-variant-* and several other font properties are not settable via the font shorthand. Maybe they should be, and that would imply a canvas solution something like my "future-A" suggestion above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
addition/proposal New features or enhancements needs concrete proposal Moving the issue forward requires someone to figure out a detailed plan needs implementer interest Moving the issue forward requires implementers to express interest topic: canvas
Development

No branches or pull requests