Skip to content

Commit 5b3e292

Browse files
Merge pull request #55 from AndresRPerez12/master
Update TextMetrics following spec discussion on whatwg/html#11000
2 parents 5ff0329 + 6f5c39b commit 5b3e292

File tree

1 file changed

+12
-11
lines changed

1 file changed

+12
-11
lines changed

spec/enhanced-textmetrics.md

+12-11
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,12 @@ All metrics available through DOM APIs should also be available on `measureText(
1414

1515
We also want to provide more power to current canvas text rendering APIs.
1616

17-
1817
## Proposal
1918

2019
```webidl
2120
dictionary TextClusterOptions {
22-
DOMString align;
23-
DOMString baseline;
21+
CanvasTextAlign align;
22+
CanvasTextBaseline baseline;
2423
double x;
2524
double y;
2625
};
@@ -29,7 +28,7 @@ dictionary TextClusterOptions {
2928
interface TextCluster {
3029
readonly attribute double x;
3130
readonly attribute double y;
32-
readonly attribute unsigned long begin;
31+
readonly attribute unsigned long start;
3332
readonly attribute unsigned long end;
3433
readonly attribute DOMString align;
3534
readonly attribute DOMString baseline;
@@ -40,6 +39,7 @@ interface TextCluster {
4039
4140
sequence<DOMRectReadOnly> getSelectionRects(unsigned long start, unsigned long end);
4241
DOMRectReadOnly getActualBoundingBox(unsigned long start, unsigned long end);
42+
sequence<TextCluster> getTextClusters(optional TextClusterOptions options);
4343
sequence<TextCluster> getTextClusters(unsigned long start, unsigned long end, optional TextClusterOptions options);
4444
4545
unsigned long getIndexFromOffset(double offset);
@@ -49,6 +49,7 @@ interface CanvasRenderingContext2D {
4949
// ... extended from current CanvasRenderingContext2D.
5050
5151
void fillTextCluster(TextCluster textCluster, double x, double y, optional TextClusterOptions options);
52+
void strokeTextCluster(TextCluster textCluster, double x, double y, optional TextClusterOptions options);
5253
};
5354
```
5455

@@ -60,13 +61,13 @@ The `getIndexFromOffset` method returns the strign indext for the character at t
6061
left to right (so negative offsets are valid). Values to the left or right of the text bounds will return 0 or
6162
`string.length` depending on the writing direction. The functionality is similar but not identical to [`document.caretPositionFromPoint`](https://developer.mozilla.org/en-US/docs/Web/API/Document/caretPositionFromPoint). In particular, there is no need to return the element containing the caret and offsets beyond the boundaries of the string are acceptable.
6263

63-
`getTextClusters()` provides the ability to render minimal grapheme clusters (in conjunction with a new method for the canvas rendering context, more on that later). That is, for the character range given as in input, it returns the minimal rendering operations broken down as much as logically possible, with their corresponding positional data. The position is calculated with the original anchor point for the text as reference, while the `align` and `baseline` parameters in the options dictionary determine the desired alignment of each cluster.
64+
`getTextClusters()` provides the ability to render minimal grapheme clusters (in conjunction with a new method for the canvas rendering context, more on that later). That is, for the character range given as in input, it returns the minimal rendering operations broken down as much as logically possible, with their corresponding positional data. The position is calculated with the original anchor point for the text as reference, while the `align` and `baseline` parameters in the options dictionary determine the desired alignment of each cluster. If no options dictionary is passed, these values are the same as for the anchor point. If no range is passed, the whole text gets split into clusters.
6465

65-
To actually render these clusters on the screen, a new method for the rendering context is proposed: `fillTextCluster()`. It renders the cluster with the `align` and `baseline` stored in the object, ignoring the values set in the context. Additionally, to guarantee that the rendered cluster is accurate with the measured text, the rest of the `CanvasTextDrawingStyles` must be applied as they were when `ctx.measureText()` was called, regardless of any changes in these values on the context since. Note that to guarantee that the shaping of each cluster is indeed the same as it was when measured, it's necessary to use the whole string as context when rendering each cluster.
66+
To actually render these clusters on the screen, two new methods for the rendering context is proposed: `fillTextCluster()` and `strokeTextCluster()`. They renders the cluster with the `align` and `baseline` stored in the object, ignoring the values set in the context. Additionally, to guarantee that the rendered cluster is accurate with the measured text, the rest of the `CanvasTextDrawingStyles` must be applied as they were when `ctx.measureText()` was called, regardless of any changes in these values on the context since. Note that to guarantee that the shaping of each cluster is indeed the same as it was when measured, it's necessary to use the whole string as context when rendering each cluster.
6667

67-
For `align` specifically, the position is calculated in regards of the advance of said grapheme cluster in the text. For example: if the `align` passed to the function is `center`, for the letter **T** in the string **Test**, the position returned will be not exactly be in the middle of the **T**. This is because the advance is reduced by the kerning between the first two letters, making it less than the width of a **T** rendered on its own.
68+
To enable additional flexibility, an options dictionary can be passed to `fillTextCluster()` and `strokeTextCluster()` to override the values for `align`, `baseline`, `x`, and `y` that will be used to render that cluster. For example, calling `ctx.fillTextCluster(cluster, 10, 10, {x: 0, y:0})` will render the cluster exactly at position `(10, 10)`, instead of rendering as if the text as a whole was placed at `(10, 10)` (which is what the internal `x` and `y` values of the cluster represent). This same overriding applies to the `align` and `baseline` parameters if they are passed in the options dictionary. These options passed to `fillTextCluster()` don't modify the underlying cluster object, and only apply to the rendering of that specific call.
6869

69-
To enable additional flexibility, an options dictionary can be passed to `fillTextCluster()` to override the values for `align`, `baseline`, `x`, and `y` that will be used to render that cluster. For example, calling `ctx.fillTextCluster(cluster, 10, 10, {x: 0, y:0})` will render the cluster exactly at position `(10, 10)`, instead of rendering as if the text as a whole was placed at `(10, 10)` (which is what the internal `x` and `y` values of the cluster represent). This same overriding applies to the `align` and `baseline` parameters if they are passed in the options dictionary. These options passed to `fillTextCluster()` don't modify the underlying cluster object, and only apply to the rendering of that specific call.
70+
For `align` specifically, the position is calculated in regards of the advance of said grapheme cluster in the text. For example: if the `align` passed to the function is `center`, for the letter **T** in the string **Test**, the position returned will be not exactly be in the middle of the **T**. This is because the advance is reduced by the kerning between the first two letters, making it less than the width of a **T** rendered on its own.
7071

7172
`getSelectionRects()`, `getActualBoundingBox()`, and `getTextClusters()` operate in character ranges and use positions relative to the text’s origin (i.e., `textBaseline`/`textAlign` is taken into account).
7273

@@ -112,7 +113,7 @@ ctx.textBaseline = 'middle';
112113

113114
const text = 'Colors 🎨 are 🏎️ fine!';
114115
let tm = ctx.measureText(text);
115-
let clusters = tm.getTextClustersForRange(0, text.length);
116+
let clusters = tm.getTextClusters();
116117

117118
const colors = ['orange', 'navy', 'teal', 'crimson'];
118119
for(let cluster of clusters) {
@@ -138,7 +139,7 @@ let text = "🐞 Render this text on a circle! 🐈‍⬛";
138139

139140
const tm = ctx.measureText(text);
140141
// We want the x-position of the center of each cluster.
141-
const clusters = tm.getTextClusters(0, text.length, {align: 'center'});
142+
const clusters = tm.getTextClusters({align: 'center'});
142143

143144
for (const cluster of clusters) {
144145
// Since ctx.textAlign was set to 'left' before measuring, all values of
@@ -164,7 +165,7 @@ Expected output:
164165

165166
![A text string containing emoji rendered in a circle, with each glyph rotated according to its position](../images/text-clusters-circle.png)
166167

167-
`getTextClusters()` and `fillTextCluster()` can be used on Chrome Canary (starting from version `132.0.6783.0`) by enabling the feature with `--enable-features=ExtendedTextMetrics` (or the general `--enable-experimental-web-platform-features`).
168+
`getTextClusters()` and `fillTextCluster()` can be used on Chrome Canary (starting from version `132.0.6783.0`) by enabling the feature with `--enable-features=ExtendedTextMetrics` (or the general `--enable-experimental-web-platform-features`). `strokeTextCluster()` is available in Chrome Canary from version `135.0.7039.0`.
168169

169170
## Alternatives and Open Questions
170171

0 commit comments

Comments
 (0)