Skip to content

fs_coordinate_format

fs_coordinate_format

Coordinate format token.

RECOMMENDED_MINIMAL_DECIMAL_PLACES module-attribute

RECOMMENDED_MINIMAL_DECIMAL_PLACES = 5

FrozenGeneralModel

Bases: BaseModel

Model with common set of general purpose features.

Source code in src/pygerber/common/frozen_general_model.py
class FrozenGeneralModel(BaseModel):
    """Model with common set of general purpose features."""

    model_config = ConfigDict(
        extra="forbid",
        frozen=True,
        arbitrary_types_allowed=True,
    )

IncrementalCoordinatesNotSupportedError

Bases: ParserError

Raised when incremental coordinates are selected. (Spec. 8.2.1.2).

Source code in src/pygerber/gerberx3/parser/errors.py
class IncrementalCoordinatesNotSupportedError(ParserError):
    """Raised when incremental coordinates are selected. (Spec. 8.2.1.2)."""

InvalidCoordinateLengthError

Bases: ParserError

Raised when coordinate string is too long.

Source code in src/pygerber/gerberx3/parser/errors.py
class InvalidCoordinateLengthError(ParserError):
    """Raised when coordinate string is too long."""

UnsupportedCoordinateTypeError

Bases: ParserError

Raised for unsupported coordinate types.

Source code in src/pygerber/gerberx3/parser/errors.py
class UnsupportedCoordinateTypeError(ParserError):
    """Raised for unsupported coordinate types."""

ZeroOmissionNotSupportedError

Bases: ParserError

Raised when incremental coordinates are selected. (Spec. 8.2.1.2).

Source code in src/pygerber/gerberx3/parser/errors.py
class ZeroOmissionNotSupportedError(ParserError):
    """Raised when incremental coordinates are selected. (Spec. 8.2.1.2)."""

GerberCodeEnum

Bases: GerberCode, Enum

Enum with GerberCode interface implementation.

Source code in src/pygerber/gerberx3/tokenizer/helpers/gerber_code_enum.py
class GerberCodeEnum(GerberCode, Enum):
    """Enum with GerberCode interface implementation."""

    def get_gerber_code(
        self,
        indent: str = "",
        endline: str = "\n",  # noqa: ARG002
    ) -> str:
        """Get gerber code represented by this token."""
        return f"{indent}{self.value}"

get_gerber_code

get_gerber_code(
    indent: str = "", endline: str = "\n"
) -> str

Get gerber code represented by this token.

Source code in src/pygerber/gerberx3/tokenizer/helpers/gerber_code_enum.py
def get_gerber_code(
    self,
    indent: str = "",
    endline: str = "\n",  # noqa: ARG002
) -> str:
    """Get gerber code represented by this token."""
    return f"{indent}{self.value}"

ExtendedCommandToken

Bases: Token

3.3 Commands.

Commands are the core syntactic element of the Gerber format. A Gerber file is a stream of commands. Commands define the graphics state, create graphical objects, defines apertures, manage attributes and so on.

Commands are built with words, the basic syntactic building block of a Gerber file. A word is a non-empty character string, excluding the reserved characters '' and '%', terminated with an ''

free_character = /[^%*]/; # All characters but * and %
word = {free_character}+ '*';

For historic reasons, there are two command syntax styles: word commands and extended commands.

command =
| extended_command
| word_command
;
word_command = word;
extended_command = '%' {word}+ '%';

Word commands are identified by a command code, the letter G, D or M followed by a positive integer, e.g. G02. Most word commands only consist of the command code, some also contain coordinates.

Extended commands are identified by a two-character command code that is followed by parameters specific to the code. An extended command is enclosed by a pair of "%" delimiters

An overview of all commands is in section 2.8, a full description in chapters 3.5 and 5.


Example 1

The example below shows a stream of Gerber commands.

G04 Different command styles*
G75*
G02*
D10*
X0Y0D02*
X2000000Y0I1000000J0D01*
D11*
X0Y2000000D03*
M02*

Example 2

The example below shows a stream of Gerber extended commands.

%FSLAX26Y26*%
%MOMM*%
%AMDonut*
1,1,$1,$2,$3*
$4=$1x0.75*
1,0,$4,$2,$3*
%
%ADD11Donut,0.30X0X0*%
%ADD10C,0.1*%

See section 3.3 of The Gerber Layer Format Specification

Source code in src/pygerber/gerberx3/tokenizer/tokens/bases/extended_command.py
class ExtendedCommandToken(Token):
    """## 3.3 Commands.

    Commands are the core syntactic element of the Gerber format. A Gerber file is a stream of
    commands. Commands define the graphics state, create graphical objects, defines apertures,
    manage attributes and so on.

    Commands are built with words, the basic syntactic building block of a Gerber file. A word is a
    non-empty character string, excluding the reserved characters '*' and '%', terminated with an '*'

    ```ebnf
    free_character = /[^%*]/; # All characters but * and %
    word = {free_character}+ '*';
    ```

    For historic reasons, there are two command syntax styles: word commands and extended
    commands.

    ```ebnf
    command =
    | extended_command
    | word_command
    ;
    word_command = word;
    extended_command = '%' {word}+ '%';
    ```


    Word commands are identified by a command code, the letter G, D or M followed by a positive
    integer, e.g. `G02`. Most word commands only consist of the command code, some also contain
    coordinates.

    Extended commands are identified by a two-character command code that is followed by
    parameters specific to the code. An extended command is enclosed by a pair of "%" delimiters

    An overview of all commands is in section 2.8, a full description in chapters 3.5 and 5.


    ---

    ## Example 1

    The example below shows a stream of Gerber commands.

    ```gerber
    G04 Different command styles*
    G75*
    G02*
    D10*
    X0Y0D02*
    X2000000Y0I1000000J0D01*
    D11*
    X0Y2000000D03*
    M02*
    ```


    ---

    ## Example 2

    The example below shows a stream of Gerber extended commands.

    ```gerber
    %FSLAX26Y26*%
    %MOMM*%
    %AMDonut*
    1,1,$1,$2,$3*
    $4=$1x0.75*
    1,0,$4,$2,$3*
    %
    %ADD11Donut,0.30X0X0*%
    %ADD10C,0.1*%
    ```

    ---

    See section 3.3 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=33)

    """  # noqa: E501

    def get_gerber_code_one_line_pretty_display(self) -> str:
        """Get gerber code represented by this token."""
        return f"%{self.get_gerber_code()}*%"

