Files
stepanalyser/.venv/lib/python3.12/site-packages/ezdxf/addons/dxf2code.py
Christian Anetzberger a197de9456 initial
2026-01-22 20:23:51 +01:00

905 lines
30 KiB
Python

# Copyright (c) 2019-2024, Manfred Moitzi
# License: MIT License
from __future__ import annotations
from typing import TYPE_CHECKING, Iterable, Mapping, Optional
import json
from ezdxf.sections.tables import TABLENAMES
from ezdxf.lldxf.tags import Tags
from ezdxf.entities import BoundaryPathType, EdgeType
import numpy as np
if TYPE_CHECKING:
from ezdxf.lldxf.types import DXFTag
from ezdxf.entities import (
Insert,
MText,
LWPolyline,
Polyline,
Spline,
Leader,
Dimension,
Image,
Mesh,
Hatch,
MPolygon,
Wipeout,
)
from ezdxf.entities import DXFEntity, Linetype
from ezdxf.entities.polygon import DXFPolygon
from ezdxf.layouts import BlockLayout
__all__ = [
"entities_to_code",
"block_to_code",
"table_entries_to_code",
"black",
]
def black(code: str, line_length=88, fast: bool = True) -> str:
"""Returns the source `code` as a single string formatted by `Black`_
Requires the installed `Black`_ formatter::
pip3 install black
Args:
code: source code
line_length: max. source code line length
fast: ``True`` for fast mode, ``False`` to check that the reformatted
code is valid
Raises:
ImportError: Black is not available
.. _black: https://pypi.org/project/black/
"""
import black
mode = black.FileMode()
mode.line_length = line_length
return black.format_file_contents(code, fast=fast, mode=mode)
def entities_to_code(
entities: Iterable[DXFEntity],
layout: str = "layout",
ignore: Optional[Iterable[str]] = None,
) -> Code:
"""
Translates DXF entities into Python source code to recreate this entities
by ezdxf.
Args:
entities: iterable of DXFEntity
layout: variable name of the layout (model space or block) as string
ignore: iterable of entities types to ignore as strings
like ``['IMAGE', 'DIMENSION']``
Returns:
:class:`Code`
"""
code = _SourceCodeGenerator(layout=layout)
code.translate_entities(entities, ignore=ignore)
return code.code
def block_to_code(
block: BlockLayout,
drawing: str = "doc",
ignore: Optional[Iterable[str]] = None,
) -> Code:
"""
Translates a BLOCK into Python source code to recreate the BLOCK by ezdxf.
Args:
block: block definition layout
drawing: variable name of the drawing as string
ignore: iterable of entities types to ignore as strings
like ['IMAGE', 'DIMENSION']
Returns:
:class:`Code`
"""
assert block.block is not None
dxfattribs = _purge_handles(block.block.dxfattribs())
block_name = dxfattribs.pop("name")
base_point = dxfattribs.pop("base_point")
code = _SourceCodeGenerator(layout="b")
prolog = f'b = {drawing}.blocks.new("{block_name}", base_point={base_point}, dxfattribs={{'
code.add_source_code_line(prolog)
code.add_source_code_lines(_fmt_mapping(dxfattribs, indent=4))
code.add_source_code_line(" }")
code.add_source_code_line(")")
code.translate_entities(block, ignore=ignore)
return code.code
def table_entries_to_code(
entities: Iterable[DXFEntity], drawing="doc"
) -> Code:
code = _SourceCodeGenerator(doc=drawing)
code.translate_entities(entities)
return code.code
class Code:
"""Source code container."""
def __init__(self) -> None:
self.code: list[str] = []
# global imports -> indentation level 0:
self.imports: set[str] = set()
# layer names as string:
self.layers: set[str] = set()
# text style name as string, requires a TABLE entry:
self.styles: set[str] = set()
# line type names as string, requires a TABLE entry:
self.linetypes: set[str] = set()
# dimension style names as string, requires a TABLE entry:
self.dimstyles: set[str] = set()
# block names as string, requires a BLOCK definition:
self.blocks: set[str] = set()
def code_str(self, indent: int = 0) -> str:
"""Returns the source code as a single string.
Args:
indent: source code indentation count by spaces
"""
lead_str = " " * indent
return "\n".join(lead_str + line for line in self.code)
def black_code_str(self, line_length=88) -> str:
"""Returns the source code as a single string formatted by `Black`_
Args:
line_length: max. source code line length
Raises:
ImportError: Black is not available
"""
return black(self.code_str(), line_length)
def __str__(self) -> str:
"""Returns the source code as a single string."""
return self.code_str()
def import_str(self, indent: int = 0) -> str:
"""Returns required imports as a single string.
Args:
indent: source code indentation count by spaces
"""
lead_str = " " * indent
return "\n".join(lead_str + line for line in self.imports)
def add_import(self, statement: str) -> None:
"""Add import statement, identical import statements are merged
together.
"""
self.imports.add(statement)
def add_line(self, code: str, indent: int = 0) -> None:
"""Add a single source code line without line ending ``\\n``."""
self.code.append(" " * indent + code)
def add_lines(self, code: Iterable[str], indent: int = 0) -> None:
"""Add multiple source code lines without line ending ``\\n``."""
for line in code:
self.add_line(line, indent=indent)
def merge(self, code: Code, indent: int = 0) -> None:
"""Add another :class:`Code` object."""
# merge used resources
self.imports.update(code.imports)
self.layers.update(code.layers)
self.linetypes.update(code.linetypes)
self.styles.update(code.styles)
self.dimstyles.update(code.dimstyles)
self.blocks.update(code.blocks)
# append source code lines
self.add_lines(code.code, indent=indent)
_PURGE_DXF_ATTRIBUTES = {
"handle",
"owner",
"paperspace",
"material_handle",
"visualstyle_handle",
"plotstyle_handle",
}
def _purge_handles(attribs: dict) -> dict:
"""Purge handles from DXF attributes which will be invalid in a new
document, or which will be set automatically by adding an entity to a
layout (paperspace).
Args:
attribs: entity DXF attributes dictionary
"""
return {k: v for k, v in attribs.items() if k not in _PURGE_DXF_ATTRIBUTES}
def _fmt_mapping(mapping: Mapping, indent: int = 0) -> Iterable[str]:
# key is always a string
fmt = " " * indent + "'{}': {},"
for k, v in mapping.items():
assert isinstance(k, str)
if isinstance(v, str):
v = json.dumps(v) # for correct escaping of quotes
else:
v = str(v) # format uses repr() for Vec3s
yield fmt.format(k, v)
def _fmt_list(l: Iterable, indent: int = 0) -> Iterable[str]:
def cleanup(values: Iterable) -> Iterable:
for value in values:
if isinstance(value, np.float64):
yield float(value)
else:
yield value
fmt = " " * indent + "{},"
for v in l:
if not isinstance(v, (float, int, str)):
v = tuple(cleanup(v))
yield fmt.format(str(v))
def _fmt_api_call(
func_call: str, args: Iterable[str], dxfattribs: dict
) -> list[str]:
attributes = dict(dxfattribs)
args = list(args) if args else []
def fmt_keywords() -> Iterable[str]:
for arg in args:
if arg not in attributes:
continue
value = attributes.pop(arg)
if isinstance(value, str):
valuestr = json.dumps(value) # quoted string!
else:
valuestr = str(value)
yield " {}={},".format(arg, valuestr)
s = [func_call]
s.extend(fmt_keywords())
s.append(" dxfattribs={")
s.extend(_fmt_mapping(attributes, indent=8))
s.extend(
[
" },",
")",
]
)
return s
def _fmt_dxf_tags(tags: Iterable[DXFTag], indent: int = 0):
fmt = " " * indent + "dxftag({}, {}),"
for code, value in tags:
assert isinstance(code, int)
if isinstance(value, str):
value = json.dumps(value) # for correct escaping of quotes
else:
value = str(value) # format uses repr() for Vec3s
yield fmt.format(code, value)
class _SourceCodeGenerator:
"""
The :class:`_SourceCodeGenerator` translates DXF entities into Python source
code for creating the same DXF entity in another model space or block
definition.
:ivar code: list of source code lines without line endings
:ivar required_imports: list of import source code lines, which are required
to create executable Python code.
"""
def __init__(self, layout: str = "layout", doc: str = "doc"):
self.doc = doc
self.layout = layout
self.code = Code()
def translate_entity(self, entity: DXFEntity) -> None:
"""Translates one DXF entity into Python source code. The generated
source code is appended to the attribute `source_code`.
Args:
entity: DXFEntity object
"""
dxftype = entity.dxftype()
try:
entity_translator = getattr(self, "_" + dxftype.lower())
except AttributeError:
self.add_source_code_line(f'# unsupported DXF entity "{dxftype}"')
else:
entity_translator(entity)
def translate_entities(
self,
entities: Iterable[DXFEntity],
ignore: Optional[Iterable[str]] = None,
) -> None:
"""Translates multiple DXF entities into Python source code. The
generated source code is appended to the attribute `source_code`.
Args:
entities: iterable of DXFEntity
ignore: iterable of entities types to ignore as strings
like ['IMAGE', 'DIMENSION']
"""
ignore = set(ignore) if ignore else set()
for entity in entities:
if entity.dxftype() not in ignore:
self.translate_entity(entity)
def add_used_resources(self, dxfattribs: Mapping) -> None:
"""Register used resources like layers, line types, text styles and
dimension styles.
Args:
dxfattribs: DXF attributes dictionary
"""
if "layer" in dxfattribs:
self.code.layers.add(dxfattribs["layer"])
if "linetype" in dxfattribs:
self.code.linetypes.add(dxfattribs["linetype"])
if "style" in dxfattribs:
self.code.styles.add(dxfattribs["style"])
if "dimstyle" in dxfattribs:
self.code.dimstyles.add(dxfattribs["dimstyle"])
def add_import_statement(self, statement: str) -> None:
self.code.add_import(statement)
def add_source_code_line(self, code: str) -> None:
self.code.add_line(code)
def add_source_code_lines(self, code: Iterable[str]) -> None:
assert not isinstance(code, str)
self.code.add_lines(code)
def add_list_source_code(
self,
values: Iterable,
prolog: str = "[",
epilog: str = "]",
indent: int = 0,
) -> None:
fmt_str = " " * indent + "{}"
self.add_source_code_line(fmt_str.format(prolog))
self.add_source_code_lines(_fmt_list(values, indent=4 + indent))
self.add_source_code_line(fmt_str.format(epilog))
def add_dict_source_code(
self,
mapping: Mapping,
prolog: str = "{",
epilog: str = "}",
indent: int = 0,
) -> None:
fmt_str = " " * indent + "{}"
self.add_source_code_line(fmt_str.format(prolog))
self.add_source_code_lines(_fmt_mapping(mapping, indent=4 + indent))
self.add_source_code_line(fmt_str.format(epilog))
def add_tags_source_code(
self, tags: Tags, prolog="tags = Tags(", epilog=")", indent=4
):
fmt_str = " " * indent + "{}"
self.add_source_code_line(fmt_str.format(prolog))
self.add_source_code_lines(_fmt_dxf_tags(tags, indent=4 + indent))
self.add_source_code_line(fmt_str.format(epilog))
def generic_api_call(
self, dxftype: str, dxfattribs: dict, prefix: str = "e = "
) -> Iterable[str]:
"""Returns the source code strings to create a DXF entity by a generic
`new_entity()` call.
Args:
dxftype: DXF entity type as string, like 'LINE'
dxfattribs: DXF attributes dictionary
prefix: prefix string like variable assignment 'e = '
"""
dxfattribs = _purge_handles(dxfattribs)
self.add_used_resources(dxfattribs)
s = [
f"{prefix}{self.layout}.new_entity(",
f" '{dxftype}',",
" dxfattribs={",
]
s.extend(_fmt_mapping(dxfattribs, indent=8))
s.extend(
[
" },",
")",
]
)
return s
def api_call(
self,
api_call: str,
args: Iterable[str],
dxfattribs: dict,
prefix: str = "e = ",
) -> Iterable[str]:
"""Returns the source code strings to create a DXF entity by the
specialised API call.
Args:
api_call: API function call like 'add_line('
args: DXF attributes to pass as arguments
dxfattribs: DXF attributes dictionary
prefix: prefix string like variable assignment 'e = '
"""
dxfattribs = _purge_handles(dxfattribs)
func_call = f"{prefix}{self.layout}.{api_call}"
return _fmt_api_call(func_call, args, dxfattribs)
def new_table_entry(self, dxftype: str, dxfattribs: dict) -> Iterable[str]:
"""Returns the source code strings to create a new table entity by
ezdxf.
Args:
dxftype: table entry type as string, like 'LAYER'
dxfattribs: DXF attributes dictionary
"""
table = f"{self.doc}.{TABLENAMES[dxftype]}"
dxfattribs = _purge_handles(dxfattribs)
name = dxfattribs.pop("name")
s = [
f"if '{name}' not in {table}:",
f" t = {table}.new(",
f" '{name}',",
" dxfattribs={",
]
s.extend(_fmt_mapping(dxfattribs, indent=12))
s.extend(
[
" },",
" )",
]
)
return s
# simple graphical types
def _line(self, entity: DXFEntity) -> None:
self.add_source_code_lines(
self.api_call("add_line(", ["start", "end"], entity.dxfattribs())
)
def _point(self, entity: DXFEntity) -> None:
self.add_source_code_lines(
self.api_call("add_point(", ["location"], entity.dxfattribs())
)
def _circle(self, entity: DXFEntity) -> None:
self.add_source_code_lines(
self.api_call(
"add_circle(", ["center", "radius"], entity.dxfattribs()
)
)
def _arc(self, entity: DXFEntity) -> None:
self.add_source_code_lines(
self.api_call(
"add_arc(",
["center", "radius", "start_angle", "end_angle"],
entity.dxfattribs(),
)
)
def _text(self, entity: DXFEntity) -> None:
self.add_source_code_lines(
self.api_call("add_text(", ["text"], entity.dxfattribs())
)
def _solid(self, entity: DXFEntity) -> None:
self.add_source_code_lines(
self.generic_api_call("SOLID", entity.dxfattribs())
)
def _trace(self, entity: DXFEntity) -> None:
self.add_source_code_lines(
self.generic_api_call("TRACE", entity.dxfattribs())
)
def _3dface(self, entity: DXFEntity) -> None:
self.add_source_code_lines(
self.generic_api_call("3DFACE", entity.dxfattribs())
)
def _shape(self, entity: DXFEntity) -> None:
self.add_source_code_lines(
self.api_call(
"add_shape(", ["name", "insert", "size"], entity.dxfattribs()
)
)
def _attrib(self, entity: DXFEntity) -> None:
self.add_source_code_lines(
self.api_call(
"add_attrib(", ["tag", "text", "insert"], entity.dxfattribs()
)
)
def _attdef(self, entity: DXFEntity) -> None:
self.add_source_code_lines(
self.generic_api_call("ATTDEF", entity.dxfattribs())
)
def _ellipse(self, entity: DXFEntity) -> None:
self.add_source_code_lines(
self.api_call(
"add_ellipse(",
["center", "major_axis", "ratio", "start_param", "end_param"],
entity.dxfattribs(),
)
)
def _viewport(self, entity: DXFEntity) -> None:
self.add_source_code_lines(
self.generic_api_call("VIEWPORT", entity.dxfattribs())
)
self.add_source_code_line(
'# Set valid handles or remove attributes ending with "_handle", '
"otherwise the DXF file is invalid for AutoCAD"
)
# complex graphical types
def _insert(self, entity: Insert) -> None:
self.code.blocks.add(entity.dxf.name)
self.add_source_code_lines(
self.api_call(
"add_blockref(", ["name", "insert"], entity.dxfattribs()
)
)
if len(entity.attribs):
for attrib in entity.attribs:
dxfattribs = attrib.dxfattribs()
dxfattribs[
"layer"
] = entity.dxf.layer # set ATTRIB layer to same as INSERT
self.add_source_code_lines(
self.generic_api_call(
"ATTRIB", attrib.dxfattribs(), prefix="a = "
)
)
self.add_source_code_line("e.attribs.append(a)")
def _mtext(self, entity: MText) -> None:
self.add_source_code_lines(
self.generic_api_call("MTEXT", entity.dxfattribs())
)
# MTEXT content 'text' is not a single DXF tag and therefore not a DXF
# attribute
self.add_source_code_line("e.text = {}".format(json.dumps(entity.text)))
def _lwpolyline(self, entity: LWPolyline) -> None:
self.add_source_code_lines(
self.generic_api_call("LWPOLYLINE", entity.dxfattribs())
)
# lwpolyline points are not DXF attributes
self.add_list_source_code(
entity.get_points(), prolog="e.set_points([", epilog="])"
)
def _spline(self, entity: Spline) -> None:
self.add_source_code_lines(
self.api_call("add_spline(", ["degree"], entity.dxfattribs())
)
# spline points, knots and weights are not DXF attributes
if len(entity.fit_points):
self.add_list_source_code(
entity.fit_points, prolog="e.fit_points = [", epilog="]"
)
if len(entity.control_points):
self.add_list_source_code(
entity.control_points, prolog="e.control_points = [", epilog="]"
)
if len(entity.knots):
self.add_list_source_code(
entity.knots, prolog="e.knots = [", epilog="]"
)
if len(entity.weights):
self.add_list_source_code(
entity.weights, prolog="e.weights = [", epilog="]"
)
def _polyline(self, entity: Polyline) -> None:
self.add_source_code_lines(
self.generic_api_call("POLYLINE", entity.dxfattribs())
)
# polyline vertices are separate DXF entities and therefore not DXF attributes
for v in entity.vertices:
attribs = _purge_handles(v.dxfattribs())
location = attribs.pop("location")
if "layer" in attribs:
del attribs[
"layer"
] # layer is automatically set to the POLYLINE layer
# each VERTEX can have different DXF attributes: bulge, start_width, end_width ...
self.add_source_code_line(
f"e.append_vertex({str(location)}, dxfattribs={attribs})"
)
def _leader(self, entity: Leader):
self.add_source_code_line(
"# Dimension style attribute overriding is not supported!"
)
self.add_source_code_lines(
self.generic_api_call("LEADER", entity.dxfattribs())
)
self.add_list_source_code(
entity.vertices, prolog="e.set_vertices([", epilog="])"
)
def _dimension(self, entity: Dimension):
self.add_import_statement(
"from ezdxf.dimstyleoverride import DimStyleOverride"
)
self.add_source_code_line(
"# Dimension style attribute overriding is not supported!"
)
self.add_source_code_lines(
self.generic_api_call("DIMENSION", entity.dxfattribs())
)
self.add_source_code_lines(
[
"# You have to create the required graphical representation for ",
"# the DIMENSION entity as anonymous block, otherwise the DXF file",
"# is invalid for AutoCAD (but not for BricsCAD):",
"# DimStyleOverride(e).render()",
"",
]
)
def _image(self, entity: Image):
self.add_source_code_line(
"# Image requires IMAGEDEF and IMAGEDEFREACTOR objects in the "
"OBJECTS section!"
)
self.add_source_code_lines(
self.generic_api_call("IMAGE", entity.dxfattribs())
)
if len(entity.boundary_path):
self.add_list_source_code(
(v for v in entity.boundary_path), # just x, y axis
prolog="e.set_boundary_path([",
epilog="])",
)
self.add_source_code_line(
"# Set valid image_def_handle and image_def_reactor_handle, "
"otherwise the DXF file is invalid for AutoCAD"
)
def _wipeout(self, entity: Wipeout):
self.add_source_code_lines(
self.generic_api_call("WIPEOUT", entity.dxfattribs())
)
if len(entity.boundary_path):
self.add_list_source_code(
(v for v in entity.boundary_path), # just x, y axis
prolog="e.set_boundary_path([",
epilog="])",
)
def _mesh(self, entity: Mesh):
self.add_source_code_lines(
self.api_call("add_mesh(", [], entity.dxfattribs())
)
if len(entity.vertices):
self.add_list_source_code(
entity.vertices, prolog="e.vertices = [", epilog="]"
)
if len(entity.edges):
# array.array -> tuple
self.add_list_source_code(
(tuple(e) for e in entity.edges),
prolog="e.edges = [",
epilog="]",
)
if len(entity.faces):
# array.array -> tuple
self.add_list_source_code(
(tuple(f) for f in entity.faces),
prolog="e.faces = [",
epilog="]",
)
if len(entity.creases):
self.add_list_source_code(
entity.creases, prolog="e.creases = [", epilog="]"
)
def _hatch(self, entity: Hatch):
dxfattribs = entity.dxfattribs()
dxfattribs["associative"] = 0 # associative hatch not supported
self.add_source_code_lines(
self.api_call("add_hatch(", ["color"], dxfattribs)
)
self._polygon(entity)
def _mpolygon(self, entity: MPolygon):
dxfattribs = entity.dxfattribs()
self.add_source_code_lines(
self.api_call("add_mpolygon(", ["color"], dxfattribs)
)
if entity.dxf.solid_fill:
self.add_source_code_line(
f"e.set_solid_fill(color={entity.dxf.fill_color})\n"
)
self._polygon(entity)
def _polygon(self, entity: DXFPolygon):
add_line = self.add_source_code_line
if len(entity.seeds):
add_line(f"e.set_seed_points({entity.seeds})")
if entity.pattern:
self.add_list_source_code(
map(str, entity.pattern.lines),
prolog="e.set_pattern_definition([",
epilog="])",
)
self.add_source_code_line("e.dxf.solid_fill = 0")
arg = " {}={},"
if entity.gradient is not None:
g = entity.gradient
add_line("e.set_gradient(")
add_line(arg.format("color1", str(g.color1)))
add_line(arg.format("color2", str(g.color2)))
add_line(arg.format("rotation", g.rotation))
add_line(arg.format("centered", g.centered))
add_line(arg.format("one_color", g.one_color))
add_line(arg.format("name", json.dumps(g.name)))
add_line(")")
for count, path in enumerate(entity.paths, start=1):
if path.type == BoundaryPathType.POLYLINE:
add_line("# {}. polyline path".format(count))
self.add_list_source_code(
path.vertices,
prolog="e.paths.add_polyline_path([",
epilog=" ],",
)
add_line(arg.format("is_closed", str(path.is_closed)))
add_line(arg.format("flags", str(path.path_type_flags)))
add_line(")")
else: # EdgePath
add_line(
f"# {count}. edge path: associative hatch not supported"
)
add_line(
f"ep = e.paths.add_edge_path(flags={path.path_type_flags})"
)
for edge in path.edges:
if edge.type == EdgeType.LINE:
add_line(f"ep.add_line({edge.start}, {str(edge.end)})")
elif edge.type == EdgeType.ARC:
# Start- and end angles are always stored in ccw
# orientation:
add_line("ep.add_arc(")
add_line(arg.format("center", str(edge.center)))
add_line(arg.format("radius", edge.radius))
add_line(arg.format("start_angle", edge.start_angle))
add_line(arg.format("end_angle", edge.end_angle))
add_line(arg.format("ccw", edge.ccw))
add_line(")")
elif edge.type == EdgeType.ELLIPSE:
# Start- and end params are always stored in ccw
# orientation:
add_line("ep.add_ellipse(")
add_line(arg.format("center", str(edge.center)))
add_line(arg.format("major_axis", str(edge.major_axis)))
add_line(arg.format("ratio", edge.ratio))
add_line(arg.format("start_angle", edge.start_angle))
add_line(arg.format("end_angle", edge.end_angle))
add_line(arg.format("ccw", edge.ccw))
add_line(")")
elif edge.type == EdgeType.SPLINE:
add_line("ep.add_spline(")
if edge.fit_points:
add_line(
arg.format(
"fit_points",
str([fp for fp in edge.fit_points]),
)
)
if edge.control_points:
add_line(
arg.format(
"control_points",
str([cp for cp in edge.control_points]),
)
)
if edge.knot_values:
add_line(
arg.format("knot_values", str(edge.knot_values))
)
if edge.weights:
add_line(arg.format("weights", str(edge.weights)))
add_line(arg.format("degree", edge.degree))
add_line(arg.format("periodic", edge.periodic))
if edge.start_tangent is not None:
add_line(
arg.format(
"start_tangent", str(edge.start_tangent)
)
)
if edge.end_tangent is not None:
add_line(
arg.format("end_tangent", str(edge.end_tangent))
)
add_line(")")
# simple table entries
def _layer(self, layer: DXFEntity):
self.add_source_code_lines(
self.new_table_entry("LAYER", layer.dxfattribs())
)
def _ltype(self, ltype: Linetype):
self.add_import_statement("from ezdxf.lldxf.tags import Tags")
self.add_import_statement("from ezdxf.lldxf.types import dxftag")
self.add_import_statement(
"from ezdxf.entities.ltype import LinetypePattern"
)
self.add_source_code_lines(
self.new_table_entry("LTYPE", ltype.dxfattribs())
)
self.add_tags_source_code(
ltype.pattern_tags.tags,
prolog="tags = Tags([",
epilog="])",
indent=4,
)
self.add_source_code_line(" t.pattern_tags = LinetypePattern(tags)")
def _style(self, style: DXFEntity):
self.add_source_code_lines(
self.new_table_entry("STYLE", style.dxfattribs())
)
def _dimstyle(self, dimstyle: DXFEntity):
self.add_source_code_lines(
self.new_table_entry("DIMSTYLE", dimstyle.dxfattribs())
)
def _appid(self, appid: DXFEntity):
self.add_source_code_lines(
self.new_table_entry("APPID", appid.dxfattribs())
)