Currently, the Canvas API does not include the ability to draw text strings. That makes several potentional Canvas applications, such as dynamic graphs or maps, more complex than neccessary. Instead of using the text facilities of the platform, Canvas applications need to bundle their own vector fonts or use inaccessible pre-rendered images for text display.
Unfortunately, adding a drawString() method is not as simple as it may seem; there are several more or less complex requirements that need to be taken into account:
Authors may reduce accessability by replacing regular HTML content with a Canvas and calls to drawString(). However, without drawString(), game designers may be tempted to use images with rendered text to display in-game messages (as in several Canvs text hacks). From an accessability point of view, drawString() has two advantages over the image approach:
Applying the browser text magnification mechanism to drawString() requires that the size of the text bounding box can be queried, so the content can adopt to enlarged labels dynamically.
Other (complex) Canvas rendering properties such as the fill style are set via properties of the class CanvasRenderingContext2D. For fillStyle, supported values are CSS color values ("rgb(r,g,b)", "#fff" etc.) or special style objects (LinearGradient, Pattern, RadialGradient), created via the context object (createLinearGradient()...).
Fortunately, most internationalization issues are solved implicitly by using unicode characters. For displaying Ruby annotations properly, it is necessary to be able to query text boundaries.
Without query capabilities for the text bounding box, it is impossible to avoid overlapping content, in particular if the user is able to change the text size.
Also, the accessability and I18N requirements depend on the availability of this information.
The size of the bounding box depends on the actual font, which may differ from the requested font. The string width of several characters may differ from the sum of all character widths when kerning is supported.
CSS1 Defines a set of font properties that could be relevant for a Canvas drawString() method:
Besides the font properties, there are additional properties that could be taken into account:
The properties text-align and vertical-align make much sense to properly align a label with a given point of reference.
CSS2 defines even more properties. While support for the font and alignement attributes seems to be a good starting point, the API should take into account that future versions may want to add support for more CSS properties.
In addition to solutions based on a separate Font or TextStyle object, there were two alternative proposals, drawElement(e) and a more minimalistic approach by Andrew Fedoniouk.
This powerful approach was originally suggested by Gervase Markham. The basic idea is to use the built-in HTML renderer to render a given subtree to an image object.
While drawElement(e) would also be a nice addition for the Canvas API, it does not replace a simple basic API for drawing labels with query capabilities:
Since drawElement(e) does not replace drawString(), its inclusion (specification, problems) should be discussed separately.
Andrew Fedoniouk suggested a nice minimalist approach:
Graphics.setFont(FontOrFamilyName, size, weight, ...); Graphics.setTextAlignment(horizontal, vertical); Graphics.getFontAscent(); Graphics.getTextWidth(string); Graphics.drawText(x,y,string);
The advantage of this approach is that it does not require a separate Font or TextStyle object. However, it seems difficult to remember the correct order of parameters in setFont(). Also, when more font or text properties are added, the GraphicsContext class may get cluttered too much.
Because of the issues described above, rhino-canvas implements a simple, string-based solution with a separate TextStyle object:
interface CanvasRenderingContext2D { // colours and styles attribute CanvasTextStyle textStyle; // (default black) CanvasTextStyle createTextStyle(); CanvasTextStyle createTextStyle( // shortcut in DOMString fontStyleVariantWeight, in DOMString fontSize, in DOMString fontFamily); void drawString(in float x, in float y, in DOMString text); [ ...Remainder of CanvasRenderingContext2D omitted... ] } interface CanvasTextStyle { // relative values and inherit are not supported! attribute DOMString fontFamily; / attribute DOMString fontStyle; attribute DOMString fontVariant; attribute DOMString fontWeight; attribute DOMString fontSize; attribute DOMString textDecoration; attribute DOMString verticalAlign; attribute DOMString textTransform; attribute DOMString textAlign; // justify not supported float stringWidth(in DOMString s); float getHeight(); // ascent + descent + leading float getBaselinePosition(); // ascent + leading float getAscent(); float getDescent(); float getLeading(); // perhaps add those later... // attribute DOMString wordSpacing; // attribute DOMString letterSpacing; };
If the requested font is not available, the broser choses the best fit, determined by the CSS font selection algorithm. All query methods return properties of the actually selected font, not the requested font.
There is much user demand for this feature on the WHATWG mailing list, and the feature completes the 2D canvas API. Moreover, it is simple and straight-forward to implement; it can be mapped more or less directly to the underlying graphics API of the platform.
Using this feature would simplify several graph- and map related applications. In the long term, authors will prefer this feature over alternatives (inaccessible images, vector fonts, proprietary plugins) because it provides a better user experience (loading time, accessability), combined with a simpler programming model.
There have been a lot of requests for a drawString() method in the Canvas API on the WHATWG mailing list.