get_gerber_code_one_line_pretty_display

get_gerber_code_one_line_pretty_display() -> str

Get gerber code represented by this token.

Source code in src/pygerber/gerberx3/tokenizer/tokens/bases/extended_command.py
def get_gerber_code_one_line_pretty_display(self) -> str:
    """Get gerber code represented by this token."""
    return f"%{self.get_gerber_code()}*%"

GerberCode

Interface of object which can be converted to gerber code.

Source code in src/pygerber/gerberx3/tokenizer/tokens/bases/gerber_code.py
class GerberCode:
    """Interface of object which can be converted to gerber code."""

    def get_gerber_code(
        self,
        indent: str = "",
        endline: str = "\n",  # noqa: ARG002
    ) -> str:
        """Get gerber code represented by this token."""
        return f"{indent}G04 {self.__class__.__qualname__} no formatting available*"

    def get_gerber_code_one_line_pretty_display(self) -> str:
        """Get gerber code represented by this token."""
        return f"G04 {self.__class__.__qualname__} no formatting available*"

get_gerber_code

get_gerber_code(
    indent: str = "", endline: str = "\n"
) -> str

Get gerber code represented by this token.

Source code in src/pygerber/gerberx3/tokenizer/tokens/bases/gerber_code.py
def get_gerber_code(
    self,
    indent: str = "",
    endline: str = "\n",  # noqa: ARG002
) -> str:
    """Get gerber code represented by this token."""
    return f"{indent}G04 {self.__class__.__qualname__} no formatting available*"

get_gerber_code_one_line_pretty_display

get_gerber_code_one_line_pretty_display() -> str

Get gerber code represented by this token.

Source code in src/pygerber/gerberx3/tokenizer/tokens/bases/gerber_code.py
def get_gerber_code_one_line_pretty_display(self) -> str:
    """Get gerber code represented by this token."""
    return f"G04 {self.__class__.__qualname__} no formatting available*"

Coordinate

Bases: GerberCode, FrozenGeneralModel

Coordinate data.

A number whose interpretation is determined by the FS command. It is used to specify the X and Y coordinates of a point in the image plane and a distance or offset in the X and Y direction.

Source code in src/pygerber/gerberx3/tokenizer/tokens/coordinate.py
class Coordinate(GerberCode, FrozenGeneralModel):
    """## Coordinate data.

    A number whose interpretation is determined by the FS command. It is used to specify
    the X and Y coordinates of a point in the image plane and a distance
    or offset in the X and Y direction.
    """

    coordinate_type: CoordinateType
    sign: CoordinateSign
    offset: str

    @classmethod
    def new(cls, coordinate_type: CoordinateType, offset: Optional[str]) -> Self:
        """Create new Coordinate object."""
        if offset is None:
            coordinate_type = coordinate_type.to_missing()
            offset = ""
            sign = CoordinateSign.Positive

        elif len(offset) > 0 and offset[0] in "+-":
            sign = CoordinateSign(offset[0])
            offset = offset[1:].ljust(1, "0")

        else:
            sign = CoordinateSign.Positive

        return cls(coordinate_type=coordinate_type, sign=sign, offset=offset)

    def get_gerber_code(
        self,
        indent: str = "",
        endline: str = "\n",  # noqa: ARG002
    ) -> str:
        """Get gerber code represented by this token."""
        return (
            ""
            if self.coordinate_type.is_missing()
            else f"{indent}{self.coordinate_type}{self.sign}{self.offset}"
        )

new classmethod

new(
    coordinate_type: CoordinateType, offset: Optional[str]
) -> Self

Create new Coordinate object.

Source code in src/pygerber/gerberx3/tokenizer/tokens/coordinate.py
@classmethod
def new(cls, coordinate_type: CoordinateType, offset: Optional[str]) -> Self:
    """Create new Coordinate object."""
    if offset is None:
        coordinate_type = coordinate_type.to_missing()
        offset = ""
        sign = CoordinateSign.Positive

    elif len(offset) > 0 and offset[0] in "+-":
        sign = CoordinateSign(offset[0])
        offset = offset[1:].ljust(1, "0")

    else:
        sign = CoordinateSign.Positive

    return cls(coordinate_type=coordinate_type, sign=sign, offset=offset)

get_gerber_code

get_gerber_code(
    indent: str = "", endline: str = "\n"
) -> str

Get gerber code represented by this token.

Source code in src/pygerber/gerberx3/tokenizer/tokens/coordinate.py
def get_gerber_code(
    self,
    indent: str = "",
    endline: str = "\n",  # noqa: ARG002
) -> str:
    """Get gerber code represented by this token."""
    return (
        ""
        if self.coordinate_type.is_missing()
        else f"{indent}{self.coordinate_type}{self.sign}{self.offset}"
    )

CoordinateSign

