ImageMagick v6 Examples --
Text to Image Handling

Index
ImageMagick Examples Preface and Index
Text Operators in ImageMagick - Converting text into an image
Label - Simple Text Label
Caption - Word Wrapped Labels
Text - Plain Text Pages of Images
Postscript/PDF - Pre-formatted Text and Graphics
Draw - Draw Text on Existing Canvas
Annotate - Better Text Drawing
Special Escape Characters in Text Arguments
Resolution, Pointsize, and Actual Font Size
Determining Font Metrics - Where is the baseline of a font
Creating Lines of Mixed Font Styles - Using IM for text formatting
Creating text labels, or adding text to images is probably one of the most basic and common operations for which ImageMagick is used. It is also one of the most simplest, but with scope for some very fancy results. As such this is a good place to start our exploration of IM's capabilities.


Text Operators in ImageMagick

ImageMagick has a lot of different ways in which you can draw text within an image, highlighting the versatility of the image processing library. This page details specific methods and styles of drawing text.

What you have to keep in mind as you study these examples is that ImageMagick is primarily a image converter and modifier. As such each of methods provided are simple text drawing operators, such as adding labels and copyright messages to images. See Annotating Images.

All the text operators also understand and use a set of standard text processing settings such as, the "-font", "-pointsize" to use. Also the "-fill"color setting and for more complex text drawing the "-strokewidth", "-stroke" and "-undercolor" colors.

And finally the newer "-kerning" and "-interword-spacing" modifiers.

