Drawing / Export Add-on

This add-on provides the functionality to render a DXF document to produce a rasterized or vector-graphic image which can be saved to a file or viewed interactively depending on the backend being used.

The module provides two example scripts in the folder examples/addons/drawing which can be run to save rendered images to files or view an interactive visualisation.

$ ./draw_cad.py --supported_formats
# will list the file formats supported by the matplotlib backend.
# Many formats are supported including vector graphics formats
# such as pdf and svg

$ ./draw_cad.py <my_file.dxf> --out image.png

# draw a layout other than the model space
$ ./draw_cad.py <my_file.dxf> --layout Layout1 --out image.png

# opens a GUI application to view CAD files
$ ./cad_viewer.py

See also

How-to section for the FAQ about the Drawing Add-on.

Design

The implementation of the drawing add-on is divided into a frontend and multiple backends. The frontend handles the translation of DXF features and properties into simplified structures, which are then processed by the backends.

Common Limitations to all Backends

  • rich text formatting of the MTEXT entity is close to AutoCAD but not pixel perfect

  • relative size of POINT entities cannot be replicated exactly

  • rendering of ACIS entities is not supported

  • no 3D rendering engine, therefore:

    • 3D entities are projected into the xy-plane and 3D text is not supported

    • only top view rendering of the modelspace

    • VIEWPORTS are always rendered as top view

    • no visual style support

  • only basic support for:

    • infinite lines (rendered as lines with a finite length)

    • OLE2FRAME entities (rendered as rectangles)

    • vertical text (will render as horizontal text)

    • rendering of additional MTEXT columns may be incorrect

MatplotlibBackend

The MatplotlibBackend is used by the Draw command of the ezdxf launcher.

Example for the usage of the Matplotlib backend:

import sys
import matplotlib.pyplot as plt
from ezdxf import recover
from ezdxf.addons.drawing import RenderContext, Frontend
from ezdxf.addons.drawing.matplotlib import MatplotlibBackend

# Safe loading procedure (requires ezdxf v0.14):
try:
    doc, auditor = recover.readfile('your.dxf')
except IOError:
    print(f'Not a DXF file or a generic I/O error.')
    sys.exit(1)
except ezdxf.DXFStructureError:
    print(f'Invalid or corrupted DXF file.')
    sys.exit(2)

# The auditor.errors attribute stores severe errors,
# which may raise exceptions when rendering.
if not auditor.has_errors:
    fig = plt.figure()
    ax = fig.add_axes([0, 0, 1, 1])
    ctx = RenderContext(doc)
    out = MatplotlibBackend(ax)
    Frontend(ctx, out).draw_layout(doc.modelspace(), finalize=True)
    fig.savefig('your.png', dpi=300)

Simplified render workflow but with less control:

from ezdxf import recover
from ezdxf.addons.drawing import matplotlib

# Exception handling left out for compactness:
doc, auditor = recover.readfile('your.dxf')
if not auditor.has_errors:
    matplotlib.qsave(doc.modelspace(), 'your.png')

PyQtBackend

The PyQtBackend is used by the View command of the ezdxf launcher.

See also

The qtviewer.py module implements the core of a simple DXF viewer and the cad_viewer.py example is a skeleton to show how to launch the CADViewer class.

Recorder

New in version 1.1.

This is a special backend which records the output of the Frontend class in compact numpy arrays and these recordings and can be played by a Player instance on one or more backends. The recorded numpy arrays support measurement of bounding boxes and transformations which is for some backends a requirement to place the DXF content on size limited pages.

Layout

New in version 1.1.

The Layout class builds the page layout and the matrix to transform the DXF content to page coordinates according to the layout Settings. The DXF coordinate transformation is required for PDF and HPGL/2 which expects the output coordinates in the first quadrant and SVG which has an inverted y-axis.

The Layout class uses following classes and enums for configuration:

  • Page - page definition

  • Margins - page margins definition

  • Settings - configuration settings

  • Units - enum for page units

SVGBackend

New in version 1.1.

Usage:

from ezdxf.addons.drawing import Frontend, RenderContext
from ezdxf.addons.drawing import layout, svg

doc = ezdxf.readfile("your.dxf")
msp = doc.modelspace()
backend = svg.SVGBackend()
Frontend(RenderContext(doc), backend).draw_layout(msp)

