Exemple #1
0
    def add_ellipse(self,
                    center: Tuple[float, float],
                    major_axis: Tuple[float, float] = (1., 0.),
                    ratio: float = 1.,
                    start_angle: float = 0.,
                    end_angle: float = 360.,
                    is_counter_clockwise: int = 0) -> 'EllipseEdge':
        """
        Add an :class:`EllipseEdge`.

        Args:
            center: center point of ellipse, ``(x, y)`` tuple
            major_axis: vector of major axis as ``(x, y)`` tuple
            ratio: ratio of minor axis to major axis as float
            start_angle: start angle of arc in degrees
            end_angle: end angle of arc in degrees
            is_counter_clockwise: ``1`` for counter clockwise ``0`` for clockwise orientation

        """
        if ratio > 1.:
            raise const.DXFValueError("Parameter 'ratio' has to be <= 1.0")
        ellipse = EllipseEdge()
        ellipse.center = center
        ellipse.major_axis = major_axis
        ellipse.ratio = ratio
        ellipse.start_angle = start_angle
        ellipse.end_angle = end_angle
        ellipse.is_counter_clockwise = is_counter_clockwise
        self.edges.append(ellipse)
        return ellipse
Exemple #2
0
    def set_arrows(self, blk: str = '', blk1: str = '', blk2: str = '',
                   ldrblk: str = '') -> None:
        """ Set arrows by block names or AutoCAD standard arrow names, set
        DIMTSZ to ``0`` which disables tick.

        Args:
            blk: block/arrow name for both arrows, if DIMSAH is 0
            blk1: block/arrow name for first arrow, if DIMSAH is 1
            blk2: block/arrow name for second arrow, if DIMSAH is 1
            ldrblk: block/arrow name for leader

        """
        self.set_dxf_attrib('dimblk', blk)
        self.set_dxf_attrib('dimblk1', blk1)
        self.set_dxf_attrib('dimblk2', blk2)
        self.set_dxf_attrib('dimldrblk', ldrblk)
        self.set_dxf_attrib('dimtsz', 0)  # use blocks

        # only existing BLOCK definitions allowed
        if self.doc:
            blocks = self.doc.blocks
            for b in (blk, blk1, blk2, ldrblk):
                if ARROWS.is_acad_arrow(b):  # not real blocks
                    ARROWS.create_block(blocks, b)
                    continue
                if b and b not in blocks:
                    raise const.DXFValueError(
                        'BLOCK "{}" does not exist.'.format(blk))
Exemple #3
0
    def get_sortents_table(self, create: bool = True) -> 'SortEntsTable':
        """ Get/Create the SORTENTSTABLE object associated to the layout.

        Args:
            create: new table if table do not exist and `create` is ``True``

        Raises:
            DXFValueError: if table not exist and `create` is ``False``

        (internal API)
        """
        xdict = self.get_extension_dict()
        try:
            sortents_table = xdict['ACAD_SORTENTS']
        except const.DXFKeyError:
            if create:
                sortents_table = self.doc.objects.new_entity(
                    'SORTENTSTABLE',
                    dxfattribs={
                        'owner': xdict.dxf.handle,
                        'block_record_handle': self.layout_key
                    },
                )
                xdict['ACAD_SORTENTS'] = sortents_table.dxf.handle
            else:
                raise const.DXFValueError(
                    'Extension dictionary entry ACAD_SORTENTS does not exist.')
        return sortents_table
Exemple #4
0
    def new(self, name: str = None, description: str = "",
            selectable: bool = True) -> DXFGroup:
        r""" Creates a new group. If `name` is ``None`` an unnamed group is
        created, which has an automatically generated name like "\*Annnn".

        Args:
            name: group name as string
            description: group description as string
            selectable: group is selectable if ``True``

        """
        if name in self:
            raise const.DXFValueError(f"GROUP '{name}' already exists.")

        if name is None:
            name = self.next_name()
            unnamed = 1
        else:
            unnamed = 0
        # The group name isn't stored in the group entity itself.
        dxfattribs = {
            'description': description,
            'unnamed': unnamed,
            'selectable': int(bool(selectable)),
        }
        return cast(DXFGroup, self._new(name, dxfattribs))
