"""
paragraph - Typeset one or multiple paragraphs.
"""
import io
from collections.abc import Sequence
from typing import Literal
from pygmt._typing import AnchorCode
from pygmt.alias import Alias, AliasSystem
from pygmt.clib import Session
from pygmt.exceptions import GMTValueError
from pygmt.helpers import (
_check_encoding,
build_arg_list,
fmt_docstring,
is_nonstr_iter,
non_ascii_to_octal,
)
__doctest_skip__ = ["paragraph"]
@fmt_docstring
def paragraph( # noqa: PLR0913
self,
x: float | str,
y: float | str,
text: str | Sequence[str],
parwidth: float | str,
linespacing: float | str,
font: str | None = None,
angle: float | None = None,
justify: AnchorCode | None = None,
fill: str | None = None,
pen: str | None = None,
alignment: Literal["left", "center", "right", "justified"] = "left",
verbose: Literal["quiet", "error", "warning", "timing", "info", "compat", "debug"]
| bool = False,
panel: int | Sequence[int] | bool = False,
transparency: float | Sequence[float] | bool | None = None,
):
r"""
Typeset one or multiple paragraphs.
This method typesets one or multiple paragraphs of text at a given position on the
figure. The text is flowed within a given paragraph width and with a specified line
spacing. The text can be aligned left, center, right, or justified.
Multiple paragraphs can be provided as a sequence of strings, where each string
represents a separate paragraph, or as a single string with a blank line (``\n\n``)
separating the paragraphs.
Full GMT docs at :gmt-docs:`text.html`.
Parameters
----------
x/y
The x, y coordinates of the paragraph.
text
The paragraph text to typeset. If a sequence of strings is provided, each string
is treated as a separate paragraph.
parwidth
The width of the paragraph.
linespacing
The spacing between lines.
font
The font of the text.
angle
The angle of the text.
justify
Set the alignment of the block of text, relative to the given x, y position.
Choose a :doc:`2-character justification code </techref/justification_codes>`.
fill
Set color for filling the paragraph box [Default is no fill].
pen
Set the pen used to draw a rectangle around the paragraph [Default is
``"0.25p,black,solid"``].
alignment
Set the alignment of the text. Valid values are ``"left"``, ``"center"``,
``"right"``, and ``"justified"``.
$verbose
$panel
$transparency
Examples
--------
>>> import pygmt
>>>
>>> fig = pygmt.Figure()
>>> fig.basemap(region=[0, 10, 0, 10], projection="X10c/10c", frame=True)
>>> fig.paragraph(
... x=4,
... y=4,
... text="This is a long paragraph. " * 10,
... parwidth="5c",
... linespacing="12p",
... font="12p",
... )
>>> fig.show()
"""
self._activate_figure()
_valid_alignments = ("left", "center", "right", "justified")
if alignment not in _valid_alignments:
raise GMTValueError(
alignment,
description="value for parameter 'alignment'",
choices=_valid_alignments,
)
aliasdict = AliasSystem(
F=[
Alias(font, name="font", prefix="+f"),
Alias(angle, name="angle", prefix="+a"),
Alias(justify, name="justify", prefix="+j"),
],
G=Alias(fill, name="fill"),
W=Alias(pen, name="pen"),
).add_common(
V=verbose,
c=panel,
t=transparency,
)
aliasdict.merge({"M": True})
confdict = {}
# Prepare the text string that will be passed to an io.StringIO object.
# Multiple paragraphs are separated by a blank line "\n\n".
_textstr: str = "\n\n".join(text) if is_nonstr_iter(text) else str(text)
if _textstr == "":
raise GMTValueError(
text,
description="text",
reason="'text' must be a non-empty string or sequence of strings.",
)
# Check the encoding of the text string and convert it to octal if necessary.
if (encoding := _check_encoding(_textstr)) != "ascii":
_textstr = non_ascii_to_octal(_textstr, encoding=encoding)
confdict["PS_CHAR_ENCODING"] = encoding
with Session() as lib:
with io.StringIO() as buffer: # Prepare the StringIO input.
buffer.write(f"> {x} {y} {linespacing} {parwidth} {alignment[0]}\n")
buffer.write(_textstr)
with lib.virtualfile_in(data=buffer) as vfile:
lib.call_module(
"text",
args=build_arg_list(aliasdict, infile=vfile, confdict=confdict),
)