Skip to content

Core

momapy.rendering.core

Bases classes and functions for rendering maps or layout elements

Classes:

Name Description
Renderer

Base class for renderers

StatefulRenderer

Base class for stateful renderers

Functions:

Name Description
register_renderer

Register a renderer class.

render_layout_element

Render a layout element to a file in the given format with the given registered renderer

render_layout_elements

Render a collection of layout elements to a file in the given format with the given registered renderer.

render_map

Render a map to a file in the given format with the given registered renderer.

render_maps

Render a collection of maps to a file in the given format with the given registered renderer.

Renderer dataclass

Renderer()

Bases: ABC

Base class for renderers

Methods:

Name Description
begin_session

Begin a session

end_session

End a session

get_bolder_font_weight

Return the lightest font weight bolder than the given font weight

get_lighter_font_weight

Return the boldest font weight lighter than the given font weight

new_page

Make a new page

render_drawing_element

Render a drawing element

render_layout_element

Render a layout element

render_map

Render a map

begin_session abstractmethod

begin_session()

Begin a session

Source code in src/momapy/rendering/core.py
@abc.abstractmethod
def begin_session(self):
    """Begin a session"""
    pass

end_session abstractmethod

end_session()

End a session

Source code in src/momapy/rendering/core.py
@abc.abstractmethod
def end_session(self):
    """End a session"""
    pass

get_bolder_font_weight classmethod

get_bolder_font_weight(font_weight: FontWeight | float) -> float

Return the lightest font weight bolder than the given font weight

Source code in src/momapy/rendering/core.py
@classmethod
def get_bolder_font_weight(
    cls, font_weight: momapy.drawing.FontWeight | float
) -> float:
    """Return the lightest font weight bolder than the given font weight"""
    if isinstance(font_weight, momapy.drawing.FontWeight):
        font_weight = cls.font_weight_value_mapping.get(font_weight)
        if font_weight is None:
            raise ValueError(
                f"font weight must be a float, {momapy.drawing.FontWeight.NORMAL}, or {momapy.drawing.FontWeight.BOLD}"
            )
    if font_weight < 400:
        new_font_weight = 400
    elif font_weight < 600:
        new_font_weight = 700
    else:
        new_font_weight = 900
    return new_font_weight

get_lighter_font_weight classmethod

get_lighter_font_weight(font_weight: FontWeight | float) -> float

Return the boldest font weight lighter than the given font weight

Source code in src/momapy/rendering/core.py
@classmethod
def get_lighter_font_weight(
    cls, font_weight: momapy.drawing.FontWeight | float
) -> float:
    """Return the boldest font weight lighter than the given font weight"""
    if isinstance(font_weight, momapy.drawing.FontWeight):
        font_weight = cls.font_weight_value_mapping.get(font_weight)
        if font_weight is None:
            raise ValueError(
                f"font weight must be a float, {momapy.drawing.FontWeight.NORMAL}, or {momapy.drawing.FontWeight.BOLD}"
            )
    if font_weight > 700:
        new_font_weight = 700
    elif font_weight > 500:
        new_font_weight = 400
    else:
        new_font_weight = 100
    return new_font_weight

new_page abstractmethod

new_page(width, height)

Make a new page

Source code in src/momapy/rendering/core.py
@abc.abstractmethod
def new_page(self, width, height):
    """Make a new page"""
    pass

render_drawing_element abstractmethod

render_drawing_element(drawing_element: DrawingElement)

Render a drawing element

Source code in src/momapy/rendering/core.py
@abc.abstractmethod
def render_drawing_element(self, drawing_element: momapy.drawing.DrawingElement):
    """Render a drawing element"""
    pass

render_layout_element abstractmethod

render_layout_element(layout_element: LayoutElement)

Render a layout element

Source code in src/momapy/rendering/core.py
@abc.abstractmethod
def render_layout_element(self, layout_element: momapy.core.LayoutElement):
    """Render a layout element"""
    pass

render_map abstractmethod

render_map(map_: Map)

Render a map

Source code in src/momapy/rendering/core.py
@abc.abstractmethod
def render_map(self, map_: momapy.core.Map):
    """Render a map"""
    pass

