Canvas Text Writing Mode - prototype [chromium/src : main]

38 views
Skip to first unread message

Stephen Chenney (Gerrit)

unread,
Jan 11, 2026, 4:55:54 PMJan 11
to Jean-Philippe Gravel, Hiroki Nakagawa, AyeAye, Chromium LUCI CQ, Chromium Metrics Reviews, chromium...@chromium.org, Kentaro Hara, Raphael Kubo da Costa, blink-revie...@chromium.org, servicewor...@chromium.org, ashleynewson+w...@chromium.org, horo+...@chromium.org, kinuko+ser...@chromium.org, shimazu+se...@chromium.org, kenjibah...@chromium.org, android-web...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, blink-revie...@chromium.org, blink-rev...@chromium.org, blink-...@chromium.org, jmedle...@chromium.org, kinuko...@chromium.org
Attention needed from Jean-Philippe Gravel

Stephen Chenney added 2 comments

Patchset-level comments
File-level comment, Patchset 7 (Latest):
Stephen Chenney . resolved

This is one more hurdle on the road to launching Extended Text Metrics. Hopefully I've learned some things since adding `lang`.

File third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml/text.yaml
Line 1506, Patchset 7 (Latest): if ('{{ ctx_writing_mode }}' == 'vertical' && '{{ text_direction }}' == 'rtl')
Stephen Chenney . unresolved

Due to issues with DOM selection rects for RTL upright text, do not try to test it here. I'll try to create some ref tests.

Open in Gerrit

Related details

Attention is currently required from:
  • Jean-Philippe Gravel
Submit Requirements:
  • requirement satisfiedCode-Coverage
  • requirement satisfiedCode-Owners
  • requirement is not satisfiedCode-Review
  • requirement is not satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: chromium/src
Gerrit-Branch: main
Gerrit-Change-Id: Ia9a4276a2cb65e1415dd7fefeabd0f719628639c
Gerrit-Change-Number: 7310865
Gerrit-PatchSet: 7
Gerrit-Owner: Stephen Chenney <sche...@chromium.org>
Gerrit-Reviewer: Jean-Philippe Gravel <jpgr...@chromium.org>
Gerrit-Reviewer: Stephen Chenney <sche...@chromium.org>
Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
Gerrit-CC: Hiroki Nakagawa <nhi...@chromium.org>
Gerrit-CC: Kentaro Hara <har...@chromium.org>
Gerrit-CC: Raphael Kubo da Costa <ku...@igalia.com>
Gerrit-Attention: Jean-Philippe Gravel <jpgr...@chromium.org>
Gerrit-Comment-Date: Sun, 11 Jan 2026 21:55:45 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
satisfied_requirement
unsatisfied_requirement
open
diffy

Jean-Philippe Gravel (Gerrit)

unread,
Jan 21, 2026, 1:47:02 PMJan 21
to Stephen Chenney, Andres Ricardo Perez, Hiroki Nakagawa, AyeAye, Chromium LUCI CQ, Chromium Metrics Reviews, chromium...@chromium.org, Kentaro Hara, Raphael Kubo da Costa, blink-revie...@chromium.org, servicewor...@chromium.org, ashleynewson+w...@chromium.org, horo+...@chromium.org, kinuko+ser...@chromium.org, shimazu+se...@chromium.org, kenjibah...@chromium.org, android-web...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, blink-revie...@chromium.org, blink-rev...@chromium.org, blink-...@chromium.org, jmedle...@chromium.org, kinuko...@chromium.org
Attention needed from Andres Ricardo Perez and Stephen Chenney

Jean-Philippe Gravel added 1 comment

Patchset-level comments
Jean-Philippe Gravel . resolved

Assigning to Andres, he would be a better reviewer for this change.

Open in Gerrit

Related details

Attention is currently required from:
  • Andres Ricardo Perez
  • Stephen Chenney
Submit Requirements:
  • requirement satisfiedCode-Coverage
  • requirement satisfiedCode-Owners
  • requirement is not satisfiedCode-Review
  • requirement is not satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: chromium/src
