Text in generativepy

By Martin McBride, 2022-01-04

Categories: generativepy generativepy tutorial


The generativepy drawing module allows you to add text to your images, using the Text class.

You can create text using any font on your system, and of course, choose whatever size and colour you wish.

Text behaves like any other shape, so as well as filling the text you can also:

  • Stroke the text, using solid or dashed lines.
  • Align the text.
  • Create a Path based on the text shape, that can be reused.
  • Use the text shape as a clip path.

Here are some further text tutorials:

Text font, size and colour

This sample code creates text in various styles:

from generativepy.drawing import make_image, setup
from generativepy.color import Color
from generativepy.geometry import Text


def draw(ctx, pixel_width, pixel_height, frame_no, frame_count):
    setup(ctx, pixel_width, pixel_height, background=Color(1))

    Text(ctx).of("Filled Times", (100, 100)).font("Times").size(40).fill(Color('blue'))
    Text(ctx).of("Filled Arial", (100, 150)).font("Arial").size(40).fill(Color('red'))
    Text(ctx).of("Small", (100, 180)).font("Arial").size(20).fill(Color('darkgreen'))
    Text(ctx).of("Large", (100, 240)).font("Arial").size(60).fill(Color('magenta'))
    Text(ctx).of("Stroke", (100, 310)).font("Arial").size(60).stroke(Color('black'), 4)
    Text(ctx).of("Fill Stroke", (100, 380)).font("Arial").size(60)\
                                           .fill(Color('blue')).stroke(Color('red'), 2)
    Text(ctx).of("Dashed", (100, 450)).font("Arial").size(60).stroke(Color('black'), 3, dash=[4])


make_image("text-drawing.png", draw, 500, 500)

This code is available on github in tutorial/shapes/text-drawing.py.

Here is the result:

The top two lines of text are drawn with this code:

    Text(ctx).of("Filled Times", (100, 100)).font("Times").size(40).fill(Color('blue'))
    Text(ctx).of("Filled Arial", (100, 150)).font("Arial").size(40).fill(Color('red'))

We set up a basic text object like this:

  • Text(ctx) creates the object
  • of(text, pos) sets the text and position. For example, in the first line, we set the text to "Filled Times" and the position to (100, 100). This means that the baseline of the start of the text is at that position.
  • font(fontname) selects the font, for example Times.
  • size(40) sets the size of the text. The value 40 makes the text approximately 40 units high. In this case, since we are using default scaling, the size is measured in pixels. The exact size of the text depends on the font you use.
  • fill fills the text in the selected colour.

So the code above displays "Filled Times" in Times font in blue, and "Filled Arial" in Arial font in red.

The next code changes the size of the text:

    Text(ctx).of("Small", (100, 180)).font("Arial").size(20).fill(Color('darkgreen'))
    Text(ctx).of("Large", (100, 240)).font("Arial").size(60).fill(Color('magenta'))

All we have done here is change the size. The text "Small" is displayed in 20 pixel high text, the text "Large" is displayed in 60 pixel high text.

Finally, we can stroke the text:

    Text(ctx).of("Stroke", (100, 310)).font("Arial").size(60).stroke(Color('black'), 4)
    Text(ctx).of("Fill Stroke", (100, 380)).font("Arial").size(60)\
                                           .fill(Color('blue')).stroke(Color('red'), 2)
    Text(ctx).of("Dashed", (100, 450)).font("Arial").size(60).stroke(Color('black'), 3, dash=[4])

As far as generativepy is concerned, a Text object is just a shape. The first case strokes the text "Stroke" using a black, 4-pixel outline.

The second case fills and strokes the text "Fill Stroke".

The third case strokes the text "Dashed", setting a dash list so that the outline is dashed.

Alignment

For any piece of text, we can mark several key positions:

Horizontally:

  • Left is the position of the leftmost point that is marked by the text.
  • Right is the position of the rightmost point that is marked by the text.
  • Centre is halfway between the left and right positions.

Vertically:

  • Top is the position of the highest point that is marked by the text.
  • Bottom is the position of the lowest point that is marked by the text.
  • Middle is halfway between the top and bottom positions.
  • Baseline is the line position of the text. This is the line the text sits on. The tails of characters such as g or q go below the baseline.

We can align the text to any position, horizontally and vertically. The default alignment is left and baseline. Here is an example of different types of alignment:

from generativepy.drawing import make_image, setup
from generativepy.color import Color
from generativepy.geometry import Text, Circle


