Page 1 of 2 For a Canvas object, drawing text is just another path to render. You can select a font and then render the shapes or glyphs it defines using stroke and fill methods. Going beyond this there are a lot of ways of positioning glyphs that control the typography and this is very specialized. In this extract from a chapter in my new book on JavaScript Graphics we look at how it works.
Now available as a paperback or ebook from Amazon.
JavaScript Bitmap Graphics With Canvas
Contents
- JavaScript Graphics
- Getting Started With Canvas
- Drawing Paths
Extract: Basic Paths Extract: SVG Paths Extract: Bezier Curves
- Stroke and Fill
Extract: Stroke Properties Extract: Fill and Holes Extract: Gradient & Pattern Fills
- Transformations
Extract: Transformations Extract: Custom Coordinates Extract Graphics State
- Text
Extract: Text, Typography & SVG Extract: Unicode
- Clipping, Compositing and Effects
Extract: Clipping & Basic Compositing
- Generating Bitmaps
Extract: Introduction To Bitmaps Extract : Animation
- WebWorkers & OffscreenCanvas
Extract: Web Workers Extract: OffscreenCanvas
- Bit Manipulation In JavaScript
Extract: Bit Manipulation
- Typed Arrays
Extract: Typed Arrays
- Files, blobs, URLs & Fetch
Extract: Blobs & Files Extract: Read/Writing Local Files Extract: Fetch API **NEW!
- Image Processing
Extract: ImageData Extract:The Filter API
- 3D WebGL
Extract: WebGL 3D
- 2D WebGL
Extract: WebGL Convolutions
<ASIN:B07XJQDS4Z>
<ASIN:1871962579>
<ASIN:1871962560>
What is important is that as text is just another example of a path it can be used with all of the techniques we have learned so far. This is good, but Canvas text processing is very primitive in terms of typography. Finally there is the problem of Unicode. We have moved beyond the 128 or 256 characters available in ASCII - how does this work in JavaScript?
In chapter but not in extract:
Typographic Positioning - textBaseline and textAlign
The meaning of the x,y in the positioning of text is more complicated than you might think. If you accept the defaults then it more or less corresponds to the bottom left corner of the text's bounding box.
You can change the horizontal positioning relative to the x value specified by setting textAlign property to one of:
start or left The text is drawn just after the x position.
center The center of the text is located at the x position.
end or right The end of the text is located the x position.
You can use these values to left or right justify or center text. For example:
ctx.font="normal normal 20px arial";
ctx.textAlign="right";
ctx.fillText("line of text followed by",200,60);
ctx.fillText("A shorter line of text",200,60+20*115/100);
gives:
The textBaseline property determines how y alters the vertical position of the text relative to the horizontal line, the baseline. It can be set to one of:
-
alphabetic Bottom of vertically oriented glyphs (default)
-
ideographic Bottom of horizontally oriented glyphs
-
top Text is aligned based on the top of the tallest glyph
-
bottom Text is aligned based on bottom of the glyph
-
middle Text is aligned according to the middle of the text
-
hanging Used by Tibetan and other indic scripts
You can see the essential details in this chart from the WHATWG specification:
Notice that the bounding box is the maximum area a font can use and not all glyphs fill it. In some fonts the majority of glyphs use a much smaller area of the total allowed to them. The key measurement is the em square. This is a square with sides equal to the current point size of the font.
When you render text onto the canvas you simply specify x,y and perhaps a maximum width. The x value gives the horizontal alignment and the y value sets the baseline - so what is the bounding box?
The answer is that the bounding box depends on the font in use and you can use the measureText() method to return a TextMetrics object which contains all of the information on the layout of a particular string in the current font as read only properties. The only problem at the moment is that this is only fully supported on Safari and on Chrome but only if you enable Experimental Web Platform Features using chrome://flags. All browsers support the TextMetrics object and at least the width property:
calculated width of the text. This is not the width of the bounding box but the number of pixels needed to the end of the text. That is, it is the y distance in pixels you need to move to continue the text in the same font.
distance from x,y to the left and right edge of the bounding box. Notice that these depend on the alignment used.
distance from x,y to the top and bottom of the bounding rectangle. The actual version of the properties gives you the top and bottom of the bounding rectangle for the actual text used in the call and the other two properties give the value for the font. This means the "actual" versions give a bounding box for the current text and the other two give a bounding box that includes any text in the font.
the distance from x,y to the top and bottom of the em square in the line box, in pixels. This gives you the font's "point" size measured in pixels.
the distance from x,y given baseline in pixels.
For example, suppose you want the actual bounding box for a specific piece of text:
ctx.font="normal normal 40px arial";
text="Hello Text World";
metric=ctx.measureText(text);
x=200;y=60;
boxL=x-metric.actualBoundingBoxLeft;
boxR=x+metric.actualBoundingBoxRight;
boxT=y-metric.actualBoundingBoxAscent;
boxB=y+metric.actualBoundingBoxDescent;
ctx.strokeRect(boxL,boxT,boxR-boxL,boxB-boxT);
ctx.fillText(text,x,y);
Where boxL,boxT is the top left corner of the bounding box and boxR,boxB is the bottom right corner. This produces a tight bounding box for the text you specify:
If you change the program to read:
boxL=x-metric.actualBoundingBoxLeft;
boxR=x+metric.actualBoundingBoxRight;
boxT=y-metric.fontBoundingBoxAscent;
boxB=y+metric.fontBoundingBoxDescent;
then you will get a bounding box that will contain any text from the font:
That is, there are characters in the font that will use the apparently extra space in the bounding box.
Note these programs currently only work on Chrome or Safari.
It is likely that support for typographic metrics will improve and it is worth checking the current state before making use of any of these properties. There are font libraries that can extract typographic metrics from font files and this might be the only way to perform accurate layout until browsers implement the full textMetric object.
There is also the direction property which can be set to one of:
-
ltr left to right
-
rtl right to left
-
inherit default
<ASIN:1871962625>
<ASIN:B07XJQDS4Z>
|