StatefulRenderer dataclass

StatefulRenderer(_current_state: dict = dict(), _states: list[dict] = list())

Bases: Renderer

Base class for stateful renderers

Methods:

Name Description
begin_session

Begin a session

end_session

End a session

get_bolder_font_weight

Return the lightest font weight bolder than the given font weight

get_current_state

Return the current state

get_current_value

Return the current value for an attribute

get_initial_value

Return the initial value for an attribute

get_lighter_font_weight

Return the boldest font weight lighter than the given font weight

new_page

Make a new page

render_drawing_element

Render a drawing element

render_layout_element

Render a layout element

render_map

Render a map

restore

Set the current state to the last saved state

save

Save the current state

self_restore

Restore the internal state of the renderer.

self_save

Save the internal state of the renderer.

set_current_state

Set the current state to the given state

set_current_state_from_drawing_element

Set the current state to a state given by a drawing element

set_current_value

Set the current value for an attribute

begin_session abstractmethod

begin_session()

Begin a session

Source code in src/momapy/rendering/core.py
@abc.abstractmethod
def begin_session(self):
    """Begin a session"""
    pass

end_session abstractmethod

end_session()

End a session

Source code in src/momapy/rendering/core.py
@abc.abstractmethod
def end_session(self):
    """End a session"""
    pass

get_bolder_font_weight classmethod

get_bolder_font_weight(font_weight: FontWeight | float) -> float

Return the lightest font weight bolder than the given font weight

Source code in src/momapy/rendering/core.py
@classmethod
def get_bolder_font_weight(
    cls, font_weight: momapy.drawing.FontWeight | float
) -> float:
    """Return the lightest font weight bolder than the given font weight"""
    if isinstance(font_weight, momapy.drawing.FontWeight):
        font_weight = cls.font_weight_value_mapping.get(font_weight)
        if font_weight is None:
            raise ValueError(
                f"font weight must be a float, {momapy.drawing.FontWeight.NORMAL}, or {momapy.drawing.FontWeight.BOLD}"
            )
    if font_weight < 400:
        new_font_weight = 400
    elif font_weight < 600:
        new_font_weight = 700
    else:
        new_font_weight = 900
    return new_font_weight

get_current_state

get_current_state() -> dict[str, Any]

Return the current state

Source code in src/momapy/rendering/core.py
def get_current_state(self) -> dict[str, typing.Any]:
    """Return the current state"""
    return self._current_state

get_current_value

get_current_value(attr_name: str) -> Any

Return the current value for an attribute

Source code in src/momapy/rendering/core.py
def get_current_value(self, attr_name: str) -> typing.Any:
    """Return the current value for an attribute"""
    return self.get_current_state()[attr_name]

get_initial_value

get_initial_value(attr_name: str) -> Any

Return the initial value for an attribute

Source code in src/momapy/rendering/core.py
def get_initial_value(self, attr_name: str) -> typing.Any:
    """Return the initial value for an attribute"""
    attr_value = self.initial_values.get(attr_name)
    if attr_value is None:
        attr_d = momapy.drawing.PRESENTATION_ATTRIBUTES[attr_name]
        attr_value = attr_d["initial"]
        if attr_value is None:
            attr_value = momapy.drawing.INITIAL_VALUES[attr_name]
    return attr_value

get_lighter_font_weight classmethod

get_lighter_font_weight(font_weight: FontWeight | float) -> float

Return the boldest font weight lighter than the given font weight

Source code in src/momapy/rendering/core.py
@classmethod
def get_lighter_font_weight(
    cls, font_weight: momapy.drawing.FontWeight | float
) -> float:
    """Return the boldest font weight lighter than the given font weight"""
    if isinstance(font_weight, momapy.drawing.FontWeight):
        font_weight = cls.font_weight_value_mapping.get(font_weight)
        if font_weight is None:
            raise ValueError(
                f"font weight must be a float, {momapy.drawing.FontWeight.NORMAL}, or {momapy.drawing.FontWeight.BOLD}"
            )
    if font_weight > 700:
        new_font_weight = 700
    elif font_weight > 500:
        new_font_weight = 400
    else:
        new_font_weight = 100
    return new_font_weight