Bases: Enum

Coordinate sign.

Source code in src/pygerber/gerberx3/tokenizer/tokens/coordinate.py
class CoordinateSign(Enum):
    """Coordinate sign."""

    Positive = "+"
    Negative = "-"

    def __str__(self) -> str:
        return "-" if self == CoordinateSign.Negative else ""

CoordinateType

Bases: Enum

Type of coordinate axis/meaning.

Source code in src/pygerber/gerberx3/tokenizer/tokens/coordinate.py
class CoordinateType(Enum):
    """Type of coordinate axis/meaning."""

    X = "X"
    Y = "Y"
    I = "I"  # noqa: E741
    J = "J"
    NULL = ""
    MISSING_X = "MISSING_X"
    MISSING_Y = "MISSING_Y"
    MISSING_I = "MISSING_I"
    MISSING_J = "MISSING_J"

    def to_missing(self) -> CoordinateType:
        """Map <coordinate> to MISSING_<coordinate>."""
        return _coordinate_type_to_missing_map[self]

    def is_missing(self) -> bool:
        """Check if coordinate is one of variants of missing coordinates."""
        return self in (
            CoordinateType.MISSING_X,
            CoordinateType.MISSING_Y,
            CoordinateType.MISSING_I,
            CoordinateType.MISSING_J,
        )

    def __str__(self) -> str:
        return self.value

to_missing

to_missing() -> CoordinateType

Map to MISSING_.

Source code in src/pygerber/gerberx3/tokenizer/tokens/coordinate.py
def to_missing(self) -> CoordinateType:
    """Map <coordinate> to MISSING_<coordinate>."""
    return _coordinate_type_to_missing_map[self]

is_missing

is_missing() -> bool

Check if coordinate is one of variants of missing coordinates.

Source code in src/pygerber/gerberx3/tokenizer/tokens/coordinate.py
def is_missing(self) -> bool:
    """Check if coordinate is one of variants of missing coordinates."""
    return self in (
        CoordinateType.MISSING_X,
        CoordinateType.MISSING_Y,
        CoordinateType.MISSING_I,
        CoordinateType.MISSING_J,
    )

Backend

Bases: ABC

Drawing backend interface.

Source code in src/pygerber/backend/abstract/backend_cls.py
class Backend(ABC):
    """Drawing backend interface."""

    handles: list[PrivateApertureHandle]
    drawing_target: DrawingTarget
    bounding_box: BoundingBox
    coordinate_origin: Vector2D

    options_class: ClassVar[type[BackendOptions]] = BackendOptions

    def __init__(self, options: Optional[BackendOptions] = None) -> None:
        """Initialize backend."""
        self.options = self.options_class() if options is None else options
        self.handles = []

    def create_aperture_handle(self, aperture_id: ApertureID) -> PrivateApertureHandle:
        """Create new aperture handle."""
        handle = self.get_aperture_handle_cls()(
            aperture_id=aperture_id,
            private_id=len(self.handles),
            backend=self,
        )
        self.handles.append(handle)
        return handle

    def get_private_aperture_handle(
        self,
        public_aperture_handle: PublicApertureHandle,
    ) -> PrivateApertureHandle:
        """Get private aperture handle."""
        return self.handles[public_aperture_handle.private_id]

    def draw(self, draws: List[DrawCommand]) -> ResultHandle:
        """Execute all draw actions to create visualization."""
        self.draws = draws

        self.finalize_aperture_creation()
        self.bounding_box = self._get_draws_bounding_box(draws)
        self.coordinate_origin = self._get_coordinate_origin()
        self.drawing_target = self._create_drawing_target()
        self._pre_drawing_hook()

        with self.drawing_target:
            for draw_action in draws:
                draw_action.draw(self.drawing_target)

        self._post_drawing_hook()

        return self.get_result_handle()

    def finalize_aperture_creation(self) -> None:
        """Apply draw operations to aperture handles."""
        for handle in self.handles:
            handle.finalize_aperture_creation()

    def _get_draws_bounding_box(self, draws: List[DrawCommand]) -> BoundingBox:
        bbox = BoundingBox.NULL

        for draw in draws:
            bbox += draw.get_bounding_box()

        return bbox

    def _get_coordinate_origin(self) -> Vector2D:
        return self.bounding_box.get_min_vector()

    @abstractmethod
    def _create_drawing_target(self) -> DrawingTarget:
        """Create drawing target object."""

    def _pre_drawing_hook(self) -> None:  # noqa: B027
        """Perform custom actions before drawing."""

    def _post_drawing_hook(self) -> None:  # noqa: B027
        """Perform custom actions after drawing."""

    @abstractmethod
    def get_result_handle(self) -> ResultHandle:
        """Return result handle to visualization."""

    @abstractmethod
    def get_aperture_handle_cls(self) -> Type[PrivateApertureHandle]:
        """Get backend-specific implementation of aperture handle class."""

    @abstractmethod
    def get_draw_circle_cls(self) -> Type[DrawCircle]:
        """Get backend-specific implementation of aperture circle component class."""

    @abstractmethod
    def get_draw_rectangle_cls(self) -> Type[DrawRectangle]:
        """Get backend-specific implementation of aperture rectangle component class."""

    @abstractmethod
    def get_draw_polygon_cls(self) -> Type[DrawPolygon]:
        """Get backend-specific implementation of aperture polygon component class."""

    @abstractmethod
    def get_draw_commands_handle_cls(self) -> type[DrawCommandsHandle]:
        """Return backend-specific implementation of draw actions handle."""

    @abstractmethod
    def get_draw_paste_cls(self) -> type[DrawPaste]:
        """Return backend-specific implementation of draw paste."""

    @abstractmethod
    def get_draw_region_cls(self) -> type[DrawRegion]:
        """Return backend-specific implementation of draw action region."""

    @abstractmethod
    def get_draw_vector_line_cls(self) -> type[DrawVectorLine]:
        """Return backend-specific implementation of draw action line."""

    @abstractmethod
    def get_draw_arc_cls(self) -> type[DrawArc]:
        """Return backend-specific implementation of draw action arc."""

