@isopodlabs/dwg
v1.2.1
Published
Self contained package for DWG reading, writing and manipulation.
Readme
@isopodlabs/dwg
Reads AutoCAD DWG drawings, from R13 to R2018, and ASCII DXF files into the same TypeScript classes. It is written in TypeScript with no native code.
☕ Support My Work
If you use this package, consider buying me a cup of tea to support future updates!
Installation
npm install @isopodlabs/dwgSupported files
| Format | Versions | |--------|----------| | DWG | R13 (AC1012), R14 (AC1014), R2000 (AC1015), R2004 (AC1018), R2007 (AC1021), R2010 (AC1024), R2013 (AC1027), R2018 (AC1032) | | DXF | ASCII DXF of any of those versions, and R12 |
Binary DXF, R13c3 (AC1013) DWG and DWGs before R13 are not read yet. Writing is not supported.
Reading a DWG
import { DWG, DRW_LINE } from '@isopodlabs/dwg';
const dwg = new DWG('drawing.dwg');
if (!await dwg.ready)
throw new Error('not a DWG file of a supported version');
console.log(dwg.version); // 1015 for R2000; see VER
console.log(dwg.vars.UCS.EXTMIN); // header variables
for (const { h } of dwg.handles) {
const obj = await dwg.get_object(h);
if (obj instanceof DRW_LINE)
console.log(obj.point1, obj.point2);
}
await dwg.close();The file stays open so that objects can be read when they are asked for, so call close() when you are done.
ready resolves false if the file is not a DWG of a supported version. It rejects with an error that says what is
wrong if the file is a DWG that cannot be read, for example if a section is corrupt.
DWG
class DWG {
constructor(source: string | Uint8Array); // a file name, or the file's bytes
ready: Promise<boolean>;
version: VER;
maintenanceVersion: number;
vars: HeaderVars; // the header variables
classes: Map<number, MoveableClass>; // the custom classes, by type number
handles: {h: number, loc: number}[]; // every object's handle and file location
get_object(handle: number): Promise<Obj | undefined>;
owned(owner: number, refs: H[], unsupported?: Map<string, number>): Promise<Entity[]>;
contents(): Promise<{
layers: Map<string, DRW_LAYER>;
blocks: Map<string, {block: DRW_BLOCK_HEADER, entities: Entity[]}>;
entities: Entity[]; // model space's and the active paper space's
unsupported: Map<string, number>; // entities not read, by the reason
}>;
close(): Promise<void>;
// the symbol tables
blocks: Table<DRW_BLOCK_HEADER>;
layers: Table<DRW_LAYER>;
textstyles: Table<DRW_STYLE>;
linetypes: Table<DRW_LTYPE>;
views: Table<DRW_VIEW>;
ucss: Table<DRW_UCS>;
vports: Table<DRW_VPORT>;
appids: Table<DRW_APPID>;
dimstyles: Table<DRW_DIMSTYLE>;
vpEntHeaders: Table<DRW_VP_ENT_HDR>;
// the entries of dictionaries
groups: Table<DRW_GROUP>;
mlinestyles: Table<DRW_MLINESTYLE>;
layouts: Table<DRW_LAYOUT>;
plotsettings: Table<DRW_PLOTSETTINGS>;
}get_object decodes an object the first time it is asked for, and keeps it only while something else references it.
It returns undefined for a handle that is not in the file, or an object of a type that has no class yet.
A Table holds the handles of a symbol table's records or a dictionary's entries, and iterates the objects:
for await (const layer of dwg.layers)
console.log(layer.name, layer.color);contents() gives the drawing in the shape a DXF has, so the same code can read either: every block's entities
(from its list of handles, or before R2004 by following each entity's link to the next), with each entity's layer
named, a polyline's vertices and an insert's attribs read into it, and an insert's block_name set. A DWG names
every anonymous block *D, *T... and every layout's paper space *Paper_Space; contents() numbers each after the
first from 0, as a DXF writer numbers them (though not with the same numbers). An entity whose layout is known not to
be read, such as the modeler geometry R2013 keeps in its AcDs section, is counted in unsupported rather than failing
the drawing. owned() is what it uses to read the entities a block header, polyline or insert owns.
Colours
An entity's or layer's color is a Color: an index into the AutoCAD Colour Index (ACI, as 0xRRGGBB), or a true
colour. rgbOf(color, layerColor?) gives its 0xRRGGBB whichever it is, a BYLAYER one as the layer's, and
undefined for BYBLOCK, which the insert decides.
Handles
Objects refer to one another by handle, a number that get_object looks up. An entity's layer, for example, is
entity.layerH, and await dwg.get_object(entity.layerH) is its DRW_LAYER. Fields that hold a handle directly as
read from the file are H objects, whose offset() is the handle and whose get_offset(obj.handle) resolves one that
is relative to the object holding it.
Important handles in the header include vars.BLOCK_MODEL_SPACE and vars.BLOCK_PAPER_SPACE (the block records of
model and paper space), and the table controls such as vars.LAYER_CONTROL.
Reading a DXF
import { readFileSync } from 'fs';
import { DXF, DRW_CIRCLE } from '@isopodlabs/dwg';
const dxf = DXF.read(readFileSync('drawing.dxf', 'utf8'));
for (const e of dxf.entities)
if (e instanceof DRW_CIRCLE)
console.log(e.centre, e.radius);DXF
class DXF {
static read(data: string | Uint8Array): DXF;
version: VER;
vars: Map<string, DXFRecord>; // header variables, by name
tables: Map<string, Obj[]>; // symbol tables, by name
layers: Map<string, Obj>; // layers, by name
blocks: Map<string, {block: Obj, entities: Obj[]}>; // blocks, by name
entities: Obj[]; // the ENTITIES section
objects: Obj[]; // the OBJECTS section
unsupported: Map<string, number>; // types that have no class yet, with counts
}A DXF entity names its layer and linetype (layer, linetype) where a DWG one holds their handles. A POLYLINE's
vertices are in its vertices, and an INSERT's attributes in its attribs. Angles are converted to radians, as a
DWG stores them.
Objects
Every object is an Obj, and every entity is also an Entity:
class Obj {
handle: number;
parentH: number; // the owner
reactors: number[];
xdict?: number; // the extension dictionary
eed?: Map<number, Resbuf[]>; // extended data, by application handle
}
class Entity extends Obj {
entmode: number; // 0: owned by parentH, 1: paper space, 2: model space
invisible: boolean;
color?: Color;
layerH: number;
linetypeH: number;
linetypeScale: number;
lWeight: LineWidth;
plotstyleH, materialH, shadowH: number;
graphics_data?: Uint8Array; // proxy or preview graphics
}
interface Color {
index: number; // the colour index: 0 by block, 256 by layer
rgb?: number;
name?: string;
book_name?: string;
alpha?: number;
handle?: H;
}Entities
| Class | DWG type | Main fields |
|-------|----------|-------------|
| DRW_LINE | LINE | point1, point2, thickness, ext_point |
| DRW_POINT | POINT | point, thickness, x_axis |
| DRW_CIRCLE | CIRCLE | centre, radius |
| DRW_ARC | ARC | centre, radius, angle0, angle1 |
| DRW_ELLIPSE | ELLIPSE | point1 (centre), point2 (major axis), ratio, angle0, angle1 |
| DRW_LWPOLYLINE | LWPOLYLINE | vertlist (x, y, bulge, width0, width1), closed, elevation |
| DRW_POLYLINE_2D, _3D, _PFACE, _MESH | POLYLINE | handles of the vertices; DXF: vertices |
| DRW_VERTEX_2D, _3D, _MESH, _PFACE, _PFACE_FACE | VERTEX | point, flags; 2D: bulge, widths |
| DRW_SPLINE | SPLINE | degree, knots, ctrl_pts (with weights w) or fit_pts |
| DRW_TEXT | TEXT | text, insert_point, height, angle, styleH |
| DRW_MTEXT | MTEXT | text, point1, height, widthscale, styleH |
| DRW_ATTRIB, DRW_ATTDEF | ATTRIB, ATTDEF | text fields, tag; ATTDEF: prompt |
| DRW_INSERT, DRW_MINSERT | INSERT, MINSERT | block (handle), base_point, scale, angle; MINSERT: rows and columns |
| DRW_BLOCK, DRW_ENDBLK, DRW_SEQEND | BLOCK, ENDBLK, SEQEND | BLOCK: name |
| DRW_SOLID, DRW_TRACE | SOLID, TRACE | point1–point4, elevation, thickness |
| DRW_FACE_3D | 3DFACE | point1–point4, invisibleflag |
| DRW_RAY, DRW_XLINE | RAY, XLINE | point1, point2 (direction) |
| DRW_HATCH | HATCH | loops (lines, arcs, ellipses, splines or polylines), pattern, solid, seeds |
| DRW_DIMENSION_* | DIMENSION | text_midpt, user_text, styleH, blockH, and the points of each kind |
| DRW_LEADER | LEADER | vertexlist, annot_type, AnnotH, styleH |
| DRW_IMAGE, DRW_WIPEOUT | IMAGE, WIPEOUT | point1, u_vector, v_vector, size, clip_verts |
| DRW_VIEWPORT | VIEWPORT | point, pssize, view |
| DRW_MULTILEADER | MULTILEADER | leaders (lines of points), content (text or block), style overrides |
| DRW_ARC_DIMENSION | ARC_DIMENSION | as the other dimensions, and the arc's centre and parameters |
| DRW_HELIX | HELIX | the fields of a spline, and axis_base_pt, radius, turns, turn_height |
| DRW_MLINE | MLINE | verts, each with its direction, miter and the parameters of each line |
| DRW_TOLERANCE | TOLERANCE | ins_pt, x_direction, text |
| DRW_MESH | MESH | vertex, faces, edges, crease |
| DRW_SOLID_3D, DRW_REGION, DRW_BODY, DRW_PLANESURFACE | 3DSOLID, REGION, BODY, PLANESURFACE | acis (SAT text or SAB bytes), wireframe |
| DRW_LIGHT | LIGHT | type, position, target, intensity, color, photometric |
| DRW_PDFUNDERLAY | PDFUNDERLAY | definition, ins_pt, scale, angle, clip_verts |
| DRW_OLE2FRAME | OLE2FRAME | type, data |
| DRW_SECTIONOBJECT | SECTIONOBJECT | verts, vert_dir, top_height, bottom_height |
Table records and other objects
DRW_BLOCK_HEADER, DRW_LAYER, DRW_STYLE, DRW_LTYPE, DRW_VIEW, DRW_UCS, DRW_VPORT, DRW_APPID,
DRW_DIMSTYLE and DRW_VP_ENT_HDR are table records, each with a name. A block record lists its entities in
entities (R2004 and later) or links its first and last in first_last.
The table control objects (ObjControl) and DRW_DICTIONARY give the handles of what they hold in handles; a
dictionary also has the entry names. DRW_XRECORD holds data, its group codes and values (Resbufs).
The other objects read are layouts and plot settings; groups; multiline, table, multileader, visual, section view and detail view styles and cell style maps; materials; scales; dictionary variables; fields and field lists; image definitions and reactors; sort tables; spatial filters and indexes; layer indexes; colours; raster variables; the annotation scale contexts of text, dimensions and leaders; geographic data; the sun; render settings and entries; section managers and settings; table geometry; the parameters, grips and evaluation graphs of dynamic blocks; the history of solids; and the associative network: actions, dependencies and action parameters.
ACAD_TABLE is read only as far as the fields every entity has; its graphics are in its anonymous block. A few
types are read no further than that, and some newer or third party types have no class: for these get_object
returns undefined. Where a file has something whose layout is not known, such as the modeler geometry R2013 keeps
in its AcDs section, reading it throws an error saying it is not supported.
Versions
VER names the versions by their release (VER.R2000 is 1015, VER.R2018 is 1032), so versions compare as numbers:
if (dwg.version >= VER.R2007)
...License
This project is licensed under the MIT License.