new_page abstractmethod

new_page(width, height)

Make a new page

Source code in src/momapy/rendering/core.py
@abc.abstractmethod
def new_page(self, width, height):
    """Make a new page"""
    pass

render_drawing_element abstractmethod

render_drawing_element(drawing_element: DrawingElement)

Render a drawing element

Source code in src/momapy/rendering/core.py
@abc.abstractmethod
def render_drawing_element(self, drawing_element: momapy.drawing.DrawingElement):
    """Render a drawing element"""
    pass

render_layout_element abstractmethod

render_layout_element(layout_element: LayoutElement)

Render a layout element

Source code in src/momapy/rendering/core.py
@abc.abstractmethod
def render_layout_element(self, layout_element: momapy.core.LayoutElement):
    """Render a layout element"""
    pass

render_map abstractmethod

render_map(map_: Map)

Render a map

Source code in src/momapy/rendering/core.py
@abc.abstractmethod
def render_map(self, map_: momapy.core.Map):
    """Render a map"""
    pass

restore

restore()

Set the current state to the last saved state

Source code in src/momapy/rendering/core.py
def restore(self):
    """Set the current state to the last saved state"""
    if len(self._states) > 0:
        state = self._states.pop()
        self.set_current_state(state)
        self.self_restore()
    else:
        raise Exception("no state to be restored")

save

save()

Save the current state

Source code in src/momapy/rendering/core.py
def save(self):
    """Save the current state"""
    self._states.append(copy.deepcopy(self.get_current_state()))
    self.self_save()

self_restore abstractmethod

self_restore()

Restore the internal state of the renderer.

This method must be implemented by subclasses to restore any internal state that is not part of the current state dictionary.

Source code in src/momapy/rendering/core.py
@abc.abstractmethod
def self_restore(self):
    """Restore the internal state of the renderer.

    This method must be implemented by subclasses to restore any internal
    state that is not part of the current state dictionary.
    """
    pass

self_save abstractmethod

self_save()

Save the internal state of the renderer.

This method must be implemented by subclasses to save any internal state that is not part of the current state dictionary.

Source code in src/momapy/rendering/core.py
@abc.abstractmethod
def self_save(self):
    """Save the internal state of the renderer.

    This method must be implemented by subclasses to save any internal
    state that is not part of the current state dictionary.
    """
    pass

set_current_state

set_current_state(state: dict)

Set the current state to the given state

Source code in src/momapy/rendering/core.py
def set_current_state(self, state: dict):
    """Set the current state to the given state"""
    for attr_name, attr_value in state.items():
        self.set_current_value(attr_name, attr_value)

set_current_state_from_drawing_element

set_current_state_from_drawing_element(drawing_element: DrawingElement)

Set the current state to a state given by a drawing element

Source code in src/momapy/rendering/core.py
def set_current_state_from_drawing_element(
    self, drawing_element: momapy.drawing.DrawingElement
):
    """Set the current state to a state given by a drawing element"""
    state = self._get_state_from_drawing_element(drawing_element)
    self.set_current_state(state)

set_current_value

set_current_value(attr_name: str, attr_value: Any)

Set the current value for an attribute

Source code in src/momapy/rendering/core.py
def set_current_value(self, attr_name: str, attr_value: typing.Any):
    """Set the current value for an attribute"""
    if attr_value is None:
        attr_d = momapy.drawing.PRESENTATION_ATTRIBUTES[attr_name]
        if not attr_d["inherited"]:
            attr_value = self.initial_values.get(attr_name)
            if attr_value is None:
                attr_value = attr_d["initial"]
            if attr_value is None:
                attr_value = momapy.drawing.INITIAL_VALUES[attr_name]
    if attr_name == "font_weight":
        if isinstance(attr_value, momapy.drawing.FontWeight):
            if (
                attr_value == momapy.drawing.FontWeight.NORMAL
                or attr_value == momapy.drawing.FontWeight.BOLD
            ):
                attr_value = self.font_weight_value_mapping[attr_value]
            elif attr_value == momapy.drawing.FontWeight.BOLDER:
                attr_value = self.get_bolder_font_weight(
                    self.get_current_value("font_weight")
                )
            elif attr_value == momapy.drawing.FontWeight.LIGHTER:
                attr_value = self.get_lighter_font_weight(
                    self.get_current_value("font_weight")
                )
    if attr_value is not None:
        self._current_state[attr_name] = attr_value

