Skip to content

API Reference¤

This section contains the API reference for the bessaplots library. It provides detailed information about the classes, functions, and modules available in the package.

bessaplots ¤

Bessa Plots - Plottting utilities for scientific papers within the Bessa Group

bessaplots_path = '/home/docs/checkouts/readthedocs.org/user_builds/bessaplots/checkouts/latest/src/bessaplots' module-attribute ¤

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

styles_path = '/home/docs/checkouts/readthedocs.org/user_builds/bessaplots/checkouts/latest/src/bessaplots/styles' module-attribute ¤

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

stylesheets = {'bessaplots': RcParams({'axes.linewidth': 0.5,'figure.dpi': 600.0,'figure.figsize': [3.3, 2.5],'font.family': ['serif'],'font.serif': ['Times'],'font.size': 8.0,'grid.linewidth': 0.5,'legend.frameon': False,'lines.linewidth': 1.0,'mathtext.fontset': 'dejavuserif','savefig.bbox': 'tight','savefig.pad_inches': 0.01,'text.usetex': False,'xtick.direction': 'in','xtick.major.size': 3.0,'xtick.major.width': 0.5,'xtick.minor.size': 1.5,'xtick.minor.visible': True,'xtick.minor.width': 0.5,'xtick.top': False,'ytick.direction': 'in','ytick.major.size': 3.0,'ytick.major.width': 0.5,'ytick.minor.size': 1.5,'ytick.minor.visible': True,'ytick.minor.width': 0.5,'ytick.right': False})} module-attribute ¤

dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)

FigureSaver ¤
Source code in src/bessaplots/save_figure.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
class FigureSaver:
    @staticmethod
    def load(path: str) -> plt.Figure:
        """Load a figure from a file."""
        _path = Path(path).with_suffix(".pdf")
        return plt.imread(_path)

    @staticmethod
    def store(object: plt.Figure, path: str) -> str:
        _path = Path(path)
        """Store a figure to a file."""
        object.savefig(
            _path.with_suffix(".pdf"),
            format="pdf",
            bbox_inches="tight",
            pad_inches=0.01,
            transparent=True,
            dpi=300,
        )
        return str(_path)
load(path: str) -> Figure staticmethod ¤

Load a figure from a file.

Source code in src/bessaplots/save_figure.py
88
89
90
91
92
@staticmethod
def load(path: str) -> plt.Figure:
    """Load a figure from a file."""
    _path = Path(path).with_suffix(".pdf")
    return plt.imread(_path)
store(object: Figure, path: str) -> str staticmethod ¤
Source code in src/bessaplots/save_figure.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
@staticmethod
def store(object: plt.Figure, path: str) -> str:
    _path = Path(path)
    """Store a figure to a file."""
    object.savefig(
        _path.with_suffix(".pdf"),
        format="pdf",
        bbox_inches="tight",
        pad_inches=0.01,
        transparent=True,
        dpi=300,
    )
    return str(_path)
TypstReport ¤

Builder for Typst-based PDF reports.

Accumulates paragraphs and figure grids, then renders them to a .typ source file and optionally compiles to PDF via the typst CLI.

Parameters:

Name Type Description Default
title str

Report title. Empty string omits the title block entirely. The default is "".

''
author str

Author name shown below the title. Ignored when title is empty. The default is "".

''
date str or None

Date string shown below the author. None uses today's ISO date; "" omits the date line. The default is None.

None
paper_size str

One of "letter", "a4", or "b5". The default is "a4".

'a4'

Examples:

>>> from bessaplots import TypstReport
>>> r = TypstReport(title="My Report", author="J. Doe")
>>> r.add_paragraph("Introduction.")
>>> r.add_figures(["fig1.pdf", "fig2.pdf"], columns=2)
>>> r.save("report")  # writes report.typ + compiles report.pdf
Source code in src/bessaplots/typst_report.py
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
@dataclass
class TypstReport:
    """
    Builder for Typst-based PDF reports.

    Accumulates paragraphs and figure grids, then renders them to a
    ``.typ`` source file and optionally compiles to PDF via the
    ``typst`` CLI.

    Parameters
    ----------
    title : str, optional
        Report title. Empty string omits the title block entirely.
        The default is ``""``.
    author : str, optional
        Author name shown below the title. Ignored when title is empty.
        The default is ``""``.
    date : str or None, optional
        Date string shown below the author.  ``None`` uses today's ISO
        date; ``""`` omits the date line.  The default is ``None``.
    paper_size : str, optional
        One of ``"letter"``, ``"a4"``, or ``"b5"``.
        The default is ``"a4"``.

    Examples
    --------
    >>> from bessaplots import TypstReport
    >>> r = TypstReport(title="My Report", author="J. Doe")
    >>> r.add_paragraph("Introduction.")
    >>> r.add_figures(["fig1.pdf", "fig2.pdf"], columns=2)
    >>> r.save("report")  # writes report.typ + compiles report.pdf
    """

    title: str = ""
    author: str = ""
    date: str | None = None
    paper_size: str = "a4"
    _blocks: list[str] = field(default_factory=list, init=False, repr=False)

    def __post_init__(self) -> None:
        """Validate *paper_size* and default *date* to today's ISO date.

        Raises
        ------
        ValueError
            If *paper_size* is not a recognised size in
            ``PAPER_SIZES``.
        """
        try:
            _ = PAPER_SIZES[self.paper_size.lower()]
        except KeyError:
            raise ValueError(
                f"Unknown paper size '{self.paper_size}'. "
                f"Available sizes: {list(PAPER_SIZES.keys())}"
            ) from None

        self.paper_size = self.paper_size.lower()
        if self.date is None:
            self.date = datetime.date.today().isoformat()

    # ------------------------------------------------------------------
    # Public API
    # ------------------------------------------------------------------

    def add_paragraph(self, text: str) -> None:
        """
        Add a paragraph of text to the report.

        Parameters
        ----------
        text : str
            Plain text or Typst markup passed through verbatim.
            Special Typst characters (``#``, ``[``, ``]``) are not
            escaped; advanced users may include Typst markup directly.
        """
        self._blocks.append(text)

    def add_figures(
        self,
        paths: list[str | Path],
        columns: int | list[str] = 1,
        caption: str | None = None,
        gutter: str = "1em",
        subcaptions: list[str] | None = None,
        rows_per_page: int | None = None,
    ) -> None:
        """
        Add a grid of figures to the report.

        Parameters
        ----------
        paths : list of str or Path
            Paths to the figure files (e.g. PDF or PNG).  Each path is
            resolved to an absolute POSIX path so the generated ``.typ``
            file works regardless of where it is written.
        columns : int or list of str, optional
            Number of equal-width columns (``int``) or explicit Typst
            column-width strings such as ``["1fr", "2fr"]``.
            The default is ``1``.
        caption : str or None, optional
            When provided, wraps the grid in a Typst ``figure`` block
            with this caption, enabling automatic figure numbering.
            The default is ``None`` (no caption, no figure numbering).
        gutter : str, optional
            Typst ``column-gutter`` value (e.g. ``"1em"``, ``"5mm"``).
            The default is ``"1em"``.
        subcaptions : list of str or None, optional
            Per-figure captions.  When provided, each image is wrapped
            in its own ``figure()`` block inside the grid.  The list
            length must equal ``len(paths)``.  Can be combined with
            *caption* for an overall grid caption.
            The default is ``None``.
        rows_per_page : int or None, optional
            Maximum number of rows per page.  When provided, the
            figures are split into multiple grids separated by
            ``#pagebreak()``.  The caption (if any) is placed on the
            last chunk only.  The default is ``None`` (no splitting).

        Raises
        ------
        ValueError
            If ``paths`` is empty, ``columns`` is an integer <= 0,
            or ``subcaptions`` length does not match ``paths``.
        """
        if not paths:
            raise ValueError("paths must be non-empty")
        if isinstance(columns, int) and columns <= 0:
            raise ValueError("columns must be a positive integer")
        if isinstance(columns, list) and len(columns) == 0:
            raise ValueError("columns list must be non-empty")
        if subcaptions is not None and len(subcaptions) != len(paths):
            raise ValueError(
                f"subcaptions length ({len(subcaptions)}) must equal "
                f"paths length ({len(paths)})"
            )

        if rows_per_page is not None:
            self._blocks.append(
                _split_figure_blocks(
                    paths,
                    columns,
                    caption,
                    gutter,
                    subcaptions,
                    rows_per_page,
                )
            )
        else:
            self._blocks.append(
                _figure_block(paths, columns, caption, gutter, subcaptions)
            )

    def write(self, path: str | Path) -> Path:
        """
        Write the ``.typ`` source file.

        Parameters
        ----------
        path : str or Path
            Destination path.  The ``.typ`` extension is enforced
            regardless of what suffix is provided.

        Returns
        -------
        Path
            Absolute path to the written ``.typ`` file.
        """
        resolved = Path(path).with_suffix(".typ")
        resolved.write_text(
            _render(
                self._blocks,
                self.title,
                self.author,
                self.date,
                self.paper_size,
            ),
            encoding="utf-8",
        )
        logger.info(f"Wrote Typst source: {resolved}")
        return resolved

    def compile(
        self,
        typ_path: str | Path,
        pdf_path: str | Path | None = None,
    ) -> Path:
        """
        Compile a ``.typ`` file to PDF using the ``typst`` CLI.

        Parameters
        ----------
        typ_path : str or Path
            Path to the ``.typ`` source file.
        pdf_path : str or Path or None, optional
            Output PDF path.  ``None`` replaces the ``.typ`` suffix with
            ``.pdf``.  The default is ``None``.

        Returns
        -------
        Path
            Path to the compiled PDF.

        Raises
        ------
        RuntimeError
            If the ``typst`` executable is not found or compilation
            fails.
        """
        typ = Path(typ_path).with_suffix(".typ")
        pdf = (
            typ.with_suffix(".pdf")
            if pdf_path is None
            else Path(pdf_path).with_suffix(".pdf")
        )
        try:
            result = subprocess.run(
                ["typst", "compile", "--root", "/", str(typ), str(pdf)],
                capture_output=True,
                text=True,
            )
        except FileNotFoundError:
            raise RuntimeError(
                "typst CLI not found. Install via "
                "https://github.com/typst/typst#installation"
            ) from None

        if result.returncode != 0:
            raise RuntimeError(f"typst compile failed:\n{result.stderr}")
        if result.stderr:
            logger.warning(result.stderr)

        logger.info(f"Compiled PDF: {pdf}")
        return pdf

    def save(self, stem: str | Path) -> Path:
        """
        Write the ``.typ`` file and compile it to PDF.

        Parameters
        ----------
        stem : str or Path
            Base name (without extension) for the output files.
            Produces ``<stem>.typ`` and ``<stem>.pdf``.

        Returns
        -------
        Path
            Path to the compiled PDF.
        """
        stem = Path(stem)
        typ_path = self.write(stem.with_suffix(".typ"))
        return self.compile(typ_path)