Exemple #5
0
    def new_geodata(self, dxfattribs: dict = None) -> 'GeoData':
        """ Creates a new :class:`GeoData` entity and replaces existing ones.
        The GEODATA entity resides in the OBJECTS section and not in the
        modelspace, it is linked to the modelspace by an
        :class:`~ezdxf.entities.ExtensionDict` located in BLOCK_RECORD of the
        modelspace.

        The GEODATA entity requires DXF R2010. The DXF reference does not
        document if other layouts than the modelspace supports geo referencing,
        so I assume getting/setting geo data may only make sense for the
        modelspace.

        Args:
            dxfattribs: DXF attributes for :class:`~ezdxf.entities.GeoData` entity

        """
        if self.doc.dxfversion < const.DXF2010:
            raise const.DXFValueError(
                'GEODATA entity requires DXF R2010 or later.')

        if dxfattribs is None:
            dxfattribs = {}
        xdict = self.get_extension_dict()
        geodata = self.doc.objects.add_geodata(
            owner=xdict.dictionary.dxf.handle,
            dxfattribs=dxfattribs,
        )
        xdict['ACAD_GEOGRAPHICDATA'] = geodata
        return geodata
Exemple #6
0
    def set_style(self, name: str) -> None:
        """Set MLINESTYLE by name and update geometry accordingly.
        The MLINESTYLE definition must exist.
        """
        if self.doc is None:
            logger.debug("Can't change style of unbounded MLINE entity.")
            return
        try:
            style = self.doc.mline_styles[name]
        except const.DXFKeyError:
            raise const.DXFValueError(f"Undefined MLINE style: {name}")
        assert isinstance(style, MLineStyle)
        # Line- and fill parametrization depends on the count of
        # elements, a change in the number of elements triggers a
        # reset of the parametrization:
        old_style = self.style
        new_element_count = len(style.elements)
        reset = False
        if old_style is not None:
            # Do not trust the stored "style_element_count" value
            reset = len(old_style.elements) != new_element_count

        self.dxf.style_name = name
        self.dxf.style_handle = style.dxf.handle
        self.dxf.style_element_count = new_element_count
        if reset:
            self.update_geometry()
Exemple #7
0
 def rgb(self, value: Optional[colors.RGB]):
     if value is None:
         self._true_color = None
     elif validator.is_valid_rgb(value):
         self._true_color = value
     else:
         raise const.DXFValueError(f"invalid true color value '{value}'")
Exemple #8
0
def set_current_lineweight(doc: "Drawing", lineweight: int):
    """Set current lineweight, see :ref:`lineweights` reference for valid
    values.
    """
    if not validator.is_valid_lineweight(lineweight):
        raise const.DXFValueError(f'invalid lineweight value: "{lineweight}"')
    doc.header[CURRENT_LINEWEIGHT] = lineweight
Exemple #9
0
    def set_gradient(
        self,
        color1: RGB = (0, 0, 0),
        color2: RGB = (255, 255, 255),
        rotation: float = 0.0,
        centered: float = 0.0,
        one_color: int = 0,
        tint: float = 0.0,
        name: str = "LINEAR",
    ) -> None:
        """Set :class:`Hatch` and :class:`MPolygon` to gradient fill mode and
        removes all pattern fill related data. Gradient support requires
        DXF R2004+.
        A gradient filled hatch is also a solid filled hatch.

        Valid gradient type names are:

            - ``'LINEAR'``
            - ``'CYLINDER'``
            - ``'INVCYLINDER'``
            - ``'SPHERICAL'``
            - ``'INVSPHERICAL'``
            - ``'HEMISPHERICAL'``
            - ``'INVHEMISPHERICAL'``
            - ``'CURVED'``
            - ``'INVCURVED'``

        Args:
            color1: (r, g, b)-tuple for first color, rgb values as int in
                the range [0, 255]
            color2: (r, g, b)-tuple for second color, rgb values as int in
                the range [0, 255]
            rotation: rotation angle in degrees
            centered: determines whether the gradient is centered or not
            one_color: 1 for gradient from `color1` to tinted `color1`
            tint: determines the tinted target `color1` for a one color
                gradient. (valid range 0.0 to 1.0)
            name: name of gradient type, default "LINEAR"

        """
        if self.doc is not None and self.doc.dxfversion < const.DXF2004:
            raise const.DXFVersionError("Gradient support requires DXF R2004")
        if name and name not in const.GRADIENT_TYPES:
            raise const.DXFValueError(f"Invalid gradient type name: {name}")

        self.pattern = None
        self.dxf.solid_fill = 1
        self.dxf.pattern_name = "SOLID"
        self.dxf.pattern_type = const.HATCH_TYPE_PREDEFINED

        gradient = Gradient()
        gradient.color1 = color1
        gradient.color2 = color2
        gradient.one_color = one_color
        gradient.rotation = rotation
        gradient.centered = centered
        gradient.tint = tint
        gradient.name = name
        self.gradient = gradient