register_renderer

register_renderer(name, renderer_cls)

Register a renderer class.

.. deprecated:: Use momapy.rendering.register_renderer() instead.

Source code in src/momapy/rendering/core.py
def register_renderer(name, renderer_cls):
    """Register a renderer class.

    .. deprecated::
        Use `momapy.rendering.register_renderer()` instead.
    """
    import momapy.rendering

    momapy.rendering.register_renderer(name, renderer_cls)

render_layout_element

render_layout_element(layout_element: LayoutElement, file_path: str | PathLike, format_: str | None = None, renderer: str | None = None, style_sheet: StyleSheet | None = None, to_top_left: bool = False)

Render a layout element to a file in the given format with the given registered renderer

Parameters:

Name Type Description Default
layout_element LayoutElement

The layout element to render

required
file_path str | PathLike

The output file path

required
format_ str | None

The output format. If None, inferred from file extension.

None
renderer str | None

The registered renderer to use. If None, auto-detected based on format.

None
style_sheet StyleSheet | None

An optional style sheet to apply before rendering

None
to_top_left bool

Whether to move the layout element to the top left or not before rendering

False
Source code in src/momapy/rendering/core.py
def render_layout_element(
    layout_element: momapy.core.LayoutElement,
    file_path: str | os.PathLike,
    format_: str | None = None,
    renderer: str | None = None,
    style_sheet: momapy.styling.StyleSheet | None = None,
    to_top_left: bool = False,
):
    """Render a layout element to a file in the given format with the given registered renderer

    Args:
        layout_element: The layout element to render
        file_path: The output file path
        format_: The output format. If None, inferred from file extension.
        renderer: The registered renderer to use. If None, auto-detected based on format.
        style_sheet: An optional style sheet to apply before rendering
        to_top_left: Whether to move the layout element to the top left or not before rendering
    """
    render_layout_elements(
        layout_elements=[layout_element],
        file_path=file_path,
        format_=format_,
        renderer=renderer,
        style_sheet=style_sheet,
        to_top_left=to_top_left,
    )

render_layout_elements

render_layout_elements(layout_elements: Collection[LayoutElement], file_path: str | PathLike, format_: str | None = None, renderer: str | None = None, style_sheet: StyleSheet | None = None, to_top_left: bool = False, multi_pages: bool = True)

Render a collection of layout elements to a file in the given format with the given registered renderer.

Parameters:

Name Type Description Default
layout_elements Collection[LayoutElement]

The layout elements to render

required
file_path str | PathLike

The output file path

required
format_ str | None

The output format. If None, inferred from file extension.

None
renderer str | None

The registered renderer to use. If None, auto-detected based on format.

None
style_sheet StyleSheet | None

An optional style sheet to apply before rendering

None
to_top_left bool

Whether to move the layout elements to the top left before rendering

False
multi_pages bool

Whether to render each layout element on a separate page

