Tutorial for External References¶
Introduction¶
This tutorial uses the ezdxf.xref
module to work with external references (XREF).
Attached XREFs are links to the modelspace of a specified drawing file. Changes made to the referenced drawing are automatically reflected in the current drawing when it’s opened or if the XREF is reloaded.
Important
AutoCAD can only display DWG files as attached XREFs. Any DXF file attached as an XREF to a DXF document must be converted to DWG in order to be viewed in AutoCAD. Fortunately, other CAD applications are more cooperative, BricsCAD has no problem displaying DXF files as XREFs.
The drawing
add-on included in ezdxf does not display external
references at all!
There are some example files included in the examples/xref folder of the repository:
attach_dxf_dwg_xref.py
detach_block_as_xref.py
embed_dxf_dwg_xref.py
load_table_resources.py
Supported Entities¶
All operations which move entities between layouts and XREFs copy these entities, therefore only entities which are copyable can be transferred. The following entities are not copyable:
All entities which are not documented by the DXF reference.
ACAD_TABLE
ACAD_PROXY_ENTITY
OLE2FRAME
ACIS based entities: BODY, 3DSOLID, REGION, …
Custom entities from applications on top of AutoCAD like Map 3D, Civil 3D or Architecture. The vertical integration stack is not documented by the DXF reference.
Unsupported entities are ignored and do not raise exceptions.
Environment Setup¶
Required imports to follow this tutorial:
import ezdxf
from ezdxf.addons import odafc
from ezdxf.document import Drawing
from ezdxf import xref, units, colors
from ezdxf.render import forms
DXFVERSION = "R2013"
Function to create a simple DXF file as XREF, the insertion point of the XREF is set to (5, 5):
def make_dxf_xref_document(name: str) -> Drawing:
ref_doc = ezdxf.new(DXFVERSION, units=units.M)
ref_doc.layers.add("GEAR", color=colors.YELLOW)
msp = ref_doc.modelspace()
gear = forms.gear(
16, top_width=0.25, bottom_width=0.75, height=0.5, outside_radius=2.5
)
msp.add_lwpolyline(
forms.translate(gear, (5, 5)), close=True, dxfattribs={"layer": "GEAR"}
)
ref_doc.header["$INSBASE"] = (5, 5, 0)
ref_doc.saveas(name)
return ref_doc
Create the DXF file:
make_dxf_xref_document("xref.dxf")
The XREF looks like this:
Attach a DXF File¶
Create a host document to which the XREF will be attached:
host_doc = ezdxf.new(DXFVERSION, units=units.M)
Attach the XREF by the ezdxf.xref.attach()
function and save the host DXF file:
xref.attach(host_doc, block_name="dxf_xref", insert=(0, 0), filename="attached_xref.dxf")
host_doc.set_modelspace_vport(height=10, center=(0, 0))
host_doc.saveas("attach_host_dxf.dxf")
The attach()
function is meant to simply attach an XREF once without any
overhead, therefore the attach()
function creates the required block definition
automatically and raises an XrefDefinitionError
exception if the block definition
already exist. To attach additional XREF references use the method
add_blockref()
:
msp.add_blockref("dxf_xref", insert=another_location)
The attached DXF file in BricsCAD:
Important
AutoCAD can not display DXF files as attached XREFs.
Attach a DWG File¶
Export the DXF file as DWG by the odafc
add-on:
# It's not required to save the DXF file!
doc = make_dxf_xref_document("attached_xref.dxf")
try:
odafc.export_dwg(doc, "attached_xref.dwg", replace=True)
except odafc.ODAFCError as e:
print(str(e))
Attach the DWG file by the ezdxf.xref.attach()
function and save the host DXF file:
host_doc = ezdxf.new(DXFVERSION, units=units.M)
xref.attach(host_doc, block_name="dwg_xref", filename="attached_xref.dwg", insert=(0, 0))
host_doc.set_modelspace_vport(height=10, center=(0, 0))
host_doc.saveas("attached_dwg.dxf")
Attached DWG file in Autodesk DWG TrueView 2023:
Detach an XREF¶
The detach()
function writes the content of a block definition into
the modelspace of a new DXF document and convert the block to an external reference (XREF).
The new DXF document has to be written/exported by the caller.
The function does not create any block references. These references should already exist
and do not need to be changed since references to blocks and XREFs are the same.
host_doc = ezdxf.new()
make_block(host_doc, "GEAR")
block_layout = host_doc.blocks.get("GEAR")
detached_block_doc = xref.detach(block_layout, xref_filename="detached_gear.dxf")
detached_block_doc.saveas("detached_gear.dxf")
host_doc.set_modelspace_vport(height=10, center=(0, 0))
host_doc.saveas("detach_host_dxf_xref.dxf")
Important
Save the host document after detaching the block! Detaching a block definition modifies the host document.
The detach()
function returns a Drawing
instance, so it’s possible
to convert the DXF document to DWG by the odafc
add-on if necessary
(e.g. for Autodesk products). It’s important that the argument xref_filename
match the filename of the exported DWG file:
host_doc = ezdxf.new()
make_block(host_doc, "GEAR")
block_layout = host_doc.blocks.get("GEAR")
detached_block_doc = xref.detach(block_layout, xref_filename="detached_gear.dwg")
try:
odafc.export_dwg(detached_block_doc, "detached_gear.dwg", replace=True)
except odafc.ODAFCError as e:
print(str(e))
host_doc.set_modelspace_vport(height=10, center=(0, 0))
host_doc.saveas("detach_host_dwg_xref.dxf")
It’s recommended to clean up the entity database of the host document afterwards:
host_doc.entitydb.purge()
For understanding, this is the make_block()
function:
def make_block(doc: Drawing, name: str) -> None:
blk = doc.blocks.new(name, base_point=(5, 5, 0))
doc.layers.add("GEAR", color=colors.YELLOW)
gear = forms.gear(
16, top_width=0.25, bottom_width=0.75, height=0.5, outside_radius=2.5
)
blk.add_lwpolyline(
forms.translate(gear, (5, 5)), close=True, dxfattribs={"layer": "GEAR"}
)
doc.modelspace().add_blockref(name, (0, 0))
Embed an XREF¶
The embed()
function loads the content of the XREF into the block definition,
this is the reverse operation of detaching an XREF.
For loading the content of DWG files is a loading function required, which loads the
DWG file as Drawing
document. The odafc
add-on module
provides such a function: readfile()
.
This example embeds the XREF “attached_xref.dwg” of the first example as content of the block definition “GEAR”, the “attach_host_dwg.dxf” file is the host DXF document:
import ezdxf
from ezdxf.addons import odafc
doc = ezdxf.readfile("attach_host_dwg.dxf")
gear_xref = doc.blocks.get("GEAR")
try:
xref.embed(gear_xref, load_fn=odafc.readfile)
except FileNotFoundError as e:
print(str(e))
The default loading function for DXF files is the ezdxf.readfile()
function and
doesn’t have to be specified. For the loading function from the recover
module use a lambda function:
import ezdxf
from ezdxf import recover
doc = ezdxf.readfile("attach_host_dxf.dxf")
gear_xref = doc.blocks.get("GEAR")
try:
xref.embed(gear_xref, load_fn=lambda f: recover.readfile(f)[0])
except FileNotFoundError as e:
print(str(e))
Load Modelspace¶
The ezdxf.xref.load_modelspace()
function loads the content of the modelspace
of the source document into a layout of the target document, the modelspace of the
target document is the default target layout.
Hint
Use this function to combine multiple existing DXF files.
If the goal is just to add new entities to an existing document, rather load the
source document as a template by ezdxf.readfile()
, add your content and save
the document as a new DXF file with the saveas()
method.
Merge multiple DXF files:
import ezdxf
from ezdxf import colors, transform, xref
from ezdxf.math import Matrix44
from ezdxf.render import forms
def make_gear(name: str) -> None:
doc = ezdxf.new()
doc.layers.add("GEAR", color=colors.YELLOW)
msp = doc.modelspace()
gear = forms.gear(
16, top_width=0.25, bottom_width=0.75, height=0.5, outside_radius=2.5
)
msp.add_lwpolyline(gear, close=True, dxfattribs={"layer": "GEAR"})
doc.saveas(name)
make_gear("gear.dxf")
merged_doc = ezdxf.new()
for index in range(3):
sdoc = ezdxf.readfile("gear.dxf") # this could be different DXF files
transform.inplace(sdoc.modelspace(), Matrix44.translate(index * 10, 0, 0))
xref.load_modelspace(sdoc, merged_doc)
merged_doc.saveas("merged.dxf")
Load Paperspace¶
The function ezdxf.xref.load_paperspace()
loads a paperspace layout as a new
paperspace layout into the target document. To be clear this function loads only
the content of the paperspace layout, the content of the modelspace isn’t loaded,
therefore the loaded VIEWPORT entities show the content of the target modelspace.
Write Block¶
The function ezdxf.xref.write_block()
writes the given entities into the
modelspace of a new DXF document, this document can be, but doesn’t have to be used as
an external referenced block.
Conflict Policies¶
Resources are definitions of layers, linetypes, text-, dimension-, mline- and mleader styles, materials and blocks.
A resource conflict occurs when the source and target documents contain elements such as layers, linetypes, text styles and so on that share the same name.
Many of the functions shown above support an argument to define the
ezdxf.xref.ConflictPolicy
, that gives you the choice how to handle resource
name conflicts.
ConflictPolicy.KEEP¶
Keeps the existing resource name of the target document and ignore the resource from the source document. The loaded entities from the source document use the resources defined in the target document and may alter their visual appearance, when the resources are different.
ConflictPolicy.XREF_PREFIX¶
This policy handles the resource import like CAD applications by always renaming the loaded resources to <xref>$0$<name>, where xref is the name of source document, the $0$ part is a number to create a unique resource name and <name> is the name of the resource itself.
Important
This policy ALWAYS renames the resource, even if the loaded resource doesn’t have a conflict in the target document.
ConflictPolicy.NUM_PREFIX¶
This policy renames the loaded resources to $0$<name> only if the resource <name> already exists. The $0$ prefix is a number to create a unique resource name and <name> is the name of the resource itself.
Important
This policy renames the resource ONLY when the loaded resource has a conflict in the target document.
Load Table Resources¶
Resources are definitions of layers, linetypes, text-, dimension-, mline- and mleader styles, materials and blocks.
The Loader
class is the low level tool to build a loading operation from simple
loading commands. Study the source code of the xref
module, most of loading
commands used above are build upon the Loader
class.
This example shows how to import layer, linetype, text- and dimension style definitions:
import ezdxf
from ezdxf import xref
sdoc = ezdxf.new(setup=True)
tdoc = ezdxf.new()
# The default conflict policy is ConflictPolicy.KEEP
loader = xref.Loader(sdoc, tdoc)
# Load all layers:
loader.load_layers([layer.dxf.name for layer in sdoc.layers])
# Load specific linetypes:
loader.load_linetypes(["CENTER", "DASHED", "DASHDOT"])
# Load specific text style:
loader.load_text_styles(["OpenSans", "LiberationMono"])
# Load all DIMENSION styles, this command loads also the dependent text styles:
loader.load_dim_styles([dimstyle.dxf.name for dimstyle in sdoc.dimstyles])
# execute all loading commands:
loader.execute()
tdoc.saveas("target.dxf")
Note
Loading a layer does not load the entities which do reference this layer, a layer is not an entity container, it’s just an DXF attribute, see also Basic Concepts: Layers.