__init__

__init__(options: Optional[BackendOptions] = None) -> None

Initialize backend.

Source code in src/pygerber/backend/abstract/backend_cls.py
def __init__(self, options: Optional[BackendOptions] = None) -> None:
    """Initialize backend."""
    self.options = self.options_class() if options is None else options
    self.handles = []

create_aperture_handle

create_aperture_handle(
    aperture_id: ApertureID,
) -> PrivateApertureHandle

Create new aperture handle.

Source code in src/pygerber/backend/abstract/backend_cls.py
def create_aperture_handle(self, aperture_id: ApertureID) -> PrivateApertureHandle:
    """Create new aperture handle."""
    handle = self.get_aperture_handle_cls()(
        aperture_id=aperture_id,
        private_id=len(self.handles),
        backend=self,
    )
    self.handles.append(handle)
    return handle

get_private_aperture_handle

get_private_aperture_handle(
    public_aperture_handle: PublicApertureHandle,
) -> PrivateApertureHandle

Get private aperture handle.

Source code in src/pygerber/backend/abstract/backend_cls.py
def get_private_aperture_handle(
    self,
    public_aperture_handle: PublicApertureHandle,
) -> PrivateApertureHandle:
    """Get private aperture handle."""
    return self.handles[public_aperture_handle.private_id]

draw

draw(draws: List[DrawCommand]) -> ResultHandle

Execute all draw actions to create visualization.

Source code in src/pygerber/backend/abstract/backend_cls.py
def draw(self, draws: List[DrawCommand]) -> ResultHandle:
    """Execute all draw actions to create visualization."""
    self.draws = draws

    self.finalize_aperture_creation()
    self.bounding_box = self._get_draws_bounding_box(draws)
    self.coordinate_origin = self._get_coordinate_origin()
    self.drawing_target = self._create_drawing_target()
    self._pre_drawing_hook()

    with self.drawing_target:
        for draw_action in draws:
            draw_action.draw(self.drawing_target)

    self._post_drawing_hook()

    return self.get_result_handle()

finalize_aperture_creation

finalize_aperture_creation() -> None

Apply draw operations to aperture handles.

Source code in src/pygerber/backend/abstract/backend_cls.py
def finalize_aperture_creation(self) -> None:
    """Apply draw operations to aperture handles."""
    for handle in self.handles:
        handle.finalize_aperture_creation()

get_result_handle abstractmethod

get_result_handle() -> ResultHandle

Return result handle to visualization.

Source code in src/pygerber/backend/abstract/backend_cls.py
@abstractmethod
def get_result_handle(self) -> ResultHandle:
    """Return result handle to visualization."""

get_aperture_handle_cls abstractmethod

get_aperture_handle_cls() -> Type[PrivateApertureHandle]

Get backend-specific implementation of aperture handle class.

Source code in src/pygerber/backend/abstract/backend_cls.py
@abstractmethod
def get_aperture_handle_cls(self) -> Type[PrivateApertureHandle]:
    """Get backend-specific implementation of aperture handle class."""

get_draw_circle_cls abstractmethod

get_draw_circle_cls() -> Type[DrawCircle]

Get backend-specific implementation of aperture circle component class.

Source code in src/pygerber/backend/abstract/backend_cls.py
@abstractmethod
def get_draw_circle_cls(self) -> Type[DrawCircle]:
    """Get backend-specific implementation of aperture circle component class."""

get_draw_rectangle_cls abstractmethod

get_draw_rectangle_cls() -> Type[DrawRectangle]

Get backend-specific implementation of aperture rectangle component class.

Source code in src/pygerber/backend/abstract/backend_cls.py
@abstractmethod
def get_draw_rectangle_cls(self) -> Type[DrawRectangle]:
    """Get backend-specific implementation of aperture rectangle component class."""

get_draw_polygon_cls abstractmethod

get_draw_polygon_cls() -> Type[DrawPolygon]

Get backend-specific implementation of aperture polygon component class.

Source code in src/pygerber/backend/abstract/backend_cls.py
@abstractmethod
def get_draw_polygon_cls(self) -> Type[DrawPolygon]:
    """Get backend-specific implementation of aperture polygon component class."""

get_draw_commands_handle_cls abstractmethod

get_draw_commands_handle_cls() -> type[DrawCommandsHandle]

Return backend-specific implementation of draw actions handle.

Source code in src/pygerber/backend/abstract/backend_cls.py
@abstractmethod
def get_draw_commands_handle_cls(self) -> type[DrawCommandsHandle]:
    """Return backend-specific implementation of draw actions handle."""

get_draw_paste_cls abstractmethod

get_draw_paste_cls() -> type[DrawPaste]

Return backend-specific implementation of draw paste.

Source code in src/pygerber/backend/abstract/backend_cls.py
@abstractmethod
def get_draw_paste_cls(self) -> type[DrawPaste]:
    """Return backend-specific implementation of draw paste."""

get_draw_region_cls abstractmethod

get_draw_region_cls() -> type[DrawRegion]

Return backend-specific implementation of draw action region.

Source code in src/pygerber/backend/abstract/backend_cls.py
@abstractmethod
def get_draw_region_cls(self) -> type[DrawRegion]:
    """Return backend-specific implementation of draw action region."""