Exemple #10
0
    def add(
        self,
        name: str,
        *,
        color: int = const.BYLAYER,
        true_color: int = None,
        linetype: str = "Continuous",
        lineweight: int = const.LINEWEIGHT_BYLAYER,
        plot: bool = True,
        transparency: Optional[float] = None,
        dxfattribs: Dict = None,
    ) -> "Layer":
        """Add a new :class:`~ezdxf.entities.Layer`.

        Args:
            name (str): layer name
            color (int): :ref:`ACI` value, default is BYLAYER
            true_color (int): true color value, use :func:`ezdxf.rgb2int` to
                create ``int`` values from RGB values
            linetype (str): line type name, default is "Continuous"
            lineweight (int): line weight, default is BYLAYER
            plot (bool): plot layer as bool, default is ``True``
            transparency: transparency value in the range [0, 1], where 1 is
                100% transparent and 0 is opaque
            dxfattribs (dict): additional DXF attributes

        .. versionadded:: 0.17

        """
        dxfattribs = dict(dxfattribs or {})
        if validator.is_valid_aci_color(color):
            dxfattribs["color"] = color
        else:
            raise const.DXFValueError(f"invalid color: {color}")
        dxfattribs["linetype"] = linetype
        if validator.is_valid_lineweight(lineweight):
            dxfattribs["lineweight"] = lineweight
        else:
            raise const.DXFValueError(f"invalid lineweight: {lineweight}")
        if true_color is not None:
            dxfattribs["true_color"] = int(true_color)
        dxfattribs["plot"] = int(plot)
        layer = cast("Layer", self.new(name, dxfattribs))
        if transparency is not None:
            layer.transparency = transparency
        return layer
Exemple #11
0
 def transparency(self, value: Optional[float]):
     if value is None:
         self._transparency = None
     elif value == TRANSPARENCY_BYBLOCK:
         self._transparency = TRANSPARENCY_BYBLOCK
     elif isinstance(value, float) and (0.0 <= value <= 1.0):
         self._transparency = value
     else:
         raise const.DXFValueError(f"invalid transparency value '{value}'")
Exemple #12
0
    def __setitem__(self, key: str, value: Any) -> None:
        """ Set header variable `key` to `value` by index operator like:
        :code:`drawing.header['$ANGDIR'] = 1`

        """
        try:
            tags = self._headervar_factory(key, value)
        except (IndexError, ValueError):
            raise const.DXFValueError(str(value))
        self.hdrvars[key] = HeaderVar(tags)
Exemple #13
0
    def acquire_arrow(self, name: str):
        """ For standard AutoCAD and ezdxf arrows create block definitions if
        required, otherwise check if block `name` exist. (internal API)

        """
        from ezdxf.render.arrows import ARROWS
        if ARROWS.is_acad_arrow(name) or ARROWS.is_ezdxf_arrow(name):
            ARROWS.create_block(self.blocks, name)
        elif name not in self.blocks:
            raise const.DXFValueError(f'Arrow block "{name}" does not exist.')
Exemple #14
0
 def set_seed_points(self, points: Sequence[Tuple[float, float]]) -> None:
     """
     Set seed points, `points` is a list of ``(x, y)`` tuples, I don't know why there can be more than one
     seed point. All points in :ref:`OCS` (:attr:`Hatch.dxf.elevation` is the Z value)
     """
     if len(points) < 1:
         raise const.DXFValueError(
             "Param points should be a collection of 2D points and requires at least one point.")
     self.seeds = list(points)
     self.dxf.n_seed_points = len(self.seeds)
Exemple #15
0
    def new_line(angle: float = 0.,
                 base_point: Tuple[float, float] = (0., 0.),
                 offset: Tuple[float, float] = (0., 0.),
                 dash_length_items: List[float] = None) -> 'PatternLine':
        """
        Create a new pattern definition line, but does not add the line to the :attr:`Pattern.lines` attribute.

        """
        if dash_length_items is None:
            raise const.DXFValueError("Parameter 'dash_length_items' must not be None.")
        return PatternLine(angle, base_point, offset, dash_length_items)
