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 a lang IDL Attribute to CanvasTextDrawingStyles, and clarify "direction" on same #10873

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
113 changes: 106 additions & 7 deletions source
Original file line number Diff line number Diff line change
Expand Up @@ -65288,6 +65288,7 @@ interface mixin <dfn interface>CanvasPathDrawingStyles</dfn> {

interface mixin <dfn interface>CanvasTextDrawingStyles</dfn> {
// text
attribute DOMString <span data-x="dom-context-2d-lang">lang</span>; // (default: "inherit")
attribute DOMString <span data-x="dom-context-2d-font">font</span>; // (default 10px sans-serif)
attribute <span>CanvasTextAlign</span> <span data-x="dom-context-2d-textAlign">textAlign</span>; // (default: "start")
attribute <span>CanvasTextBaseline</span> <span data-x="dom-context-2d-textBaseline">textBaseline</span>; // (default: "alphabetic")
Expand Down Expand Up @@ -65752,6 +65753,7 @@ context.fillRect(100,0,50,50); // only this square remains</code></pre>
data-x="dom-context-2d-shadowOffsetX">shadowOffsetX</code>, <code
data-x="dom-context-2d-shadowOffsetY">shadowOffsetY</code>, <code
data-x="dom-context-2d-shadowBlur">shadowBlur</code>, <code
data-x="dom-context-2d-lang">lang</code>, <code
data-x="dom-context-2d-font">font</code>, <code
data-x="dom-context-2d-textAlign">textAlign</code>, <code
data-x="dom-context-2d-textBaseline">textBaseline</code>, <code
Expand Down Expand Up @@ -66246,6 +66248,20 @@ transform. ack Shaun Morris. -->
<h6>Text styles</h6>

<dl class="domintro">
<dt><code data-x=""><var>context</var>.<span subdfn data-x="dom-context-2d-lang">lang</span> [ = <var>value</var> ]</code></dt>
<dt><code data-x=""><var>styles</var>.<span data-x="dom-context-2d-lang">lang</span> [ = <var>value</var> ]</code></dt>
<dd>
<p>Returns the current language setting.</p>

<p>Can be set, to change the language used when resolving fonts. The syntax and valid values
are the same as those for the <span data-x="attr-lang">lang</span> element attribute, or the
value "inherit".</p>

<p>The default is "inherit", which uses the <span data-x="attr-lang">lang</span> of the
<code>canvas</code> element, or the Document associated with a Worker when there is no
<code>canvas</code> element, or a user-agent default language when neither source exists.</p>
</dd>

<dt><code data-x=""><var>context</var>.<span subdfn data-x="dom-context-2d-font">font</span> [ = <var>value</var> ]</code></dt>
<dt><code data-x=""><var>styles</var>.<span data-x="dom-context-2d-font">font</span> [ = <var>value</var> ]</code></dt>
<dd>
Expand Down Expand Up @@ -66424,15 +66440,56 @@ worker.postMessage(offscreenCanvas, [offscreenCanvas]);</code></pre>
Notice that the font is only loaded inside the worker, and not in the document context.</p>
</div>

<p>The choice of fonts and glyphs within a font may vary according to the <dfn
attribute for="CanvasTextDrawingStyles"><code data-x="dom-context-2d-lang">lang</code></dfn>
IDL attribute. On setting, <code data-x="dom-context-2d-lang">lang</code> must be
a <span data-x="attr-lang">valid BCP 47 language tag</span> or the string "inherit".
To determine the <dfn data-x="dom-context-2d-lang-used-value">used value</dfn>
for <code data-x="dom-context-2d-lang">lang</code>, use the following steps:</p>

<ol>
<li><p>If <var>object</var>'s <code data-x="dom-context-2d-lang">lang</code> value is
"inherit", then</p>
<ol>
<li><p>If <var>object</var>'s <span>font style source object</span> is a <code>canvas</code>
element, then return the element's <span data-x="attr-lang">lang atribute
value</span>.</p></li>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should return https://html.spec.whatwg.org/#language. The lang attribute can be specified in the ancestor chain.


<li>
<p>Otherwise, <var>object</var>'s <span>font style source object</span> is an
<code>OffscreenCanvas</code> object:</p>

<ol>
<li><p>Let <var>global</var> be <var>object</var>'s <span>relevant global
object</span>.</p></li>

<li><p>If <var>global</var> is a <code>Window</code> object, then return the
<span data-x="attr-lang">lang attribute</span> from <var>global</var>'s
<span data-x="concept-document-window">associated <code>Document</code></span>.</p></li>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This creates race conditions. We could snapshot the language when we create the worker, but I suspect it's not worth the complexity. On the fence as to whether it's worth doing when we create an OffscreenCanvas from a <canvas>.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's easy enough to snapshot the language when the OffscreenCanvas is created, at least in Chromium. I think we should snapshot for all OffscreenCanvas objects to keep things easy to understand. And we should only update the value for a canvas element when the lang attribute is set, plus at creation.

It's moderately expensive to look up the language because it requires walking up the DOM tree evaluated a bunch of conditionals. So snapshotting is also a performance win.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the new version for snapshotting language.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This creates race conditions. We could snapshot the language when we create the worker, but I suspect it's not worth the complexity. On the fence as to whether it's worth doing when we create an OffscreenCanvas from a <canvas>.

Why would that create race conditions? Could you please give an example.


<li><p>Otherwise, return a user-agent-defined default lang.</p></li>
schenney-chromium marked this conversation as resolved.
Show resolved Hide resolved
</ol>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is "primary language" here the same as CSS's content language?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was intending it to mean the "primary language" as used in https://html.spec.whatwg.org/multipage/dom.html#the-lang-and-xml:lang-attributes. The solution is to convert that linked reference to a definition and then refer to the definition in the canvas change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This concept has been cleaned up.

</li>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this work to define the used value here? What if I don't ever set the font attribute, but set the lang one. Will my default sans-serif font be able to get this used value?
Would it work better if it was called in text preparation algo instead?

+Nit: might be better as "used value" (without the "-") for easing search in page

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will take a look at what happens in Chromium for a non-defined font, but regardless I will update this to make it clear what happens when there is no defined font. And yes to moving it to the text preparation algorithm (and the direction resolution) because it does indeed need to be dynamic. I was wondering about the right way to ensure that dynamic changes were handled, and you've answered my question.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at this some more, this section going through getters and setters defines how the font is determined, while the text preparation algorithm says
Let font be the current font of target, as given by that object's font attribute.

The current font is never really defined. With that in mind, given that the lang is an input into the font selection process (at least in Chromium), I think the description of how to determine the lang belongs here, just after the font source determination method.

I will add text to clarify that the language applies even for the default font (which it does in Chromium), and also that any inherited value is snapshot when an offscreen canvas is created and for canvas elements is snapshot when the font or lang attributes are set.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the new version.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Be aware that https://whatpr.org/html/10873/canvas.html doesn't reflect the last commit's changes. Don't know why.

</ol>
<li><p>Otherwise, the <span>used value</span> is the <code data-x="dom-context-2d-lang">lang</code>
value.</p></li>
</ol>

<p>The <code data-x="dom-context-2d-lang">lang</code> IDL attribute, on getting, must return
the current value.</p>

<p>The <dfn attribute for="CanvasTextDrawingStyles"><code
data-x="dom-context-2d-font">font</code></dfn> IDL attribute, on setting, must be <span
data-x="parse something according to a CSS grammar">parsed as a CSS &lt;'font'> value</span> (but
without supporting property-independent style sheet syntax like 'inherit'), and the resulting font
must be assigned to the context, with the <span>'line-height'</span> component forced to 'normal',
with the <span>'font-size'</span> component converted to <span data-x="'px'">CSS pixels</span>,
and with system fonts being computed to explicit values. If the new value is syntactically
incorrect (including using property-independent style sheet syntax like 'inherit' or 'initial'),
then it must be ignored, without assigning a new font value. <ref>CSS</ref></p>
must be assigned to the context, with the primary language set to the <span
data-x="dom-context-2d-lang-used-value">used-value</span> for <code
data-x="dom-context-2d-lang">lang</code>, with the <span>'line-height'</span>
component forced to 'normal', with the <span>'font-size'</span> component converted to <span
data-x="'px'">CSS pixels</span>, and with system fonts being computed to explicit values. If the
new value is syntactically incorrect (including using property-independent style sheet syntax
like 'inherit' or 'initial'), then it must be ignored, without assigning a new font value.
<ref>CSS</ref></p>

<p>Font family names must be interpreted in the context of the <span>font style source
object</span> when the font is to be used; any fonts embedded using <code
Expand Down Expand Up @@ -66638,8 +66695,49 @@ worker.postMessage(offscreenCanvas, [offscreenCanvas]);</code></pre>

<dt><dfn enum-value for="CanvasDirection"><code data-x="dom-context-2d-direction-inherit">inherit</code></dfn>

<dd><p>Default to the directionality of the <code>canvas</code> element or <code>Document</code>
as appropriate.</p></dd>
<dd><p>Use the following process to determine which direction to treat input to the <span>text preparation algorithm</span></p>

<ol>
<li><p>If <var>object</var>'s <span>font style source object</span> is a <code>canvas</code>
element:</p>

<ol>
<li><p>If <span data-x="attr-dir">dir</span> attribute of the element is
<code data-x="attr-dir-auto">auto</code> then use the element's <span>computed value</span> of
<span>'direction'</span> as the direction.</p></li>

<li><p>Otherwise, use the value of the <span data-x="attr-dir">dir</span> attribute of the element
as the direction.</p></li>
</ol>

<li>
<p>Otherwise, <var>object</var>'s <span>font style source object</span> is an
<code>OffscreenCanvas</code> object:</p>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't actually define what the setter does.

I recommend looking at letterSpacing and wordSpacing. We want the same kind of language for lang and direction with the distinction between public API and internal state.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, I think.


<ol>
<li><p>Let <var>global</var> be <var>object</var>'s <span>relevant global
object</span>.</p></li>

<li><p>If <var>global</var> is a <code>Window</code> object:</p>
<ol>
schenney-chromium marked this conversation as resolved.
Show resolved Hide resolved
<li><p>Let <var>Document</var> be the <var>global</var>'s
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple issues here:

  1. "The lang getter steps are to return ..."
  2. It needs to return the value of some internal concept, similar to what letterSpacing does.
  3. It should probably be its own paragraph. We no longer really define getters and setters and concepts all in one paragraph.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I did "2" as you meant.

<span data-x="concept-document-window">associated <code>Document</code></span>.</p></li>

<li><p>If the value of the <span data-x="attr-dir">dir attribute</span> from
<var>Document</var> is <code data-x="attr-dir-auto">auto</code> then use the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to manipulate an internal concept.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what you mean here. Has the last set of edits addressed this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. If you look at the letterSpacing getter it is defined as follows:

The letterSpacing getter steps are to return the serialized form of this's letter spacing.

"letter spacing" there is the internal concept. That is what's missing here. The internal concept can have the same name as the getter, but it is distinct.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we're talking about the setter here. I feel I'm being slow, but is this sort of what you mean (sans links):

The lang setter steps are to set the value and update the internal font language

I think we agree that the value in lang is the string that round-trips, while the internal font language really is internal and not visible to authors.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's about both the getter and the setter. If the internal font language is the concept you could rewrite the getter as:

The language getter steps are to return this's internal font language.

(Note that you need to use "this" as well. We need to know where to find this internal concept after all.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The desired behavior for the getter relates to #10884. Do we want to return the "inherit" string or the internal font language? For direction Chromium and Webkit does the latter, while Firefox does the former.

From a back-compat position it's probably very hard to change direction, so maybe we should spec lang and direction to do the Chromium/WebKit thing. That's probably what developers most want. But it means no round-tripping.

Copy link
Member

@Kaiido Kaiido Jan 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there is no interop, is there really a back-compat issue? Has Firefox received bug reports about this?
I am very interested in the results from your counters, but as a web-dev I was very surprised when you metionned Chrome and Safari's behavior for direction. I personnaly think it's bad to have the computed value instead of "inherit" there. We don't have a granular way of resetting the context's attributes, so I wouldn't be surprised someone made their own thing by just reading all the props on the context and setting them back later. Returning the computed value would break that.

For lang, as currently written, "inherit" does not update outside of the setter, because the value stored is computed in the setter itself. So in that case it would make sense to return the stored (computed) value, but I think "inherit" wouldn't be the proper keyword. If "inherit" really did update even after setting, then returning "inherit" would make more sense IMO.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there is no interop, is there really a back-compat issue? Has Firefox received bug reports about this? I am very interested in the results from your counters, but as a web-dev I was very surprised when you metionned

It will take a couple of months to get counter information, as far as I can tell. I'm not sure what it can tell us about intent or desire, so I'll also try to find some hints from various developer forums (like how many people have asked about this issue, or asked how to get the direction or something like that).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, per https://w3ctag.github.io/design-principles/#attributes-like-data, it should roundtrip, if it doesn't it should be, a setter method, and a getter method.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I tend to agree with @Kaiido as to what we want here. It seems very strange to return the computed value, especially if it's informed by layout. (To be clear, I know how it all works, just speaking from the perspective of HTML standard semantics.)

<var>Document</var>'s <span>computed value</span> of <span>'direction'</span>
as the direction.</p></li>

<li><p>Otherwise, use the value of the <span data-x="attr-dir">dir attribute</span> from
<var>Document</var> as the direction.</p></li>
</ol>
</li>

<li><p>Otherwise, treat input to the <span>text preparation algorithm</span> as left-to-right text.</p></li>
</ol>
</li>
schenney-chromium marked this conversation as resolved.
Show resolved Hide resolved
</ol>
</dd>
</dl>

<p>The <code data-x="dom-context-2d-fontKerning">fontKerning</code> attribute's allowed keywords
Expand Down Expand Up @@ -146715,6 +146813,7 @@ INSERT INTERFACES HERE
Ştefan Vargyas,
Stefan Weiss,
Steffen Meschkat,
Stephen Chenney,
Stephen Ma,
Stephen Stewart,
Stephen White,
Expand Down