get_draw_vector_line_cls abstractmethod

get_draw_vector_line_cls() -> type[DrawVectorLine]

Return backend-specific implementation of draw action line.

Source code in src/pygerber/backend/abstract/backend_cls.py
@abstractmethod
def get_draw_vector_line_cls(self) -> type[DrawVectorLine]:
    """Return backend-specific implementation of draw action line."""

get_draw_arc_cls abstractmethod

get_draw_arc_cls() -> type[DrawArc]

Return backend-specific implementation of draw action arc.

Source code in src/pygerber/backend/abstract/backend_cls.py
@abstractmethod
def get_draw_arc_cls(self) -> type[DrawArc]:
    """Return backend-specific implementation of draw action arc."""

DrawCommand

Bases: ABC

Description of aperture component.

Source code in src/pygerber/backend/abstract/draw_commands/draw_command.py
class DrawCommand(ABC):
    """Description of aperture component."""

    backend: Backend
    polarity: Polarity

    def __init__(self, backend: Backend, polarity: Polarity) -> None:
        """Initialize draw command."""
        self.backend = backend
        self.polarity = polarity

    @abstractmethod
    def draw(self, target: DrawingTarget) -> None:
        """Apply aperture draw component to handle."""

    @abstractmethod
    def get_bounding_box(self) -> BoundingBox:
        """Return bounding box of draw operation."""

    def __str__(self) -> str:
        return f"{self.__class__.__qualname__}({self.polarity})"

__init__

__init__(backend: Backend, polarity: Polarity) -> None

Initialize draw command.

Source code in src/pygerber/backend/abstract/draw_commands/draw_command.py
def __init__(self, backend: Backend, polarity: Polarity) -> None:
    """Initialize draw command."""
    self.backend = backend
    self.polarity = polarity

draw abstractmethod

draw(target: DrawingTarget) -> None

Apply aperture draw component to handle.

Source code in src/pygerber/backend/abstract/draw_commands/draw_command.py
@abstractmethod
def draw(self, target: DrawingTarget) -> None:
    """Apply aperture draw component to handle."""

get_bounding_box abstractmethod

get_bounding_box() -> BoundingBox

Return bounding box of draw operation.

Source code in src/pygerber/backend/abstract/draw_commands/draw_command.py
@abstractmethod
def get_bounding_box(self) -> BoundingBox:
    """Return bounding box of draw operation."""

State

Bases: FrozenGeneralModel

GerberX3 interpreter state.

Source code in src/pygerber/gerberx3/parser/state.py
class State(FrozenGeneralModel):
    """GerberX3 interpreter state."""

    current_position: Vector2D = Vector2D(x=Offset.NULL, y=Offset.NULL)

    # MO | Mode | Sets the unit to mm or inch                           | 4.2.1
    draw_units: Optional[Unit] = None
    # FS | Format specification | Sets the coordinate format,           | 4.2.2
    #    |                      | e.g. the number of decimals
    coordinate_parser: Optional[CoordinateParser] = None
    # Dnn | (nn≥10) | Sets the current aperture to D code nn.           | 4.6
    current_aperture: Optional[PublicApertureHandle] = None
    # G01 | | Sets linear/circular mode to linear.                      | 4.7.1
    # G02 | | Sets linear/circular mode to clockwise circular           | 4.7.2
    # G03 | | Sets linear/circular mode to counterclockwise circular    | 4.7.3
    draw_mode: DrawMode = DrawMode.Linear
    # LP  | | Load polarity | Loads the polarity object transformation  | 4.9.2
    #                       parameter.
    polarity: Polarity = Polarity.Dark
    # LM  | | Load mirroring | Loads the mirror object transformation   | 4.9.3
    #                         parameter.
    mirroring: Mirroring = Mirroring.NoMirroring
    # LR  | Load rotation |  Loads the rotation object transformation   | 4.9.4
    #                       parameter.
    rotation: Decimal = Decimal("0.0")

    region_boundary_points: List[Vector2D] = Field(default_factory=list)
    """Points defining the shape of the region."""

    # LS  | Load scaling |   Loads the scale object transformation      | 4.9.5
    #                       parameter
    scaling: Decimal = Decimal("1.0")
    # G36 | |   Starts a region statement which creates a region by     | 4.10
    #     | |   defining its contours.
    # G37 | |   Ends the region statement.                              | 4.10
    is_region: bool = False
    # AB  | |   Aperture blockOpens a block aperture statement and      | 4.11
    #     | |   assigns its aperture number or closes a block aperture  |
    #     | |   statement.                                              |
    is_aperture_block: bool = False
    # SR  | |   Step and repeatOpen or closes a step and repeat         | 4.11
    #     | |   statement.                                              |
    is_step_and_repeat: bool = False
    # TF  | |   Attribute on fileSet a file attribute.                  | 5.3
    # TD  | |   Attribute deleteDelete one or all attributes in the     | 5.5
    #     | |   dictionary.                                             |
    file_attributes: Dict[str, str] = Field(default_factory=dict)
    # G75 | |   Sets multi quadrant mode
    # G74 | |   Sets single quadrant mode
    is_multi_quadrant: bool = False

    is_output_image_negation_required: bool = False
    """In Gerber specification deprecated IP command is mentioned.
    It can set image polarity to either positive, the usual one, or to negative.
    Under negative image polarity, image generation is different. Its purpose is to
    create a negative image, clear areas in a dark background. The entire image plane
    in the background is initially dark instead of clear. The effect of dark and clear
    polarity is toggled. The entire image is simply reversed, dark becomes white and
    vice versa.
    This effect can be achieved by simply inverting colors of output image.
    """

    apertures: Dict[ApertureID, PublicApertureHandle] = Field(default_factory=dict)
    """Collection of all apertures defined until given point in code."""

    macros: Dict[str, MacroDefinition] = Field(default_factory=dict)
    """Collection of all macros defined until given point in code."""

    def get_units(self) -> Unit:
        """Get drawing unit or raise UnitNotSetError."""
        if self.draw_units is None:
            raise UnitNotSetError
        return self.draw_units

    def get_coordinate_parser(self) -> CoordinateParser:
        """Get coordinate parser or raise CoordinateFormatNotSetError."""
        if self.coordinate_parser is None:
            raise CoordinateFormatNotSetError
        return self.coordinate_parser

    def get_current_aperture(self) -> PublicApertureHandle:
        """Get current aperture or raise ApertureNotSelectedError."""
        if self.current_aperture is None:
            raise ApertureNotSelectedError
        return self.current_aperture

    def parse_coordinate(self, coordinate: Coordinate) -> Offset:
        """Parse, include substitution with current and conversion to Offset."""
        if coordinate.coordinate_type == CoordinateType.MISSING_X:
            return self.current_position.x

        if coordinate.coordinate_type == CoordinateType.MISSING_Y:
            return self.current_position.y

        if coordinate.coordinate_type == CoordinateType.MISSING_I:
            return Offset.NULL

        if coordinate.coordinate_type == CoordinateType.MISSING_J:
            return Offset.NULL

        return Offset.new(
            self.get_coordinate_parser().parse(coordinate),
            unit=self.get_units(),
        )