True
Source code in src/momapy/rendering/core.py
def render_layout_elements(
    layout_elements: collections.abc.Collection[momapy.core.LayoutElement],
    file_path: str | os.PathLike,
    format_: str | None = None,
    renderer: str | None = None,
    style_sheet: momapy.styling.StyleSheet | None = None,
    to_top_left: bool = False,
    multi_pages: bool = True,
):
    """Render a collection of layout elements to a file in the given format with the given registered renderer.

    Args:
        layout_elements: The layout elements to render
        file_path: The output file path
        format_: The output format. If None, inferred from file extension.
        renderer: The registered renderer to use. If None, auto-detected based on format.
        style_sheet: An optional style sheet to apply before rendering
        to_top_left: Whether to move the layout elements to the top left before rendering
        multi_pages: Whether to render each layout element on a separate page
    """
    if format_ is None:
        file_path_obj = pathlib.Path(file_path)
        format_ = file_path_obj.suffix[1:]
        if not format_:
            raise ValueError(
                "Cannot determine format from file path. Please specify format_ parameter."
            )
    if renderer is None:
        renderer = _detect_renderer(format_)

    def _prepare_layout_elements(layout_elements, style_sheet=None, to_top_left=False):
        bboxes = [layout_element.bbox() for layout_element in layout_elements]
        bbox = momapy.positioning.fit(bboxes)
        max_x = bbox.x + bbox.width / 2
        max_y = bbox.y + bbox.height / 2
        if style_sheet is not None or to_top_left:
            new_layout_elements = []
            for layout_element in layout_elements:
                if isinstance(layout_element, momapy.core.LayoutElement):
                    new_layout_elements.append(
                        momapy.builder.builder_from_object(layout_element)
                    )
                elif isinstance(layout_element, momapy.core.LayoutElementBuilder):
                    new_layout_elements.append(copy.deepcopy(layout_element))
            layout_elements = new_layout_elements
        if style_sheet is not None:
            if (
                not isinstance(style_sheet, collections.abc.Collection)
                or isinstance(style_sheet, str)
                or isinstance(style_sheet, momapy.styling.StyleSheet)
            ):
                style_sheets = [style_sheet]
            else:
                style_sheets = style_sheet
            style_sheets = [
                (
                    momapy.styling.StyleSheet.from_file(style_sheet)
                    if not isinstance(style_sheet, momapy.styling.StyleSheet)
                    else style_sheet
                )
                for style_sheet in style_sheets
            ]
            style_sheet = momapy.styling.combine_style_sheets(style_sheets)
            for layout_element in layout_elements:
                momapy.styling.apply_style_sheet(layout_element, style_sheet)
        if to_top_left:
            min_x = bbox.x - bbox.width / 2
            min_y = bbox.y - bbox.height / 2
            max_x -= min_x
            max_y -= min_y
            translation = momapy.geometry.Translation(-min_x, -min_y)
            for layout_element in layout_elements:
                for attr_name in ["group_transform", "transform"]:
                    if hasattr(layout_element, attr_name):
                        if getattr(layout_element, attr_name) is None:
                            setattr(
                                layout_element, attr_name, momapy.core.TupleBuilder()
                            )
                        getattr(layout_element, attr_name).append(translation)
                        break
        return layout_elements, max_x, max_y

    from momapy.rendering import get_renderer

    renderer_cls = get_renderer(renderer)
    if not multi_pages:
        prepared_layout_elements, max_x, max_y = _prepare_layout_elements(
            layout_elements, style_sheet, to_top_left
        )
        renderer_instance = renderer_cls.from_file(file_path, max_x, max_y, format_)
        renderer_instance.begin_session()
        for prepared_layout_element in prepared_layout_elements:
            renderer_instance.render_layout_element(prepared_layout_element)
        renderer_instance.end_session()
    else:
        if layout_elements:
            layout_element = layout_elements[0]
            prepared_layout_elements, max_x, max_y = _prepare_layout_elements(
                [layout_element], style_sheet, to_top_left
            )
            renderer_instance = renderer_cls.from_file(file_path, max_x, max_y, format_)
            renderer_instance.begin_session()
            renderer_instance.render_layout_element(prepared_layout_elements[0])
            for layout_element in layout_elements[1:]:
                prepared_layout_elements, max_x, max_y = _prepare_layout_elements(
                    [layout_element], style_sheet, to_top_left
                )
                renderer_instance.new_page(max_x, max_y)
                renderer_instance.render_layout_element(prepared_layout_elements[0])
        else:
            renderer_instance = renderer_cls.from_file(file_path, 0, 0, format_)
        renderer_instance.end_session()

render_map

render_map(map_: Map, file_path: str | PathLike, format_: str | None = None, renderer: str | None = None, style_sheet: StyleSheet | None = None, to_top_left: bool = False)

Render a map to a file in the given format with the given registered renderer.

Parameters:

Name Type Description Default
map_ Map

The map to render

required
file_path str | PathLike

The output file path

required
format_ str | None

The output format. If None, inferred from file extension.

