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

Extend Canvas TextMetrics for editing and text styling #11000

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
251 changes: 249 additions & 2 deletions source
Original file line number Diff line number Diff line change
Expand Up @@ -4165,6 +4165,7 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
<dfn data-x="DOMPointInit-x" data-xref="https://drafts.fxtf.org/geometry/#dom-dompointinit-x">x</dfn> and
<dfn data-x="DOMPointInit-y" data-xref="https://drafts.fxtf.org/geometry/#dom-dompointinit-y">y</dfn> members</li>
<li><dfn data-x-href="https://drafts.fxtf.org/geometry/#matrix-multiply">Matrix multiplication</dfn></li>
<li><dfn data-x-href="https://www.w3.org/TR/geometry-1/#DOMRect">DOMRectReadOnly and DOMRect</dfn></li>
</ul>

<p>The following terms are defined in the <cite>CSS Scoping</cite>: <ref>CSSSCOPING</ref></p>
Expand Down Expand Up @@ -65501,10 +65502,20 @@ interface mixin <dfn interface>CanvasUserInterface</dfn> {
undefined <span data-x="dom-context-2d-drawFocusIfNeeded-path-element">drawFocusIfNeeded</span>(<span>Path2D</span> path, <span>Element</span> element);
};


dictionary <dfn dictionary>TextClusterOptions</dfn> {
<span>CanvasTextAlign</span> align;
<span>CanvasTextBaseline</span> baseline;
double x;
double y;
};

interface mixin <dfn interface>CanvasText</dfn> {
// text (see also the <span>CanvasPathDrawingStyles</span> and <span>CanvasTextDrawingStyles</span> interfaces)
undefined <span data-x="dom-context-2d-fillText">fillText</span>(DOMString text, unrestricted double x, unrestricted double y, optional unrestricted double maxWidth);
undefined <span data-x="dom-context-2d-strokeText">strokeText</span>(DOMString text, unrestricted double x, unrestricted double y, optional unrestricted double maxWidth);
undefined <span data-x="dom-context-2d-fillTextCluster">fillTextCluster</span>(<span>TextCluster</span> cluster, double x, double y, optional <span>TextClusterOptions</span> options);
undefined <span data-x="dom-context-2d-strokeTextCluster">strokeTextCluster</span>(<span>TextCluster</span> cluster, double x, double y, optional <span>TextClusterOptions</span> options);
<span>TextMetrics</span> <span data-x="dom-context-2d-measureText">measureText</span>(DOMString text);
};

Expand Down Expand Up @@ -65604,6 +65615,22 @@ interface <dfn interface>TextMetrics</dfn> {
readonly attribute double <span data-x="dom-textmetrics-hangingBaseline">hangingBaseline</span>;
readonly attribute double <span data-x="dom-textmetrics-alphabeticBaseline">alphabeticBaseline</span>;
readonly attribute double <span data-x="dom-textmetrics-ideographicBaseline">ideographicBaseline</span>;

sequence&lt;<span data-x="DOMRectReadOnly and DOMRect">DOMRectReadOnly</span>> <span data-x="dom-textmetrics-getSelectionRects">getSelectionRects</span>(unsigned long start, unsigned long end);
<span data-x="DOMRectReadOnly and DOMRect">DOMRectReadOnly</span> <span data-x="dom-textmetrics-getActualBoundingBox">getActualBoundingBox</span>(unsigned long start, unsigned long end);
unsigned long <span data-x="dom-textmetrics-getIndexFromOffset">getIndexFromOffset</span>(double offset);
sequence&lt;<span>TextCluster</span>> <span data-x="dom-textmetrics-getTextClusters">getTextClusters</span>(unsigned long start, unsigned long end, optional <span>TextClusterOptions</span> options);
};

