pyglet 2.1.13__py3-none-any.whl → 3.0.dev1__py3-none-any.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.
- pyglet/__init__.py +67 -61
- pyglet/__init__.pyi +15 -8
- pyglet/app/__init__.py +22 -13
- pyglet/app/async_app.py +212 -0
- pyglet/app/base.py +2 -1
- pyglet/app/{xlib.py → linux.py} +3 -3
- pyglet/config/__init__.py +101 -0
- pyglet/config/gl/__init__.py +30 -0
- pyglet/config/gl/egl.py +120 -0
- pyglet/config/gl/macos.py +262 -0
- pyglet/config/gl/windows.py +267 -0
- pyglet/config/gl/x11.py +142 -0
- pyglet/customtypes.py +43 -2
- pyglet/display/__init__.py +8 -6
- pyglet/display/base.py +3 -63
- pyglet/display/cocoa.py +12 -17
- pyglet/display/emscripten.py +39 -0
- pyglet/display/headless.py +23 -30
- pyglet/display/wayland.py +157 -0
- pyglet/display/win32.py +5 -21
- pyglet/display/xlib.py +19 -27
- pyglet/display/xlib_vidmoderestore.py +2 -2
- pyglet/enums.py +183 -0
- pyglet/event.py +0 -1
- pyglet/experimental/geoshader_sprite.py +15 -13
- pyglet/experimental/hidraw.py +6 -15
- pyglet/experimental/multitexture_sprite.py +31 -19
- pyglet/experimental/particles.py +13 -35
- pyglet/font/__init__.py +251 -85
- pyglet/font/base.py +116 -61
- pyglet/font/dwrite/__init__.py +349 -204
- pyglet/font/dwrite/dwrite_lib.py +27 -5
- pyglet/font/fontconfig.py +14 -6
- pyglet/font/freetype.py +138 -87
- pyglet/font/freetype_lib.py +19 -0
- pyglet/font/group.py +179 -0
- pyglet/font/harfbuzz/__init__.py +3 -3
- pyglet/font/pyodide_js.py +310 -0
- pyglet/font/quartz.py +319 -126
- pyglet/font/ttf.py +45 -3
- pyglet/font/user.py +14 -19
- pyglet/font/win32.py +45 -21
- pyglet/graphics/__init__.py +8 -787
- pyglet/graphics/allocation.py +115 -1
- pyglet/graphics/api/__init__.py +77 -0
- pyglet/graphics/api/base.py +299 -0
- pyglet/graphics/api/gl/__init__.py +58 -0
- pyglet/graphics/api/gl/base.py +24 -0
- pyglet/graphics/{vertexbuffer.py → api/gl/buffer.py} +104 -159
- pyglet/graphics/api/gl/cocoa/context.py +76 -0
- pyglet/graphics/api/gl/context.py +391 -0
- pyglet/graphics/api/gl/default_shaders.py +0 -0
- pyglet/graphics/api/gl/draw.py +627 -0
- pyglet/graphics/api/gl/egl/__init__.py +0 -0
- pyglet/graphics/api/gl/egl/context.py +92 -0
- pyglet/graphics/api/gl/enums.py +76 -0
- pyglet/graphics/api/gl/framebuffer.py +315 -0
- pyglet/graphics/api/gl/gl.py +5463 -0
- pyglet/graphics/api/gl/gl_info.py +188 -0
- pyglet/graphics/api/gl/global_opengl.py +226 -0
- pyglet/{gl → graphics/api/gl}/lib.py +34 -18
- pyglet/graphics/api/gl/shader.py +1476 -0
- pyglet/graphics/api/gl/shapes.py +55 -0
- pyglet/graphics/api/gl/sprite.py +102 -0
- pyglet/graphics/api/gl/state.py +219 -0
- pyglet/graphics/api/gl/text.py +190 -0
- pyglet/graphics/api/gl/texture.py +1526 -0
- pyglet/graphics/{vertexarray.py → api/gl/vertexarray.py} +11 -13
- pyglet/graphics/api/gl/vertexdomain.py +751 -0
- pyglet/graphics/api/gl/win32/__init__.py +0 -0
- pyglet/graphics/api/gl/win32/context.py +108 -0
- pyglet/graphics/api/gl/win32/wgl_info.py +24 -0
- pyglet/graphics/api/gl/xlib/__init__.py +0 -0
- pyglet/graphics/api/gl/xlib/context.py +174 -0
- pyglet/{gl → graphics/api/gl/xlib}/glx_info.py +26 -31
- pyglet/graphics/api/gl1/__init__.py +0 -0
- pyglet/{gl → graphics/api/gl1}/gl_compat.py +3 -2
- pyglet/graphics/api/gl2/__init__.py +0 -0
- pyglet/graphics/api/gl2/buffer.py +320 -0
- pyglet/graphics/api/gl2/draw.py +600 -0
- pyglet/graphics/api/gl2/global_opengl.py +122 -0
- pyglet/graphics/api/gl2/shader.py +200 -0
- pyglet/graphics/api/gl2/shapes.py +51 -0
- pyglet/graphics/api/gl2/sprite.py +79 -0
- pyglet/graphics/api/gl2/text.py +175 -0
- pyglet/graphics/api/gl2/vertexdomain.py +364 -0
- pyglet/graphics/api/webgl/__init__.py +233 -0
- pyglet/graphics/api/webgl/buffer.py +302 -0
- pyglet/graphics/api/webgl/context.py +234 -0
- pyglet/graphics/api/webgl/draw.py +590 -0
- pyglet/graphics/api/webgl/enums.py +76 -0
- pyglet/graphics/api/webgl/framebuffer.py +360 -0
- pyglet/graphics/api/webgl/gl.py +1537 -0
- pyglet/graphics/api/webgl/gl_info.py +130 -0
- pyglet/graphics/api/webgl/shader.py +1346 -0
- pyglet/graphics/api/webgl/shapes.py +92 -0
- pyglet/graphics/api/webgl/sprite.py +102 -0
- pyglet/graphics/api/webgl/state.py +227 -0
- pyglet/graphics/api/webgl/text.py +187 -0
- pyglet/graphics/api/webgl/texture.py +1227 -0
- pyglet/graphics/api/webgl/vertexarray.py +54 -0
- pyglet/graphics/api/webgl/vertexdomain.py +616 -0
- pyglet/graphics/api/webgl/webgl_js.pyi +307 -0
- pyglet/{image → graphics}/atlas.py +33 -32
- pyglet/graphics/base.py +10 -0
- pyglet/graphics/buffer.py +245 -0
- pyglet/graphics/draw.py +578 -0
- pyglet/graphics/framebuffer.py +26 -0
- pyglet/graphics/instance.py +178 -69
- pyglet/graphics/shader.py +267 -1553
- pyglet/graphics/state.py +83 -0
- pyglet/graphics/texture.py +703 -0
- pyglet/graphics/vertexdomain.py +695 -538
- pyglet/gui/ninepatch.py +10 -10
- pyglet/gui/widgets.py +120 -10
- pyglet/image/__init__.py +20 -1973
- pyglet/image/animation.py +12 -12
- pyglet/image/base.py +730 -0
- pyglet/image/codecs/__init__.py +9 -0
- pyglet/image/codecs/bmp.py +53 -30
- pyglet/image/codecs/dds.py +53 -31
- pyglet/image/codecs/gdiplus.py +38 -14
- pyglet/image/codecs/gdkpixbuf2.py +0 -2
- pyglet/image/codecs/js_image.py +99 -0
- pyglet/image/codecs/ktx2.py +161 -0
- pyglet/image/codecs/pil.py +1 -1
- pyglet/image/codecs/png.py +1 -1
- pyglet/image/codecs/wic.py +11 -2
- pyglet/info.py +26 -24
- pyglet/input/__init__.py +8 -0
- pyglet/input/base.py +163 -105
- pyglet/input/controller.py +13 -19
- pyglet/input/controller_db.py +39 -24
- pyglet/input/emscripten/__init__.py +18 -0
- pyglet/input/emscripten/gamepad_js.py +397 -0
- pyglet/input/linux/__init__.py +11 -5
- pyglet/input/linux/evdev.py +10 -11
- pyglet/input/linux/x11_xinput.py +2 -2
- pyglet/input/linux/x11_xinput_tablet.py +1 -1
- pyglet/input/macos/__init__.py +7 -2
- pyglet/input/macos/darwin_gc.py +559 -0
- pyglet/input/win32/__init__.py +1 -1
- pyglet/input/win32/directinput.py +34 -29
- pyglet/input/win32/xinput.py +11 -61
- pyglet/lib.py +3 -3
- pyglet/libs/__init__.py +1 -1
- pyglet/{gl → libs/darwin}/agl.py +1 -1
- pyglet/libs/darwin/cocoapy/__init__.py +2 -2
- pyglet/libs/darwin/cocoapy/cocoahelpers.py +181 -0
- pyglet/libs/darwin/cocoapy/cocoalibs.py +31 -0
- pyglet/libs/darwin/cocoapy/cocoatypes.py +27 -0
- pyglet/libs/darwin/cocoapy/runtime.py +81 -45
- pyglet/libs/darwin/coreaudio.py +4 -4
- pyglet/{gl → libs/darwin}/lib_agl.py +9 -8
- pyglet/libs/darwin/quartzkey.py +1 -3
- pyglet/libs/egl/__init__.py +2 -0
- pyglet/libs/egl/egl_lib.py +576 -0
- pyglet/libs/egl/eglext.py +51 -5
- pyglet/libs/linux/__init__.py +0 -0
- pyglet/libs/linux/egl/__init__.py +0 -0
- pyglet/libs/linux/egl/eglext.py +22 -0
- pyglet/libs/linux/glx/__init__.py +0 -0
- pyglet/{gl → libs/linux/glx}/glx.py +13 -14
- pyglet/{gl → libs/linux/glx}/glxext_arb.py +408 -192
- pyglet/{gl → libs/linux/glx}/glxext_mesa.py +1 -1
- pyglet/{gl → libs/linux/glx}/glxext_nv.py +345 -164
- pyglet/{gl → libs/linux/glx}/lib_glx.py +3 -2
- pyglet/libs/linux/wayland/__init__.py +0 -0
- pyglet/libs/linux/wayland/client.py +1068 -0
- pyglet/libs/linux/wayland/lib_wayland.py +207 -0
- pyglet/libs/linux/wayland/wayland_egl.py +38 -0
- pyglet/libs/{wayland → linux/wayland}/xkbcommon.py +26 -0
- pyglet/libs/{x11 → linux/x11}/xf86vmode.py +4 -4
- pyglet/libs/{x11 → linux/x11}/xinerama.py +2 -2
- pyglet/libs/{x11 → linux/x11}/xinput.py +10 -10
- pyglet/libs/linux/x11/xrandr.py +0 -0
- pyglet/libs/{x11 → linux/x11}/xrender.py +1 -1
- pyglet/libs/shared/__init__.py +0 -0
- pyglet/libs/shared/spirv/__init__.py +0 -0
- pyglet/libs/shared/spirv/lib_shaderc.py +85 -0
- pyglet/libs/shared/spirv/lib_spirv_cross.py +126 -0
- pyglet/libs/win32/__init__.py +28 -8
- pyglet/libs/win32/constants.py +59 -48
- pyglet/libs/win32/context_managers.py +20 -3
- pyglet/libs/win32/dinput.py +105 -88
- pyglet/{gl → libs/win32}/lib_wgl.py +52 -26
- pyglet/libs/win32/types.py +58 -23
- pyglet/{gl → libs/win32}/wgl.py +32 -25
- pyglet/{gl → libs/win32}/wglext_arb.py +364 -2
- pyglet/media/__init__.py +9 -10
- pyglet/media/codecs/__init__.py +12 -1
- pyglet/media/codecs/base.py +99 -96
- pyglet/media/codecs/ffmpeg.py +2 -2
- pyglet/media/codecs/ffmpeg_lib/libavformat.py +3 -8
- pyglet/media/codecs/webaudio_pyodide.py +111 -0
- pyglet/media/drivers/__init__.py +9 -4
- pyglet/media/drivers/base.py +4 -4
- pyglet/media/drivers/openal/__init__.py +1 -1
- pyglet/media/drivers/openal/adaptation.py +3 -3
- pyglet/media/drivers/pulse/__init__.py +1 -1
- pyglet/media/drivers/pulse/adaptation.py +3 -3
- pyglet/media/drivers/pyodide_js/__init__.py +8 -0
- pyglet/media/drivers/pyodide_js/adaptation.py +288 -0
- pyglet/media/drivers/xaudio2/adaptation.py +3 -3
- pyglet/media/player.py +276 -193
- pyglet/media/player_worker_thread.py +1 -1
- pyglet/model/__init__.py +39 -29
- pyglet/model/codecs/base.py +4 -4
- pyglet/model/codecs/gltf.py +3 -3
- pyglet/model/codecs/obj.py +71 -43
- pyglet/resource.py +129 -78
- pyglet/shapes.py +154 -194
- pyglet/sprite.py +47 -164
- pyglet/text/__init__.py +44 -54
- pyglet/text/caret.py +12 -7
- pyglet/text/document.py +19 -17
- pyglet/text/formats/html.py +2 -2
- pyglet/text/formats/structured.py +10 -40
- pyglet/text/layout/__init__.py +20 -13
- pyglet/text/layout/base.py +176 -287
- pyglet/text/layout/incremental.py +9 -10
- pyglet/text/layout/scrolling.py +7 -95
- pyglet/window/__init__.py +183 -172
- pyglet/window/cocoa/__init__.py +62 -51
- pyglet/window/cocoa/pyglet_delegate.py +2 -25
- pyglet/window/cocoa/pyglet_view.py +9 -8
- pyglet/window/dialog/__init__.py +184 -0
- pyglet/window/dialog/base.py +99 -0
- pyglet/window/dialog/darwin.py +121 -0
- pyglet/window/dialog/linux.py +72 -0
- pyglet/window/dialog/windows.py +194 -0
- pyglet/window/emscripten/__init__.py +779 -0
- pyglet/window/headless/__init__.py +44 -28
- pyglet/window/key.py +2 -0
- pyglet/window/mouse.py +2 -2
- pyglet/window/wayland/__init__.py +377 -0
- pyglet/window/win32/__init__.py +101 -46
- pyglet/window/xlib/__init__.py +104 -66
- {pyglet-2.1.13.dist-info → pyglet-3.0.dev1.dist-info}/METADATA +2 -3
- pyglet-3.0.dev1.dist-info/RECORD +322 -0
- {pyglet-2.1.13.dist-info → pyglet-3.0.dev1.dist-info}/WHEEL +1 -1
- pyglet/gl/__init__.py +0 -208
- pyglet/gl/base.py +0 -499
- pyglet/gl/cocoa.py +0 -309
- pyglet/gl/gl.py +0 -4625
- pyglet/gl/gl.pyi +0 -2320
- pyglet/gl/gl_compat.pyi +0 -3097
- pyglet/gl/gl_info.py +0 -190
- pyglet/gl/headless.py +0 -166
- pyglet/gl/wgl_info.py +0 -36
- pyglet/gl/wglext_nv.py +0 -1096
- pyglet/gl/win32.py +0 -268
- pyglet/gl/xlib.py +0 -295
- pyglet/image/buffer.py +0 -274
- pyglet/image/codecs/s3tc.py +0 -354
- pyglet/libs/x11/xrandr.py +0 -166
- pyglet-2.1.13.dist-info/RECORD +0 -234
- /pyglet/{libs/wayland → graphics/api/gl/cocoa}/__init__.py +0 -0
- /pyglet/libs/{egl → linux/egl}/egl.py +0 -0
- /pyglet/libs/{egl → linux/egl}/lib.py +0 -0
- /pyglet/libs/{ioctl.py → linux/ioctl.py} +0 -0
- /pyglet/libs/{wayland → linux/wayland}/gbm.py +0 -0
- /pyglet/libs/{x11 → linux/x11}/__init__.py +0 -0
- /pyglet/libs/{x11 → linux/x11}/cursorfont.py +0 -0
- /pyglet/libs/{x11 → linux/x11}/xlib.py +0 -0
- /pyglet/libs/{x11 → linux/x11}/xsync.py +0 -0
- {pyglet-2.1.13.dist-info/licenses → pyglet-3.0.dev1.dist-info}/LICENSE +0 -0
pyglet/font/quartz.py
CHANGED
|
@@ -2,17 +2,18 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
4
|
import math
|
|
5
|
-
import warnings
|
|
6
5
|
from ctypes import byref, c_int32, c_void_p, string_at, cast, c_char_p, c_float
|
|
7
|
-
from
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from typing import BinaryIO, TYPE_CHECKING
|
|
8
8
|
|
|
9
9
|
import pyglet.image
|
|
10
|
-
from pyglet.
|
|
11
|
-
from pyglet.font.base import Glyph, GlyphPosition
|
|
12
|
-
from pyglet.libs.darwin import CGFloat, cocoapy, kCTFontURLAttribute, cfnumber_to_number,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
from pyglet.enums import Weight, Stretch
|
|
11
|
+
from pyglet.font.base import Glyph, GlyphPosition, GlyphRenderer, Font, get_grapheme_clusters
|
|
12
|
+
from pyglet.libs.darwin import CGFloat, cocoapy, kCTFontURLAttribute, cfnumber_to_number, CGPoint, CFRange
|
|
13
|
+
from pyglet.font.harfbuzz import harfbuzz_available, get_resource_from_ct_font, get_harfbuzz_shaped_glyphs
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from pyglet.font import FontManager
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
cf = cocoapy.cf
|
|
@@ -29,10 +30,37 @@ UIFontWeightBold = 0.4
|
|
|
29
30
|
UIFontWeightHeavy = 0.56
|
|
30
31
|
UIFontWeightBlack = 0.62
|
|
31
32
|
|
|
33
|
+
_UIFontWeightMap: tuple[tuple[float, Weight], ...] = (
|
|
34
|
+
(-0.8, Weight.EXTRALIGHT), # ultra light
|
|
35
|
+
(-0.6, Weight.THIN),
|
|
36
|
+
(-0.4, Weight.LIGHT),
|
|
37
|
+
(0.0, Weight.NORMAL),
|
|
38
|
+
(0.23, Weight.MEDIUM),
|
|
39
|
+
(0.3, Weight.SEMIBOLD),
|
|
40
|
+
(0.4, Weight.BOLD),
|
|
41
|
+
(0.56, Weight.EXTRABOLD),
|
|
42
|
+
(0.62, Weight.BLACK),
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
def _quartz_font_mapping_to_enum(mapping_tuple: tuple, value: float) -> Enum:
|
|
46
|
+
"""Gets the closest matching value for value."""
|
|
47
|
+
closest = min(mapping_tuple, key=lambda w: abs(w[0] - value))
|
|
48
|
+
return closest[1]
|
|
49
|
+
|
|
50
|
+
def _quartz_font_weight_to_enum(weight: float) -> Weight:
|
|
51
|
+
"""Map a UIFont weight float to the closest Weight enum.
|
|
52
|
+
|
|
53
|
+
The value returned is a float and may have imprecision.
|
|
54
|
+
|
|
55
|
+
The test font Action Man Bold produces: 0.4000000059604645
|
|
56
|
+
"""
|
|
57
|
+
return _quartz_font_mapping_to_enum(_UIFontWeightMap, weight)
|
|
58
|
+
|
|
59
|
+
|
|
32
60
|
name_to_weight = {
|
|
33
|
-
True: UIFontWeightBold,
|
|
34
|
-
False: UIFontWeightRegular,
|
|
35
|
-
None: UIFontWeightRegular,
|
|
61
|
+
True: UIFontWeightBold, # Bold as default for True
|
|
62
|
+
False: UIFontWeightRegular, # Regular for False
|
|
63
|
+
None: UIFontWeightRegular, # Regular if no weight provided
|
|
36
64
|
"thin": UIFontWeightThin,
|
|
37
65
|
"extralight": UIFontWeightUltraLight,
|
|
38
66
|
"ultralight": UIFontWeightUltraLight,
|
|
@@ -67,6 +95,56 @@ name_to_stretch = {
|
|
|
67
95
|
"ultraexpanded": 0.4,
|
|
68
96
|
}
|
|
69
97
|
|
|
98
|
+
_UIFontStretchMap = (
|
|
99
|
+
(-0.4, Stretch.ULTRACONDENSED),
|
|
100
|
+
(-0.3, Stretch.EXTRACONDENSED),
|
|
101
|
+
(-0.2, Stretch.CONDENSED),
|
|
102
|
+
(-0.1, Stretch.SEMICONDENSED),
|
|
103
|
+
(0.0, Stretch.NORMAL),
|
|
104
|
+
(-0.1, Stretch.SEMIEXPANDED),
|
|
105
|
+
(0.2, Stretch.EXPANDED),
|
|
106
|
+
(0.3, Stretch.EXTRAEXPANDED),
|
|
107
|
+
(0.4, Stretch.ULTRAEXPANDED),
|
|
108
|
+
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
def _quartz_font_stretch_to_enum(stretch: float) -> Stretch:
|
|
112
|
+
"""Map a UIFont weight float to the closest Weight enum.
|
|
113
|
+
|
|
114
|
+
The value returned is a float and may have imprecision.
|
|
115
|
+
"""
|
|
116
|
+
return _quartz_font_mapping_to_enum(_UIFontStretchMap, stretch)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
kCTFontUIFontUser = 0
|
|
121
|
+
kCTFontUIFontUserFixedPitch = 1
|
|
122
|
+
kCTFontUIFontSystem = 2
|
|
123
|
+
kCTFontUIFontEmphasizedSystem = 3
|
|
124
|
+
kCTFontUIFontSmallSystem = 4
|
|
125
|
+
kCTFontUIFontSmallEmphasizedSystem = 5
|
|
126
|
+
kCTFontUIFontMiniSystem = 6
|
|
127
|
+
kCTFontUIFontMiniEmphasizedSystem = 7
|
|
128
|
+
kCTFontUIFontViews = 8
|
|
129
|
+
kCTFontUIFontApplication = 9
|
|
130
|
+
kCTFontUIFontLabel = 10
|
|
131
|
+
kCTFontUIFontMenuTitle = 11
|
|
132
|
+
kCTFontUIFontMenuItem = 12
|
|
133
|
+
kCTFontUIFontMenuItemMark = 13
|
|
134
|
+
kCTFontUIFontMenuItemCmdKey = 14
|
|
135
|
+
kCTFontUIFontWindowTitle = 15
|
|
136
|
+
kCTFontUIFontPushButton = 16
|
|
137
|
+
kCTFontUIFontUtilityWindowTitle = 17
|
|
138
|
+
kCTFontUIFontAlertHeader = 18
|
|
139
|
+
kCTFontUIFontSystemDetail = 19
|
|
140
|
+
kCTFontUIFontEmphasizedSystemDetail = 20
|
|
141
|
+
kCTFontUIFontToolbar = 21
|
|
142
|
+
kCTFontUIFontSmallToolbar = 22
|
|
143
|
+
kCTFontUIFontMessage = 23
|
|
144
|
+
kCTFontUIFontPalette = 24
|
|
145
|
+
kCTFontUIFontToolTip = 25
|
|
146
|
+
kCTFontUIFontControlContent = 26
|
|
147
|
+
|
|
70
148
|
|
|
71
149
|
if harfbuzz_available():
|
|
72
150
|
"""Build the callbacks and information needed for Harfbuzz to work with CoreText Fonts.
|
|
@@ -75,7 +153,12 @@ if harfbuzz_available():
|
|
|
75
153
|
retrieve the full font bytes from memory, we must construct callbacks for harfbuzz
|
|
76
154
|
to retrieve the tag tables.
|
|
77
155
|
"""
|
|
78
|
-
from pyglet.font.harfbuzz.harfbuzz_lib import
|
|
156
|
+
from pyglet.font.harfbuzz.harfbuzz_lib import (
|
|
157
|
+
hb_lib,
|
|
158
|
+
hb_destroy_func_t,
|
|
159
|
+
hb_reference_table_func_t,
|
|
160
|
+
HB_MEMORY_MODE_READONLY,
|
|
161
|
+
)
|
|
79
162
|
|
|
80
163
|
def py_coretext_table_data_destroy(user_data: c_void_p):
|
|
81
164
|
"""Release the table resources once harfbuzz is done."""
|
|
@@ -105,33 +188,33 @@ if harfbuzz_available():
|
|
|
105
188
|
|
|
106
189
|
# Create a blob that references this table data.
|
|
107
190
|
data_ptr_char = cast(data_ptr, c_char_p)
|
|
108
|
-
blob = hb_lib.hb_blob_create(
|
|
109
|
-
|
|
191
|
+
blob = hb_lib.hb_blob_create(
|
|
192
|
+
data_ptr_char, length, HB_MEMORY_MODE_READONLY, table_data, py_coretext_table_data_destroy_c,
|
|
193
|
+
)
|
|
110
194
|
return blob
|
|
111
195
|
|
|
112
196
|
# Null callback, so harfbuzz cannot destroy our CGFont.
|
|
113
197
|
_destroy_callback_null = cast(None, hb_destroy_func_t)
|
|
114
198
|
|
|
115
|
-
|
|
199
|
+
|
|
200
|
+
class QuartzGlyphRenderer(GlyphRenderer):
|
|
116
201
|
font: QuartzFont
|
|
117
202
|
|
|
118
203
|
def __init__(self, font: QuartzFont) -> None:
|
|
119
204
|
super().__init__(font)
|
|
120
205
|
self.font = font
|
|
121
206
|
|
|
122
|
-
def render_index(self, glyph_index: int):
|
|
123
|
-
ctFont = self.font.ctFont
|
|
124
|
-
|
|
207
|
+
def render_index(self, ct_font: c_void_p, glyph_index: int) -> None:
|
|
125
208
|
# Create an attributed string using text and font.
|
|
126
209
|
# Determine the glyphs involved for the text (if any)
|
|
127
210
|
# Get a bounding rectangle for glyphs in string.
|
|
128
211
|
count = 1
|
|
129
212
|
glyphs = (cocoapy.CGGlyph * count)(glyph_index)
|
|
130
213
|
|
|
131
|
-
rect = ct.CTFontGetBoundingRectsForGlyphs(
|
|
214
|
+
rect = ct.CTFontGetBoundingRectsForGlyphs(ct_font, 0, glyphs, None, count)
|
|
132
215
|
|
|
133
216
|
# Get advance for all glyphs in string.
|
|
134
|
-
advance = ct.CTFontGetAdvancesForGlyphs(
|
|
217
|
+
advance = ct.CTFontGetAdvancesForGlyphs(ct_font, 0, glyphs, None, count)
|
|
135
218
|
|
|
136
219
|
# Set image parameters:
|
|
137
220
|
# We add 2 pixels to the bitmap width and height so that there will be a 1-pixel border
|
|
@@ -148,26 +231,29 @@ class QuartzGlyphRenderer(base.GlyphRenderer):
|
|
|
148
231
|
bits_per_components = 8
|
|
149
232
|
bytes_per_row = 4 * width
|
|
150
233
|
colorSpace = c_void_p(quartz.CGColorSpaceCreateDeviceRGB())
|
|
151
|
-
bitmap_context = c_void_p(
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
234
|
+
bitmap_context = c_void_p(
|
|
235
|
+
quartz.CGBitmapContextCreate(
|
|
236
|
+
None,
|
|
237
|
+
width,
|
|
238
|
+
height,
|
|
239
|
+
bits_per_components,
|
|
240
|
+
bytes_per_row,
|
|
241
|
+
colorSpace,
|
|
242
|
+
cocoapy.kCGImageAlphaPremultipliedLast,
|
|
243
|
+
),
|
|
244
|
+
)
|
|
159
245
|
|
|
160
246
|
# Draw text to bitmap context.
|
|
161
247
|
quartz.CGContextSetShouldAntialias(bitmap_context, pyglet.options.text_antialiasing)
|
|
162
248
|
quartz.CGContextSetTextPosition(bitmap_context, -lsb, baseline)
|
|
163
249
|
quartz.CGContextSetRGBFillColor(bitmap_context, 1, 1, 1, 1)
|
|
164
|
-
quartz.CGContextSetFont(bitmap_context,
|
|
250
|
+
quartz.CGContextSetFont(bitmap_context, ct_font)
|
|
165
251
|
quartz.CGContextSetFontSize(bitmap_context, self.font.pixel_size)
|
|
166
252
|
quartz.CGContextTranslateCTM(bitmap_context, 0, height) # Move origin to top-left
|
|
167
253
|
quartz.CGContextScaleCTM(bitmap_context, 1, -1) # Flip vertically
|
|
168
254
|
|
|
169
255
|
positions = (cocoapy.CGPoint * 1)(*[cocoapy.CGPoint(0, 0)])
|
|
170
|
-
quartz.CTFontDrawGlyphs(
|
|
256
|
+
quartz.CTFontDrawGlyphs(ct_font, glyphs, positions, 1, bitmap_context)
|
|
171
257
|
|
|
172
258
|
# Create an image to get the data out.
|
|
173
259
|
image_ref = c_void_p(quartz.CGBitmapContextCreateImage(bitmap_context))
|
|
@@ -194,7 +280,7 @@ class QuartzGlyphRenderer(base.GlyphRenderer):
|
|
|
194
280
|
|
|
195
281
|
raise Exception("CG Image buffer could not be read.")
|
|
196
282
|
|
|
197
|
-
def render(self, text: str) ->
|
|
283
|
+
def render(self, text: str) -> Glyph:
|
|
198
284
|
# Using CTLineDraw seems to be the only way to make sure that the text
|
|
199
285
|
# is drawn with the specified font when that font is a graphics font loaded from
|
|
200
286
|
# memory. For whatever reason, [NSAttributedString drawAtPoint:] ignores
|
|
@@ -205,10 +291,10 @@ class QuartzGlyphRenderer(base.GlyphRenderer):
|
|
|
205
291
|
|
|
206
292
|
# Create an attributed string using text and font.
|
|
207
293
|
attributes = c_void_p(
|
|
208
|
-
cf.CFDictionaryCreateMutable(None, 2, cf.kCFTypeDictionaryKeyCallBacks, cf.kCFTypeDictionaryValueCallBacks)
|
|
294
|
+
cf.CFDictionaryCreateMutable(None, 2, cf.kCFTypeDictionaryKeyCallBacks, cf.kCFTypeDictionaryValueCallBacks),
|
|
209
295
|
)
|
|
210
296
|
cf.CFDictionaryAddValue(attributes, cocoapy.kCTFontAttributeName, ctFont)
|
|
211
|
-
cf.CFDictionaryAddValue(attributes, cocoapy.kCTForegroundColorFromContextAttributeName
|
|
297
|
+
cf.CFDictionaryAddValue(attributes, cocoapy.kCTForegroundColorFromContextAttributeName, cocoapy.kCFBooleanTrue)
|
|
212
298
|
cf_str = cocoapy.CFSTR(text)
|
|
213
299
|
string = c_void_p(cf.CFAttributedStringCreate(None, cf_str, attributes))
|
|
214
300
|
|
|
@@ -256,14 +342,21 @@ class QuartzGlyphRenderer(base.GlyphRenderer):
|
|
|
256
342
|
bits_per_components = 8
|
|
257
343
|
bytes_per_row = 4 * width
|
|
258
344
|
colorSpace = c_void_p(quartz.CGColorSpaceCreateDeviceRGB())
|
|
259
|
-
bitmap_context = c_void_p(
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
345
|
+
bitmap_context = c_void_p(
|
|
346
|
+
quartz.CGBitmapContextCreate(
|
|
347
|
+
None,
|
|
348
|
+
width,
|
|
349
|
+
height,
|
|
350
|
+
bits_per_components,
|
|
351
|
+
bytes_per_row,
|
|
352
|
+
colorSpace,
|
|
353
|
+
cocoapy.kCGImageAlphaPremultipliedLast,
|
|
354
|
+
),
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
# Transform the context to a top-left origin.
|
|
358
|
+
# quartz.CGContextTranslateCTM(bitmap, 0, height) # Move origin to top-left.
|
|
359
|
+
# quartz.CGContextScaleCTM(bitmap, 1.0, -1.0) # Flip Y-axis.
|
|
267
360
|
|
|
268
361
|
# Draw text to bitmap context.
|
|
269
362
|
quartz.CGContextSetShouldAntialias(bitmap_context, pyglet.options.text_antialiasing)
|
|
@@ -293,78 +386,96 @@ class QuartzGlyphRenderer(base.GlyphRenderer):
|
|
|
293
386
|
glyph_image = pyglet.image.ImageData(width, height, "RGBA", buffer, bytes_per_row)
|
|
294
387
|
|
|
295
388
|
glyph = self.font.create_glyph(glyph_image)
|
|
296
|
-
|
|
389
|
+
|
|
390
|
+
# Flip baseline if it's vulkan since it's top left based.
|
|
391
|
+
# TODO: Revisit when flipping OpenGL coordinates.
|
|
392
|
+
if pyglet.options.backend == "vulkan":
|
|
393
|
+
glyph.set_bearings(height - baseline - self.font.descent - self.font.ascent, lsb, advance)
|
|
394
|
+
else:
|
|
395
|
+
glyph.set_bearings(baseline, lsb, advance)
|
|
297
396
|
|
|
298
397
|
return glyph
|
|
299
398
|
|
|
300
399
|
raise Exception("CG Image buffer could not be read.")
|
|
301
400
|
|
|
302
401
|
|
|
303
|
-
class QuartzFont(
|
|
304
|
-
glyph_renderer_class: type[
|
|
402
|
+
class QuartzFont(Font):
|
|
403
|
+
glyph_renderer_class: type[GlyphRenderer] = QuartzGlyphRenderer
|
|
305
404
|
_loaded_CGFont_table: dict[str, dict[int, tuple[c_void_p, bytes]]] = {}
|
|
306
405
|
|
|
307
|
-
def __init__(
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
self.
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
self.
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
self.italic = italic
|
|
329
|
-
|
|
330
|
-
# Construct traits value.
|
|
331
|
-
traits = 0
|
|
332
|
-
if italic:
|
|
333
|
-
traits |= cocoapy.kCTFontItalicTrait
|
|
334
|
-
|
|
335
|
-
if isinstance(stretch, str):
|
|
336
|
-
self.stretch_value = name_to_stretch[stretch]
|
|
337
|
-
else:
|
|
338
|
-
self.stretch_value = None
|
|
339
|
-
|
|
340
|
-
name = str(name)
|
|
341
|
-
self.traits = traits
|
|
406
|
+
def __init__(
|
|
407
|
+
self,
|
|
408
|
+
name: str,
|
|
409
|
+
size: float,
|
|
410
|
+
weight: str = "normal",
|
|
411
|
+
style: str = "normal",
|
|
412
|
+
stretch: str = "normal",
|
|
413
|
+
dpi: int | None = None,
|
|
414
|
+
) -> None:
|
|
415
|
+
super().__init__(name, size, weight, style, stretch, dpi)
|
|
416
|
+
|
|
417
|
+
# Weight value
|
|
418
|
+
self._weight = name_to_weight[weight]
|
|
419
|
+
|
|
420
|
+
# Construct traits value for italics/obliques.
|
|
421
|
+
self._italic = 0
|
|
422
|
+
if style != "normal" and style is not False:
|
|
423
|
+
self._italic |= cocoapy.kCTFontItalicTrait
|
|
424
|
+
|
|
425
|
+
self._stretch = name_to_stretch[stretch]
|
|
342
426
|
# First see if we can find an appropriate font from our table of loaded fonts.
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
self.ctFont =
|
|
427
|
+
|
|
428
|
+
# MacOS protects default fonts from being queried by name, it has to be done by an enumeration and loaded
|
|
429
|
+
# using a separate function.
|
|
430
|
+
if name == "System Default":
|
|
431
|
+
self.ctFont = self._get_system_default(weight, style)
|
|
348
432
|
else:
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
433
|
+
result = self._lookup_font_with_family_and_traits(name, self._italic)
|
|
434
|
+
if result:
|
|
435
|
+
cgFont = result[0]
|
|
436
|
+
# Use cgFont from table to create a CTFont object with the specified size.
|
|
437
|
+
self.ctFont = c_void_p(ct.CTFontCreateWithGraphicsFont(cgFont, self.pixel_size, None, None))
|
|
438
|
+
else:
|
|
439
|
+
# Create a font descriptor for given name and traits and use it to create font.
|
|
440
|
+
descriptor = self._create_font_descriptor(name, self._italic, self._weight, self._stretch)
|
|
441
|
+
self.ctFont = c_void_p(ct.CTFontCreateWithFontDescriptor(descriptor, self.pixel_size, None))
|
|
442
|
+
cf.CFRelease(descriptor)
|
|
443
|
+
assert self.ctFont, "Couldn't load font: " + name
|
|
354
444
|
|
|
355
445
|
string = c_void_p(ct.CTFontCopyFamilyName(self.ctFont))
|
|
356
446
|
self._family_name = str(cocoapy.cfstring_to_string(string))
|
|
357
447
|
cf.CFRelease(string)
|
|
358
448
|
|
|
359
|
-
self.ascent =
|
|
360
|
-
self.descent = -
|
|
449
|
+
self.ascent = math.ceil(ct.CTFontGetAscent(self.ctFont))
|
|
450
|
+
self.descent = -math.ceil(ct.CTFontGetDescent(self.ctFont))
|
|
361
451
|
|
|
362
452
|
if pyglet.options.text_shaping == 'harfbuzz' and harfbuzz_available():
|
|
363
453
|
self._cg_font = None
|
|
364
454
|
self.hb_resource = get_resource_from_ct_font(self)
|
|
365
455
|
|
|
366
|
-
|
|
367
|
-
|
|
456
|
+
def _get_system_default(self, weight: str, italic_style: str) -> c_void_p:
|
|
457
|
+
if weight == "bold":
|
|
458
|
+
font_enum = kCTFontUIFontEmphasizedSystem
|
|
459
|
+
else:
|
|
460
|
+
font_enum = kCTFontUIFontSystem
|
|
461
|
+
|
|
462
|
+
base_ft = ct.CTFontCreateUIFontForLanguage(font_enum, float(self.pixel_size), None)
|
|
463
|
+
final_ft = base_ft
|
|
464
|
+
if italic_style != "normal":
|
|
465
|
+
italic_font = ct.CTFontCreateCopyWithSymbolicTraits(
|
|
466
|
+
base_ft,
|
|
467
|
+
0.0,
|
|
468
|
+
None,
|
|
469
|
+
cocoapy.kCTFontItalicTrait,
|
|
470
|
+
cocoapy.kCTFontItalicTrait,
|
|
471
|
+
)
|
|
472
|
+
final_ft = italic_font
|
|
473
|
+
cf.CFRelease(base_ft)
|
|
474
|
+
|
|
475
|
+
ct_font = c_void_p(final_ft)
|
|
476
|
+
return ct_font
|
|
477
|
+
|
|
478
|
+
def _get_hb_face(self) -> None:
|
|
368
479
|
assert self._cg_font is None
|
|
369
480
|
|
|
370
481
|
# Create a CGFont from the CTFont for the face.
|
|
@@ -384,7 +495,7 @@ class QuartzFont(base.Font):
|
|
|
384
495
|
"""
|
|
385
496
|
filename = self.filename
|
|
386
497
|
if filename == "Unknown":
|
|
387
|
-
result = self._lookup_font_with_family_and_traits(self.name, self.
|
|
498
|
+
result = self._lookup_font_with_family_and_traits(self.name, self._traits)
|
|
388
499
|
if result:
|
|
389
500
|
_, data = result
|
|
390
501
|
else:
|
|
@@ -397,7 +508,7 @@ class QuartzFont(base.Font):
|
|
|
397
508
|
|
|
398
509
|
@property
|
|
399
510
|
def filename(self) -> str:
|
|
400
|
-
descriptor = self._create_font_descriptor(self.name, self.
|
|
511
|
+
descriptor = self._create_font_descriptor(self.name, self._italic, self._weight, self._stretch)
|
|
401
512
|
ref = c_void_p(ct.CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute))
|
|
402
513
|
if ref:
|
|
403
514
|
url = cocoapy.ObjCInstance(ref) # NSURL
|
|
@@ -411,7 +522,7 @@ class QuartzFont(base.Font):
|
|
|
411
522
|
|
|
412
523
|
@property
|
|
413
524
|
def name(self) -> str:
|
|
414
|
-
return self.
|
|
525
|
+
return self._name
|
|
415
526
|
|
|
416
527
|
def __del__(self) -> None:
|
|
417
528
|
cf.CFRelease(self.ctFont)
|
|
@@ -434,7 +545,7 @@ class QuartzFont(base.Font):
|
|
|
434
545
|
if traits in fonts:
|
|
435
546
|
return fonts[traits]
|
|
436
547
|
# Otherwise try to find a font with some of the traits.
|
|
437
|
-
for
|
|
548
|
+
for t, f in fonts.items():
|
|
438
549
|
if traits & t:
|
|
439
550
|
return f
|
|
440
551
|
# Otherwise try to return a regular font.
|
|
@@ -443,24 +554,28 @@ class QuartzFont(base.Font):
|
|
|
443
554
|
# Otherwise return whatever we have.
|
|
444
555
|
return list(fonts.values())[0]
|
|
445
556
|
|
|
446
|
-
def _create_font_descriptor(
|
|
447
|
-
|
|
448
|
-
|
|
557
|
+
def _create_font_descriptor(
|
|
558
|
+
self, family_name: str, italic_traits: int, weight: float | None = None, stretch: float | None = None,
|
|
559
|
+
) -> c_void_p:
|
|
449
560
|
# Create an attribute dictionary.
|
|
450
561
|
attributes = c_void_p(
|
|
451
|
-
cf.CFDictionaryCreateMutable(None, 0, cf.kCFTypeDictionaryKeyCallBacks, cf.kCFTypeDictionaryValueCallBacks)
|
|
562
|
+
cf.CFDictionaryCreateMutable(None, 0, cf.kCFTypeDictionaryKeyCallBacks, cf.kCFTypeDictionaryValueCallBacks),
|
|
563
|
+
)
|
|
452
564
|
# Add family name to attributes.
|
|
453
565
|
cfname = cocoapy.CFSTR(family_name)
|
|
454
566
|
cf.CFDictionaryAddValue(attributes, cocoapy.kCTFontFamilyNameAttribute, cfname)
|
|
455
567
|
cf.CFRelease(cfname)
|
|
456
568
|
|
|
457
569
|
# Construct a CFNumber to represent the traits.
|
|
458
|
-
itraits = c_int32(
|
|
570
|
+
itraits = c_int32(italic_traits)
|
|
459
571
|
symTraits = c_void_p(cf.CFNumberCreate(None, cocoapy.kCFNumberSInt32Type, byref(itraits)))
|
|
460
572
|
if symTraits:
|
|
461
573
|
# Construct a dictionary to hold the traits values.
|
|
462
|
-
traitsDict = c_void_p(
|
|
463
|
-
|
|
574
|
+
traitsDict = c_void_p(
|
|
575
|
+
cf.CFDictionaryCreateMutable(
|
|
576
|
+
None, 0, cf.kCFTypeDictionaryKeyCallBacks, cf.kCFTypeDictionaryValueCallBacks,
|
|
577
|
+
),
|
|
578
|
+
)
|
|
464
579
|
if traitsDict:
|
|
465
580
|
if weight is not None:
|
|
466
581
|
weight_value = c_float(weight)
|
|
@@ -498,7 +613,7 @@ class QuartzFont(base.Font):
|
|
|
498
613
|
descriptor = ct.CTFontDescriptorCreateWithNameAndSize(cocoapy.CFSTR(name), 0.0)
|
|
499
614
|
matched = ct.CTFontDescriptorCreateMatchingFontDescriptor(descriptor, None)
|
|
500
615
|
|
|
501
|
-
exists =
|
|
616
|
+
exists = bool(matched)
|
|
502
617
|
|
|
503
618
|
if descriptor:
|
|
504
619
|
cf.CFRelease(descriptor)
|
|
@@ -509,7 +624,7 @@ class QuartzFont(base.Font):
|
|
|
509
624
|
return exists
|
|
510
625
|
|
|
511
626
|
@classmethod
|
|
512
|
-
def add_font_data(cls: type[QuartzFont], data: BinaryIO) -> None:
|
|
627
|
+
def add_font_data(cls: type[QuartzFont], data: BinaryIO, manager: FontManager) -> None:
|
|
513
628
|
# Create a cgFont with the data. There doesn't seem to be a way to
|
|
514
629
|
# register a font loaded from memory such that the operating system will
|
|
515
630
|
# find it later. So instead we just store the cgFont in a table where
|
|
@@ -524,7 +639,7 @@ class QuartzFont(base.Font):
|
|
|
524
639
|
quartz.CGDataProviderRelease(provider)
|
|
525
640
|
|
|
526
641
|
# Create a template CTFont from the graphics font so that we can get font info.
|
|
527
|
-
ctFont = c_void_p(ct.CTFontCreateWithGraphicsFont(cgFont, 1, None, None))
|
|
642
|
+
ctFont = c_void_p(ct.CTFontCreateWithGraphicsFont(cgFont, 1.0, None, None))
|
|
528
643
|
|
|
529
644
|
# Get info about the font to use as key in our font table.
|
|
530
645
|
string = c_void_p(ct.CTFontCopyFamilyName(ctFont))
|
|
@@ -536,6 +651,9 @@ class QuartzFont(base.Font):
|
|
|
536
651
|
cf.CFRelease(string)
|
|
537
652
|
|
|
538
653
|
traits = ct.CTFontGetSymbolicTraits(ctFont)
|
|
654
|
+
|
|
655
|
+
weight, italic, stretch = cls._get_font_style_traits(ctFont)
|
|
656
|
+
|
|
539
657
|
cf.CFRelease(ctFont)
|
|
540
658
|
|
|
541
659
|
# Store font in table. We store it under both its family name and its
|
|
@@ -548,11 +666,13 @@ class QuartzFont(base.Font):
|
|
|
548
666
|
cls._loaded_CGFont_table[fullName] = {}
|
|
549
667
|
cls._loaded_CGFont_table[fullName][traits] = (cgFont, data)
|
|
550
668
|
|
|
669
|
+
manager._add_loaded_font({(familyName, weight, italic, stretch)}) # noqa: SLF001
|
|
670
|
+
|
|
551
671
|
def get_text_size(self, text: str) -> tuple[int, int]:
|
|
552
672
|
ctFont = self.ctFont
|
|
553
673
|
|
|
554
674
|
attributes = c_void_p(
|
|
555
|
-
cf.CFDictionaryCreateMutable(None, 1, cf.kCFTypeDictionaryKeyCallBacks, cf.kCFTypeDictionaryValueCallBacks)
|
|
675
|
+
cf.CFDictionaryCreateMutable(None, 1, cf.kCFTypeDictionaryKeyCallBacks, cf.kCFTypeDictionaryValueCallBacks),
|
|
556
676
|
)
|
|
557
677
|
cf.CFDictionaryAddValue(attributes, cocoapy.kCTFontAttributeName, ctFont)
|
|
558
678
|
cf_str = cocoapy.CFSTR(text)
|
|
@@ -570,32 +690,99 @@ class QuartzFont(base.Font):
|
|
|
570
690
|
cf.CFRelease(cf_str)
|
|
571
691
|
return round(width), round(height)
|
|
572
692
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
cf.
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
def _get_font_stretch(self):
|
|
580
|
-
traits = ct.CTFontCopyTraits(self.ctFont)
|
|
693
|
+
@staticmethod
|
|
694
|
+
def _get_font_style_traits(ctFont: c_void_p) -> tuple[Weight, bool, Stretch]:
|
|
695
|
+
traits = ct.CTFontCopyTraits(ctFont)
|
|
696
|
+
font_weight = cfnumber_to_number(c_void_p(cf.CFDictionaryGetValue(traits, cocoapy.kCTFontWeightTrait)))
|
|
697
|
+
font_style = cfnumber_to_number(c_void_p(cf.CFDictionaryGetValue(traits, cocoapy.kCTFontSlantTrait)))
|
|
581
698
|
font_width = cfnumber_to_number(c_void_p(cf.CFDictionaryGetValue(traits, cocoapy.kCTFontWidthTrait)))
|
|
582
699
|
cf.CFRelease(traits)
|
|
583
|
-
return
|
|
700
|
+
return (_quartz_font_weight_to_enum(font_weight),
|
|
701
|
+
True if font_style != 0 else False,
|
|
702
|
+
_quartz_font_stretch_to_enum(font_width))
|
|
584
703
|
|
|
585
|
-
def render_glyph_indices(self, indices: list[int]) -> None:
|
|
704
|
+
def render_glyph_indices(self, ct_font: c_void_p, font_name: str, indices: list[int]) -> None:
|
|
586
705
|
# Process any glyphs that have not been rendered.
|
|
587
706
|
self._initialize_renderer()
|
|
588
707
|
|
|
589
708
|
missing = set()
|
|
590
709
|
for glyph_indice in set(indices):
|
|
591
|
-
|
|
710
|
+
dict_key = (font_name, glyph_indice)
|
|
711
|
+
if dict_key not in self.glyphs:
|
|
592
712
|
missing.add(glyph_indice)
|
|
593
713
|
|
|
594
714
|
# Missing glyphs, get their info.
|
|
595
715
|
for glyph_indice in missing:
|
|
596
|
-
self.glyphs[glyph_indice] = self._glyph_renderer.render_index(glyph_indice)
|
|
716
|
+
self.glyphs[(font_name, glyph_indice)] = self._glyph_renderer.render_index(ct_font, glyph_indice)
|
|
717
|
+
|
|
718
|
+
def _get_shaped_glyphs(self, text: str) -> tuple[list[Glyph], list[GlyphPosition]]:
|
|
719
|
+
attributes = c_void_p(
|
|
720
|
+
cf.CFDictionaryCreateMutable(None, 1, cf.kCFTypeDictionaryKeyCallBacks, cf.kCFTypeDictionaryValueCallBacks)
|
|
721
|
+
)
|
|
722
|
+
cf.CFDictionaryAddValue(attributes, cocoapy.kCTFontAttributeName, self.ctFont)
|
|
723
|
+
cf_str = cocoapy.CFSTR(text)
|
|
724
|
+
string = c_void_p(cf.CFAttributedStringCreate(None, cf_str, attributes))
|
|
725
|
+
|
|
726
|
+
line = c_void_p(ct.CTLineCreateWithAttributedString(string))
|
|
727
|
+
run_array = c_void_p(ct.CTLineGetGlyphRuns(line))
|
|
728
|
+
all_glyphs = []
|
|
729
|
+
all_positions = []
|
|
730
|
+
if run_array:
|
|
731
|
+
for run in cocoapy.cfarray_to_list(run_array):
|
|
732
|
+
count = ct.CTRunGetGlyphCount(run)
|
|
733
|
+
|
|
734
|
+
glyph_array = (cocoapy.CGGlyph * count)()
|
|
735
|
+
position_array = (CGPoint * count)()
|
|
597
736
|
|
|
598
|
-
|
|
737
|
+
run_attributes = c_void_p(ct.CTRunGetAttributes(run))
|
|
738
|
+
|
|
739
|
+
# Store Glyph by (font name, index ID), as run's can contain more than 1 font.
|
|
740
|
+
run_ft = c_void_p(cf.CFDictionaryGetValue(run_attributes, cocoapy.kCTFontAttributeName))
|
|
741
|
+
cfstring = c_void_p(ct.CTFontCopyPostScriptName(run_ft))
|
|
742
|
+
font_name = str(cocoapy.cfstring_to_string(cfstring))
|
|
743
|
+
|
|
744
|
+
ct.CTRunGetGlyphs(run, cocoapy.CFRange(0, 0), glyph_array)
|
|
745
|
+
ct.CTRunGetPositions(run, cocoapy.CFRange(0, 0), position_array)
|
|
746
|
+
|
|
747
|
+
# Get the offset of the run inside the full line
|
|
748
|
+
run_start_x = position_array[0].x
|
|
749
|
+
run_start_y = position_array[0].y
|
|
750
|
+
|
|
751
|
+
run_glyphs = []
|
|
752
|
+
run_positions = []
|
|
753
|
+
|
|
754
|
+
# Ensure all glyphs are rendered and stored.
|
|
755
|
+
self.render_glyph_indices(run_ft, font_name, list(glyph_array))
|
|
756
|
+
|
|
757
|
+
for i in range(count):
|
|
758
|
+
glyph_index = glyph_array[i]
|
|
759
|
+
|
|
760
|
+
glyph = self._glyph_renderer.render_index(run_ft, glyph_index)
|
|
761
|
+
run_glyphs.append(glyph)
|
|
762
|
+
current_x = position_array[i].x - run_start_x
|
|
763
|
+
current_y = position_array[i].y - run_start_y
|
|
764
|
+
|
|
765
|
+
if i + 1 < count:
|
|
766
|
+
next_x = position_array[i + 1].x - run_start_x
|
|
767
|
+
next_y = position_array[i + 1].y - run_start_y
|
|
768
|
+
kerning_x = next_x - current_x - run_glyphs[i].advance
|
|
769
|
+
kerning_y = next_y - current_y
|
|
770
|
+
else:
|
|
771
|
+
kerning_x = 0
|
|
772
|
+
kerning_y = 0
|
|
773
|
+
|
|
774
|
+
run_positions.append(GlyphPosition(kerning_x, kerning_y, 0, 0))
|
|
775
|
+
|
|
776
|
+
all_glyphs.extend(run_glyphs)
|
|
777
|
+
all_positions.extend(run_positions)
|
|
778
|
+
|
|
779
|
+
cf.CFRelease(string)
|
|
780
|
+
cf.CFRelease(attributes)
|
|
781
|
+
cf.CFRelease(cf_str)
|
|
782
|
+
cf.CFRelease(line)
|
|
783
|
+
return all_glyphs, all_positions
|
|
784
|
+
|
|
785
|
+
def get_glyphs(self, text: str, shaping: bool) -> tuple[list[Glyph], list[GlyphPosition]]:
|
|
599
786
|
"""Create and return a list of Glyphs for `text`.
|
|
600
787
|
|
|
601
788
|
If any characters do not have a known glyph representation in this
|
|
@@ -604,20 +791,26 @@ class QuartzFont(base.Font):
|
|
|
604
791
|
Args:
|
|
605
792
|
text:
|
|
606
793
|
Text to render.
|
|
794
|
+
shaping:
|
|
795
|
+
If the text will be shaped using the global option.
|
|
607
796
|
"""
|
|
608
797
|
self._initialize_renderer()
|
|
609
798
|
|
|
610
|
-
if
|
|
611
|
-
|
|
799
|
+
if shaping:
|
|
800
|
+
if pyglet.options.text_shaping == "platform":
|
|
801
|
+
return self._get_shaped_glyphs(text)
|
|
802
|
+
|
|
803
|
+
if pyglet.options.text_shaping == "harfbuzz" and harfbuzz_available():
|
|
804
|
+
return get_harfbuzz_shaped_glyphs(self, text)
|
|
612
805
|
|
|
613
806
|
glyphs = [] # glyphs that are committed.
|
|
614
807
|
offsets = []
|
|
615
|
-
for c in
|
|
808
|
+
for c in get_grapheme_clusters(str(text)):
|
|
616
809
|
if c == "\t":
|
|
617
810
|
c = " " # noqa: PLW2901
|
|
618
811
|
if c not in self.glyphs:
|
|
619
812
|
self.glyphs[c] = self._glyph_renderer.render(c)
|
|
620
813
|
glyphs.append(self.glyphs[c])
|
|
621
|
-
offsets.append(
|
|
814
|
+
offsets.append(GlyphPosition(0, 0, 0, 0))
|
|
622
815
|
|
|
623
|
-
return glyphs, offsets
|
|
816
|
+
return glyphs, offsets
|