Exemple #16
0
    def delete_layout(self, name: str) -> None:
        """
        Delete paper space layout `name` and all entities owned by this layout.
        Available only for DXF R2000 or later, DXF R12 supports only one
        paperspace and it can't be deleted.

        """
        if name not in self.layouts:
            raise const.DXFValueError(f"Layout '{name}' does not exist.")
        else:
            self.layouts.delete(name)
Exemple #17
0
    def set_gradient(self,
                     color1: 'RGB' = (0, 0, 0),
                     color2: 'RGB' = (255, 255, 255),
                     rotation: float = 0.,
                     centered: float = 0.,
                     one_color: int = 0,
                     tint: float = 0.,
                     name: str = 'LINEAR') -> None:
        """
        Set :class:`Hatch` to gradient fill mode and removes all pattern fill related data. Gradient support requires
        DXF DXF R2004. A gradient filled hatch is also a solid filled hatch.

        Valid gradient type names are:


            - ``'LINEAR'``
            - ``'CYLINDER'``
            - ``'INVCYLINDER'``
            - ``'SPHERICAL'``
            - ``'INVSPHERICAL'``
            - ``'HEMISPHERICAL'``
            - ``'INVHEMISPHERICAL'``
            - ``'CURVED'``
            - ``'INVCURVED'``

        Args:
            color1: ``(r, g, b)`` tuple for first color, rgb values as int in range 0..255
            color2: ``(r, g, b)`` tuple for second color, rgb values as int in range 0..255
            rotation: rotation in degrees
            centered: determines whether the gradient is centered or not
            one_color: ``1`` for gradient from `color1` to tinted `color1``
            tint: determines the tinted target `color1` for a one color gradient. (valid range ``0.0`` to ``1.0``)
            name: name of gradient type, default ``'LINEAR'``

        """
        if self.doc is not None and self.drawing.dxfversion < DXF2004:
            raise const.DXFVersionError("Gradient support requires DXF R2004")
        if name not in const.GRADIENT_TYPES:
            raise const.DXFValueError('Invalid gradient type name: %s' % name)

        self.pattern = None
        self.dxf.solid_fill = 1
        self.dxf.pattern_name = 'SOLID'
        self.dxf.pattern_type = const.HATCH_TYPE_PREDEFINED

        gradient = Gradient()
        gradient.color1 = color1
        gradient.color2 = color2
        gradient.one_color = one_color
        gradient.rotation = rotation
        gradient.centered = centered
        gradient.tint = tint
        gradient.name = name
        self.gradient = gradient
Exemple #18
0
 def check(value):
     value = cast_value(attrib_def.code, value)
     if not attrib_def.is_valid_value(value):
         if attrib_def.fixer:
             value = attrib_def.fixer(value)
             logger.debug(f'Fixed invalid attribute "{key}" in entity'
                          f' {entity()} to "{str(value)}".')
         else:
             raise const.DXFValueError(
                 f'Invalid value {str(value)} for attribute "{key}" in '
                 f'entity {entity()}.')
     return value
Exemple #19
0
    def add_ellipse(
        self,
        center: "Vertex",
        major_axis: "Vertex" = (1.0, 0.0),
        ratio: float = 1.0,
        start_angle: float = 0.0,
        end_angle: float = 360.0,
        ccw: bool = True,
    ) -> "EllipseEdge":
        """Add an :class:`EllipseEdge`.

        **Adding Clockwise Oriented Ellipses:**

        Clockwise oriented :class:`EllipseEdge` objects are sometimes necessary
        to build closed loops, but the :class:`EllipseEdge` objects are always
        represented in counter-clockwise orientation.
        To add a clockwise oriented :class:`EllipseEdge` you have to swap the
        start- and end angle and set the `ccw` flag to ``False``,
        e.g. to add a clockwise oriented :class:`EllipseEdge` from 180 to 90
        degree, add the :class:`EllipseEdge` in counter-clockwise orientation
        with swapped angles::

            edge_path.add_ellipse(center, major_axis, ratio, start_angle=90, end_angle=180, ccw=False)

        Args:
            center: center point of ellipse, (x, y)-tuple
            major_axis: vector of major axis as (x, y)-tuple
            ratio: ratio of minor axis to major axis as float
            start_angle: start angle of ellipse in degrees (`end_angle` for a
                clockwise oriented ellipse)
            end_angle: end angle of ellipse in degrees (`start_angle` for a
                clockwise oriented ellipse)
            ccw: ``True`` for counter clockwise ``False`` for
                clockwise orientation

        """

        if ratio > 1.0:
            raise const.DXFValueError("argument 'ratio' has to be <= 1.0")
        ellipse = EllipseEdge()
        ellipse.center = Vec2(center)
        ellipse.major_axis = Vec2(major_axis)
        ellipse.ratio = ratio
        # Start- and end angles are always stored in counter-clockwise
        # orientation!
        ellipse.start_angle = start_angle
        ellipse.end_angle = end_angle
        # Flag to export the counter-clockwise oriented ellipse in
        # correct clockwise orientation:
        ellipse.ccw = bool(ccw)
        self.edges.append(ellipse)
        return ellipse
