Skip to content

Commit

Permalink
Figure.paragraph
Browse files Browse the repository at this point in the history
  • Loading branch information
seisman committed Dec 24, 2024
1 parent 5a3a290 commit bbb0de1
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 0 deletions.
1 change: 1 addition & 0 deletions pygmt/figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@ def _repr_html_(self):
legend,
logo,
meca,
paragraph,
plot,
plot3d,
psconvert,
Expand Down
1 change: 1 addition & 0 deletions pygmt/src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
from pygmt.src.makecpt import makecpt
from pygmt.src.meca import meca
from pygmt.src.nearneighbor import nearneighbor
from pygmt.src.paragraph import paragraph
from pygmt.src.plot import plot
from pygmt.src.plot3d import plot3d
from pygmt.src.project import project
Expand Down
101 changes: 101 additions & 0 deletions pygmt/src/paragraph.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
"""
paragraph - Typeset one or multiple paragraphs.
"""

import io
from collections.abc import Sequence
from typing import Literal

from pygmt._typing import AnchorCode
from pygmt.clib import Session
from pygmt.helpers import (
_check_encoding,
build_arg_list,
is_nonstr_iter,
non_ascii_to_octal,
)


def _parse_option_f_upper(
font: float | str | None, angle: float | None, justify: AnchorCode | None
) -> str | None:
"""
Parse the font, angle, and justification arguments and return the string to be
appened to the module options.
Examples
--------
>>> _parse_font_angle_justify(None, None, None)
>>> _parse_font_angle_justify("10p", None, None)
'+f10p'
>>> _parse_font_angle_justify(None, 45, None)
'+a45'
>>> _parse_font_angle_justify(None, None, "CM")
'+jCM'
>>> _parse_font_angle_justify("10p,Helvetica-Bold", 45, "CM")
'+f10p,Helvetica-Bold+a45+jCM'
"""
args = ((font, "+f"), (angle, "+a"), (justify, "+j"))
if all(arg is None for arg, _ in args):
return None
return "".join(f"{flag}{arg}" for arg, flag in args if arg is not None)

Check warning on line 41 in pygmt/src/paragraph.py

View check run for this annotation

Codecov / codecov/patch

pygmt/src/paragraph.py#L41

Added line #L41 was not covered by tests


def paragraph(
self,
x: float | str,
y: float | str,
text: str | Sequence[str],
parwidth: float | str,
linespacing: float | str,
font: float | str | None = None,
angle: float | None = None,
justify: AnchorCode | None = None,
alignment: Literal["left", "center", "right", "justified"] = "left",
):
"""
Typeset one or multiple paragraphs.
Parameters
----------
x/y
The x, y coordinates of the paragraph.
text
The paragraph text to typeset.
parwidth
The width of the paragraph.
linespacing
The spacing between lines.
font
The font of the text.
angle
The angle of the text.
justify
The justification of the block of text, relative to the given x, y position.
alignment
The alignment of the text. Valid values are ``"left"``, ``"center"``,
``"right"``, and ``"justified"``.
"""
self._preprocess()

# Initialize a stringio object for storing the data input.
stringio = io.StringIO()
# The header line.
stringio.write(f"> {x} {y} {linespacing} {parwidth} {alignment[0]}\n")
# The text. Multiple paragraphs are separated by a blank line.
text_in_stringio = "\n\n".join(text) if is_nonstr_iter(text) else text
encoding = _check_encoding(text_in_stringio)
stringio.write(non_ascii_to_octal(text_in_stringio, encoding=encoding))

confdict = {}
if encoding not in {"ascii", "ISOLatin1+"}:
confdict["PS_CHAR_ENCODING"] = encoding

Check warning on line 92 in pygmt/src/paragraph.py

View check run for this annotation

Codecov / codecov/patch

pygmt/src/paragraph.py#L92

Added line #L92 was not covered by tests

# Prepare the keyword dictionary for the module options
kwdict = {"M": True, "F": _parse_option_f_upper(font, angle, justify)}

with Session() as lib:
with lib.virtualfile_from_stringio(stringio) as vfile:
lib.call_module(
"text", args=build_arg_list(kwdict, infile=vfile, confdict=confdict)
)
67 changes: 67 additions & 0 deletions pygmt/tests/test_paragraph.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"""
Tests for Figure.paragraph.
"""

import pytest
from pygmt import Figure


@pytest.mark.mpl_image_compare
def test_paragraph():
"""
Test typesetting a single paragraph.
"""
fig = 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",
)
return fig


@pytest.mark.mpl_image_compare
def test_paragraph_multiple_paragraphs_list():
"""
Test typesetting a single paragraph.
"""
fig = Figure()
fig.basemap(region=[0, 10, 0, 10], projection="X10c/10c", frame=True)
fig.paragraph(
x=4,
y=4,
text=[
"This is the first paragraph. " * 5,
"This is the second paragraph. " * 5,
],
parwidth="5c",
linespacing="12p",
)
return fig


@pytest.mark.mpl_image_compare
def test_paragraph_multiple_paragraphs_blankline():
"""
Test typesetting a single paragraph.
"""
text = """
This is the first paragraph.
This is the first paragraph.
This is the first paragraph.
This is the first paragraph.
This is the first paragraph.
This is the second paragraph.
This is the second paragraph.
This is the second paragraph.
This is the second paragraph.
This is the second paragraph.
"""
fig = Figure()
fig.basemap(region=[0, 10, 0, 10], projection="X10c/10c", frame=True)
fig.paragraph(x=4, y=4, text=text, parwidth="5c", linespacing="12p")
return fig

0 comments on commit bbb0de1

Please sign in to comment.