[Exposed=(Window,Worker)]
interface <dfn interface>TextCluster</dfn> {
// opaque object
readonly attribute double <span data-x="dom-TextCluster-x">x</span>;
readonly attribute double <span data-x="dom-TextCluster-y">y</span>;
readonly attribute unsigned long <span data-x="dom-TextCluster-begin">begin</span>;
readonly attribute unsigned long <span data-x="dom-TextCluster-end">end</span>;
readonly attribute <span>CanvasTextAlign</span> <span data-x="dom-TextCluster-align">align</span>;
readonly attribute <span>CanvasTextBaseline</span> <span data-x="dom-TextCluster-baseline">baseline</span>;
};

dictionary <dfn dictionary>ImageDataSettings</dfn> {
Expand Down Expand Up @@ -69122,6 +69149,16 @@ try {
provided, the text will be scaled to fit that width if necessary.</p>
</dd>

<dt><code data-x=""><var>context</var>.<span subdfn data-x="dom-context-2d-fillTextCluster">fillTextCluster</span>(<var>cluster</var>, <var>x</var>, <var>y</var> [, <var>options</var> ])</code></dt>
<dt><code data-x=""><var>context</var>.<span subdfn data-x="dom-context-2d-strokeTextCluster">strokeTextCluster</span>(<var>cluster</var>, <var>x</var>, <var>y</var> [, <var>options</var> ])</code></dt>

<dd>
<p>Fills or strokes (respectively) the given text cluster as it would be positioned if the text
as a whole was rendered at the given position. The align and baseline used to render, as well as
the position of the cluster in relation to the anchor point of the while text, can be modified
with the options dictionary.</p>
</dd>

<dt><code data-x=""><var>metrics</var> = <var>context</var>.<span subdfn data-x="dom-context-2d-measureText">measureText</span>(<var>text</var>)</code></dt>

<dd>
Expand All @@ -69143,6 +69180,27 @@ try {
<dt><code data-x=""><var>metrics</var>.<span subdfn data-x="dom-textmetrics-ideographicBaseline">ideographicBaseline</span></code></dt>

<dd><p>Returns the measurement described below.</p></dd>

<dt><code data-x=""><var>metrics</var>.<span subdfn data-x="dom-textmetrics-getSelectionRects">getSelectionRects</span>(<var>start</var>, <var>end</var>)</code></dt>

<dd><p>Returns a list of <code data-x="DOMRectReadOnly and DOMRect">DOMRectReadOnly</code> objects corresponding to the selection
rectangles for the given range in the string.</p></dd>

<dt><code data-x=""><var>metrics</var>.<span subdfn data-x="dom-textmetrics-getActualBoundingBox">getActualBoundingBox</span>(<var>start</var>, <var>end</var>)</code></dt>

<dd><p>Returns a <code data-x="DOMRectReadOnly and DOMRect">DOMRectReadOnly</code> that corresponds to the bounding rectangle for the
given range in the string.</p></dd>

<dt><code data-x=""><var>metrics</var>.<span subdfn data-x="dom-textmetrics-getIndexFromOffset">getIndexFromOffset</span>(<var>offset</var>)</code></dt>

<dd><p>Returns the index for the character at the given offset from the start of the text.</p></dd>

<dt><code data-x=""><var>metrics</var>.<span subdfn data-x="dom-textmetrics-getTextClusters">getTextClusters</span>(<var>start</var>, <var>end</var> [, <var>options</var> ])</code></dt>

<dd><p>Returns a list of <code>TextCluster</code> objects, with positional data for each one.
They correspond to splitting the text into the minimal rendering units possible. The options
dictionary enables selecting a specific point to be returned for each cluster be specifying align
and baseline values.</p></dd>
</dl>

<div w-nodev>
Expand Down Expand Up @@ -69196,6 +69254,77 @@ try {
</li>
</ol>

<p>The <dfn method for="CanvasText"><code
data-x="dom-context-2d-fillTextCluster">fillTextCluster(<var>cluster</var>, <var>x</var>,
<var>y</var>, <var>options</var>)</code></dfn> and <dfn method for="CanvasText"><code
data-x="dom-context-2d-strokeTextCluster">strokeTextCluster(<var>cluster</var>, <var>x</var>,
<var>y</var>, <var>options</var>)</code></dfn> methods render a <code>TextCluster</code> object.
The cluster is rendered where it would be if the whole text that was passed to <code
data-x="dom-context-2d-measureText">measureText()</code> to obtain the cluster was rendered at
position (<var>x</var>,<var>y</var>), unless the positioning is modified with the options
argument. Specifically, when the method is invoked, the user agent must run these steps:</p>

<ol>
<li><p>If any of the arguments are infinite or NaN, then return.</p></li>

<li><p>Run the <span>text preparation algorithm</span>, passing it the complete text and the
<code>CanvasTextDrawingStyles</code> from the opaque <code>TextCluster</code>
<var>cluster</var>, with the exception of <code
data-x="dom-context-2d-textAlign">textAlign</code> and <code
data-x="dom-context-2d-textBaseline">textBaseline</code>, which are taken from the <code
data-x="dom-TextCluster-align">align</code> and <code
data-x="dom-TextCluster-baseline">baseline</code> attributes of <var>cluster</var>. Let
<var>glyphs</var> be the result.</p></li>

<li><p>Filter <var>glyphs</var> to include only the glyphs that contain <span
data-x="code point">code points</span> within the range <code
data-x="dom-TextCluster-begin">cluster["begin"]</code> to <code
data-x="dom-TextCluster-end">cluster["end"]</code>.</p></li>

<li><p>Move all the shapes in <var>glyphs</var> to the right by <var>x</var>
<span data-x="'px'">CSS pixels</span> and down by <var>y</var> <span data-x="'px'">CSS
pixels</span>.</p></li>

<li>
<p>If a <code>TextClusterOptions</code> options dictionary is passed and it has an <code
data-x="">options["x"]</code> value, move all the shapes in <var>glyphs</var> to the right by
<code data-x="">options["x"] &minus; cluster["x"]</code>.</p>

<p>If a <code>TextClusterOptions</code> options dictionary is passed and it has an <code
data-x="">options["y"]</code> value, move all the shapes in <var>glyphs</var> down by <code
data-x="">options["y"] &minus; cluster["y"]</code>.</p>
</li>

<li>
<p>Paint the shapes given in <var>glyphs</var>, as transformed by the <span
data-x="dom-context-2d-transformation">current transformation matrix</span>, with each <span
data-x="'px'">CSS pixel</span> in the coordinate space of <var>glyphs</var> mapped to one
coordinate space unit.</p>

<p>For <code data-x="dom-context-2d-fillTextCluster">fillTextCluster()</code>,
<span>this</span>'s <span data-x="concept-CanvasFillStrokeStyles-fill-style">fill style</span>
must be applied to the shapes and <span>this</span>'s <span
data-x="concept-CanvasFillStrokeStyles-stroke-style">stroke style</span> must be ignored. For
<code data-x="dom-context-2d-strokeTextCluster">strokeTextCluster()</code>, the reverse holds:
<span>this</span>'s <span data-x="concept-CanvasFillStrokeStyles-stroke-style">stroke
style</span> must be applied to the result of <span data-x="trace a path">tracing</span> the
shapes using the object implementing the <code>CanvasText</code> interface for the line
styles, and <span>this</span>'s <span data-x="concept-CanvasFillStrokeStyles-fill-style">fill
style</span> must be ignored.</p>

<p>These shapes are painted without affecting the current path, and are subject to <span
data-x="shadows">shadow effects</span>, <span data-x="concept-canvas-global-alpha">global
alpha</span>, the <span>clipping region</span>, and the <span>current compositing and blending
operator</span>.</p>
</li>
</ol>

<p class="note">By setting <code data-x="">options["x"]</code> and <code
data-x="">options["y"]</code> to 0, the cluster will be rendered exactly at the position
(<var>x</var>,<var>y</var>) passed to <code
data-x="dom-context-2d-fillTextCluster">fillTextCluster()</code> and <code
data-x="dom-context-2d-strokeTextCluster">strokeTextCluster()</code>.</p>

<p>
<!--INSERT TRACKING-->
The <dfn method for="CanvasText"><code
Expand Down Expand Up @@ -69327,6 +69456,125 @@ try {
positive numbers indicating that the given baseline is below the <span>ideographic-under
baseline</span>. (Zero if the given baseline is the <span>ideographic-under
baseline</span>.)</p></dd>

<dt><dfn method for="TextMetrics"><code data-x="dom-textmetrics-getSelectionRects">getSelectionRects(<var>start</var>, <var>end</var>)</code></dfn> method</dt>

<dd><p>Returns the set of rectangles, in <span data-x="'px'">CSS pixels</span>, that the user
agent would render as a selection to select the characters within the specified range. The
range includes the character at the <var>start</var> index but stops before the <var>end</var>
index. The positions are returned relative to the alignment point given by the
<code data-x="dom-context-2d-textAlign">textAlign</code> and <code
data-x="dom-context-2d-textBaseline">textBaseline</code> attributes.</p></dd>

<dt><dfn method for="TextMetrics"><code data-x="dom-textmetrics-getActualBoundingBox">getActualBoundingBox(<var>start</var>, <var>end</var>)</code></dfn> method</dt>

<dd>
<p>Returns the rectangle equivalent to the box described by <code
data-x="dom-textmetrics-actualBoundingBoxLeft">actualBoundingBoxLeft</code>, <code
data-x="dom-textmetrics-actualBoundingBoxRight">actualBoundingBoxRight</code>, <code
data-x="dom-textmetrics-actualBoundingBoxAscent">actualBoundingBoxAscent</code>, <code
data-x="dom-textmetrics-actualBoundingBoxDescent">actualBoundingBoxDescent</code>, for the given
range. The range includes the character at the <var>start</var> index but stops before the
<var>end</var> index. The positions are returned relative to the alignment point given by the
<code data-x="dom-context-2d-textAlign">textAlign</code> and <code
data-x="dom-context-2d-textBaseline">textBaseline</code> attributes.</p>

<p class="note">The bounding box can be (and usually is) different from the selection
rectangles, which are based on the advance of the text. A font that is particularly slanted or
with accents that go beyond the flow of text will have a different paint bounding box.</p>
</dd>

<dt><dfn method for="TextMetrics"><code data-x="dom-textmetrics-getIndexFromOffset">getIndexFromOffset(<var>offset</var>)</code></dfn> method</dt>

<dd><p>Returns the string index for the character at the given offset distance from the start
position of the text run, relative to the alignment point given by the <code
data-x="dom-context-2d-textAlign">textAlign</code> attribute, with offset always increasing left
to right (negative offsets are valid). Values to the left or right of the text bounds will return
0 or the length of the text, depending on the writing direction.</p></dd>

<dt><dfn method for="TextMetrics"><code data-x="dom-textmetrics-getTextClusters">getTextClusters(<var>start</var>, <var>end</var>, <var>options</var>)</code></dfn> method</dt>

<dd>
<p>Splits the given range of the text into clusters and returns the positional data for each
cluster. The range includes the character at the <var>start</var> index but stops before the
<var>end</var> index. Each cluster represents a minimal group of characters such that their
corresponding glyphs cannot be broken down any further, and each character in the range is part
of only one cluster. If a cluster is only partially contained by the given character range, it
should still be included in the returned list.</p>

<p>The result is a list of new <code>TextCluster</code> objects. These objects are opaque as
they encapsulate the complete text that was segmented, along with the
<code>CanvasTextDrawingStyles</code> values that were active at the time <code
data-x="dom-context-2d-measureText">measureText()</code> was called. Note that <code
data-x="dom-context-2d-textAlign">textAlign</code> and <code
data-x="dom-context-2d-textBaseline">textBaseline</code> are exceptions, as they are explicitly
set attributes and are handled separately. Each <code>TextCluster</code> object has members
behaving as described in the following list:</p>

<dl>
<dt><dfn attribute for="TextCluster"><code data-x="dom-TextCluster-x">x</code></dfn> attribute</dt>

<dd>
<p>The x coordinate of the cluster, on a coordinate space using <span
data-x="'px'">CSS pixels</span>, with its origin at the anchor point defined by the <code
data-x="dom-context-2d-textAlign">textAlign</code> attribute (at the time <code
data-x="dom-context-2d-measureText">measureText()</code> was called) in relation to the text
as a whole.</p>

<p>The x position specified for each cluster corresponds to the alignment point given by the
<code data-x="dom-TextCluster-align">align</code> attribute of the cluster (e.g. if
this attribute is set to "<code data-x="">left</code>", the calculated position corresponds
to the <code data-x="dom-context-2d-textAlign-left">left</code> of each cluster). The
selection criteria for this alignment point is explained in the section for this attribute
of the cluster.</p>
</dd>

<dt><dfn attribute for="TextCluster"><code data-x="dom-TextCluster-y">y</code></dfn> attribute</dt>

<dd>
<p>The y coordinate of the cluster, on a coordinate space using <span
data-x="'px'">CSS pixels</span>, with its origin at the anchor point defined by the <code
data-x="dom-context-2d-textBaseline">textBaseline</code> attribute (at the time <code
data-x="dom-context-2d-measureText">measureText()</code> was called) in relation to the text
as a whole.</p>

<p>The y position specified for each cluster corresponds to the alignment point given by the
<code data-x="dom-TextCluster-baseline">baseline</code> attribute of the cluster (e.g. if
this attribute is set to "<code data-x="">top</code>", the calculated position corresponds
to the <code data-x="dom-context-2d-textBaseline-top">top</code> of each cluster). The
selection criteria for this alignment point is explained in the section for this attribute
of the cluster.</p>
</dd>

<dt><dfn attribute for="TextCluster"><code data-x="dom-TextCluster-begin">begin</code></dfn> attribute</dt>

<dd><p>The starting index for the range of <span data-x="code point">code points</span> that are
rendered as this cluster.</p></dd>

<dt><dfn attribute for="TextCluster"><code data-x="dom-TextCluster-end">end</code></dfn> attribute</dt>

<dd><p>The index immediately after the last included index for the range of <span
data-x="code point">code points</span> that are rendered as this cluster.</p></dd>

<dt><dfn attribute for="TextCluster"><code data-x="dom-TextCluster-align">align</code></dfn> attribute</dt>

<dd><p>The align for the specific point returned for the cluster. If a
<code>TextClusterOptions</code> options dictionary is passed, and it has a value for
<code data-x="">options["align"]</code>, this will be the assigned value. Otherwise, it will be
set as the <code data-x="dom-context-2d-textAlign">textAlign</code> attribute. Note that this
doesn't change the origin of the coordinate system, just which point is specified for each
cluster.</p></dd>

<dt><dfn attribute for="TextCluster"><code data-x="dom-TextCluster-baseline">baseline</code></dfn> attribute</dt>

<dd><p>The baseline for the specific point returned for the cluster. If a
<code>TextClusterOptions</code> options dictionary is passed, and it has a value for
<code data-x="">options["baseline"]</code>, this will be the assigned value. Otherwise, it will
be set as the <code data-x="dom-context-2d-textBaseline">textBaseline</code> attribute. Note
that this doesn't change the origin of the coordinate system, just which point is specified
for each cluster.</p></dd>
</dl>
</dd>
</dl>

<p class="note">Glyphs rendered using <code data-x="dom-context-2d-fillText">fillText()</code> and
Expand All @@ -69339,8 +69587,6 @@ try {
documents, rendered using CSS, straight to the canvas. This would be provided in preference to a
dedicated way of doing multiline layout.</p>



<h6>Drawing paths to the canvas</h6>

<p>Objects that implement the <code>CanvasDrawPath</code> interface have a <dfn>current default
Expand Down Expand Up @@ -146080,6 +146326,7 @@ INSERT INTERFACES HERE
Andreas Kling,
Andrei Popescu,
Andres Gomez,
Andrés Ricardo Pérez Rojas,
Andres Rios,
Andreu Botella,
Andrew Barfield,
Expand Down