region_boundary_points class-attribute instance-attribute

region_boundary_points: List[Vector2D] = Field(
    default_factory=list
)

Points defining the shape of the region.

is_output_image_negation_required class-attribute instance-attribute

is_output_image_negation_required: bool = False

In Gerber specification deprecated IP command is mentioned. It can set image polarity to either positive, the usual one, or to negative. Under negative image polarity, image generation is different. Its purpose is to create a negative image, clear areas in a dark background. The entire image plane in the background is initially dark instead of clear. The effect of dark and clear polarity is toggled. The entire image is simply reversed, dark becomes white and vice versa. This effect can be achieved by simply inverting colors of output image.

apertures class-attribute instance-attribute

apertures: Dict[ApertureID, PublicApertureHandle] = Field(
    default_factory=dict
)

Collection of all apertures defined until given point in code.

macros class-attribute instance-attribute

macros: Dict[str, MacroDefinition] = Field(
    default_factory=dict
)

Collection of all macros defined until given point in code.

get_units

get_units() -> Unit

Get drawing unit or raise UnitNotSetError.

Source code in src/pygerber/gerberx3/parser/state.py
def get_units(self) -> Unit:
    """Get drawing unit or raise UnitNotSetError."""
    if self.draw_units is None:
        raise UnitNotSetError
    return self.draw_units

get_coordinate_parser

get_coordinate_parser() -> CoordinateParser

Get coordinate parser or raise CoordinateFormatNotSetError.

Source code in src/pygerber/gerberx3/parser/state.py
def get_coordinate_parser(self) -> CoordinateParser:
    """Get coordinate parser or raise CoordinateFormatNotSetError."""
    if self.coordinate_parser is None:
        raise CoordinateFormatNotSetError
    return self.coordinate_parser

get_current_aperture

get_current_aperture() -> PublicApertureHandle

Get current aperture or raise ApertureNotSelectedError.

Source code in src/pygerber/gerberx3/parser/state.py
def get_current_aperture(self) -> PublicApertureHandle:
    """Get current aperture or raise ApertureNotSelectedError."""
    if self.current_aperture is None:
        raise ApertureNotSelectedError
    return self.current_aperture

parse_coordinate

parse_coordinate(coordinate: Coordinate) -> Offset

Parse, include substitution with current and conversion to Offset.

Source code in src/pygerber/gerberx3/parser/state.py
def parse_coordinate(self, coordinate: Coordinate) -> Offset:
    """Parse, include substitution with current and conversion to Offset."""
    if coordinate.coordinate_type == CoordinateType.MISSING_X:
        return self.current_position.x

    if coordinate.coordinate_type == CoordinateType.MISSING_Y:
        return self.current_position.y

    if coordinate.coordinate_type == CoordinateType.MISSING_I:
        return Offset.NULL

    if coordinate.coordinate_type == CoordinateType.MISSING_J:
        return Offset.NULL

    return Offset.new(
        self.get_coordinate_parser().parse(coordinate),
        unit=self.get_units(),
    )

CoordinateFormat

Bases: ExtendedCommandToken

Description of coordinate format token.

See: - section 4.1.1 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - section 4.2.2 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html

