manimpango 0.3.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ManimPango-0.3.0 → ManimPango-0.4.0}/MANIFEST.in +1 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/ManimPango.egg-info/PKG-INFO +7 -7
- {ManimPango-0.3.0 → ManimPango-0.4.0}/ManimPango.egg-info/SOURCES.txt +1 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/PKG-INFO +7 -7
- {ManimPango-0.3.0 → ManimPango-0.4.0}/docs/RELEASE.rst +6 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/manimpango/_version.py +1 -1
- {ManimPango-0.3.0 → ManimPango-0.4.0}/manimpango/cmanimpango.pyx +17 -10
- ManimPango-0.4.0/pyproject.toml +11 -0
- ManimPango-0.4.0/requirements-dev.txt +4 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/setup.cfg +1 -1
- {ManimPango-0.3.0 → ManimPango-0.4.0}/setup.py +8 -7
- ManimPango-0.4.0/tests/_manim.py +278 -0
- ManimPango-0.4.0/tests/test_colors.py +26 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/tests/test_fonts.py +67 -37
- {ManimPango-0.3.0 → ManimPango-0.4.0}/tests/test_list_fonts.py +1 -1
- ManimPango-0.3.0/pyproject.toml +0 -2
- ManimPango-0.3.0/requirements-dev.txt +0 -5
- ManimPango-0.3.0/tests/_manim.py +0 -106
- {ManimPango-0.3.0 → ManimPango-0.4.0}/.coveragerc +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/CODE_OF_CONDUCT.md +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/LICENSE +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/ManimPango.egg-info/dependency_links.txt +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/ManimPango.egg-info/not-zip-safe +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/ManimPango.egg-info/top_level.txt +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/README.md +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/docs/_static/favicon.ico +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/docs/_templates/autosummary/class.rst +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/docs/_templates/autosummary/module.rst +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/docs/conf.py +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/docs/index.rst +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/docs/make.bat +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/docs/reference.rst +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/manimpango/__init__.py +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/manimpango/cairo.pxd +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/manimpango/cmanimpango.pxd +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/manimpango/enums.pyx +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/manimpango/glib.pxd +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/manimpango/pango.pxd +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/manimpango/register_font.pxd +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/manimpango/register_font.pyx +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/manimpango/utils.py +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/tests/__init__.py +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/tests/conftest.py +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/tests/fonts/AdobeVFPrototype.ttf +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/tests/fonts/BungeeColor-Regular_colr_Windows.ttf +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/tests/fonts/BungeeOutline-Regular.ttf +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/tests/fonts/LICENSE.txt +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/tests/fonts/MaShanZheng-Regular.ttf +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/tests/svg_tester.py +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/tests/test_markup.py +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/tests/test_utils.py +0 -0
- {ManimPango-0.3.0 → ManimPango-0.4.0}/tests/test_version.py +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ManimPango
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Bindings for Pango for using with Manim.
|
|
5
5
|
Home-page: https://manimpango.manim.community/
|
|
6
6
|
Author: Naveen M K
|
|
7
|
-
Author-email: naveen@
|
|
7
|
+
Author-email: naveen@manim.community
|
|
8
8
|
Maintainer: The Manim Community Developers
|
|
9
|
-
License:
|
|
9
|
+
License: GPL-3.0-or-later
|
|
10
10
|
Project-URL: Documentation, https://manimpango.manim.community/
|
|
11
11
|
Project-URL: Source, https://github.com/ManimCommunity/manimpango
|
|
12
12
|
Project-URL: Release notes, https://github.com/ManimCommunity/ManimPango/releases/
|
|
@@ -193,14 +193,14 @@ Platform: Linux
|
|
|
193
193
|
Platform: macOS
|
|
194
194
|
Platform: Windows
|
|
195
195
|
Classifier: Programming Language :: Python :: 3
|
|
196
|
-
Classifier: Programming Language :: Python :: 3.6
|
|
197
196
|
Classifier: Programming Language :: Python :: 3.7
|
|
198
197
|
Classifier: Programming Language :: Python :: 3.8
|
|
199
198
|
Classifier: Programming Language :: Python :: 3.9
|
|
200
|
-
Classifier:
|
|
199
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
200
|
+
Classifier: Development Status :: 4 - Beta
|
|
201
201
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
202
|
-
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
202
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
203
203
|
Classifier: Operating System :: OS Independent
|
|
204
204
|
Classifier: Programming Language :: Cython
|
|
205
|
-
Requires-Python: >=3.
|
|
205
|
+
Requires-Python: >=3.7
|
|
206
206
|
Description-Content-Type: text/markdown
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ManimPango
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Bindings for Pango for using with Manim.
|
|
5
5
|
Home-page: https://manimpango.manim.community/
|
|
6
6
|
Author: Naveen M K
|
|
7
|
-
Author-email: naveen@
|
|
7
|
+
Author-email: naveen@manim.community
|
|
8
8
|
Maintainer: The Manim Community Developers
|
|
9
|
-
License:
|
|
9
|
+
License: GPL-3.0-or-later
|
|
10
10
|
Project-URL: Documentation, https://manimpango.manim.community/
|
|
11
11
|
Project-URL: Source, https://github.com/ManimCommunity/manimpango
|
|
12
12
|
Project-URL: Release notes, https://github.com/ManimCommunity/ManimPango/releases/
|
|
@@ -193,14 +193,14 @@ Platform: Linux
|
|
|
193
193
|
Platform: macOS
|
|
194
194
|
Platform: Windows
|
|
195
195
|
Classifier: Programming Language :: Python :: 3
|
|
196
|
-
Classifier: Programming Language :: Python :: 3.6
|
|
197
196
|
Classifier: Programming Language :: Python :: 3.7
|
|
198
197
|
Classifier: Programming Language :: Python :: 3.8
|
|
199
198
|
Classifier: Programming Language :: Python :: 3.9
|
|
200
|
-
Classifier:
|
|
199
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
200
|
+
Classifier: Development Status :: 4 - Beta
|
|
201
201
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
202
|
-
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
202
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
203
203
|
Classifier: Operating System :: OS Independent
|
|
204
204
|
Classifier: Programming Language :: Cython
|
|
205
|
-
Requires-Python: >=3.
|
|
205
|
+
Requires-Python: >=3.7
|
|
206
206
|
Description-Content-Type: text/markdown
|
|
@@ -31,13 +31,18 @@ This means older version would have a backport of bugs fixes.
|
|
|
31
31
|
Here, ``-s`` is used to sign the tag with gpg so that users
|
|
32
32
|
can later verify it, and a tag shouldn't be created with
|
|
33
33
|
signing because Github shows it unverified.
|
|
34
|
+
|
|
34
35
|
.. important::
|
|
35
36
|
|
|
36
37
|
The message should include the changelog of the release.
|
|
38
|
+
There is a github actions which will creates a draft `release`_
|
|
39
|
+
with the changelog. You can edit them and copy it to the tag you
|
|
40
|
+
create.
|
|
37
41
|
|
|
38
42
|
8. Push the tag to remote.
|
|
39
43
|
|
|
40
44
|
9. Go to `Github`_, and `draft a new release`_ with the same tag pushed.
|
|
45
|
+
You can copy the same changelog you copied when you created the tag.
|
|
41
46
|
|
|
42
47
|
10. Check whether the CI uploads the wheels and the ``.tar.gz`` file to
|
|
43
48
|
PyPi.
|
|
@@ -50,3 +55,4 @@ This means older version would have a backport of bugs fixes.
|
|
|
50
55
|
.. _Github: https://github.com
|
|
51
56
|
.. _draft a new release: https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/managing-releases-in-a-repository#creating-a-release
|
|
52
57
|
.. _PyPi: https://pypi.org/project/manimpango/
|
|
58
|
+
.. _release: https://github.com/ManimCommunity/ManimPango/releases
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
__version__ = "0.
|
|
2
|
+
__version__ = "0.4.0"
|
|
@@ -13,14 +13,17 @@ class TextSetting:
|
|
|
13
13
|
font:str,
|
|
14
14
|
slant,
|
|
15
15
|
weight,
|
|
16
|
-
line_num=-1
|
|
16
|
+
line_num=-1,
|
|
17
|
+
color: str=None,
|
|
17
18
|
):
|
|
18
19
|
self.start = start
|
|
19
20
|
self.end = end
|
|
20
|
-
self.font = font
|
|
21
|
+
self.font = font
|
|
21
22
|
self.slant = slant
|
|
22
23
|
self.weight = weight
|
|
23
24
|
self.line_num = line_num
|
|
25
|
+
self.color = color
|
|
26
|
+
|
|
24
27
|
|
|
25
28
|
def text2svg(
|
|
26
29
|
settings:list,
|
|
@@ -79,11 +82,11 @@ def text2svg(
|
|
|
79
82
|
pango_layout_set_width(layout, pango_units_from_double(pango_width))
|
|
80
83
|
|
|
81
84
|
for setting in settings:
|
|
82
|
-
family = setting.font
|
|
85
|
+
family = setting.font.encode('utf-8')
|
|
83
86
|
style = PangoUtils.str2style(setting.slant)
|
|
84
87
|
weight = PangoUtils.str2weight(setting.weight)
|
|
88
|
+
color = setting.color
|
|
85
89
|
text_str = orig_text[setting.start : setting.end].replace("\n", " ")
|
|
86
|
-
text = text_str.encode('utf-8')
|
|
87
90
|
font_desc = pango_font_description_new()
|
|
88
91
|
if font_desc==NULL:
|
|
89
92
|
cairo_destroy(cr)
|
|
@@ -103,13 +106,17 @@ def text2svg(
|
|
|
103
106
|
cairo_move_to(cr,START_X + offset_x,START_Y + line_spacing * setting.line_num)
|
|
104
107
|
|
|
105
108
|
pango_cairo_update_layout(cr,layout)
|
|
109
|
+
markup = escape(text_str)
|
|
110
|
+
if color:
|
|
111
|
+
markup = (f"<span color='{color}'>{markup}</span>")
|
|
112
|
+
if MarkupUtils.validate(markup):
|
|
113
|
+
cairo_destroy(cr)
|
|
114
|
+
cairo_surface_destroy(surface)
|
|
115
|
+
g_object_unref(layout)
|
|
116
|
+
raise ValueError(f"Pango cannot recognize your color '{color}' for text '{text_str}'.")
|
|
106
117
|
if disable_liga:
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
markup_bytes = markup.encode('utf-8')
|
|
110
|
-
pango_layout_set_markup(layout, markup_bytes, -1)
|
|
111
|
-
else:
|
|
112
|
-
pango_layout_set_text(layout,text,-1)
|
|
118
|
+
markup = f"<span font_features='liga=0,dlig=0,clig=0,hlig=0'>{markup}</span>"
|
|
119
|
+
pango_layout_set_markup(layout, markup.encode('utf-8'), -1)
|
|
113
120
|
pango_cairo_show_layout(cr, layout)
|
|
114
121
|
pango_layout_get_size(layout,&temp_width,NULL)
|
|
115
122
|
offset_x += pango_units_to_double(temp_width)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools", "wheel", "Cython>=0.27"]
|
|
3
|
+
|
|
4
|
+
[tool.isort]
|
|
5
|
+
# from https://black.readthedocs.io/en/stable/compatible_configs.html
|
|
6
|
+
multi_line_output = 3
|
|
7
|
+
include_trailing_comma = true
|
|
8
|
+
force_grid_wrap = 0
|
|
9
|
+
use_parentheses = true
|
|
10
|
+
ensure_newline_before_comments = true
|
|
11
|
+
line_length = 88
|
|
@@ -90,8 +90,8 @@ class PKG_CONFIG:
|
|
|
90
90
|
return True
|
|
91
91
|
except Exception:
|
|
92
92
|
warnings.warn(
|
|
93
|
-
f"{self.pkg_config} doesn't exists or doesn't seem to work"
|
|
94
|
-
"We assume that you give the compiler flags using"
|
|
93
|
+
f"{self.pkg_config} doesn't exists or doesn't seem to work\n"
|
|
94
|
+
"We assume that you give the compiler flags using\n"
|
|
95
95
|
"corresponding environment variables.",
|
|
96
96
|
RuntimeWarning,
|
|
97
97
|
)
|
|
@@ -243,7 +243,7 @@ setup(
|
|
|
243
243
|
name=NAME,
|
|
244
244
|
version=MANIMPANGO_VERSION,
|
|
245
245
|
author="Naveen M K",
|
|
246
|
-
author_email="naveen@
|
|
246
|
+
author_email="naveen@manim.community",
|
|
247
247
|
maintainer="The Manim Community Developers",
|
|
248
248
|
url="https://manimpango.manim.community/",
|
|
249
249
|
description="Bindings for Pango for using with Manim.",
|
|
@@ -251,18 +251,19 @@ setup(
|
|
|
251
251
|
zip_safe=False,
|
|
252
252
|
long_description_content_type="text/markdown",
|
|
253
253
|
packages=["manimpango"],
|
|
254
|
-
python_requires=">=3.
|
|
254
|
+
python_requires=">=3.7",
|
|
255
255
|
platforms=["Linux", "macOS", "Windows"],
|
|
256
256
|
keywords=["cython", "pango", "cairo", "manim"],
|
|
257
|
+
license="GPL-3.0-or-later",
|
|
257
258
|
classifiers=[
|
|
258
259
|
"Programming Language :: Python :: 3",
|
|
259
|
-
"Programming Language :: Python :: 3.6",
|
|
260
260
|
"Programming Language :: Python :: 3.7",
|
|
261
261
|
"Programming Language :: Python :: 3.8",
|
|
262
262
|
"Programming Language :: Python :: 3.9",
|
|
263
|
-
"
|
|
263
|
+
"Programming Language :: Python :: 3.10",
|
|
264
|
+
"Development Status :: 4 - Beta",
|
|
264
265
|
"Programming Language :: Python :: 3 :: Only",
|
|
265
|
-
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
|
266
|
+
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
|
|
266
267
|
"Operating System :: OS Independent",
|
|
267
268
|
"Programming Language :: Cython",
|
|
268
269
|
],
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""This file contains helpers for the tests copied and modified
|
|
3
|
+
from Manim.
|
|
4
|
+
"""
|
|
5
|
+
import copy
|
|
6
|
+
import os
|
|
7
|
+
import re
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Sequence
|
|
10
|
+
|
|
11
|
+
from manimpango import Alignment, MarkupUtils, TextSetting, text2svg
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class MarkupText:
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
text: str,
|
|
18
|
+
*,
|
|
19
|
+
size: int = 1,
|
|
20
|
+
line_spacing: int = None,
|
|
21
|
+
font: str = None,
|
|
22
|
+
slant: str = "NORMAL",
|
|
23
|
+
weight: str = "NORMAL",
|
|
24
|
+
tab_width: int = 4,
|
|
25
|
+
disable_ligatures: bool = False,
|
|
26
|
+
justify: bool = None,
|
|
27
|
+
indent: float = None,
|
|
28
|
+
alignment: Alignment = None,
|
|
29
|
+
# for the tests
|
|
30
|
+
filename: str = "test.svg",
|
|
31
|
+
wrap_text: bool = True,
|
|
32
|
+
**kwargs,
|
|
33
|
+
):
|
|
34
|
+
self.text = text
|
|
35
|
+
self.size = size
|
|
36
|
+
self.line_spacing = line_spacing
|
|
37
|
+
self.font = font
|
|
38
|
+
self.slant = slant
|
|
39
|
+
self.weight = weight
|
|
40
|
+
self.tab_width = tab_width
|
|
41
|
+
self.filename = filename
|
|
42
|
+
self.original_text = text
|
|
43
|
+
self.disable_ligatures = disable_ligatures
|
|
44
|
+
|
|
45
|
+
self.justify = justify
|
|
46
|
+
self.indent = indent
|
|
47
|
+
self.alignment = alignment
|
|
48
|
+
self.wrap_text = wrap_text
|
|
49
|
+
if MarkupUtils.validate(self.text):
|
|
50
|
+
raise ValueError(
|
|
51
|
+
f"Pango cannot parse your markup in {self.text}. "
|
|
52
|
+
"Please check for typos, unmatched tags or unescaped "
|
|
53
|
+
"special chars like < and &."
|
|
54
|
+
)
|
|
55
|
+
self.text2svg()
|
|
56
|
+
|
|
57
|
+
def text2svg(self):
|
|
58
|
+
"""Convert the text to SVG using Pango."""
|
|
59
|
+
size = self.size * 10
|
|
60
|
+
dir_name = Path(self.filename).parent
|
|
61
|
+
disable_liga = self.disable_ligatures
|
|
62
|
+
if not os.path.exists(dir_name):
|
|
63
|
+
os.makedirs(dir_name)
|
|
64
|
+
file_name = self.filename
|
|
65
|
+
if self.wrap_text:
|
|
66
|
+
return MarkupUtils.text2svg(
|
|
67
|
+
f"{self.text}",
|
|
68
|
+
self.font,
|
|
69
|
+
self.slant,
|
|
70
|
+
self.weight,
|
|
71
|
+
size,
|
|
72
|
+
True, # stray positional argument
|
|
73
|
+
disable_liga,
|
|
74
|
+
file_name,
|
|
75
|
+
20,
|
|
76
|
+
20,
|
|
77
|
+
600, # width
|
|
78
|
+
400, # height
|
|
79
|
+
justify=self.justify,
|
|
80
|
+
indent=self.indent,
|
|
81
|
+
line_spacing=self.line_spacing,
|
|
82
|
+
alignment=self.alignment,
|
|
83
|
+
)
|
|
84
|
+
else:
|
|
85
|
+
return MarkupUtils.text2svg(
|
|
86
|
+
f"{self.text}",
|
|
87
|
+
self.font,
|
|
88
|
+
self.slant,
|
|
89
|
+
self.weight,
|
|
90
|
+
size,
|
|
91
|
+
True, # stray positional argument
|
|
92
|
+
disable_liga,
|
|
93
|
+
file_name,
|
|
94
|
+
20,
|
|
95
|
+
20,
|
|
96
|
+
600, # width
|
|
97
|
+
400, # height
|
|
98
|
+
justify=self.justify,
|
|
99
|
+
indent=self.indent,
|
|
100
|
+
line_spacing=self.line_spacing,
|
|
101
|
+
alignment=self.alignment,
|
|
102
|
+
pango_width=-1,
|
|
103
|
+
)
|
|
104
|
+
# -1 for no wrapping
|
|
105
|
+
# default is full width and then wrap.
|
|
106
|
+
|
|
107
|
+
def __repr__(self):
|
|
108
|
+
return f"MarkupText({repr(self.original_text)})"
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class Text:
|
|
112
|
+
def __init__(
|
|
113
|
+
self,
|
|
114
|
+
text: str,
|
|
115
|
+
fill_opacity: float = 1.0,
|
|
116
|
+
stroke_width: int = 0,
|
|
117
|
+
size: int = 1,
|
|
118
|
+
line_spacing: int = -1,
|
|
119
|
+
font: str = "",
|
|
120
|
+
slant: str = "NORMAL",
|
|
121
|
+
weight: str = "NORMAL",
|
|
122
|
+
gradient: tuple = None,
|
|
123
|
+
tab_width: int = 4,
|
|
124
|
+
disable_ligatures: bool = False,
|
|
125
|
+
filename: str = "text.svg",
|
|
126
|
+
color: str = "white",
|
|
127
|
+
**kwargs,
|
|
128
|
+
) -> None:
|
|
129
|
+
self.color = color
|
|
130
|
+
self.size = size
|
|
131
|
+
self.filename = filename
|
|
132
|
+
self.line_spacing = line_spacing
|
|
133
|
+
self.font = font
|
|
134
|
+
self.slant = slant
|
|
135
|
+
self.weight = weight
|
|
136
|
+
self.gradient = gradient
|
|
137
|
+
self.tab_width = tab_width
|
|
138
|
+
self.original_text = text
|
|
139
|
+
self.disable_ligatures = disable_ligatures
|
|
140
|
+
text_without_tabs = text
|
|
141
|
+
self.t2f = self.t2s = self.t2w = self.t2c = {}
|
|
142
|
+
if text.find("\t") != -1:
|
|
143
|
+
text_without_tabs = text.replace("\t", " " * self.tab_width)
|
|
144
|
+
self.text = text_without_tabs
|
|
145
|
+
if self.line_spacing == -1:
|
|
146
|
+
self.line_spacing = self.size + self.size * 0.3
|
|
147
|
+
else:
|
|
148
|
+
self.line_spacing = self.size + self.size * self.line_spacing
|
|
149
|
+
self.text2svg()
|
|
150
|
+
|
|
151
|
+
def _merge_settings(
|
|
152
|
+
self, left_setting: TextSetting, right_setting: TextSetting, args: Sequence[str]
|
|
153
|
+
) -> TextSetting:
|
|
154
|
+
contained = right_setting.end < left_setting.end
|
|
155
|
+
new_setting = copy.copy(left_setting) if contained else copy.copy(right_setting)
|
|
156
|
+
|
|
157
|
+
new_setting.start = right_setting.end if contained else left_setting.end
|
|
158
|
+
left_setting.end = right_setting.start
|
|
159
|
+
if not contained:
|
|
160
|
+
right_setting.end = new_setting.start
|
|
161
|
+
|
|
162
|
+
for arg in args:
|
|
163
|
+
left = getattr(left_setting, arg)
|
|
164
|
+
right = getattr(right_setting, arg)
|
|
165
|
+
default = getattr(self, arg)
|
|
166
|
+
if left != default and getattr(right_setting, arg) != default:
|
|
167
|
+
raise ValueError(
|
|
168
|
+
"Ambiguous style for text"
|
|
169
|
+
f"'{self.text[right_setting.start:right_setting.end]}':"
|
|
170
|
+
f"'{arg}' cannot be both '{left}' and '{right}'."
|
|
171
|
+
)
|
|
172
|
+
setattr(right_setting, arg, left if left != default else right)
|
|
173
|
+
return new_setting
|
|
174
|
+
|
|
175
|
+
def text2settings(self):
|
|
176
|
+
"""Internally used function. Converts the texts and styles
|
|
177
|
+
to a setting for parsing."""
|
|
178
|
+
settings = []
|
|
179
|
+
t2xs = [
|
|
180
|
+
(self.t2f, "font"),
|
|
181
|
+
(self.t2s, "slant"),
|
|
182
|
+
(self.t2w, "weight"),
|
|
183
|
+
(self.t2c, "color"),
|
|
184
|
+
]
|
|
185
|
+
t2xwords = {
|
|
186
|
+
*self.t2f.keys(),
|
|
187
|
+
*self.t2s.keys(),
|
|
188
|
+
*self.t2w.keys(),
|
|
189
|
+
*self.t2c.keys(),
|
|
190
|
+
}
|
|
191
|
+
for word in t2xwords:
|
|
192
|
+
setting_args = {
|
|
193
|
+
arg: t2x[word] if word in t2x else getattr(self, arg)
|
|
194
|
+
for t2x, arg in t2xs
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
for start, end in self.find_indexes(word, self.text):
|
|
198
|
+
settings.append(TextSetting(start, end, **setting_args))
|
|
199
|
+
|
|
200
|
+
# Handle overlaps
|
|
201
|
+
setting_args = {arg: getattr(self, arg) for _, arg in t2xs}
|
|
202
|
+
|
|
203
|
+
settings.sort(key=lambda setting: setting.start)
|
|
204
|
+
index = 0
|
|
205
|
+
for setting in settings:
|
|
206
|
+
index += 1
|
|
207
|
+
if index == len(settings):
|
|
208
|
+
break
|
|
209
|
+
|
|
210
|
+
next_setting = settings[index]
|
|
211
|
+
if setting.end > next_setting.start:
|
|
212
|
+
new_setting = self._merge_settings(setting, next_setting, setting_args)
|
|
213
|
+
new_index = index
|
|
214
|
+
while (
|
|
215
|
+
new_index < len(settings)
|
|
216
|
+
and settings[new_index].start < new_setting.start
|
|
217
|
+
):
|
|
218
|
+
new_index += 1
|
|
219
|
+
settings.insert(new_index, new_setting)
|
|
220
|
+
|
|
221
|
+
# Set all text settings (default font, slant, weight)
|
|
222
|
+
temp_settings = settings.copy()
|
|
223
|
+
start = 0
|
|
224
|
+
for setting in settings:
|
|
225
|
+
if setting.start != start:
|
|
226
|
+
temp_settings.append(TextSetting(start, setting.start, **setting_args))
|
|
227
|
+
start = setting.end
|
|
228
|
+
if start != len(self.text):
|
|
229
|
+
temp_settings.append(TextSetting(start, len(self.text), **setting_args))
|
|
230
|
+
settings = sorted(temp_settings, key=lambda setting: setting.start)
|
|
231
|
+
|
|
232
|
+
if re.search(r"\n", self.text):
|
|
233
|
+
line_num = 0
|
|
234
|
+
for start, end in self.find_indexes("\n", self.text):
|
|
235
|
+
for setting in settings:
|
|
236
|
+
if setting.line_num == -1:
|
|
237
|
+
setting.line_num = line_num
|
|
238
|
+
if start < setting.end:
|
|
239
|
+
line_num += 1
|
|
240
|
+
new_setting = copy.copy(setting)
|
|
241
|
+
setting.end = end
|
|
242
|
+
new_setting.start = end
|
|
243
|
+
new_setting.line_num = line_num
|
|
244
|
+
settings.append(new_setting)
|
|
245
|
+
settings.sort(key=lambda setting: setting.start)
|
|
246
|
+
break
|
|
247
|
+
for setting in settings:
|
|
248
|
+
if setting.line_num == -1:
|
|
249
|
+
setting.line_num = 0
|
|
250
|
+
return settings
|
|
251
|
+
|
|
252
|
+
def text2svg(self):
|
|
253
|
+
"""Internally used function.
|
|
254
|
+
Convert the text to SVG using Pango
|
|
255
|
+
"""
|
|
256
|
+
size = self.size * 10
|
|
257
|
+
line_spacing = self.line_spacing * 10
|
|
258
|
+
dir_name = Path(self.filename).parent
|
|
259
|
+
disable_liga = self.disable_ligatures
|
|
260
|
+
if not os.path.exists(dir_name):
|
|
261
|
+
os.makedirs(dir_name)
|
|
262
|
+
file_name = self.filename
|
|
263
|
+
settings = self.text2settings()
|
|
264
|
+
width = 600
|
|
265
|
+
height = 400
|
|
266
|
+
|
|
267
|
+
return text2svg(
|
|
268
|
+
settings,
|
|
269
|
+
size,
|
|
270
|
+
line_spacing,
|
|
271
|
+
disable_liga,
|
|
272
|
+
file_name,
|
|
273
|
+
30,
|
|
274
|
+
30,
|
|
275
|
+
width,
|
|
276
|
+
height,
|
|
277
|
+
self.text,
|
|
278
|
+
)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from . import CASES_DIR
|
|
7
|
+
from ._manim import Text
|
|
8
|
+
from .svg_tester import SVGStyleTester
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_invalid_color_fail(tmpdir):
|
|
12
|
+
loc = Path(tmpdir, "test.svg")
|
|
13
|
+
assert not loc.exists()
|
|
14
|
+
with pytest.raises(ValueError):
|
|
15
|
+
Text("color", color="invalid", filename=str(loc))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_colors(tmpdir):
|
|
19
|
+
expected = Path(CASES_DIR, "color_red.svg")
|
|
20
|
+
loc = Path(tmpdir, "test.svg")
|
|
21
|
+
assert not loc.exists()
|
|
22
|
+
Text("color", color="red", filename=str(loc))
|
|
23
|
+
assert loc.exists()
|
|
24
|
+
s = SVGStyleTester(gotSVG=loc, expectedSVG=expected)
|
|
25
|
+
assert len(s.got_svg_style) == len(s.expected_svg_style)
|
|
26
|
+
assert s.got_svg_style == s.expected_svg_style
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
+
import os
|
|
3
|
+
import subprocess
|
|
2
4
|
import sys
|
|
5
|
+
import textwrap
|
|
6
|
+
from contextlib import contextmanager
|
|
3
7
|
from pathlib import Path
|
|
4
8
|
from shutil import copyfile
|
|
5
9
|
|
|
6
|
-
import manim
|
|
7
10
|
import pytest
|
|
8
11
|
|
|
9
12
|
import manimpango
|
|
@@ -11,13 +14,31 @@ import manimpango
|
|
|
11
14
|
from . import FONT_DIR
|
|
12
15
|
from ._manim import MarkupText
|
|
13
16
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
|
|
18
|
+
@contextmanager
|
|
19
|
+
def register_font(font_file: Path):
|
|
20
|
+
font_file = os.fspath(font_file)
|
|
21
|
+
init = manimpango.list_fonts()
|
|
22
|
+
assert manimpango.register_font(font_file), "Invalid Font possibly."
|
|
23
|
+
final = manimpango.list_fonts()
|
|
24
|
+
yield list(set(final) - set(init))[0]
|
|
25
|
+
assert manimpango.unregister_font(font_file), "Can't unregister Font"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def font_list():
|
|
29
|
+
_t = [
|
|
30
|
+
(FONT_DIR / "AdobeVFPrototype.ttf").absolute(),
|
|
31
|
+
(FONT_DIR / "BungeeColor-Regular_colr_Windows.ttf").absolute(),
|
|
32
|
+
(FONT_DIR / "MaShanZheng-Regular.ttf").absolute(),
|
|
33
|
+
]
|
|
34
|
+
_d = {}
|
|
35
|
+
for i in _t:
|
|
36
|
+
with register_font(i) as f:
|
|
37
|
+
_d[i] = f
|
|
38
|
+
return _d
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
font_lists_dict = font_list()
|
|
21
42
|
|
|
22
43
|
|
|
23
44
|
def test_unicode_font_name(tmpdir):
|
|
@@ -27,46 +48,54 @@ def test_unicode_font_name(tmpdir):
|
|
|
27
48
|
assert manimpango.unregister_font(final_font)
|
|
28
49
|
|
|
29
50
|
|
|
30
|
-
@pytest.mark.parametrize("font_name",
|
|
31
|
-
def
|
|
51
|
+
@pytest.mark.parametrize("font_name", font_lists_dict)
|
|
52
|
+
def test_register_and_unregister_font(font_name):
|
|
32
53
|
intial = manimpango.list_fonts()
|
|
33
54
|
assert manimpango.register_font(str(font_name)), "Invalid Font possibly."
|
|
34
55
|
final = manimpango.list_fonts()
|
|
35
56
|
assert intial != final
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
@pytest.mark.parametrize("font_name", font_lists.values())
|
|
39
|
-
def test_warning(capfd, font_name):
|
|
40
|
-
print(font_name)
|
|
41
|
-
manim.Text("Testing", font=font_name)
|
|
42
|
-
captured = capfd.readouterr()
|
|
43
|
-
assert "Pango-WARNING **" not in captured.err, "Looks like pango raised a warning?"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
@pytest.mark.skipif(
|
|
47
|
-
sys.platform.startswith("linux"), reason="unsupported api for linux"
|
|
48
|
-
)
|
|
49
|
-
@pytest.mark.parametrize("font_name", font_lists)
|
|
50
|
-
def test_unregister_font(font_name):
|
|
51
|
-
intial = manimpango.list_fonts()
|
|
52
|
-
assert manimpango.unregister_font(str(font_name)), "Failed to unregister the font"
|
|
53
|
-
final = manimpango.list_fonts()
|
|
54
|
-
assert intial != final
|
|
57
|
+
assert manimpango.unregister_font(os.fspath(font_name)), "Can't unregister font."
|
|
58
|
+
assert intial == manimpango.list_fonts()
|
|
55
59
|
|
|
56
60
|
|
|
57
61
|
@pytest.mark.skipif(
|
|
58
|
-
sys.platform.startswith("linux"), reason="
|
|
62
|
+
sys.platform.startswith("linux"), reason="no warning are raised for linux"
|
|
59
63
|
)
|
|
60
|
-
@pytest.mark.parametrize("font_name",
|
|
61
|
-
def
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
@pytest.mark.parametrize("font_file,font_name", font_lists_dict.items())
|
|
65
|
+
def test_warning(font_file, font_name):
|
|
66
|
+
# this tests need to be run in a separate subprocess because
|
|
67
|
+
# of fontmap cache in Pango.
|
|
68
|
+
command = textwrap.dedent(
|
|
69
|
+
f"""\
|
|
70
|
+
from tests import set_dll_search_path
|
|
71
|
+
set_dll_search_path() # this is needed on Windows to run
|
|
72
|
+
import manimpango
|
|
73
|
+
import os
|
|
74
|
+
from tests._manim import Text
|
|
75
|
+
font_file = r'{os.fspath(font_file)}'
|
|
76
|
+
font_name = r'{font_name}'
|
|
77
|
+
intial = manimpango.list_fonts()
|
|
78
|
+
manimpango.register_font(os.fspath(font_file))
|
|
79
|
+
final = manimpango.list_fonts()
|
|
80
|
+
assert intial != final
|
|
81
|
+
Text("Testing", font=font_name)
|
|
82
|
+
manimpango.unregister_font(os.fspath(font_file))
|
|
83
|
+
"""
|
|
84
|
+
)
|
|
85
|
+
a = subprocess.run(
|
|
86
|
+
[sys.executable, "-c", command],
|
|
87
|
+
check=True,
|
|
88
|
+
stderr=subprocess.PIPE,
|
|
89
|
+
cwd=Path(__file__).parent.parent,
|
|
90
|
+
)
|
|
91
|
+
captured = a.stderr.decode()
|
|
92
|
+
assert "Pango-WARNING **" not in captured, "Looks like Pango raised a warning?"
|
|
64
93
|
|
|
65
94
|
|
|
66
95
|
@pytest.mark.skipif(
|
|
67
96
|
sys.platform.startswith("linux"), reason="unsupported api for linux"
|
|
68
97
|
)
|
|
69
|
-
@pytest.mark.parametrize("font_name",
|
|
98
|
+
@pytest.mark.parametrize("font_name", font_lists_dict)
|
|
70
99
|
@pytest.mark.skipif(sys.platform.startswith("darwin"), reason="always returns true")
|
|
71
100
|
def test_fail_just_unregister(font_name):
|
|
72
101
|
assert not manimpango.unregister_font(
|
|
@@ -78,7 +107,7 @@ def test_fail_just_unregister(font_name):
|
|
|
78
107
|
sys.platform.startswith("win32"), reason="unsupported api for win32"
|
|
79
108
|
)
|
|
80
109
|
@pytest.mark.skipif(sys.platform.startswith("darwin"), reason="unsupported api for mac")
|
|
81
|
-
def
|
|
110
|
+
def test_unregister_not_fail_linux():
|
|
82
111
|
assert manimpango.unregister_font("random")
|
|
83
112
|
|
|
84
113
|
|
|
@@ -90,6 +119,7 @@ def test_adding_dummy_font(tmpdir):
|
|
|
90
119
|
with open(dummy, "wb") as f:
|
|
91
120
|
f.write(b"dummy")
|
|
92
121
|
assert not manimpango.register_font(str(dummy)), "Registered a dummy font?"
|
|
122
|
+
assert not manimpango.fc_register_font(str(dummy)), "Registered a dummy font?"
|
|
93
123
|
|
|
94
124
|
|
|
95
125
|
def test_simple_fonts_render(tmpdir):
|
|
@@ -106,7 +136,7 @@ def test_both_fc_and_register_font_are_same():
|
|
|
106
136
|
assert manimpango.fc_unregister_font == manimpango.unregister_font
|
|
107
137
|
|
|
108
138
|
|
|
109
|
-
@pytest.mark.parametrize("font_file",
|
|
139
|
+
@pytest.mark.parametrize("font_file", font_lists_dict)
|
|
110
140
|
def test_fc_font_register(setup_fontconfig, font_file):
|
|
111
141
|
intial = manimpango.list_fonts()
|
|
112
142
|
assert manimpango.fc_register_font(str(font_file)), "Invalid Font possibly."
|
ManimPango-0.3.0/pyproject.toml
DELETED
ManimPango-0.3.0/tests/_manim.py
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
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 = None,
|
|
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
|
-
wrap_text: bool = True,
|
|
30
|
-
**kwargs,
|
|
31
|
-
):
|
|
32
|
-
self.text = text
|
|
33
|
-
self.size = size
|
|
34
|
-
self.line_spacing = line_spacing
|
|
35
|
-
self.font = font
|
|
36
|
-
self.slant = slant
|
|
37
|
-
self.weight = weight
|
|
38
|
-
self.tab_width = tab_width
|
|
39
|
-
self.filename = filename
|
|
40
|
-
self.original_text = text
|
|
41
|
-
self.disable_ligatures = disable_ligatures
|
|
42
|
-
|
|
43
|
-
self.justify = justify
|
|
44
|
-
self.indent = indent
|
|
45
|
-
self.alignment = alignment
|
|
46
|
-
self.wrap_text = wrap_text
|
|
47
|
-
if MarkupUtils.validate(self.text):
|
|
48
|
-
raise ValueError(
|
|
49
|
-
f"Pango cannot parse your markup in {self.text}. "
|
|
50
|
-
"Please check for typos, unmatched tags or unescaped "
|
|
51
|
-
"special chars like < and &."
|
|
52
|
-
)
|
|
53
|
-
self.text2svg()
|
|
54
|
-
|
|
55
|
-
def text2svg(self):
|
|
56
|
-
"""Convert the text to SVG using Pango."""
|
|
57
|
-
size = self.size * 10
|
|
58
|
-
dir_name = Path(self.filename).parent
|
|
59
|
-
disable_liga = self.disable_ligatures
|
|
60
|
-
if not os.path.exists(dir_name):
|
|
61
|
-
os.makedirs(dir_name)
|
|
62
|
-
file_name = self.filename
|
|
63
|
-
if self.wrap_text:
|
|
64
|
-
return MarkupUtils.text2svg(
|
|
65
|
-
f"{self.text}",
|
|
66
|
-
self.font,
|
|
67
|
-
self.slant,
|
|
68
|
-
self.weight,
|
|
69
|
-
size,
|
|
70
|
-
True, # stray positional argument
|
|
71
|
-
disable_liga,
|
|
72
|
-
file_name,
|
|
73
|
-
20,
|
|
74
|
-
20,
|
|
75
|
-
600, # width
|
|
76
|
-
400, # height
|
|
77
|
-
justify=self.justify,
|
|
78
|
-
indent=self.indent,
|
|
79
|
-
line_spacing=self.line_spacing,
|
|
80
|
-
alignment=self.alignment,
|
|
81
|
-
)
|
|
82
|
-
else:
|
|
83
|
-
return MarkupUtils.text2svg(
|
|
84
|
-
f"{self.text}",
|
|
85
|
-
self.font,
|
|
86
|
-
self.slant,
|
|
87
|
-
self.weight,
|
|
88
|
-
size,
|
|
89
|
-
True, # stray positional argument
|
|
90
|
-
disable_liga,
|
|
91
|
-
file_name,
|
|
92
|
-
20,
|
|
93
|
-
20,
|
|
94
|
-
600, # width
|
|
95
|
-
400, # height
|
|
96
|
-
justify=self.justify,
|
|
97
|
-
indent=self.indent,
|
|
98
|
-
line_spacing=self.line_spacing,
|
|
99
|
-
alignment=self.alignment,
|
|
100
|
-
pango_width=-1,
|
|
101
|
-
)
|
|
102
|
-
# -1 for no wrapping
|
|
103
|
-
# default is full width and then wrap.
|
|
104
|
-
|
|
105
|
-
def __repr__(self):
|
|
106
|
-
return f"MarkupText({repr(self.original_text)})"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|