EST. 2026 — Notes from real server work SYSADMIN · SECURITY · AI · DIRECTADMIN Issue 01
THE SYSADMIN ALMANAC
Home / Hardware / Drawing SVG Electrical Panels You Can Measure on Paper
Hardware

Drawing SVG Electrical Panels You Can Measure on Paper

by Ton-Or ·24 Oct 2026·9 min read
Drawing SVG Electrical Panels You Can Measure on Paper

A drawing a technician uses to build something cannot be even slightly out of proportion, because they do more than read it. They also put a ruler against the paper. If the dimensions on the drawing do not match the distances on paper, the technician is the one who drills the plate incorrectly, but the person who drew it is the one actually at fault.

I had a customer's electrical panel job that needed drawings for the technician to build from. I initially generated the drawings from HTML as SVG and sent the file over for review. The reply was one short sentence saying the drawing was not properly proportioned and was off scale.

At the time, I thought it was probably only slightly off, until I went back and measured my own work. Then I realized it was not slightly off. Different parts of the same sheet were at different scales.

This article is not about printing

I have already covered browser printing mechanics, including @page, controlling page margins, and aligning multipage documents with the page edges, in Creating Dozens of Pages from HTML That Print with Correct Margins. Extracting dimensions from drawings whose originals are not to scale is covered in Extracting Dimensions from a PDF That Is Not Drawn to Scale. Here I am interested in just one thing: the dimensional accuracy of drawings technicians build from, and how to keep the dimensions on the drawing, the distances on screen, and the distances on paper consistent throughout.

What was wrong with the original: three views, three scales

In my first version, I gave each view its own <svg>: one front view with the doors closed, one with the doors open, and one side view. Then I arranged all three in a flex container to make them look tidy.

The problem was that each SVG had its own viewBox, and the containers around them had different widths, so the browser scaled each one by a different factor. On screen, it looked like a complete, well-formed drawing, but the front and side views had been at different scales from the start. There was no way to compare them with a ruler.

Worse still were the numbers I used to draw it. I drew the breaker box as 150 x 250 because it "looked right," while the label beside it already showed the correct catalog dimensions of 140 x 257. The drawing told the truth in its text but lied in its shapes.

Drawing from pixels

Taking proportions from reference images

I measured product images I found online, then rounded the dimensions to convenient numbers. The cabinet proportions ended up changing three times along the way, and nothing in the file could tell me which numbers were real.

Drawing from the catalog

Actual dimensions as the anchor

Every rectangle uses the outline dimensions published by the manufacturer, with no rounding. If a component does not fit, the cabinet really is too small; the drawing is not wrong. That is something I need to know before ordering fabrication, rather than when the technician starts assembly.

One rule fixes it all: one SVG, one viewBox, 1 unit = 1 mm

I rebuilt the whole sheet with just one <svg> per sheet of paper. Every view shares the same coordinate system, and one viewBox unit is one real millimeter. There are no scale transforms on the geometry and no SVGs nested inside SVGs. All remaining transforms are pure translations to position the views.

With this rule in place, the numbers in the file and the numbers in the manufacturer's quotation are identical. Reading the code tells me immediately how wide the cabinet is.

<svg class="ga" viewBox="0 0 3350 2450">
  <!-- ตู้ W1000 x H1600 -->
  <rect x="150" y="200" width="1000" height="1600" class="out"/>
  <!-- แบ่งบาน 700 (เบรกเกอร์) + 300 (มิเตอร์) -->
  <line x1="850" y1="200" x2="850" y2="1800" class="part"/>
  <!-- หน้าปัดมิเตอร์ 96x96 / สวิตช์เลือก 64x64 -->
  <rect x="952" y="380" width="96" height="96" class="mtr"/>
  <rect x="968" y="820" width="64" height="64" class="mtr"/>
  <!-- ขาตั้งเหล็กกล่อง 2 นิ้ว สูง 400 -->
  <rect x="250" y="1800" width="70" height="400" class="leg"/>
</svg>