Source code in src/pygerber/gerberx3/tokenizer/tokens/fs_coordinate_format.py
class CoordinateFormat(ExtendedCommandToken):
    """Description of coordinate format token.

    See:
    -   section 4.1.1 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html
    -   section 4.2.2 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html
    """

    def __init__(
        self,
        string: str,
        location: int,
        zeros_mode: TrailingZerosMode,
        coordinate_mode: CoordinateMode,
        x_format: AxisFormat,
        y_format: AxisFormat,
    ) -> None:
        super().__init__(string, location)
        self.zeros_mode = zeros_mode
        self.coordinate_mode = coordinate_mode
        self.x_format = x_format
        self.y_format = y_format

    @classmethod
    def new(cls, string: str, location: int, tokens: ParseResults) -> Self:
        """Create instance of this class.

        Created to be used as callback in `ParserElement.set_parse_action()`.
        """
        zeros_mode = TrailingZerosMode(tokens["zeros_mode"])
        coordinate_mode = CoordinateMode(tokens["coordinate_mode"])
        x_format = AxisFormat(
            integer=int(tokens["x_format"][0]),  # type: ignore[pylance]
            decimal=int(tokens["x_format"][1]),  # type: ignore[pylance]
        )
        y_format = AxisFormat(
            integer=int(tokens["y_format"][0]),  # type: ignore[pylance]
            decimal=int(tokens["y_format"][1]),  # type: ignore[pylance]
        )
        return cls(
            string=string,
            location=location,
            zeros_mode=zeros_mode,
            coordinate_mode=coordinate_mode,
            x_format=x_format,
            y_format=y_format,
        )

    def update_drawing_state(
        self,
        state: State,
        _backend: Backend,
    ) -> Tuple[State, Iterable[DrawCommand]]:
        """Set coordinate parser."""
        if state.coordinate_parser is not None:
            logging.warning(
                "Overriding coordinate format is illegal."
                "(See 4.2.2 in Gerber Layer Format Specification)",
            )
        return (
            state.model_copy(
                update={
                    "coordinate_parser": CoordinateParser.new(
                        x_format=self.x_format,
                        y_format=self.y_format,
                    ),
                },
            ),
            (),
        )

    def get_gerber_code(
        self,
        indent: str = "",
        endline: str = "\n",
    ) -> str:
        """Get gerber code represented by this token."""
        return (
            f"FS"
            f"{self.zeros_mode.get_gerber_code(indent, endline)}"
            f"{self.coordinate_mode.get_gerber_code(indent, endline)}"
            f"X{self.x_format.get_gerber_code(indent, endline)}"
            f"Y{self.y_format.get_gerber_code(indent, endline)}"
        )

new classmethod

new(
    string: str, location: int, tokens: ParseResults
) -> Self

Create instance of this class.

Created to be used as callback in ParserElement.set_parse_action().

Source code in src/pygerber/gerberx3/tokenizer/tokens/fs_coordinate_format.py
@classmethod
def new(cls, string: str, location: int, tokens: ParseResults) -> Self:
    """Create instance of this class.

    Created to be used as callback in `ParserElement.set_parse_action()`.
    """
    zeros_mode = TrailingZerosMode(tokens["zeros_mode"])
    coordinate_mode = CoordinateMode(tokens["coordinate_mode"])
    x_format = AxisFormat(
        integer=int(tokens["x_format"][0]),  # type: ignore[pylance]
        decimal=int(tokens["x_format"][1]),  # type: ignore[pylance]
    )
    y_format = AxisFormat(
        integer=int(tokens["y_format"][0]),  # type: ignore[pylance]
        decimal=int(tokens["y_format"][1]),  # type: ignore[pylance]
    )
    return cls(
        string=string,
        location=location,
        zeros_mode=zeros_mode,
        coordinate_mode=coordinate_mode,
        x_format=x_format,
        y_format=y_format,
    )

update_drawing_state

update_drawing_state(
    state: State, _backend: Backend
) -> Tuple[State, Iterable[DrawCommand]]

Set coordinate parser.

Source code in src/pygerber/gerberx3/tokenizer/tokens/fs_coordinate_format.py
def update_drawing_state(
    self,
    state: State,
    _backend: Backend,
) -> Tuple[State, Iterable[DrawCommand]]:
    """Set coordinate parser."""
    if state.coordinate_parser is not None:
        logging.warning(
            "Overriding coordinate format is illegal."
            "(See 4.2.2 in Gerber Layer Format Specification)",
        )
    return (
        state.model_copy(
            update={
                "coordinate_parser": CoordinateParser.new(
                    x_format=self.x_format,
                    y_format=self.y_format,
                ),
            },
        ),
        (),
    )

get_gerber_code

get_gerber_code(
    indent: str = "", endline: str = "\n"
) -> str

Get gerber code represented by this token.

Source code in src/pygerber/gerberx3/tokenizer/tokens/fs_coordinate_format.py
def get_gerber_code(
    self,
    indent: str = "",
    endline: str = "\n",
) -> str:
    """Get gerber code represented by this token."""
    return (
        f"FS"
        f"{self.zeros_mode.get_gerber_code(indent, endline)}"
        f"{self.coordinate_mode.get_gerber_code(indent, endline)}"
        f"X{self.x_format.get_gerber_code(indent, endline)}"
        f"Y{self.y_format.get_gerber_code(indent, endline)}"
    )

TrailingZerosMode

Bases: GerberCodeEnum

Coordinate format mode.

GerberX3 supports only one, L, the other is required for backwards compatibility.

Source code in src/pygerber/gerberx3/tokenizer/tokens/fs_coordinate_format.py
class TrailingZerosMode(GerberCodeEnum):
    """Coordinate format mode.

    GerberX3 supports only one, L, the other is required for backwards compatibility.
    """

    OmitLeading = "L"
    OmitTrailing = "T"

CoordinateMode

Bases: GerberCodeEnum

Coordinate format mode.

GerberX3 supports only one, A, the other required for backwards compatibility.

Source code in src/pygerber/gerberx3/tokenizer/tokens/fs_coordinate_format.py
class CoordinateMode(GerberCodeEnum):
    """Coordinate format mode.

    GerberX3 supports only one, A, the other required for backwards compatibility.
    """

    Absolute = "A"
    Incremental = "I"

AxisFormat

Bases: FrozenGeneralModel, GerberCode

Wrapper for single axis format.

