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/dwrite/__init__.py
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
3
4
|
import math
|
|
4
5
|
import pathlib
|
|
5
6
|
from ctypes import POINTER, Array, byref, c_void_p, cast, create_unicode_buffer, pointer, py_object, sizeof, string_at
|
|
6
7
|
from ctypes.wintypes import BOOL, FLOAT, UINT
|
|
7
8
|
from enum import Flag
|
|
8
|
-
from typing import TYPE_CHECKING, BinaryIO, Sequence
|
|
9
|
+
from typing import TYPE_CHECKING, BinaryIO, Sequence, Generator
|
|
9
10
|
|
|
10
11
|
import pyglet
|
|
11
|
-
from pyglet.font import base
|
|
12
|
+
from pyglet.font import base, FontManager
|
|
12
13
|
from pyglet.font.base import Glyph, GlyphPosition
|
|
13
14
|
from pyglet.font.dwrite.d2d1_lib import (
|
|
14
15
|
D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT,
|
|
@@ -104,6 +105,7 @@ from pyglet.font.dwrite.dwrite_lib import (
|
|
|
104
105
|
IID_IDWriteLocalFontFileLoader,
|
|
105
106
|
LegacyCollectionLoader,
|
|
106
107
|
LegacyFontFileLoader,
|
|
108
|
+
DWRITE_INFORMATIONAL_STRING_FULL_NAME,
|
|
107
109
|
)
|
|
108
110
|
from pyglet.font.harfbuzz import get_harfbuzz_shaped_glyphs, get_resource_from_dw_font, harfbuzz_available
|
|
109
111
|
from pyglet.image.codecs.wincodec_lib import GUID_WICPixelFormat32bppPBGRA
|
|
@@ -132,9 +134,9 @@ except ImportError as err:
|
|
|
132
134
|
|
|
133
135
|
|
|
134
136
|
name_to_weight = {
|
|
135
|
-
True: DWRITE_FONT_WEIGHT_BOLD,
|
|
136
|
-
False: DWRITE_FONT_WEIGHT_NORMAL,
|
|
137
|
-
None: DWRITE_FONT_WEIGHT_NORMAL,
|
|
137
|
+
True: DWRITE_FONT_WEIGHT_BOLD, # Temporary alias for attributed text
|
|
138
|
+
False: DWRITE_FONT_WEIGHT_NORMAL, # Temporary alias for attributed text
|
|
139
|
+
None: DWRITE_FONT_WEIGHT_NORMAL, # Temporary alias for attributed text
|
|
138
140
|
"thin": DWRITE_FONT_WEIGHT_THIN,
|
|
139
141
|
"extralight": DWRITE_FONT_WEIGHT_EXTRA_LIGHT,
|
|
140
142
|
"ultralight": DWRITE_FONT_WEIGHT_ULTRA_LIGHT,
|
|
@@ -152,6 +154,26 @@ name_to_weight = {
|
|
|
152
154
|
"heavy": DWRITE_FONT_WEIGHT_HEAVY,
|
|
153
155
|
"extrablack": DWRITE_FONT_WEIGHT_EXTRA_BLACK,
|
|
154
156
|
}
|
|
157
|
+
|
|
158
|
+
weight_to_name = {
|
|
159
|
+
DWRITE_FONT_WEIGHT_BOLD: "bold",
|
|
160
|
+
DWRITE_FONT_WEIGHT_NORMAL: "normal",
|
|
161
|
+
DWRITE_FONT_WEIGHT_THIN: "thin",
|
|
162
|
+
DWRITE_FONT_WEIGHT_EXTRA_LIGHT: "extralight",
|
|
163
|
+
DWRITE_FONT_WEIGHT_ULTRA_LIGHT: "ultralight",
|
|
164
|
+
DWRITE_FONT_WEIGHT_LIGHT: "light",
|
|
165
|
+
DWRITE_FONT_WEIGHT_SEMI_LIGHT: "semilight",
|
|
166
|
+
DWRITE_FONT_WEIGHT_REGULAR: "regular",
|
|
167
|
+
DWRITE_FONT_WEIGHT_MEDIUM: "medium",
|
|
168
|
+
DWRITE_FONT_WEIGHT_DEMI_BOLD: "demibold",
|
|
169
|
+
DWRITE_FONT_WEIGHT_SEMI_BOLD: "semibold",
|
|
170
|
+
DWRITE_FONT_WEIGHT_EXTRA_BOLD: "extrabold",
|
|
171
|
+
DWRITE_FONT_WEIGHT_ULTRA_BOLD: "ultrabold",
|
|
172
|
+
DWRITE_FONT_WEIGHT_BLACK: "black",
|
|
173
|
+
DWRITE_FONT_WEIGHT_HEAVY: "heavy",
|
|
174
|
+
DWRITE_FONT_WEIGHT_EXTRA_BLACK: "extrablack",
|
|
175
|
+
}
|
|
176
|
+
|
|
155
177
|
name_to_stretch = {
|
|
156
178
|
"undefined": DWRITE_FONT_STRETCH_UNDEFINED,
|
|
157
179
|
"ultracondensed": DWRITE_FONT_STRETCH_ULTRA_CONDENSED,
|
|
@@ -165,12 +187,27 @@ name_to_stretch = {
|
|
|
165
187
|
"extraexpanded": DWRITE_FONT_STRETCH_EXTRA_EXPANDED,
|
|
166
188
|
"narrow": DWRITE_FONT_STRETCH_CONDENSED,
|
|
167
189
|
}
|
|
190
|
+
stretch_to_name = {
|
|
191
|
+
DWRITE_FONT_STRETCH_UNDEFINED: "undefined",
|
|
192
|
+
DWRITE_FONT_STRETCH_ULTRA_CONDENSED: "ultracondensed",
|
|
193
|
+
DWRITE_FONT_STRETCH_EXTRA_CONDENSED: "extracondensed",
|
|
194
|
+
DWRITE_FONT_STRETCH_CONDENSED: "condensed",
|
|
195
|
+
DWRITE_FONT_STRETCH_SEMI_CONDENSED: "semicondensed",
|
|
196
|
+
DWRITE_FONT_STRETCH_NORMAL: "normal",
|
|
197
|
+
DWRITE_FONT_STRETCH_SEMI_EXPANDED: "semiexpanded",
|
|
198
|
+
DWRITE_FONT_STRETCH_EXPANDED: "expanded",
|
|
199
|
+
DWRITE_FONT_STRETCH_EXTRA_EXPANDED: "extraexpanded",
|
|
200
|
+
}
|
|
168
201
|
name_to_style = {
|
|
169
202
|
"normal": DWRITE_FONT_STYLE_NORMAL,
|
|
170
203
|
"oblique": DWRITE_FONT_STYLE_OBLIQUE,
|
|
171
204
|
"italic": DWRITE_FONT_STYLE_ITALIC,
|
|
172
205
|
}
|
|
173
|
-
|
|
206
|
+
style_to_name = {
|
|
207
|
+
DWRITE_FONT_STYLE_NORMAL: "normal",
|
|
208
|
+
DWRITE_FONT_STYLE_OBLIQUE: "oblique",
|
|
209
|
+
DWRITE_FONT_STYLE_ITALIC: "italic",
|
|
210
|
+
}
|
|
174
211
|
|
|
175
212
|
class DWRITE_GLYPH_IMAGE_FORMAT_FLAG(Flag):
|
|
176
213
|
NONE = 0x00000000
|
|
@@ -185,7 +222,6 @@ class DWRITE_GLYPH_IMAGE_FORMAT_FLAG(Flag):
|
|
|
185
222
|
COLR_PAINT_TREE = 0x00000100
|
|
186
223
|
|
|
187
224
|
|
|
188
|
-
|
|
189
225
|
def get_system_locale() -> str:
|
|
190
226
|
"""Retrieve the string representing the system locale."""
|
|
191
227
|
local_name = create_unicode_buffer(LOCALE_NAME_MAX_LENGTH)
|
|
@@ -199,6 +235,7 @@ class _DWriteTextRenderer(com.COMObject):
|
|
|
199
235
|
This allows the use of DirectWrite shaping to offload manual shaping, fallback detection, glyph combining, and
|
|
200
236
|
other complicated scenarios.
|
|
201
237
|
"""
|
|
238
|
+
|
|
202
239
|
_interfaces_ = [IDWriteTextRenderer] # noqa: RUF012
|
|
203
240
|
|
|
204
241
|
def __init__(self) -> None:
|
|
@@ -217,7 +254,7 @@ class _DWriteTextRenderer(com.COMObject):
|
|
|
217
254
|
def DrawUnderline(self, *_args) -> int: # noqa: ANN002, N802
|
|
218
255
|
return com.E_NOTIMPL
|
|
219
256
|
|
|
220
|
-
def DrawStrikethrough(self, *_args)-> int: # noqa: ANN002, N802
|
|
257
|
+
def DrawStrikethrough(self, *_args) -> int: # noqa: ANN002, N802
|
|
221
258
|
return com.E_NOTIMPL
|
|
222
259
|
|
|
223
260
|
def DrawInlineObject(self, *_args) -> int: # noqa: ANN002, N802
|
|
@@ -235,15 +272,19 @@ class _DWriteTextRenderer(com.COMObject):
|
|
|
235
272
|
transform[0] = self.dmatrix
|
|
236
273
|
return 0
|
|
237
274
|
|
|
238
|
-
def DrawGlyphRun(
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
275
|
+
def DrawGlyphRun(
|
|
276
|
+
self,
|
|
277
|
+
drawing_context: c_void_p,
|
|
278
|
+
_baseline_x: float,
|
|
279
|
+
_baseline_y: float,
|
|
280
|
+
mode: int,
|
|
281
|
+
glyph_run_ptr: POINTER(DWRITE_GLYPH_RUN),
|
|
282
|
+
_run_des: POINTER(DWRITE_GLYPH_RUN_DESCRIPTION),
|
|
283
|
+
_effect: c_void_p,
|
|
284
|
+
) -> int:
|
|
244
285
|
c_buf = create_unicode_buffer(_run_des.contents.text)
|
|
245
286
|
|
|
246
|
-
c_wchar_txt = c_buf[:_run_des.contents.textLength]
|
|
287
|
+
c_wchar_txt = c_buf[: _run_des.contents.textLength]
|
|
247
288
|
pystr_len = len(c_wchar_txt)
|
|
248
289
|
|
|
249
290
|
glyph_renderer: DirectWriteGlyphRenderer = cast(drawing_context, py_object).value
|
|
@@ -282,10 +323,10 @@ class _DWriteTextRenderer(com.COMObject):
|
|
|
282
323
|
# In some cases (italics) the offsets may be NULL.
|
|
283
324
|
if glyph_run.glyphOffsets:
|
|
284
325
|
offset = base.GlyphPosition(
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
326
|
+
(glyph_run.glyphAdvances[i] - glyph.advance),
|
|
327
|
+
0,
|
|
328
|
+
glyph_run.glyphOffsets[i].advanceOffset,
|
|
329
|
+
glyph_run.glyphOffsets[i].ascenderOffset,
|
|
289
330
|
)
|
|
290
331
|
|
|
291
332
|
else:
|
|
@@ -306,35 +347,42 @@ class _DWriteTextRenderer(com.COMObject):
|
|
|
306
347
|
|
|
307
348
|
_renderer = _DWriteTextRenderer()
|
|
308
349
|
|
|
309
|
-
def get_glyph_metrics(font_face: IDWriteFontFace, indices: Array[UINT16], count: int) -> list[
|
|
310
|
-
tuple[float, float, float, float, float]]:
|
|
311
|
-
"""Obtain metrics for the specific string.
|
|
312
350
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
351
|
+
def get_glyph_metrics(
|
|
352
|
+
font_face: IDWriteFontFace, indices: Array[UINT16], count: int,
|
|
353
|
+
) -> list[tuple[float, float, float, float, float]]:
|
|
354
|
+
"""Obtain metrics for the specific string.
|
|
355
|
+
|
|
356
|
+
Returns:
|
|
357
|
+
A list of tuples with the following metrics per indice:
|
|
358
|
+
. (glyph width, glyph height, left side bearing, advance width, bottom side bearing)
|
|
359
|
+
"""
|
|
360
|
+
glyph_metrics = (DWRITE_GLYPH_METRICS * count)()
|
|
361
|
+
font_face.GetDesignGlyphMetrics(indices, count, glyph_metrics, False)
|
|
319
362
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
363
|
+
metrics_out = []
|
|
364
|
+
for metric in glyph_metrics:
|
|
365
|
+
glyph_width = metric.advanceWidth + abs(metric.leftSideBearing) + abs(metric.rightSideBearing)
|
|
366
|
+
glyph_height = metric.advanceHeight - metric.topSideBearing - metric.bottomSideBearing
|
|
324
367
|
|
|
325
|
-
|
|
326
|
-
|
|
368
|
+
lsb = metric.leftSideBearing
|
|
369
|
+
bsb = metric.bottomSideBearing
|
|
327
370
|
|
|
328
|
-
|
|
371
|
+
advance_width = metric.advanceWidth
|
|
329
372
|
|
|
330
|
-
|
|
373
|
+
metrics_out.append((glyph_width, glyph_height, lsb, advance_width, bsb))
|
|
374
|
+
|
|
375
|
+
return metrics_out
|
|
331
376
|
|
|
332
|
-
return metrics_out
|
|
333
377
|
|
|
334
378
|
class DirectWriteGlyphRenderer(base.GlyphRenderer): # noqa: D101
|
|
335
379
|
current_run: list[DWRITE_GLYPH_RUN]
|
|
336
380
|
font: Win32DirectWriteFont
|
|
337
|
-
antialias_mode =
|
|
381
|
+
antialias_mode = (
|
|
382
|
+
D2D1_TEXT_ANTIALIAS_MODE_DEFAULT
|
|
383
|
+
if pyglet.options.text_antialiasing is True
|
|
384
|
+
else D2D1_TEXT_ANTIALIAS_MODE_ALIASED
|
|
385
|
+
)
|
|
338
386
|
draw_options = D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT if WINDOWS_8_1_OR_GREATER else D2D1_DRAW_TEXT_OPTIONS_NONE
|
|
339
387
|
measuring_mode = DWRITE_MEASURING_MODE_NATURAL
|
|
340
388
|
|
|
@@ -391,10 +439,7 @@ class DirectWriteGlyphRenderer(base.GlyphRenderer): # noqa: D101
|
|
|
391
439
|
|
|
392
440
|
rt.Clear(transparent)
|
|
393
441
|
|
|
394
|
-
rt.DrawTextLayout(no_offset,
|
|
395
|
-
text_layout,
|
|
396
|
-
self._brush,
|
|
397
|
-
self.draw_options)
|
|
442
|
+
rt.DrawTextLayout(no_offset, text_layout, self._brush, self.draw_options)
|
|
398
443
|
|
|
399
444
|
rt.EndDraw(None, None)
|
|
400
445
|
|
|
@@ -402,10 +447,9 @@ class DirectWriteGlyphRenderer(base.GlyphRenderer): # noqa: D101
|
|
|
402
447
|
|
|
403
448
|
return wic.extract_image_data(bitmap, wic_fmt)
|
|
404
449
|
|
|
405
|
-
def render_single_glyph(
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
mode: int) -> base.Glyph:
|
|
450
|
+
def render_single_glyph(
|
|
451
|
+
self, font_face: IDWriteFontFace, indice: int, metrics: tuple[float, float, float, float, float], mode: int,
|
|
452
|
+
) -> base.Glyph:
|
|
409
453
|
"""Renders a single glyph indice using Direct2D."""
|
|
410
454
|
glyph_width, glyph_height, glyph_lsb, glyph_advance, glyph_bsb = metrics
|
|
411
455
|
|
|
@@ -451,8 +495,7 @@ class DirectWriteGlyphRenderer(base.GlyphRenderer): # noqa: D101
|
|
|
451
495
|
self._create_bitmap(render_width, render_height)
|
|
452
496
|
|
|
453
497
|
# Glyphs are drawn at the baseline, and with LSB, so we need to offset it based on top left position.
|
|
454
|
-
baseline_offset = D2D_POINT_2F(-render_offset_x,
|
|
455
|
-
self.font.ascent)
|
|
498
|
+
baseline_offset = D2D_POINT_2F(-render_offset_x, self.font.ascent)
|
|
456
499
|
|
|
457
500
|
self._render_target.BeginDraw()
|
|
458
501
|
|
|
@@ -499,21 +542,15 @@ class DirectWriteGlyphRenderer(base.GlyphRenderer): # noqa: D101
|
|
|
499
542
|
baseline_offset,
|
|
500
543
|
color_run.contents.glyphRun,
|
|
501
544
|
self.measuring_mode,
|
|
502
|
-
|
|
545
|
+
)
|
|
503
546
|
else:
|
|
504
547
|
glyph_run = color_run.contents.glyphRun
|
|
505
|
-
self._render_target.DrawGlyphRun(baseline_offset,
|
|
506
|
-
glyph_run,
|
|
507
|
-
brush,
|
|
508
|
-
mode)
|
|
548
|
+
self._render_target.DrawGlyphRun(baseline_offset, glyph_run, brush, mode)
|
|
509
549
|
enumerator.Release()
|
|
510
550
|
if temp_brush:
|
|
511
551
|
temp_brush.Release()
|
|
512
552
|
else:
|
|
513
|
-
self._render_target.DrawGlyphRun(baseline_offset,
|
|
514
|
-
run,
|
|
515
|
-
self._brush,
|
|
516
|
-
mode)
|
|
553
|
+
self._render_target.DrawGlyphRun(baseline_offset, run, self._brush, mode)
|
|
517
554
|
|
|
518
555
|
self._render_target.EndDraw(None, None)
|
|
519
556
|
|
|
@@ -521,12 +558,13 @@ class DirectWriteGlyphRenderer(base.GlyphRenderer): # noqa: D101
|
|
|
521
558
|
|
|
522
559
|
glyph = self.font.create_glyph(image)
|
|
523
560
|
|
|
524
|
-
glyph.set_bearings(-self.font.descent, render_offset_x,
|
|
525
|
-
glyph_advance * self.font.font_scale_ratio)
|
|
561
|
+
glyph.set_bearings(-self.font.descent, render_offset_x, glyph_advance * self.font.font_scale_ratio)
|
|
526
562
|
|
|
527
563
|
return glyph
|
|
528
564
|
|
|
529
|
-
def _get_color_enumerator(
|
|
565
|
+
def _get_color_enumerator(
|
|
566
|
+
self, dwrite_run: DWRITE_GLYPH_RUN,
|
|
567
|
+
) -> IDWriteColorGlyphRunEnumerator | IDWriteColorGlyphRunEnumerator1 | None:
|
|
530
568
|
"""Obtain a color enumerator if possible."""
|
|
531
569
|
try:
|
|
532
570
|
if WINDOWS_10_CREATORS_UPDATE_OR_GREATER:
|
|
@@ -545,7 +583,8 @@ class DirectWriteGlyphRenderer(base.GlyphRenderer): # noqa: D101
|
|
|
545
583
|
elif WINDOWS_8_1_OR_GREATER:
|
|
546
584
|
enumerator = IDWriteColorGlyphRunEnumerator()
|
|
547
585
|
self.font._write_factory.TranslateColorGlyphRun(
|
|
548
|
-
0.0,
|
|
586
|
+
0.0,
|
|
587
|
+
0.0,
|
|
549
588
|
dwrite_run,
|
|
550
589
|
None,
|
|
551
590
|
self.measuring_mode,
|
|
@@ -590,10 +629,7 @@ class DirectWriteGlyphRenderer(base.GlyphRenderer): # noqa: D101
|
|
|
590
629
|
|
|
591
630
|
self._render_target.Clear(transparent)
|
|
592
631
|
|
|
593
|
-
self._render_target.DrawTextLayout(point,
|
|
594
|
-
text_layout,
|
|
595
|
-
self._brush,
|
|
596
|
-
self.draw_options)
|
|
632
|
+
self._render_target.DrawTextLayout(point, text_layout, self._brush, self.draw_options)
|
|
597
633
|
|
|
598
634
|
self._render_target.EndDraw(None, None)
|
|
599
635
|
|
|
@@ -646,9 +682,10 @@ def _get_font_file(font_face: IDWriteFontFace) -> IDWriteFontFile:
|
|
|
646
682
|
font_files = (IDWriteFontFile * file_ct.value)()
|
|
647
683
|
font_face.GetFiles(byref(file_ct), font_files)
|
|
648
684
|
|
|
649
|
-
return font_files[
|
|
685
|
+
return font_files[font_face.GetIndex()]
|
|
650
686
|
|
|
651
|
-
|
|
687
|
+
|
|
688
|
+
def _get_font_ref(font_file: IDWriteFontFile, release_file: bool = True) -> tuple[c_void_p, int]:
|
|
652
689
|
"""Get a unique font reference for the font face.
|
|
653
690
|
|
|
654
691
|
Callbacks will generate new addresses for the same IDWriteFontFace, so a unique value
|
|
@@ -669,9 +706,152 @@ def _get_font_ref(font_file: IDWriteFontFile, release_file: bool=True) -> tuple[
|
|
|
669
706
|
|
|
670
707
|
return key_data, ff_key_size.value
|
|
671
708
|
|
|
709
|
+
def _get_font_face_names(font_family: IDWriteFontFamily, locale: str) -> set[str]:
|
|
710
|
+
face_names = set()
|
|
711
|
+
ct = font_family.GetFontCount()
|
|
712
|
+
for i in range(ct):
|
|
713
|
+
temp_ft = IDWriteFont()
|
|
714
|
+
font_family.GetFont(i, byref(temp_ft))
|
|
715
|
+
|
|
716
|
+
# When a font is loaded, it may not include things like bold or italic. These get simulated
|
|
717
|
+
# by the font renderer. Ignore those and only utilize those not actually simulated.
|
|
718
|
+
if temp_ft.GetSimulations() == 0:
|
|
719
|
+
exists = BOOL()
|
|
720
|
+
lstring = IDWriteLocalizedStrings()
|
|
721
|
+
|
|
722
|
+
temp_ft.GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_FULL_NAME, byref(lstring), byref(exists))
|
|
723
|
+
|
|
724
|
+
if exists.value:
|
|
725
|
+
full_name = ''.join(_unpack_localized_string(lstring, locale))
|
|
726
|
+
|
|
727
|
+
weight_name = weight_to_name.get(temp_ft.GetWeight(), "normal")
|
|
728
|
+
style_name = style_to_name.get(temp_ft.GetStyle(), "normal")
|
|
729
|
+
stretch_name = stretch_to_name.get(temp_ft.GetStretch(), "normal")
|
|
730
|
+
|
|
731
|
+
face_names.add((full_name, weight_name, style_name, stretch_name))
|
|
732
|
+
|
|
733
|
+
temp_ft.Release()
|
|
734
|
+
|
|
735
|
+
return face_names
|
|
736
|
+
|
|
737
|
+
def _get_font_families(collection: IDWriteFontCollection) -> Generator[IDWriteFontFamily, None, None]:
|
|
738
|
+
"""Generate IDWriteFontFamily objects associated with this collection."""
|
|
739
|
+
ct = collection.GetFontFamilyCount()
|
|
740
|
+
|
|
741
|
+
for i in range(ct):
|
|
742
|
+
family = IDWriteFontFamily()
|
|
743
|
+
collection.GetFontFamily(i, byref(family))
|
|
744
|
+
try:
|
|
745
|
+
yield family
|
|
746
|
+
finally:
|
|
747
|
+
family.Release()
|
|
748
|
+
|
|
749
|
+
def _get_font_collection_family_names(collection: IDWriteFontCollection) -> set[str]:
|
|
750
|
+
"""Retrieve the family names of font's from the font collection."""
|
|
751
|
+
locale = get_system_locale()
|
|
752
|
+
|
|
753
|
+
names = set()
|
|
754
|
+
|
|
755
|
+
for family in _get_font_families(collection):
|
|
756
|
+
family_name_str = IDWriteLocalizedStrings()
|
|
757
|
+
family.GetFamilyNames(byref(family_name_str))
|
|
758
|
+
family_names = _unpack_localized_string(family_name_str, locale)
|
|
759
|
+
|
|
760
|
+
names.update(family_names)
|
|
761
|
+
|
|
762
|
+
return names
|
|
763
|
+
|
|
764
|
+
def _get_font_collection_family_data(collection: IDWriteFontCollection) -> set[str]:
|
|
765
|
+
"""Retrieve font data information associated with this collection.
|
|
766
|
+
|
|
767
|
+
The function will iterate over every font family, and then the faces of that family.
|
|
768
|
+
"""
|
|
769
|
+
locale = get_system_locale()
|
|
770
|
+
|
|
771
|
+
data = set()
|
|
772
|
+
|
|
773
|
+
for family in _get_font_families(collection):
|
|
774
|
+
family_name_str = IDWriteLocalizedStrings()
|
|
775
|
+
family.GetFamilyNames(byref(family_name_str))
|
|
776
|
+
family_name = ''.join(_unpack_localized_string(family_name_str, locale))
|
|
777
|
+
|
|
778
|
+
face_data = _get_font_face_names(family, locale)
|
|
779
|
+
|
|
780
|
+
family_data = [(family_name, weight, italic, stretch) for (_, weight, italic, stretch) in face_data]
|
|
781
|
+
|
|
782
|
+
data.update(family_data)
|
|
783
|
+
|
|
784
|
+
return data
|
|
785
|
+
|
|
786
|
+
def _get_font_collection_face_names(collection: IDWriteFontCollection) -> set[tuple[str, str, str, str]]:
|
|
787
|
+
"""Retrieve the font face names of all font's from the collection.
|
|
788
|
+
|
|
789
|
+
The function will iterate over every font family, and then the faces of that family.
|
|
790
|
+
|
|
791
|
+
Returns:
|
|
792
|
+
A set of tuples containing: the family name, the weight, the style, and the stretch.
|
|
793
|
+
"""
|
|
794
|
+
locale = get_system_locale()
|
|
795
|
+
|
|
796
|
+
names = set()
|
|
797
|
+
|
|
798
|
+
for family in _get_font_families(collection):
|
|
799
|
+
family_name_str = IDWriteLocalizedStrings()
|
|
800
|
+
family.GetFamilyNames(byref(family_name_str))
|
|
801
|
+
face_names = _get_font_face_names(family, locale)
|
|
802
|
+
names.update(face_names)
|
|
803
|
+
|
|
804
|
+
return names
|
|
805
|
+
|
|
806
|
+
def _get_localized_index(strings: IDWriteLocalizedStrings, locale: str) -> int:
|
|
807
|
+
idx = UINT32()
|
|
808
|
+
exists = BOOL()
|
|
809
|
+
|
|
810
|
+
if locale:
|
|
811
|
+
strings.FindLocaleName(locale, byref(idx), byref(exists))
|
|
812
|
+
|
|
813
|
+
if not exists.value:
|
|
814
|
+
# fallback to english.
|
|
815
|
+
strings.FindLocaleName("en-us", byref(idx), byref(exists))
|
|
816
|
+
|
|
817
|
+
if not exists:
|
|
818
|
+
return 0
|
|
819
|
+
|
|
820
|
+
return idx.value
|
|
821
|
+
|
|
822
|
+
return 0
|
|
823
|
+
|
|
824
|
+
def _unpack_localized_string(local_string: IDWriteLocalizedStrings, locale: str) -> list[str]:
|
|
825
|
+
"""Takes IDWriteLocalizedStrings and unpacks the strings inside of it into a list.
|
|
826
|
+
|
|
827
|
+
..note:: local_string is released before this function returns.
|
|
828
|
+
"""
|
|
829
|
+
str_array_len = local_string.GetCount()
|
|
830
|
+
|
|
831
|
+
strings = []
|
|
832
|
+
for _ in range(str_array_len):
|
|
833
|
+
string_size = UINT32()
|
|
834
|
+
|
|
835
|
+
idx = _get_localized_index(local_string, locale)
|
|
836
|
+
|
|
837
|
+
local_string.GetStringLength(idx, byref(string_size))
|
|
838
|
+
|
|
839
|
+
buffer_size = string_size.value
|
|
840
|
+
|
|
841
|
+
buffer = create_unicode_buffer(buffer_size + 1)
|
|
842
|
+
|
|
843
|
+
local_string.GetString(idx, buffer, len(buffer))
|
|
844
|
+
|
|
845
|
+
strings.append(buffer.value)
|
|
846
|
+
|
|
847
|
+
local_string.Release()
|
|
848
|
+
|
|
849
|
+
return strings
|
|
850
|
+
|
|
672
851
|
|
|
673
852
|
class Win32DirectWriteFont(base.Font):
|
|
674
853
|
"""DirectWrite Font object for Windows 7+."""
|
|
854
|
+
|
|
675
855
|
# To load fonts from files, we need to produce a custom collection.
|
|
676
856
|
_custom_collection = None
|
|
677
857
|
|
|
@@ -688,31 +868,27 @@ class Win32DirectWriteFont(base.Font):
|
|
|
688
868
|
_font_cache = []
|
|
689
869
|
_font_loader_key = None
|
|
690
870
|
|
|
691
|
-
_default_name = "Segoe UI" # Default font for Windows 7+
|
|
692
|
-
|
|
693
871
|
_glyph_renderer = None
|
|
694
872
|
_empty_glyph = None
|
|
695
873
|
_zero_glyph = None
|
|
696
874
|
|
|
697
875
|
glyph_renderer_class = DirectWriteGlyphRenderer
|
|
698
|
-
texture_internalformat = pyglet.gl.GL_RGBA
|
|
699
876
|
|
|
700
|
-
def __init__(
|
|
701
|
-
|
|
877
|
+
def __init__( # noqa: D107
|
|
878
|
+
self,
|
|
879
|
+
name: str,
|
|
880
|
+
size: float,
|
|
881
|
+
weight: str = "normal",
|
|
882
|
+
style: str = "normal",
|
|
883
|
+
stretch: str = "normal",
|
|
884
|
+
dpi: int | None = None,
|
|
885
|
+
locale: str | None = None,
|
|
886
|
+
) -> None:
|
|
702
887
|
self._filename: str | None = None
|
|
703
888
|
|
|
704
|
-
super().__init__()
|
|
705
|
-
|
|
706
|
-
if not name:
|
|
707
|
-
name = self._default_name
|
|
889
|
+
super().__init__(name, size, weight, style, stretch, dpi)
|
|
708
890
|
|
|
709
891
|
self.buffers = []
|
|
710
|
-
self._name = name
|
|
711
|
-
self.weight = weight
|
|
712
|
-
self.size = size
|
|
713
|
-
self.italic = italic
|
|
714
|
-
self.stretch = stretch
|
|
715
|
-
self.dpi = dpi
|
|
716
892
|
self.locale = locale
|
|
717
893
|
|
|
718
894
|
if self.locale is None:
|
|
@@ -720,17 +896,11 @@ class Win32DirectWriteFont(base.Font):
|
|
|
720
896
|
self.rtl = False # Right to left should be handled by pyglet?
|
|
721
897
|
# Use system locale string?
|
|
722
898
|
|
|
723
|
-
if self.dpi is None:
|
|
724
|
-
self.dpi = 96
|
|
725
|
-
|
|
726
|
-
# From DPI to DIP (Device Independent Pixels) which is what the fonts rely on.
|
|
727
|
-
self.pixel_size = (self.size * self.dpi) // 72
|
|
728
|
-
|
|
729
899
|
self._weight = name_to_weight[self.weight]
|
|
730
900
|
|
|
731
|
-
if self.
|
|
732
|
-
if isinstance(self.
|
|
733
|
-
self._style = name_to_style[self.
|
|
901
|
+
if self.style:
|
|
902
|
+
if isinstance(self.style, str):
|
|
903
|
+
self._style = name_to_style[self.style]
|
|
734
904
|
else:
|
|
735
905
|
self._style = DWRITE_FONT_STYLE_ITALIC
|
|
736
906
|
else:
|
|
@@ -747,8 +917,11 @@ class Win32DirectWriteFont(base.Font):
|
|
|
747
917
|
self._font_index, self._collection = self.get_collection(name)
|
|
748
918
|
write_font = None
|
|
749
919
|
# If not font found, search all collections for legacy GDI naming.
|
|
750
|
-
if pyglet.options["dw_legacy_naming"] and (self._font_index is None and self._collection is None):
|
|
751
|
-
|
|
920
|
+
#if pyglet.options["dw_legacy_naming"] and (self._font_index is None and self._collection is None):
|
|
921
|
+
# write_font, self._collection = self.find_font_face(name, self._weight, self._style, self._stretch)
|
|
922
|
+
|
|
923
|
+
# if self._collection is None:
|
|
924
|
+
# write_font, self._collection = self.find_font_face(self._default_name, self._weight, self._style, self._stretch)
|
|
752
925
|
|
|
753
926
|
assert self._collection is not None, f"Font: '{name}' not found in loaded or system font collection."
|
|
754
927
|
|
|
@@ -793,7 +966,7 @@ class Win32DirectWriteFont(base.Font):
|
|
|
793
966
|
self._font_metrics = DWRITE_FONT_METRICS()
|
|
794
967
|
self.font_face.GetMetrics(byref(self._font_metrics))
|
|
795
968
|
|
|
796
|
-
self.font_scale_ratio =
|
|
969
|
+
self.font_scale_ratio = self.pixel_size / self._font_metrics.designUnitsPerEm
|
|
797
970
|
|
|
798
971
|
self.ascent = math.ceil(self._font_metrics.ascent * self.font_scale_ratio)
|
|
799
972
|
self.descent = -round(self._font_metrics.descent * self.font_scale_ratio)
|
|
@@ -876,7 +1049,7 @@ class Win32DirectWriteFont(base.Font):
|
|
|
876
1049
|
def name(self) -> str:
|
|
877
1050
|
return self._name
|
|
878
1051
|
|
|
879
|
-
def render_to_image(self, text: str, width: int=10000, height: int=80) -> ImageData:
|
|
1052
|
+
def render_to_image(self, text: str, width: int = 10000, height: int = 80) -> ImageData:
|
|
880
1053
|
"""This process uses only DirectWrite to shape and render text for layout and graphics.
|
|
881
1054
|
|
|
882
1055
|
This may allow more accurate fonts (bidi, rtl, etc) in very special circumstances at the cost of
|
|
@@ -926,51 +1099,36 @@ class Win32DirectWriteFont(base.Font):
|
|
|
926
1099
|
metrics = get_glyph_metrics(self.font_face, (UINT16 * len(missing))(*missing), len(missing))
|
|
927
1100
|
|
|
928
1101
|
for idx, glyph_indice in enumerate(missing):
|
|
929
|
-
glyph = self._glyph_renderer.render_single_glyph(
|
|
930
|
-
|
|
1102
|
+
glyph = self._glyph_renderer.render_single_glyph(
|
|
1103
|
+
self.font_face, glyph_indice, metrics[idx], self._glyph_renderer.measuring_mode,
|
|
1104
|
+
)
|
|
931
1105
|
self.glyphs[glyph_indice] = glyph
|
|
932
1106
|
|
|
933
|
-
def _get_fallback_glyph(self, text: str) -> base.Glyph:
|
|
934
|
-
for fallback in self.fallbacks:
|
|
935
|
-
indices, missing = fallback.get_glyph_indices(text)
|
|
936
|
-
# If the amount of indices match what's missing, nothing was retrieved.
|
|
937
|
-
if len(indices) == len(missing):
|
|
938
|
-
continue
|
|
939
|
-
# Fallback should render the glyphs it found.
|
|
940
|
-
fallback.render_glyph_indices(indices)
|
|
941
|
-
for indice in indices:
|
|
942
|
-
if indice != 0:
|
|
943
|
-
return fallback.glyphs[indice]
|
|
944
|
-
|
|
945
|
-
return self.glyphs[0]
|
|
946
1107
|
|
|
947
|
-
def get_glyphs(self, text: str) -> tuple[list[Glyph], list[base.GlyphPosition]]:
|
|
1108
|
+
def get_glyphs(self, text: str, shaping: bool) -> tuple[list[Glyph], list[base.GlyphPosition]]:
|
|
948
1109
|
self._initialize_renderer()
|
|
949
1110
|
|
|
950
|
-
if
|
|
951
|
-
|
|
1111
|
+
if shaping:
|
|
1112
|
+
if pyglet.options.text_shaping == 'harfbuzz' and harfbuzz_available():
|
|
1113
|
+
return get_harfbuzz_shaped_glyphs(self, text)
|
|
952
1114
|
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
1115
|
+
if pyglet.options.text_shaping == 'platform':
|
|
1116
|
+
self._glyph_renderer.current_glyphs.clear()
|
|
1117
|
+
self._glyph_renderer.current_offsets.clear()
|
|
1118
|
+
text_layout = self.create_text_layout(text)
|
|
957
1119
|
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1120
|
+
ptr = cast(id(self._glyph_renderer), c_void_p)
|
|
1121
|
+
text_layout.Draw(ptr, _renderer.as_interface(IDWriteTextRenderer), 0, 0)
|
|
1122
|
+
text_layout.Release()
|
|
961
1123
|
|
|
962
|
-
|
|
1124
|
+
return self._glyph_renderer.current_glyphs, self._glyph_renderer.current_offsets
|
|
963
1125
|
|
|
964
1126
|
glyphs = []
|
|
965
1127
|
offsets = []
|
|
966
1128
|
indices, missing = self.get_glyph_indices(text)
|
|
967
1129
|
self.render_glyph_indices(indices)
|
|
968
1130
|
for i, indice in enumerate(indices):
|
|
969
|
-
|
|
970
|
-
glyph = self._get_fallback_glyph(missing[i])
|
|
971
|
-
glyphs.append(glyph)
|
|
972
|
-
else:
|
|
973
|
-
glyphs.append(self.glyphs[indice])
|
|
1131
|
+
glyphs.append(self.glyphs[indice])
|
|
974
1132
|
offsets.append(GlyphPosition(0, 0, 0, 0))
|
|
975
1133
|
|
|
976
1134
|
return glyphs, offsets
|
|
@@ -984,8 +1142,11 @@ class Win32DirectWriteFont(base.Font):
|
|
|
984
1142
|
|
|
985
1143
|
text_layout = IDWriteTextLayout()
|
|
986
1144
|
self._write_factory.CreateTextLayout(
|
|
987
|
-
text_buffer,
|
|
988
|
-
|
|
1145
|
+
text_buffer,
|
|
1146
|
+
len(text_buffer) - 1,
|
|
1147
|
+
self._text_format,
|
|
1148
|
+
10000,
|
|
1149
|
+
10000, # Doesn't affect glyph, bitmap size.
|
|
989
1150
|
byref(text_layout),
|
|
990
1151
|
)
|
|
991
1152
|
|
|
@@ -1034,7 +1195,21 @@ class Win32DirectWriteFont(base.Font):
|
|
|
1034
1195
|
cls._font_loader_key = cast(create_unicode_buffer("legacy_font_loader"), c_void_p)
|
|
1035
1196
|
|
|
1036
1197
|
@classmethod
|
|
1037
|
-
def
|
|
1198
|
+
def get_added_fonts(cls: type[Win32DirectWriteFont]) -> list[str]:
|
|
1199
|
+
"""Retrieves the added Font Family names from the custom font set.
|
|
1200
|
+
|
|
1201
|
+
Note that this API functionality is only supported in Windows 10 1607 and higher.
|
|
1202
|
+
"""
|
|
1203
|
+
if not cls._write_factory:
|
|
1204
|
+
cls._initialize_direct_write()
|
|
1205
|
+
|
|
1206
|
+
if not cls._font_loader:
|
|
1207
|
+
cls._initialize_custom_loaders()
|
|
1208
|
+
|
|
1209
|
+
return _get_font_collection_family_data(cls._custom_collection)
|
|
1210
|
+
|
|
1211
|
+
@classmethod
|
|
1212
|
+
def add_font_data(cls: type[Win32DirectWriteFont], data: BinaryIO, font_manager: FontManager) -> None:
|
|
1038
1213
|
if not cls._write_factory:
|
|
1039
1214
|
cls._initialize_direct_write()
|
|
1040
1215
|
|
|
@@ -1043,11 +1218,9 @@ class Win32DirectWriteFont(base.Font):
|
|
|
1043
1218
|
|
|
1044
1219
|
if WINDOWS_10_CREATORS_UPDATE_OR_GREATER:
|
|
1045
1220
|
font_file = IDWriteFontFile()
|
|
1046
|
-
cls._font_loader.CreateInMemoryFontFileReference(
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
None,
|
|
1050
|
-
byref(font_file))
|
|
1221
|
+
cls._font_loader.CreateInMemoryFontFileReference(
|
|
1222
|
+
cls._write_factory, data, len(data), None, byref(font_file),
|
|
1223
|
+
)
|
|
1051
1224
|
|
|
1052
1225
|
hr = cls._font_builder.AddFontFile(font_file)
|
|
1053
1226
|
if hr != 0:
|
|
@@ -1086,13 +1259,17 @@ class Win32DirectWriteFont(base.Font):
|
|
|
1086
1259
|
|
|
1087
1260
|
cls._custom_collection = IDWriteFontCollection()
|
|
1088
1261
|
|
|
1089
|
-
cls._write_factory.CreateCustomFontCollection(
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1262
|
+
cls._write_factory.CreateCustomFontCollection(
|
|
1263
|
+
cls._font_collection_loader,
|
|
1264
|
+
cls._font_loader_key,
|
|
1265
|
+
sizeof(cls._font_loader_key),
|
|
1266
|
+
byref(cls._custom_collection),
|
|
1267
|
+
)
|
|
1268
|
+
|
|
1269
|
+
font_manager._add_loaded_font(cls.get_added_fonts())
|
|
1093
1270
|
|
|
1094
1271
|
@classmethod
|
|
1095
|
-
def get_collection(cls: type[Win32DirectWriteFont],
|
|
1272
|
+
def get_collection(cls: type[Win32DirectWriteFont], name: str) -> tuple[int | None, IDWriteFontCollection1 | None]:
|
|
1096
1273
|
"""Obtain a collection of fonts based on the font name.
|
|
1097
1274
|
|
|
1098
1275
|
Returns:
|
|
@@ -1107,9 +1284,9 @@ class Win32DirectWriteFont(base.Font):
|
|
|
1107
1284
|
|
|
1108
1285
|
# Check custom loaded font collections.
|
|
1109
1286
|
if cls._custom_collection:
|
|
1110
|
-
cls._custom_collection.FindFamilyName(
|
|
1111
|
-
|
|
1112
|
-
|
|
1287
|
+
cls._custom_collection.FindFamilyName(
|
|
1288
|
+
create_unicode_buffer(name), byref(font_index), byref(font_exists),
|
|
1289
|
+
)
|
|
1113
1290
|
|
|
1114
1291
|
if font_exists.value:
|
|
1115
1292
|
return font_index.value, cls._custom_collection
|
|
@@ -1119,9 +1296,7 @@ class Win32DirectWriteFont(base.Font):
|
|
|
1119
1296
|
sys_collection = IDWriteFontCollection()
|
|
1120
1297
|
if not font_exists.value:
|
|
1121
1298
|
cls._write_factory.GetSystemFontCollection(byref(sys_collection), 1)
|
|
1122
|
-
sys_collection.FindFamilyName(create_unicode_buffer(
|
|
1123
|
-
byref(font_index),
|
|
1124
|
-
byref(font_exists))
|
|
1299
|
+
sys_collection.FindFamilyName(create_unicode_buffer(name), byref(font_index), byref(font_exists))
|
|
1125
1300
|
|
|
1126
1301
|
if font_exists.value:
|
|
1127
1302
|
return font_index.value, sys_collection
|
|
@@ -1129,8 +1304,9 @@ class Win32DirectWriteFont(base.Font):
|
|
|
1129
1304
|
return None, None
|
|
1130
1305
|
|
|
1131
1306
|
@classmethod
|
|
1132
|
-
def find_font_face(
|
|
1133
|
-
|
|
1307
|
+
def find_font_face(
|
|
1308
|
+
cls, font_name: str, weight: str, italic: str, stretch: str,
|
|
1309
|
+
) -> tuple[IDWriteFont | None, IDWriteFontCollection | None]:
|
|
1134
1310
|
"""Search font collections for legacy RBIZ names.
|
|
1135
1311
|
|
|
1136
1312
|
Matching to weight, italic, stretch is problematic in that there are many values. Attempt to parse the font
|
|
@@ -1199,7 +1375,14 @@ class Win32DirectWriteFont(base.Font):
|
|
|
1199
1375
|
return found_weight, found_style, found_stretch
|
|
1200
1376
|
|
|
1201
1377
|
@staticmethod
|
|
1202
|
-
def find_legacy_font(
|
|
1378
|
+
def find_legacy_font(
|
|
1379
|
+
collection: IDWriteFontCollection,
|
|
1380
|
+
font_name: str,
|
|
1381
|
+
weight: str,
|
|
1382
|
+
style: bool | str,
|
|
1383
|
+
stretch: bool | str,
|
|
1384
|
+
full_debug: bool = False,
|
|
1385
|
+
) -> IDWriteFont | None:
|
|
1203
1386
|
coll_count = collection.GetFontFamilyCount()
|
|
1204
1387
|
|
|
1205
1388
|
assert _debug_print(f"directwrite: Found {coll_count} fonts in collection.")
|
|
@@ -1214,7 +1397,7 @@ class Win32DirectWriteFont(base.Font):
|
|
|
1214
1397
|
family_name_str = IDWriteLocalizedStrings()
|
|
1215
1398
|
family.GetFamilyNames(byref(family_name_str))
|
|
1216
1399
|
|
|
1217
|
-
family_names =
|
|
1400
|
+
family_names = _unpack_localized_string(family_name_str, locale)
|
|
1218
1401
|
family_name = family_names[0]
|
|
1219
1402
|
|
|
1220
1403
|
if family_name[0] != font_name[0]:
|
|
@@ -1236,7 +1419,7 @@ class Win32DirectWriteFont(base.Font):
|
|
|
1236
1419
|
fc_str = IDWriteLocalizedStrings()
|
|
1237
1420
|
temp_ft.GetFaceNames(byref(fc_str))
|
|
1238
1421
|
|
|
1239
|
-
strings =
|
|
1422
|
+
strings = _unpack_localized_string(fc_str, locale)
|
|
1240
1423
|
face_names.extend(strings)
|
|
1241
1424
|
|
|
1242
1425
|
assert _debug_print(f"directwrite: Face names found: {strings}")
|
|
@@ -1244,18 +1427,19 @@ class Win32DirectWriteFont(base.Font):
|
|
|
1244
1427
|
# Check for GDI compatibility name
|
|
1245
1428
|
compat_names = IDWriteLocalizedStrings()
|
|
1246
1429
|
exists = BOOL()
|
|
1247
|
-
temp_ft.GetInformationalStrings(
|
|
1248
|
-
|
|
1249
|
-
|
|
1430
|
+
temp_ft.GetInformationalStrings(
|
|
1431
|
+
DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES, byref(compat_names), byref(exists),
|
|
1432
|
+
)
|
|
1250
1433
|
|
|
1251
1434
|
# Successful in finding GDI name.
|
|
1252
1435
|
match_found = False
|
|
1253
1436
|
if exists.value != 0:
|
|
1254
|
-
for compat_name in
|
|
1437
|
+
for compat_name in _unpack_localized_string(compat_names, locale):
|
|
1255
1438
|
if compat_name == font_name:
|
|
1256
1439
|
assert _debug_print(
|
|
1257
1440
|
f"Found legacy name '{font_name}' as '{family_name}' in font face '{j}' (collection "
|
|
1258
|
-
f"id #{i})."
|
|
1441
|
+
f"id #{i}).",
|
|
1442
|
+
)
|
|
1259
1443
|
|
|
1260
1444
|
match_found = True
|
|
1261
1445
|
matches.append((temp_ft.GetWeight(), temp_ft.GetStyle(), temp_ft.GetStretch(), temp_ft))
|
|
@@ -1269,7 +1453,7 @@ class Win32DirectWriteFont(base.Font):
|
|
|
1269
1453
|
|
|
1270
1454
|
# If we have matches, we've already parsed through the proper family. Now try to match.
|
|
1271
1455
|
if matches:
|
|
1272
|
-
write_font = Win32DirectWriteFont.match_closest_font(matches, weight,
|
|
1456
|
+
write_font = Win32DirectWriteFont.match_closest_font(matches, weight, style, stretch)
|
|
1273
1457
|
|
|
1274
1458
|
# Cleanup other matches not used.
|
|
1275
1459
|
for match in matches:
|
|
@@ -1281,7 +1465,9 @@ class Win32DirectWriteFont(base.Font):
|
|
|
1281
1465
|
return None
|
|
1282
1466
|
|
|
1283
1467
|
@staticmethod
|
|
1284
|
-
def match_closest_font(
|
|
1468
|
+
def match_closest_font(
|
|
1469
|
+
font_list: list[tuple[int, int, int, IDWriteFont]], weight: str, italic: int, stretch: int,
|
|
1470
|
+
) -> IDWriteFont | None:
|
|
1285
1471
|
"""Match the closest font to the parameters specified.
|
|
1286
1472
|
|
|
1287
1473
|
If a full match is not found, a secondary match will be found based on similar features. This can probably
|
|
@@ -1294,7 +1480,8 @@ class Win32DirectWriteFont(base.Font):
|
|
|
1294
1480
|
# Found perfect match, no need for the rest.
|
|
1295
1481
|
if f_weight == weight and f_style == italic and f_stretch == stretch:
|
|
1296
1482
|
_debug_print(
|
|
1297
|
-
f"directwrite: full match found. (weight: {f_weight}, italic: {f_style}, stretch: {f_stretch})"
|
|
1483
|
+
f"directwrite: full match found. (weight: {f_weight}, italic: {f_style}, stretch: {f_stretch})",
|
|
1484
|
+
)
|
|
1298
1485
|
return writefont
|
|
1299
1486
|
|
|
1300
1487
|
prop_match = 0
|
|
@@ -1324,56 +1511,14 @@ class Win32DirectWriteFont(base.Font):
|
|
|
1324
1511
|
if closest:
|
|
1325
1512
|
# Take the first match after sorting.
|
|
1326
1513
|
closest_match = closest[0]
|
|
1327
|
-
_debug_print(
|
|
1328
|
-
|
|
1514
|
+
_debug_print(
|
|
1515
|
+
f"directwrite: falling back to partial match. "
|
|
1516
|
+
f"(weight: {closest_match[2]}, italic: {closest_match[3]}, stretch: {closest_match[4]})",
|
|
1517
|
+
)
|
|
1329
1518
|
return closest_match[5]
|
|
1330
1519
|
|
|
1331
1520
|
return None
|
|
1332
1521
|
|
|
1333
|
-
@staticmethod
|
|
1334
|
-
def unpack_localized_string(local_string: IDWriteLocalizedStrings, locale: str) -> list[str]:
|
|
1335
|
-
"""Takes IDWriteLocalizedStrings and unpacks the strings inside of it into a list."""
|
|
1336
|
-
str_array_len = local_string.GetCount()
|
|
1337
|
-
|
|
1338
|
-
strings = []
|
|
1339
|
-
for _ in range(str_array_len):
|
|
1340
|
-
string_size = UINT32()
|
|
1341
|
-
|
|
1342
|
-
idx = Win32DirectWriteFont.get_localized_index(local_string, locale)
|
|
1343
|
-
|
|
1344
|
-
local_string.GetStringLength(idx, byref(string_size))
|
|
1345
|
-
|
|
1346
|
-
buffer_size = string_size.value
|
|
1347
|
-
|
|
1348
|
-
buffer = create_unicode_buffer(buffer_size + 1)
|
|
1349
|
-
|
|
1350
|
-
local_string.GetString(idx, buffer, len(buffer))
|
|
1351
|
-
|
|
1352
|
-
strings.append(buffer.value)
|
|
1353
|
-
|
|
1354
|
-
local_string.Release()
|
|
1355
|
-
|
|
1356
|
-
return strings
|
|
1357
|
-
|
|
1358
|
-
@staticmethod
|
|
1359
|
-
def get_localized_index(strings: IDWriteLocalizedStrings, locale: str) -> int:
|
|
1360
|
-
idx = UINT32()
|
|
1361
|
-
exists = BOOL()
|
|
1362
|
-
|
|
1363
|
-
if locale:
|
|
1364
|
-
strings.FindLocaleName(locale, byref(idx), byref(exists))
|
|
1365
|
-
|
|
1366
|
-
if not exists.value:
|
|
1367
|
-
# fallback to english.
|
|
1368
|
-
strings.FindLocaleName("en-us", byref(idx), byref(exists))
|
|
1369
|
-
|
|
1370
|
-
if not exists:
|
|
1371
|
-
return 0
|
|
1372
|
-
|
|
1373
|
-
return idx.value
|
|
1374
|
-
|
|
1375
|
-
return 0
|
|
1376
|
-
|
|
1377
1522
|
|
|
1378
1523
|
d2d_factory = ID2D1Factory()
|
|
1379
1524
|
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, IID_ID2D1Factory, None, byref(d2d_factory))
|