ManimPango 0.6.1__cp314-cp314-win_amd64.whl
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/__init__.py +35 -0
- manimpango/_register_font.cp314-win_amd64.pyd +0 -0
- manimpango/_register_font.pxd +71 -0
- manimpango/_register_font.pyx +160 -0
- manimpango/_version.py +2 -0
- manimpango/cairo.pxd +27 -0
- manimpango/cmanimpango.cp314-win_amd64.pyd +0 -0
- manimpango/cmanimpango.pxd +3 -0
- manimpango/cmanimpango.pyx +321 -0
- manimpango/enums.cp314-win_amd64.pyd +0 -0
- manimpango/enums.pyx +113 -0
- manimpango/glib.pxd +13 -0
- manimpango/pango.pxd +193 -0
- manimpango/register_font.py +156 -0
- manimpango/utils.py +49 -0
- manimpango-0.6.1.dist-info/METADATA +219 -0
- manimpango-0.6.1.dist-info/RECORD +21 -0
- manimpango-0.6.1.dist-info/WHEEL +5 -0
- manimpango-0.6.1.dist-info/licenses/LICENSE +21 -0
- manimpango-0.6.1.dist-info/licenses/LICENSE.win32 +3208 -0
- manimpango-0.6.1.dist-info/top_level.txt +1 -0
manimpango/__init__.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from ._version import __version__ # noqa: F403,F401
|
|
6
|
+
|
|
7
|
+
if os.name == "nt": # pragma: no cover
|
|
8
|
+
os.environ["PATH"] = (
|
|
9
|
+
f"{os.path.abspath(os.path.dirname(__file__))}"
|
|
10
|
+
f"{os.pathsep}"
|
|
11
|
+
f"{os.environ['PATH']}"
|
|
12
|
+
)
|
|
13
|
+
try:
|
|
14
|
+
from .register_font import * # isort:skip # noqa: F403,F401
|
|
15
|
+
from .cmanimpango import * # noqa: F403,F401
|
|
16
|
+
from .enums import * # noqa: F403,F401
|
|
17
|
+
except ImportError as ie: # pragma: no cover
|
|
18
|
+
py_ver = ".".join(map(str, sys.version_info[:3]))
|
|
19
|
+
msg = f"""
|
|
20
|
+
|
|
21
|
+
ManimPango could not import and load the necessary shared libraries.
|
|
22
|
+
This error may occur when ManimPango and its dependencies are improperly set up.
|
|
23
|
+
Please make sure the following versions are what you expect:
|
|
24
|
+
|
|
25
|
+
* ManimPango v{__version__}, Python v{py_ver}
|
|
26
|
+
|
|
27
|
+
If you believe there is a greater problem,
|
|
28
|
+
feel free to contact us or create an issue on GitHub:
|
|
29
|
+
|
|
30
|
+
* Discord: https://www.manim.community/discord/
|
|
31
|
+
* GitHub: https://github.com/ManimCommunity/ManimPango/issues
|
|
32
|
+
|
|
33
|
+
Original error: {ie}
|
|
34
|
+
"""
|
|
35
|
+
raise ImportError(msg)
|
|
Binary file
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from libc.stddef cimport wchar_t
|
|
2
|
+
from pango cimport *
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
cdef extern from "Python.h":
|
|
6
|
+
wchar_t* PyUnicode_AsWideCharString(
|
|
7
|
+
object unicode,
|
|
8
|
+
Py_ssize_t* size
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
cdef extern from "fontconfig/fontconfig.h":
|
|
12
|
+
ctypedef int FcBool
|
|
13
|
+
ctypedef struct FcConfig:
|
|
14
|
+
pass
|
|
15
|
+
FcBool FcConfigAppFontAddFile(
|
|
16
|
+
FcConfig* config,
|
|
17
|
+
const unsigned char* file_name
|
|
18
|
+
)
|
|
19
|
+
FcConfig* FcConfigGetCurrent()
|
|
20
|
+
void FcConfigAppFontClear(void *)
|
|
21
|
+
|
|
22
|
+
# Windows and macOS specific API's
|
|
23
|
+
IF UNAME_SYSNAME == "Windows":
|
|
24
|
+
cdef extern from "windows.h":
|
|
25
|
+
ctypedef const wchar_t* LPCWSTR
|
|
26
|
+
ctypedef enum DWORD:
|
|
27
|
+
FR_PRIVATE
|
|
28
|
+
int AddFontResourceExW(
|
|
29
|
+
LPCWSTR name,
|
|
30
|
+
DWORD fl,
|
|
31
|
+
unsigned int res
|
|
32
|
+
)
|
|
33
|
+
bint RemoveFontResourceExW(
|
|
34
|
+
LPCWSTR name,
|
|
35
|
+
DWORD fl,
|
|
36
|
+
unsigned int pdv
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
ctypedef void* HANDLE
|
|
40
|
+
HANDLE CreateMutexA(void* lpMutexAttributes, int bInitialOwner, const char* lpName)
|
|
41
|
+
int ReleaseMutex(HANDLE hMutex)
|
|
42
|
+
int WaitForSingleObject(HANDLE hHandle, unsigned long dwMilliseconds)
|
|
43
|
+
int CloseHandle(HANDLE hObject)
|
|
44
|
+
|
|
45
|
+
ELIF UNAME_SYSNAME == "Darwin":
|
|
46
|
+
cdef extern from "Carbon/Carbon.h":
|
|
47
|
+
ctypedef struct CFURLRef:
|
|
48
|
+
pass
|
|
49
|
+
ctypedef enum CTFontManagerScope:
|
|
50
|
+
kCTFontManagerScopeProcess
|
|
51
|
+
ctypedef unsigned int UInt8
|
|
52
|
+
ctypedef long CFIndex
|
|
53
|
+
ctypedef unsigned int UInt32
|
|
54
|
+
ctypedef UInt32 CFStringEncoding
|
|
55
|
+
CFURLRef CFURLCreateWithBytes(
|
|
56
|
+
void*,
|
|
57
|
+
unsigned char *URLBytes,
|
|
58
|
+
CFIndex length,
|
|
59
|
+
CFStringEncoding encoding,
|
|
60
|
+
void*
|
|
61
|
+
)
|
|
62
|
+
bint CTFontManagerRegisterFontsForURL(
|
|
63
|
+
CFURLRef fontURL,
|
|
64
|
+
CTFontManagerScope scope,
|
|
65
|
+
void* error
|
|
66
|
+
)
|
|
67
|
+
bint CTFontManagerUnregisterFontsForURL(
|
|
68
|
+
CFURLRef fontURL,
|
|
69
|
+
CTFontManagerScope scope,
|
|
70
|
+
void* error
|
|
71
|
+
)
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from pango cimport *
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
include "utils.pxi"
|
|
8
|
+
|
|
9
|
+
@dataclass(frozen=True)
|
|
10
|
+
class RegisteredFont:
|
|
11
|
+
"""A class to represent a font file.
|
|
12
|
+
|
|
13
|
+
Attributes
|
|
14
|
+
----------
|
|
15
|
+
path : :class:`str`
|
|
16
|
+
The path to the font file.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
path: str
|
|
20
|
+
type: "fontconfig" | "win32" | "macos"
|
|
21
|
+
|
|
22
|
+
cpdef bint _fc_register_font(set registered_fonts, str font_path):
|
|
23
|
+
a = Path(font_path)
|
|
24
|
+
assert a.exists(), f"font doesn't exist at {a.absolute()}"
|
|
25
|
+
font_path = os.fspath(a.absolute())
|
|
26
|
+
font_path_bytes = font_path.encode('utf-8')
|
|
27
|
+
cdef const unsigned char* fontPath = font_path_bytes
|
|
28
|
+
fontAddStatus = FcConfigAppFontAddFile(FcConfigGetCurrent(), fontPath)
|
|
29
|
+
if fontAddStatus:
|
|
30
|
+
registered_fonts.add(RegisteredFont(font_path, "fontconfig"))
|
|
31
|
+
return True
|
|
32
|
+
else:
|
|
33
|
+
return False
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
cpdef bint _fc_unregister_font(set registered_fonts, str font_path):
|
|
37
|
+
FcConfigAppFontClear(NULL)
|
|
38
|
+
# remove all type "fontconfig" files
|
|
39
|
+
copy = registered_fonts.copy()
|
|
40
|
+
for font in copy:
|
|
41
|
+
if font.type == 'fontconfig':
|
|
42
|
+
registered_fonts.remove(font)
|
|
43
|
+
|
|
44
|
+
return True
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
IF UNAME_SYSNAME == "Linux":
|
|
48
|
+
_register_font = _fc_register_font
|
|
49
|
+
_unregister_font = _fc_unregister_font
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
ELIF UNAME_SYSNAME == "Windows":
|
|
53
|
+
cpdef bint _register_font(set registered_fonts, str font_path):
|
|
54
|
+
a = Path(font_path)
|
|
55
|
+
assert a.exists(), f"font doesn't exist at {a.absolute()}"
|
|
56
|
+
font_path = os.fspath(a.absolute())
|
|
57
|
+
cdef LPCWSTR wchar_path = PyUnicode_AsWideCharString(font_path, NULL)
|
|
58
|
+
fontAddStatus = AddFontResourceExW(
|
|
59
|
+
wchar_path,
|
|
60
|
+
FR_PRIVATE,
|
|
61
|
+
0
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
if fontAddStatus > 0:
|
|
65
|
+
registered_fonts.add(RegisteredFont(font_path, "win32"))
|
|
66
|
+
return True
|
|
67
|
+
else:
|
|
68
|
+
return False
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
cpdef bint _unregister_font(set registered_fonts, str font_path):
|
|
72
|
+
a = Path(font_path)
|
|
73
|
+
assert a.exists(), f"font doesn't exist at {a.absolute()}"
|
|
74
|
+
font_path = os.fspath(a.absolute())
|
|
75
|
+
|
|
76
|
+
font = RegisteredFont(font_path, "win32")
|
|
77
|
+
if font in registered_fonts:
|
|
78
|
+
registered_fonts.remove(font)
|
|
79
|
+
|
|
80
|
+
cdef LPCWSTR wchar_path = PyUnicode_AsWideCharString(font_path, NULL)
|
|
81
|
+
return RemoveFontResourceExW(
|
|
82
|
+
wchar_path,
|
|
83
|
+
FR_PRIVATE,
|
|
84
|
+
0
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
ELIF UNAME_SYSNAME == "Darwin":
|
|
89
|
+
cpdef bint _register_font(set registered_fonts, str font_path):
|
|
90
|
+
a = Path(font_path)
|
|
91
|
+
assert a.exists(), f"font doesn't exist at {a.absolute()}"
|
|
92
|
+
font_path_bytes_py = str(a.absolute().as_uri()).encode('utf-8')
|
|
93
|
+
cdef unsigned char* font_path_bytes = <bytes>font_path_bytes_py
|
|
94
|
+
b = len(a.absolute().as_uri())
|
|
95
|
+
cdef CFURLRef cf_url = CFURLCreateWithBytes(NULL, font_path_bytes, b, 0x08000100, NULL)
|
|
96
|
+
res = CTFontManagerRegisterFontsForURL(
|
|
97
|
+
cf_url,
|
|
98
|
+
kCTFontManagerScopeProcess,
|
|
99
|
+
NULL
|
|
100
|
+
)
|
|
101
|
+
if res:
|
|
102
|
+
registered_fonts.add(RegisteredFont(os.fspath(a.absolute()), "macos"))
|
|
103
|
+
return True
|
|
104
|
+
else:
|
|
105
|
+
return False
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
cpdef bint _unregister_font(set registered_fonts, str font_path):
|
|
109
|
+
a = Path(font_path)
|
|
110
|
+
assert a.exists(), f"font doesn't exist at {a.absolute()}"
|
|
111
|
+
font_path_bytes_py = str(a.absolute().as_uri()).encode('utf-8')
|
|
112
|
+
cdef unsigned char* font_path_bytes = <bytes>font_path_bytes_py
|
|
113
|
+
b = len(a.absolute().as_uri())
|
|
114
|
+
cdef CFURLRef cf_url = CFURLCreateWithBytes(NULL, font_path_bytes, b, 0x08000100, NULL)
|
|
115
|
+
res = CTFontManagerUnregisterFontsForURL(
|
|
116
|
+
cf_url,
|
|
117
|
+
kCTFontManagerScopeProcess,
|
|
118
|
+
NULL
|
|
119
|
+
)
|
|
120
|
+
if res:
|
|
121
|
+
font = RegisteredFont(os.fspath(a.absolute()), "macos")
|
|
122
|
+
if font in registered_fonts:
|
|
123
|
+
registered_fonts.remove(font)
|
|
124
|
+
return True
|
|
125
|
+
else:
|
|
126
|
+
return False
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
cpdef list _list_fonts(tuple registered_fonts):
|
|
130
|
+
cdef PangoFontMap* fontmap = pango_cairo_font_map_new()
|
|
131
|
+
if fontmap == NULL:
|
|
132
|
+
raise MemoryError("Pango.FontMap can't be created.")
|
|
133
|
+
|
|
134
|
+
for font in registered_fonts:
|
|
135
|
+
if font.type == 'win32':
|
|
136
|
+
add_to_fontmap(fontmap, font.path)
|
|
137
|
+
|
|
138
|
+
cdef int n_families=0
|
|
139
|
+
cdef PangoFontFamily** families=NULL
|
|
140
|
+
pango_font_map_list_families(
|
|
141
|
+
fontmap,
|
|
142
|
+
&families,
|
|
143
|
+
&n_families
|
|
144
|
+
)
|
|
145
|
+
if families is NULL or n_families == 0:
|
|
146
|
+
raise MemoryError("Pango returned unexpected length on families.")
|
|
147
|
+
|
|
148
|
+
family_list = []
|
|
149
|
+
for i in range(n_families):
|
|
150
|
+
name = pango_font_family_get_name(families[i])
|
|
151
|
+
# according to pango's docs, the `char *` returned from
|
|
152
|
+
# `pango_font_family_get_name`is owned by pango, and python
|
|
153
|
+
# shouldn't interfere with it. I hope Cython handles it.
|
|
154
|
+
# https://cython.readthedocs.io/en/stable/src/tutorial/strings.html#dealing-with-const
|
|
155
|
+
family_list.append(name.decode())
|
|
156
|
+
|
|
157
|
+
g_free(families)
|
|
158
|
+
g_object_unref(fontmap)
|
|
159
|
+
family_list.sort()
|
|
160
|
+
return family_list
|
manimpango/_version.py
ADDED
manimpango/cairo.pxd
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
cdef extern from "cairo.h":
|
|
2
|
+
ctypedef struct cairo_surface_t:
|
|
3
|
+
pass
|
|
4
|
+
ctypedef struct cairo_t:
|
|
5
|
+
pass
|
|
6
|
+
ctypedef enum cairo_status_t:
|
|
7
|
+
CAIRO_STATUS_SUCCESS
|
|
8
|
+
CAIRO_STATUS_NO_MEMORY
|
|
9
|
+
cairo_t* cairo_create(cairo_surface_t* target)
|
|
10
|
+
void cairo_move_to(
|
|
11
|
+
cairo_t* cr,
|
|
12
|
+
double x,
|
|
13
|
+
double y
|
|
14
|
+
)
|
|
15
|
+
void cairo_destroy(cairo_t* cr)
|
|
16
|
+
void cairo_surface_destroy(cairo_surface_t* surface)
|
|
17
|
+
|
|
18
|
+
cairo_status_t cairo_status(cairo_t *cr)
|
|
19
|
+
const char* cairo_status_to_string(cairo_status_t status)
|
|
20
|
+
const char* cairo_version_string()
|
|
21
|
+
|
|
22
|
+
cdef extern from "cairo-svg.h":
|
|
23
|
+
cairo_surface_t* cairo_svg_surface_create(
|
|
24
|
+
const char* filename,
|
|
25
|
+
double width_in_points,
|
|
26
|
+
double height_in_points
|
|
27
|
+
)
|
|
Binary file
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
import warnings
|
|
3
|
+
from xml.sax.saxutils import escape
|
|
4
|
+
|
|
5
|
+
from . import registered_fonts
|
|
6
|
+
from .enums import Alignment
|
|
7
|
+
from .utils import *
|
|
8
|
+
|
|
9
|
+
include "utils.pxi"
|
|
10
|
+
|
|
11
|
+
class TextSetting:
|
|
12
|
+
"""Formatting for slices of a :class:`manim.mobject.svg.text_mobject.Text` object."""
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
start: int,
|
|
16
|
+
end: int,
|
|
17
|
+
font: str,
|
|
18
|
+
slant: str,
|
|
19
|
+
weight: str,
|
|
20
|
+
line_num = -1,
|
|
21
|
+
color: str = None,
|
|
22
|
+
):
|
|
23
|
+
self.start = start
|
|
24
|
+
self.end = end
|
|
25
|
+
self.font = font
|
|
26
|
+
self.slant = slant
|
|
27
|
+
self.weight = weight
|
|
28
|
+
self.line_num = line_num
|
|
29
|
+
self.color = color
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def text2svg(
|
|
33
|
+
settings: list,
|
|
34
|
+
size: float,
|
|
35
|
+
line_spacing: float,
|
|
36
|
+
disable_liga: bool,
|
|
37
|
+
file_name: str,
|
|
38
|
+
START_X: int,
|
|
39
|
+
START_Y: int,
|
|
40
|
+
width: int,
|
|
41
|
+
height: int,
|
|
42
|
+
orig_text: str,
|
|
43
|
+
pango_width: typing.Union[int, None] = None,
|
|
44
|
+
) -> str:
|
|
45
|
+
"""Render an SVG file from a :class:`manim.mobject.svg.text_mobject.Text` object."""
|
|
46
|
+
cdef cairo_surface_t* surface
|
|
47
|
+
cdef cairo_t* cr
|
|
48
|
+
cdef PangoFontDescription* font_desc
|
|
49
|
+
cdef PangoLayout* layout
|
|
50
|
+
cdef double font_size_c = size
|
|
51
|
+
cdef cairo_status_t status
|
|
52
|
+
cdef int temp_width
|
|
53
|
+
cdef PangoFontMap* fontmap
|
|
54
|
+
|
|
55
|
+
file_name_bytes = file_name.encode("utf-8")
|
|
56
|
+
surface = cairo_svg_surface_create(file_name_bytes,width,height)
|
|
57
|
+
|
|
58
|
+
if surface == NULL:
|
|
59
|
+
raise MemoryError("Cairo.SVGSurface can't be created.")
|
|
60
|
+
|
|
61
|
+
cr = cairo_create(surface)
|
|
62
|
+
status = cairo_status(cr)
|
|
63
|
+
|
|
64
|
+
if cr == NULL or status == CAIRO_STATUS_NO_MEMORY:
|
|
65
|
+
cairo_destroy(cr)
|
|
66
|
+
cairo_surface_destroy(surface)
|
|
67
|
+
raise MemoryError("Cairo.Context can't be created.")
|
|
68
|
+
elif status != CAIRO_STATUS_SUCCESS:
|
|
69
|
+
cairo_destroy(cr)
|
|
70
|
+
cairo_surface_destroy(surface)
|
|
71
|
+
raise Exception(cairo_status_to_string(status))
|
|
72
|
+
|
|
73
|
+
cairo_move_to(cr,START_X,START_Y)
|
|
74
|
+
offset_x = 0
|
|
75
|
+
last_line_num = 0
|
|
76
|
+
|
|
77
|
+
layout = pango_cairo_create_layout(cr)
|
|
78
|
+
fontmap = pango_context_get_font_map (pango_layout_get_context (layout));
|
|
79
|
+
|
|
80
|
+
for font_item in registered_fonts:
|
|
81
|
+
if font_item.type == 'win32':
|
|
82
|
+
add_to_fontmap(fontmap, font_item.path)
|
|
83
|
+
|
|
84
|
+
if layout == NULL:
|
|
85
|
+
cairo_destroy(cr)
|
|
86
|
+
cairo_surface_destroy(surface)
|
|
87
|
+
raise MemoryError("Pango.Layout can't be created from Cairo Context.")
|
|
88
|
+
|
|
89
|
+
if pango_width is None:
|
|
90
|
+
pango_layout_set_width(layout, pango_units_from_double(width))
|
|
91
|
+
else:
|
|
92
|
+
pango_layout_set_width(layout, pango_units_from_double(pango_width))
|
|
93
|
+
|
|
94
|
+
for setting in settings:
|
|
95
|
+
family = setting.font.encode('utf-8')
|
|
96
|
+
style = PangoUtils.str2style(setting.slant)
|
|
97
|
+
weight = PangoUtils.str2weight(setting.weight)
|
|
98
|
+
color = setting.color
|
|
99
|
+
text_str = orig_text[setting.start : setting.end].replace("\n", " ")
|
|
100
|
+
font_desc = pango_font_description_new()
|
|
101
|
+
if font_desc==NULL:
|
|
102
|
+
cairo_destroy(cr)
|
|
103
|
+
cairo_surface_destroy(surface)
|
|
104
|
+
g_object_unref(layout)
|
|
105
|
+
raise MemoryError("Pango.FontDesc can't be created.")
|
|
106
|
+
pango_font_description_set_size(font_desc, pango_units_from_double(font_size_c))
|
|
107
|
+
if family:
|
|
108
|
+
pango_font_description_set_family(font_desc, family)
|
|
109
|
+
pango_font_description_set_style(font_desc, style.value)
|
|
110
|
+
pango_font_description_set_weight(font_desc, weight.value)
|
|
111
|
+
pango_layout_set_font_description(layout, font_desc)
|
|
112
|
+
pango_font_description_free(font_desc)
|
|
113
|
+
if setting.line_num != last_line_num:
|
|
114
|
+
offset_x = 0
|
|
115
|
+
last_line_num = setting.line_num
|
|
116
|
+
cairo_move_to(cr,START_X + offset_x,START_Y + line_spacing * setting.line_num)
|
|
117
|
+
|
|
118
|
+
pango_cairo_update_layout(cr,layout)
|
|
119
|
+
markup = escape(text_str)
|
|
120
|
+
if color:
|
|
121
|
+
markup = (f"<span color='{color}'>{markup}</span>")
|
|
122
|
+
if MarkupUtils.validate(markup):
|
|
123
|
+
cairo_destroy(cr)
|
|
124
|
+
cairo_surface_destroy(surface)
|
|
125
|
+
g_object_unref(layout)
|
|
126
|
+
raise ValueError(f"Pango cannot recognize your color '{color}' for text '{text_str}'.")
|
|
127
|
+
if disable_liga:
|
|
128
|
+
markup = f"<span font_features='liga=0,dlig=0,clig=0,hlig=0'>{markup}</span>"
|
|
129
|
+
pango_layout_set_markup(layout, markup.encode('utf-8'), -1)
|
|
130
|
+
pango_cairo_show_layout(cr, layout)
|
|
131
|
+
pango_layout_get_size(layout,&temp_width,NULL)
|
|
132
|
+
offset_x += pango_units_to_double(temp_width)
|
|
133
|
+
|
|
134
|
+
status = cairo_status(cr)
|
|
135
|
+
|
|
136
|
+
if cr == NULL or status == CAIRO_STATUS_NO_MEMORY:
|
|
137
|
+
cairo_destroy(cr)
|
|
138
|
+
cairo_surface_destroy(surface)
|
|
139
|
+
g_object_unref(layout)
|
|
140
|
+
raise MemoryError("Cairo.Context can't be created.")
|
|
141
|
+
elif status != CAIRO_STATUS_SUCCESS:
|
|
142
|
+
cairo_destroy(cr)
|
|
143
|
+
cairo_surface_destroy(surface)
|
|
144
|
+
g_object_unref(layout)
|
|
145
|
+
raise Exception(cairo_status_to_string(status).decode())
|
|
146
|
+
|
|
147
|
+
cairo_destroy(cr)
|
|
148
|
+
cairo_surface_destroy(surface)
|
|
149
|
+
g_object_unref(layout)
|
|
150
|
+
return file_name
|
|
151
|
+
|
|
152
|
+
class MarkupUtils:
|
|
153
|
+
@staticmethod
|
|
154
|
+
def validate(markup: str) -> str:
|
|
155
|
+
"""Validates whether markup is a valid Markup
|
|
156
|
+
and return the error's if any.
|
|
157
|
+
|
|
158
|
+
Parameters
|
|
159
|
+
==========
|
|
160
|
+
markup : :class:`str`
|
|
161
|
+
The markup which should be checked.
|
|
162
|
+
|
|
163
|
+
Returns
|
|
164
|
+
=======
|
|
165
|
+
:class:`str`
|
|
166
|
+
Returns empty string if markup is valid. If markup
|
|
167
|
+
contains error it return the error message.
|
|
168
|
+
|
|
169
|
+
"""
|
|
170
|
+
cdef GError *err = NULL
|
|
171
|
+
text_bytes = markup.encode("utf-8")
|
|
172
|
+
res = pango_parse_markup(
|
|
173
|
+
text_bytes,
|
|
174
|
+
-1,
|
|
175
|
+
0,
|
|
176
|
+
NULL,
|
|
177
|
+
NULL,
|
|
178
|
+
NULL,
|
|
179
|
+
&err
|
|
180
|
+
)
|
|
181
|
+
if res:
|
|
182
|
+
return ""
|
|
183
|
+
else:
|
|
184
|
+
message = <bytes>err.message
|
|
185
|
+
g_error_free(err)
|
|
186
|
+
return message.decode('utf-8')
|
|
187
|
+
|
|
188
|
+
@staticmethod
|
|
189
|
+
def text2svg(
|
|
190
|
+
text: str,
|
|
191
|
+
font: str | None,
|
|
192
|
+
slant: str,
|
|
193
|
+
weight: str,
|
|
194
|
+
size: float,
|
|
195
|
+
_, # for some there was a keyword here.
|
|
196
|
+
disable_liga: bool,
|
|
197
|
+
file_name: str,
|
|
198
|
+
START_X: int,
|
|
199
|
+
START_Y: int,
|
|
200
|
+
width: int,
|
|
201
|
+
height: int,
|
|
202
|
+
*, # keyword only arguments below
|
|
203
|
+
justify: bool | None = None,
|
|
204
|
+
indent: float | int | None = None,
|
|
205
|
+
line_spacing: float | None = None,
|
|
206
|
+
alignment: Alignment | None = None,
|
|
207
|
+
pango_width: int | None = None,
|
|
208
|
+
) -> str:
|
|
209
|
+
"""Render an SVG file from a :class:`manim.mobject.svg.text_mobject.MarkupText` object."""
|
|
210
|
+
cdef cairo_surface_t* surface
|
|
211
|
+
cdef cairo_t* context
|
|
212
|
+
cdef PangoFontDescription* font_desc
|
|
213
|
+
cdef PangoLayout* layout
|
|
214
|
+
cdef cairo_status_t status
|
|
215
|
+
cdef double font_size = size
|
|
216
|
+
cdef int temp_int # a temporary C integer for conversion
|
|
217
|
+
cdef PangoFontMap* fontmap
|
|
218
|
+
|
|
219
|
+
file_name_bytes = file_name.encode("utf-8")
|
|
220
|
+
|
|
221
|
+
if disable_liga:
|
|
222
|
+
text_bytes = f"<span font_features='liga=0,dlig=0,clig=0,hlig=0'>{text}</span>".encode("utf-8")
|
|
223
|
+
else:
|
|
224
|
+
text_bytes = text.encode("utf-8")
|
|
225
|
+
|
|
226
|
+
surface = cairo_svg_surface_create(file_name_bytes,width,height)
|
|
227
|
+
if surface == NULL:
|
|
228
|
+
raise MemoryError("Cairo.SVGSurface can't be created.")
|
|
229
|
+
context = cairo_create(surface)
|
|
230
|
+
status = cairo_status(context)
|
|
231
|
+
if context == NULL or status == CAIRO_STATUS_NO_MEMORY:
|
|
232
|
+
cairo_destroy(context)
|
|
233
|
+
cairo_surface_destroy(surface)
|
|
234
|
+
raise MemoryError("Cairo.Context can't be created.")
|
|
235
|
+
elif status != CAIRO_STATUS_SUCCESS:
|
|
236
|
+
cairo_destroy(context)
|
|
237
|
+
cairo_surface_destroy(surface)
|
|
238
|
+
raise Exception(cairo_status_to_string(status))
|
|
239
|
+
|
|
240
|
+
cairo_move_to(context,START_X,START_Y)
|
|
241
|
+
layout = pango_cairo_create_layout(context)
|
|
242
|
+
if layout == NULL:
|
|
243
|
+
cairo_destroy(context)
|
|
244
|
+
cairo_surface_destroy(surface)
|
|
245
|
+
raise MemoryError("Pango.Layout can't be created from Cairo Context.")
|
|
246
|
+
|
|
247
|
+
fontmap = pango_context_get_font_map (pango_layout_get_context (layout));
|
|
248
|
+
|
|
249
|
+
for font_item in registered_fonts:
|
|
250
|
+
if font_item.type == 'win32':
|
|
251
|
+
add_to_fontmap(fontmap, font_item.path)
|
|
252
|
+
|
|
253
|
+
if pango_width is None:
|
|
254
|
+
pango_layout_set_width(layout, pango_units_from_double(width))
|
|
255
|
+
else:
|
|
256
|
+
pango_layout_set_width(layout, pango_units_from_double(pango_width))
|
|
257
|
+
|
|
258
|
+
if justify:
|
|
259
|
+
pango_layout_set_justify(layout, justify)
|
|
260
|
+
|
|
261
|
+
if indent:
|
|
262
|
+
temp_int = pango_units_from_double(indent)
|
|
263
|
+
pango_layout_set_indent(layout, temp_int)
|
|
264
|
+
|
|
265
|
+
if line_spacing:
|
|
266
|
+
# Typical values are: 0, 1, 1.5, 2.
|
|
267
|
+
ret = set_line_width(layout, line_spacing)
|
|
268
|
+
if not ret:
|
|
269
|
+
# warn that line spacing don't work
|
|
270
|
+
# because of old Pango version they
|
|
271
|
+
# have
|
|
272
|
+
warnings.warn(
|
|
273
|
+
"Pango Version<1.44 found."
|
|
274
|
+
"Impossible to set line_spacing."
|
|
275
|
+
"Expect Ugly Output."
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
if alignment:
|
|
279
|
+
pango_layout_set_alignment(layout, alignment.value)
|
|
280
|
+
|
|
281
|
+
font_desc = pango_font_description_new()
|
|
282
|
+
if font_desc == NULL:
|
|
283
|
+
cairo_destroy(context)
|
|
284
|
+
cairo_surface_destroy(surface)
|
|
285
|
+
g_object_unref(layout)
|
|
286
|
+
raise MemoryError("Pango.FontDesc can't be created.")
|
|
287
|
+
pango_font_description_set_size(font_desc, pango_units_from_double(font_size))
|
|
288
|
+
if font is not None and len(font) != 0:
|
|
289
|
+
pango_font_description_set_family(font_desc, font.encode("utf-8"))
|
|
290
|
+
pango_font_description_set_style(font_desc, PangoUtils.str2style(slant).value)
|
|
291
|
+
pango_font_description_set_weight(font_desc, PangoUtils.str2weight(weight).value)
|
|
292
|
+
pango_layout_set_font_description(layout, font_desc)
|
|
293
|
+
pango_font_description_free(font_desc)
|
|
294
|
+
|
|
295
|
+
cairo_move_to(context,START_X,START_Y)
|
|
296
|
+
pango_cairo_update_layout(context,layout)
|
|
297
|
+
pango_layout_set_markup(layout,text_bytes,-1)
|
|
298
|
+
pango_cairo_show_layout(context, layout)
|
|
299
|
+
|
|
300
|
+
status = cairo_status(context)
|
|
301
|
+
if context == NULL or status == CAIRO_STATUS_NO_MEMORY:
|
|
302
|
+
cairo_destroy(context)
|
|
303
|
+
cairo_surface_destroy(surface)
|
|
304
|
+
g_object_unref(layout)
|
|
305
|
+
raise MemoryError("Cairo.Context can't be created.")
|
|
306
|
+
elif status != CAIRO_STATUS_SUCCESS:
|
|
307
|
+
cairo_destroy(context)
|
|
308
|
+
cairo_surface_destroy(surface)
|
|
309
|
+
g_object_unref(layout)
|
|
310
|
+
raise Exception(cairo_status_to_string(status).decode())
|
|
311
|
+
|
|
312
|
+
cairo_destroy(context)
|
|
313
|
+
cairo_surface_destroy(surface)
|
|
314
|
+
g_object_unref(layout)
|
|
315
|
+
return file_name
|
|
316
|
+
|
|
317
|
+
cpdef str pango_version():
|
|
318
|
+
return pango_version_string().decode('utf-8')
|
|
319
|
+
|
|
320
|
+
cpdef str cairo_version():
|
|
321
|
+
return cairo_version_string().decode('utf-8')
|
|
Binary file
|