Source code in src/pygerber/gerberx3/tokenizer/tokens/fs_coordinate_format.py
class AxisFormat(FrozenGeneralModel, GerberCode):
    """Wrapper for single axis format."""

    integer: int
    decimal: int

    @property
    def total_length(self) -> int:
        """Total format length."""
        return self.integer + self.decimal

    def __str__(self) -> str:
        return f"{self.integer}{self.decimal}"

    def get_gerber_code(
        self,
        indent: str = "",
        endline: str = "\n",  # noqa: ARG002
    ) -> str:
        """Get gerber code represented by this token."""
        return f"{indent}{self.integer}{self.decimal}"

total_length property

total_length: int

Total format length.

get_gerber_code

get_gerber_code(
    indent: str = "", endline: str = "\n"
) -> str

Get gerber code represented by this token.

Source code in src/pygerber/gerberx3/tokenizer/tokens/fs_coordinate_format.py
def get_gerber_code(
    self,
    indent: str = "",
    endline: str = "\n",  # noqa: ARG002
) -> str:
    """Get gerber code represented by this token."""
    return f"{indent}{self.integer}{self.decimal}"

CoordinateParser

Bases: FrozenGeneralModel

Coordinate Parser class.

Source code in src/pygerber/gerberx3/tokenizer/tokens/fs_coordinate_format.py
class CoordinateParser(FrozenGeneralModel):
    """Coordinate Parser class."""

    x_format: AxisFormat
    y_format: AxisFormat

    @classmethod
    def new(
        cls,
        x_format: AxisFormat,
        y_format: AxisFormat,
        coordinate_mode: CoordinateMode = CoordinateMode.Absolute,
        zeros_mode: TrailingZerosMode = TrailingZerosMode.OmitLeading,
    ) -> Self:
        """Update coordinate parser format configuration."""
        if coordinate_mode != CoordinateMode.Absolute:
            raise IncrementalCoordinatesNotSupportedError

        if zeros_mode != TrailingZerosMode.OmitLeading:
            raise ZeroOmissionNotSupportedError

        for axis, axis_format in (("X", x_format), ("Y", y_format)):
            if axis_format.decimal < RECOMMENDED_MINIMAL_DECIMAL_PLACES:
                logging.warning(
                    "It is recommended to use at least 5 decimal places for coordinate "
                    "data when using metric units and 6 decimal places for imperial "
                    "units. (Detected for %s)"
                    "(See 4.2.2 in Gerber Layer Format Specification)",
                    axis,
                )

        return cls(x_format=x_format, y_format=y_format)

    def parse(self, coordinate: Coordinate) -> Decimal:
        """Parse raw coordinate data."""
        if coordinate.coordinate_type in (CoordinateType.X, CoordinateType.I):
            return self._parse(self.x_format, coordinate.offset, coordinate.sign)

        if coordinate.coordinate_type in (CoordinateType.Y, CoordinateType.J):
            return self._parse(self.y_format, coordinate.offset, coordinate.sign)

        raise UnsupportedCoordinateTypeError(coordinate.coordinate_type)

    def _parse(
        self,
        axis_format: AxisFormat,
        offset: str,
        sign: CoordinateSign,
    ) -> Decimal:
        total_length = axis_format.total_length

        if len(offset) > total_length:
            msg = f"Got {offset!r} with length {len(offset)} expected {total_length}."
            raise InvalidCoordinateLengthError(msg)

        offset = offset.rjust(axis_format.total_length, "0")
        integer, decimal = offset[: axis_format.integer], offset[axis_format.integer :]

        return Decimal(f"{sign.value}{integer}.{decimal}")

new classmethod

new(
    x_format: AxisFormat,
    y_format: AxisFormat,
    coordinate_mode: CoordinateMode = CoordinateMode.Absolute,
    zeros_mode: TrailingZerosMode = TrailingZerosMode.OmitLeading,
) -> Self

Update coordinate parser format configuration.

Source code in src/pygerber/gerberx3/tokenizer/tokens/fs_coordinate_format.py
@classmethod
def new(
    cls,
    x_format: AxisFormat,
    y_format: AxisFormat,
    coordinate_mode: CoordinateMode = CoordinateMode.Absolute,
    zeros_mode: TrailingZerosMode = TrailingZerosMode.OmitLeading,
) -> Self:
    """Update coordinate parser format configuration."""
    if coordinate_mode != CoordinateMode.Absolute:
        raise IncrementalCoordinatesNotSupportedError

    if zeros_mode != TrailingZerosMode.OmitLeading:
        raise ZeroOmissionNotSupportedError

    for axis, axis_format in (("X", x_format), ("Y", y_format)):
        if axis_format.decimal < RECOMMENDED_MINIMAL_DECIMAL_PLACES:
            logging.warning(
                "It is recommended to use at least 5 decimal places for coordinate "
                "data when using metric units and 6 decimal places for imperial "
                "units. (Detected for %s)"
                "(See 4.2.2 in Gerber Layer Format Specification)",
                axis,
            )

    return cls(x_format=x_format, y_format=y_format)

parse

parse(coordinate: Coordinate) -> Decimal

Parse raw coordinate data.

Source code in src/pygerber/gerberx3/tokenizer/tokens/fs_coordinate_format.py
def parse(self, coordinate: Coordinate) -> Decimal:
    """Parse raw coordinate data."""
    if coordinate.coordinate_type in (CoordinateType.X, CoordinateType.I):
        return self._parse(self.x_format, coordinate.offset, coordinate.sign)

    if coordinate.coordinate_type in (CoordinateType.Y, CoordinateType.J):
        return self._parse(self.y_format, coordinate.offset, coordinate.sign)

    raise UnsupportedCoordinateTypeError(coordinate.coordinate_type)