Open drott opened 6 years ago
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.
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
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.
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.
/cc @jfkthame @FremyCompany
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.
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.
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.
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):
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.)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.
+1 to what @tabatkins said
Yes, @tabatkins is right.
+1 this would be a great feature to add.
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.
I think we're just missing a concrete proposal.
using measuretext to measure text on canvas using a variable font is among the concrete needs people have...
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.
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.
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>.
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.
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.
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.)
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);
@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.
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, andfont-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
, andfont-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 thefont-optical-sizing
property as well as the low levelfont-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.