_images/toyplot.png

Text

Rich Text

Toyplot provides a limited subset of HTML5 markup that you can use to format your text (technically, Toyplot uses the XHTML5 Syntax for HTML5, but this distinction only shows-up when using the <br/> tag, discussed below). For example, you can create text with superscripts and subscripts:

import toyplot

canvas = toyplot.Canvas(width=600, height=150)
canvas.text(300, 100, "100<sup>-53</sup>", style={"font-size":"32px"});
100-53
canvas = toyplot.Canvas(width=600, height=150)
canvas.text(300, 100, "H<sub>2</sub>O", style={"font-size":"32px"});
H2O

There are a variety of tags to alter the inline appearance of text:

canvas = toyplot.Canvas(width=600, height=150)
canvas.text(
    300,
    100,
    "normal <b>bold</b> <i>italic</i> <strong>strong</strong> <em>emphasis</em> <small>small</small> <code>code</code>",
    style={"font-size":"24px"});
normal bold italic strong emphasis small code

And these tags can be nested to combine effects:

canvas = toyplot.Canvas(width=600, height=150)
canvas.text(300, 100, "foo <b>bar <i>baz <code>blah</code></i></b>", style={"font-size":"32px"});
foo bar baz blah

You can insert line breaks into your text using the <br/> tag:

canvas = toyplot.Canvas(width=600, height=200)
canvas.text(300, 100, "0.567832<br/><small>(243, 128, 19)</small>", style={"font-size":"16px"});
0.567832(243, 128, 19)

Finally, you can apply a limited subset of CSS styles within rich text:

canvas = toyplot.Canvas(width=600, height=200)
canvas.text(300, 100, "This is a <span style='fill:red;font-size:120%'>special</span> word.", style={"font-size":"16px"});
This is a special word.

Note that additional tags or style attributes currently aren’t allowed in rich-text. We expect that rich text capabilities will continue to expand in the future.

Keep in mind that you can use rich text formatting anywhere that text is displayed, including table cells, axis labels and tick labels. You can also use rich text in format strings for tick locators - as an example, the toyplot.locator.Log locator uses the <sup> tag to format tick labels for Logarithmic Scales.

Caveats

Because all text in Toyplot is parsed as XHTML5, there are a few important caveats to be aware of:

  • You must use <br/> or <br></br> to insert a line break ... <br> is not allowed.
  • You must escape < as &lt; and > as &gt; because otherwise they will be confused with XHTML5 tags.
  • You must escape & as &amp; because otherwise it will be confused with an XHTML5 entity.
canvas = toyplot.Canvas(width=600, height=200)
canvas.text(300, 100, "3 &lt; 4 &amp; 5 &gt; 6", style={"font-size":"16px"});
3 < 4 & 5 > 6

Alignment

By default, blocks of text in Toyplot are centered vertically and horizontally around their anchor. To illustrate this, the following figures display the anchor as a small black dot:

canvas = toyplot.Canvas(width=500, height=150)
axes = canvas.cartesian(show=False)
axes.text(0, 0, "Text!", style={"font-size":"24px"})
axes.scatterplot(0, 0, color="black", size=3);
Text!

To control horizontal alignment, use the CSS text-anchor property to alter the position of a line of text along its baseline, relative to the anchor:

canvas = toyplot.Canvas(width=500, height=300)
axes = canvas.cartesian(show=False, ymin=-1.5, ymax=1.5)

axes.vlines(0, color="lightgray")

axes.text(0, 1, "Centered", style={"text-anchor":"middle", "font-size":"24px"})
axes.scatterplot(0, 1, color="black", size=3)

axes.text(0, 0, "Left Justified", style={"text-anchor":"start", "font-size":"24px"})
axes.scatterplot(0, 0, color="black", size=3)

axes.text(0, -1, "Right Justified", style={"text-anchor":"end", "font-size":"24px"})
axes.scatterplot(0, -1, color="black", size=3);
CenteredLeft JustifiedRight Justified

