25 June 2008

DPI: part 1

Another example of "too much choice can be a bad thing" rears its head in the way that Linux renders text. The headaches arise out of the interaction between a few variables, some of which may or may not be known. Lets try to break this down a little bit.

Screen resolution
The easiest variable to understand in Linux's windowing/font-rendering system is what most people call screen resolution. It's also the easiest part for Linux's X.Org windowing system to get right. Screen resolution is simply a measure of the raw number of pixels used to draw your display. Common sizes are 800x600, 1024x768, 1280x800, etc. These are familiar numbers to Windows users.

The X.Org server installation process executes a command that 1) tries to determine what resolutions are supported by your connected display, 2) asks you which ones you want to use, and 3) writes a configuration file /etc/X11/xorg.conf that stores this and other information. Sweet. Easy.

Screen size
Screen size refers to the actual physical size of the display, measured in millimeters. This is where things can start to go a little wonky. When the X.Org server installer creates its main configuration file (i.e., /etc/X11/xorg.conf), it also asks the display (not you) how big it is. Sometimes a display knows how big it is, sometimes it doesn't. If the display reports a size in a way the installer understands, this information gets stored in the configuration file. If the display doesn't, then it doesn't.

The result is that sometimes X.Org server knows how big your monitor is, and sometimes it doesn't.

DPI
DPI stands for dots-per-inch and is probably the hardest concept to get your head around -- and the hardest part for Linux to get right.

Font sizes are typically defined in points, where one point equals 1/72 inch. Therefore, a system that renders fonts on a computer screen needs to know how many dots-per-inch it should use to draw the text. If the DPI setting is not correct, then all text will appear either too small or too large.

It's quite natural to think that a screen's DPI should simply equal the number of pixels in a dimension divided by the physical size. If we have a 1024x768 screen and the screen is 10.7" wide by 8" high, then we should get 96 DPI in both horizontal and vertical dimensions. If we have an 800x600 screen that is the same physical size, then that should result in 75 DPI. However, life in Linux-land is not quite that simple. One of the reasons is technological, the other is contextual.

First the technological issue. Remember above we said that the X.Org server configuration process tries to figure out physically how big your screen is? A problem arises when for whatever reason the screen's physical size doesn't get recorded into the /etc/X11/xorg.conf configuration file. This happens often enough, and when it does, the poor system simply has to guess what DPI to use. The results can be pretty tragic.

The contextual issue is a result of the way things work in Windows and MacOS. In both of these systems, the DPI is fixed no matter what physical screen size you have. Windows' default is 96 DPI, although you can change this to 120 DPI by using the "large fonts" mode. (In fact, you can tell Windows to use whatever DPI you want, but I've never seen this in practice so I am concluding that it is rarely done.) Apparently, MacOS defaults to 72 DPI.

Some Linux developers assert that Linux should follow Windows' "defacto" standard and set systems to 96 DPI, no questions asked. Some developers (therefore?) assume 96 DPI when they develop their software. Others don't. This results in a situation where you can't get all your software to look right no matter what your DPI setting is without additional tweaking.

Note that this DPI concept only affects text rendering. The sizes of icons and similar things are usually set in pixels, and a pixel is a pixel in Linux.

Overriding DPI
Normally, X.Org will try to calculate your system DPI based on the screen size and the display's resolution. If it doesn't know the screen size, it will make a guess. However, it is possible to override the calculated/guessed DPI setting. Since this is Linux, there's more than one way to do it, and I have listed the methods I have actually used below. Except for one they all involve editing configuration files. If the following doesn't work for you, please do some Googling and let me know what else you find.

1. Overriding DPI with a local configuration file
I have used this method with Debian Etch and Xubuntu 7.10. It did not seem to work with Xubuntu 8.04, so expect your milage to vary. It is, however, the easiest way to set DPI.

If a file called .Xresources doesn't already exist in your home directory, create it. (Rememeber that the '.' makes the file hidden!) Add the following line to it:

Xft.dpi: <dpi>

where <dpi> is the DPI you want to use. For example, to force the system to use 96 DPI:

Xft.dpi: 96

When you log out and log back in, you should be using the new DPI setting. If not, delete the file and try another method.

2. Overriding DPI using a session manager's global configuration file
The following will only work if you are using XDM (rather than the more common GDM or KDM) as your session manager. However, you can do something similar with GDM and KDM. I have used this with Debian Etch using XDM instead of the default GDM, and it works as expected.

Make a backup copy of the file /etc/X11/xdm/Xservers. (This will let you get back to where you were in case you mess something up.) Open /etc/X11/xdm/Xservers as root in a text editor. Find the line towards the bottom of the file that looks something like

:0 local /usr/bin/X vt7 -nolisten tcp

The line in your file may not be identical to the above, but it should be similar. Add a -dpi <dpi> option to the line that specifies the DPI you wish to use. For example, to force 96 DPI, you would add the option as below:

:0 local /usr/bin/X vt7 -dpi 96 -nolisten tcp

If the line already has a -dpi option, then change the number to reflect the DPI you want.

Restart the X server (by rebooting or typing Ctrl-Alt-Backspace) and log back in. You should now be using the DPI you specified.

3. Overriding DPI with the global X configuration file
This is the most foolproof method and also the most difficult. Basically, we are going to hand edit the /etc/X11/xorg.conf file and tell it the size of the screen we have so that it produces the desired DPI. I am using this method on an old laptop with an 800x600 screen running Xubuntu 8.04 and it works.

Make a backup copy of the file /etc/X11/xorg.conf. (This will let you get back to where you were in case you mess something up.) Open /etc/X11/xorg.conf as root in a text editor. Find the section in the file that begins Section "Monitor" and add a line (or edit the line if one already exists) that specifies the width and height of your screen (in mm) using the syntax:

DisplaySize <width> <height>

(In case you forgot, one inch equals 25.4 millimeters.)

As an example, the section from the xorg.conf file used in my 800x600 laptop system looks like:

Don't change anything else! Just add (or edit) the one line that specifies the monitor's size. The dimensions you use should be either the real physical dimensions of the screen (in mm) or values that you have calculated to produce the DPI you want.

Restart X (reboot or Ctrl-Alt-Backspace) and see the results. From what I gather, there are some video cards that require even more xorg.conf tweaking to make behave, so if this doesn't work, do some Googling with xorg.conf and the brand/name of your card.

In case something goes wrong
If you really mess up your /etc/X11/xorg.conf file, you can regenerate it by running from the command line:$ sudo dpkg-reconfigure -phigh xserver-xorg

for the simple version, or:$ sudo dpkg-reconfigure -plow xserver-xorg

to set each and every option. Depending on your distribution, you may need to delete the messed up /etc/X11/xorg.conf file before it will be replaced. You should also execute the above if you change displays.

4. Overriding DPI when not using a session manager
This is also a foolproof method, but it only works if you don't use a session manager and instead use the command-line to start the window system. (If you are using SLiM, I believe you can tweak it to use this method as well.)

The standard way to start X from the command line is the startx command. You can pass the command a parameter that tells it what DPI to use. For example, to start X windows and tell it to use 96 DPI,$ startx -- -dpi 96

Make sure you get all the dashes correct.

Looking forward
In an upcoming post, I will let you know what appears to be best practices regarding DPI settings. But first I need to do some more work on this. In particular, it looks as though there are DPIs for Xserver DPI and for Xft (the tool used to render Linux's "pretty" fonts) and it looks like they don't need to agree. And then there's the fonts themselves.