author = '' class-attribute ¤

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

paper_size = 'a4' class-attribute ¤

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

title = '' class-attribute ¤

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

add_figures(paths: list[str | pathlib.Path], columns: int | list[str] = 1, caption: str | None = None, gutter: str = '1em', subcaptions: list[str] | None = None, rows_per_page: int | None = None) -> None ¤

Add a grid of figures to the report.

Parameters:

Name Type Description Default
paths list of str or Path

Paths to the figure files (e.g. PDF or PNG). Each path is resolved to an absolute POSIX path so the generated .typ file works regardless of where it is written.

required
columns int or list of str

Number of equal-width columns (int) or explicit Typst column-width strings such as ["1fr", "2fr"]. The default is 1.

1
caption str or None

When provided, wraps the grid in a Typst figure block with this caption, enabling automatic figure numbering. The default is None (no caption, no figure numbering).

None
gutter str

Typst column-gutter value (e.g. "1em", "5mm"). The default is "1em".

'1em'
subcaptions list of str or None

Per-figure captions. When provided, each image is wrapped in its own figure() block inside the grid. The list length must equal len(paths). Can be combined with caption for an overall grid caption. The default is None.

None
rows_per_page int or None

Maximum number of rows per page. When provided, the figures are split into multiple grids separated by #pagebreak(). The caption (if any) is placed on the last chunk only. The default is None (no splitting).

None

Raises:

Type Description
ValueError

If paths is empty, columns is an integer <= 0, or subcaptions length does not match paths.

Source code in src/bessaplots/typst_report.py
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
def add_figures(
    self,
    paths: list[str | Path],
    columns: int | list[str] = 1,
    caption: str | None = None,
    gutter: str = "1em",
    subcaptions: list[str] | None = None,
    rows_per_page: int | None = None,
) -> None:
    """
    Add a grid of figures to the report.

    Parameters
    ----------
    paths : list of str or Path
        Paths to the figure files (e.g. PDF or PNG).  Each path is
        resolved to an absolute POSIX path so the generated ``.typ``
        file works regardless of where it is written.
    columns : int or list of str, optional
        Number of equal-width columns (``int``) or explicit Typst
        column-width strings such as ``["1fr", "2fr"]``.
        The default is ``1``.
    caption : str or None, optional
        When provided, wraps the grid in a Typst ``figure`` block
        with this caption, enabling automatic figure numbering.
        The default is ``None`` (no caption, no figure numbering).
    gutter : str, optional
        Typst ``column-gutter`` value (e.g. ``"1em"``, ``"5mm"``).
        The default is ``"1em"``.
    subcaptions : list of str or None, optional
        Per-figure captions.  When provided, each image is wrapped
        in its own ``figure()`` block inside the grid.  The list
        length must equal ``len(paths)``.  Can be combined with
        *caption* for an overall grid caption.
        The default is ``None``.
    rows_per_page : int or None, optional
        Maximum number of rows per page.  When provided, the
        figures are split into multiple grids separated by
        ``#pagebreak()``.  The caption (if any) is placed on the
        last chunk only.  The default is ``None`` (no splitting).

    Raises
    ------
    ValueError
        If ``paths`` is empty, ``columns`` is an integer <= 0,
        or ``subcaptions`` length does not match ``paths``.
    """
    if not paths:
        raise ValueError("paths must be non-empty")
    if isinstance(columns, int) and columns <= 0:
        raise ValueError("columns must be a positive integer")
    if isinstance(columns, list) and len(columns) == 0:
        raise ValueError("columns list must be non-empty")
    if subcaptions is not None and len(subcaptions) != len(paths):
        raise ValueError(
            f"subcaptions length ({len(subcaptions)}) must equal "
            f"paths length ({len(paths)})"
        )

    if rows_per_page is not None:
        self._blocks.append(
            _split_figure_blocks(
                paths,
                columns,
                caption,
                gutter,
                subcaptions,
                rows_per_page,
            )
        )
    else:
        self._blocks.append(
            _figure_block(paths, columns, caption, gutter, subcaptions)
        )
add_paragraph(text: str) -> None ¤

Add a paragraph of text to the report.

Parameters:

Name Type Description Default
text str

