DWG to DXF Conversion¶
ezdwg can convert DWG files to DXF format using ezdxf as the writing backend.
pip install "ezdwg[dxf]"
Basic Usage¶
From a File Path¶
import ezdwg
result = ezdwg.to_dxf("input.dwg", "output.dxf")
print(f"Written: {result.written_entities}/{result.total_entities}")
From a Document¶
doc = ezdwg.read("input.dwg")
result = doc.export_dxf("output.dxf")
From a Layout¶
doc = ezdwg.read("input.dwg")
msp = doc.modelspace()
result = msp.export_dxf("output.dxf")
Options¶
Filtering by Entity Type¶
result = ezdwg.to_dxf(
"input.dwg",
"output.dxf",
types="LINE ARC LWPOLYLINE",
)
DXF Version¶
result = ezdwg.to_dxf(
"input.dwg",
"output.dxf",
dxf_version="R2010", # default
)
Supported DXF versions: R2000, R2004, R2007, R2010, R2013, R2018.
Strict Mode¶
In strict mode, conversion fails if any entity cannot be written:
result = ezdwg.to_dxf(
"input.dwg",
"output.dxf",
strict=True,
)
ConvertResult¶
to_dxf() returns a ConvertResult with:
| Attribute | Type | Description |
|---|---|---|
source_path |
str |
Input DWG path |
output_path |
str |
Output DXF path |
total_entities |
int |
Total entities processed |
written_entities |
int |
Entities successfully written |
skipped_entities |
int |
Entities that could not be written |
skipped_by_type |
dict[str, int] |
Skip counts grouped by type |
Supported Entity Types¶
The following entity types can be written to DXF:
LINE, RAY, XLINE, POINT, ARC, CIRCLE, ELLIPSE, LWPOLYLINE, POLYLINE_2D, POLYLINE_3D, POLYLINE_MESH, POLYLINE_PFACE, 3DFACE, SOLID, TRACE, SHAPE, SPLINE, TEXT, ATTRIB, ATTDEF, MTEXT, LEADER, HATCH, TOLERANCE, MLINE, INSERT, MINSERT, DIMENSION
Limitations¶
- Conversion is best-effort — some entities may be skipped if they cannot be represented in DXF.
- Block definitions referenced by INSERT entities are reconstructed from the DWG source.
- DIMENSION entities use native ezdxf dimension builders with a text fallback for unsupported subtypes.
- Style, linetype, and layer properties may not be fully preserved.
Native DWG Writer (AC1015)¶
ezdwg also provides a native DWG writer for AC1015.
import ezdwg
result = ezdwg.to_dwg(
"input.dwg",
"output.dwg",
version="AC1015",
types="LINE ARC CIRCLE LWPOLYLINE TEXT MTEXT",
)
print(result.written_entities, result.skipped_by_type)
You can also call:
doc = ezdwg.read("input.dwg")
doc.export_dwg("output.dwg", version="AC1015")
Current native writer scope:
- Version:
AC1015only - Entities:
LINE,RAY,XLINE,POINT,ARC,CIRCLE,ELLIPSE,SPLINE,SOLID,TRACE,3DFACE,LWPOLYLINE,POLYLINE_2D,POLYLINE_3D,TEXT,MTEXT,INSERT,ATTDEF,ATTRIB,HATCH,DIMENSION(all seven kinds),IMAGE - Named/anonymous static block definitions, including nested INSERTs and attached attributes
- Raster image definitions (
IMAGEDEF,IMAGEDEF_REACTOR,RASTERVARIABLES,ACAD_IMAGE_DICT) and aStandardDIMSTYLE - True colours are rounded to the nearest ACI (R2000 stores ACI only) and reported as
approximated
A file path or Document exports model space. It does not export block
definitions as loose geometry. An explicitly supplied Layout selects that
layout's entities, which are written into the new file's model space. Referenced
block definitions and attached attributes are included as dependencies.
The writer builds a new AC1015 database with standard tables, model/paper space records, layouts, dictionaries, entity links and CRCs. It has no runtime dependency on an external converter or a template drawing. This is a basic entity export. It preserves the following decoded attributes:
- Referenced layers: name, ACI color, on/off, frozen, frozen in new viewports, locked, plot flag, lineweight and simple linetype.
- Entity ACI (including
0/BYBLOCK and256/BYLAYER), linetype selection and scale, lineweight and invisibility. Simple dash/gap/dot patterns are supported. - Drawing units (
INSUNITS,LUNITS,LUPREC,AUNITS,AUPREC) andLTSCALE. - TEXT/MTEXT content and basic text styles (font/bigfont names, height, width, oblique, vertical and generation flags). TEXT alignment and MTEXT line spacing are retained. Font files are not embedded; display still depends on installed fonts.
- Thickness/extrusion for LINE, ARC, CIRCLE, POINT and TEXT; MTEXT extrusion; LWPOLYLINE closure, elevation, extrusion, thickness, widths and bulges.
- Ordinary legacy 2D POLYLINE closure, continuous linetype, default/per-vertex widths, bulges, elevation, extrusion and thickness; ordinary 3D POLYLINE WCS vertices and closure. VERTEX/SEQEND ownership and links are rebuilt.
- Full/partial ELLIPSE geometry: WCS center, semi-major axis, normal, minor/major ratio and radian parameters. Ellipses have no thickness property.
- SOLID/TRACE stored OCS corners with shared Z, thickness and extrusion; 3DFACE WCS corners and invisible edge flags.
- SPLINE control points, knots, rational weights, closed/periodic flags and tolerances; open cubic fit points, fit tolerance and endpoint tangents.
- HATCH boundary paths (polyline with bulges, or line/arc/ellipse/spline edges), solid or pattern fill with its definition lines, style, elevation and extrusion.
- DIMENSION definition points, text override, measurement, attachment and the
anonymous block that holds the rendered graphics; the
StandardDIMSTYLE. - IMAGE placement (insertion, pixel vectors, size, display properties, brightness, contrast, fade, clip boundary) with its IMAGEDEF file path and pixel size.
The high-level writer chooses Windows-1252 or Windows-932 for the whole drawing, including layer/style names. Japanese text is supported. Characters that cannot be represented in the selected encoding are explicitly reported before replacement in non-strict mode. Invalid table names or unresolved raw references are rejected even in non-strict mode. Unicode/MIF escapes emitted by external legacy writers are recognized when reading.
With the default types=None, WriteResult counts all selected non-structural
entities, including unsupported types and entities that failed to decode.
An explicit types filter selects root entities. An INSERT also brings its
referenced definitions (transitively) and attached attributes. Counts include
those entities once per definition, plus each attached ATTRIB; structural BLOCK,
ENDBLK and SEQEND records are not counted.
strict=True rejects skipped entities and detected attribute loss before
opening the destination. strict=False reports skipped geometry in
skipped_by_type and attribute defaults, drops or replacements in diagnostics.
The CLI prints these diagnostics as warnings. A written entity may still have
attribute diagnostics even when skipped_entities == 0:
result = ezdwg.to_dwg("input.dwg", "output.dwg")
for issue in result.diagnostics:
print(issue.handle, issue.dxftype, issue.attribute, issue.action, issue.message)
Examples of reported losses include TrueColor (including RGB black), complex linetypes, MTEXT backgrounds, transparency, XDATA, extension dictionaries, nondefault plot/material/visual-style references, extended font/style data and unresolved attributes. NaN/Infinity, nonpositive radii or text heights, zero direction vectors and invalid polyline widths are rejected; non-strict high-level export skips invalid geometry. The raw writer also validates before opening its output file and raises on invalid input. It does not silently truncate extra polyline data or replace text bytes.
This is still a bounded export into a new drawing. Dynamic blocks, external references, source layouts, custom objects and complete document metadata are outside its scope. Strict mode checks the selected entities and the decoded attributes described above; it does not guarantee preservation of every possible DWG property or data that the reader cannot discover. The editable model-space workflow below has the same bounded writer scope.
For example, types="INSERT" with an unsupported SPLINE inside the referenced
block fails in strict mode. Non-strict export reports the skipped SPLINE while
retaining the block and INSERT; definition geometry stays in its block.
An optional external compatibility check creates empty, mixed-entity and 1,500-line and Japanese styled drawings, reads them with ODA without audit/repair, saves them back to AC1015, and compares geometry, attributes and DXF audit results. A modern external fixture also verifies TrueColor, transparency and XDATA loss diagnostics. Block tests cover base points, nonuniform/reflected scale, normals, nested attributes, independent AC1015/AC1027 input, and strict detection of unsupported block contents:
EZDWG_ODA_FILE_CONVERTER=/usr/bin/ODAFileConverter \
uv run pytest -q tests/test_write_dwg_oda.py tests/test_write_blocks.py tests/test_write_ellipse.py tests/test_write_polyline.py tests/test_write_faces.py tests/test_write_spline.py
This requires a separately installed ODA File Converter, ezdxf, and on
headless Linux, xvfb-run. Normal CI runs the native regression tests and skips
the external check unless the environment variable is set. ODA acceptance is
separate from AutoCAD UI verification.
The existing raw tuple arguments remain compatible. Advanced callers can pass
keyword-only entity_attributes (keyed by nonzero input handle), layers,
linetypes, text_styles and metadata to ezdwg.raw.write_ac1015_dwg.
metadata={"codepage": 38} selects Windows-932; raw output defaults to codepage
30 (Windows-1252). Attributes name table entries rather than reusing source table
handles. Unknown keys, missing references and invalid values raise ValueError.
Creating and Editing a Drawing¶
ezdwg.new() returns an in-memory Drawing for AC1015. Its model space holds
live Entity objects with unique integer handles:
import ezdwg
drawing = ezdwg.new()
drawing.header["insunits"] = 4 # millimeters
drawing.linetypes["DASHED"] = {"description": "Dashed", "dashes": [1.0, -0.5]}
drawing.layers["作図"] = {"color_index": 2, "linetype": "DASHED", "lineweight": 35}
drawing.text_styles["Notes"] = {"font": "txt.shx", "width": 1.0}
msp = drawing.modelspace()
line = msp.add_line((0, 0), (10, 5), layer="作図")
circle = msp.add_circle((5, 5), radius=2)
msp.add_text("日本語", insert=(1, 2), height=2, style="Notes")
msp.update(line, end=(20, 5))
line.dxf["color_index"] = 1
msp.delete(circle)
result = drawing.save("created.dwg")
Convenience methods cover add_line, add_point, add_ray, add_xline,
add_arc, add_circle, add_ellipse, add_lwpolyline, add_text, and add_mtext.
Legacy polyline helpers are add_polyline2d and add_polyline3d.
Face helpers are add_solid, add_trace and add_3dface.
Spline helpers are add_spline and add_spline_fit. add_hatch, the dimension
helpers (add_linear_dim, add_aligned_dim, add_radius_dim,
add_diameter_dim, add_angular_dim, add_ordinate_dim, add_dimension) and
add_image are described in their own sections below.
add_entity("LINE", {"start": (0, 0), "end": (1, 1)}, handle=100) accepts an
explicit handle; duplicates, zero, negatives and values outside unsigned 64-bit
range are rejected. Automatic handles are not reused after deletion.
Positions accept two or three coordinates; normals and direction vectors require
three. LWPOLYLINE vertices are XY pairs, with elevation stored separately; use
closed=True, bulges, widths and const_width for its other geometry.
ARC start/end angles and TEXT rotation/oblique are degrees. MTEXT uses text
for its raw formatting string, char_height for height and text_direction for
orientation. It has no separate plain-text field in an editable drawing.
query("LINE CIRCLE") (also comma-separated names or type globs), get(handle)
and iteration return the same live entities. update(entity_or_handle, **attrs)
validates geometry before changing the entity. Direct entity.dxf changes are
validated when saving. Unknown attribute names raise during add/update; a direct
dictionary mutation is diagnosed on save. Input dictionaries and point arrays
are copied when adding entities.
The three table dictionaries use names as keys and raw writer property dictionaries
as values (omit the name property). Unused definitions in a new Drawing are
saved too. Undefined references and invalid table definitions reject saving even
with strict=False. header supports insunits, lunits, luprec, aunits,
auprec and ltscale; text encoding is selected automatically.
For an existing file:
source = ezdwg.read("created.dwg")
edited = source.edit() # defaults to strict=True
line = next(edited.modelspace().query("LINE"))
edited.modelspace().update(line, end=(30, 10))
edited.save("edited.dwg")
Document.edit() copies model-space entities, all discovered block definitions
(including unused/empty ones), attached attributes, referenced writer tables and
supported header settings. It is independent of the source file after the
copy; it does not modify the read-only Document or its cached layer state.
It does not clone unused layer/linetype/style tables, custom objects or full
document metadata. Known losses, unsupported entities and discovered paper-space
content reject the copy by default. To deliberately work with a lossy subset,
use edit(strict=False) and inspect drawing.diagnostics. Import losses remain
attached to the snapshot and cause subsequent strict saves to fail. Deleting an
entity removes its entity-specific import diagnostics; document/table diagnostics
remain. Repairing an attribute does not erase an earlier import-loss diagnostic.
Drawing.save(path, strict=True) returns WriteResult. save() without a path
reuses the last successful destination, or the imported source path. A new drawing
requires a path for its first save. Drawing.export_dwg is an alias for saving;
ezdwg.to_dwg(drawing_or_editable_layout, path, types=..., strict=True) also works.
The existing to_dwg function retains its strict=False default. For a new
drawing, WriteResult.source_path is an empty string.
All native DWG writes serialize and validate before writing the destination. They write and sync a temporary file in the destination directory, close it, then rename it over the destination. Validation, write and replacement failures leave the previous destination intact, and temporary files are cleaned up. Existing file permission bits are retained; symlink and directory destinations are rejected. A successful high-level save clears decode caches so subsequent queries read the replacement. The save path changes only after success. This provides atomic file replacement on supported local filesystems; it does not promise directory-fsync durability across power loss or identical behavior on every network filesystem.
Plotting/DXF conversion continue to use read-only documents and layouts; to use those APIs with edited geometry, save and read the resulting DWG first. Complete layouts and newer DWG write versions remain outside this editing API's scope.
Ellipses¶
add_ellipse is available in model space and block definitions. It preserves
ELLIPSE entities, including elliptical arcs, without tessellation.
import math
import ezdwg
drawing = ezdwg.new()
msp = drawing.modelspace()
msp.add_ellipse((10, 20), major_axis=(5, 0, 0), axis_ratio=0.5)
arc = msp.add_ellipse(
(1, 2, 3), major_axis=(2, 3, -3), axis_ratio=0.25,
extrusion=(0, 1, 1), start_angle=math.pi / 4, end_angle=math.pi,
)
msp.update(arc, axis_ratio=0.6)
drawing.save("ellipses.dwg")
The center and semi-major-axis vector use WCS coordinates. The vector is relative
to the center. axis_ratio is the minor/major radius ratio. The existing reader
field names start_angle and end_angle denote eccentric anomaly parameters in
radians; they are not polar angles. A full ellipse defaults to 0 through
math.tau. Wrapped arcs such as 5.5 through 0.5 and negative parameters are
retained. See the Autodesk ELLIPSE reference.
Geometry must be finite. The writer requires a ratio in [1e-6, 1], axis and
normal lengths greater than 1e-6, and perpendicular axis/normal directions
(normalized dot-product tolerance 1e-6). It accepts at most one revolution;
partial arcs must have a counterclockwise parameter span greater than 1e-6.
Full revolutions use a 1e-12 parameter tolerance. Invalid geometry is rejected
by add/update, diagnosed by strict save, or skipped by non-strict export.
Common layer/color/linetype/lineweight/visibility attributes are supported;
ELLIPSE does not store thickness.
The raw writer accepts the keyword-only ellipses argument, with rows matching
raw.decode_ellipse_entities: (handle, center, major_axis, extrusion,
axis_ratio, start_parameter, end_parameter). All three vectors have three
coordinates, and both parameters are radians. Existing positional writer
arguments are unchanged.
Tests cover existing R14/R2004/R2007/R2010/R2013 inputs and ODA read/resave of full, partial, wrapped, tilted, thin and block-contained ellipses. Independent AC1015/AC1027 input is also imported, edited and exported to AC1015.
Legacy Polylines¶
add_polyline2d and add_polyline3d create ordinary POLYLINE records with owned
VERTEX records and a SEQEND. Both work in model space and block definitions.
import ezdwg
drawing = ezdwg.new()
msp = drawing.modelspace()
poly = msp.add_polyline2d(
[(0, 0), (4, 0), (4, 3)], closed=True, flags=128,
bulges=[0, 0.5, -0.25], widths=[(0.1, 0.2), (0.3, 0.4), (0.5, 0.6)],
width_start=0.7, width_end=0.8, elevation=4, extrusion=(0, 1, 0), thickness=2,
)
msp.add_polyline3d([(0, 0, 1), (2, 3, 4), (5, 6, -2)], closed=False)
msp.update(poly, closed=False, elevation=7)
drawing.save("polylines.dwg")
2D points are OCS XY pairs; use elevation for their shared Z coordinate.
3D points are WCS XYZ triples. These coordinate conventions follow the
Autodesk VERTEX reference.
Both require at least two finite vertices. closed overrides the closure bit in
flags. 2D supports flags 1 (closed) and 128 (continuous linetype); 3D supports
closure and its implicit 3D type bit 8. Curve/spline fitting and mesh flags are
rejected. 3D polylines do not have bulges, widths, extrusion or thickness.
For 2D polylines, bulges and widths follow vertex order and may be shorter
than points; omitted entries are zero. Widths must be nonnegative, and extra
array entries are rejected. width_start / width_end retain the parent's
default widths. Use closed=True for closure; there is no need to repeat the
first point. Editable imports retain the actual stored vertex sequence, including
an explicit repeated endpoint, rather than the reader's display interpolation.
Document.edit() and to_dwg() read the owned-vertex arrays or legacy
first/last/next links, retaining order even for sparse, non-monotonic handles.
Missing, inconsistent or unsupported geometry rejects strict export; non-strict
export skips the whole polyline. A polyline counts as one entity in WriteResult;
its structural VERTEX/SEQEND records are not counted separately. Parent entity
handles are retained; VERTEX and SEQEND handles are regenerated on saving.
Child records use the parent layer and default BYLAYER display properties. Individual child display overrides, XDATA/extended metadata and R2010+ vertex IDs are diagnosed and rejected in strict mode. Fitted polylines, meshes and polyface meshes remain outside this writer scope. Saving does not evaluate fit curves or replace them with straight segments.
The raw writer adds keyword-only polylines2d and polylines3d lists of
dictionaries containing handle, points and flags; 2D rows also accept
width_start, width_end, widths, and bulges. Pass parent formatting through
entity_attributes and block membership through entity_owners. Existing
positional arguments remain compatible.
Native tests cover model/block ownership, vertex order, closure and validation. Opt-in ODA tests check direct read/resave, tilted 2D geometry, 3D vertices, independent AC1015/AC1018/AC1027 input, and strict child-property loss handling.
SOLID, TRACE and 3DFACE¶
add_solid, add_trace and add_3dface accept three or four finite XYZ
corners, in both model space and blocks. A triangle is stored by repeating its
third corner as the fourth. Input order is retained, including during editing.
import ezdwg
drawing = ezdwg.new()
msp = drawing.modelspace()
# SOLID/TRACE stored order: the quad perimeter visits indices 0, 1, 3, 2.
corners = [(0, 0, 5), (4, 0, 5), (0, 3, 5), (4, 3, 5)]
solid = msp.add_solid(corners, thickness=2, extrusion=(0, 0.6, 0.8))
msp.add_trace(corners[:3], extrusion=(0, 0, -1))
# 3DFACE uses perimeter order and independently varying WCS Z coordinates.
face = msp.add_3dface(
[(0, 0, 0), (4, 0, 1), (4, 3, 2), (0, 3, -1)],
invisible_edge_flags=5,
)
msp.update(solid, thickness=-1)
msp.update(face, invisible_edge_flags=0)
drawing.save("faces.dwg")
SOLID/TRACE coordinates are OCS, with equal Z on every corner. Their shared
elevation comes from the corners; a separate elevation attribute is not accepted.
Negative thickness and non-default extrusion are supported; extrusion must be
finite and nonzero. See Autodesk's OCS conventions.
3DFACE coordinates are WCS; thickness and extrusion are not supported. Its
invisible_edge_flags is an integer from 0 to 15, combining bits 1, 2, 4 and 8
for successive edges. See the Autodesk 3DFACE reference.
Unknown flags, non-finite coordinates, wrong corner counts and unequal planar
Z values are rejected. Direct dictionary mutations receive the same checks on
save; non-strict exports skip invalid geometry and report diagnostics.
The raw writer adds keyword-only solids, traces and faces3d lists using the
existing decoded row shapes, always with four corners:
- SOLID/TRACE:
(handle, p1, p2, p3, p4, thickness, extrusion). - 3DFACE:
(handle, p1, p2, p3, p4, invisible_edge_flags).
Common attributes and block owners use entity_attributes and entity_owners.
An explicitly supplied formatting attribute overrides its raw-row value; omitted
attributes retain the raw-row geometry. Existing positional arguments are unchanged.
Tests cover native edit/save and strict destination preservation, ODA direct read/resave without repair, all 16 edge masks, triangles, tilted planar faces, block dependencies and independent AC1015/AC1018/AC1027 import/edit/export.
Splines¶
The writer retains either the native control-point representation (scenario 1) or the fit-point representation (scenario 2). It does not tessellate, refit or convert between them. Both work in model space and block definitions.
import ezdwg
drawing = ezdwg.new()
msp = drawing.modelspace()
control = msp.add_spline(
[(0, 0, 1), (2, 5, 2), (5, -1, 3), (8, 2, 0)],
degree=2, knots=[0, 0, 0, 0.4, 1, 1, 1], weights=[1, 0.5, 2, 1],
)
fit = msp.add_spline_fit(
[(0, 0, 1), (2, 4, 2), (5, -1, 3), (8, 2, 0)],
start_tangent=(1, 0, 0), end_tangent=(0, 1, 0), fit_tolerance=1e-8,
)
msp.update(control, weights=[1, 0.7, 2, 1])
drawing.save("splines.dwg")
Coordinates and tangents are WCS XYZ. Property names follow the existing reader:
scenario, degree, rational, closed, periodic, knots, control_points,
weights, fit_points, fit_tolerance, knot_tolerance, ctrl_tolerance,
start_tangent, end_tangent. The reader's display points field is derived
and is not an editable spline input. See the Autodesk SPLINE reference.
Control splines support degrees 1 through 10, with more control points than the
degree. Knot count must equal len(control_points) + degree + 1. Knots must be
finite and nondecreasing, with a nonempty parameter domain and multiplicity no
greater than degree + 1. Rational splines require one finite positive weight
per control point. add_spline infers rational from supplied weights and
generates clamped uniform knots when knots is omitted. Direct raw/entity rows
require explicit knots and a consistent rational flag.
Closed control splines accept clamped knots with matching endpoint control points,
or periodic wrapped data. Wrapped data repeats the first degree control points
and weights at the end, with repeating knot intervals. periodic=True requires
closed=True. Pass explicit knots for a wrapped periodic curve. Native saves
retain the supplied data. ODA can clamp an equivalent periodic curve and adjust
closure flags when exporting DXF, so external tests compare evaluated curve
positions as well as attributes.
Fit splines currently support open cubic curves with at least two points,
distinct adjacent points, chord parameterization and optional endpoint tangents.
An omitted tangent is stored as a zero vector. Tolerances are finite and
nonnegative; defaults are 1e-7 for knot/control and 1e-10 for fit tolerance.
An entity cannot combine control data with fit data. Closed fit splines, other
fit degrees, non-chord R2013+ fit parameterization, and spline thickness/extrusion
are outside the writer scope. Invalid/unrepresentable geometry is skipped with
diagnostics in non-strict exports, and rejected before output replacement in strict
mode. Unpreserved control-frame display metadata or XDATA also rejects strict saves.
raw.write_ac1015_dwg(..., splines=[...]) accepts keyword-only dictionaries
with handle and the same geometry fields. Common formatting and ownership use
entity_attributes and entity_owners. raw.decode_write_splines(path) exposes
endpoint tangents and R2013+ interpretation fields for import; the existing
decode_spline_entities tuple contract is unchanged. Document.edit() imports
the extra data automatically and keeps it after the source file is removed.
Tests use existing AC1014 through AC1032 samples (including strict loss rejection), independent AC1015/AC1018/AC1027 inputs and ODA direct read/resave. These are local and ODA checks; they do not establish native AutoCAD UI behavior.
True colours¶
AC1015 stores colours as ACI only. An entity or layer true_color
(0xRRGGBB) is accepted by the editable API and by Document.edit(); on save
it is rounded to the nearest of the 255 palette entries (pure black and white
map to 7, the foreground colour) and reported as a WriteDiagnostic with action
approximated. Approximations do not fail strict saves or strict imports; the
drawing keeps the requested true_color so a later writer can use it.
drawing.layers["塗"] = {"true_color": 0x73B9FF}
msp.add_line((0, 0), (10, 0), true_color=0xFF0000) # written as ACI 1
ezdwg._aci.nearest_aci(rgb) and aci_palette() expose the palette.
Hatches¶
msp.add_hatch([[(0, 0), (10, 0), (10, 5), (0, 5)]], color_index=1) # solid fill
msp.add_hatch(
[{"edges": [
{"type": "line", "start": (0, 0), "end": (4, 0)},
{"type": "arc", "center": (4, 2), "radius": 2, "start_angle": -90, "end_angle": 90},
{"type": "line", "start": (4, 4), "end": (0, 4)},
{"type": "line", "start": (0, 4), "end": (0, 0)},
]}],
pattern="ANSI31", pattern_angle=30, pattern_scale=25.4,
)
A path is a point list (a closed polyline), a dictionary with points
(optional bulges, closed, flags), or a dictionary with edges (line, arc,
ellipse with major_axis/ratio, or spline with control points and knots;
angles in degrees). Without pattern the hatch is a solid fill. pattern is a
name of the built-in definitions (ANSI31, ANSI32, ANSI33, ANSI37, DOTS,
LINE, NET, in inches) or acad.pat rows (angle, base_x, base_y, dx, dy,
dashes); it is rotated by pattern_angle, scaled by pattern_scale and stored
as pattern_lines (offsets in the OCS orientation, as AutoCAD stores them).
hatch_style (0 odd parity, 1 outermost, 2 whole area), elevation,
extrusion and pixel_size/seed_points are accepted. The writer stores
non-associative hatches. The reader returns tessellated path points; an edited
import keeps those as polyline paths.
Dimensions¶
AutoCAD does not regenerate dimension graphics when it opens a drawing, so each
DIMENSION references an anonymous block (*D1, ...) with its lines, arrowheads
(filled SOLIDs) and measurement TEXT. The helpers render that block with the
Standard DIMSTYLE the writer stores (ISO-25 like: arrow 2.5, text 2.5, gap
0.625, extension offset 0.625 / extension 1.25, two decimals, . separator):
msp.add_linear_dim((0, 10), (0, 0), (50, 0)) # base on the dimension line
msp.add_aligned_dim((0, 20), (30, 40), distance=5)
msp.add_radius_dim((80, 10), radius=12, angle=30)
msp.add_diameter_dim((80, 40), radius=10)
msp.add_angular_dim((120, 0), (140, 0), (120, 20), radius=15)
msp.add_ordinate_dim((150, 10), (150, 25)) # X datum, origin (0, 0)
msp.add_dimension( # two-line angular
"angular2ln",
defpoint2=(0, 100), defpoint3=(40, 100), # first line
defpoint4=(0, 100), defpoint=(0, 140), # second line
defpoint5=(20, 120), # point on the arc
)
text overrides the measurement ("<>" inserts it, " " suppresses it).
update() of a rendered dimension renders its block again; pass
geometry="*D9" to use a block you built yourself (nothing is rendered then).
Definition points follow the DXF names defpoint (10), defpoint2 (13),
defpoint3 (14), defpoint4 (15), defpoint5 (16); angle, oblique_angle,
text_rotation and horizontal_direction are degrees. Only the Standard
DIMSTYLE can be referenced. Document.edit() imports the dimensions of a file
with their existing blocks; to_dwg() carries them through a re-export.
Raster images¶
drawing.image_defs["logo"] = {"file_path": "images/logo.png", "size": (640, 480)}
msp.add_image("logo", (10, 20), width=64) # keeps the pixel aspect ratio
msp.add_image("logo", (100, 20), width=32, height=32, rotation=90, clipping=True)
image_defs maps a name to file_path, size (pixels), optional
pixel_size (drawing units per pixel, default 1), resolution_units (0 none,
2 centimeters, 5 inches) and is_loaded. add_image derives the pixel step
vectors (u_vector, v_vector) from width/height/rotation, or takes them
explicitly. display_props, brightness, contrast, fade, clipping and a
rectangular (clip_type=1, two corners) or polygonal (clip_type=2) clip
boundary in pixel coordinates are stored. The writer adds the IMAGE,
IMAGEDEF, IMAGEDEF_REACTOR and RASTERVARIABLES classes, the
ACAD_IMAGE_DICT/ACAD_IMAGE_VARS dictionary entries and one reactor per
image. The image file itself is not embedded; the path is stored as given. The
reader exposes IMAGE entities with file_path and image_def (the file stem)
resolved from their IMAGEDEF, and Document.edit() recreates image_defs.
Blocks and Attributes¶
drawing.blocks is a case-insensitive BlockTable. new() creates a
BlockLayout supporting the same query/add/update/delete methods as model space.
Its base_point and description are editable. Entity handles are unique across
model space, all blocks and all attached attributes.
import ezdwg
drawing = ezdwg.new()
part = drawing.blocks.new("Part", base_point=(1, 2, 0), description="Component")
part.add_line((1, 2), (5, 2), color_index=0) # BYBLOCK
part.add_attdef("ID", "default", insert=(1, 3), height=1, prompt="Identifier")
part.add_attdef("TYPE", "fixed", insert=(1, 4), height=1, attribute_flags=2)
assembly = drawing.blocks.new("Assembly")
nested = assembly.add_blockref("Part", insert=(3, 4), scale=(-2, 3, 1), rotation=35)
value = assembly.add_attrib(nested, "ID", "P-001", insert=(3, 5), height=1.5)
assembly.update(value, text="P-002")
drawing.modelspace().add_blockref("Assembly", insert=(100, 200), rotation=90)
drawing.save("assembly.dwg")
add_blockref(name, insert=(0, 0, 0), *, scale=(1, 1, 1), rotation=0, **attrs)stores an INSERT, without exploding the definition. Scale components must be finite and nonzero; negative/nonuniform scales and arbitrary nonzero extrusion normals are retained. After creation, editxscale,yscale,zscaleorrotationin the entity'sdxfdictionary or throughupdate().add_attdef(tag, text="", insert=(0, 0, 0), height=1, *, prompt="", **attrs)stores a single-line attribute definition with TEXT placement/style properties.add_attrib(blockref, tag, text, insert=(0, 0, 0), height=1, **attrs)attaches a single-line attribute to that INSERT.attribs(blockref)returns the live attribute entities as a tuple. The parent layout'sget,updateanddeletecan address attached attributes by entity or handle.attribute_flagsretains the standard bits: 1 invisible, 2 constant, 4 verify, 8 preset. Tags must be nonempty without whitespace or!; duplicate tags on one INSERT are rejected. Geometry, color, layer, text style, alignment and rotation follow the TEXT conventions.
Attribute positions are explicit in the containing layout's coordinate system,
using the same OCS/extrusion convention as TEXT. They are not block-local ATTDEF
positions to be automatically transformed by add_attrib. This API does not
instantiate defaults, transform attribute placements, or synchronize existing
ATTRIB values when an ATTDEF changes. Constant definitions remain ATTDEF records.
delete(insert) also removes its attached attributes. drawing.blocks.delete(name)
refuses a referenced block; delete its references first. Empty definitions are
valid. Drawing.save() preserves all in-memory definitions. Exporting a layout
or using a root type filter includes the referenced block closure; exporting a
BlockLayout itself writes its selected local geometry into output model space.
Undefined block names, duplicate handles, cyclic block references, invalid
ownership and duplicate attribute tags reject saving even with strict=False.
Invalid INSERT geometry can be skipped in non-strict mode, along with its owned
ATTRIBs, with diagnostics for both. Unsupported entities inside referenced blocks
are included in strict validation and skipped counts, even with types="INSERT".
The writer emits BLOCK_CONTROL/BLOCK_HEADER, BLOCK/ENDBLK, block member links, INSERT references and reverse references, ATTRIB ownership and SEQEND records. Reading preserves INSERT ACI and extrusion; nested attributes inherit their INSERT's layout membership instead of appearing as loose model-space geometry. Editable imports preserve attached-attribute order, including sparse handles, and diagnose missing or inconsistent owned-attribute references.
Static blocks are supported, including anonymous names such as *U1. MINSERT
arrays, dynamic block behavior, xrefs, multiline attributes, lock-position data,
per-block units/scale restrictions, block previews and extended block metadata
are outside this scope. Detected import losses remain diagnostics; R2018
attribute imports are conservatively diagnosed because embedded multiline
properties have not been qualified for AC1015 export. ODA tests establish
read/resave compatibility, not AutoCAD UI or Windows execution coverage.
For raw writers, existing positional arguments are unchanged. Additional keyword
arguments are blocks (name/base_point/description dictionaries), inserts,
attdefs, attribs, and entity_owners. Owner keys are entity handles; values are
block names for block contents or INSERT handles for attached ATTRIBs. New raw
entity rows use dictionaries; INSERT scale is a three-tuple and raw rotation
is radians, as in the older raw TEXT rows. Shared formatting still goes in
entity_attributes. A zero/omitted handle cannot be referenced as an owner.