0
votes

For a plotting application, I am exploring using SVG and having trouble understanding how units of length apply to styles.

In this example, the width and height of the SVG element are given in inches and the rectangle and text are positioned properly in these user units, but font-size and stroke-width style attributes do not work as I expect with units.

text.labela {
  fill: green;
  font-family: sans-serif;
  font-size: 0.1in;
  stroke: none;
  text-anchor: middle;
}

text.labelb {
  fill: red;
  font-family: sans-serif;
  font-size: 0.1px;
  stroke: none;
  text-anchor: middle;
}

rect.box {
  fill: none;
  stroke: black;
  stroke-width: 0.01in;
}
<svg width="4in" height="4in" viewBox="0 0 4 4" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
      <rect class="box" x="0" y="0" width="4" height="4" />
      <text class="labela" x="2" y="3">a</text>
      <text class="labelb" x="2" y="2">b</text>
</svg>

In order to get 0.1in wide lines and 0.1in height text, what should I do?

(In my actual application, there are nested coordinate systems so I'd really like to be able to specify "0.1 inches" and have that work regardless of the viewports and coordinate systems.)

Thanks!

1
The S in SVG stands for scalable. There are no absolute sizes. - Robert Longson

1 Answers

2
votes

Every size you define is a size in the local coordinate system. The relative size of px and inches remains the same, device-dependent number in every local coordinate system you establish:

The absolute length units are fixed in relation to each other and anchored to some physical measurement. The absolute units consist of the physical units (in, cm, mm, pt, pc, q) and the visual angle unit (px)

The confusing thing is that writing sizes in CSS style sheets, you are sometimes forced to use a unit, even if it is "px". That is not a screen pixel, but the unit of the local coordinate system you would use without unit identifiers in attributes:

Note that at initialization, a user unit in the the initial coordinate system is equivalenced to the parent environment's notion of a px unit. Thus...because often the parent's coordinate system aligns with the device pixel grid, "5px" might actually map to 5 devices pixels. However, if there are any coordinate system transformation due to the use of transform or viewBox attributes, because "5px" maps to 5 user units and because the coordinate system transformations have resulted in a revised user coordinate system, "5px" likely will not map to 5 device pixels. As a result, in most circumstances, "px" units will not map to the device pixel grid.

The only sensible relation you can establish is to write everything inside your SVG in abstract units (adding "px" to satisfy CSS rules if needed), and then set the width/height of your SVG in inches.

After that, you have to compute for yourself: 1 viewport unit in the below example is mapped to 0.01in, so a font size of 10px equates to 0.1in, inside the scaled group it equates to 0.5in, so you would have to use a font-size of 2px to end up with the same real-world size.

(Do not use very small viewBox sizes. You will run into bugs.)

text.labela {
  fill: green;
  font-family: sans-serif;
  font-size: 10px;
  stroke: none;
  text-anchor: middle;
}

text.labelb {
  fill: red;
  font-family: sans-serif;
  font-size: 0.2in;
  stroke: none;
  text-anchor: middle;
}

.box {
  fill: none;
  stroke: black;
  stroke-width: 0.5;
}
<svg width="4in" height="4in" viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
      <rect class="box" x="0" y="0" width="400" height="400" />
      <line class="box" x1="0" y1="100" x2="400" y2="100" />
      <line class="box" x1="0" y1="90" x2="400" y2="90" />
      <line class="box" x1="0" y1="200" x2="400" y2="200" />
      <line class="box" x1="0" y1="150" x2="400" y2="150" />
      <text class="labela" x="150" y="100">ab</text>
      <text class="labelb" x="250" y="100">ab</text>
      <g transform="scale(5)">
           <text class="labela" x="30" y="40">ab</text>
           <text class="labelb" x="50" y="40">ab</text>
      </g>
</svg>