Nightmare units

As I'm developer and helper on Inkscape, I sometimes get questions about features in Inkscape, how things work, and that prompts me to go see, check, and understand things about the world.
Lately, I've been asked about units. There has been some change when I joined the dev team changing some dpi from 90 to 96, between inkscape 0.48.5 and 0.91, but I did not take an interest in those things at the time. Recently, I've been asked about how to export to some px dimensions, but "at 72dpi because photoshop wants it that way". So I wanted to know what was the deal with units.

After all, it's just down to one-dimensional geometry stuff and can't be much harder than a rule of three, right?

Short answer: Right!

Yes, it's only a matter of rule of three...

Long answer:

... but you have to know what units you are talking about.

In the short introduction above, there are several types of units involved: real-life inches (the ones you can measure with a ruler on paper. Note that this unit has been deprecated since the XVIIIth century and the introduction of the metric system, but I guess that some countries just like living with antique units. In most countries your typical ruler only has SI units on it. Despite this, since one of these old-fashioned countries rule the Internet, we're still stuck with them.), monitor pixels, display pixels, SVG user-units-pixel, SVG user-unit-inches, SVG-export-inches or SVG-export-pixels .

Down the rabbit hole: from hardware to software.

Let's start with screen physical pixels. Screen physical pixels are these nice square-like sets of three lights on your monitor (Historical note: there used to exist vector monitors, like oscilloscopes, which did not have pixels). The three dots are typicaly red, green, and blue. when you light none, you have a black dot, when you light all, you have a white dot. So far, so good.

A slight complication is that you can have a screen resolution that does not match the physical resolution of your screen. In all operating system, even if you have a 1920x1080 monitor, you can tell your OS (or rather you graphic card/chipset) to use it as a 1024x768 display (or any other standard size. Ideally you want to keep the same aspect ratio). The graphic card will frown and just tell the monitor to light several physical pixels when you ask to display one pixel, so that all the monitor space is used. This is also important when you want to improve things like antialiasing by making smaller physical pixels. This is what the "retina" or "HiDPI" screens do: make physical pixels smaller, so that artifacts between pixels will be smaller and less noticeable.

When you have to deal with a printer (or cnc machine, or any machine that deals with physical objects like paper), the deal is different. There are no pixels, and you will want to specify the size of whatever you want to print. This size, that you can measure on paper, is measured in real-life units, like meters(m), millimeters (10-3 m), centimeters(10-2 m), kilometers(103 m), ångströms(10-10m), inches(25.4mm), feet(304.8mm), yards(914.4mm), miles(1,609,344 mm), parsecs, ropes, furlongs, or whatever you want to make up as long as you can convert it from and to meters.

Images when stored in electronic format, like PNG and SVG images, can usually both be printed on paper and displayed on screen.

PNG

PNG being a raster image format, it is simpler, so let's deal with that one first.

PNG images have by definition a size defined in pixels, with a precise width and heigth, and a complete matrix of pixel of that width and height define the PNG content. The base PNG format does not deal with real-life units, or print, but there is a very widely-used official PNG extension called pHYs (for "pHYsical pixel dimensions", don't ask me why some are uppercase), that allows to specify the number of image pixels per (real-life) unit. The unit is either unspecified (so that you can just state that the width/height ratio should change for print, but not tell anything about the size), or meter. Take that, imperial units. So, pixel per meter it is.

SVG

SVG is a vector format. That means that by definition, it does not care about scale or units. Also, it was designed to be embedded in webpages. The main way to define how a SVG should be displayed is two attributes of the root <svg> element: "width" and "height" attributes. They can be specified in px (which is fine to render things at some wanted size on a screen), or in paper units like centimeters, millimeters, or inches, points and pica (seriously, who uses those?), or in relative units (like em, ex, and %).
Relative units are fine when you embed an SVG in a webpage, but a weird thing to understand from a printer or image viewer point of view. Honestly, I have no idea what a printer is supposed to do when told "hey, print that single image with a size of 30 font-size". I guess they have a default value for what a font-size or a x-height is. Also, to make things cooler, the default value (if you don't put the attribute in the file) is "100%".

Conversely, when a web browser receives an image with a size in inches, it converts it into pixels with a 1in = 96px ratio, as defined in the CSS2 Specification.

That was about the export units in SVG. When we look closer into our root <svg> node, we also see a "viewBox" attribute. It may look like "0 0 210 297". That attribute defines the part of the image that will be rendered. Indeed, as SVG is vector, you can draw anywhere in its 2d universe, but only a rectangle in it will be considered as being the actual drawing, and that rectangles will be defined in the document by these coordinates. Here, we are between the point at coordinates (0,0) and the one at coordinates (210,297). Also, when exported to a browser or a printer, the size of this 210-wide and 297-high rectangle will be the ones in the "width" and "height" attributes (see above). You may have noticed that these numbers are given without units: their unit is named "user unit"... hereafter denoted "pixel".
- Wait, what?
- Also, to make things more complicated, when you are in the svg space, you draw in a cartesian infinite space metered in user units, but you can specify a lot of units0! We'll just say that "1 user unit" is called "1 pixel", even though there is absolutely no reason to name it a pixel, since it has next to nothing to do with physical screens (it's just an arbitrary length on a mathematical plane from which one rectangle is extracted to form a picture whose apparent size, whether px or cm, is defined elsewhere). And 1 = 1px = 1/90in = 1/35.43307cm = 1/3.543307mm = 1/15pc = 1/1.25pt. It's all in the SVG Specification1.
- But...
- And you can also use "em" (font-size) and "ex" (x-height) relative units here! Even "%"!
-...

Now, for harmonization with CSS2 (have you noticed that the px-to-other-units ratio is different in SVG and CSS? This should not be important as the content and length units in the SVG are quite arbitrary, but browser vendors sort of did not want to support both. If you ask me, there should never have been more than one unit in the SVG plane in the first place), the pixel to inch ratio has been informally changed in all viewers and editors to be 1inch=96px instead of 90, so that a centimeter is equal to 35.43307*96/90 "pixels" (that still are not real pixels but "user units").

Inkscape PNG export

When inkscape exports a png, you basically set a number of pixels and the "dpi" assumes that 96 user units are worth 1 real-life inch, so it sets that value into the pHYs PNG header. That makes sense.
Also, this should only affect images when printing them.

Inkscape change from 90 to 96 ``dpi'' and consequences

The main problem to understand here is that old files (pre-0.91) did not have a viewbox set by default. Their viewbox was inferred from the document size. If the document size was in pixels, the page was that size in user units, and if the document size was in inches, it was that size, ×90, in user units.

Two main things to note here: If your document size was defined 1inch-wide, then what measured 90 user units before, and thus was the size of the page, will not be that size anymore, since if going by the same guess about the page size, one will find the page is now 96userunits-long. On the other hand, if your document was defined as 90px-wide, what was 90px long will still be the same as the page size in Inkscape, but if you happen to send it to a printer, the printer will understand these 90px as 0.9375 inches on paper. Of course, I'll always recommend not printing directly from your drawing app and always do it through a DTP like scribus where you can resize vector stuff to the page and paper, but assuming you don't, it will happen.

The first problem can simply be solved by declaring a new viewbox with the old ratio. That's the ``set viewbox'' option. Your document will still be declared as 1in-wide, but with an explicit declaration that it's 90userunits-wide when you draw in this document. The drawback is that when you set a line to be 1inch-long (not measured directly in user units, but using inches in the document itself), it will be 96 userunits-long, so when printed, may measure on paper... 1.067in (what I called in the intro "real-life inches" vs "SVG user-unit-inches"). I'd recommend not using units in your documents. The other solution is to take all your elements and scale them by a 1.067 factor so that they match a new viewbox with the new coordinate plane. You may find bugs with scaling stuff like clones, offsets, masks, or other complex stuff.

The second problem is only a problem on for-print document that are already mislabeled as pixel-sized. To convert them, in both cases you have to scale the document size, then you have the same problem and dilemma as above.

Or, if it's not for print, just keep it with the old ratio, you have no reason to care what an inch is.

What's the deal with photoshop3?

Despite being a raster editor, the basic unit of photoshop seems to be the inch. Yes, you read me correctly, the inch. Then, it uses a constant 72dpi internal value2, or something like that. So when you import a 96ppi 96x96 png, it does not result in a 96px x 96px image in photoshop. It results in a 1in x 1in image, so, in terms of pixels... 72x72. Heh.

--
Mc


End note: the content of this file is what I understood from the world. There are still many things that are obscure to me, for instance what the deal with retina display is (why does it matter at all?). If I'm wrong about something, just email me and I'll do my best to update.

0: ``The original idea of SVG was that if you said a rectangle was 1cm high it would be drawn on the screen 1cm high regardless of what screen (or printer) one used. This proved to be useless as screens rarely reported back their correct pixel size.''

1: Update: What the SVG spec wrote is that the user agent defines itself what the px-to-in ratio is when using "a unit identifier from CSS", then makes a lengthy example when 1in = 90px. This results in a confusion where the standard seems to tell that this is the normal ppi. The standard does not explicitely tell which conversion rate to use (the CSS one or another), leaving that task to "the user agent". This has apparently lead all browser vendors to define it "like CSS and what's already done for HTML" and Inkscape "like the example in the standard". So that Inkscape and browsers both did things correctly, yet documents could look different. The SVG2 standard is much clearer on that issue and states that SVG does not only share with CSS its "list of unit identifiers" but "SVG follows the description and definition of common values and units from CSS".

2: This ratio comes from Adobe Postscript (then stayed in PDF)

3: Probably. This part is mostly speculative, as I don't have photoshop to test, but matches the inputs I've had about what happens.

Thanks to Tav for clarifications.