Plain text or Typst markup passed through verbatim. Special Typst characters (#, [, ]) are not escaped; advanced users may include Typst markup directly.

required
Source code in src/bessaplots/typst_report.py
318
319
320
321
322
323
324
325
326
327
328
329
def add_paragraph(self, text: str) -> None:
    """
    Add a paragraph of text to the report.

    Parameters
    ----------
    text : str
        Plain text or Typst markup passed through verbatim.
        Special Typst characters (``#``, ``[``, ``]``) are not
        escaped; advanced users may include Typst markup directly.
    """
    self._blocks.append(text)
compile(typ_path: str | pathlib.Path, pdf_path: str | pathlib.Path | None = None) -> Path ¤

Compile a .typ file to PDF using the typst CLI.

Parameters:

Name Type Description Default
typ_path str or Path

Path to the .typ source file.

required
pdf_path str or Path or None

Output PDF path. None replaces the .typ suffix with .pdf. The default is None.

None

Returns:

Type Description
Path

Path to the compiled PDF.

Raises:

Type Description
RuntimeError

If the typst executable is not found or compilation fails.

Source code in src/bessaplots/typst_report.py
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
def compile(
    self,
    typ_path: str | Path,
    pdf_path: str | Path | None = None,
) -> Path:
    """
    Compile a ``.typ`` file to PDF using the ``typst`` CLI.

    Parameters
    ----------
    typ_path : str or Path
        Path to the ``.typ`` source file.
    pdf_path : str or Path or None, optional
        Output PDF path.  ``None`` replaces the ``.typ`` suffix with
        ``.pdf``.  The default is ``None``.

    Returns
    -------
    Path
        Path to the compiled PDF.

    Raises
    ------
    RuntimeError
        If the ``typst`` executable is not found or compilation
        fails.
    """
    typ = Path(typ_path).with_suffix(".typ")
    pdf = (
        typ.with_suffix(".pdf")
        if pdf_path is None
        else Path(pdf_path).with_suffix(".pdf")
    )
    try:
        result = subprocess.run(
            ["typst", "compile", "--root", "/", str(typ), str(pdf)],
            capture_output=True,
            text=True,
        )
    except FileNotFoundError:
        raise RuntimeError(
            "typst CLI not found. Install via "
            "https://github.com/typst/typst#installation"
        ) from None

    if result.returncode != 0:
        raise RuntimeError(f"typst compile failed:\n{result.stderr}")
    if result.stderr:
        logger.warning(result.stderr)

    logger.info(f"Compiled PDF: {pdf}")
    return pdf
save(stem: str | pathlib.Path) -> Path ¤

Write the .typ file and compile it to PDF.

Parameters:

Name Type Description Default
stem str or Path

Base name (without extension) for the output files. Produces <stem>.typ and <stem>.pdf.

required

Returns:

Type Description
Path

Path to the compiled PDF.

Source code in src/bessaplots/typst_report.py
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
def save(self, stem: str | Path) -> Path:
    """
    Write the ``.typ`` file and compile it to PDF.

    Parameters
    ----------
    stem : str or Path
        Base name (without extension) for the output files.
        Produces ``<stem>.typ`` and ``<stem>.pdf``.

    Returns
    -------
    Path
        Path to the compiled PDF.
    """
    stem = Path(stem)
    typ_path = self.write(stem.with_suffix(".typ"))
    return self.compile(typ_path)
write(path: str | pathlib.Path) -> Path ¤

Write the .typ source file.

Parameters:

Name Type Description Default
path str or Path

Destination path. The .typ extension is enforced regardless of what suffix is provided.

required

Returns:

Type Description
Path

Absolute path to the written .typ file.

Source code in src/bessaplots/typst_report.py
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
def write(self, path: str | Path) -> Path:
    """
    Write the ``.typ`` source file.

    Parameters
    ----------
    path : str or Path
        Destination path.  The ``.typ`` extension is enforced
        regardless of what suffix is provided.

    Returns
    -------
    Path
        Absolute path to the written ``.typ`` file.
    """
    resolved = Path(path).with_suffix(".typ")
    resolved.write_text(
        _render(
            self._blocks,
            self.title,
            self.author,
            self.date,
            self.paper_size,
        ),
        encoding="utf-8",
    )
    logger.info(f"Wrote Typst source: {resolved}")
    return resolved
read_styles_in_folders(root_path) ¤

Reads all stylesheets in the given path and its subfolders.

Parameters:

Name Type Description Default
root_path str

Path to the root folder containing the stylesheets and other subfolders with stylesheets.

required

Returns:

Name Type Description
stylesheets dict

Dictionary of stylesheets in the form of {style_name: rcParams}. Should be compatible with matplotlib's plt.style.library dictionary.

Source code in src/bessaplots/styles_discovery.py
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
def read_styles_in_folders(root_path):
    """
    Reads all stylesheets in the given path and its subfolders.

    Parameters
    ----------
    root_path : str
        Path to the root folder containing the stylesheets and other subfolders
        with stylesheets.

    Returns
    -------
    stylesheets : dict
        Dictionary of stylesheets in the form of {style_name: rcParams}.
        Should be compatible with matplotlib's plt.style.library dictionary.
    """
    stylesheets = {}  # plt.style.library is a dictionary
    for folder, _, _ in os.walk(root_path):
        new_stylesheets = plt.style.core.read_style_directory(folder)
        stylesheets.update(new_stylesheets)
    return stylesheets
savefig(fig: Figure, path: str, n_side_by_side=1, span_columns=False, height=0.8, paper_size: str = 'letter') -> None ¤

Saves a figure scaled exactly for an IEEE subfigure slot.

Parameters:

Name Type Description Default
fig Figure

The matplotlib figure object.

required
path str

The storage location (including file name) for the figure. The .pdf suffix is added automatically.

required
n_side_by_side int

How many figures will sit horizontally (1, 2, or 3). The default is 1.

1
span_columns bool

False for single column (3.48"), True for full page (7.14"). The default is False.

False
height float

The height of the figure as a fraction of the width. The default is 0.8.

0.8
paper_size str

The paper size ("letter", "a4", "b5"). The default is "letter".

'letter'
Source code in src/bessaplots/save_figure.py
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
def savefig(
    fig: plt.Figure,
    path: str,
    n_side_by_side=1,
    span_columns=False,
    height=0.8,
    paper_size: str = "letter",
) -> None:
    """
    Saves a figure scaled exactly for an IEEE subfigure slot.

    Parameters
    ----------
    fig : plt.Figure
        The matplotlib figure object.
    path : str
        The storage location (including file name) for the figure.
        The .pdf suffix is added automatically.
    n_side_by_side : int, optional
        How many figures will sit horizontally (1, 2, or 3).
        The default is 1.
    span_columns : bool, optional
        False for single column (3.48"), True for full page (7.14").
        The default is False.
    height : float, optional
        The height of the figure as a fraction of the width.
        The default is 0.8.
    paper_size : str, optional
        The paper size ("letter", "a4", "b5"). The default is "letter".
    """

    fig = set_size(
        fig,
        n_side_by_side=n_side_by_side,
        span_columns=span_columns,
        height=height,
        paper_size=paper_size,
    )

    _path = Path(path).with_suffix(".pdf")

    # 5. Save with tight bounding box
    # pad_inches is tiny to ensure the figure maximizes the LaTeX slot
    fig.savefig(
        _path,
        format="pdf",
        bbox_inches="tight",
        pad_inches=0.01,
        transparent=True,
    )
    logger.info(f"Saved figure: {_path}")
set_size(fig: Figure, n_side_by_side=1, span_columns=False, height: float = 0.8, paper_size: str = 'letter') -> Figure ¤

Set figure size to fit in IEEE column layout.

Parameters:

Name Type Description Default
fig Figure

The matplotlib figure object.

required
n_side_by_side int

How many figures will sit horizontally (1, 2, or 3). The default is 1.

1
span_columns bool

False for single column (3.48"), True for full page (7.14"). The default is False.

False
height float

The height of the figure as a fraction of the width. The default is 0.8.

0.8
paper_size str

The paper size ("letter", "a4", "b5"). The default is "letter".

'letter'

Returns:

Name Type Description
fig Figure

The matplotlib figure object with the new size.

Source code in src/bessaplots/save_figure.py
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
def set_size(
    fig: plt.Figure,
    n_side_by_side=1,
    span_columns=False,
    height: float = 0.8,
    paper_size: str = "letter",
) -> plt.Figure:
    """
    Set figure size to fit in IEEE column layout.

    Parameters
    ----------
    fig : plt.Figure
        The matplotlib figure object.
    n_side_by_side : int, optional
        How many figures will sit horizontally (1, 2, or 3). The default is 1.
    span_columns : bool, optional
        False for single column (3.48"), True for full page (7.14").
        The default is False.
    height : float, optional
        The height of the figure as a fraction of the width.
        The default is 0.8.
    paper_size : str, optional
        The paper size ("letter", "a4", "b5"). The default is "letter".

    Returns
    -------
    fig : plt.Figure
        The matplotlib figure object with the new size.
    """

    try:
        sizes = PAPER_SIZES[paper_size.lower()]
    except KeyError:
        raise ValueError(
            f"Unknown paper size '{paper_size}'. "
            f"Available sizes: {list(PAPER_SIZES.keys())}"
        ) from None

    total_width = (
        sizes["double_col_width"]
        if span_columns
        else sizes["single_col_width"]
    )

    # 2. Account for LaTeX margins/gutters between subfigures
    # We use a 2% "safety margin" so LaTeX doesn't force a line break
    available_fraction = 0.98 / n_side_by_side
    target_width = total_width * available_fraction

    # 3. Set Height (Golden ratio is standard, but 0.8 is great for ERTD)
    target_height = target_width * height

    # 4. Apply dimensions and force 8pt font
    fig.set_size_inches(target_width, target_height)
    return fig

TypstReport¤

bessaplots.TypstReport ¤

Builder for Typst-based PDF reports.

Accumulates paragraphs and figure grids, then renders them to a .typ source file and optionally compiles to PDF via the typst CLI.

Parameters:

Name Type Description Default
title str

Report title. Empty string omits the title block entirely. The default is "".

''
author str

Author name shown below the title. Ignored when title is empty. The default is "".

''
date str or None

Date string shown below the author. None uses today's ISO date; "" omits the date line. The default is None.

None
paper_size str

One of "letter", "a4", or "b5". The default is "a4".

'a4'

Examples:

>>> from bessaplots import TypstReport
>>> r = TypstReport(title="My Report", author="J. Doe")
>>> r.add_paragraph("Introduction.")
>>> r.add_figures(["fig1.pdf", "fig2.pdf"], columns=2)
>>> r.save("report")  # writes report.typ + compiles report.pdf
Source code in src/bessaplots/typst_report.py
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
@dataclass
class TypstReport:
    """
    Builder for Typst-based PDF reports.

    Accumulates paragraphs and figure grids, then renders them to a
    ``.typ`` source file and optionally compiles to PDF via the
    ``typst`` CLI.

    Parameters
    ----------
    title : str, optional
        Report title. Empty string omits the title block entirely.
        The default is ``""``.
    author : str, optional
        Author name shown below the title. Ignored when title is empty.
        The default is ``""``.
    date : str or None, optional
        Date string shown below the author.  ``None`` uses today's ISO
        date; ``""`` omits the date line.  The default is ``None``.
    paper_size : str, optional
        One of ``"letter"``, ``"a4"``, or ``"b5"``.
        The default is ``"a4"``.

    Examples
    --------
    >>> from bessaplots import TypstReport
    >>> r = TypstReport(title="My Report", author="J. Doe")
    >>> r.add_paragraph("Introduction.")
    >>> r.add_figures(["fig1.pdf", "fig2.pdf"], columns=2)
    >>> r.save("report")  # writes report.typ + compiles report.pdf
    """

    title: str = ""
    author: str = ""
    date: str | None = None
    paper_size: str = "a4"
    _blocks: list[str] = field(default_factory=list, init=False, repr=False)

    def __post_init__(self) -> None:
        """Validate *paper_size* and default *date* to today's ISO date.

        Raises
        ------
        ValueError
            If *paper_size* is not a recognised size in
            ``PAPER_SIZES``.
        """
        try:
            _ = PAPER_SIZES[self.paper_size.lower()]
        except KeyError:
            raise ValueError(
                f"Unknown paper size '{self.paper_size}'. "
                f"Available sizes: {list(PAPER_SIZES.keys())}"
            ) from None

        self.paper_size = self.paper_size.lower()
        if self.date is None:
            self.date = datetime.date.today().isoformat()

    # ------------------------------------------------------------------
    # Public API
    # ------------------------------------------------------------------

    def add_paragraph(self, text: str) -> None:
        """
        Add a paragraph of text to the report.

        Parameters
        ----------
        text : str
            Plain text or Typst markup passed through verbatim.
            Special Typst characters (``#``, ``[``, ``]``) are not
            escaped; advanced users may include Typst markup directly.
        """
        self._blocks.append(text)

    def add_figures(
        self,
        paths: list[str | Path],
        columns: int | list[str] = 1,
        caption: str | None = None,
        gutter: str = "1em",
        subcaptions: list[str] | None = None,
        rows_per_page: int | None = None,
    ) -> None:
        """
        Add a grid of figures to the report.

        Parameters
        ----------
        paths : list of str or Path
            Paths to the figure files (e.g. PDF or PNG).  Each path is
            resolved to an absolute POSIX path so the generated ``.typ``
            file works regardless of where it is written.
        columns : int or list of str, optional
            Number of equal-width columns (``int``) or explicit Typst
            column-width strings such as ``["1fr", "2fr"]``.
            The default is ``1``.
        caption : str or None, optional
            When provided, wraps the grid in a Typst ``figure`` block
            with this caption, enabling automatic figure numbering.
            The default is ``None`` (no caption, no figure numbering).
        gutter : str, optional
            Typst ``column-gutter`` value (e.g. ``"1em"``, ``"5mm"``).
            The default is ``"1em"``.
        subcaptions : list of str or None, optional
            Per-figure captions.  When provided, each image is wrapped
            in its own ``figure()`` block inside the grid.  The list
            length must equal ``len(paths)``.  Can be combined with
            *caption* for an overall grid caption.
            The default is ``None``.
        rows_per_page : int or None, optional
            Maximum number of rows per page.  When provided, the
            figures are split into multiple grids separated by
            ``#pagebreak()``.  The caption (if any) is placed on the
            last chunk only.  The default is ``None`` (no splitting).

        Raises
        ------
        ValueError
            If ``paths`` is empty, ``columns`` is an integer <= 0,
            or ``subcaptions`` length does not match ``paths``.
        """
        if not paths:
            raise ValueError("paths must be non-empty")
        if isinstance(columns, int) and columns <= 0:
            raise ValueError("columns must be a positive integer")
        if isinstance(columns, list) and len(columns) == 0:
            raise ValueError("columns list must be non-empty")
        if subcaptions is not None and len(subcaptions) != len(paths):
            raise ValueError(
                f"subcaptions length ({len(subcaptions)}) must equal "
                f"paths length ({len(paths)})"
            )

        if rows_per_page is not None:
            self._blocks.append(
                _split_figure_blocks(
                    paths,
                    columns,
                    caption,
                    gutter,
                    subcaptions,
                    rows_per_page,
                )
            )
        else:
            self._blocks.append(
                _figure_block(paths, columns, caption, gutter, subcaptions)
            )

    def write(self, path: str | Path) -> Path:
        """
        Write the ``.typ`` source file.

        Parameters
        ----------
        path : str or Path
            Destination path.  The ``.typ`` extension is enforced
            regardless of what suffix is provided.

        Returns
        -------
        Path
            Absolute path to the written ``.typ`` file.
        """
        resolved = Path(path).with_suffix(".typ")
        resolved.write_text(
            _render(
                self._blocks,
                self.title,
                self.author,
                self.date,
                self.paper_size,
            ),
            encoding="utf-8",
        )
        logger.info(f"Wrote Typst source: {resolved}")
        return resolved

    def compile(
        self,
        typ_path: str | Path,
        pdf_path: str | Path | None = None,
    ) -> Path:
        """
        Compile a ``.typ`` file to PDF using the ``typst`` CLI.

        Parameters
        ----------
        typ_path : str or Path
            Path to the ``.typ`` source file.
        pdf_path : str or Path or None, optional
            Output PDF path.  ``None`` replaces the ``.typ`` suffix with
            ``.pdf``.  The default is ``None``.

        Returns
        -------
        Path
            Path to the compiled PDF.

        Raises
        ------
        RuntimeError
            If the ``typst`` executable is not found or compilation
            fails.
        """
        typ = Path(typ_path).with_suffix(".typ")
        pdf = (
            typ.with_suffix(".pdf")
            if pdf_path is None
            else Path(pdf_path).with_suffix(".pdf")
        )
        try:
            result = subprocess.run(
                ["typst", "compile", "--root", "/", str(typ), str(pdf)],
                capture_output=True,
                text=True,
            )
        except FileNotFoundError:
            raise RuntimeError(
                "typst CLI not found. Install via "
                "https://github.com/typst/typst#installation"
            ) from None

        if result.returncode != 0:
            raise RuntimeError(f"typst compile failed:\n{result.stderr}")
        if result.stderr:
            logger.warning(result.stderr)

        logger.info(f"Compiled PDF: {pdf}")
        return pdf

    def save(self, stem: str | Path) -> Path:
        """
        Write the ``.typ`` file and compile it to PDF.

        Parameters
        ----------
        stem : str or Path
            Base name (without extension) for the output files.
            Produces ``<stem>.typ`` and ``<stem>.pdf``.

        Returns
        -------
        Path
            Path to the compiled PDF.
        """
        stem = Path(stem)
        typ_path = self.write(stem.with_suffix(".typ"))
        return self.compile(typ_path)
author = '' class-attribute ¤

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

paper_size = 'a4' class-attribute ¤

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

title = '' class-attribute ¤

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

add_figures(paths: list[str | pathlib.Path], columns: int | list[str] = 1, caption: str | None = None, gutter: str = '1em', subcaptions: list[str] | None = None, rows_per_page: int | None = None) -> None ¤

Add a grid of figures to the report.

Parameters:

Name Type Description Default
paths list of str or Path

Paths to the figure files (e.g. PDF or PNG). Each path is resolved to an absolute POSIX path so the generated .typ file works regardless of where it is written.

required
columns int or list of str

Number of equal-width columns (int) or explicit Typst column-width strings such as ["1fr", "2fr"]. The default is 1.

1
caption str or None

When provided, wraps the grid in a Typst figure block with this caption, enabling automatic figure numbering. The default is None (no caption, no figure numbering).

None
gutter str

Typst column-gutter value (e.g. "1em", "5mm"). The default is "1em".

'1em'
subcaptions list of str or None

Per-figure captions. When provided, each image is wrapped in its own figure() block inside the grid. The list length must equal len(paths). Can be combined with caption for an overall grid caption. The default is None.

None
rows_per_page int or None

Maximum number of rows per page. When provided, the figures are split into multiple grids separated by #pagebreak(). The caption (if any) is placed on the last chunk only. The default is None (no splitting).

None

Raises:

Type Description
ValueError

If paths is empty, columns is an integer <= 0, or subcaptions length does not match paths.

Source code in src/bessaplots/typst_report.py
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
def add_figures(
    self,
    paths: list[str | Path],
    columns: int | list[str] = 1,
    caption: str | None = None,
    gutter: str = "1em",
    subcaptions: list[str] | None = None,
    rows_per_page: int | None = None,
) -> None:
    """
    Add a grid of figures to the report.

    Parameters
    ----------
    paths : list of str or Path
        Paths to the figure files (e.g. PDF or PNG).  Each path is
        resolved to an absolute POSIX path so the generated ``.typ``
        file works regardless of where it is written.
    columns : int or list of str, optional
        Number of equal-width columns (``int``) or explicit Typst
        column-width strings such as ``["1fr", "2fr"]``.
        The default is ``1``.
    caption : str or None, optional
        When provided, wraps the grid in a Typst ``figure`` block
        with this caption, enabling automatic figure numbering.
        The default is ``None`` (no caption, no figure numbering).
    gutter : str, optional
        Typst ``column-gutter`` value (e.g. ``"1em"``, ``"5mm"``).
        The default is ``"1em"``.
    subcaptions : list of str or None, optional
        Per-figure captions.  When provided, each image is wrapped
        in its own ``figure()`` block inside the grid.  The list
        length must equal ``len(paths)``.  Can be combined with
        *caption* for an overall grid caption.
        The default is ``None``.
    rows_per_page : int or None, optional
        Maximum number of rows per page.  When provided, the
        figures are split into multiple grids separated by
        ``#pagebreak()``.  The caption (if any) is placed on the
        last chunk only.  The default is ``None`` (no splitting).

    Raises
    ------
    ValueError
        If ``paths`` is empty, ``columns`` is an integer <= 0,
        or ``subcaptions`` length does not match ``paths``.
    """
    if not paths:
        raise ValueError("paths must be non-empty")
    if isinstance(columns, int) and columns <= 0:
        raise ValueError("columns must be a positive integer")
    if isinstance(columns, list) and len(columns) == 0:
        raise ValueError("columns list must be non-empty")
    if subcaptions is not None and len(subcaptions) != len(paths):
        raise ValueError(
            f"subcaptions length ({len(subcaptions)}) must equal "
            f"paths length ({len(paths)})"
        )

    if rows_per_page is not None:
        self._blocks.append(
            _split_figure_blocks(
                paths,
                columns,
                caption,
                gutter,
                subcaptions,
                rows_per_page,
            )
        )
    else:
        self._blocks.append(
            _figure_block(paths, columns, caption, gutter, subcaptions)
        )
add_paragraph(text: str) -> None ¤

Add a paragraph of text to the report.

Parameters:

Name Type Description Default
text str

Plain text or Typst markup passed through verbatim. Special Typst characters (#, [, ]) are not escaped; advanced users may include Typst markup directly.

required
Source code in src/bessaplots/typst_report.py
318
319
320
321
322
323
324
325
326
327
328
329
def add_paragraph(self, text: str) -> None:
    """
    Add a paragraph of text to the report.

    Parameters
    ----------
    text : str
        Plain text or Typst markup passed through verbatim.
        Special Typst characters (``#``, ``[``, ``]``) are not
        escaped; advanced users may include Typst markup directly.
    """
    self._blocks.append(text)
compile(typ_path: str | pathlib.Path, pdf_path: str | pathlib.Path | None = None) -> Path ¤

Compile a .typ file to PDF using the typst CLI.

Parameters:

Name Type Description Default
typ_path str or Path

Path to the .typ source file.

required
pdf_path str or Path or None

Output PDF path. None replaces the .typ suffix with .pdf. The default is None.

None

Returns:

Type Description
Path

Path to the compiled PDF.

Raises:

Type Description
RuntimeError

If the typst executable is not found or compilation fails.

Source code in src/bessaplots/typst_report.py
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
def compile(
    self,
    typ_path: str | Path,
    pdf_path: str | Path | None = None,
) -> Path:
    """
    Compile a ``.typ`` file to PDF using the ``typst`` CLI.

    Parameters
    ----------
    typ_path : str or Path
        Path to the ``.typ`` source file.
    pdf_path : str or Path or None, optional
        Output PDF path.  ``None`` replaces the ``.typ`` suffix with
        ``.pdf``.  The default is ``None``.

    Returns
    -------
    Path
        Path to the compiled PDF.

    Raises
    ------
    RuntimeError
        If the ``typst`` executable is not found or compilation
        fails.
    """
    typ = Path(typ_path).with_suffix(".typ")
    pdf = (
        typ.with_suffix(".pdf")
        if pdf_path is None
        else Path(pdf_path).with_suffix(".pdf")
    )
    try:
        result = subprocess.run(
            ["typst", "compile", "--root", "/", str(typ), str(pdf)],
            capture_output=True,
            text=True,
        )
    except FileNotFoundError:
        raise RuntimeError(
            "typst CLI not found. Install via "
            "https://github.com/typst/typst#installation"
        ) from None

    if result.returncode != 0:
        raise RuntimeError(f"typst compile failed:\n{result.stderr}")
    if result.stderr:
        logger.warning(result.stderr)

    logger.info(f"Compiled PDF: {pdf}")
    return pdf
save(stem: str | pathlib.Path) -> Path ¤

Write the .typ file and compile it to PDF.

Parameters:

Name Type Description Default
stem str or Path

Base name (without extension) for the output files. Produces <stem>.typ and <stem>.pdf.

required

Returns:

Type Description
Path

Path to the compiled PDF.

Source code in src/bessaplots/typst_report.py
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
def save(self, stem: str | Path) -> Path:
    """
    Write the ``.typ`` file and compile it to PDF.

    Parameters
    ----------
    stem : str or Path
        Base name (without extension) for the output files.
        Produces ``<stem>.typ`` and ``<stem>.pdf``.

    Returns
    -------
    Path
        Path to the compiled PDF.
    """
    stem = Path(stem)
    typ_path = self.write(stem.with_suffix(".typ"))
    return self.compile(typ_path)
write(path: str | pathlib.Path) -> Path ¤

Write the .typ source file.

Parameters:

Name Type Description Default
path str or Path

Destination path. The .typ extension is enforced regardless of what suffix is provided.

required

Returns:

Type Description
Path

Absolute path to the written .typ file.

Source code in src/bessaplots/typst_report.py
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
def write(self, path: str | Path) -> Path:
    """
    Write the ``.typ`` source file.

    Parameters
    ----------
    path : str or Path
        Destination path.  The ``.typ`` extension is enforced
        regardless of what suffix is provided.

    Returns
    -------
    Path
        Absolute path to the written ``.typ`` file.
    """
    resolved = Path(path).with_suffix(".typ")
    resolved.write_text(
        _render(
            self._blocks,
            self.title,
            self.author,
            self.date,
            self.paper_size,
        ),
        encoding="utf-8",
    )
    logger.info(f"Wrote Typst source: {resolved}")
    return resolved