Skip to content

Commit 942f2f6

Browse files
committed
feat: add justify, indent, alignment options
to MarkupText fixes #34
1 parent 903a434 commit 942f2f6

File tree

5 files changed

+165
-3
lines changed

5 files changed

+165
-3
lines changed

manimpango/cmanimpango.pyx

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from xml.sax.saxutils import escape
22
from .utils import *
3-
3+
from .enums import Alignment
44
class TextSetting:
55
"""Formatting for slices of a :class:`manim.mobject.svg.text_mobject.Text` object."""
66
def __init__(
@@ -138,22 +138,28 @@ class MarkupUtils:
138138
slant: str,
139139
weight: str,
140140
size: int,
141-
line_spacing: int,
141+
_: int, # for some there was a keyword here.
142142
disable_liga: bool,
143143
file_name: str,
144144
START_X: int,
145145
START_Y: int,
146146
width: int,
147147
height: int,
148+
*, # keyword only arguments below
149+
justify: bool = None,
150+
indent: float = None,
151+
line_spacing: float = None,
152+
alignment: Alignment = None
148153
) -> int:
149154
"""Render an SVG file from a :class:`manim.mobject.svg.text_mobject.MarkupText` object."""
150155
cdef cairo_surface_t* surface
151156
cdef cairo_t* context
152157
cdef PangoFontDescription* font_desc
153158
cdef PangoLayout* layout
154159
cdef cairo_status_t status
155-
cdef double width_layout = width
160+
cdef double width_layout = width - 50
156161
cdef double font_size = size
162+
cdef int temp_int # a temporary C integer for conversion
157163

158164
file_name_bytes = file_name.encode("utf-8")
159165

@@ -182,8 +188,23 @@ class MarkupUtils:
182188
cairo_destroy(context)
183189
cairo_surface_destroy(surface)
184190
raise MemoryError("Pango.Layout can't be created from Cairo Context.")
191+
185192
pango_layout_set_width(layout, pango_units_from_double(width_layout))
186193

194+
if justify:
195+
pango_layout_set_justify(layout, justify)
196+
197+
if indent:
198+
temp_int = pango_units_from_double(indent)
199+
pango_layout_set_indent(layout, temp_int)
200+
201+
if line_spacing:
202+
# Typical values are: 0, 1, 1.5, 2.
203+
pango_layout_set_line_spacing(layout, line_spacing)
204+
205+
if alignment:
206+
pango_layout_set_alignment(layout, alignment.value)
207+
187208
font_desc = pango_font_description_new()
188209
if font_desc==NULL:
189210
cairo_destroy(context)