What ImageMagick is not, is a full formatted text and document processor. If you want heavy text processing, you are better off using a full interactive word-processor, or batch text formatter like "TeX" (or one of its flavors. The output of these programs (generally postscript format) can then be converted into an image and further modified by ImageMagick. That is, use the right tool for the right job.

That said some mixed font handling can be done with difficultly. For a starting point look at Creating Lines of Mixed Font Styles, near the bottom of this page.

Now, lets now look at the basic ways you can convert text into images, then later we'll look at generating some interesting font effects.

Label - Simple Text Label

Creating a font image using a "label:" image, is the more typical way of drawing a font quickly in ImageMagick. The biggest advantage is that generates its own canvas according the the current "-background" setting. For example here is a typical generated label.

  convert -background lightblue -fill blue \
          -font Candice -pointsize 72 label:Anthony \
          label.gif
[IM Output]

The above is probably the most typical usage of label, with a font selection and "-pointsize" defining the results. But it by far the least interesting way of generating text labels.

Label to Size

If the "-size" setting has been specified with both a width and a height (but not a "-pointsize", the label will be created at that size. Also the size of the draw text will be adjusted to best fit the final given label size!

  convert -background lightblue -fill blue  -font Candice \
          -size 165x70  label:Anthony     label_size.gif
[IM Output]

If a "-size" setting has been given, any "-pointsize" setting for the font is currently ignored! This may change, so don't count on it.

As you can see by setting a "-size" setting, you could end up with some extra space to the right or below the image. You can adjust the position of the label in that extra space by adjusting the "-gravity" setting.

  convert -background lightblue -fill blue  -font Candice \
          -size 165x70 -gravity center label:Anthony     label_gravity.gif
[IM Output]

Of course if you do not set a "-size" for the label, no extra space will be available in the generated "label:" for "-gravity" to use, making it rather useless.

Now for the best news. If the "-size" setting you give only contains just width or the height for the label, the text "-pointsize" will be set to best fit that dimension, and the other missing dimension will be auto-adjusted to best fit that text!

  convert -background lightblue -fill blue -font Candice \
          -size 165x  label:Anthony     label_size_w.gif
[IM Output]

Basically that means the above "label:" will always be 160 pixels wide, with the largest font size for that width, and the height of the label adjusted to suit.

Using a "-size" setting with "label:" is however rarely done, as users generally prefer to specify the "-pointsize" of the font.

If you want to use a specific "-pointsize" remember, you MUST turn off the size setting using "+size".

Stroke Outlines

As of IM v6.3.2 you can also use the "-stroke", "-strokewidth", and "-undercolor" (on older IM's use "-box" instead) setting with "label:" or "caption:".

   convert -background white -fill dodgerblue  -font Candice \
           -strokewidth 2  -stroke blue   -undercolor lightblue \
           -size 165x70 -gravity center label:Anthony     label_color.gif
[IM Output]

For more on these extra settings see Undercolor Box below, and Stroke, StrokeWidth in the drawing section.

At this time, you can not use tiling images defined using "-tile", "-fill", "-background", and "-origin", with either "label:" or "caption:". Only solid colors can be used. Attempting to so will just produce a undefined (black) color.

Inter-character Kerning

As of IM v6.4.7-8 you can use "-kerning" to insert extra inter-character space between each letter in text strings. For example

  convert -pointsize 12               label:Anthony   label_kerning_0.gif
  convert -pointsize 12 -kerning 1    label:Anthony   label_kerning_1.gif
  convert -pointsize 12 -kerning 2.5  label:Anthony   label_kerning_2.gif
  convert -pointsize 12 -kerning 5    label:Anthony   label_kerning_5.gif
  convert -pointsize 12 -kerning -1   label:Anthony   label_kerning-1.gif
[IM Output] [IM Output] [IM Output] [IM Output] [IM Output]

Note that the actual kerning value can be a floating point value, or even a negative value.

For another example of using a negative "-kerning" value see the Joined Compound Font example.

Inter-word Spacing

Also as of IM v 6.4.8-0 the option "-interword-spacing" can be used to modify the size of a space character used between words. For example

  convert                        label:'I Love IM!'  label_wspace_off.gif
  convert -interword-spacing 1   label:'I Love IM!'  label_wspace_1.gif
  convert -interword-spacing 10  label:'I Love IM!'  label_wspace_10.gif
  convert -interword-spacing 25  label:'I Love IM!'  label_wspace_25.gif
[IM Output] [IM Output] [IM Output] [IM Output]

Note how you can not only increase the size of a space character between the words but also decrease the default size.

Note however that spaces will cause the words to be re-aligned to pixel boundaries (unlike Inter-character Kerning above) so the output of a label with spaces set to zero will still be different to a label that does not contain any spaces at all.

Both the Inter-character Kerning and the Inter-word Spacing will also effect the results of IM's ability to automatically fit a text string to a specific sized image.

  convert -size 150x                       label:'I Love IM!' label_wsize_of.gif
  convert -size 150x -interword-spacing 25 label:'I Love IM!' label_wsize_25.gif
  convert -size 150x -interword-spacing 50 label:'I Love IM!' label_wsize_50.gif
[IM Output] [IM Output] [IM Output]

What is happening is that by setting the "-interword-spacing" the 'space' character size no longer changes as IM attempted to work out the best "-pointsize" to use. As a consequence the larger the "-interword-spacing" the smaller sized font that is needed to actually to fit the line of text into the same specified image width.

A negative value can be used, and can in fact to make words overlap, or produce unusual effects using specific characters and fonts. But make it too negative and undefined behaviours can creep in. Please use with caution.

While the above is not an example of text justification (though it looks like it), you can use these options as a starting point to providing proper text justification. Though if you really need that you may be better off looking at pre-formated Postscript or better still SVG drawing to generated justified text.

Multiple Line Labels

The "label:" generator can (as of IM version 6.2.5) generate multi-line labels.

  convert -background lightblue  -fill blue  -font Ravie -pointsize 20 \
          label:'ImageMagick\nRules - OK!'     label_multiline.gif
[IM Output]

As you can see "label:" understands the use of '\n' as representing newlines. This means you may have to pre-process your input text to ensure that any special characters are escaped when placing the data on the command line. See Special Escape Characters in Text Arguments below for more details.

As "-gravity" also effects "label:" generation (as of IM version 6.2.6), you can use it to 'justify' multi-line labels.

  convert -background lightblue -fill blue -font Corsiva -pointsize 24 \
          -gravity center    label:'ImageMagick\nExamples\nby Anthony' \
          label_centered.gif
[IM Output]

One important feature of IM is that it can read the text data to use from a file. This is done by prefixing the filename with an 'at' character '@', and using this as the string argument.

For example, here we create a label from my workstations 'message of the day' file...

  convert -background lightblue  -fill blue \
          label:@/etc/motd   label_file.gif
[IM Output]

You can also read the text for a label from the standard input pipeline. For example here I convert the output of a quote generator to a multi-line label.

  mesgs ImageResolution |\
    convert -background lightblue  -fill blue \
            label:@-   label_file_multiline.gif
[IM Output]

Notice that the filename I used was just a '-' character. This means that the file is to be read from standard input. Remember you can use the '@filename' to read ANY command line string arguments into IM. This includes all the other text input methods given below. However it can only be used to replace the whole string argument, not a part of a string argument.

Note also that in the above examples, an extra blank line was added to the label image. This blank line is caused by a final newline in the input text file. Unless you somehow strip the final newline from the input file (see caption: example below for method to fix this), "label:" will always have this blank line from input text files.

Most older versions of IM (before v6.2.5), do not handle multiple line labels. In these versions the lines would have been appended together to form a single, very very long line.

Vertical Labels

Of course you can also add newlines to the input text too. For example here I take a simple word and add a newline between each and every letter, to create some centered vertical text.

  echo -n "Vertical" | sed 's/./&@/g; s/@$//' | tr '@' '\012' |\
    convert -background lightblue -fill blue -font Ravie -pointsize 24 \
            -gravity center    label:@-   label_vertical.gif

Note that the "sed" command adds a '@' character after every character, except at the end of the string. The "tr" then replaces the '@' characters with newlines. It also assumes the input text does not end with a newline, which would cause an extra blank line to be added at the bottom of the resulting image.

[IM Output]

Users running linux, and thus using the GNU version of the "sed" command can remove the "tr", and replace the '@' with '\n' in the sed command directly.

Label Image Bounds

When using some exotic fonts, the font may use extended characters, and in the past IM had lots of trouble creating labels for these fonts. That is the text overflows the provided canvas.

For example here are two capital letters in a 'LokiCola' font reminiscent of a certain famous softdrink.

  convert -background lightblue -fill blue -font LokiCola -pointsize 64 \
          label:HC  label_overflow.gif
[IM Output]

As you can see IM manages to contain this font in a label without cutting of the fonts leader or trailing graphics.

Before IM v6.3.2, "label:" would have chopped of the 'H' lead-in and parts of the tails from both characters, in the above example.

The reason this problem existed is because the fonts 'glyphs' or character description draw outside the the fonts defined boundaries for specific letters, allowing them to overlap (generally either above or below) the other characters within the font.

This is a problem with the way the font itself is designed and defined, and was not the fault of IM, though IM now handles these odd situations in the best interests of the users. In other situations it could still be a problem, and one that can not be solved simply due to multi-line text interactions. For more information see Bounding Box Overflow examples below, for a more precise description.

Unicode or UTF8 Format Text Labels

This method of supplying string arguments to IM is very important as it allows you to do things which ordinarily could be very difficult to do from the command line. Specifically handling 'unicode text', or selecting specific characters using character codes.

Now if you can type unicode characters into commands or scripts you can use them directly..

  convert -background lightblue -fill blue -pointsize 32 \
          label:' é è à ù ç Ö ÿ ‘ ’ “ ” ° ² ³ € x ÷ '    label_i8n.gif
[IM Output]

However few people have keyboards or editors properly set up to handle unicode character input. Even if you can not directly type unicode characters, one simple solution is to just 'copy-n-paste' the desired characters from some existing UTF-8 text file, or web page.

If the UTF-8 text you wanting to draw has already been generated you can read it either directly from a file using '@filename'. For example here I create a Chinese label from a UTF-8 encoded Chinese text file (without a final newline in the file).

  convert -background lightblue -fill blue -pointsize 48 \
          -font ZenKaiUni label:@chinese_words.utf8   label_utf8.gif
[IM Output]

The font used in the above example is a special one, with the full set of Chinese Glyphs defined, such as the fedora linux fonts 'SimSun' (or in the font file "gkai00mp.ttf"), "ZenKaiUni" ("ukai.ttf") or "ShanHeiSunUni" ("uming.ttf" or "zysong.ttf" or "bsmi00lp.ttf"). The special script "imagick_type_gen" was used to find, extract the fonts proper name, and add the font into an ImageMagick "type.xml" configuration file.

Note that the windows font 'Mincho' (used in a later example) also defines many of the Chienese Glyphs but incompletely. If you use it with the above you will get some question marks where the glyph is undefined.

We can also generate UTF-8 strings from unicode character codes using the 'GNU' "printf" program (on linux systems) to convert unicode numbers to the specific UTF-8 encoded string, in this case proper typeset opening and closing quotes (again no final newline in the UTF-8 input).

Here for example I generate the UTF-8 text using unicode character codes, and feed it using a command pipeline (read from 'stdin' using "@-"), rather than from an actual file.

  env LC_CTYPE=en_AU.utf8 \
    printf "\u2018single\u2019 - \u201Cdouble\u201D" | \
      convert -background lightblue -fill blue -pointsize 36 \
              label:@-  label_quotes.gif
[IM Output]

On other systems (like Mac OSX and Windows) you can use the perl "printf" to output a UTF-8 encoded character string from unicode character codes.

  perl -e 'binmode(STDOUT, ":utf8"); \
    print "\x{201C}Unicode \x{2018}\x{263A}\x{2019} Please\x{201D}";' |\
      convert -background lightblue -fill blue -pointsize 36 \
              label:@-  label_unifun.gif
[IM Output]

For more information and to look up the unicode character codes for various languages and symbols see Unicode Character Code Charts.

Not only can unicode characters contain international characters, but with the right font you can also use special 'symbol' sets it defines. The most famous these is the 'DingBats' symbol font. This font has become so common that it is now part of the standard Unicode fontset.

For example here I extract the first 24 characters of the 'DingBats' unicode symbol area using a special "graphics_utf" shell script I wrote to generate a 'block' of unicode characters as UTF-8 text.

  graphics_utf -N 2701 2718 |\
    convert -font Mincho -pointsize 32 label:@-   label_dingbats.gif
[IM Output]

The question marks in the above are specific characters which were not defined by either unicode, or in the windows 'Mincho' font. More specifically the symbol that was part of the original 'dingbat' font is present in unicode, but using another unicode character code, than the expected dingbat code. See Dingbats Unicode Specification Chart for more detail, and its referal to the correct unicode charcater to use for the 'missing' dingbat characters.

Rather than a question mark, many fonts would just print a box or a blank character for such undefined characters. If you see too many such characters, or missing characters in your output, you probably should be using a different font.

Other symbol sets also available as part of the enormous unicode character font include: Tolkan runic symbols, mathematical symbols, roman numerals, arrows, Braile and technical symbols. It is a large set to explore, and the "graphics_utf" shell script can help you explore it.

Here is another example of unicode characters which the Microsoft 'Mincho' font can render. In this case from the "Miscellaneous Symbols" section...

  graphics_utf -N 2620 2630 |\
    convert -font Mincho   -pointsize 40 label:@- label_misc.gif
[IM Output]

Symbol Fonts

More commonly, symbol fonts are much smaller than a unicode font, as they replace only the normal standard ASCII characters (letters and numbers) with specific shapes and images, though sometimes they also have more symbols in the Latin meta-characters area.

For example, one font I rather like to use symbols from is "WebDings", as it has a very nice 'curvy heart' symbol in the position of the letter 'Y'...

  convert -size 20x20 -gravity center -font WebDings label:Y label_heart_20.gif
  convert -size 40x40 -gravity center -font WebDings label:Y label_heart_40.gif
  convert -size 60x60 -gravity center -font WebDings label:Y label_heart_60.gif
  convert -size 80x80 -gravity center -font WebDings label:Y label_heart_80.gif
[IM Output] [IM Output] [IM Output] [IM Output]

The important thing to remember is that symbol fonts are actually a special type of Vector Image Format containing multiple images (one for each character). This means it should allow you 'draw' a character, shape or symbol at any size, using the controls provided by "-size", "-pointsize", and "-density".

Here is some other symbols I have found in various symbol fonts...

  convert -pointsize 48 -font WebDings label:' " _ ~ ) - '  label_webdings.gif
  convert -pointsize 48 -font LittleGidding label:' x o w ' label_ltgidding.gif
  convert -pointsize 48 -font WingDings2      label:'ab'    label_wingdings2.gif
  convert -pointsize 48 -font Zymbols  label:' ? , - I Z '  label_zymbols.gif
  convert -pointsize 48 -font TattoEF  label:' B Y D I H '  label_tatooef.gif
  convert -pointsize 48 -font SoundFX  label:' V 3 t f 9 '  label_soundfx.gif
[IM Output] [IM Output] [IM Output] [IM Output] [IM Output] [IM Output]

This is only a small sample of what is available. Hugh libraries of every symbol, shape, or image, is available on the WWW for your to browse and download.

Remember that each drawn character has two separate parts that can be drawn: the 'filled' area (which I showed above), and an 'stroke' or outline, which can look very different to the filled area. Each of these areas can be drawn separately, or in different colors, so it may be a good idea to examine a promising symbol or shape more closely, in a number of ways. You may get a very surprising result. See Compound Fonts, Stroke for some examples of doing this.

Many creators of symbol fonts just generate the shapes using a simple scanner and convertor, without any proper design or cleaning of the image or shape, so that it can be used at any pointsize or density. Caution is recommended when looking at such 'scanned' fonts.

The last font shown above is one such example of a 'scanned' font, giving it a poor looking 'dotty' quality, when compared to the other fonts.

Caption - Word Wrapped Label

The "caption:" image from text input generator, is in most respects exactly like "label:" except that instead of expanding the size of the text to fit a specified "-size" setting, it word wraps any long lines that do not fit into the specified "-size" width.

The "-size" setting is not optional however and must at least specify a maximum width in pixels.

For example here is a caption of a long line that will not fit into the width specified.

  convert -background lightblue  -fill blue  -font Corsiva -pointsize 36 \
          -size 320x   caption:'This is a very long caption line.' \
          caption.gif
[IM Output]

By default the text is all left justified, however as of IM version 6.2.0, "caption:" respects "-gravity" for text justification purposes.

  convert -background lightblue  -fill blue  -font Candice -pointsize 40 \
          -size 320x  -gravity Center  caption:'ImageMagick Rules OK!' \
          caption_centered.gif
[IM Output]

If you do provide a height as well as a width to the "-size" setting, then the image height will be set to that height as well. You can then also use the "-gravity" setting, to position the text vertically.

  convert -background lightblue  -fill blue  -font Gecko -pointsize 32 \
          -size 320x100  -gravity South caption:'Captions at their height!' \
          caption_height.gif
[IM Output]

Please note however that if the text will not fit (height-wise) into the "-size" you specified with the given "-pointsize", then the text will overflow the box. The current "-gravity" setting will naturally determine what part of the text is cut off.

For example this is exactly the same as the previous example, but will an image "-size" that is too small for the result.

  convert -background lightblue  -fill blue  -font Gecko -pointsize 32 \
          -size 320x60  -gravity South caption:'Captions at their height!' \
          caption_height_toosmall.gif
[IM Output]

As of IM v6.3.2, if you provide both the width and the height of the final image, but do not define the "-pointsize" of the font (or turn off the pointsize with "+pointsize"), IM will attempt to automatically adjust the size of the font so as to best fill the "-size" of the image you requested.

For example, here I ask ImageMagick to fill a fairly large area...

  convert -background lightblue -fill blue -font Candice -size 320x140 \
          caption:'This text is resized to best fill the space given.' \
          caption_filled.gif
[IM Output]
And now a much smaller thinner area, for the same font and text string.

  convert -background lightblue -fill blue -font Candice -size 80x110 \
          caption:'This text is resized to best fill the space given.' \
          caption_filled_sm.gif
[IM Output]

This is extremely useful to fit a unknown bit of text into a given space, without it overflowing the area bounds. However internally it is equivalent to running caption multiple times as IM searches for the right point size to use to best fill the given space. In other words it can often be 10 times or more slower than if you supplied a specific "-pointsize" to use.

The "caption:" image operator (as of IM v6.2.5) understands the use of the '\n' shell escape (and thus you need to double backslash '\\' to escape backslashes), as meaning a new line or paragraph. Before this version separate paragraphs would have to be processed by separate "caption:" operations.

  convert -background lightblue -fill blue \
          -font Ravie -pointsize 24 -size 360x \
          caption:"Here I use caption to wordwrap.\nTwo separate lines." \
          caption_multi_line.gif
[IM Output]

You can read the text to be drawn from a file, or standard input (from a previous pipeline command), using the '@' filename prefix, just as as we can with "label:".

  mesgs FilePermissions |\
    convert -background  lightblue  -fill blue  -pointsize 12 \
            -size 320x  caption:@-  caption_file.gif
[IM Output]

As you can see newlines in the input text will (as of IM v6.2.5) will be treated as paragraph separators. This includes any final newlines in the input file. Of course "label:" will not word wrap the lines, but preserve them.

If you really want a file to be treated as a single paragraph, then you will need to replace the newline characters with a space character, so your text is all on one line. For example, here we take the same text, but replace the line feeds with spaces, then replace any multiple spaces between the words, with a single space...

  mesgs FilePermissions |      tr '\012' ' ' | sed 's/  */ /g' |\
    convert -background  lightblue  -fill blue  -pointsize 12 \
            -size 320x  caption:@-  caption_one_line.gif
[IM Output]

As you can see this works a lot better.

However often what you want is to treat a blank line as a paragraph break. That means you need to remove all newlines, except those involved with blank lines. Here is a special "sed" command to convert such text into the format needed by "caption:". In this case the text is the first page of the "convert" manpage.

  man convert | col -b | expand | \
    sed '/^$/d; :loop y/\n/ /; N; /\n$/! b loop;  s/   */ /g; s/^ //' |\
      head |    convert -size 400x  caption:@-  caption_manual.gif
[IM Output]

There is no 'justified' text to image converter in IM at this time.

Text - Plain Text as Formatted Pages of Images

The "text:" input format is designed to convert plain text into images consisting one image per page of text. It is the 'paged text' input operator of ImageMagick.

In other words its purpose is to convert the larger preformatted text files into pages in much the same way that printers print plain text onto separate pieces of paper.

Do not confuse the "text:" file input format with the similar "txt:" input format. The latter will first attempt to read a special 'ascii text image' format of IM.

That doesn't mean that a plain text file with a ".txt" will fail. In fact such a file will probably be converted as you would expect, as the "txt:" file format will automatically fall back to the "text:" format if IM does not recognise it as a 'IM pixel enumeration' format image.

Handling text in this way however had a number of problems. First the text is drawn onto a large canvas, leave you with the problem of removing the unused space. The other is that lines are not 'word-wrapped' but will overflow canvas and get truncated if they are too long. And finally, for very long text files, multiple pages (images) will be generated unless some extra precautions are taken.

On the other hand, "text:" will handle just about any text file, without modifying the final image size produced, or word-wrapping very long lines. You also do not need to pre-process and special characters as you would if you used the text on the command line. Finally and more importantly if a fixed width font (like Courier) is used, files with spaced out columns of data, will still have that data in spaced out columns.

Basically "text:" will convert the input file 'AS IS'.

The input text data read from the file is essentially passed directly to the font library to draw the UTF text. As a consequence of this some control characters may be drawn using unusual 'glyphs'. This includes TAB and FORMFEED characters, which, at the time of writing, the 'freetype' library gets wrong.

If this is a concern, you may like to pre-process your text file using a filter program, such as "expand", to convert TAB characters into the appropriate number of spaces.

When drawing text, a large 'letter' sized page is created at the the current resolution (set with "-density"). By default (at 72 dpi) this will be '612x792' pixels in size, which for most purposes is very large.

For example here is a direct conversion of the plain text formatted manual for the "convert" command, into a image (it is large so to see it select the 'picture' image on the right)...

  man convert | col -b | expand | \
    convert -font CourierNew  text:- -delete 1--1 text_manpage.gif
[IM Output]

The above manual to image conversion however generated multiple pages (images), so I Deleted the second and later ones to leave me with just the first page, instead of a GIF animation of all the pages.

I could also have appended a Read Modifier, '[0]' input filename, such as "text:-'[0]'", to tell IM only to read the first image generated. Though all the pages are still generated by "text:".

I purposely used the 'fixed-width' font 'CourierNew' in the above so as to preserve the character spaced formatting that is present in the printed page.

Note how this output varies from that of caption: above. The overall look of this image can also be improved by using the same techniques given in Postscript section next.

Because the text is being 'drawn' onto a large canvas, you will likely want to remove all the unused space produced. This can be done by using the image operations "-trim", "+repage", then to make it look reasonable, re-adding some edge space using "-border". Of course you will also need to match the "-background" color you used as the "-bordercolor" you are re-adding.

Sounds complex? It isn't really, for example...

  echo "    Hello Cruel World    " |\
    convert -background  lightblue  -fill blue  -pointsize 18 \
            text:-    -trim +repage  -bordercolor lightblue  -border 3 \
            text_trimmed.gif
[IM Output]

In the above example "-trim" is used to remove the extra white space in the "text:" image. Otherwise the resulting image fills this web page. This however will also remove any leading spaces in front of a line!

There is however a interesting technique which will allow you to "-trim" the image down to the size of the actual text drawn onto the page, including any leading and trailing spaces in the input. This uses a special "-undercolor" setting, which we'll talk about later.

  echo "    Hello Cruel World    " |\
    convert -background  white -undercolor lightblue  -fill blue \
            -pointsize 18   text:-  -trim +repage \
            -bordercolor white  -border 3    text_boxed.gif
[IM Output]

The extra space at bottom of the text is a result of the last 'newline' character in the text input, creating an extra blank line in the image. But as you can see the leading and trailing spaces of the input text was preserved.

If you use a transparent background color in the above, you can then flatten the trimmed image to convert the undrawn areas into the same color as the 'undercolor' used.

  echo "    Hello Cruel World    " |\
    convert -background  none  -undercolor lightblue  -fill blue \
            -pointsize 18   text:-  -trim +repage  \
            -bordercolor lightblue  -border 3 \
            -background lightblue  -flatten    text_box_trimmed.gif
[IM Output]

The result of the above (except for the added "-border" is actually almost exactly what IM now produces using a "label:" and reading from a '@' escaped filename. However "label:" does it in a faster, and far cleaner way (via the "freetype" library rather than a postscript conversion).

You can specify a smaller "-page" size, either in pixels (see next example), or using a media page size (such as 'A5'), using the "-density" or pixel resolution setting. You can also specify the offset at which to start drawing the text on the page, relative to the top left corner. For example...

  echo "This is a long line that shows that 'text:' does not word wrap." |\
     convert -background  lightblue  -pointsize 18 \
             -fill blue  -page 320x95+50+10  text:-'[0]'  text_page.gif
[IM Output]

Almost all other image creation operators use the "-page" setting to set a larger virtual 'canvas' and an 'offset' for the image on that canvas, generally for the purpose of layering images or generating animations. Because of this it is probably a good idea to reset your page setting using "+page" after any "text:" or "ps:" operation, or you may get unexpected results for any secondary images you may latter read in on the same command line.

For more details of using this offset see Page Image Attributes.

Note how in the last example, any text line that is too long to fit the width of the page will overflow the page, and not be 'wrapped'. This will effectively crop and junk the end of the lines. Also if there are too many lines, then "text:" will generate multiple pages and thus multiple images, one for each page generated by the postscript translation of the text file.

If you are only interesting in the first page of text, or just want to avoid the possibility of multiple images, add a '[0]' to the "text:" filename, to tell IM to only read the first page generated after the text has been converted to images (see previous example).

Postscript/PDF - Pre-formatted Text and Graphics Input
(or other vector image formats)

The following gives a standard vector image handling technique that can not only be used for "PS:" (postscript) images but all other images handled using vector graphics. This includes image formats such as: "PDF:" (portable document format), "TEXT:" (paged plain text), and even "SVG:" (scaled vector graphic) and "WMF:".

This method can be expanded to give you a very fine control of exactly how the text will look as an image. For example with the right 'text to postscript' filter, you can control the word wrapping, justifications, multiple font handling, bolding, borders, titles, filenames, dates, and other fluff in the postscript image.

However as this section is about text to image, it means you need to first convert your text to a formatted postscript file. There are lots of external programs that can be used to do this. For example "a2ps", "enscript" or "pstext".

Essentially you can use a word processor (like 'OpenOffice' or 'Word', or even 'Notepad'), OR if you want a batch text processing system you could use at 'TeX' and 'LaTeX' to generate your pre-formatted text. These programs are all designed to handle the complexity of mixing plain, bold, different size, and font text together, along with word-wrapping, justification and paragraphing controls. The output from these programs can then be passed to IM to convert it to an image of the size and quality you desire.

So lets first generate some postscript (converting text from a personal fortune program). I provide two such text conversion program examples for you to pick what you have available. I will use the "enscript" version as the resulting postscript is more interesting.

  # -- a2ps version --
  mesgs LN-ManKzinWars | \
    a2ps -q -1 --rows=10 --prologue=bold \
         --center-title="Postscript via 'a2ps'" \
         --header='' --left-footer='' --right-footer=''   -o - | \
      perl -pe 'print "%%LanguageLevel: 3\n" if 2..2' > ps_version.ps

  # -- enscript version --
  mesgs LN-ManKzinWars | \
    enscript -q -G -f CourierBold12 -t "Postscript via 'enscript'" -o - |\
      perl -pe 'print "%%LanguageLevel: 3\n" if 2..2' > ps_version.ps
[IM Output]

The "perl" command in the above example adds a '%%LanguageLevel: 3' line to the generated postscript file. This is needed so that the later transparent postscript options to work correctly to preserve the transparency.

The real problem here is that the "ghostscript" delegate program does not provide a single conversion method for IM to use, requiring IM to try and figure out correct call style to use to generate a correct image for the given postscript. The 'Language Level' gives IM the hint it needs to figure it out correctly.

Now we can convert that into an image, trimming the result (as per "text:" examples above) to remove the excess blank areas from the default page/canvas generated.

  convert ps_version.ps'[0]' \
          -trim +repage   -bordercolor white -border 3  ps_version_raw.gif
[IM Output]

Note the use of '[0]' to limit the input to just the first page. If your postscript image generates multiple pages, it will still be completely processed by the "ghostscript" delegate, but IM will only read the first image returned, rather than generate multiple images, one image per page. If you postscript is very large, you may like to use other postscript utilities to limit the number of pages before processing it with IM and "ghostscript".

As you can see postscript converted to the default "-density" of 72 dpi, often does not look as good as it should, with only a minimal amount of anti-aliasing. This is particularly the case when dealing with postscript fonts, which are not designed to work at these low-resolutions.

To improve this, you can use a Super Sample technique to generate a better image. In this case you ask "ghostscript" draw at a higher resolution (or image "-density"). You can then "-resample" (a specalized resize) to bring this larger image back to a more 'normal' screen resolution density.

  convert -density 196   ps_version.ps'[0]' -resample 72 \
          -trim +repage   -bordercolor white -border 3  ps_version.gif
[IM Output]

The value '196' is 3 times the final 72dpi, which means when "-resample" is used approximately 3×3 pixels are merged down into each pixel. This produces better anti-aliasing pixels along the edges of the text improving the overall look of the result.

Also note that using a larger density or resolution is not quite the same as just enlarging a font. The font definitions could have adjustments for handling low resolution situations. For example compare the 'e' in the word 'enscript in the title of the two images. For more information see Resolution, Pointsize, and Actual Font Size below.

You don't have to use the values above, as sometimes slightly different value may produce a better or more desirable result. Also if you need more anti-aliasing, rather than using an even larger input resolution, you could try blurring the image a little bit before you reduce its size.

Of course having "ghostscript" generate an image 2, 3, or even 4 times larger also means IM will take 4, 9 or 16 times longer to generate the image! It will also use that much more memory and temporary disk space! But the results can be worth it. The best idea is to just try it out for your own problem, and see what gives you the best results.

I have also sometimes found that a very small unsharpening, can improve the overall result.

  convert -density 196   ps_version.ps'[0]' -resample 72 -unsharp 0x.5 \
          -trim +repage   -bordercolor white -border 3  ps_unsharp.gif
[IM Output]

But I would leave that as a final addition, as it could make things worse.


If you like to have a transparent background instead of white, you can specify a "-channel" setting of 'RGBA', to include the alpha channel in the image. Of course you will need to use a image format that can handle semi-transparent colors.

  convert -channel RGBA -density 196  ps_version.ps'[0]' -resample 72 \
          -trim +repage   -bordercolor none -border 3  ps_transparent.png
[IM Output]

Note that I used a PNG format for this last example otherwise you will lose the semi-transparent anti-aliasing present, giving the text a bad case of the 'jaggies'. See Anti-Aliasing for more info.

See the note above, about adding a '%%LanguageLevel: 3' to the postscript image, so that the above conversion works within the "ghostscript" delegate.

Making the background transparent like this will allow you to overlay your postscript image on a specific background color.

  convert ps_transparent.png -background skyblue -flatten  ps_bgnd_color.gif
[IM Output]

Using a Alpha Compositing Method you can even overlay it onto a specific background image, or a tiled background.

  composite -tile bg.gif  ps_transparent.png  -compose DstOver \
            ps_bgnd_tiled.gif
[IM Output]

Notice that the banner in the above examples still uses an off white color for its backgrounds, rather than also being made transparent or semi-transparent. That is because the postscript generated actually draws that background, replacing the default background of the page (be it white or transparent).

As almost all postscript printers only can darken paper or a overhead transparency, (this includes color printers), when the above is printed the banner would be made semi-transparent automatically.

If you also want IM do the same thing as what a printer would do, you can use a special 'Multiply' alpha composition, to overlay the 'white background' image, onto the desired 'paper' background.

  composite -tile bg.gif  ps_version.gif  -compose Multiply  ps_multiply.gif
[IM Output]

If you have a color postscript image, you also can simulate a pure black and white printer onto colored paper by using the special 'BumpMap' compose method. This will grey-scale the source overlay image, before it uses uses multiply to composite the images together.

You can also generate the grey-scale image equivalent of a overhead transparency slide. This basically uses the opaque, white background image (from above) as a 'mask' which to set shaped transparent image using the Alpha Shape Operator


  convert ps_version.gif -negate -background black -alpha shape  ps_overhead.png
[IM Output]

Like the "text:" converter above, the "ps:" converter also makes use of the "-page" setting to set the size of the image 'media' onto which the page is drawn. Though offset supplied will be ignored. However as most postscript files define the drawing media size internally, this is usually not necessary.

Most other image creation operators use the "-page" setting set a wrapping canvas and image position on that canvas (for example to generate GIF animations). As such it is probably a good idea to reset it using "+page" after using it for a "text:" or "ps:" image read operation, or you may get unexpected results with the later images.

For more details of using this offset see Page Image Attributes.

As a final practical example, have a look at my Ray Traced Tetrahedron image...

The background page was generated from that same data used to produce the displayed 3D mathematical object. The text data was converted using "a2ps", then IM was used to convert this to an image. Onto this image other pre-prepared line drawings of the same mathematical object was added to the page. This final image (saved in 'targa' or TGA format) was then passed to the "PovRay" ray-tracer for inclusion in the final image or ray traced scene.

ASIDE: Using Ghostscript directly

While this not strictly IM, Richard Bollinger has reported that running the "ghostscript" delegate directly is much more efficient, producing an order of magnitude faster processing due to less file handling, by IM.

For example instead of running...

  convert -density 300x300 -compress Group4 file.ps  file.tif

You can get ghostscript to do it directly.

  gs -dBATCH -dNOPAUSE -sDEVICE=tiffg4 -r300x300 \
     -sOutputFile=file.tif  file.ps

This prevents the need for IM to generate a large temporary file, and quite a lot of extra file processing time.

However "ghostscript" can not resize images, (other than adjust the density or resolution) and will probably not be able to output the image in the image file format you need. It can also be a difficult program to figure out how to use, or fix for specific types of postscript.

Cristy constantly battles with these issues on behalf of IM users, which is why IM is a bit slow in its handling of postscript.

Draw - Draw Text on Existing Canvas

By using the low level "-draw" operator to draw the font we get a lot more control, especially as to the exact position of the font, and the size of the image it is drawn into.

   convert -size 320x100 xc:lightblue  -font Candice -pointsize 72 \
           -fill blue  -draw "text 25,65 'Anthony'" text_draw.gif
[IM Output]

However to use it we need to generate a background image of the appropriate size to draw the font, which can be tricky when drawing some unknown text. See Auto Sizing of Font Images for ways to solve this problem.

A lot of extra options, beyond the standard text options also effect how "-draw", actually draws text on an image. Not only can you specify a "-fill" color, but you can also specify a "-undercolor" (on old IM's this was "-box", as well as an edge or "-stroke" color, both of which are turned off by default (set to a color of 'none'.

The "-fill" color can also be replaced by a "-tile" image pattern, while the stroke edge width can be changed using "-strokewidth". Then the relative position of the drawn text can be changed a with "-gravity" setting.

For example here I used many of the extra features I just mentioned.

   convert -size 320x100 xc:lightblue  -font Candice -pointsize 72 \
           -tile bg.gif  -undercolor dodgerblue  -stroke navy  -strokewidth 2 \
           -gravity center  -draw "text 0,0 'Anthony'" text_options.gif
[IM Output]

As of IM version 6.2.4, the "-draw text" operation no longer understands the use of '\n' as meaning newline, or the use of percent '%' image information escapes. (See Drawing a Percent Bug).

These abilities, and problems, however remain available in the new IM v6 operator "-annotate". See the Annotate Text Drawing Operator below.

All the above options can also be used within the "-draw" (MVG - Magick Vector Graphic) string. However if you set the above option within the draw argument, that option will only apply to that specific draw MVG string.

On top of this the draw MVG format can do much much more, such a text rotation and font 'decorating' and of course you can also draw various shapes like circles onto the image.

For example here we draw underlined, and rotated text, overlaid on a couple of background circles.

   convert -size 320x120 xc:lightblue \
           -draw "fill tomato  circle 250,30 310,30 \
                  fill limegreen  circle 55,75 15,80 \
                  font Candice  font-size 72  decorate UnderLine \
                  fill dodgerblue  stroke navy  stroke-width 2 \
                  translate 10,110 rotate -15 text 0,0 ' Anthony '" \
           draw_mvg.gif
[IM Output]

If you really want to make maximum use of "-draw" for creating your image, I suggest you look at the Drawing Examples Page.

Undercolor Box

The "-undercolor" color setting, as demonstrated above, and later below, will color the defined drawing area for that character and font. generally it just fits the drawn character. This is particularly the case the left and right edges of the drawn font, as the top and bottom edges are usually larger enough to accommodate all the characters. The drawing area basically represents the character 'cell' boundaries surrounding the area in which the font is drawn.

The major use of using the "-undercolor" option, is as a simple and quick way to clear a 'noisy' background from around the text. For example look at Annotating on Top Images. However it is recommended you also add a extra space character at the start and end of the string being drawn in that case.

Bounding Box Overflow

One of the biggest problems you will probably come across when drawing text, or just generally handling fonts, is that not all fonts obey the normal rules.

A font designer can 'draw' individual characters (or 'glyphs') anywhere relative to the current text position (known as the caret). The font position does not even have to move forward, and in some international fonts could even move backward!.

The result of this freedom of design is that some 'glyphs' do not fit inside the defined drawing area which the font declares the character fits into, especially on slanted, or script-like fonts where some parts of the letters extend well outside the bounds and into the areas used by later (or previous) characters.

The worst font I have seen in this regards is the 'LokiCola' font, which draws around half of its capital letters with long wavy tails, well beyond the bounds of the individual character cells. The font basically assumes each capital letter will be followed by 3 or more lowercase letters.

To show this I'll draw a number of the font's capital letters separately, allowing you to see just how far the letters could extend beyond the cell 'undercolor' or drawing bounds. I also use a couple of them to form the fonts name, so you can see just as they were designed to be used, and why they overflow their bounding boxes.

   convert -size 500x200  xc:lightblue \
           -font LokiCola  -pointsize 72  -undercolor dodgerblue \
           -draw "text  15,65 'L'"   -draw "text 130,65 'C'" \
           -draw "text 245,65 '1'"   -draw "text 360,65 'H'" \
           -gravity South -draw "text 0,10 'Loki Cola'"    draw_undercolor.gif
[IM Output]

Also note how the 'H' actually overflows on the left side as well as the right side of its drawing area. This can make it difficult to use at the beginning of lines.

Remember this problem is NOT a bug in IM, but caused by the interaction of the the font library IM uses, and the settings within the font itself, usually on purpose by the font designer. IM just uses the results as they are programmed in the font, which does not always produce what the user intended. Caution is thus advised with unusual fonts.

Annotate - Text Drawing Operator

With IM version 6, a new font drawing operator, "-annotate", was made available. This operator is in may ways much simpler that using a "-draw text" operation, but as it uses the 'annotate()' API (Application Program Interface), it is also more powerful.

While the operator does make use of the "-draw" primitives, it does so in a more complex way, such as expanding special escape characters to add extra image information and even multiple lines, and applying a coordinate system transform to the drawn text to produce slants and rotations.

Because of this, the operator is now the preferred text drawing operator for all ImageMagick text drawing and image annotation, and this is now reflected in these example pages.

Here is a basic example using this operator.

   convert -size 320x100 xc:lightblue -font Candice -pointsize 72 \
           -fill blue  -annotate +25+70 'Anthony'    annotate.gif
[IM Output]

One of the extra features of the "-annotate" operator is that it can rotate the X and Y axis of the drawn text completely separately to each other. This is done by providing the angle in which to rotate each axis as a 'image size' in the operators argument.

Just to show how complex a single "-annotate" operation can be, here is a boxed, stroked, and slanted image...

   convert -size 320x100 xc:lightblue -font Candice -pointsize 72 \
           -tile bg.gif  -undercolor dodgerblue   -stroke navy -strokewidth 2 \
           -annotate 0x20+20+67 'Anthony' annotate_opts.gif
[IM Output]

In this example all four annotate arguments are given. Specifically, X axis rotation, Y axis rotation, and the fonts X and Y position on the background image.

Also note that the fill pattern (set with "-tile") is also slanted with the font. That is because it is drawn using a sheared/rotated coordinate system, which also shears and fill pattern tiled within the drawn text.

Further example of this shearing capability is the Sheared Shadow Font example. Compare that with the Slanted Font created with the equivalent "-draw" MVG string.

For a table summarizing the effects of the "-annotate" shearing operation, see Annotate Argument Usage.

You can also add other information about the current image to the Annotated string using escape characters. For example lets overwrite the built-in "rose:" image with information about the images size. To center the text on the image we use a "-gravity" setting, and turn off any and all rotations and offsets by using an "-annotate" argument of '0'.

  convert rose:  -fill white -stroke black  -font Candice -pointsize 20 \
          -gravity center   -annotate 0 '%wx%h\nPixels'    annotate_rose.gif
[IM Output]

For more information see Special Escape Characters in Text Arguments below.

For other examples of annotating text onto a larger image in various ways (such as centered on the side, or rotated in the bottom-right corner) see Practical Text Annotation Examples.

Automatically Sized Annotated Text Canvases

Often you need much more control than what "label:" can provide. For example you want to use a tile or gradient image, requiring you to Annotate the text. Unfortunately you then need to know in advance the size of the canvas you need for you Annotated Text. Here is a typical example of the problem. When I first set up this command I set my size to the results I wanted, and at first it worked quite well. But then I got this...

  convert -size 480x80   gradient:yellow-green \
          -font ArialBkI -pointsize 70 -tile gradient:blue-red \
          -annotate +10+65 'Gradient Fun'    funfont_gradients.jpg
[IM Output]
Unfortunately when guessing at the canvas size I had miss-spelt the word 'Gradient' in that above (missing the letter 'i'). Of course when I fixed that spelling, my image size was now wrong, producing shown incorrect result.

What we wanted is to be able to use the "-annotate" operator, but with the canvas sized to fit the Annotated Text.

One solution is to use a much larger canvas then "Trim" the background to the right size. I also added a "Border" to add a little extra space around the font and the final edge of the image, for a better look.

  convert -size 800x120 xc:black -font Corsiva -pointsize 100 \
          -tile tile_disks.jpg   -annotate +20+80 'Psychedelic!' \
          -trim +repage  -bordercolor black  -border 10   funfont_groovy.jpg
[IM Output]

This method is much better than trying to make a guess at just how big your final image should be, however "Canvas Trim", will not trim a tiled multi-colored background.

The better solution is to create the canvas using "label:", to generate the canvas of the right size. A Draw Color Fill is then used to tile an image over the canvas (and the label text), and finally we Annotate our text using another tiling image.

  convert -font Ravie -pointsize 72  label:'Get Wet!' -border 10 \
          -tile tile_aqua.jpg   -draw "color 0,0 reset"  \
          -tile tile_water.jpg -gravity center -annotate +0+0 'Get Wet!' \
          autosize_wet.jpg
[IM Output]
Note that position of the text in a centered "label:" image may not exactly match the position of a centered "-annotate" operation. The two methods follow completely different processing algorithms, and as such may not match. Especially when unusual fonts are involved.

Auto Sized using 'Undercolor Box'

Rather than using a "label:" image, you can draw the font on a large canvas using an Undercolor Box and a large stroke width, before trimming the canvas to fit.

For example

  convert -size 500x100 xc:lightblue -font SheerBeauty -pointsize 72 \
     -gravity center -undercolor white -stroke none -strokewidth 3 \
     -annotate +0+0 ' Invitation ' -trim +repage -shave 1x1 \
     invitation_box.jpg
[IM Output]
The amount of space around the font can be adjusted using the "-strokewidth" setting. The only important requirement is that the initial canvas be a color different to the background color, ('lightblue' in this case) and is larger than the final result.

Just a word of warning, some fonts draw characters well outside the individual character drawing area. (For example see Undercolor Box above). In this case the above result will work, but may require you to use a transparent canvas, and then overlay the result over white (using an operation like "-background white -flatten" for example), to convert the unused and still transparent areas to white. However that character will likely be touching an edge of the resulting image. Basically you can't really win, in all situations, just try your best.

Coloring a Gray-scale Text Image

I purposefully generated the above image as a grey-scale black and white image, as this can be used as a masking template. From a pure image like this you can then color the background and the foreground of image either separately or both at the same time.

For example.

  convert invitation_box.jpg \
          \( +clone -size 300x150 -tile gradient:LightYellow \
                                             -draw "color 0,0 reset" \) \
          \( +clone -size 300x150 -tile plasma:tomato \
                                             -draw "color 0,0 reset" \) \
          -reverse  -composite      invitation_rose.jpg
[IM Output]
For other techniques of coloring a gray-scale image like this, see Using a Mask to Limit the Composed Area, And more generally Using Masks with Images.

For methods of generating gradients for tiling see Gradients of Color, and Randomized Canvases.


Special Escape Characters in Text Arguments

We have already introduced the special escape characters used in various text arguments, above. Specifically you can escape special characters like newlines using backslash '\', or you can insert extra information into the string using percent '%' escapes, as defined on the Image Properties page. Also there is a special '@' escape that if used at the start of a line will use the rest of the text argument as a filename to read the data from the file specified (or STDIN in '-' is used).

Not only do these escape characters effect "-format", for use by the "identify" (as well as "-identify" and the "info:"), but they also effect "label:", and "caption:" text to image generators, and control the image meta-data setting options "-label", "-comment", "-caption". And finally they are also used by "-annotate".

While backslash '\' is used by the "-draw" 'text' method, the percent '%' escapes are not as it interferes with ImageMagick's SVG image handling. This was one of the reasons the "-annotate" operator was created for IM version 6.

The other important point about escape characters is that while they are used for command line text arguments. At no time do they apply within the data being read from a text file (usually read in using the '@' escape).

This means you do not need to worry about escaping 'escapes' for text file data, but it also means you have to process file data yourself outside of IM if you need to insert information into the text.

Protecting input text file from escape handling was finalised in IM version 6.3.3.

For example here I set and the report a images 'label' and 'comment' meta-data using the two methods to set that information from a source text file. The "info.txt" file contains the string [IM Text], (no final newline).

  convert -label   @info.txt  rose:      -format '%l label'   info:
  convert -comment @info.txt  rose:      -format '%c set "'   info:
  convert  rose: -set label   @info.txt  -format '%l caption' info:
  convert  rose: -set comment @info.txt  -format '%c set "'   info:
[IM Text]

Notice that IM did not expand any of the escape character sequences that it read in using the '@' file read escape. This is important as it means that any time IM reads text from a file, it will never handle any special characters that was present in that file.

IM reads text files, as literal text, without any escapes

Unfortunately this also includes any final newline that may be present in the file (or stream) that is being read! This results in a extra 'blank' line in the resulting image. For example...

  echo "Anthony" | convert label:@-  label_stdin.gif
[IM Output]

As you can see the label not only had out input string but also an extra blank line due to the newline character on the end.

If you don't want that final newline, you will need to remove it yourself. This however could be a tricky matter, depending on where and how the text is sourced or created, and what API, you are running IM from.

The best way is to try not to generate that final newline. For example using a command line API...

  echo -n "Anthony" | convert label:@-  label_stdin_2.gif
[IM Output]

  printf "Anthony" | convert label:@-  label_stdin_3.gif
[IM Output]
Or you can 'junk' that final newline using a tricky little perl one-liner...

  echo "Anthony" | perl -0777 -pe 's/\n$//' |\
     convert label:@-  label_stdin_4.gif
[IM Output]

In other API's you can look for that final newline before feeding the text to a IM command via a 'piped open'.

User defined option escapes

A major problem is trying to use escaped information from one image in some other image, such as when generating a separate "label:", or "caption:" image with information about some other image.

This is a very difficult problem, and the current solution, (for a single image) is to create a special 'user option', that is attached to an image being processed. This 'setting' can then be looked up by the "label:", "caption:", or "-annotate", as a percent escape sequence, when needed.

For example here I create a completely new label image using information from the built-in rose image. That information source image is then deleted, though I could just as easily append the new label to the original image.

  convert rose: \
          -set option:roseinfo 'rose image\nsize = %w x %h\nwith %k colors' \
          label:'%[roseinfo]'  -delete 0   label_escape.gif
[IM Output]

Yes the above is tricky, but that due to some internal IM core library limitations that are involved. See Accessing Data from other images for more details.

Escaping Escapes

If you must feed a string as an argument to IM (especially as a API call), but don't want IM to expand escapes, you can simply 'escape' all three escapes using a extra backslash '\'. Note the '@' only needs to be 'escaped' if it is the first character, and for backward compatibility, percent escapes can also be escaped by doubling it. That is '%%' will produce a single percent. For example...

  convert -background lightblue -fill blue -font Candice -pointsize 48 \
          label:'\@ \\n \% 5%% '    label_escapes.gif
[IM Output]

Before IM version 6.3.2, you could not use a backslash to escape an initial '@' to turn off the 'read from a file' function. In that case the only way to escape an initial '@' was to read it from a file. This was not very practical in API's.

Here is a similar 'escape the escapes' example for "-annotate"...

  convert rose:  -fill white -stroke black  -font Candice  -pointsize 20 \
          -gravity center   -annotate 0 '\@ \\n 5%%'  annotate_escapes.gif
[IM Output]

Of course as shown, reading the text from a file (using the '@' escape, will always be treated as literal. This avoids the need for any pre-processing of the text, just watch out for any final newlines.

  echo -n '@ \n 5%' |\
    convert rose:  -fill white -stroke black  -font Candice  -pointsize 20 \
            -gravity center    -annotate 0 '@-'   annotate_escapes_file.gif
[IM Output]

In other words when reading from a file you don't have to worry about escaping things, but can just write exactly the text you want IM to use.

Label on Older version of IM

The above definitions were only finalised in IM version 6.3.3. Before this escapes were sometimes handled in some options, and sometimes not, according to any requests, problems, and complaints, sent by IM users.

This was especially the case with regards to the percent escapes with "label:" and "caption:", which was for a period deemed as 'non-sensible'.

For example whether you see a '%c' in the following label image is very version dependant (at least before IM v6.3.3).

  convert -background lightblue -fill blue -font Candice -pointsize 48 \
          label:'ab%cde'    label_percent.gif
[IM Output]

If you see a 'abde' (percent escape applied) or 'ab%cde' (percent not applied) depends on exactly what version of IM you are using.

IM v6.2.4, percent escapes were removed from "label:" and "caption:" as being non-sensible.

However they returned in IM v6.3.2, as new '%[fx:...] construct, which can reference any image, made percent escapes in text to image generators useful again. See FX Expression Escapes.

This 'what is escaped' was also a problem with regard to the handling of escapes from files. Before IM v6.3.3, the following would have produced two lines, rather than a single line.

  echo -n ' Love \n/ Hate ' |\
    convert -background lightblue -fill blue -font Ravie -pointsize 18 \
            label:@-    label_escapes_file.gif
[IM Output]

As the results of escape handling vary greatly from version to version, in IM's older than v6.3.2, I recommend that scripts test its escape handling, adjust themselves if it is important to the programs correct working.

If anyone like to create an automatic test for IM scripts, please contribute. Or if you find such as test, please let me know.


Resolution, Pointsize, and Actual Font Size

Pixels are dots on the display or in an image, and that is what IM works in.

On the other hand, images are printed at a specific resolution (specified as 'dots per inch' (dpi) or pixels per inch (ppi)). As such an images resolution effects how other programs will size a image onto a specific media. EG: It effects the images physical size in the real world.

The resolution (density or dpi) of an image is irrelevant to the pixel size of an image, and the amount of space an image takes in memory or on disk. It is also, in general, irrelevant to most IM image operations. As such, to ImageMagick, the resolution is just a set of numbers stored with the image, and is normally ignored. The only time the resolution or density of an image becomes relevant is for fonts and for converting vector formats like postscript, pdf, MWF, to the raster image formats IM handles.

The "-density" setting tells IM how many pixels (dots) per inch (ppi) is present on the output device, which it can then use to adjust the image generation and font sizes to match.

For example by default IM works with a "-density" setting of 72 ppi, which is a typical setting for displaying images on a monitor or webpage. As a fonts size is specified in 'points' (using "-pointsize" and by definition 1 point is 1/72 inches, then a 72 point font should produce text sized to be roughly 1 inch high...

  convert -pointsize 72 label:Hello  pointsize.gif
[IM Output]

However most modern displays have a better resolution than this, typically somewhere between 90 to 120 pixels per inch (ppi). As such...

  convert -density 90 -pointsize 72 label:Hello  density.gif
[IM Output]

Should produce a label that is 1 inch high on a 90 dpi display. It is on my display! You can measure the height of these images on your screen, to check if your displays resolution.

As the number of pixels per inch is larger, the drawn font is also naturally larger in the number of pixels, producing a larger image was created. Different image programs often have a different default density, and this may case fonts to appear differentially when drawn by different programs, even at the same point size.

Note that "-pointsize" actually means the line separation of a font (actually its drawing area height), and does NOT refer to the actual height of the drawn letters! As such one font can appear larger or smaller than another font, at the same pointsize and density. Only the line spacing be the same, anything else is depends on the font and the font designer.

As such with a default "-density" of 72dpi (at which 1 point = 1 pixel) a 12 point font should have 12 pixels separation between the baselines to two lines of text.

Note that the the height of a generated "label:" image is based on the images drawing area or bounding box, which is often, the the fonts line spacing, and pointsize. This is not always the case, as such just appending lines of text vertically as we have done in various examples on this page, is actually incorrect font handling!

Some fonts may even extend well beyond the normal line separation boundaries, extending well above or more commonly below the line spacing. This is especially true of hand script fonts.

The look of a font is also effected by the fonts "-pointsize" and "-density".

Doubling a fonts pointsize ("-pointsize 24") will also produce a font that looks about the same size as one with doubled density or resolution. However because a font is designed to look a particular way, the thickness of lines in a font may not change much at a larger point size. That is the larger font size is slightly different.

But if you just double the density ("-density 144"), a 12 point font will be drawn with its dimensions doubled, should still look like the original 12 point font, just drawn at a larger scale with better smoothing of the edges.

However at very low resolutions the physical size limitations of the pixels may also effect the look of a font. This means thin lines may be thickened at lower densities due to the large pixel size the density is defining. The relationship between 'density' and 'pointsize' is all a very complex issue, and one that only a professional font graphic designer can understand fully, and design their fonts to handle correctly.

According to 'Lithium' from the IM forums... I think it is a feature of TrueType font renderer. TrueType glyph is not only a set of curves, it may contain multiple levels of detail and instructions that adjust point coordinates according to output size in pixels, which is more visible for small size in pixels. Because of that, small text looks different (and more clear, one can notice) than shrunk large text.

FUTURE Example: difference between font at same 'pixel' size, but different density and point size.

Basically increasing one of these factors while decreasing the other by the same amount may not produce the same result. Particularly with regard to line thickness and overall 'style' of the font. You are better off adjusting the right factor for what you are doing. Use "-density" when scaling a font for a output device, or later resizing of the font, and use "-pointsize" for normal font size changes.

If you would like to know more about fonts, then have a look at the document TrueType Fundamentals (PDF), which I found very interesting.
FUTURE: Info on use of other font options wanted...
   -family    -weight   -stretch

I do not think these are used very often in IM, particularly with the now near
universal use of imported True Type fonts.

Determining Font Metrics, without using an API

As you can see above the width of a drawn string can only really be calculated easily by actually drawing the font, especially as most fonts are proportional, varying the amount of space used for each character.

However the vertical spacing of a font is fixed for all characters, but can be just as tricky to figure out when you don't have access to a IM API. This information can be useful to know, and might be something a image processing script might like to figure out before starting its work.

The Debugging Setting "-debug annotate" can be used to get IM to directly report a TTF font's metrics, for a specific string. For example...

  convert xc: -font Candice -pointsize 24 \
          -debug annotate -annotate 0 'Test' null: 2>&1 |\
    grep Metrics: | fmt -w80
[IM Text]

As you can see you get a mixed bag of information you can use: from the bounds of the font, relative to the drawing point; to the movement of the 'caret' or the current text drawing point.

The full debug output (not shown above) also reports the actual font file used (twice) so you can also use it to check that you have the right font too.

This was added to "-debug annotate" for IM v6.3.9-2


Older techniques

This debug output however may not be convenient, or you may have to handle IM's that are older than this version. The following are older examples where the text is actually drawn in various ways and colors, and then information (as integers) extracted from the resulting image.

For example lets find out the dimensions of the 'Ravie' font relative to a fixed baseline, at 72 point.

Here is the image we will be studying, as a reference. You don't actually need to drawn and save it as an image, as we are only extracting data, not an image. the colors of this image will be modified so we can look at the white and black parts separately using "-trim" to extract the metrics used.

  convert -size 100x150 xc:lightblue -font Ravie -pointsize 72 \
          -fill black -undercolor white  -annotate +20+100 'A' font_drawn.gif
[IM Output]

For the basic basic font metrics, we first draw the font itself with a transparent color ('None'), so that we can measure find the size and location of the bounding box or drawing area of this specific character, for this font. Note that for height information you can just draw anything.

  convert -size 100x150 xc:lightblue -font Ravie -pointsize 72 \
          -fill none -undercolor white  -annotate +20+100 'A' -trim  info:
[IM Text]

From the results above we can see that a 'Ravie' font at 72 points, will have a total bounding box height of 74 pixels. The top of the box is 42 pixels from the top of the image, as the baseline which was positioned at a 100 pixels y coordinate, the box starts is 100 - 42 or 58 pixels above the baseline. That leaves 74 - 58 or 16 pixels for the bounding box below the baseline for the descenders.

Note that not all fonts limit their drawing to within their defined drawing bounding box! Some letters can extend far outside those boundaries. That is why the above example sets a "-fill" color of 'none'. That way ill behaved fonts will not effect the above measurements.

Also note that the distance separating lines (baselines actually) should be purely determined by the point size of the font, and has nothing to do with how the font is drawn. In out example, as the font has a point size of 72 points, and a point is defined as 1/72th of an inch, then the baselines should be 1 inch apart. With a current output resolution (density) of 72 pixels per inch, that means the baselines will be 72 pixels apart.

Interestingly enough that means that for this font, with a 74 pixel bounding box, the font has a two pixel overlap for the drawing area between the lines of properly single spaced text!

Also from the above measurements we can see that in drawing the string "A" in this font at this point size, the next character should be drawn 66 pixels to the right of the starting point (known as the caret). This is the strings 'logical' length. That is the 'caret' or start point for the next character should start at 20 + 66, or at '+86+100' (the baseline does not change vertically). Be warned that some Arabic fonts can in fact draw right to left, so the 'caret' offset will be negative.

That gives use the font metrics for the character 'A' but what about physical dimensions of drawn 'A' relative to the 'caret' or start point. Well just swap the two color settings...

  convert -size 100x150 xc:lightblue -font Ravie -pointsize 72 \
          -fill black -undercolor none  -annotate +20+100 'A'  -trim   info:
[IM Text]

Height-wise the character was within its defined drawing boundaries, with its height going from 100 - 43 or 57 pixels above the baseline (hard against its bounding box) to 60 - 57 or only 3 pixels below the fonts baseline. In other words this letter does not have a 'descender' drawn in area below the baseline.

From this we can see that the 'A' draws from 3 pixels before the caret (positioned at +20, but final image is at +17), to 70 - 3 or 67 pixels after the caret position. In other words this font is slightly wider than its horizontal bonding box, when drawn.

Note that while this gives you the actual drawn string length, this is different to the caret offset needed when appending text, (which is defined by the strings bounding box, and not its drawn length). In other words text should be appended together using their bounding boxes, and not their actual drawn length size as we have do in other examples.

Of course if you get a very ill-behaved font, you may like to check how far a specific string draws beyond its bounds so that you can still provide space for it, say at the end of a line.

The dimensions extracted from a font will also vary with the current "-strokewidth" used for drawing the font. If you increase the size of the outline stroke, then the dimensions (and bounding box size) needed to draw the font is also expanded by the same amount to accommodate the thicker outline.

Dimensions also vary with the operating system, (type and version) and the version of the delegate font drawing library IM is using on that system, even when the exact same font library and IM version has not changed. Caution is recommended when different computers could be used for text drawing, as results can vary even for the same font.

For more information see the document TrueType Fundamentals (PDF). This shows that even my generalizations above may not always hold true, though is generally the case.

Note the above examples will only return dimensions in whole pixels, where all the dimensions used by fonts are floating point numbers. In fact whether a font is even drawn from a whole pixel (caret) starting point, can be application dependant, and effect the resulting look of the font.


Creating Lines of Mixed Font Styles

Creating a single line using multiple fonts, point sizes, and styles is not something IM is really designed to do. It gets even worse when you also start to consider things like text justification, word wrapping, and wrapping around images and other things.

This is the sort of thing that programs like Word Processors, Web Browsers, Document Printers do very well, usually under user interaction, but few can do so well under program control.

One exception to this is "TeX", and its family of programs (MetaFont, LaTeX, and so on), so if you are serious about processing text graphically, I suggest you look at this program family.

Another alternative is to look at various document 'pretty' printing programs such as a HTML converter. You can use these to convert program generated documents into postscript which IM can then happily post-process into whatever image format or style you want.

Now while IM is not designed for 'word processing', it does not mean you can't use it for such. It is just more difficult. Here I will give some examples of mixing text in different fonts and styles, to give people a starting point.

The simplest solution people usually think of is to just append "label:" images together...

  convert -font LokiCola     -pointsize 36  label:'LokiCola ' \
          -font Candice      -pointsize 24  label:'Candice ' \
          -font SheerBeauty  -pointsize 48  label:'SheerBeauty' \
          +append   wp_label_append.jpg
[IM Output]

However as you can see all the images are vertically aligned to the top of the image, and unless you use similar fonts, will not look very good.

Alternatively, you can use a append justification trick to align them along the bottom.

  convert -size 1x50 xc:none +size \
          \( -background white -font LokiCola -pointsize 36 \
             label:'LokiCola ' \
             -clone 0 +swap  -background none -append \) \
          \( -background white -font Candice  -pointsize 24 \
             label:'Candice ' \
             -clone 0 +swap  -background none -append \) \
          \( -background white -font SheerBeauty  -pointsize 48 \
             label:'SheerBeauty' \
             -clone 0 +swap  -background none -append \) \
          -delete 0   -gravity South -crop 0x50+0+0   +append \
          -bordercolor none  -border 1 -trim  +repage \
          -background white -flatten    wp_label_bottom.jpg
[IM Output]

What this did was to add some extra padding to the top of each label, and cropping them all to the same height before appending them horizontally. After that a simple "-trim" and a "-flatten" was used to set the height of the line to the highest label, and fill in the background.

As you can see this produces a better job, but a small font tends to produce subscript like behaviour, rather than properly aligned text.

What we really need to do is align all the text strings by their 'baselines' and that is very difficult without access to more textual information. This information is easily obtainable under a program API, but much more difficult from the command line. One method is shown in the previous example section.

However it is possible to align words by their baseline without actually collecting baseline information. While "label:" text images do not provide any clue as to the images baseline, you can specifically draw images at a fixed baseline.

Without an API you also can not directly find out how long or high drawn text is, so you first need to use a canvas that is large enough to ensure that we don't loose any information about text image. Then to preserve trailing spaces and text height you also have to make good use of the ("-undercolor") feature available to text annotation and provide a boundary for image trimming.

So lets see how you can do it from the command line.

  convert -size 500x100 xc:none  -fill blue  -draw 'line 15,0 15,99' \
          -undercolor white -fill black \
          \( -clone 0   -font LokiCola    -pointsize 36 \
             -annotate +5+60 'Loki Cola ' \) \
          \( -clone 0   -font Candice     -pointsize 24 \
             -annotate +5+60 'Candice ' \) \
          \( -clone 0   -font SheerBeauty -pointsize 48 \
             -annotate +5+60 'Sheer Beauty' \) \
          -delete 0 -trim +repage  +append \
          -transparent blue  -trim +repage \
          -background white -flatten   wp_draw_baseline.jpg
[IM Output]

As before the trimming of the images is done in two steps.

First draw the text on a base image which contains a vertical blue line. Thus when we trim the text, only the width of the text image will be trimmed, leaving all the words at the same baseline height.

After appending them together, we can now remove the blue construction line by making it fully-transparent. A second trim will then trim the top and bottom sections of the line, shrinking it to the largest bounding box.

A final flatten to the same color as the as the bounding box then removes all evidence of its use in constructing the line.

As you can see all the text is now properly baseline aligned, regardless of the font or the pointsize used.

Of course in these examples I only used black on white text. Other colors can be used, as long at they don't interfere with the construction line and transparent background used for text alignment.

With this technique you can now generate mixed font text lines, and vertically append them all together into a larger document.

You can also see that doing all this is a lot of work, work that is normally hidden from the user by word processors and web browsers. If you do plan to do a lot of this sort of stuff, I do suggest you look into the alternatives I previously mentioned.


Created: 25 October 2005 (separated from 'fonts' page)
Updated: 11 January 2007
Author: Anthony Thyssen, <A.Thyssen@griffith.edu.au>
Examples Generated with: [version image]
URL: http://www.imagemagick.org/Usage/text/

a