Gerrit-Branch: main
Gerrit-Change-Id: Ia9a4276a2cb65e1415dd7fefeabd0f719628639c
Gerrit-Change-Number: 7310865
Gerrit-PatchSet: 7
Gerrit-Owner: Stephen Chenney <sche...@chromium.org>
Gerrit-Reviewer: Andres Ricardo Perez <andres...@chromium.org>
Gerrit-Reviewer: Stephen Chenney <sche...@chromium.org>
Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
Gerrit-CC: Hiroki Nakagawa <nhi...@chromium.org>
Gerrit-CC: Kentaro Hara <har...@chromium.org>
Gerrit-CC: Raphael Kubo da Costa <ku...@igalia.com>
Gerrit-Attention: Stephen Chenney <sche...@chromium.org>
Gerrit-Attention: Andres Ricardo Perez <andres...@chromium.org>
Gerrit-Comment-Date: Wed, 21 Jan 2026 18:46:56 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
satisfied_requirement
unsatisfied_requirement
open
diffy

Andres Ricardo Perez (Gerrit)

unread,
Jan 30, 2026, 5:22:38 PM (12 days ago) Jan 30
to Stephen Chenney, Hiroki Nakagawa, AyeAye, Chromium LUCI CQ, Chromium Metrics Reviews, chromium...@chromium.org, Kentaro Hara, Raphael Kubo da Costa, blink-revie...@chromium.org, servicewor...@chromium.org, ashleynewson+w...@chromium.org, horo+...@chromium.org, kinuko+ser...@chromium.org, shimazu+se...@chromium.org, kenjibah...@chromium.org, android-web...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, blink-revie...@chromium.org, blink-rev...@chromium.org, blink-...@chromium.org, jmedle...@chromium.org, kinuko...@chromium.org
Attention needed from Stephen Chenney

Andres Ricardo Perez added 7 comments

Patchset-level comments
Andres Ricardo Perez . resolved

This looks really good! I still want to look deeper into parts of the CL but these are my comments so far. I hope to be able to keep looking into it on Monday.

File third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
Line 1117, Patchset 7 (Latest): paint_canvas->translate(x, y);
if (use_max_width) {
paint_canvas->scale(1, ClampTo<float>(width / font_width));
}
paint_canvas->rotate(90);
paint_canvas->translate(-x, -y);
Andres Ricardo Perez . unresolved

Can you add a comment explaining why this order of operations is needed for vertical text rendering?

Line 1124, Patchset 7 (Latest): // We draw when fontWidth is 0 so compositing operations (eg, a "copy" op)
// still work. As the width of canvas is scaled, so text can be scaled to
// match the given maxwidth, update text location so it appears on desired
// place.
Andres Ricardo Perez . unresolved

Nit: Not sure if we should move this comment further up now since it still applies for vertical text.

File third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml/text.yaml
Line 919, Patchset 7 (Latest):- name: 2d.text.draw.align.start.ltr
desc: textAlign start with ltr is the left edge
canvas: dir="ltr"
test_type: promise
fonts:
- CanvasTest
code: |
{{ load_font }}
ctx.font = '50px CanvasTest';
{% if canvas_type != 'HtmlCanvas' %}
ctx.direction = 'ltr';
{% endif %}
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = '#0f0';
ctx.textAlign = 'start';
ctx.fillText('DD', 0, 37.5);
@assert pixel 5,5 ==~ 0,255,0,255;
@assert pixel 95,5 ==~ 0,255,0,255;
@assert pixel 25,25 ==~ 0,255,0,255;
@assert pixel 75,25 ==~ 0,255,0,255;
@assert pixel 5,45 ==~ 0,255,0,255;
@assert pixel 95,45 ==~ 0,255,0,255;
expected: green
variants:
- *load-font-variant-definition
Andres Ricardo Perez . unresolved

Could we add versions of these tests to check that the positioning is as expected?