Exemple #20
0
    def set_seed_points(self, points: Iterable[Tuple[float, float]]) -> None:
        """Set seed points, `points` is an iterable of (x, y)-tuples.
        I don't know why there can be more than one seed point.
        All points in :ref:`OCS` (:attr:`Hatch.dxf.elevation` is the Z value)

        """
        points = list(points)
        if len(points) < 1:
            raise const.DXFValueError(
                "Param points should be an iterable of 2D points and requires at "
                "least one point.")
        self.seeds = list(points)
        self.dxf.n_seed_points = len(self.seeds)
Exemple #21
0
    def get_xdata(self, appid: str) -> Tags:
        """ Returns extended data for `appid`.

        Args:
            appid: application name as defined in the APPID table.

        Raises:
            DXFValueError: no extended data for `appid` found

        """
        if self.xdata:
            return Tags(self.xdata.get(appid)[1:])
        else:
            raise const.DXFValueError(appid)
Exemple #22
0
    def replace(self, tag: str, value: str) -> None:
        """ Replaces the value of the first custom property `tag` by a new
        `value`.

        Raises :class:`DXFValueError` if `tag`  does not exist.

        """
        properties = self.properties
        for index in range(len(properties)):
            name = properties[index][0]
            if name == tag:
                properties[index] = (name, value)
                return

        raise const.DXFValueError(f"Tag '{tag}' does not exist")
Exemple #23
0
 def set_vertices(self, vertices: Sequence[Sequence[float]], is_closed: bool = True) -> None:
     """ Set new `vertices` as new polyline path, a vertex has to be a ``(x, y)`` or a ``(x, y, bulge)`` tuple.
     """
     new_vertices = []
     for vertex in vertices:
         if len(vertex) == 2:
             x, y = vertex
             bulge = 0
         elif len(vertex) == 3:
             x, y, bulge = vertex
         else:
             raise const.DXFValueError("Invalid vertex format, expected (x, y) or (x, y, bulge)")
         new_vertices.append((x, y, bulge))
     self.vertices = new_vertices
     self.is_closed = is_closed
Exemple #24
0
    def get_xdata_list(self, appid: str, name: str) -> Tags:
        """ Returns tag list `name` for extended data `appid`.

        Args:
            appid: application name as defined in the APPID table.
            name: extended data list name

        Raises:
            DXFValueError: no extended data for `appid` found or no data list `name` not found

        """
        if self.xdata:
            return Tags(self.xdata.get_xlist(appid, name))
        else:
            raise const.DXFValueError(appid)
Exemple #25
0
 def new(cls,
         start: Vertex,
         line_direction: Vertex,
         miter_direction: Vertex,
         line_params: Iterable = None,
         fill_params: Iterable = None) -> 'MLineVertex':
     vtx = MLineVertex()
     vtx.location = Vec3(start)
     vtx.line_direction = Vec3(line_direction)
     vtx.miter_direction = Vec3(miter_direction)
     vtx.line_params = list(line_params or [])
     vtx.fill_params = list(fill_params or [])
     if len(vtx.line_params) != len(vtx.fill_params):
         raise const.DXFValueError(
             'Count mismatch of line- and fill parameters')
     return vtx
Exemple #26
0
    def remove(self, tag: str, all: bool = False) -> None:
        """ Removes the first occurrence of custom property `tag`, removes all
        occurrences if `all` is ``True``.

        Raises `:class:`DXFValueError` if `tag`  does not exist.

        """
        found_tag = False
        for item in self.properties:
            if item[0] == tag:
                self.properties.remove(item)
                found_tag = True
                if not all:
                    return
        if not found_tag:
            raise const.DXFValueError(f"Tag '{tag}' does not exist")
