Canvas Localization Support
The Web must support all users, with a multitude of languages and writing styles. HTML supports localization through the HTTP content language
representation
header,
while HTML defines the lang
and dir
attributes.
Yet the canvas element has no direct way of specifying the language for text drawing, nor
good support for controlling the writing direction. Such localization controls the
choice of characters from a given font and the bidi ordering.
Canvas Localization Today #
In browsers at the time of writing, a <canvas>
element in a web page uses the
HTML lang
attribute of the element for glyph selection, and uses the CSS
direction
property
for the text direction. Note that in all browsers the CSS direction
property will
have the HTML dir
value unless otherwise set. In addition, there is an explicit
direction
attribute on the <canvas>
element’s
CanvasRenderingContext2D
to control the text drawing direction. That attribute accepts the special "inherit"
value, the default, by which the direction is taken from the element.
This example demonstrates the current behavior for language in canvas text.
<div>
<h3>Canvas lang="en"</h3>
<canvas id="en" width="200px" height="60px" lang="en"></canvas>
</div>
<div>
<h3>Canvas lang="tr"</h3>
<canvas id="tr" width="200px" height="60px" lang="tr"></canvas>
</div>
<script>
let en_context = document.getElementById('en').getContext('2d');
let tr_context = document.getElementById('tr').getContext('2d');
function drawText(context) {
context.font = '20px Lato-Medium';
context.color = 'black';
context.fillText('finish', 50, 20);
}
function run_tests() {
drawText(en_context);
drawText(tr_context);
};
// See the example for the code to load the font
</script>
The language comes from the lang
attribute on the
canvas element, and here it controls the use of ligatures.
OffscreenCanvas
provides no such localization support. The "inherit"
text direction is always
left-to-right and the language is always the default browser language. As a result,
in many locales text content will be drawn differently in offscreen vs. DOM canvases.
This is a significant problem given offscreen canvas is the preferred method for
high performance applications.
The new canvas lang
attribute #
The first step to improving localization is giving authors explicit control over
the content language for canvas text metrics and drawing. The HTML WHATWG standards
group has agreed to add the lang
attribute to CanvasRenderingContext2D
and
OffscreenCanvasRenderingContext2D
. The attribute takes any value that an HTML
lang
attribute can take, or the "inherit"
value specifying that the language
be taken from the <canvas>
element.
This example shows the simplest use of the new attribute:
<script>
function run_test(language_string) {
let context = document.getElementById(language_string).getContext('2d');
context.lang = language_string;
context.font = '20px Lato-Medium';
context.color = 'black';
context.fillText('finish', 50, 20);
}
let latoMediumFontFace = new FontFace(
// Lato-Medium is a font with language specific ligatures.
'Lato-Medium',
'url(../fonts/Lato-Medium.ttf)'
);
latoMediumFontFace.load().then((font) => {
document.fonts.add(font);
run_test('en');
run_test('tr');
});
</script>
The line context.lang = language_string;
sets the language for text to the given
string, which can be any valid BC47 locale. In this case we use generic English (“en”)
and Turkish (“tr”).
The default value for the lang
attribute is "inherit"
whereby the text uses the
language of the canvas. The first example works as it always has, where the canvas
text inherits the language of the element.
Offscreen canvas, however, may now use the lang
attribute to localize the
language. This was not possible before.
-
The language may be set explicitly by setting the
lang
attribute in the offscreen context. -
The language may be inherited from the most intuitive source, such as a canvas element that the offscreen was transferred from (the
placeholder canvas
) or the document in which the Offscreen canvas is created.
Let’s explore these situations, starting with the explicit setting.
offscreen_ctx.lang = 'tr';
offscreen_ctx.font = '20px Lato-Medium';
offscreen_ctx.color = 'black';
offscreen_ctx.fillText('finish', 50, 20);
The language for the offscreen context is explicitly set to Turkish, and the font uses that language to render appropriate glyphs (ligatures or not, in this case).
When the offscreen is created from a canvas element, through the
transferControlToOffscreen()
method, the language is taken from the canvas
element. Here is an example:
<!DOCTYPE html>
<meta charset="utf-8">
<div>
<h3>Canvas lang="tr"</h3>
<canvas lang="tr" id="tr" width="200px" height="60px"></canvas>
</div>
<script>
let context = document.getElementById("tr").transferControlToOffscreen().getContext("2d");
// The default value for lang is "inherit". The canvas that transferred this
// offscreen context has a lang="tr" attribute, so the language used for text
// is "tr".
context.font = '20px Lato-Medium';
context.color = 'black';
context.fillText('finish', 50, 20);
</script>
The value used for "inherit"
is captured when control is transferred. If the
language on the original canvas element is subsequently changed, the language
used for "inherit"
on the offscreen does not change.
When the offscreen canvas is created directly in script, there is no canvas
element to use, so "inherit"
takes the value from the document element for the
realm the script is running in. Here’s an example:
<!DOCTYPE html>
<htnl lang="tr">
<meta charset="utf-8">
<div>
<h3>Canvas lang="tr"</h3>
<canvas id="tr" width="200px" height="60px"></canvas>
</div>
<script>
// This is the output canvas.
var canvas = document.getElementById('tr');
var bitmap_ctx = canvas.getContext('bitmaprenderer');
// A newly constructed offscreen canvas.
// Note the lang attribute on the document element, in this case "tr"
// is stored in the offscreen when it is created.
var offscreen = new OffscreenCanvas(canvas.width, canvas.height);
var offscreen_ctx = offscreen.getContext('2d');
offscreen_ctx.font = '20px Lato-Medium';
offscreen_ctx.color = 'black';
offscreen_ctx.fillText('finish', 50, 20);
// Transfer the OffscreenCanvas image to the output canvas.
const bitmap = offscreen.transferToImageBitmap();
bitmap_ctx.transferFromImageBitmap(bitmap);
</script>
</htnl>
Finally, when the offscreen canvas is transferred to a worker, the worker
will use the offscreen’s "inherit"
language for it’s own ‘“inherit”’
value. This is a complete example,
but the key snippet is:
// Create a canvas to serve as the placeholder for the offscreen worker.
const placeholder_canvas = document.createElement('canvas');
placeholder_canvas.setAttribute('width', '200');
placeholder_canvas.setAttribute('height', '60');
// Set the `lang` attribute on the placeholder canvas`
placeholder_canvas.setAttribute('lang', 'tr');
// Create the offscreen from the placeholder. The offscreen will get the
// language from this placeholder, in this case 'tr'.
const offscreen = placeholder_canvas.transferControlToOffscreen();
// Create the worker and transfer the offscreen to it. The language is
// transferred along with the offscreen, so content rendered in the
// offscreen will use 'tr' for the language (unless the `lang` is
// set explicitly on the context in the worker).
const worker = new Worker('canvas-lang-inherit-worker.js');
worker.postMessage({canvas: offscreen}, [offscreen]);
The lang
canvas text attribute is implemented in Chrome 135 and later
when “Experimental Web Platform Features” is enabled or the command line
flag “–enable-blink-features=CanvasTextLang” is used.
Fixing direction
for offscreen canvas #
The problem of direction = "inherit"
for OffscreenCanvas
is solved along
the same lines as lang
: Capture the inherited value at creation from the
placeholder <canvas>
element or the document hosting the script that created
the offscreen canvas.
All of the examples above for lang
work equally well for direction
, where the
HTML element attribute is dir
.
Thanks #
Canvas lang
attribute support in Chromium was implemented by Igalia S.L. funded
by Bloomberg L.P.
- Previous: Canvas Text Editing