Tools for creating text as bitmaps (anti-aliased text, custom spacing, transparent background)
Asked Answered
M

3

5

I need to batch create images with text. Requirements:

  1. arbitrary size of bitmap
  2. PNG format
  3. transparent background
  4. black text anti-aliased against transparency
  5. adjustable character spacing
  6. adjustable text position (x and y coordinates where text begins)
  7. TrueType and/or Type1 support
  8. Unix command line tool or Python library

So far I've evaluated the following:

The problem with PIL is that e.g. the default spacing for Verdana is way too sparse. I need the text to be a bit tighter, but there's no way to adjust it in PIL.

In ImageMagick I haven't found an easy way to specify where in the image the text begins (I'm using -size WIDTHxHEIGHT and caption:'TEXT'). Adding a transparent border will move the text away from the corner it's achored to, but

  • image size needs to be adjusted accordingly since border adds to the extents
  • it's not possible to adjust horizontal and vertical offset independently

Have I missed some obvious alternatives or failed to find necessary features from the above mentioned?

Murmur answered 21/1, 2009 at 12:45 Comment(2)
Can you elaborate some on the specs? Mostly 5 and 6 since that seems to be where your alternatives lack.Flexuosity
@Flexuosity Ok, I extended the question.Murmur
M
3

Here's the SVG + ImageMagick solution:

Programmatically create SVG documents based on this template, replacing "TEXT HERE" with the desired text content:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE svg PUBLIC
      "-//W3C//DTD SVG 1.0//EN"
      "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" width="152px" height="50px">
  <text style="font-size: 22px; font-weight:bold; font-family: Verdana-Bold;
               letter-spacing: -1.3%;">
    <tspan x="10" y="39">TEXT HERE</tspan>
  </text>
</svg>

Convert the documents to background-transparent PNGs with ImageMagick's convert:

$ convert -background none input.svg output.png
Murmur answered 21/1, 2009 at 13:53 Comment(0)
C
4

(5) indeed looks tricky, short of inserting dummy narrow-spaces into the string (which will break kerning) or using something much higher-level like the SVG or HTML/CSS renderer.

However, if you don't mind getting your hands dirty, it looks quite easy to hack PIL's freetype renderer into adding horizontal space. See _imagingft.c; after the following code in both font_getsize and font_render:

if (kerning && last_index && index) {
    FT_Vector delta;
    FT_Get_Kerning(self->face, last_index, index, ft_kerning_default,
                   &delta);
    x += delta.x >> 6;
}

Add:

if (last_index && index) {
    x += tracking;
}

Try it with a plain integer for tracking (probably quite large judging by that '>>6') first; compile and see if it works. The next step would be to get the tracking value into the C function from Python, for which you would have to change the ParseTuple call in font_render to:

long tracking;
if (!PyArg_ParseTuple(args, "Ol|il:render", &string, &id, &mask, &tracking))
    return NULL;

And in font_getsize:

long tracking;
if (!PyArg_ParseTuple(args, "O|l:getsize", &string, &tracking))
    return NULL;

Then look at what Python interface you want. This is a trivial but quite tedious case of adding the extra 'tracking' argument through each level of the interface, for example:

def truetype(filename, size, index=0, encoding="", tracking= 0): # added optional tracking
    "Load a truetype font file."
    try:
        return FreeTypeFont(filename, size, index, encoding, tracking) # added tracking
    ...

class FreeTypeFont:
    "FreeType font wrapper (requires _imagingft service)"

    def __init__(self, file, size, index=0, encoding="", tracking= 0): # added tracking
        import _imagingft
        self.font = _imagingft.getfont(file, size, index, encoding)
        self.tracking= tracking # add this line

    ...

    def getmask2(self, text, mode="", fill=Image.core.fill):
        size, offset = self.font.getsize(text, self.tracking) # use tracking
        im = fill("L", size, 0)
        self.font.render(text, im.id, mode=="1", self.tracking) # use tracking
        return im, offset

I haven't tested any of this! If it works, might be worth submitting as a patch.

Concavoconcave answered 21/1, 2009 at 13:51 Comment(2)
Neat! To improve upon your idea, it would be useful to be able to change spacing not only in absolute units but also relative to the symbol's normal kerning.Murmur
Agreed! I haven't even looked at what exactly the units are here, but Doing It Properly at the external API level, units relative to the font size would probably be the best thing.Concavoconcave
M
3

Here's the SVG + ImageMagick solution:

Programmatically create SVG documents based on this template, replacing "TEXT HERE" with the desired text content:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE svg PUBLIC
      "-//W3C//DTD SVG 1.0//EN"
      "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" width="152px" height="50px">
  <text style="font-size: 22px; font-weight:bold; font-family: Verdana-Bold;
               letter-spacing: -1.3%;">
    <tspan x="10" y="39">TEXT HERE</tspan>
  </text>
</svg>

Convert the documents to background-transparent PNGs with ImageMagick's convert:

$ convert -background none input.svg output.png
Murmur answered 21/1, 2009 at 13:53 Comment(0)
R
2

From a quick glance, Pango has support for letter spacing. Pango has Python bindings and is integrated with Cairo.

Reviere answered 21/1, 2009 at 14:36 Comment(1)
Actually I think I was looking at the documentation for Pango-related features in PyCairo, but evidently I missed letter spacing. Thanks!Murmur

© 2022 - 2024 — McMap. All rights reserved.