Exemple #27
0
    def delete(self, group: Union[DXFGroup, str]) -> None:
        """ Delete `group`, `group` can be an object of type :class:`DXFGroup`
        or a group name as string.

        """
        # Delete group by name:
        if isinstance(group, str):
            name = group
        elif group.dxftype() == 'GROUP':
            name = get_group_name(group, self.entitydb)
        else:
            raise TypeError(group.dxftype())

        if name in self:
            super().delete(name)
        else:
            raise const.DXFValueError("GROUP not in group table registered.")
Exemple #28
0
    def export_dxf(self, tagwriter: 'TagWriter') -> None:
        write_tag = tagwriter.write_tag2
        write_tag(72, 4)  # edge type
        write_tag(94, int(self.degree))
        write_tag(73, int(self.rational))
        write_tag(74, int(self.periodic))
        write_tag(95, len(self.knot_values))  # number of knots
        write_tag(96, len(self.control_points))  # number of control points
        # build knot values list
        # knot values have to be present and valid, otherwise AutoCAD crashes
        if len(self.knot_values):
            for value in self.knot_values:
                write_tag(40, float(value))
        else:
            raise const.DXFValueError(
                "SplineEdge: missing required knot values")

        # build control points
        # control points have to be present and valid, otherwise AutoCAD crashes
        for x, y, *_ in self.control_points:
            tagwriter.write_tag2(10, float(x))
            tagwriter.write_tag2(20, float(y))

        # build weights list, optional
        for value in self.weights:
            write_tag(42, float(value))

        # build fit points
        # fit points have to be present and valid, otherwise AutoCAD crashes
        # edit 2016-12-20: this is not true - there are examples with no fit points and without crashing AutoCAD
        write_tag(97, len(self.fit_points))
        for x, y, *_ in self.fit_points:
            tagwriter.write_tag2(11, float(x))
            tagwriter.write_tag2(21, float(y))

        if self.start_tangent is not None:
            x, y, *_ = self.start_tangent
            tagwriter.write_tag2(12, float(x))
            tagwriter.write_tag2(22, float(y))

        if self.end_tangent is not None:
            x, y, *_ = self.end_tangent
            tagwriter.write_tag2(13, float(x))
            tagwriter.write_tag2(23, float(y))
Exemple #29
0
    def add_underlay_def(
        self, filename: str, fmt: str = "pdf", name: str = None
    ) -> "UnderlayDefinition":
        """Add an :class:`~ezdxf.entities.underlay.UnderlayDefinition` entity
        to the drawing (OBJECTS section). `filename` is the underlay file name
        as relative or absolute path and `fmt` as string (pdf, dwf, dgn).
        The underlay definition is required to create an underlay reference.

        Args:
            filename: underlay file name
            fmt: file format as string ``'pdf'|'dwf'|'dgn'``
            name: pdf format = page number to display; dgn format = ``'default'``; dwf: ????

        """
        fmt = fmt.upper()
        if fmt in ("PDF", "DWF", "DGN"):
            underlay_dict_name = f"ACAD_{fmt}DEFINITIONS"
            underlay_def_entity = f"{fmt}DEFINITION"
        else:
            raise const.DXFValueError(f"Unsupported file format: '{fmt}'")

        if name is None:
            if fmt == "PDF":
                name = "1"  # Display first page by default
            elif fmt == "DGN":
                name = "default"
            else:
                name = "Model"  # Display model space for DWF ???

        underlay_dict = self.rootdict.get_required_dict(underlay_dict_name)
        underlay_def = self.new_entity(
            underlay_def_entity,
            dxfattribs={
                "owner": underlay_dict.dxf.handle,
                "filename": filename,
                "name": name,
            },
        )

        # auto-generated underlay key
        key = self.next_underlay_key(lambda k: k not in underlay_dict)
        underlay_dict[key] = underlay_def.dxf.handle
        return cast("UnderlayDefinition", underlay_def)
Exemple #30
0
    def new_layout(self, name, dxfattribs=None) -> 'Layout':
        """
        Create a new paperspace layout `name`. Returns a
        :class:`~ezdxf.layouts.Layout` object.
        DXF R12 (AC1009) supports only one paperspace layout, only the active
        paperspace layout is saved, other layouts are dismissed.

        Args:
            name: unique layout name
            dxfattribs: additional DXF attributes for the
                :class:`~ezdxf.entities.layout.DXFLayout` entity

        Raises:
            DXFValueError: :class:`~ezdxf.layouts.Layout` `name` already exist

        """
        if name in self.layouts:
            raise const.DXFValueError(f"Layout '{name}' already exists.")
        else:
            return self.layouts.new(name, dxfattribs)