The two bits that sent our DWG reader the wrong way

Our DWG reader decoded one field with the wrong encoding. The number looked plausible, became the plan, and the plan solved a problem that did not exist.

cicada, the CAD editor on this site, has to open DWG files. AutoCAD writes DWG and does not document it, so we built the reader one layer at a time and checked each layer against the same drawing exported as DXF, a format we could already read.

That check is the whole method. A binary format never tells you that you read it wrong. Read a field one bit early and you still get a number, it still looks like a number, and everything after it believes it.

The number#

Every object in a DWG says what type it is. Our reader counted the 21,241 objects in a real drawing: 21,072 came back as custom types, the kind an add-on defines and only that add-on knows how to draw. Only 169 were ordinary lines, arcs and text.

If that were true, reading ordinary shapes would be almost worthless. The only way to show the drawing would be to decode the preview pictures that custom objects sometimes carry, a much bigger and much less certain job. It became the plan and stayed at the top of the roadmap for months.

What the field really is#

From the 2010 format on, an object type is not a plain number. It starts with two bits that say how to read the rest: one byte, one byte plus 496, or two bytes. Our reader used the older encoding and treated those two bits as part of the value, which pushed every type code into the range reserved for custom objects.

Read asOrdinary typesCustom typesMost common codes
The old way16921,072785, 845, 787
Correctly21,11612517, 77, 19
AutoCAD's DXF export——ARC, LWPOLYLINE, LINE
Codes 17, 77 and 19 are arc, polyline and line. The counts match the exported drawing exactly: 6,734 arcs, 5,850 polylines and 4,762 lines.

The corrected reading is not just more plausible. It matches AutoCAD's own export of the same drawing type for type: thirteen shape types, thirteen exact counts. A wrong reading does not do that.

Why the tests did not catch it#

We had fifty-eight tests, and they were green. One of them checked that the drawing had more custom objects than ordinary ones.

What found it was a small program that did nothing but print what the bytes said, run against the real file and compared with the DXF export. We wrote five of these probes in an afternoon, one per layer of the format. They now live next to the reader, because the last set was kept as scratch files and lost.

Where it left us#

  • The reader decodes 19,067 shapes from the drawing. AutoCAD's export of the same drawing has 19,067.
  • On screen, both produce 27,028 elements with the same bounding box and the same thirteen colours.
  • The big speculative job would add 23 shapes out of 19,067. It is no longer the plan.

Two bugs in our DXF reader surfaced along the way, in code that had shipped for months. Rotated labels were drawn flat, because the angle is stored as a direction rather than a number and we read the number. Long labels were cut to their last piece, because text over 250 characters arrives in chunks and we kept only the final one. Neither showed up until a second, independent reader disagreed.

The part worth keeping#

A number your own code produced is not a measurement yet. It becomes one when something you did not write agrees with it.

The note in the repository said “ninety-nine percent custom” for months, and everyone who read it treated it as a fact about the file. It was a fact about a bug. The cheapest way to tell the two apart is to go and look.