manimpango/enums.pyx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,21 @@ class Variant(Enum):
9090
"""
9191
NORMAL = PANGO_VARIANT_NORMAL
9292
SMALL_CAPS = PANGO_VARIANT_SMALL_CAPS
93+
94+
class Alignment(Enum):
95+
"""
96+
An enumeration specifying alignment.
97+
98+
Attributes
99+
----------
100+
101+
NORMAL :
102+
A normal font.
103+
104+
SMALL_CAPS :
105+
A font with the lower case characters replaced by smaller variants
106+
of the capital characters.
107+
"""
108+
LEFT = PANGO_ALIGN_LEFT
109+
CENTER = PANGO_ALIGN_CENTER
110+
RIGHT = PANGO_ALIGN_RIGHT

manimpango/pango.pxd

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ cdef extern from "pango/pangocairo.h":
3737
PANGO_WRAP_WORD
3838
PANGO_WRAP_CHAR
3939
PANGO_WRAP_WORD_CHAR
40+
ctypedef enum PangoAlignment:
41+
PANGO_ALIGN_LEFT
42+
PANGO_ALIGN_CENTER
43+
PANGO_ALIGN_RIGHT
44+
4045
PangoLayout* pango_cairo_create_layout(cairo_t* cr)
4146
void pango_cairo_show_layout(
4247
cairo_t* cr,
@@ -121,3 +126,19 @@ cdef extern from "pango/pangocairo.h":
121126
const char* pango_font_family_get_name(
122127
PangoFontFamily *family
123128
)
129+
void pango_layout_set_justify(
130+
PangoLayout *layout,
131+
gboolean justify
132+
)
133+
void pango_layout_set_indent(
134+
PangoLayout *layout,
135+
int indent
136+
)
137+
void pango_layout_set_line_spacing(
138+
PangoLayout *layout,
139+
float factor
140+
)
141+
void pango_layout_set_alignment(
142+
PangoLayout *layout,
143+
PangoAlignment alignment
144+
)

tests/_manim.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# -*- coding: utf-8 -*-
2+
"""This file contains helpers for the tests copied and modified
3+
from Manim.
4+
"""
5+
6+
import os
7+
from pathlib import Path
8+
9+
from manimpango import Alignment, MarkupUtils
10+
11+
12+
class MarkupText:
13+
def __init__(
14+
self,
15+
text: str,
16+
*,
17+
size: int = 1,
18+
line_spacing: int = -1,
19+
font: str = None,
20+
slant: str = "NORMAL",
21+
weight: str = "NORMAL",
22+
tab_width: int = 4,
23+
disable_ligatures: bool = False,
24+
justify: bool = None,
25+
indent: float = None,
26+
alignment: Alignment = None,
27+
# for the tests
28+
filename: str = "test.svg",
29+
**kwargs,
30+
):
31+
self.text = text
32+
self.size = size
33+
self.line_spacing = line_spacing
34+
self.font = font
35+
self.slant = slant
36+
self.weight = weight
37+
self.tab_width = tab_width
38+
self.filename = filename
39+
self.original_text = text
40+
self.disable_ligatures = disable_ligatures
41+
42+
self.justify = justify
43+
self.indent = indent
44+
self.alignment = alignment
45+
if not MarkupUtils.validate(self.text):
46+
raise ValueError(
47+
f"Pango cannot parse your markup in {self.text}. "
48+
"Please check for typos, unmatched tags or unescaped "
49+
"special chars like < and &."
50+
)
51+
52+
if self.line_spacing == -1:
53+
self.line_spacing = self.size + self.size * 0.3
54+
else:
55+
self.line_spacing = self.size + self.size * self.line_spacing
56+
57+
self.text2svg()
58+
59+
def text2svg(self):
60+
"""Convert the text to SVG using Pango."""
61+
size = self.size * 10
62+
line_spacing = self.line_spacing * 10
63+
dir_name = Path(self.filename).parent
64+
disable_liga = self.disable_ligatures
65+
if not os.path.exists(dir_name):
66+
os.makedirs(dir_name)
67+
file_name = self.filename
68+
return MarkupUtils.text2svg(
69+
f"{self.text}",
70+
self.font,
71+
self.slant,
72+
self.weight,
73+
size,
74+
line_spacing,
75+
disable_liga,
76+
file_name,
77+
20,
78+
20,
79+
600, # width
80+
400, # height
81+
justify=self.justify,
82+
indent=self.indent,
83+
line_spacing=self.line_spacing,
84+
alignment=self.alignment,
85+
)
86+
87+
def __repr__(self):
88+
return f"MarkupText({repr(self.original_text)})"

tests/test_markup.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,18 @@
11
# -*- coding: utf-8 -*-
2+
3+
ipsum_text = (
4+
"Lorem ipsum dolor sit amet, consectetur adipiscing elit,"
5+
"sed do eiusmod tempor incididunt ut labore et dolore"
6+
"magna aliqua. Ut enim ad minim veniam, quis nostrud"
7+
"exercitation ullamco laboris nisi ut aliquip"
8+
"ex ea commodo consequat. Duis aute irure dolor"
9+
"in reprehenderit in voluptate velit esse cillum"
10+
"dolore eu fugiat nulla pariatur. Excepteur sint"
11+
"occaecat cupidatat non proident, sunt in culpa qui"
12+
"officia deserunt mollit anim id est laborum."
13+
)
14+
15+
216
def test_good_markup():
317
import manimpango # noqa: F401
418

0 commit comments

Comments
 (0)