I am using dvipng to render sans serif documents, and the results are especially bad. At the default resolution (100 dpi IIRC), there are some serious kerning issues. Here, I rendered with two sans serif faces; on the left is Helvetica with [scaled=0.92], on the right is the default CM sans. Below is CM serif which for some reason is blurry. It also has kerning problems, but not apparent at first glimpse because of the serifs.

Some facts:

Mediawiki uses a program called texvc written in OCaml to render all the equations. Basically, it validates the equation through a parser, and creates a temporary latex file. It runs latex on the file, and renders the resulting dvi with dvipng. I've never seen an equation with bad kerning in Wikipedia, and I assume that is because they render at a higher resolution with the default CM serif.

I also tried rendering at higher resolutions, and the kerning looks fine at 120+ dpi. However scaling the image down is not an option, since it messes with anti aliasing. I must make sure that the kerning is perfect at screen resolution, without any loss in quality.

Any thoughts?

MWE:

\documentclass[11pt]{article}

\setlength\paperwidth  {230pt}
\setlength\paperheight  {297mm}
\setlength\textwidth  {\paperwidth}
\setlength\textheight {\paperheight}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{amsmath}
\usepackage{layouts}
\usepackage{calc}
%\usepackage{uarial}
\usepackage[scaled=0.92]{helvet}
%\usepackage{helvet}
%\usepackage{nimbus}
\renewcommand{\familydefault}{\sfdefault}
\nofiles
\raggedright
\begin{document}
kompozisyonun sanatsal açıdan bir hayli basit
olmasına karşın figürlerin özel bir dikkatle
çizildiğini ve resmin, her yere nüfuz etmiş
ışık ve hava sayesinde kaynaşıp bir bütün
oluşturduğunu görüyoruz. Figürlerden bu küçük
tablonun çoğunu kaplayan manzaraya bakıyoruz,
sonra tekrar figürlere dönüyoruz. Giorgione,
kendisinden önce gelenler gibi önce nesneleri
ve kişileri çizip sonra onları bir mekana
yerleştirmiyor; doğayı, toprağı, ağaçları, ışığı,
havayı, bulutları ve kentleriyle insanları bir
bütün olarak düşünüyor.{\bf Bu parçada anlatılanlara
göre Giorgione’nin eserini oluştururken güzelliğin
hangi niteliğini öne çıkardığı söylenebilir?}
\end{document}


I create the png by:

latex myfile.tex
dvipng myfile.dvi


I run it with different widths, faces etc. and in every run, there are cases of unacceptable kerning.

EDIT

Could it be the non-ascii characters? I ran the same file with \lipsum and the result is not so bad. Could it be the length of the words?

Kerning with ASCII encoding:

EDIT

Maybe this this could be helpful for future reference

-
Please include a minimal working example (MWE). It's possible that this is an issue with your installation: I tried briefly to reproduce it and failed. –  Lev Bishop Dec 15 '12 at 22:21
@LevBishop I added the example. I'm using the texlive distribution from Arch Linux repositories. –  nrs Dec 15 '12 at 22:50
What is wrong with doing good scaling down? That way you can obtain the best quality. –  tohecz Dec 16 '12 at 0:00
@tohecz I used imagemagick to scale it down, and the characters got bolder and more blurry than the original. I think aliasing for raster displays only works in that instance. I mean the aliasing for a larger resolution does not apply to a smaller resolution in the same way. That is why vectorized type gets redrawn every time you zoom in or out. –  nrs Dec 16 '12 at 0:08
@nrs: have you tried with gs from ps or pdf? gs -sDEVICE=pnggray -r500 -dDownScaleFactor=5 -o file.png file.ps (or file.pdf)? Note that convert when applied to a pdf in fact calls gs. –  jfbu Dec 16 '12 at 22:56

There is a long article about the challenges and tradeoffs involved in rendering crisp fonts at low resolution for screen: The raster tragedy.