In addition, the text can be shifted along its baseline in arbitrary amounts, using the -toyplot-anchor-shift property (note that this is non-standard CSS, provided by Toyplot for symmetry with the standard baseline-shift property which we will see below):

canvas = toyplot.Canvas(width=500, height=300)
axes = canvas.cartesian(show=False, ymin=-2.5, ymax=1.5)

axes.vlines(0, color="lightgray")

axes.text(0, 1, "Shifted +0px", style={"-toyplot-anchor-shift":"0", "text-anchor":"start", "font-size":"24px"})
axes.scatterplot(0, 1, color="black", size=3)

axes.text(0, 0, "Shifted +20px", style={"-toyplot-anchor-shift":"20px", "text-anchor":"start", "font-size":"24px"})
axes.scatterplot(0, 0, color="black", size=3)

axes.text(0, -1, "Shifted +40px", style={"-toyplot-anchor-shift":"40px", "text-anchor":"start", "font-size":"24px"})
axes.scatterplot(0, -1, color="black", size=3);

axes.text(0, -2, "Shifted -20px", style={"-toyplot-anchor-shift":"-20px", "text-anchor":"start", "font-size":"24px"})
axes.scatterplot(0, -2, color="black", size=3);
Shifted +0pxShifted +20pxShifted +40pxShifted -20px

Vertically, you can use the -toyplot-vertical-align property to alter the vertical position of a block of text relative to its anchor:

canvas = toyplot.Canvas(width=800, height=300)
axes = canvas.cartesian(show=False)

axes.hlines(0, color="lightgray")

axes.text(-1, 0, "Top", style={"-toyplot-vertical-align":"top", "font-size":"24px"})
axes.scatterplot(-1, 0, color="black", size=3)

axes.text(0, 0, "Middle", style={"-toyplot-vertical-align":"middle", "font-size":"24px"})
axes.scatterplot(0, 0, color="black", size=3)

axes.text(1, 0, "Bottom", style={"-toyplot-vertical-align":"bottom", "font-size":"24px"})
axes.scatterplot(1, 0, color="black", size=3)

axes.text(2, 0, "1st Baseline", style={"-toyplot-vertical-align":"first-baseline", "font-size":"24px"})
axes.scatterplot(2, 0, color="black", size=3)

axes.text(3, 0, "Last Baseline", style={"-toyplot-vertical-align":"last-baseline", "font-size":"24px"})
axes.scatterplot(3, 0, color="black", size=3);
TopMiddleBottom1st BaselineLast Baseline

Note that to see the difference between first-baseline and last-baseline requires a block of text with more than one line, since they align the anchor with the first and last line’s baselines respectively:

canvas = toyplot.Canvas(width=800, height=300)
axes = canvas.cartesian(show=False)

axes.hlines(0, color="lightgray")

axes.text(-1, 0, "Top<br/>Top", style={"-toyplot-vertical-align":"top", "font-size":"24px"})
axes.scatterplot(-1, 0, color="black", size=3)

axes.text(0, 0, "Middle<br/>Middle", style={"-toyplot-vertical-align":"middle", "font-size":"24px"})
axes.scatterplot(0, 0, color="black", size=3)

axes.text(1, 0, "Bottom<br/>Bottom", style={"-toyplot-vertical-align":"bottom", "font-size":"24px"})
axes.scatterplot(1, 0, color="black", size=3)

axes.text(2, 0, "1st Baseline<br/>1st Baseline", style={"-toyplot-vertical-align":"first-baseline", "font-size":"24px"})
axes.scatterplot(2, 0, color="black", size=3)

axes.text(3, 0, "Last Baseline<br/>Last Baseline", style={"-toyplot-vertical-align":"last-baseline", "font-size":"24px"})
axes.scatterplot(3, 0, color="black", size=3);
TopTopMiddleMiddleBottomBottom1st Baseline1st BaselineLast BaselineLast Baseline

As you can see, -toyplot-vertical-align alters the alignment of an entire block of text. If you wish to alter the vertical alignment of text within the block, you can use the standard CSS alignment-baseline property:

text = """<span style="alignment-baseline:alphabetic">Alphabetic</span>"""
text += """  <span style="alignment-baseline:middle">Middle</span>"""
text += """  <span style="alignment-baseline:central">Central</span>"""
text += """  <span style="alignment-baseline:hanging">Hanging</span>"""

canvas = toyplot.Canvas(width=600, height=300)
axes = canvas.cartesian(show=False)

axes.hlines(0, color="lightgray")

axes.text(-1, 0, text, style={"font-size":"24px"})
axes.scatterplot(-1, 0, color="black", size=3);
Alphabetic Middle Central Hanging

As you might expect, you can also shift text perpendicular to its baseline by arbitrary amounts, using baseline-shift. While you are free to use any of Toyplot’s supported CSS length units for the shift, percentages are especially useful, because they represent a distance relative to the font height:

canvas = toyplot.Canvas(width=700, height=300)
axes = canvas.cartesian(show=False)

axes.hlines(0, color="lightgray")

axes.text(-1, 0, "Shift -100%", style={"baseline-shift":"-100%", "font-size":"24px"})
axes.scatterplot(-1, 0, color="black", size=3)

axes.text(0, 0, "Shift 0%", style={"baseline-shift":"0", "font-size":"24px"})
axes.scatterplot(0, 0, color="black", size=3)

axes.text(1, 0, "Shift 66%", style={"baseline-shift":"66%", "font-size":"24px"})
axes.scatterplot(1, 0, color="black", size=3)

axes.text(2, 0, "Shift 100%", style={"baseline-shift":"100%", "font-size":"24px"})
axes.scatterplot(2, 0, color="black", size=3);
Shift -100%Shift 0%Shift 66%Shift 100%

Of course, you’re free to combine all these style properties in any way that you like.

One final thing to keep in mind is that -toyplot-anchor-shift and baseline-shift move the text relative to its baseline, not the canvas. This is important because it affects their behavior when text is rotated. In the following example, look carefully and note that the text with -toyplot-anchor-shift is shifted along its rotated baseline, not simply moved left or right on the canvas. Similarly, the baseline-shift text is shifted perpendicular to its rotated baseline, not merely up or down:

canvas = toyplot.Canvas(width=500, height=300)

axes = canvas.cartesian(grid=(1,3,0), xshow=False, yshow=False, label="default")
axes.vlines(0, color="lightgray")
axes.text(0, 0, "a + b", angle=45, style={"font-size":"24px"})
axes.scatterplot(0, 0, color="black", size=3)

axes = canvas.cartesian(grid=(1,3,1), xshow=False, yshow=False, label="-toyplot-anchor-shift")
axes.vlines(0, color="lightgray")
axes.text(0, 0, "a + b", angle=45, style={"-toyplot-anchor-shift":"20px", "font-size":"24px"})
axes.scatterplot(0, 0, color="black", size=3)

axes = canvas.cartesian(grid=(1,3,2), xshow=False, yshow=False, label="baseline-shift")
axes.vlines(0, color="lightgray")
axes.text(0, 0, "a + b", angle=45, style={"baseline-shift":"-20px", "font-size":"24px"})
axes.scatterplot(0, 0, color="black", size=3);
a + bdefaulta + b-toyplot-anchor-shifta + bbaseline-shift

Coordinate System Text

In addition to all the above, Cartesian Coordinates and Numberline Coordinates provide additional parameters that affect text layout and alignment.

First, ticks and labels have a parameter location that controls whether they appear above or below an axis:

canvas = toyplot.Canvas(width=600, height=200)

numberline1 = canvas.numberline(grid=(2, 1, 0))
numberline1.axis.ticks.location="above"

numberline2 = canvas.numberline(grid=(2, 1, 1))
numberline2.axis.ticks.location="below"
-0.50.00.5-0.50.00.5

Note that although the location can be specified explicitly, in most cases the defaults should just work ... note how the location of the Y axis ticks and labels automatically changes from “above” to “below” when the Y axis spine is repositioned in the following example:

canvas = toyplot.Canvas(width=600, height=300)

axis1 = canvas.cartesian(grid=(1, 2, 0))

axis2 = canvas.cartesian(grid=(1, 2, 1))
axis2.y.spine.position="high"
-0.50.00.5-0.50.00.5-0.50.00.5-0.50.00.5

In addition to positioning tick labels above or below an axis, you can also adjust their offset - the distance from the axis spine to the text anchor. The offset parameter is specified so that increasing values move text further from the axis, whether its location is above or below - in the following example, note that both offsets are positive:

canvas = toyplot.Canvas(width=600, height=300)

axis1 = canvas.cartesian(grid=(1, 2, 0))
axis1.y.ticks.labels.offset=30

axis2 = canvas.cartesian(grid=(1, 2, 1))
axis2.y.spine.position="high"
axis2.y.ticks.labels.offset=30
-0.50.00.5-0.50.00.5-0.50.00.5-0.50.00.5

The default text alignment parameters have been carefully chosen to provide good quality layout even if you change the label font size, and regardless of label location:

canvas = toyplot.Canvas(width=600, height=400)

numberline1 = canvas.numberline(grid=(4, 1, 0))
numberline1.axis.ticks.location="above"

numberline2 = canvas.numberline(grid=(4, 1, 1))
numberline2.axis.ticks.location="above"
numberline2.axis.ticks.labels.style = {"font-size":"16px"}

numberline3 = canvas.numberline(grid=(4, 1, 2))
numberline3.axis.ticks.location="below"

numberline4 = canvas.numberline(grid=(4, 1, 3))
numberline4.axis.ticks.location="below"
numberline4.axis.ticks.labels.style = {"font-size":"16px"}
-0.50.00.5-0.50.00.5-0.50.00.5-0.50.00.5

Similarly, alignment parameters are automatically adjusted when you rotate tick labels, adjusting the anchor and baseline to provide good results:

import numpy

colormap = toyplot.color.brewer.map("BlueRed", domain_min=0, domain_max=1)

canvas = toyplot.Canvas()
numberline = canvas.color_scale(x1="50%", x2="50%", y1="-10%", y2="10%", colormap=colormap)
numberline.axis.ticks.show = True
numberline.axis.ticks.labels.angle=-90
0.00.51.0

Of-course, you are free to override any of these behaviors. For example, suppose we use rich text to add multi-line tick labels to the preceding example:

def format_color(color):
    return "(%.2f, %.2f, %.2f)" % (color["r"], color["g"], color["b"])

values = numpy.linspace(colormap.domain.min, colormap.domain.max, 4)
labels = ["%.4f<br/><small>%s</small>" % (value, format_color(colormap.color(value))) for value in values]
locator = toyplot.locator.Explicit(values, labels)
canvas = toyplot.Canvas()
numberline = canvas.color_scale(x1="50%", x2="50%", y1="-10%", y2="10%", colormap=colormap)
numberline.axis.ticks.show = True
numberline.axis.ticks.labels.angle=-90

numberline.axis.ticks.locator = locator
numberline.axis.ticks.labels.style = {"font-size":"16px"}
0.0000(0.02, 0.19, 0.38)0.3333(0.65, 0.81, 0.89)0.6667(0.97, 0.72, 0.60)1.0000(0.40, 0.00, 0.12)

We might choose to override the defaults to center each line of text within the label:

canvas = toyplot.Canvas()
numberline = canvas.color_scale(x1="50%", x2="50%", y1="-10%", y2="10%", colormap=colormap)
numberline.axis.ticks.labels.angle=-90
numberline.axis.ticks.show = True
numberline.axis.ticks.locator = locator
numberline.axis.ticks.labels.style = {"font-size":"16px"}

numberline.axis.ticks.labels.style = {"text-anchor":"middle"}
numberline.axis.ticks.labels.offset = 60
0.0000(0.02, 0.19, 0.38)0.3333(0.65, 0.81, 0.89)0.6667(0.97, 0.72, 0.60)1.0000(0.40, 0.00, 0.12)