None
renderer str | None

The registered renderer to use. If None, auto-detected based on format.

None
style_sheet StyleSheet | None

An optional style sheet to apply before rendering

None
to_top_left bool

Whether to move the map to the top left before rendering

False
Example

from momapy.io.core import read from momapy.rendering.core import render_map

Read a map from file

result = read("path/to/map.sbgn") sbgn_map = result.obj

Render the map to SVG

render_map(sbgn_map, "output.svg")

Source code in src/momapy/rendering/core.py
def render_map(
    map_: momapy.core.Map,
    file_path: str | os.PathLike,
    format_: str | None = None,
    renderer: str | None = None,
    style_sheet: momapy.styling.StyleSheet | None = None,
    to_top_left: bool = False,
):
    """Render a map to a file in the given format with the given registered renderer.

    Args:
        map_: The map to render
        file_path: The output file path
        format_: The output format. If None, inferred from file extension.
        renderer: The registered renderer to use. If None, auto-detected based on format.
        style_sheet: An optional style sheet to apply before rendering
        to_top_left: Whether to move the map to the top left before rendering

    Example:
        >>> from momapy.io.core import read
        >>> from momapy.rendering.core import render_map
        >>> # Read a map from file
        >>> result = read("path/to/map.sbgn")
        >>> sbgn_map = result.obj
        >>> # Render the map to SVG
        >>> render_map(sbgn_map, "output.svg")
    """
    render_maps([map_], file_path, format_, renderer, style_sheet, to_top_left)

render_maps

render_maps(maps: Collection[Map], file_path: str | PathLike, format_: str | None = None, renderer: str | None = None, style_sheet: StyleSheet | None = None, to_top_left: bool = False, multi_pages: bool = True)

Render a collection of maps to a file in the given format with the given registered renderer.

Parameters:

Name Type Description Default
maps Collection[Map]

The maps to render

required
file_path str | PathLike

The output file path

required
format_ str | None

The output format. If None, inferred from file extension.

None
renderer str | None

The registered renderer to use. If None, auto-detected based on format.

None
style_sheet StyleSheet | None

An optional style sheet to apply before rendering

None
to_top_left bool

Whether to move the maps to the top left before rendering

False
multi_pages bool

Whether to render each map on a separate page

True
Example

from momapy.io.core import read from momapy.rendering.core import render_maps

Read multiple maps from files

result1 = read("path/to/map1.sbgn") first_map = result1.obj result2 = read("path/to/map2.sbgn") second_map = result2.obj

Render both maps to a multi-page PDF

render_maps([first_map, second_map], "output.pdf", multi_pages=True)

Source code in src/momapy/rendering/core.py
def render_maps(
    maps: collections.abc.Collection[momapy.core.Map],
    file_path: str | os.PathLike,
    format_: str | None = None,
    renderer: str | None = None,
    style_sheet: momapy.styling.StyleSheet | None = None,
    to_top_left: bool = False,
    multi_pages: bool = True,
):
    """Render a collection of maps to a file in the given format with the given registered renderer.

    Args:
        maps: The maps to render
        file_path: The output file path
        format_: The output format. If None, inferred from file extension.
        renderer: The registered renderer to use. If None, auto-detected based on format.
        style_sheet: An optional style sheet to apply before rendering
        to_top_left: Whether to move the maps to the top left before rendering
        multi_pages: Whether to render each map on a separate page

    Example:
        >>> from momapy.io.core import read
        >>> from momapy.rendering.core import render_maps
        >>> # Read multiple maps from files
        >>> result1 = read("path/to/map1.sbgn")
        >>> first_map = result1.obj
        >>> result2 = read("path/to/map2.sbgn")
        >>> second_map = result2.obj
        >>> # Render both maps to a multi-page PDF
        >>> render_maps([first_map, second_map], "output.pdf", multi_pages=True)
    """
    layout_elements = [map_.layout for map_ in maps]
    render_layout_elements(
        layout_elements=layout_elements,
        file_path=file_path,
        format_=format_,
        renderer=renderer,
        style_sheet=style_sheet,
        to_top_left=to_top_left,
        multi_pages=multi_pages,
    )