Line 1688, Patchset 7 (Latest): if ('{{ writing_mode }}' == 'horizontal') {
Andres Ricardo Perez . unresolved

These tests can be written using Jinja conditionals instead of having both version in JS. They are used in other places of this yaml file: crsrc.org/c/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml/text.yaml;l=1478-1481;drc=138bb615274409ac21fffd63f29fe5eba0ee0ad0

That way, the resulting HTML doesn't have an unnecessary arm of the if statement that will never execute.

Line 2282, Patchset 7 (Latest): const text = '横p横p';
Andres Ricardo Perez . unresolved

Do these characters render in the `Ahem` font? From https://github.com/Kozea/Ahem I understand that they would all be the square glyph assigned to `X`.

Line 3600, Patchset 7 (Latest):- name: 2d.text.textOrientation.settings.tentative
desc: Testing value setting of textOrientation in Canvas
code: |
// Initial value is mixed
@assert ctx.textOrientation === "mixed";

// Setting writingMode to valid values
ctx.textOrientation = "upright";
@assert ctx.textOrientation === "upright";

ctx.textOrientation = "sideways";
@assert ctx.textOrientation === "sideways";

ctx.textOrientation = "mixed";
@assert ctx.textOrientation === "mixed";

// Check that likely incorrect values do not apply
ctx.textOrientation = "vertical";
@assert ctx.textOrientation === "mixed";

ctx.textOrientation = "horizontal";
@assert ctx.textOrientation === "mixed";

ctx.textOrientation = "up";
@assert ctx.textOrientation === "mixed";

ctx.textOrientation = "side";
@assert ctx.textOrientation === "mixed";

ctx.textOrientation = "left";
@assert ctx.textOrientation === "mixed";

ctx.textOrientation = "right";
@assert ctx.textOrientation === "mixed";

ctx.textOrientation = "inherit";
@assert ctx.textOrientation === "mixed";

- name: 2d.text.writingMode.settings.tentative
desc: Testing value setting of writingMode in Canvas
code: |
// Initial value is horizontal
@assert ctx.writingMode === "horizontal";

// Setting writingMode to valid values
ctx.writingMode = "vertical";
@assert ctx.writingMode === "vertical";

ctx.writingMode = "horizontal";
@assert ctx.writingMode === "horizontal";

// Check that CSS values do not apply
ctx.writingMode = "vertical-lr";
@assert ctx.writingMode === "horizontal";

ctx.writingMode = "vertical-rl";
@assert ctx.writingMode === "horizontal";

ctx.writingMode = "sideways-lr";
@assert ctx.writingMode === "horizontal";

ctx.writingMode = "sideways-rl";
@assert ctx.writingMode === "horizontal";

// Invalid but likely values
ctx.writingMode = "sideways";
@assert ctx.writingMode === "horizontal";

ctx.writingMode = "inherit";
@assert ctx.writingMode === "horizontal";
Andres Ricardo Perez . unresolved

I think these tests match some tests higher up this file that use a `valid` and `invalid` naming scheme. If they have they same objective, can we have them up with the other ones and use the same naming?

Open in Gerrit

Related details

Attention is currently required from:
  • Stephen Chenney
Gerrit-Comment-Date: Fri, 30 Jan 2026 22:22:33 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
satisfied_requirement
unsatisfied_requirement
open
diffy

Stephen Chenney (Gerrit)

unread,
Feb 2, 2026, 11:08:23 AM (9 days ago) Feb 2
to Andres Ricardo Perez, Hiroki Nakagawa, AyeAye, Chromium LUCI CQ, Chromium Metrics Reviews, chromium...@chromium.org, Kentaro Hara, Raphael Kubo da Costa, blink-revie...@chromium.org, servicewor...@chromium.org, ashleynewson+w...@chromium.org, horo+...@chromium.org, kinuko+ser...@chromium.org, shimazu+se...@chromium.org, kenjibah...@chromium.org, android-web...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, blink-revie...@chromium.org, blink-rev...@chromium.org, blink-...@chromium.org, jmedle...@chromium.org, kinuko...@chromium.org
Attention needed from Andres Ricardo Perez

Stephen Chenney added 7 comments

Patchset-level comments
Stephen Chenney . resolved

Thanks for the first pass. It's worth saying there's no big rush because the tests should not land until the Spec PR is landed. I might even be asked to move them out to a separate WPT PR.

I'll work on the things you mentioned in the meantime.

File third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
Line 1117, Patchset 7 (Latest): paint_canvas->translate(x, y);
if (use_max_width) {
paint_canvas->scale(1, ClampTo<float>(width / font_width));
}
paint_canvas->rotate(90);
paint_canvas->translate(-x, -y);
Andres Ricardo Perez . unresolved

Can you add a comment explaining why this order of operations is needed for vertical text rendering?

Stephen Chenney

Will do.

Line 1124, Patchset 7 (Latest): // We draw when fontWidth is 0 so compositing operations (eg, a "copy" op)
// still work. As the width of canvas is scaled, so text can be scaled to
// match the given maxwidth, update text location so it appears on desired
// place.
Andres Ricardo Perez . unresolved

Nit: Not sure if we should move this comment further up now since it still applies for vertical text.

Stephen Chenney

Yes, I think it moves up.

File third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml/text.yaml
Stephen Chenney

Yes, I can do that. It was a bit tricky figuring out all the testing required for his change.

Line 1688, Patchset 7 (Latest): if ('{{ writing_mode }}' == 'horizontal') {
Andres Ricardo Perez . unresolved

These tests can be written using Jinja conditionals instead of having both version in JS. They are used in other places of this yaml file: crsrc.org/c/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml/text.yaml;l=1478-1481;drc=138bb615274409ac21fffd63f29fe5eba0ee0ad0

That way, the resulting HTML doesn't have an unnecessary arm of the if statement that will never execute.

Stephen Chenney

Thanks. I'll take a look and update.

Line 2282, Patchset 7 (Latest): const text = '横p横p';
Andres Ricardo Perez . unresolved

Do these characters render in the `Ahem` font? From https://github.com/Kozea/Ahem I understand that they would all be the square glyph assigned to `X`.

Stephen Chenney

I did open the Ahem font in a font viewer to check what which glyphs were used, but now I don't recall the result. I'll have to check again.

Stephen Chenney

Yes, can do.

Open in Gerrit

Related details

Attention is currently required from:
  • Andres Ricardo Perez
Submit Requirements:
  • requirement satisfiedCode-Coverage
  • requirement satisfiedCode-Owners
  • requirement is not satisfiedCode-Review
  • requirement is not satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: chromium/src
Gerrit-Branch: main
Gerrit-Change-Id: Ia9a4276a2cb65e1415dd7fefeabd0f719628639c
Gerrit-Change-Number: 7310865
Gerrit-PatchSet: 7
Gerrit-Owner: Stephen Chenney <sche...@chromium.org>
Gerrit-Reviewer: Andres Ricardo Perez <andres...@chromium.org>
Gerrit-Reviewer: Stephen Chenney <sche...@chromium.org>
Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
Gerrit-CC: Hiroki Nakagawa <nhi...@chromium.org>
Gerrit-CC: Kentaro Hara <har...@chromium.org>
Gerrit-CC: Raphael Kubo da Costa <ku...@igalia.com>
Gerrit-Attention: Andres Ricardo Perez <andres...@chromium.org>
Gerrit-Comment-Date: Mon, 02 Feb 2026 16:08:14 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Andres Ricardo Perez <andres...@chromium.org>
satisfied_requirement
unsatisfied_requirement
open
diffy

Andres Ricardo Perez (Gerrit)

unread,
Feb 6, 2026, 4:11:25 PM (5 days ago) Feb 6
to Stephen Chenney, Hiroki Nakagawa, AyeAye, Chromium LUCI CQ, Chromium Metrics Reviews, chromium...@chromium.org, Kentaro Hara, Raphael Kubo da Costa, blink-revie...@chromium.org, servicewor...@chromium.org, ashleynewson+w...@chromium.org, horo+...@chromium.org, kinuko+ser...@chromium.org, shimazu+se...@chromium.org, kenjibah...@chromium.org, android-web...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, blink-revie...@chromium.org, blink-rev...@chromium.org, blink-...@chromium.org, jmedle...@chromium.org, kinuko...@chromium.org
Attention needed from Stephen Chenney

Andres Ricardo Perez voted and added 5 comments

Votes added by Andres Ricardo Perez

Code-Review+1

5 comments

Patchset-level comments
Andres Ricardo Perez . resolved

Finally did my second pass. LGTM with some testing thoughts and nits.

File third_party/blink/renderer/core/html/canvas/text_metrics.cc
Line 557, Patchset 7 (Latest): // If to the left (or right), clamp to the left (or right) point
Andres Ricardo Perez . unresolved

I think this comment needs to be rephrased given the possibility of vertical text.

File third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
Line 896, Patchset 7 (Latest): state.GetWritingMode(), 0, text.length());
Andres Ricardo Perez . unresolved
This is originally our fault, but given that you are adding a parameter, could you please add inline comments for `run_start` and `run_end`?
```suggestion
state.GetWritingMode(), /*run_start=*/0, /*run_end=*/text.length());
```
Line 945, Patchset 7 (Latest): text_cluster->start(), text_cluster->end(), nullptr,
Andres Ricardo Perez . unresolved
Same as above, could you please add the inline comment here too?
```suggestion
text_cluster->start(), text_cluster->end(), /*max_width=*/nullptr,
```
Line 1271, Patchset 7 (Latest):void BaseRenderingContext2D::setTextOrientation(
const V8CanvasTextOrientation orientation) {
UseCounter::Count(GetTopExecutionContext(),
WebFeature::kCanvasRenderingContext2DTextOrientation);
CanvasRenderingContext2DState& state = GetState();
if (!state.HasRealizedFont()) {
setFont(font());
}

if (state.GetTextOrientation() == orientation) {
return;
}
state.SetTextOrientation(orientation.AsEnum(), GetFontSelector());
}

void BaseRenderingContext2D::setWritingMode(
const V8CanvasTextWritingMode mode) {
UseCounter::Count(GetTopExecutionContext(),
WebFeature::kCanvasRenderingContext2DWritingMode);
CanvasRenderingContext2DState& state = GetState();
if (!state.HasRealizedFont()) {
setFont(font());
}

if (state.GetWritingMode() == mode) {
return;
}
state.SetWritingMode(mode.AsEnum(), GetFontSelector());
}
Andres Ricardo Perez . unresolved

Seeing the setters made me think of the relationship of these new properties with `direction`. Does it make sense to have `ctx.direction = 'rtl'`, `ctx.writingMode = 'vertical'`, and `ctx.textOrientation = 'upright`?

Should we have tests for the edge cases with ligatures and other scripts? I found the following W3C guide for vertical upright text in Arabic: https://www.w3.org/TR/alreq/#h_vertical_upright.

I understand the behavior depending from `textOrientation` is handled from the `FontDescription` update, so I'm just wandering if this should be explicitly tested and/or added to the explainer as a potential edge case clarification.

Open in Gerrit

Related details

Attention is currently required from:
  • Stephen Chenney
Submit Requirements:
    • requirement satisfiedCode-Coverage
    • requirement satisfiedCode-Owners
    • requirement satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement satisfiedReview-Enforcement
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: chromium/src
    Gerrit-Branch: main
    Gerrit-Change-Id: Ia9a4276a2cb65e1415dd7fefeabd0f719628639c
    Gerrit-Change-Number: 7310865
    Gerrit-PatchSet: 7
    Gerrit-Owner: Stephen Chenney <sche...@chromium.org>
    Gerrit-Reviewer: Andres Ricardo Perez <andres...@chromium.org>
    Gerrit-Reviewer: Stephen Chenney <sche...@chromium.org>
    Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
    Gerrit-CC: Hiroki Nakagawa <nhi...@chromium.org>
    Gerrit-CC: Kentaro Hara <har...@chromium.org>
    Gerrit-CC: Raphael Kubo da Costa <ku...@igalia.com>
    Gerrit-Attention: Stephen Chenney <sche...@chromium.org>
    Gerrit-Comment-Date: Fri, 06 Feb 2026 21:11:18 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: Yes
    satisfied_requirement
    unsatisfied_requirement
    open
    diffy
    Reply all
    Reply to author
    Forward
    0 new messages