Writing Mode for Canvas text
What problem are you trying to solve?
Canvas text supports text direction and language for internationalization, but there is no way to render characters in the correct orientation for vertical writing modes. While the text string may be rotated using canvas transforms to get a vertical orientation, the characters within the string will not be oriented correctly.
What solutions exist today?
The only way to solve this today is to render each character separately, each one positioned vertically below the previous.
How would you solve it?
I think the simplest solution for web developers is to support the CSS writing modes, which is possibly easiest for browsers too. Someone on Stack Overflow claims that Firefox applies CSS writing mode to rendered text when the canvas element has such style. There's quite a bit of work to make this happen because the definition of origin, start, end, align etc all need to make sense with the various writing modes.
Anything else?
This issue came up in TAG review for Extended Text Metrics. I think we really need to solve it.
This has since been removed from the source resource, but there used to be an API sketch in there for this when text support was first added: https://github.com/whatwg/html/commit/96b43465a8a302fe451bbf550608594415bd08f9. It seems like it should be more tractable now that all browsers have vertical text support of some kind.
cc @whatwg/canvas
In that case I'll take a look at implementing this and come back with a concrete proposal.
I guess I am that "someone on StackOverflow", so indeed, Firefox does respect writing-mode and text-orientation from CSS. https://jsfiddle.net/c8uqko3x/
There it seems the textBaseline will affect the x-axis position, and so will the textAlign, though this latter will use the horizontal text width as reference, which both seem very weird to me.
Chromium and Safari also do something here, in that they will render the text vertically, but rotated so that it's actually rendered horizontally...
There, the textBaseline will affect the y axis, which would have made sense if the text wasn't rotated at 90°... And textAlign will affect the x-axis, which does make sense given the text is rotated.
Personally, I'd be in favor of adding explicit attributes to the 2D context interface, and to try to remove this CSS inheritance thing. Given the interop discrepancies there, I doubt there are much websites in the wild that do make use of it.
Ps: I now see that Chrome and Safari incorrectly pass the tests from https://chromium-review.googlesource.com/c/chromium/src/+/5732132 because they didn't set the text-orientation along the writing-mode. I opened https://github.com/web-platform-tests/wpt/pull/53782
I think this is a dupe of #4070.
I've finally started to spec and implement this. We'll also need the concept of text-orientation from CSS.
I think I understand the existing Chromium behavior now too.
I've put together an Explainer as part of thinking through the details of how this feature should work.
I'm most interested in feedback about the alignment attributes. The Explainer has one proposal but there are alternatives with their own pros and cons. The choices are:
- Should
textAlign/textBaselinecontrol horizontal/vertical or vertical/horizontal alignment for vertical writing mode? I think the latter becausestartandendwould then make sense and as I understand it vertical text is written top-aligned in general. - Do we use existing values like
top,leftetc such that their semantic meaning aligns, or so that a particular value would not need to change when you switch the writing mode? That is,textAlign = "left"would physically be the top iftextAligncontrols vertical alignment and "do the right thing" if you switched writing modes, but do we really wantleftto refer to a vertical location? Should we usetextAlign = "top"instead which would be invalid for horizontal text? - What do we do about
textBaselinevalues that really have no sensible interpretation in this situation?
CC'ing @kojiishi and @fantasai as the CSS Writing Modes editors, who might be able to answer the questions above.
- Should
textAlign/textBaselinecontrol horizontal/vertical or vertical/horizontal ...
I agree with the latter. Setting writingMode to sideways should be equivalent to rotating the canvas by 90 degree.
- Do we use existing values like
top,leftetc such that their semantic meaning aligns, ...
Matching to CSS text-align would be helpful. That is to say, textAlign = "left" would be top-alignment for a vertical text.
- What do we do about
textBaselinevalues ...
textBaseline is a subset of CSS dominant-baseline. All major browsers support dominant-baseline for vertical SVG <text>, and we can follow its behavior.
BTW, don't we support the sideways-lr behavior, which draws text from bottom to top?
- What do we do about
textBaselinevalues ...
textBaselineis a subset of CSSdominant-baseline. All major browsers supportdominant-baselinefor vertical SVG<text>, and we can follow its behavior.
That's super useful, thanks. My concern with always following the dominant-baseline values/semantics is incompatibility with the current values for canvas textBaseline. I would prefer continuing to use the existing canvas top, middle and bottom values and use spec language to align them with the CSS dominant-baseline behavior. i.e top -> text-top, bottom -> text-bottom, middle -> middle.
BTW, don't we support the
sideways-lrbehavior, which draws text from bottom to top?
I considered supporting both lr and rl version of sideways. I decided against it because it complicates the discussion of textBaseline and textAlign because sideway-rl has inline and block directions the same as vertical, but sideways-lr reverses everything. I think describing things using the text itself as the frame of reference might make it easier to spec both, and I guess mixing sideways with textDirection = "rtl" also reverses start and end.
But it remains the case that it's easier to rotate transform canvas text than it is to rotate HTML text, in part because it's only a single line, so it's not clear to me that the extra spec work for sideways-lr is worth the effort. It would also mean we have lr and rl versions of sideways but not vertical, which I just don't like (weak argument).