Friday, 31 October 2008
SVG font rasterising with Batik and Inkscape
Recently, I stumbled upon a problem when trying to rasterise an SVG document. The following simple SVG document shall be used to illustrate the point:
<svg xmlns="http://www.w3.org/2000/svg" version="1.0"
width="35" height="14">
<g>
<text x="3" y="10"
style="font-size: 8px;
font-family: PF Ronda Seven"
text-rendering="optimizeLegibility">
<tspan>illegible</tspan>
</text>
</g>
</svg>
The document contains text set in a very small size.
When rasterised, the font should still look crisp and legible, without any anti-aliasing.
I therefore chose an 8 pixel bitmap font (which was made for exactly such purposes) and provided the attribute text-rendering="optimizeLegibility" to give potential rasteriser applications a hint as to how to deal with fonts.
Let's compare the output of two open source rasterisers: Batik (version 1.7, bitmap on the left) and Inkscape (version 0.46, bitmap on the right), at original scale and scaled up to 800%:
Batik behaves as expected and makes proper use of the bitmap font, which consists only of white and black pixels.
Inkscape however applies anti-aliasing even though we explicitly asked not to do so by specifying the appropriate text-rendering attribute. This is a known problem and will hopefully be addressed in a future release.
Now imagine the above SVG is created programmatically (e.g. an XSL transformation) and the x/y coordinates of the text element turn out to be floating point numbers rather than integers. For instance, let's change the the x coordinate to 3.3:
...
<text x="3.3" y="10"
...
No problem for Batik, the output stays the same. Inkscape sticks to its anti-aliasing and produces blurry output that is practically illegible.
Now let's complicate the situation even more. Imagine the coordinates are floating point numbers with a decimal part of exactly 0.5:
...
<text x="3.5" y="10"
...
Now even Batik, which previously produced perfect results, has problems. It seems like every character of the string is positioned independently. Because of alleged rounding problems (there are probably ties), the characters are not glued next to each other with the same amount of space in between. The result looks awkward and is practically illegible, not to speak of the output of Inkscape which is basically a gray smear.
It took me a while to figure out how to remedy the situation. The simple workaround I found consists in making sure that the coordinates never have a decimal part of exactly 0.5. This can be achieved e.g. by putting the text element into a group element which is translated by a tiny little bit (here by 0.01 in the x direction):
...
<g transform="translate(0.01, 0)">
<text x="3.5" y="10"
style="font-size: 8px;
font-family: PF Ronda Seven"
text-rendering="optimizeLegibility">
<tspan>illegible</tspan>
</text>
</g>
...
Batik produces perfect output, supposedly because there are no ties when it comes to rounding to the next integer anymore. Inkscape again applies anti-aliasing.
Posted at 22:57 by Thomas in XML | Comments[0]