Line thickness is also specified in real millimeters. I put everything in a <style> inside <defs> and used class names as drafting layers. The colors are not decorative; they have meaning. Red is reserved for dimension lines, and gray dashed lines mark spare spaces where no equipment is installed yet.

.out   { fill:#fbfcfe; stroke:#161b22; stroke-width:6 }   /* 6 มม.จริง = 0.6 มม.บนกระดาษ */
.innercv{ fill:#f2f4f8; stroke:#5a6470; stroke-width:4 }
.dev   { fill:#fbeee6; stroke:#b5411b; stroke-width:4 }
.devs  { fill:#eef1f5; stroke:#7a828f; stroke-width:4; stroke-dasharray:13 8 }
.dim   { stroke:#c0392b; stroke-width:2.4 }               /* เส้นบอกระยะ */
.ext   { stroke:#c0392b; stroke-width:1.6; stroke-dasharray:6 5 }
.dimt  { font:700 27px Sarabun; fill:#c0392b }            /* 27 มม.จริง = 2.7 มม.บนกระดาษ */
Dashes are in millimeters too

stroke-dasharray:13 8 at 1:10 produces 1.3 mm dashes with 0.8 mm gaps, which are still clearly visible. But setting it below roughly 5 4 turns it into a solid grayish line on an ordinary laser printer. Text works the same way: 27 units becomes 2.7 mm on paper and is comfortable to read, while 18 units becomes 1.8 mm, the lower limit I will accept. Any smaller and the technician needs glasses.

The anchor dimensions must be catalog outline dimensions

This is the heart of this article. Every dimension I drew came from manufacturer documents or the actual dimensions ordered for fabrication. None came from eyeballing it.

1000x1600Cabinet, width x height
620x1510Equipment mounting plate
140x257Main MCCB
92x92Meter cutout
  • Floor-standing cabinet W1000 x H1600 x D400 mm. Doors split into 700 and 300 sections, with 2-inch square steel tube legs adding another 400 in height and a rain canopy projecting 45.
  • Equipment mounting plate 620 x 1510 mm, 2.0 mm thick. Set (0,0) at the plate's upper-left corner and specify every hole's coordinates relative to that single point.
  • Main MCCB 140 x 257 · large branch breakers 105 x 165 · small branch breakers 75 x 130 for every frame · three ring CTs, 78 x 78 each.
  • The meter face on the hinged door is 96 x 96, but the actual cutout is 92 x 92, allowing 2 mm of overlap on each side. Selector switches and indicator lamps need 22 mm holes.

For the holes, I also marked no-drill zones: the busbar runs at x 140-240 and 375-475. No holes are allowed in those strips, because discovering the problem after drilling means replacing the entire plate.

Just as importantly, I printed a hole coordinate table on the same sheet. Technicians who want to measure from the paper can do so, and those who want to enter coordinates into a machine can read the table. I do not need to choose for them.

<tr><td>MAIN</td><td>MCCB 3P 350A</td><td>240</td><td>110</td><td>140 x 257</td></tr>
<tr><td>1</td><td>MCCB 3P 150A</td><td>15</td><td>630</td><td>105 x 165</td></tr>
<tr><td>2</td><td>MCCB 3P 100A</td><td>30</td><td>825</td><td>75 x 130</td></tr>

Dimension lines need to be assembled by hand

SVG has no dimension object like drafting software does, so I had to build one myself: two arrowheads as markers, one dimension line, and two thin dashed extension lines running out from the edges of the part.

<defs>
  <marker id="ah"  markerWidth="9" markerHeight="9" refX="8" refY="3" orient="auto">
    <path d="M0,0 L8,3 L0,6 z" fill="#c0392b"/></marker>
  <marker id="ah2" markerWidth="9" markerHeight="9" refX="1" refY="3" orient="auto">
    <path d="M8,0 L0,3 L8,6 z" fill="#c0392b"/></marker>
</defs>

<line x1="300" y1="245" x2="920" y2="245" class="dim"
      marker-start="url(#ah2)" marker-end="url(#ah)"/>
<line x1="300" y1="238" x2="300" y2="300" class="ext"/>
<line x1="920" y1="238" x2="920" y2="300" class="ext"/>
<text x="610" y="232" text-anchor="middle" class="dimt">620</text>

markerWidth is not in millimeters. Under the default markerUnits setting, it is tied to stroke-width. Arrowheads are therefore the only things in the drawing that I deliberately do not give real dimensions, because making them millimeter-based too would leave the arrowheads on a 1:10 drawing so small they would disappear.

For vertical dimensions, I rotate the text around its own anchor point, then work outward through the dimension chains one lane at a time. Each lane is 60 units, or 6 mm on paper, which gives two-digit numbers enough room not to collide.

<line x1="115" y1="300" x2="115" y2="1125" class="dim"
      marker-start="url(#ah2)" marker-end="url(#ah)"/>
<text x="105" y="712" class="dimt" transform="rotate(90 105 712)">825</text>
<line x1="55" y1="300" x2="55" y2="1285" class="dim"
      marker-start="url(#ah2)" marker-end="url(#ah)"/>
<text x="45" y="792" class="dimt" transform="rotate(90 45 792)">985</text>

Every chain starts from the same datum: the plate's upper-left edge. I do not dimension successively from one feature to the next, because that allows errors to accumulate. The technician ends up with the last hole out of position and no way to know which hole the error came from.

A scale bar on paper is a promise a ruler can check

The most worthwhile thing on the drawing sheet, in my view, is the scale bar in the bottom corner. It consists of five alternating black and white rectangles, each representing 100 mm in real life, for a total of 500 mm. On paper printed at the correct scale, it must measure exactly 50 mm.

<g transform="translate(150,2380)">
  <rect x="0"   y="0" width="100" height="24" fill="#161b22"/>
  <rect x="100" y="0" width="100" height="24" fill="#fff" stroke="#161b22" stroke-width="2"/>
  <rect x="200" y="0" width="100" height="24" fill="#161b22"/>
  <rect x="300" y="0" width="100" height="24" fill="#fff" stroke="#161b22" stroke-width="2"/>
  <rect x="400" y="0" width="100" height="24" fill="#161b22"/>
  <text x="0" y="56" class="t2s">0</text>
  <text x="500" y="56" text-anchor="middle" class="t2s">500 mm</text>
  <text x="560" y="18" class="t2s">แถบนี้ = 500 มม.จริง = 50 มม.บนกระดาษ (สเกล 1:10)</text>
</g>

I need it not because I distrust my own code, but because nothing in CSS can stop someone from checking Fit to page. However carefully I lock the width in millimeters, one click at the receiving end to shrink it to fit the paper puts the drawing off scale immediately, without warning.

A 500 mm scale bar: a promise a ruler can checkPrint at 100%100 mm each, 500 mm totalExactly 50 mm on paper = correct 1:10 scaleFit to pageStill 500 mm in the file, shrunk on paperNot 50 mm by ruler = wrong scale, no warning
The scale bar measures 50 mm when printed at 100 percent. If someone shrinks it to fit the page, the bar immediately gets shorter, and the technician can see that the drawing is off scale.
Automatic file converters are the enemy of scale drawings

I already had an HTML-to-PDF conversion script in the same project. Its clever feature was that if the content exceeded one page, it would reduce the scale and compensate by increasing the width to fit A4 automatically. That is the right behavior for quotations, but it completely destroys a scale drawing. For the cabinet drawing, I therefore had to print directly through the browser's print dialog at 100% only. When content overflowed the page, I fixed it by reducing the viewBox height, rather than shrinking the whole sheet.

The content on this sheet overflowed the page once. I reduced the viewBox height from 2720 to 2600 and finally to 2450, without touching the width at any point. The width determines the scale; the height is just a question of what will fit on the sheet.

Check with pixels, but do not draw with pixels

After fixing the drawing, I went back to measuring pixels, but this time I changed their role from "source of dimensions" to "verification tool." I rendered the drawing sheet as an image, found connected components by line color, took their bounding boxes, subtracted the line thickness, and divided by pixels per millimeter to compare the results with the catalog dimensions.

PXMM = 636 / 800.0        # วัดจากขอบตู้ในภาพที่ render ออกมา

def row(name, box, stroke, ref):
    x, y, w, h = box
    W = (w - stroke * PXMM) / PXMM     # ลบความหนาเส้นออกก่อน
    H = (h - stroke * PXMM) / PXMM
    print('%-28s %-10s %-14s %s' % (name, '%dx%d' % (w, h),
                                    '%.0f x %.0f' % (W, H), ref))

for b in mains[:1]:
    row('MAIN MCCB (กรอบน้ำเงิน)', b, 5, '140 x 257')

Every result matched the catalog. My favorite number was the cabinet's overall aspect ratio: I measured 1274 / 636 = 2.003, compared with 1600/800 = 2.000. The difference was 0.15%, entirely explainable by line thickness rather than incorrect geometry.

Line thickness takes up millimeters

A rectangle with a width of 1000 and stroke-width:6 actually occupies 1006, because half the stroke lies on either side of the edge. That is fine when looking at it, but I always have to subtract it before measuring. When locating drill holes, use the coordinate table, not the ink marks. Also, never put vector-effect="non-scaling-stroke" in a scale drawing, because the lines must shrink with the drawing; otherwise, their thickness will no longer correspond to millimeters.

The remaining traps, and two drawings offset by 50 mm

The path of a single dimensionCatalog140 x 257->viewBox1 unit = 1 mm->PrintLock width in mm->Scale bar500 -> 50 mm->Technician's ruler

There are two things I have not yet fixed in the files, and they are lessons worth discussing rather than hiding.

First, on the subpanel sheet I added later, I labeled the scale as 1:5, but dividing the locked print width by the viewBox size gives approximately 1:6. The label does not match reality, and that sheet has no scale bar, so nothing exposes the error. This is why every sheet needs a scale bar, not just the main sheet.

Second, my assembly drawing and plate drilling drawing place the meter face on the door 50 mm apart. One sheet centers it on the door, while the other shifts it toward one side, even though it is the same part. The technician drilling the holes will follow the plate drilling sheet, while the person reviewing the overall layout will see something different, and they will end up arguing on site.

One part cannot have two originals

If the same feature is drawn in two places in two files, only one file can be the source. The others must be generated from it, or at least checked by a script that compares coordinates between the two files before delivery. I made the mistake because I edited each file manually and thought I remembered the numbers in the other file.

The steps I reuse on every job

  1. Collect all actual dimensions from catalogs and fabrication orders first. Do not start drawing with guessed numbers.
  2. Use one SVG per sheet, with the viewBox in millimeters and every view in the same coordinate system.
  3. Lock the print width in millimeters so the scaling factor works out cleanly, such as 0.1 for 1:10.
  4. Put a scale bar on every sheet, with text stating how many millimeters it must measure.
  5. Print one actual sheet and check it with a ruler before sending it to the technician.
  6. Render it as an image, then measure it back with a script and compare it with the coordinate table, not by eye.

Personally, I think what this job really taught me was not about SVG. It was that an engineering drawing and a nice-looking illustration are different things. An illustration with incorrect proportions does not cause anyone trouble, but if a drawing used for fabrication puts a drill hole 10 mm out of position, the cost is an entire plate and another half day of the technician's time. The person asked why it does not line up is the person who drew it.

If you make drawings for technicians using HTML, feel free to try adding the 1 unit = 1 mm rule and a scale bar. These two things add less than ten minutes per sheet, but they turn "a picture that looks like a drawing" into a drawing that can actually be checked. If I have left anything out of the explanation or made a typo, I will come back and correct it later.

Hit something like this? Stuck somewhere?

Come trade notes on the LinuxThai board

→ Continue on the board

Comments 0

No comments yet — be the first