with open("your.svg", "wt") as fp:
    fp.write(backend.get_string(layout.Page(0, 0))

PyMuPdfBackend

New in version 1.1.

Usage:

import ezdxf
from ezdxf.addons.drawing import Frontend, RenderContext
from ezdxf.addons.drawing import layout, pymupdf

doc = ezdxf.readfile("your.dxf")
msp = doc.modelspace()
backend = pymupdf.PyMuPdfBackend()
Frontend(RenderContext(doc), backend).draw_layout(msp)

with open("your.pdf", "wb") as fp:
    fp.write(backend.get_pdf_bytes(layout.Page(0, 0))

Load the output of the PyMuPdfBackend into the Image class of the Pillow package for further processing or to output additional image formats:

import io
from PIL import Image

...  # see above

# the ppm format is faster to process than png
fp = io.BytesIO(backend.get_pixmap_bytes(layout.Page(0, 0), fmt="ppm", dpi=300))
image = Image.open(fp, formats=["ppm"])

PlotterBackend

New in version 1.1.

Usage:

import ezdxf
from ezdxf.addons.drawing import Frontend, RenderContext
from ezdxf.addons.drawing import layout, hpgl2

doc = ezdxf.readfile("your.dxf")
psp = doc.paperspace("Layout1")
backend = hpgl2.PlotterBackend()
Frontend(RenderContext(doc), backend).draw_layout(psp)
page = layout.Page.from_dxf_layout(psp)

with open("your.plt", "wb") as fp:
    fp.write(backend.normal_quality(page)

You can check the output by the HPGL/2 viewer:

ezdxf hpgl your.plt

DXFBackend

New in version 1.1.

Render a paperspace layout into modelspace:

import ezdxf
from ezdxf.addons.drawing import Frontend, RenderContext
from ezdxf.addons.drawing import layout, dxf

doc = ezdxf.readfile("your.dxf")
layout1 = doc.paperspace("Layout1")
output_doc = ezdxf.new()
output_msp = output_doc.modelspace()

backend = dxf.DXFBackend(output_msp)
Frontend(RenderContext(doc), backend).draw_layout(layout1)

output_doc.saveas("layout1_in_modelspace.dxf")

Configuration

Additional options for the drawing add-on can be passed by the config argument of the Frontend constructor __init__(). Not every option will be supported by all backends.

Usage:

my_config = Configuration(lineweight_scaling=2)

BackgroundPolicy

ColorPolicy

HatchPolicy

ImagePolicy

LinePolicy

LineweightPolicy

ProxyGraphicPolicy

TextPolicy

Properties

LayerProperties

class ezdxf.addons.drawing.properties.LayerProperties

Actual layer properties, inherits from class Properties.

is_visible

Modified meaning: whether entities belonging to this layer should be drawn

layer

Modified meaning: stores real layer name (mixed case)

LayoutProperties

class ezdxf.addons.drawing.properties.LayoutProperties

Actual layout properties.

name

Layout name as string

units

Layout units as InsertUnits enum.

RenderContext

The RenderContext class can be used isolated from the drawing add-on to resolve DXF properties.

Frontend

BackendInterface

class ezdxf.addons.drawing.backend.BackendInterface

Public interface definition for 2D rendering backends.

For more information read the source code: backend.py

Backend

class ezdxf.addons.drawing.backend.Backend

Abstract base class for concrete backend implementations and implements some default features.

For more information read the source code: backend.py

Details

The rendering is performed in two stages. The frontend traverses the DXF document structure, converting each encountered entity into primitive drawing commands. These commands are fed to a backend which implements the interface: Backend.

Although the resulting images will not be pixel-perfect with AutoCAD (which was taken as the ground truth when developing this add-on) great care has been taken to achieve similar behavior in some areas:

  • The algorithm for determining color should match AutoCAD. However, the color palette is not stored in the DXF file, so the chosen colors may be different to what is expected. The RenderContext class supports passing a plot style table (CTB-file) as custom color palette but uses the same palette as AutoCAD by default.

  • Text rendering is quite accurate, text positioning, alignment and word wrapping are very faithful. Differences may occur if a different font from what was used by the CAD application but even in that case, for supported backends, measurements are taken of the font being used to match text as closely as possible.

  • Visibility determination (based on which layers are visible) should match AutoCAD

See also