It seems that dvipng does not do subpixel positioning (which is an advantage because it means the individual glyphs can be made sharper, by careful hinting as done by freetype if your version of dvipng has this capability compiled into it). Unfortunately, this means that the glyph widths are constrained as integer number of pixels, which does not match with the TeX width (and we wouldn't want to force TeX to use the pixel width, because TeX/DVI strives to be device-independent). The way most DVI processors deal with this problem is to allow a few pixels' worth of "drift" to accumulate during a word, and then to absorb this drift into the next interword space that comes. If too much drift accumulates during a long word then a pixel has be added/subtracted as a lesser-of-two-evils move in order to keep the rendering faithful to the device-independent ideal. Check the documentation of the -e option to dvips for example. This explains why you see problems with the longer words of the Turkish text, but not the lipsum.

Your options to deal with it are:

1. Allow subpixel glyph positioning, and accept that the glyphs will not be as sharp. This is effectively what you were doing by rendering at higher resolution and then scaling down. Eg, if you do a linear scaling by 4, then you have allowed glyph positioning on the 1/4 pixel boundaries of the final image. (Actually, it's better to do this directly with the help of the glyph rasterizer, because then it could at least arrange for horizontal lines to be sharp (eg, crossbars on EFLITeH) even though nothing can be done for the verticals); or
2. Write a patch for dvipng implementing some equivalent of dvips -e1000 and accept that the interword spaces might be uneven, and the right edge of justified text might fluctuate distractingly.

There aren't really any other options. Rendering text on a screen is all about tradeoff and compromise. Text rendered by the operating system mostly looks fine, because it is not trying for device-independent layout, and can reflow the text at will to absorb the drift. But if you look at device-independent text (eg, from your PostScript or PDF viewer of choice) you'll see that they all have either fuzzy glyphs or uneven spacing. They probably look a bit better than dvipng's attempt, because they have access to subpixel glyph rendering (significantly reducing the drift-per-glyph). You wouldn't want dvipng to implement subpixel rendering, though, because you would need to make assumptions about the viewers' monitor setup.

-
excellent answer! made things a lot clearer, thanks. –  nrs Dec 16 '12 at 9:07

This is migrated from my comment. Update: I had forgotten a useful switch which produces one image per page of the input.

I am no expert at all in graphics but it seems that by sheer luck my proposal to do gs -sDEVICE=pnggray -r500 -dDownScaleFactor=5 -o file.png file.ps has been found useful.

A more complete set of switches could be (Edited following Lev Bishop's advice)

gs -dSAFER -dBATCH -dNOPAUSE -sDEVICE=pnggray -r500 -dDownScaleFactor=5 -o file%d.png file.ps


The additional switches (apart from -o which specifies the name of the output file, and has the useful %d so that each page of the original file gives a separate png image) suppress interactive prompts and enable some security checks.

The command is here applied to a file.ps but it works exactly the same also for file.pdf. In my workflow I first had applied pdfcrop to the original pdf file, so that the png images are reduced to the actual ink on each page.

The most important switches for us here are -sDEVICE, -r500 and -dDownScaleFactor=5. The -r switches simply tells gs to work internally at 500dpi. Then the -dDownScaleFactor=5 tells it to reduce intelligently to only 100dpi. The -sDEVICE=pnggray switch tells gs to produce a PNG image with a gray color model. Here are relevant extracts from the ghostscript documentation (version 9.05 on my system, TL2012, in /usr/local/share/ghostscript/9.05/doc/Devices.htm#PNG):

Ghostscript provides a variety of devices for PNG output varying by bit depth. For normal use we recommend png16m for 24-bit RGB color, or pnggray for grayscale. The png256, png16 and pngmono devices respectively provide 8-bit color, 4-bit color and black-and-white for special needs. The pngmonod device is also a black-and-white device, but the output is formed from an internal 8 bit grayscale rendering which is then error diffused and converted down to 1bpp.

The pngalpha device is 32-bit RGBA color with transparency indicating pixel coverage. The background is transparent unless it has been explicitly filled. PDF 1.4 transparent files do not give a transparent background with this device. Text and graphics anti-aliasing are enabled by default. Options

The pngmonod, png16m and pnggray devices all respond to the following:

-dDownScaleFactor=integer This causes the internal rendering to be scaled down by the given (small integer) factor before being output. For example, the following will produce a 200dpi output png from a 300dpi internal rendering:

        gs -sDEVICE=png16m -r600 -dDownScaleFactor=3 -o tiger.png\
examples/tiger.png


Some more from the Ghostscript documentation:

Examples of how to use Ghostscript to convert postscript to PNG image files:

gs -dSAFER -dBATCH -dNOPAUSE -sDEVICE=png16m -dGraphicsAlphaBits=4 \
-sOutputFile=tiger.png examples/tiger.png

gs -dSAFER -dBATCH -dNOPAUSE -r150 -sDEVICE=pnggray -dTextAlphaBits=4 \
-sOutputFile=doc-%02d.png doc.pdf


From my notes, I see that the device pngmonod seemed unavailable, and that using the device pngmono did not work in combination with -dDownScaleFactor.

I chose an internal 500dpi rendering because in my case, both in terms of quality and of file size it seemed to produce a similar result from a pdf input as dvipng had done on similar dvi input. If it solves the kerning problem mentioned by nrs, it is pure serendipity and I have no merits in it at all.

A final note is that contrarily to dvipng, gs does put in the png image the information relative to the absolute size of the pixels. This does not change anything in web browsers, which seem to work on the model 1 image pixel=1 screen pixel.

EDIT:

Result of using gs:

For comparison, result of:

dvipng -D500 --freetype0 -Q1 dvipng
convert dvipng1.png  -resize 20% dvipng1r.png


-
@LevBishop I was hoping for someone to tell me so! indeed this was the result of a hastily done copy paste from some incantation of gs for some other things. I was suspicious this had not its place here. Thanks! –  jfbu Dec 22 '12 at 16:36
To explain benefits/drawbacks of this technique: using -r500 -dDownScaleFactor=5 is rendering without antialiasing at 500dpi and then rescaling the image by a factor of 5, producing well-kerned but fuzzy text at 100dpi. An alternative of -r100 -dTextAlphaBits=4 would render directly at 100dpi with antialiasing, giving poorly-kerned but sharp text, more like the output from dvipng discussed in the question. (To get similarly downscaled output from dvipng can try dvipng -D500 --freetype0 -Q1 file then convert a.png -resize 20% b.png, maybe with a -gamma option added.) –  Lev Bishop Dec 22 '12 at 17:04