def draw(ctx, pixel_width, pixel_height, frame_no, frame_count):
    setup(ctx, pixel_width, pixel_width, background=Color(0.8))

    Text(ctx).of("Left", (50, 50)).font("Times").size(20).align_left().align_baseline().fill(Color('blue'))
    Text(ctx).of("Aligned", (50, 70)).font("Times").size(20).align_left().align_baseline().fill(Color('red'))
    Text(ctx).of("Text", (50, 90)).font("Times").size(20).align_left().align_baseline().fill(Color('blue'))

    Text(ctx).of("Centre", (250, 50)).font("Times").size(20).align_center().align_baseline().fill(Color('blue'))
    Text(ctx).of("Aligned", (250, 70)).font("Times").size(20).align_center().align_baseline().fill(Color('red'))
    Text(ctx).of("Text", (250, 90)).font("Times").size(20).align_center().align_baseline().fill(Color('blue'))

    Text(ctx).of("Right", (450, 50)).font("Times").size(20).align_right().align_baseline().fill(Color('blue'))
    Text(ctx).of("Aligned", (450, 70)).font("Times").size(20).align_right().align_baseline().fill(Color('red'))
    Text(ctx).of("Text", (450, 90)).font("Times").size(20).align_right().align_baseline().fill(Color('blue'))

    Circle(ctx).of_center_radius((190, 200), 2).fill(Color(0, 0, 1))
    Text(ctx).of("gTop", (200, 200)).font("Times").size(20).align_left().align_top().fill(Color('black'))

    Circle(ctx).of_center_radius((190, 250), 2).fill(Color(0, 0, 1))
    Text(ctx).of("gMid", (200, 250)).font("Times").size(20).align_left().align_middle().fill(Color('black'))

    Circle(ctx).of_center_radius((190, 300), 2).fill(Color(0, 0, 1))
    Text(ctx).of("gBase", (200, 300)).font("Times").size(20).align_left().align_baseline().fill(Color('black'))

    Circle(ctx).of_center_radius((190, 350), 2).fill(Color(0, 0, 1))
    Text(ctx).of("gBottom", (200, 350)).font("Times").size(20).align_left().align_bottom().fill(Color('black'))


make_image("text-align.png", draw, 500, 400)

This code is available on github in tutorial/shapes/text-align.py.

Here is the result:

You can use the align function as an alternative:

    Text(ctx).of("Centre", (2.5, 0.5)).font("Times").size(0.2).align(CENTER, BASELINE).fill(Color('blue'))

Flip

The setup function in the drawing module has an option to flip the y-axis so that y-coordinates increase from bottom to top (rather than the default top to bottom). This means the coordinate system matches the system normally used in mathematics.

The only problem with flipping the y-axis is that it causes text to be drawn upside down.

If you call the flip method on a Text object, it will cause the text to be flipped too, so the text will appear correctly.

Paths and clipping

You can create a path from some text. That path can be re-drawn multiple times, using different sizes, colours and styles if you wish. For an example, see the Path tutorial.

You can also use text as a clipping path. In that case, anything you draw will be clipped to the text. This is a good way to create text with a fancy fill, see the clipping tutorial.

See also

If you found this article useful, you might be interested in the book NumPy Recipes or other books by the same author.

Join the PythonInformer Newsletter

Sign up using this form to receive an email when new content is added:

Popular tags

2d arrays abstract data type alignment and angle animation arc array arrays bar chart bar style behavioural pattern bezier curve built-in function callable object chain circle classes clipping close closure cmyk colour combinations comparison operator comprehension context context manager conversion count creational pattern data science data types decorator design pattern device space dictionary drawing duck typing efficiency ellipse else encryption enumerate fill filter font font style for loop formula function function composition function plot functools game development generativepy tutorial generator geometry gif global variable gradient greyscale higher order function hsl html image image processing imagesurface immutable object in operator index inner function input installing iter iterable iterator itertools join l system lambda function latex len lerp line line plot line style linear gradient linspace list list comprehension logical operator lru_cache magic method mandelbrot mandelbrot set map marker style matplotlib monad mutability named parameter numeric python numpy object open operator optimisation optional parameter or pandas partial application path pattern permutations pie chart pil pillow polygon pong positional parameter print product programming paradigms programming techniques pure function python standard library radial gradient range recipes rectangle recursion reduce regular polygon repeat rgb rotation roundrect scaling scatter plot scipy sector segment sequence setup shape singleton slice slicing sound spirograph sprite square str stream string stroke structural pattern subpath symmetric encryption template tex text text metrics tinkerbell fractal transform translation transparency triangle truthy value tuple turtle unpacking user space vectorisation webserver website while loop zip zip_longest