pyglet 2.1.3__py3-none-any.whl → 2.1.4__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.
Files changed (61) hide show
  1. pyglet/__init__.py +21 -9
  2. pyglet/__init__.pyi +3 -1
  3. pyglet/app/cocoa.py +6 -3
  4. pyglet/display/win32.py +14 -15
  5. pyglet/display/xlib_vidmoderestore.py +1 -1
  6. pyglet/extlibs/earcut.py +2 -2
  7. pyglet/font/__init__.py +3 -3
  8. pyglet/font/base.py +118 -51
  9. pyglet/font/dwrite/__init__.py +1381 -0
  10. pyglet/font/dwrite/d2d1_lib.py +637 -0
  11. pyglet/font/dwrite/d2d1_types_lib.py +60 -0
  12. pyglet/font/dwrite/dwrite_lib.py +1577 -0
  13. pyglet/font/fontconfig.py +79 -16
  14. pyglet/font/freetype.py +252 -77
  15. pyglet/font/freetype_lib.py +234 -125
  16. pyglet/font/harfbuzz/__init__.py +275 -0
  17. pyglet/font/harfbuzz/harfbuzz_lib.py +212 -0
  18. pyglet/font/quartz.py +432 -112
  19. pyglet/font/user.py +18 -11
  20. pyglet/font/win32.py +9 -1
  21. pyglet/gl/wgl.py +94 -87
  22. pyglet/gl/wglext_arb.py +472 -218
  23. pyglet/gl/wglext_nv.py +410 -188
  24. pyglet/gui/widgets.py +6 -1
  25. pyglet/image/codecs/bmp.py +3 -5
  26. pyglet/image/codecs/gdiplus.py +28 -9
  27. pyglet/image/codecs/wic.py +198 -489
  28. pyglet/image/codecs/wincodec_lib.py +413 -0
  29. pyglet/input/base.py +3 -2
  30. pyglet/input/macos/darwin_hid.py +28 -2
  31. pyglet/input/win32/directinput.py +2 -1
  32. pyglet/input/win32/xinput.py +10 -9
  33. pyglet/lib.py +14 -2
  34. pyglet/libs/darwin/cocoapy/cocoalibs.py +74 -3
  35. pyglet/libs/darwin/coreaudio.py +0 -2
  36. pyglet/libs/win32/__init__.py +4 -2
  37. pyglet/libs/win32/com.py +65 -12
  38. pyglet/libs/win32/constants.py +1 -0
  39. pyglet/libs/win32/dinput.py +1 -9
  40. pyglet/libs/win32/types.py +72 -8
  41. pyglet/media/codecs/coreaudio.py +1 -0
  42. pyglet/media/codecs/wmf.py +93 -72
  43. pyglet/media/devices/win32.py +5 -4
  44. pyglet/media/drivers/directsound/lib_dsound.py +4 -4
  45. pyglet/media/drivers/xaudio2/interface.py +21 -17
  46. pyglet/media/drivers/xaudio2/lib_xaudio2.py +42 -25
  47. pyglet/shapes.py +1 -1
  48. pyglet/text/document.py +7 -53
  49. pyglet/text/formats/attributed.py +3 -1
  50. pyglet/text/formats/plaintext.py +1 -1
  51. pyglet/text/formats/structured.py +1 -1
  52. pyglet/text/layout/base.py +76 -68
  53. pyglet/text/layout/incremental.py +38 -8
  54. pyglet/text/layout/scrolling.py +1 -1
  55. pyglet/text/runlist.py +2 -114
  56. pyglet/window/win32/__init__.py +1 -3
  57. {pyglet-2.1.3.dist-info → pyglet-2.1.4.dist-info}/METADATA +1 -1
  58. {pyglet-2.1.3.dist-info → pyglet-2.1.4.dist-info}/RECORD +60 -54
  59. pyglet/font/directwrite.py +0 -2798
  60. {pyglet-2.1.3.dist-info → pyglet-2.1.4.dist-info}/LICENSE +0 -0
  61. {pyglet-2.1.3.dist-info → pyglet-2.1.4.dist-info}/WHEEL +0 -0
@@ -1,2798 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import copy
4
- import math
5
- import os
6
- import pathlib
7
- import platform
8
- from ctypes import (
9
- HRESULT,
10
- POINTER,
11
- Array,
12
- Structure,
13
- byref,
14
- c_int,
15
- c_int16,
16
- c_int32,
17
- c_ubyte,
18
- c_uint8,
19
- c_uint16,
20
- c_uint32,
21
- c_uint64,
22
- c_void_p,
23
- c_wchar,
24
- c_wchar_p,
25
- cast,
26
- create_unicode_buffer,
27
- pointer,
28
- sizeof,
29
- windll,
30
- )
31
- from ctypes.wintypes import BOOL, FLOAT, HDC, UINT, WCHAR
32
- from typing import TYPE_CHECKING, BinaryIO, NoReturn
33
-
34
- import pyglet
35
- from pyglet.font import base
36
- from pyglet.image import ImageData
37
- from pyglet.image.codecs.wic import GUID_WICPixelFormat32bppPBGRA, IWICBitmap, WICDecoder
38
- from pyglet.libs.win32 import LOGFONTW, c_void, com
39
- from pyglet.libs.win32 import _kernel32 as kernel32
40
- from pyglet.libs.win32.constants import (
41
- LOCALE_NAME_MAX_LENGTH,
42
- WINDOWS_8_1_OR_GREATER,
43
- WINDOWS_10_CREATORS_UPDATE_OR_GREATER,
44
- )
45
- from pyglet.util import debug_print
46
-
47
- if TYPE_CHECKING:
48
- from pyglet.font.base import Glyph
49
-
50
- try:
51
- dwrite = "dwrite"
52
-
53
- # System32 and SysWOW64 folders are opposite perception in Windows x64.
54
- # System32 = x64 dll's | SysWOW64 = x86 dlls
55
- # By default ctypes only seems to look in system32 regardless of Python architecture, which has x64 dlls.
56
- if platform.architecture()[0] == "32bit":
57
- if platform.machine().endswith("64"): # Machine is 64 bit, Python is 32 bit.
58
- dwrite = os.path.join(os.environ["WINDIR"], "SysWOW64", "dwrite.dll")
59
-
60
- dwrite_lib = windll.LoadLibrary(dwrite)
61
- except OSError:
62
- # Doesn't exist? Should stop import of library.
63
- msg = "DirectWrite Not Found"
64
- raise ImportError(msg) # noqa: B904
65
-
66
- _debug_font = pyglet.options["debug_font"]
67
-
68
- _debug_print = debug_print("debug_font")
69
-
70
-
71
- def DWRITE_MAKE_OPENTYPE_TAG(a: str, b: str, c: str, d: str) -> int:
72
- return ord(d) << 24 | ord(c) << 16 | ord(b) << 8 | ord(a)
73
-
74
-
75
- DWRITE_FACTORY_TYPE = UINT
76
- DWRITE_FACTORY_TYPE_SHARED = 0
77
- DWRITE_FACTORY_TYPE_ISOLATED = 1
78
-
79
- DWRITE_FONT_WEIGHT = UINT
80
- DWRITE_FONT_WEIGHT_THIN = 100
81
- DWRITE_FONT_WEIGHT_EXTRA_LIGHT = 200
82
- DWRITE_FONT_WEIGHT_ULTRA_LIGHT = 200
83
- DWRITE_FONT_WEIGHT_LIGHT = 300
84
- DWRITE_FONT_WEIGHT_SEMI_LIGHT = 350
85
- DWRITE_FONT_WEIGHT_NORMAL = 400
86
- DWRITE_FONT_WEIGHT_REGULAR = 400
87
- DWRITE_FONT_WEIGHT_MEDIUM = 500
88
- DWRITE_FONT_WEIGHT_DEMI_BOLD = 600
89
- DWRITE_FONT_WEIGHT_SEMI_BOLD = 600
90
- DWRITE_FONT_WEIGHT_BOLD = 700
91
- DWRITE_FONT_WEIGHT_EXTRA_BOLD = 800
92
- DWRITE_FONT_WEIGHT_ULTRA_BOLD = 800
93
- DWRITE_FONT_WEIGHT_BLACK = 900
94
- DWRITE_FONT_WEIGHT_HEAVY = 900
95
- DWRITE_FONT_WEIGHT_EXTRA_BLACK = 950
96
-
97
- name_to_weight = {
98
- True: DWRITE_FONT_WEIGHT_BOLD, # Temporary alias for attributed text
99
- False: DWRITE_FONT_WEIGHT_NORMAL, # Temporary alias for attributed text
100
- None: DWRITE_FONT_WEIGHT_NORMAL, # Temporary alias for attributed text
101
- "thin": DWRITE_FONT_WEIGHT_THIN,
102
- "extralight": DWRITE_FONT_WEIGHT_EXTRA_LIGHT,
103
- "ultralight": DWRITE_FONT_WEIGHT_ULTRA_LIGHT,
104
- "light": DWRITE_FONT_WEIGHT_LIGHT,
105
- "semilight": DWRITE_FONT_WEIGHT_SEMI_LIGHT,
106
- "normal": DWRITE_FONT_WEIGHT_NORMAL,
107
- "regular": DWRITE_FONT_WEIGHT_REGULAR,
108
- "medium": DWRITE_FONT_WEIGHT_MEDIUM,
109
- "demibold": DWRITE_FONT_WEIGHT_DEMI_BOLD,
110
- "semibold": DWRITE_FONT_WEIGHT_SEMI_BOLD,
111
- "bold": DWRITE_FONT_WEIGHT_BOLD,
112
- "extrabold": DWRITE_FONT_WEIGHT_EXTRA_BOLD,
113
- "ultrabold": DWRITE_FONT_WEIGHT_ULTRA_BOLD,
114
- "black": DWRITE_FONT_WEIGHT_BLACK,
115
- "heavy": DWRITE_FONT_WEIGHT_HEAVY,
116
- "extrablack": DWRITE_FONT_WEIGHT_EXTRA_BLACK,
117
- }
118
-
119
- DWRITE_FONT_STRETCH = UINT
120
- DWRITE_FONT_STRETCH_UNDEFINED = 0
121
- DWRITE_FONT_STRETCH_ULTRA_CONDENSED = 1
122
- DWRITE_FONT_STRETCH_EXTRA_CONDENSED = 2
123
- DWRITE_FONT_STRETCH_CONDENSED = 3
124
- DWRITE_FONT_STRETCH_SEMI_CONDENSED = 4
125
- DWRITE_FONT_STRETCH_NORMAL = 5
126
- DWRITE_FONT_STRETCH_MEDIUM = 5
127
- DWRITE_FONT_STRETCH_SEMI_EXPANDED = 6
128
- DWRITE_FONT_STRETCH_EXPANDED = 7
129
- DWRITE_FONT_STRETCH_EXTRA_EXPANDED = 8
130
-
131
- name_to_stretch = {
132
- "undefined": DWRITE_FONT_STRETCH_UNDEFINED,
133
- "ultracondensed": DWRITE_FONT_STRETCH_ULTRA_CONDENSED,
134
- "extracondensed": DWRITE_FONT_STRETCH_EXTRA_CONDENSED,
135
- "condensed": DWRITE_FONT_STRETCH_CONDENSED,
136
- "semicondensed": DWRITE_FONT_STRETCH_SEMI_CONDENSED,
137
- "normal": DWRITE_FONT_STRETCH_NORMAL,
138
- "medium": DWRITE_FONT_STRETCH_MEDIUM,
139
- "semiexpanded": DWRITE_FONT_STRETCH_SEMI_EXPANDED,
140
- "expanded": DWRITE_FONT_STRETCH_EXPANDED,
141
- "extraexpanded": DWRITE_FONT_STRETCH_EXTRA_EXPANDED,
142
- "narrow": DWRITE_FONT_STRETCH_CONDENSED,
143
- }
144
-
145
- DWRITE_GLYPH_IMAGE_FORMATS = c_int
146
-
147
- DWRITE_GLYPH_IMAGE_FORMATS_NONE = 0x00000000
148
- DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE = 0x00000001
149
- DWRITE_GLYPH_IMAGE_FORMATS_CFF = 0x00000002
150
- DWRITE_GLYPH_IMAGE_FORMATS_COLR = 0x00000004
151
- DWRITE_GLYPH_IMAGE_FORMATS_SVG = 0x00000008
152
- DWRITE_GLYPH_IMAGE_FORMATS_PNG = 0x00000010
153
- DWRITE_GLYPH_IMAGE_FORMATS_JPEG = 0x00000020
154
- DWRITE_GLYPH_IMAGE_FORMATS_TIFF = 0x00000040
155
- DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8 = 0x00000080
156
-
157
- DWRITE_MEASURING_MODE = UINT
158
- DWRITE_MEASURING_MODE_NATURAL = 0
159
- DWRITE_MEASURING_MODE_GDI_CLASSIC = 1
160
- DWRITE_MEASURING_MODE_GDI_NATURAL = 2
161
-
162
- DWRITE_GLYPH_IMAGE_FORMATS_ALL = DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE | \
163
- DWRITE_GLYPH_IMAGE_FORMATS_CFF | \
164
- DWRITE_GLYPH_IMAGE_FORMATS_COLR | \
165
- DWRITE_GLYPH_IMAGE_FORMATS_SVG | \
166
- DWRITE_GLYPH_IMAGE_FORMATS_PNG | \
167
- DWRITE_GLYPH_IMAGE_FORMATS_JPEG | \
168
- DWRITE_GLYPH_IMAGE_FORMATS_TIFF | \
169
- DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8
170
-
171
- DWRITE_FONT_STYLE = UINT
172
- DWRITE_FONT_STYLE_NORMAL = 0
173
- DWRITE_FONT_STYLE_OBLIQUE = 1
174
- DWRITE_FONT_STYLE_ITALIC = 2
175
-
176
- name_to_style = {
177
- "normal": DWRITE_FONT_STYLE_NORMAL,
178
- "oblique": DWRITE_FONT_STYLE_OBLIQUE,
179
- "italic": DWRITE_FONT_STYLE_ITALIC,
180
- }
181
-
182
- UINT8 = c_uint8
183
- UINT16 = c_uint16
184
- INT16 = c_int16
185
- INT32 = c_int32
186
- UINT32 = c_uint32
187
- UINT64 = c_uint64
188
-
189
- DWRITE_INFORMATIONAL_STRING_ID = UINT32
190
-
191
- DWRITE_INFORMATIONAL_STRING_NONE = 0
192
- DWRITE_INFORMATIONAL_STRING_COPYRIGHT_NOTICE = 1
193
- DWRITE_INFORMATIONAL_STRING_VERSION_STRINGS = 2
194
- DWRITE_INFORMATIONAL_STRING_TRADEMARK = 3
195
- DWRITE_INFORMATIONAL_STRING_MANUFACTURER = 4
196
- DWRITE_INFORMATIONAL_STRING_DESIGNER = 5
197
- DWRITE_INFORMATIONAL_STRING_DESIGNER_URL = 6
198
- DWRITE_INFORMATIONAL_STRING_DESCRIPTION = 7
199
- DWRITE_INFORMATIONAL_STRING_FONT_VENDOR_URL = 8
200
- DWRITE_INFORMATIONAL_STRING_LICENSE_DESCRIPTION = 9
201
- DWRITE_INFORMATIONAL_STRING_LICENSE_INFO_URL = 10
202
- DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES = 11
203
- DWRITE_INFORMATIONAL_STRING_WIN32_SUBFAMILY_NAMES = 12
204
- DWRITE_INFORMATIONAL_STRING_TYPOGRAPHIC_FAMILY_NAMES = 13
205
- DWRITE_INFORMATIONAL_STRING_TYPOGRAPHIC_SUBFAMILY_NAMES = 14
206
- DWRITE_INFORMATIONAL_STRING_SAMPLE_TEXT = 15
207
- DWRITE_INFORMATIONAL_STRING_FULL_NAME = 16
208
- DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME = 17
209
- DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_CID_NAME = 18
210
- DWRITE_INFORMATIONAL_STRING_WEIGHT_STRETCH_STYLE_FAMILY_NAME = 19
211
- DWRITE_INFORMATIONAL_STRING_DESIGN_SCRIPT_LANGUAGE_TAG = 20
212
- DWRITE_INFORMATIONAL_STRING_SUPPORTED_SCRIPT_LANGUAGE_TAG = 21
213
- DWRITE_INFORMATIONAL_STRING_PREFERRED_FAMILY_NAMES = 22
214
- DWRITE_INFORMATIONAL_STRING_PREFERRED_SUBFAMILY_NAMES = 23
215
- DWRITE_INFORMATIONAL_STRING_WWS_FAMILY_NAME = 24
216
-
217
-
218
- class D2D_POINT_2F(Structure):
219
- _fields_ = (
220
- ("x", FLOAT),
221
- ("y", FLOAT),
222
- )
223
-
224
-
225
- class D2D1_RECT_F(Structure):
226
- _fields_ = (
227
- ("left", FLOAT),
228
- ("top", FLOAT),
229
- ("right", FLOAT),
230
- ("bottom", FLOAT),
231
- )
232
-
233
-
234
- class D2D1_COLOR_F(Structure):
235
- _fields_ = (
236
- ("r", FLOAT),
237
- ("g", FLOAT),
238
- ("b", FLOAT),
239
- ("a", FLOAT),
240
- )
241
-
242
-
243
- class DWRITE_TEXT_METRICS(Structure):
244
- _fields_ = (
245
- ("left", FLOAT),
246
- ("top", FLOAT),
247
- ("width", FLOAT),
248
- ("widthIncludingTrailingWhitespace", FLOAT),
249
- ("height", FLOAT),
250
- ("layoutWidth", FLOAT),
251
- ("layoutHeight", FLOAT),
252
- ("maxBidiReorderingDepth", UINT32),
253
- ("lineCount", UINT32),
254
- )
255
-
256
-
257
- class DWRITE_FONT_METRICS(Structure):
258
- _fields_ = (
259
- ("designUnitsPerEm", UINT16),
260
- ("ascent", UINT16),
261
- ("descent", UINT16),
262
- ("lineGap", INT16),
263
- ("capHeight", UINT16),
264
- ("xHeight", UINT16),
265
- ("underlinePosition", INT16),
266
- ("underlineThickness", UINT16),
267
- ("strikethroughPosition", INT16),
268
- ("strikethroughThickness", UINT16),
269
- )
270
-
271
-
272
- class DWRITE_GLYPH_METRICS(Structure):
273
- _fields_ = (
274
- ("leftSideBearing", INT32),
275
- ("advanceWidth", UINT32),
276
- ("rightSideBearing", INT32),
277
- ("topSideBearing", INT32),
278
- ("advanceHeight", UINT32),
279
- ("bottomSideBearing", INT32),
280
- ("verticalOriginY", INT32),
281
- )
282
-
283
- def __repr__(self):
284
- return (f"DWRITE_GLYPH_METRICS(leftSideBearing={self.leftSideBearing}, advanceWidth={self.advanceWidth}, "
285
- f"rightSideBearing={self.rightSideBearing}, topSideBearing={self.topSideBearing}, advanceHeight={self.advanceHeight}, "
286
- f"bottomSideBearing={self.bottomSideBearing}, verticalOriginY={self.verticalOriginY})")
287
-
288
-
289
- class DWRITE_GLYPH_OFFSET(Structure):
290
- _fields_ = (
291
- ("advanceOffset", FLOAT),
292
- ("ascenderOffset", FLOAT),
293
- )
294
-
295
- def __repr__(self) -> str:
296
- return f"DWRITE_GLYPH_OFFSET({self.advanceOffset}, {self.ascenderOffset})"
297
-
298
-
299
- class DWRITE_CLUSTER_METRICS(Structure):
300
- _fields_ = (
301
- ("width", FLOAT),
302
- ("length", UINT16),
303
- ("canWrapLineAfter", UINT16, 1),
304
- ("isWhitespace", UINT16, 1),
305
- ("isNewline", UINT16, 1),
306
- ("isSoftHyphen", UINT16, 1),
307
- ("isRightToLeft", UINT16, 1),
308
- ("padding", UINT16, 11),
309
- )
310
-
311
-
312
- class IDWriteFontFileStream(com.IUnknown):
313
- _methods_ = [
314
- ("ReadFileFragment",
315
- com.STDMETHOD(POINTER(c_void_p), UINT64, UINT64, POINTER(c_void_p))),
316
- ("ReleaseFileFragment",
317
- com.STDMETHOD(c_void_p)),
318
- ("GetFileSize",
319
- com.STDMETHOD(POINTER(UINT64))),
320
- ("GetLastWriteTime",
321
- com.STDMETHOD(POINTER(UINT64))),
322
- ]
323
-
324
-
325
- class IDWriteFontFileLoader_LI(com.IUnknown): # Local implementation use only.
326
- _methods_ = [
327
- ("CreateStreamFromKey",
328
- com.STDMETHOD(c_void_p, UINT32, POINTER(POINTER(IDWriteFontFileStream)))),
329
- ]
330
-
331
-
332
- class IDWriteFontFileLoader(com.pIUnknown):
333
- _methods_ = [
334
- ("CreateStreamFromKey",
335
- com.STDMETHOD(c_void_p, UINT32, POINTER(POINTER(IDWriteFontFileStream)))),
336
- ]
337
-
338
-
339
- class IDWriteLocalFontFileLoader(IDWriteFontFileLoader, com.pIUnknown):
340
- _methods_ = [
341
- ("GetFilePathLengthFromKey",
342
- com.STDMETHOD(c_void_p, UINT32, POINTER(UINT32))),
343
- ("GetFilePathFromKey",
344
- com.STDMETHOD(c_void_p, UINT32, c_wchar_p, UINT32)),
345
- ("GetLastWriteTimeFromKey",
346
- com.STDMETHOD()),
347
- ]
348
-
349
-
350
- IID_IDWriteLocalFontFileLoader = com.GUID(0xb2d9f3ec, 0xc9fe, 0x4a11, 0xa2, 0xec, 0xd8, 0x62, 0x08, 0xf7, 0xc0, 0xa2)
351
-
352
-
353
- class IDWriteFontFile(com.pIUnknown):
354
- _methods_ = [
355
- ("GetReferenceKey",
356
- com.STDMETHOD(POINTER(c_void_p), POINTER(UINT32))),
357
- ("GetLoader",
358
- com.STDMETHOD(POINTER(IDWriteFontFileLoader))),
359
- ("Analyze",
360
- com.STDMETHOD()),
361
- ]
362
-
363
-
364
- class IDWriteFontFace(com.pIUnknown):
365
- _methods_ = [
366
- ("GetType",
367
- com.STDMETHOD()),
368
- ("GetFiles",
369
- com.STDMETHOD(POINTER(UINT32), POINTER(IDWriteFontFile))),
370
- ("GetIndex",
371
- com.STDMETHOD()),
372
- ("GetSimulations",
373
- com.STDMETHOD()),
374
- ("IsSymbolFont",
375
- com.STDMETHOD()),
376
- ("GetMetrics",
377
- com.METHOD(c_void, POINTER(DWRITE_FONT_METRICS))),
378
- ("GetGlyphCount",
379
- com.METHOD(UINT16)),
380
- ("GetDesignGlyphMetrics",
381
- com.STDMETHOD(POINTER(UINT16), UINT32, POINTER(DWRITE_GLYPH_METRICS), BOOL)),
382
- ("GetGlyphIndices",
383
- com.STDMETHOD(POINTER(UINT32), UINT32, POINTER(UINT16))),
384
- ("TryGetFontTable",
385
- com.STDMETHOD(UINT32, c_void_p, POINTER(UINT32), c_void_p, POINTER(BOOL))),
386
- ("ReleaseFontTable",
387
- com.METHOD(c_void)),
388
- ("GetGlyphRunOutline",
389
- com.STDMETHOD()),
390
- ("GetRecommendedRenderingMode",
391
- com.STDMETHOD()),
392
- ("GetGdiCompatibleMetrics",
393
- com.STDMETHOD()),
394
- ("GetGdiCompatibleGlyphMetrics",
395
- com.STDMETHOD()),
396
- ]
397
-
398
-
399
- IID_IDWriteFontFace1 = com.GUID(0xa71efdb4, 0x9fdb, 0x4838, 0xad, 0x90, 0xcf, 0xc3, 0xbe, 0x8c, 0x3d, 0xaf)
400
-
401
-
402
- class IDWriteFontFace1(IDWriteFontFace, com.pIUnknown):
403
- _methods_ = [
404
- ("GetMetric1",
405
- com.STDMETHOD()),
406
- ("GetGdiCompatibleMetrics1",
407
- com.STDMETHOD()),
408
- ("GetCaretMetrics",
409
- com.STDMETHOD()),
410
- ("GetUnicodeRanges",
411
- com.STDMETHOD()),
412
- ("IsMonospacedFont",
413
- com.STDMETHOD()),
414
- ("GetDesignGlyphAdvances",
415
- com.METHOD(c_void, POINTER(DWRITE_FONT_METRICS))),
416
- ("GetGdiCompatibleGlyphAdvances",
417
- com.STDMETHOD()),
418
- ("GetKerningPairAdjustments",
419
- com.STDMETHOD(UINT32, POINTER(UINT16), POINTER(INT32))),
420
- ("HasKerningPairs",
421
- com.METHOD(BOOL)),
422
- ("GetRecommendedRenderingMode1",
423
- com.STDMETHOD()),
424
- ("GetVerticalGlyphVariants",
425
- com.STDMETHOD()),
426
- ("HasVerticalGlyphVariants",
427
- com.STDMETHOD()),
428
- ]
429
-
430
-
431
- class DWRITE_GLYPH_RUN(Structure):
432
- _fields_ = (
433
- ("fontFace", IDWriteFontFace),
434
- ("fontEmSize", FLOAT),
435
- ("glyphCount", UINT32),
436
- ("glyphIndices", POINTER(UINT16)),
437
- ("glyphAdvances", POINTER(FLOAT)),
438
- ("glyphOffsets", POINTER(DWRITE_GLYPH_OFFSET)),
439
- ("isSideways", BOOL),
440
- ("bidiLevel", UINT32),
441
- )
442
-
443
-
444
- DWRITE_SCRIPT_SHAPES = UINT
445
- DWRITE_SCRIPT_SHAPES_DEFAULT = 0
446
-
447
-
448
- class DWRITE_SCRIPT_ANALYSIS(Structure):
449
- _fields_ = (
450
- ("script", UINT16),
451
- ("shapes", DWRITE_SCRIPT_SHAPES),
452
- )
453
-
454
-
455
- DWRITE_FONT_FEATURE_TAG = UINT
456
-
457
-
458
- class DWRITE_FONT_FEATURE(Structure):
459
- _fields_ = (
460
- ("nameTag", DWRITE_FONT_FEATURE_TAG),
461
- ("parameter", UINT32),
462
- )
463
-
464
-
465
- class DWRITE_TYPOGRAPHIC_FEATURES(Structure):
466
- _fields_ = (
467
- ("features", POINTER(DWRITE_FONT_FEATURE)),
468
- ("featureCount", UINT32),
469
- )
470
-
471
-
472
- class DWRITE_SHAPING_TEXT_PROPERTIES(Structure):
473
- _fields_ = (
474
- ("isShapedAlone", UINT16, 1),
475
- ("reserved1", UINT16, 1),
476
- ("canBreakShapingAfter", UINT16, 1),
477
- ("reserved", UINT16, 13),
478
- )
479
-
480
- def __repr__(self) -> str:
481
- return f"DWRITE_SHAPING_TEXT_PROPERTIES({self.isShapedAlone}, {self.reserved1}, {self.canBreakShapingAfter})"
482
-
483
-
484
- class DWRITE_SHAPING_GLYPH_PROPERTIES(Structure):
485
- _fields_ = (
486
- ("justification", UINT16, 4),
487
- ("isClusterStart", UINT16, 1),
488
- ("isDiacritic", UINT16, 1),
489
- ("isZeroWidthSpace", UINT16, 1),
490
- ("reserved", UINT16, 9),
491
- )
492
-
493
-
494
- DWRITE_READING_DIRECTION = UINT
495
- DWRITE_READING_DIRECTION_LEFT_TO_RIGHT = 0
496
-
497
-
498
- class IDWriteTextAnalysisSource(com.IUnknown):
499
- _methods_ = [
500
- ("GetTextAtPosition",
501
- com.STDMETHOD(UINT32, POINTER(c_wchar_p), POINTER(UINT32))),
502
- ("GetTextBeforePosition",
503
- com.STDMETHOD(UINT32, POINTER(c_wchar_p), POINTER(UINT32))),
504
- ("GetParagraphReadingDirection",
505
- com.METHOD(DWRITE_READING_DIRECTION)),
506
- ("GetLocaleName",
507
- com.STDMETHOD(UINT32, POINTER(UINT32), POINTER(c_wchar_p))),
508
- ("GetNumberSubstitution",
509
- com.STDMETHOD(UINT32, POINTER(UINT32), c_void_p)),
510
- ]
511
-
512
-
513
- class IDWriteTextAnalysisSink(com.IUnknown):
514
- _methods_ = [
515
- ("SetScriptAnalysis",
516
- com.STDMETHOD(UINT32, UINT32, POINTER(DWRITE_SCRIPT_ANALYSIS))),
517
- ("SetLineBreakpoints",
518
- com.STDMETHOD(UINT32, UINT32, c_void_p)),
519
- ("SetBidiLevel",
520
- com.STDMETHOD(UINT32, UINT32, UINT8, UINT8)),
521
- ("SetNumberSubstitution",
522
- com.STDMETHOD(UINT32, UINT32, c_void_p)),
523
- ]
524
-
525
-
526
- class Run:
527
- def __init__(self) -> None:
528
- self.text_start = 0
529
- self.text_length = 0
530
- self.glyph_start = 0
531
- self.glyph_count = 0
532
- self.script = DWRITE_SCRIPT_ANALYSIS()
533
- self.bidi = 0
534
- self.isNumberSubstituted = False
535
- self.isSideways = False
536
-
537
- self.next_run = None
538
-
539
- def ContainsTextPosition(self, textPosition: int) -> bool:
540
- return textPosition >= self.text_start and textPosition < self.text_start + self.text_length
541
-
542
-
543
- class TextAnalysis(com.COMObject):
544
- _interfaces_ = [IDWriteTextAnalysisSource, IDWriteTextAnalysisSink]
545
-
546
- def __init__(self) -> None:
547
- super().__init__()
548
- self._textstart = 0
549
- self._textlength = 0
550
- self._glyphstart = 0
551
- self._glyphcount = 0
552
- self._ptrs = []
553
-
554
- self._script = None
555
- self._bidi = 0
556
- # self._sideways = False # noqa: ERA001
557
-
558
- def GenerateResults(self, analyzer: IDWriteTextAnalyzer, text: c_wchar_p, text_length: int):
559
- self._text = text
560
- self._textstart = 0
561
- self._textlength = text_length
562
- self._glyphstart = 0
563
- self._glyphcount = 0
564
- self._ptrs.clear()
565
-
566
- self._start_run = Run()
567
- self._start_run.text_length = text_length
568
-
569
- self._current_run = self._start_run
570
-
571
- analyzer.AnalyzeScript(self, 0, text_length, self)
572
-
573
- def SetScriptAnalysis(self, textPosition: UINT32, textLength: UINT32,
574
- scriptAnalysis: POINTER(DWRITE_SCRIPT_ANALYSIS)) -> int:
575
- # textPosition - The index of the first character in the string that the result applies to
576
- # textLength - How many characters of the string from the index that the result applies to
577
- # scriptAnalysis - The analysis information for all glyphs starting at position for length.
578
- self.SetCurrentRun(textPosition)
579
- self.SplitCurrentRun(textPosition)
580
-
581
- while textLength > 0:
582
- run, textLength = self.FetchNextRun(textLength)
583
-
584
- run.script.script = scriptAnalysis[0].script
585
- run.script.shapes = scriptAnalysis[0].shapes
586
-
587
- self._script = run.script
588
-
589
- return 0
590
- # return 0x80004001
591
-
592
- def GetTextBeforePosition(self, textPosition: UINT32, textString: POINTER(POINTER(WCHAR)),
593
- textLength: POINTER(UINT32)) -> NoReturn:
594
- msg = "Currently not implemented."
595
- raise Exception(msg)
596
-
597
- def GetTextAtPosition(self, textPosition: UINT32, textString: c_wchar_p, textLength: POINTER(UINT32)) -> int:
598
- # This method will retrieve a substring of the text in this layout
599
- # to be used in an analysis step.
600
- # Arguments:
601
- # textPosition - The index of the first character of the text to retrieve.
602
- # textString - The pointer to the first character of text at the index requested.
603
- # textLength - The characters available at/after the textString pointer (string length).
604
-
605
- if textPosition >= self._textlength:
606
- self._no_ptr = c_wchar_p(None)
607
- textString[0] = self._no_ptr
608
- textLength[0] = 0
609
- else:
610
- ptr = c_wchar_p(self._text[textPosition:])
611
- self._ptrs.append(ptr)
612
- textString[0] = ptr
613
- textLength[0] = self._textlength - textPosition
614
-
615
- return 0
616
-
617
- def GetParagraphReadingDirection(self) -> int:
618
- return 0
619
-
620
- def GetLocaleName(self, textPosition: UINT32, textLength: POINTER(UINT32),
621
- localeName: POINTER(POINTER(WCHAR))) -> int:
622
- self.__local_name = c_wchar_p("") # TODO: Add more locales.
623
- localeName[0] = self.__local_name
624
- textLength[0] = self._textlength - textPosition
625
- return 0
626
-
627
- def GetNumberSubstitution(self) -> int:
628
- return 0
629
-
630
- def SetCurrentRun(self, textPosition: UINT32) -> None:
631
- if self._current_run and self._current_run.ContainsTextPosition(textPosition):
632
- return
633
-
634
- def SplitCurrentRun(self, textPosition: UINT32) -> None:
635
- if not self._current_run:
636
- return
637
-
638
- if textPosition <= self._current_run.text_start:
639
- # Already first start of the run.
640
- return
641
-
642
- new_run = copy.copy(self._current_run)
643
-
644
- new_run.next_run = self._current_run.next_run
645
- self._current_run.next_run = new_run
646
-
647
- splitPoint = textPosition - self._current_run.text_start
648
- new_run.text_start += splitPoint
649
- new_run.text_length -= splitPoint
650
-
651
- self._current_run.text_length = splitPoint
652
- self._current_run = new_run
653
-
654
- def FetchNextRun(self, textLength: UINT32) -> tuple[Run, int]:
655
- original_run = self._current_run
656
-
657
- if (textLength < self._current_run.text_length):
658
- self.SplitCurrentRun(self._current_run.text_start + textLength)
659
- else:
660
- self._current_run = self._current_run.next_run
661
-
662
- textLength -= original_run.text_length
663
-
664
- return original_run, textLength
665
-
666
-
667
- class IDWriteTextAnalyzer(com.pIUnknown):
668
- _methods_ = [
669
- ("AnalyzeScript",
670
- com.STDMETHOD(POINTER(IDWriteTextAnalysisSource), UINT32, UINT32, POINTER(IDWriteTextAnalysisSink))),
671
- ("AnalyzeBidi",
672
- com.STDMETHOD()),
673
- ("AnalyzeNumberSubstitution",
674
- com.STDMETHOD()),
675
- ("AnalyzeLineBreakpoints",
676
- com.STDMETHOD()),
677
- ("GetGlyphs",
678
- com.STDMETHOD(c_wchar_p, UINT32, IDWriteFontFace, BOOL, BOOL, POINTER(DWRITE_SCRIPT_ANALYSIS),
679
- c_wchar_p, c_void_p, POINTER(POINTER(DWRITE_TYPOGRAPHIC_FEATURES)), POINTER(UINT32),
680
- UINT32, UINT32, POINTER(UINT16), POINTER(DWRITE_SHAPING_TEXT_PROPERTIES),
681
- POINTER(UINT16), POINTER(DWRITE_SHAPING_GLYPH_PROPERTIES), POINTER(UINT32))),
682
- ("GetGlyphPlacements",
683
- com.STDMETHOD(c_wchar_p, POINTER(UINT16), POINTER(DWRITE_SHAPING_TEXT_PROPERTIES), UINT32, POINTER(UINT16),
684
- POINTER(DWRITE_SHAPING_GLYPH_PROPERTIES), UINT32, IDWriteFontFace, FLOAT, BOOL, BOOL,
685
- POINTER(DWRITE_SCRIPT_ANALYSIS), c_wchar_p, POINTER(DWRITE_TYPOGRAPHIC_FEATURES),
686
- POINTER(UINT32), UINT32, POINTER(FLOAT), POINTER(DWRITE_GLYPH_OFFSET))),
687
- ("GetGdiCompatibleGlyphPlacements",
688
- com.STDMETHOD()),
689
- ]
690
-
691
-
692
- class IDWriteLocalizedStrings(com.pIUnknown):
693
- _methods_ = [
694
- ("GetCount",
695
- com.METHOD(UINT32)),
696
- ("FindLocaleName",
697
- com.STDMETHOD(c_wchar_p, POINTER(UINT32), POINTER(BOOL))),
698
- ("GetLocaleNameLength",
699
- com.STDMETHOD(UINT32, POINTER(UINT32))),
700
- ("GetLocaleName",
701
- com.STDMETHOD(UINT32, c_wchar_p, UINT32)),
702
- ("GetStringLength",
703
- com.STDMETHOD(UINT32, POINTER(UINT32))),
704
- ("GetString",
705
- com.STDMETHOD(UINT32, c_wchar_p, UINT32)),
706
- ]
707
-
708
-
709
- class IDWriteFontList(com.pIUnknown):
710
- _methods_ = [
711
- ("GetFontCollection",
712
- com.STDMETHOD()),
713
- ("GetFontCount",
714
- com.METHOD(UINT32)),
715
- ("GetFont",
716
- com.STDMETHOD(UINT32, c_void_p)), # IDWriteFont, use void because of forward ref.
717
- ]
718
-
719
-
720
- class IDWriteFontFamily(IDWriteFontList, com.pIUnknown):
721
- _methods_ = [
722
- ("GetFamilyNames",
723
- com.STDMETHOD(POINTER(IDWriteLocalizedStrings))),
724
- ("GetFirstMatchingFont",
725
- com.STDMETHOD(DWRITE_FONT_WEIGHT, DWRITE_FONT_STRETCH, DWRITE_FONT_STYLE, c_void_p)),
726
- ("GetMatchingFonts",
727
- com.STDMETHOD()),
728
- ]
729
-
730
-
731
- class IDWriteFontFamily1(IDWriteFontFamily, IDWriteFontList, com.pIUnknown):
732
- _methods_ = [
733
- ("GetFontLocality",
734
- com.STDMETHOD()),
735
- ("GetFont1",
736
- com.STDMETHOD()),
737
- ("GetFontFaceReference",
738
- com.STDMETHOD()),
739
- ]
740
-
741
-
742
- class IDWriteFont(com.pIUnknown):
743
- _methods_ = [
744
- ("GetFontFamily",
745
- com.STDMETHOD(POINTER(IDWriteFontFamily))),
746
- ("GetWeight",
747
- com.METHOD(DWRITE_FONT_WEIGHT)),
748
- ("GetStretch",
749
- com.METHOD(DWRITE_FONT_STRETCH)),
750
- ("GetStyle",
751
- com.METHOD(DWRITE_FONT_STYLE)),
752
- ("IsSymbolFont",
753
- com.METHOD(BOOL)),
754
- ("GetFaceNames",
755
- com.STDMETHOD(POINTER(IDWriteLocalizedStrings))),
756
- ("GetInformationalStrings",
757
- com.STDMETHOD(DWRITE_INFORMATIONAL_STRING_ID, POINTER(IDWriteLocalizedStrings), POINTER(BOOL))),
758
- ("GetSimulations",
759
- com.STDMETHOD()),
760
- ("GetMetrics",
761
- com.STDMETHOD()),
762
- ("HasCharacter",
763
- com.STDMETHOD(UINT32, POINTER(BOOL))),
764
- ("CreateFontFace",
765
- com.STDMETHOD(POINTER(IDWriteFontFace))),
766
- ]
767
-
768
-
769
- class IDWriteFont1(IDWriteFont, com.pIUnknown):
770
- _methods_ = [
771
- ("GetMetrics1",
772
- com.STDMETHOD()),
773
- ("GetPanose",
774
- com.STDMETHOD()),
775
- ("GetUnicodeRanges",
776
- com.STDMETHOD()),
777
- ("IsMonospacedFont",
778
- com.STDMETHOD()),
779
- ]
780
-
781
-
782
- class IDWriteFontCollection(com.pIUnknown):
783
- _methods_ = [
784
- ("GetFontFamilyCount",
785
- com.METHOD(UINT32)),
786
- ("GetFontFamily",
787
- com.STDMETHOD(UINT32, POINTER(IDWriteFontFamily))),
788
- ("FindFamilyName",
789
- com.STDMETHOD(c_wchar_p, POINTER(UINT), POINTER(BOOL))),
790
- ("GetFontFromFontFace",
791
- com.STDMETHOD()),
792
- ]
793
-
794
-
795
- class IDWriteFontCollection1(IDWriteFontCollection, com.pIUnknown):
796
- _methods_ = [
797
- ("GetFontSet",
798
- com.STDMETHOD()),
799
- ("GetFontFamily1",
800
- com.STDMETHOD(POINTER(IDWriteFontFamily1))),
801
- ]
802
-
803
-
804
- DWRITE_TEXT_ALIGNMENT = UINT
805
- DWRITE_TEXT_ALIGNMENT_LEADING = 1
806
- DWRITE_TEXT_ALIGNMENT_TRAILING = 2
807
- DWRITE_TEXT_ALIGNMENT_CENTER = 3
808
- DWRITE_TEXT_ALIGNMENT_JUSTIFIED = 4
809
-
810
-
811
- class IDWriteGdiInterop(com.pIUnknown):
812
- _methods_ = [
813
- ("CreateFontFromLOGFONT",
814
- com.STDMETHOD(POINTER(LOGFONTW), POINTER(IDWriteFont))),
815
- ("ConvertFontToLOGFONT",
816
- com.STDMETHOD()),
817
- ("ConvertFontFaceToLOGFONT",
818
- com.STDMETHOD()),
819
- ("CreateFontFaceFromHdc",
820
- com.STDMETHOD(HDC, POINTER(IDWriteFontFace))),
821
- ("CreateBitmapRenderTarget",
822
- com.STDMETHOD()),
823
- ]
824
-
825
-
826
- class IDWriteTextFormat(com.pIUnknown):
827
- _methods_ = [
828
- ("SetTextAlignment",
829
- com.STDMETHOD(DWRITE_TEXT_ALIGNMENT)),
830
- ("SetParagraphAlignment",
831
- com.STDMETHOD()),
832
- ("SetWordWrapping",
833
- com.STDMETHOD()),
834
- ("SetReadingDirection",
835
- com.STDMETHOD()),
836
- ("SetFlowDirection",
837
- com.STDMETHOD()),
838
- ("SetIncrementalTabStop",
839
- com.STDMETHOD()),
840
- ("SetTrimming",
841
- com.STDMETHOD()),
842
- ("SetLineSpacing",
843
- com.STDMETHOD()),
844
- ("GetTextAlignment",
845
- com.STDMETHOD()),
846
- ("GetParagraphAlignment",
847
- com.STDMETHOD()),
848
- ("GetWordWrapping",
849
- com.STDMETHOD()),
850
- ("GetReadingDirection",
851
- com.STDMETHOD()),
852
- ("GetFlowDirection",
853
- com.STDMETHOD()),
854
- ("GetIncrementalTabStop",
855
- com.STDMETHOD()),
856
- ("GetTrimming",
857
- com.STDMETHOD()),
858
- ("GetLineSpacing",
859
- com.STDMETHOD()),
860
- ("GetFontCollection",
861
- com.STDMETHOD()),
862
- ("GetFontFamilyNameLength",
863
- com.STDMETHOD(UINT32, POINTER(UINT32))),
864
- ("GetFontFamilyName",
865
- com.STDMETHOD(UINT32, c_wchar_p, UINT32)),
866
- ("GetFontWeight",
867
- com.STDMETHOD()),
868
- ("GetFontStyle",
869
- com.STDMETHOD()),
870
- ("GetFontStretch",
871
- com.STDMETHOD()),
872
- ("GetFontSize",
873
- com.STDMETHOD()),
874
- ("GetLocaleNameLength",
875
- com.STDMETHOD()),
876
- ("GetLocaleName",
877
- com.STDMETHOD()),
878
- ]
879
-
880
-
881
- class IDWriteTypography(com.pIUnknown):
882
- _methods_ = [
883
- ("AddFontFeature",
884
- com.STDMETHOD(DWRITE_FONT_FEATURE)),
885
- ("GetFontFeatureCount",
886
- com.METHOD(UINT32)),
887
- ("GetFontFeature",
888
- com.STDMETHOD()),
889
- ]
890
-
891
-
892
- class DWRITE_TEXT_RANGE(Structure):
893
- _fields_ = (
894
- ("startPosition", UINT32),
895
- ("length", UINT32),
896
- )
897
-
898
-
899
- class DWRITE_OVERHANG_METRICS(Structure):
900
- _fields_ = (
901
- ("left", FLOAT),
902
- ("top", FLOAT),
903
- ("right", FLOAT),
904
- ("bottom", FLOAT),
905
- )
906
-
907
-
908
- class IDWriteTextLayout(IDWriteTextFormat, com.pIUnknown):
909
- _methods_ = [
910
- ("SetMaxWidth",
911
- com.STDMETHOD()),
912
- ("SetMaxHeight",
913
- com.STDMETHOD()),
914
- ("SetFontCollection",
915
- com.STDMETHOD()),
916
- ("SetFontFamilyName",
917
- com.STDMETHOD()),
918
- ("SetFontWeight", # 30
919
- com.STDMETHOD()),
920
- ("SetFontStyle",
921
- com.STDMETHOD()),
922
- ("SetFontStretch",
923
- com.STDMETHOD()),
924
- ("SetFontSize",
925
- com.STDMETHOD()),
926
- ("SetUnderline",
927
- com.STDMETHOD()),
928
- ("SetStrikethrough",
929
- com.STDMETHOD()),
930
- ("SetDrawingEffect",
931
- com.STDMETHOD()),
932
- ("SetInlineObject",
933
- com.STDMETHOD()),
934
- ("SetTypography",
935
- com.STDMETHOD(IDWriteTypography, DWRITE_TEXT_RANGE)),
936
- ("SetLocaleName",
937
- com.STDMETHOD()),
938
- ("GetMaxWidth", # 40
939
- com.METHOD(FLOAT)),
940
- ("GetMaxHeight",
941
- com.METHOD(FLOAT)),
942
- ("GetFontCollection2",
943
- com.STDMETHOD()),
944
- ("GetFontFamilyNameLength2",
945
- com.STDMETHOD(UINT32, POINTER(UINT32), c_void_p)),
946
- ("GetFontFamilyName2",
947
- com.STDMETHOD(UINT32, c_wchar_p, UINT32, c_void_p)),
948
- ("GetFontWeight2",
949
- com.STDMETHOD(UINT32, POINTER(DWRITE_FONT_WEIGHT), POINTER(DWRITE_TEXT_RANGE))),
950
- ("GetFontStyle2",
951
- com.STDMETHOD()),
952
- ("GetFontStretch2",
953
- com.STDMETHOD()),
954
- ("GetFontSize2",
955
- com.STDMETHOD()),
956
- ("GetUnderline",
957
- com.STDMETHOD()),
958
- ("GetStrikethrough",
959
- com.STDMETHOD(UINT32, POINTER(BOOL), POINTER(DWRITE_TEXT_RANGE))),
960
- ("GetDrawingEffect",
961
- com.STDMETHOD()),
962
- ("GetInlineObject",
963
- com.STDMETHOD()),
964
- ("GetTypography", # Always returns NULL without SetTypography being called.
965
- com.STDMETHOD(UINT32, POINTER(IDWriteTypography), POINTER(DWRITE_TEXT_RANGE))),
966
- ("GetLocaleNameLength1",
967
- com.STDMETHOD()),
968
- ("GetLocaleName1",
969
- com.STDMETHOD()),
970
- ("Draw",
971
- com.STDMETHOD()),
972
- ("GetLineMetrics",
973
- com.STDMETHOD()),
974
- ("GetMetrics",
975
- com.STDMETHOD(POINTER(DWRITE_TEXT_METRICS))),
976
- ("GetOverhangMetrics",
977
- com.STDMETHOD(POINTER(DWRITE_OVERHANG_METRICS))),
978
- ("GetClusterMetrics",
979
- com.STDMETHOD(POINTER(DWRITE_CLUSTER_METRICS), UINT32, POINTER(UINT32))),
980
- ("DetermineMinWidth",
981
- com.STDMETHOD(POINTER(FLOAT))),
982
- ("HitTestPoint",
983
- com.STDMETHOD()),
984
- ("HitTestTextPosition",
985
- com.STDMETHOD()),
986
- ("HitTestTextRange",
987
- com.STDMETHOD()),
988
- ]
989
-
990
-
991
- class IDWriteTextLayout1(IDWriteTextLayout, IDWriteTextFormat, com.pIUnknown):
992
- _methods_ = [
993
- ("SetPairKerning",
994
- com.STDMETHOD()),
995
- ("GetPairKerning",
996
- com.STDMETHOD()),
997
- ("SetCharacterSpacing",
998
- com.STDMETHOD()),
999
- ("GetCharacterSpacing",
1000
- com.STDMETHOD(UINT32, POINTER(FLOAT), POINTER(FLOAT), POINTER(FLOAT), POINTER(DWRITE_TEXT_RANGE))),
1001
- ]
1002
-
1003
-
1004
- class IDWriteFontFileEnumerator(com.IUnknown):
1005
- _methods_ = [
1006
- ("MoveNext",
1007
- com.STDMETHOD(POINTER(BOOL))),
1008
- ("GetCurrentFontFile",
1009
- com.STDMETHOD(c_void_p)),
1010
- ]
1011
-
1012
-
1013
- class IDWriteFontCollectionLoader(com.IUnknown):
1014
- _methods_ = [
1015
- ("CreateEnumeratorFromKey",
1016
- com.STDMETHOD(c_void_p, c_void_p, UINT32, POINTER(POINTER(IDWriteFontFileEnumerator)))),
1017
- ]
1018
-
1019
-
1020
- class MyFontFileStream(com.COMObject):
1021
- _interfaces_ = [IDWriteFontFileStream]
1022
-
1023
- def __init__(self, data: bytes) -> None:
1024
- super().__init__()
1025
- self._data = data
1026
- self._size = len(data)
1027
- self._ptrs = []
1028
-
1029
- def ReadFileFragment(self, fragmentStart: POINTER(c_void_p), fileOffset: UINT64, fragmentSize: UINT64,
1030
- fragmentContext: POINTER(c_void_p)) -> int:
1031
- if fileOffset + fragmentSize > self._size:
1032
- return 0x80004005 # E_FAIL
1033
-
1034
- fragment = self._data[fileOffset:]
1035
- buffer = (c_ubyte * len(fragment)).from_buffer(bytearray(fragment))
1036
- ptr = cast(buffer, c_void_p)
1037
-
1038
- self._ptrs.append(ptr)
1039
- fragmentStart[0] = ptr
1040
- fragmentContext[0] = None
1041
- return 0
1042
-
1043
- def ReleaseFileFragment(self, fragmentContext: c_void_p) -> int:
1044
- return 0
1045
-
1046
- def GetFileSize(self, fileSize: POINTER(UINT64)) -> int:
1047
- fileSize[0] = self._size
1048
- return 0
1049
-
1050
- def GetLastWriteTime(self, lastWriteTime: POINTER(UINT64)) -> int:
1051
- return 0x80004001 # E_NOTIMPL
1052
-
1053
-
1054
- class LegacyFontFileLoader(com.COMObject):
1055
- _interfaces_ = [IDWriteFontFileLoader_LI]
1056
-
1057
- def __init__(self) -> None:
1058
- super().__init__()
1059
- self._streams = {}
1060
-
1061
- def CreateStreamFromKey(self, fontfileReferenceKey: c_void_p, fontFileReferenceKeySize: UINT32,
1062
- fontFileStream: POINTER(IDWriteFontFileStream)) -> int:
1063
- convert_index = cast(fontfileReferenceKey, POINTER(c_uint32))
1064
-
1065
- self._ptr = cast(self._streams[convert_index.contents.value].as_interface(IDWriteFontFileStream),
1066
- POINTER(IDWriteFontFileStream))
1067
- fontFileStream[0] = self._ptr
1068
- return 0
1069
-
1070
- def SetCurrentFont(self, index: int, data: bytes) -> int:
1071
- self._streams[index] = MyFontFileStream(data)
1072
-
1073
-
1074
- class MyEnumerator(com.COMObject):
1075
- _interfaces_ = [IDWriteFontFileEnumerator]
1076
-
1077
- def __init__(self, factory: c_void_p, loader: LegacyFontFileLoader) -> None:
1078
- super().__init__()
1079
- self.factory = cast(factory, IDWriteFactory)
1080
- self.key = "pyglet_dwrite"
1081
- self.size = len(self.key)
1082
- self.current_index = -1
1083
-
1084
- self._keys = []
1085
- self._font_data = []
1086
- self._font_files = []
1087
- self._current_file = None
1088
-
1089
- self._font_key_ref = create_unicode_buffer("none")
1090
- self._font_key_len = len(self._font_key_ref)
1091
-
1092
- self._file_loader = loader
1093
-
1094
- def AddFontData(self, fonts: list[str]) -> None:
1095
- self._font_data = fonts
1096
-
1097
- def MoveNext(self, hasCurrentFile: BOOL) -> None:
1098
-
1099
- self.current_index += 1
1100
- if self.current_index != len(self._font_data):
1101
- font_file = IDWriteFontFile()
1102
-
1103
- self._file_loader.SetCurrentFont(self.current_index, self._font_data[self.current_index])
1104
-
1105
- key = self.current_index
1106
-
1107
- if self.current_index not in self._keys:
1108
- buffer = pointer(c_uint32(key))
1109
-
1110
- ptr = cast(buffer, c_void_p)
1111
-
1112
- self._keys.append(ptr)
1113
-
1114
- self.factory.CreateCustomFontFileReference(self._keys[self.current_index],
1115
- sizeof(buffer),
1116
- self._file_loader,
1117
- byref(font_file))
1118
-
1119
- self._font_files.append(font_file)
1120
-
1121
- hasCurrentFile[0] = 1
1122
- else:
1123
- hasCurrentFile[0] = 0
1124
-
1125
- def GetCurrentFontFile(self, fontFile: IDWriteFontFile) -> int:
1126
- fontFile = cast(fontFile, POINTER(IDWriteFontFile))
1127
- fontFile[0] = self._font_files[self.current_index]
1128
- return 0
1129
-
1130
-
1131
- class LegacyCollectionLoader(com.COMObject):
1132
- _interfaces_ = [IDWriteFontCollectionLoader]
1133
-
1134
- def __init__(self, factory: c_void_p, loader: LegacyFontFileLoader) -> None:
1135
- super().__init__()
1136
- self._enumerator = MyEnumerator(factory, loader)
1137
-
1138
- def AddFontData(self, fonts) -> None:
1139
- self._enumerator.AddFontData(fonts)
1140
-
1141
- def CreateEnumeratorFromKey(self, factory: IDWriteFactory, key: c_void_p, key_size: UINT32,
1142
- enumerator: MyEnumerator) -> int:
1143
- self._ptr = cast(self._enumerator.as_interface(IDWriteFontFileEnumerator),
1144
- POINTER(IDWriteFontFileEnumerator))
1145
-
1146
- enumerator[0] = self._ptr
1147
- return 0
1148
-
1149
-
1150
- IID_IDWriteFactory = com.GUID(0xb859ee5a, 0xd838, 0x4b5b, 0xa2, 0xe8, 0x1a, 0xdc, 0x7d, 0x93, 0xdb, 0x48)
1151
-
1152
-
1153
- class IDWriteRenderingParams(com.pIUnknown):
1154
- _methods_ = [
1155
- ("GetGamma",
1156
- com.METHOD(FLOAT)),
1157
- ("GetEnhancedContrast",
1158
- com.METHOD(FLOAT)),
1159
- ("GetClearTypeLevel",
1160
- com.METHOD(FLOAT)),
1161
- ("GetPixelGeometry",
1162
- com.METHOD(UINT)),
1163
- ("GetRenderingMode",
1164
- com.METHOD(UINT)),
1165
- ]
1166
-
1167
-
1168
- class IDWriteFactory(com.pIUnknown):
1169
- _methods_ = [
1170
- ("GetSystemFontCollection",
1171
- com.STDMETHOD(POINTER(IDWriteFontCollection), BOOL)),
1172
- ("CreateCustomFontCollection",
1173
- com.STDMETHOD(POINTER(IDWriteFontCollectionLoader), c_void_p, UINT32, POINTER(IDWriteFontCollection))),
1174
- ("RegisterFontCollectionLoader",
1175
- com.STDMETHOD(POINTER(IDWriteFontCollectionLoader))),
1176
- ("UnregisterFontCollectionLoader",
1177
- com.STDMETHOD(POINTER(IDWriteFontCollectionLoader))),
1178
- ("CreateFontFileReference",
1179
- com.STDMETHOD(c_wchar_p, c_void_p, POINTER(IDWriteFontFile))),
1180
- ("CreateCustomFontFileReference",
1181
- com.STDMETHOD(c_void_p, UINT32, POINTER(IDWriteFontFileLoader_LI), POINTER(IDWriteFontFile))),
1182
- ("CreateFontFace",
1183
- com.STDMETHOD()),
1184
- ("CreateRenderingParams",
1185
- com.STDMETHOD(POINTER(IDWriteRenderingParams))),
1186
- ("CreateMonitorRenderingParams",
1187
- com.STDMETHOD()),
1188
- ("CreateCustomRenderingParams",
1189
- com.STDMETHOD(FLOAT, FLOAT, FLOAT, UINT, UINT, POINTER(IDWriteRenderingParams))),
1190
- ("RegisterFontFileLoader",
1191
- com.STDMETHOD(c_void_p)), # Ambigious as newer is a pIUnknown and legacy is IUnknown.
1192
- ("UnregisterFontFileLoader",
1193
- com.STDMETHOD(POINTER(IDWriteFontFileLoader_LI))),
1194
- ("CreateTextFormat",
1195
- com.STDMETHOD(c_wchar_p, IDWriteFontCollection, DWRITE_FONT_WEIGHT, DWRITE_FONT_STYLE, DWRITE_FONT_STRETCH,
1196
- FLOAT, c_wchar_p, POINTER(IDWriteTextFormat))),
1197
- ("CreateTypography",
1198
- com.STDMETHOD(POINTER(IDWriteTypography))),
1199
- ("GetGdiInterop",
1200
- com.STDMETHOD(POINTER(IDWriteGdiInterop))),
1201
- ("CreateTextLayout",
1202
- com.STDMETHOD(c_wchar_p, UINT32, IDWriteTextFormat, FLOAT, FLOAT, POINTER(IDWriteTextLayout))),
1203
- ("CreateGdiCompatibleTextLayout",
1204
- com.STDMETHOD()),
1205
- ("CreateEllipsisTrimmingSign",
1206
- com.STDMETHOD()),
1207
- ("CreateTextAnalyzer",
1208
- com.STDMETHOD(POINTER(IDWriteTextAnalyzer))),
1209
- ("CreateNumberSubstitution",
1210
- com.STDMETHOD()),
1211
- ("CreateGlyphRunAnalysis",
1212
- com.STDMETHOD()),
1213
- ]
1214
-
1215
-
1216
- IID_IDWriteFactory1 = com.GUID(0x30572f99, 0xdac6, 0x41db, 0xa1, 0x6e, 0x04, 0x86, 0x30, 0x7e, 0x60, 0x6a)
1217
-
1218
-
1219
- class IDWriteFactory1(IDWriteFactory, com.pIUnknown):
1220
- _methods_ = [
1221
- ("GetEudcFontCollection",
1222
- com.STDMETHOD()),
1223
- ("CreateCustomRenderingParams1",
1224
- com.STDMETHOD()),
1225
- ]
1226
-
1227
-
1228
- class IDWriteFontFallback(com.pIUnknown):
1229
- _methods_ = [
1230
- ("MapCharacters",
1231
- com.STDMETHOD(POINTER(IDWriteTextAnalysisSource), UINT32, UINT32, IDWriteFontCollection, c_wchar_p,
1232
- DWRITE_FONT_WEIGHT, DWRITE_FONT_STYLE, DWRITE_FONT_STRETCH, POINTER(UINT32),
1233
- POINTER(IDWriteFont),
1234
- POINTER(FLOAT))),
1235
- ]
1236
-
1237
-
1238
- class IDWriteColorGlyphRunEnumerator(com.pIUnknown):
1239
- _methods_ = [
1240
- ("MoveNext",
1241
- com.STDMETHOD()),
1242
- ("GetCurrentRun",
1243
- com.STDMETHOD()),
1244
- ]
1245
-
1246
-
1247
- class IDWriteFactory2(IDWriteFactory1, IDWriteFactory, com.pIUnknown):
1248
- _methods_ = [
1249
- ("GetSystemFontFallback",
1250
- com.STDMETHOD(POINTER(IDWriteFontFallback))),
1251
- ("CreateFontFallbackBuilder",
1252
- com.STDMETHOD()),
1253
- ("TranslateColorGlyphRun",
1254
- com.STDMETHOD(FLOAT, FLOAT, POINTER(DWRITE_GLYPH_RUN), c_void_p, DWRITE_MEASURING_MODE, c_void_p, UINT32,
1255
- POINTER(IDWriteColorGlyphRunEnumerator))),
1256
- ("CreateCustomRenderingParams2",
1257
- com.STDMETHOD()),
1258
- ("CreateGlyphRunAnalysis",
1259
- com.STDMETHOD()),
1260
- ]
1261
-
1262
-
1263
- IID_IDWriteFactory2 = com.GUID(0x0439fc60, 0xca44, 0x4994, 0x8d, 0xee, 0x3a, 0x9a, 0xf7, 0xb7, 0x32, 0xec)
1264
-
1265
-
1266
- class IDWriteFontSet(com.pIUnknown):
1267
- _methods_ = [
1268
- ("GetFontCount",
1269
- com.STDMETHOD()),
1270
- ("GetFontFaceReference",
1271
- com.STDMETHOD()),
1272
- ("FindFontFaceReference",
1273
- com.STDMETHOD()),
1274
- ("FindFontFace",
1275
- com.STDMETHOD()),
1276
- ("GetPropertyValues",
1277
- com.STDMETHOD()),
1278
- ("GetPropertyOccurrenceCount",
1279
- com.STDMETHOD()),
1280
- ("GetMatchingFonts",
1281
- com.STDMETHOD()),
1282
- ("GetMatchingFonts",
1283
- com.STDMETHOD()),
1284
- ]
1285
-
1286
-
1287
- class IDWriteFontSetBuilder(com.pIUnknown):
1288
- _methods_ = [
1289
- ("AddFontFaceReference",
1290
- com.STDMETHOD()),
1291
- ("AddFontFaceReference",
1292
- com.STDMETHOD()),
1293
- ("AddFontSet",
1294
- com.STDMETHOD()),
1295
- ("CreateFontSet",
1296
- com.STDMETHOD(POINTER(IDWriteFontSet))),
1297
- ]
1298
-
1299
-
1300
- class IDWriteFontSetBuilder1(IDWriteFontSetBuilder, com.pIUnknown):
1301
- _methods_ = [
1302
- ("AddFontFile",
1303
- com.STDMETHOD(IDWriteFontFile)),
1304
- ]
1305
-
1306
-
1307
- class IDWriteFactory3(IDWriteFactory2, com.pIUnknown):
1308
- _methods_ = [
1309
- ("CreateGlyphRunAnalysis",
1310
- com.STDMETHOD()),
1311
- ("CreateCustomRenderingParams3",
1312
- com.STDMETHOD()),
1313
- ("CreateFontFaceReference",
1314
- com.STDMETHOD()),
1315
- ("CreateFontFaceReference",
1316
- com.STDMETHOD()),
1317
- ("GetSystemFontSet",
1318
- com.STDMETHOD()),
1319
- ("CreateFontSetBuilder",
1320
- com.STDMETHOD(POINTER(IDWriteFontSetBuilder))),
1321
- ("CreateFontCollectionFromFontSet",
1322
- com.STDMETHOD(IDWriteFontSet, POINTER(IDWriteFontCollection1))),
1323
- ("GetSystemFontCollection3",
1324
- com.STDMETHOD()),
1325
- ("GetFontDownloadQueue",
1326
- com.STDMETHOD()),
1327
- # ('GetSystemFontSet',
1328
- # com.STDMETHOD()),
1329
- ]
1330
-
1331
-
1332
- class IDWriteColorGlyphRunEnumerator1(IDWriteColorGlyphRunEnumerator, com.pIUnknown):
1333
- _methods_ = [
1334
- ("GetCurrentRun1",
1335
- com.STDMETHOD()),
1336
- ]
1337
-
1338
-
1339
- class IDWriteFactory4(IDWriteFactory3, com.pIUnknown):
1340
- _methods_ = [
1341
- ("TranslateColorGlyphRun4", # Renamed to prevent clash from previous factories.
1342
- com.STDMETHOD(D2D_POINT_2F, POINTER(DWRITE_GLYPH_RUN), c_void_p, DWRITE_GLYPH_IMAGE_FORMATS,
1343
- DWRITE_MEASURING_MODE, c_void_p, UINT32, POINTER(IDWriteColorGlyphRunEnumerator1))),
1344
- ("ComputeGlyphOrigins_",
1345
- com.STDMETHOD()),
1346
- ("ComputeGlyphOrigins",
1347
- com.STDMETHOD()),
1348
- ]
1349
-
1350
-
1351
- class IDWriteInMemoryFontFileLoader(com.pIUnknown):
1352
- _methods_ = [
1353
- ("CreateStreamFromKey",
1354
- com.STDMETHOD()),
1355
- ("CreateInMemoryFontFileReference",
1356
- com.STDMETHOD(IDWriteFactory, c_void_p, UINT, c_void_p, POINTER(IDWriteFontFile))),
1357
- ("GetFileCount",
1358
- com.STDMETHOD()),
1359
- ]
1360
-
1361
-
1362
- IID_IDWriteFactory5 = com.GUID(0x958DB99A, 0xBE2A, 0x4F09, 0xAF, 0x7D, 0x65, 0x18, 0x98, 0x03, 0xD1, 0xD3)
1363
-
1364
-
1365
- class IDWriteFactory5(IDWriteFactory4, IDWriteFactory3, IDWriteFactory2, IDWriteFactory1, IDWriteFactory,
1366
- com.pIUnknown):
1367
- _methods_ = [
1368
- ("CreateFontSetBuilder1",
1369
- com.STDMETHOD(POINTER(IDWriteFontSetBuilder1))),
1370
- ("CreateInMemoryFontFileLoader",
1371
- com.STDMETHOD(POINTER(IDWriteInMemoryFontFileLoader))),
1372
- ("CreateHttpFontFileLoader",
1373
- com.STDMETHOD()),
1374
- ("AnalyzeContainerType",
1375
- com.STDMETHOD()),
1376
- ]
1377
-
1378
-
1379
- DWriteCreateFactory = dwrite_lib.DWriteCreateFactory
1380
- DWriteCreateFactory.restype = HRESULT
1381
- DWriteCreateFactory.argtypes = [DWRITE_FACTORY_TYPE, com.REFIID, POINTER(com.pIUnknown)]
1382
-
1383
-
1384
- class ID2D1Resource(com.pIUnknown):
1385
- _methods_ = [
1386
- ("GetFactory",
1387
- com.STDMETHOD()),
1388
- ]
1389
-
1390
-
1391
- class ID2D1Brush(ID2D1Resource, com.pIUnknown):
1392
- _methods_ = [
1393
- ("SetOpacity",
1394
- com.STDMETHOD()),
1395
- ("SetTransform",
1396
- com.STDMETHOD()),
1397
- ("GetOpacity",
1398
- com.STDMETHOD()),
1399
- ("GetTransform",
1400
- com.STDMETHOD()),
1401
- ]
1402
-
1403
-
1404
- class ID2D1SolidColorBrush(ID2D1Brush, ID2D1Resource, com.pIUnknown):
1405
- _methods_ = [
1406
- ("SetColor",
1407
- com.STDMETHOD()),
1408
- ("GetColor",
1409
- com.STDMETHOD()),
1410
- ]
1411
-
1412
-
1413
- D2D1_TEXT_ANTIALIAS_MODE = UINT
1414
- D2D1_TEXT_ANTIALIAS_MODE_DEFAULT = 0
1415
- D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE = 1
1416
- D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE = 2
1417
- D2D1_TEXT_ANTIALIAS_MODE_ALIASED = 3
1418
-
1419
- D2D1_RENDER_TARGET_TYPE = UINT
1420
- D2D1_RENDER_TARGET_TYPE_DEFAULT = 0
1421
- D2D1_RENDER_TARGET_TYPE_SOFTWARE = 1
1422
- D2D1_RENDER_TARGET_TYPE_HARDWARE = 2
1423
-
1424
- D2D1_FEATURE_LEVEL = UINT
1425
- D2D1_FEATURE_LEVEL_DEFAULT = 0
1426
-
1427
- D2D1_RENDER_TARGET_USAGE = UINT
1428
- D2D1_RENDER_TARGET_USAGE_NONE = 0
1429
- D2D1_RENDER_TARGET_USAGE_FORCE_BITMAP_REMOTING = 1
1430
- D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE = 2
1431
-
1432
- DXGI_FORMAT = UINT
1433
- DXGI_FORMAT_UNKNOWN = 0
1434
-
1435
- D2D1_ALPHA_MODE = UINT
1436
- D2D1_ALPHA_MODE_UNKNOWN = 0
1437
- D2D1_ALPHA_MODE_PREMULTIPLIED = 1
1438
- D2D1_ALPHA_MODE_STRAIGHT = 2
1439
- D2D1_ALPHA_MODE_IGNORE = 3
1440
-
1441
- D2D1_DRAW_TEXT_OPTIONS = UINT
1442
- D2D1_DRAW_TEXT_OPTIONS_NO_SNAP = 0x00000001
1443
- D2D1_DRAW_TEXT_OPTIONS_CLIP = 0x00000002
1444
- D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT = 0x00000004
1445
- D2D1_DRAW_TEXT_OPTIONS_DISABLE_COLOR_BITMAP_SNAPPING = 0x00000008
1446
- D2D1_DRAW_TEXT_OPTIONS_NONE = 0x00000000
1447
- D2D1_DRAW_TEXT_OPTIONS_FORCE_DWORD = 0xffffffff
1448
-
1449
-
1450
- class D2D1_PIXEL_FORMAT(Structure):
1451
- _fields_ = (
1452
- ("format", DXGI_FORMAT),
1453
- ("alphaMode", D2D1_ALPHA_MODE),
1454
- )
1455
-
1456
-
1457
- class D2D1_RENDER_TARGET_PROPERTIES(Structure):
1458
- _fields_ = (
1459
- ("type", D2D1_RENDER_TARGET_TYPE),
1460
- ("pixelFormat", D2D1_PIXEL_FORMAT),
1461
- ("dpiX", FLOAT),
1462
- ("dpiY", FLOAT),
1463
- ("usage", D2D1_RENDER_TARGET_USAGE),
1464
- ("minLevel", D2D1_FEATURE_LEVEL),
1465
- )
1466
-
1467
-
1468
- DXGI_FORMAT_B8G8R8A8_UNORM = 87
1469
-
1470
- pixel_format = D2D1_PIXEL_FORMAT()
1471
- pixel_format.format = DXGI_FORMAT_UNKNOWN
1472
- pixel_format.alphaMode = D2D1_ALPHA_MODE_UNKNOWN
1473
-
1474
- default_target_properties = D2D1_RENDER_TARGET_PROPERTIES()
1475
- default_target_properties.type = D2D1_RENDER_TARGET_TYPE_DEFAULT
1476
- default_target_properties.pixelFormat = pixel_format
1477
- default_target_properties.dpiX = 0.0
1478
- default_target_properties.dpiY = 0.0
1479
- default_target_properties.usage = D2D1_RENDER_TARGET_USAGE_NONE
1480
- default_target_properties.minLevel = D2D1_FEATURE_LEVEL_DEFAULT
1481
-
1482
-
1483
- class ID2D1RenderTarget(ID2D1Resource, com.pIUnknown):
1484
- _methods_ = [
1485
- ("CreateBitmap",
1486
- com.STDMETHOD()),
1487
- ("CreateBitmapFromWicBitmap",
1488
- com.STDMETHOD()),
1489
- ("CreateSharedBitmap",
1490
- com.STDMETHOD()),
1491
- ("CreateBitmapBrush",
1492
- com.STDMETHOD()),
1493
- ("CreateSolidColorBrush",
1494
- com.STDMETHOD(POINTER(D2D1_COLOR_F), c_void_p, POINTER(ID2D1SolidColorBrush))),
1495
- ("CreateGradientStopCollection",
1496
- com.STDMETHOD()),
1497
- ("CreateLinearGradientBrush",
1498
- com.STDMETHOD()),
1499
- ("CreateRadialGradientBrush",
1500
- com.STDMETHOD()),
1501
- ("CreateCompatibleRenderTarget",
1502
- com.STDMETHOD()),
1503
- ("CreateLayer",
1504
- com.STDMETHOD()),
1505
- ("CreateMesh",
1506
- com.STDMETHOD()),
1507
- ("DrawLine",
1508
- com.STDMETHOD()),
1509
- ("DrawRectangle",
1510
- com.STDMETHOD()),
1511
- ("FillRectangle",
1512
- com.STDMETHOD()),
1513
- ("DrawRoundedRectangle",
1514
- com.STDMETHOD()),
1515
- ("FillRoundedRectangle",
1516
- com.STDMETHOD()),
1517
- ("DrawEllipse",
1518
- com.STDMETHOD()),
1519
- ("FillEllipse",
1520
- com.STDMETHOD()),
1521
- ("DrawGeometry",
1522
- com.STDMETHOD()),
1523
- ("FillGeometry",
1524
- com.STDMETHOD()),
1525
- ("FillMesh",
1526
- com.STDMETHOD()),
1527
- ("FillOpacityMask",
1528
- com.STDMETHOD()),
1529
- ("DrawBitmap",
1530
- com.STDMETHOD()),
1531
- ("DrawText",
1532
- com.STDMETHOD(c_wchar_p, UINT, IDWriteTextFormat, POINTER(D2D1_RECT_F), ID2D1Brush, D2D1_DRAW_TEXT_OPTIONS,
1533
- DWRITE_MEASURING_MODE)),
1534
- ("DrawTextLayout",
1535
- com.METHOD(c_void, D2D_POINT_2F, IDWriteTextLayout, ID2D1Brush, UINT32)),
1536
- ("DrawGlyphRun",
1537
- com.METHOD(c_void, D2D_POINT_2F, POINTER(DWRITE_GLYPH_RUN), ID2D1Brush, UINT32)),
1538
- ("SetTransform",
1539
- com.METHOD(c_void)),
1540
- ("GetTransform",
1541
- com.STDMETHOD()),
1542
- ("SetAntialiasMode",
1543
- com.METHOD(c_void, D2D1_TEXT_ANTIALIAS_MODE)),
1544
- ("GetAntialiasMode",
1545
- com.STDMETHOD()),
1546
- ("SetTextAntialiasMode",
1547
- com.METHOD(c_void, D2D1_TEXT_ANTIALIAS_MODE)),
1548
- ("GetTextAntialiasMode",
1549
- com.STDMETHOD()),
1550
- ("SetTextRenderingParams",
1551
- com.STDMETHOD(IDWriteRenderingParams)),
1552
- ("GetTextRenderingParams",
1553
- com.STDMETHOD()),
1554
- ("SetTags",
1555
- com.STDMETHOD()),
1556
- ("GetTags",
1557
- com.STDMETHOD()),
1558
- ("PushLayer",
1559
- com.STDMETHOD()),
1560
- ("PopLayer",
1561
- com.STDMETHOD()),
1562
- ("Flush",
1563
- com.STDMETHOD(c_void_p, c_void_p)),
1564
- ("SaveDrawingState",
1565
- com.STDMETHOD()),
1566
- ("RestoreDrawingState",
1567
- com.STDMETHOD()),
1568
- ("PushAxisAlignedClip",
1569
- com.STDMETHOD()),
1570
- ("PopAxisAlignedClip",
1571
- com.STDMETHOD()),
1572
- ("Clear",
1573
- com.METHOD(c_void, POINTER(D2D1_COLOR_F))),
1574
- ("BeginDraw",
1575
- com.METHOD(c_void)),
1576
- ("EndDraw",
1577
- com.STDMETHOD(c_void_p, c_void_p)),
1578
- ("GetPixelFormat",
1579
- com.STDMETHOD()),
1580
- ("SetDpi",
1581
- com.STDMETHOD()),
1582
- ("GetDpi",
1583
- com.STDMETHOD()),
1584
- ("GetSize",
1585
- com.STDMETHOD()),
1586
- ("GetPixelSize",
1587
- com.STDMETHOD()),
1588
- ("GetMaximumBitmapSize",
1589
- com.STDMETHOD()),
1590
- ("IsSupported",
1591
- com.STDMETHOD()),
1592
- ]
1593
-
1594
-
1595
- IID_ID2D1Factory = com.GUID(0x06152247, 0x6f50, 0x465a, 0x92, 0x45, 0x11, 0x8b, 0xfd, 0x3b, 0x60, 0x07)
1596
-
1597
-
1598
- class ID2D1Factory(com.pIUnknown):
1599
- _methods_ = [
1600
- ("ReloadSystemMetrics",
1601
- com.STDMETHOD()),
1602
- ("GetDesktopDpi",
1603
- com.STDMETHOD()),
1604
- ("CreateRectangleGeometry",
1605
- com.STDMETHOD()),
1606
- ("CreateRoundedRectangleGeometry",
1607
- com.STDMETHOD()),
1608
- ("CreateEllipseGeometry",
1609
- com.STDMETHOD()),
1610
- ("CreateGeometryGroup",
1611
- com.STDMETHOD()),
1612
- ("CreateTransformedGeometry",
1613
- com.STDMETHOD()),
1614
- ("CreatePathGeometry",
1615
- com.STDMETHOD()),
1616
- ("CreateStrokeStyle",
1617
- com.STDMETHOD()),
1618
- ("CreateDrawingStateBlock",
1619
- com.STDMETHOD()),
1620
- ("CreateWicBitmapRenderTarget",
1621
- com.STDMETHOD(IWICBitmap, POINTER(D2D1_RENDER_TARGET_PROPERTIES), POINTER(ID2D1RenderTarget))),
1622
- ("CreateHwndRenderTarget",
1623
- com.STDMETHOD()),
1624
- ("CreateDxgiSurfaceRenderTarget",
1625
- com.STDMETHOD()),
1626
- ("CreateDCRenderTarget",
1627
- com.STDMETHOD()),
1628
- ]
1629
-
1630
-
1631
- d2d_lib = windll.d2d1
1632
-
1633
- D2D1_FACTORY_TYPE = UINT
1634
- D2D1_FACTORY_TYPE_SINGLE_THREADED = 0
1635
- D2D1_FACTORY_TYPE_MULTI_THREADED = 1
1636
-
1637
- D2D1CreateFactory = d2d_lib.D2D1CreateFactory
1638
- D2D1CreateFactory.restype = HRESULT
1639
- D2D1CreateFactory.argtypes = [D2D1_FACTORY_TYPE, com.REFIID, c_void_p, c_void_p]
1640
-
1641
- # We need a WIC factory to make this work. Make sure one is in the initialized decoders.
1642
- wic_decoder = None
1643
- for decoder in pyglet.image.codecs.get_decoders():
1644
- if isinstance(decoder, WICDecoder):
1645
- wic_decoder = decoder
1646
-
1647
- if not wic_decoder:
1648
- raise Exception("Cannot use DirectWrite without a WIC Decoder")
1649
-
1650
-
1651
- def get_system_locale() -> str:
1652
- """Retrieve the string representing the system locale."""
1653
- local_name = create_unicode_buffer(LOCALE_NAME_MAX_LENGTH)
1654
- kernel32.GetUserDefaultLocaleName(local_name, LOCALE_NAME_MAX_LENGTH)
1655
- return local_name.value
1656
-
1657
-
1658
- class DirectWriteGlyphRenderer(base.GlyphRenderer):
1659
- font: Win32DirectWriteFont
1660
- antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT
1661
- draw_options = D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT if WINDOWS_8_1_OR_GREATER else D2D1_DRAW_TEXT_OPTIONS_NONE
1662
- measuring_mode = DWRITE_MEASURING_MODE_NATURAL
1663
-
1664
- def __init__(self, font: Win32DirectWriteFont) -> None:
1665
- self._render_target = None
1666
- self._bitmap = None
1667
- self._brush = None
1668
- self._bitmap_dimensions = (0, 0)
1669
- super().__init__(font)
1670
- self.font = font
1671
-
1672
- self._analyzer = IDWriteTextAnalyzer()
1673
- self.font._write_factory.CreateTextAnalyzer(byref(self._analyzer))
1674
-
1675
- self._text_analysis = TextAnalysis()
1676
-
1677
- def render(self, text: str) -> Glyph:
1678
- pass
1679
-
1680
- def render_to_image(self, text: str, width: int, height: int) -> ImageData:
1681
- """This process takes Pyglet out of the equation and uses only DirectWrite to shape and render text.
1682
- This may allows more accurate fonts (bidi, rtl, etc) in very special circumstances.
1683
- """
1684
- text_buffer = create_unicode_buffer(text)
1685
-
1686
- text_layout = IDWriteTextLayout()
1687
- self.font._write_factory.CreateTextLayout(
1688
- text_buffer,
1689
- len(text_buffer),
1690
- self.font._text_format,
1691
- width, # Doesn't affect bitmap size.
1692
- height,
1693
- byref(text_layout),
1694
- )
1695
-
1696
- layout_metrics = DWRITE_TEXT_METRICS()
1697
- text_layout.GetMetrics(byref(layout_metrics))
1698
-
1699
- width, height = int(math.ceil(layout_metrics.width)), int(math.ceil(layout_metrics.height))
1700
-
1701
- bitmap = IWICBitmap()
1702
- wic_decoder._factory.CreateBitmap(
1703
- width,
1704
- height,
1705
- GUID_WICPixelFormat32bppPBGRA,
1706
- WICBitmapCacheOnDemand,
1707
- byref(bitmap),
1708
- )
1709
-
1710
- rt = ID2D1RenderTarget()
1711
- d2d_factory.CreateWicBitmapRenderTarget(bitmap, default_target_properties, byref(rt))
1712
-
1713
- # Font aliasing rendering quality.
1714
- rt.SetTextAntialiasMode(self.antialias_mode)
1715
-
1716
- if not self._brush:
1717
- self._brush = ID2D1SolidColorBrush()
1718
-
1719
- rt.CreateSolidColorBrush(white, None, byref(self._brush))
1720
-
1721
- rt.BeginDraw()
1722
-
1723
- rt.Clear(transparent)
1724
-
1725
- rt.DrawTextLayout(no_offset,
1726
- text_layout,
1727
- self._brush,
1728
- self.draw_options)
1729
-
1730
- rt.EndDraw(None, None)
1731
-
1732
- rt.Release()
1733
-
1734
- return wic_decoder.get_image(bitmap)
1735
-
1736
- def get_string_info(self, text: str, font_face: IDWriteFontFace) -> tuple[
1737
- c_wchar, int, Array[UINT16], Array[FLOAT], Array[DWRITE_GLYPH_OFFSET], Array[UINT16]]:
1738
- """Converts a string of text into a list of indices and advances used for shaping."""
1739
- text_length = len(text.encode("utf-16-le")) // 2
1740
-
1741
- # Unicode buffer splits each two byte chars into separate indices.
1742
- text_buffer = create_unicode_buffer(text, text_length)
1743
-
1744
- # Analyze the text.
1745
- # noinspection PyTypeChecker
1746
- self._text_analysis.GenerateResults(self._analyzer, text_buffer, len(text_buffer))
1747
-
1748
- # Formula for text buffer size from Microsoft.
1749
- max_glyph_size = int(3 * text_length / 2 + 16)
1750
-
1751
- length = text_length
1752
- clusters = (UINT16 * length)()
1753
- text_props = (DWRITE_SHAPING_TEXT_PROPERTIES * length)()
1754
- indices = (UINT16 * max_glyph_size)()
1755
- glyph_props = (DWRITE_SHAPING_GLYPH_PROPERTIES * max_glyph_size)()
1756
- actual_count = UINT32()
1757
-
1758
- self._analyzer.GetGlyphs(
1759
- text_buffer,
1760
- length,
1761
- font_face,
1762
- False, # sideways
1763
- False, # righttoleft
1764
- self._text_analysis._script, # scriptAnalysis
1765
- None, # localName
1766
- None, # numberSub
1767
- None, # typo features
1768
- None, # feature range length
1769
- 0, # feature range
1770
- max_glyph_size, # max glyph size
1771
- clusters, # cluster map
1772
- text_props, # text props
1773
- indices, # glyph indices
1774
- glyph_props, # glyph pops
1775
- byref(actual_count), # glyph count
1776
- )
1777
-
1778
- advances = (FLOAT * length)()
1779
- offsets = (DWRITE_GLYPH_OFFSET * length)()
1780
- self._analyzer.GetGlyphPlacements(
1781
- text_buffer,
1782
- clusters,
1783
- text_props,
1784
- text_length,
1785
- indices,
1786
- glyph_props,
1787
- actual_count,
1788
- font_face,
1789
- self.font._font_metrics.designUnitsPerEm,
1790
- False, False,
1791
- self._text_analysis._script,
1792
- self.font.locale,
1793
- None,
1794
- None,
1795
- 0,
1796
- advances,
1797
- offsets,
1798
- )
1799
-
1800
- return text_buffer, actual_count.value, indices, advances, offsets, clusters
1801
-
1802
- def get_glyph_metrics(self, font_face: IDWriteFontFace, indices: Array[UINT16], count: int) -> list[
1803
- tuple[float, float, float, float, float]]:
1804
- """Returns a list of tuples with the following metrics per indice:
1805
- . (glyph width, glyph height, lsb, advanceWidth)
1806
- """
1807
- glyph_metrics = (DWRITE_GLYPH_METRICS * count)()
1808
- font_face.GetDesignGlyphMetrics(indices, count, glyph_metrics, False)
1809
-
1810
- metrics_out = []
1811
- for metric in glyph_metrics:
1812
- glyph_width = (metric.advanceWidth + abs(metric.leftSideBearing) + abs(metric.rightSideBearing))
1813
- glyph_height = (metric.advanceHeight - metric.topSideBearing - metric.bottomSideBearing)
1814
-
1815
- lsb = metric.leftSideBearing
1816
- bsb = metric.bottomSideBearing
1817
-
1818
- advance_width = metric.advanceWidth
1819
-
1820
- metrics_out.append((glyph_width, glyph_height, lsb, advance_width, bsb))
1821
-
1822
- return metrics_out
1823
-
1824
- def _get_single_glyph_run(self, font_face: IDWriteFontFace, size: float, indices: Array[UINT16],
1825
- advances: Array[FLOAT], offsets: Array[DWRITE_GLYPH_OFFSET], sideways: bool,
1826
- bidi: int) -> DWRITE_GLYPH_RUN:
1827
- run = DWRITE_GLYPH_RUN(
1828
- font_face,
1829
- size,
1830
- 1,
1831
- indices,
1832
- advances,
1833
- offsets,
1834
- sideways,
1835
- bidi,
1836
- )
1837
- return run
1838
-
1839
- def is_color_run(self, run: DWRITE_GLYPH_RUN) -> bool:
1840
- """Will return True if the run contains a colored glyph."""
1841
- try:
1842
- if WINDOWS_10_CREATORS_UPDATE_OR_GREATER:
1843
- enumerator = IDWriteColorGlyphRunEnumerator1()
1844
- color = self.font._write_factory.TranslateColorGlyphRun4(
1845
- no_offset,
1846
- run,
1847
- None,
1848
- DWRITE_GLYPH_IMAGE_FORMATS_ALL,
1849
- self.measuring_mode,
1850
- None,
1851
- 0,
1852
- byref(enumerator),
1853
- )
1854
- elif WINDOWS_8_1_OR_GREATER:
1855
- enumerator = IDWriteColorGlyphRunEnumerator()
1856
- color = self.font._write_factory.TranslateColorGlyphRun(
1857
- 0.0, 0.0,
1858
- run,
1859
- None,
1860
- self.measuring_mode,
1861
- None,
1862
- 0,
1863
- byref(enumerator),
1864
- )
1865
- else:
1866
- return False
1867
-
1868
- return True
1869
- except OSError as dw_err:
1870
- # HRESULT returns -2003283956 (DWRITE_E_NOCOLOR) if no color run is detected. Anything else is unexpected.
1871
- if dw_err.winerror != -2003283956:
1872
- raise dw_err
1873
-
1874
- return False
1875
-
1876
- def render_single_glyph(self, font_face: IDWriteFontFace, indice: int, advance: float, offset: DWRITE_GLYPH_OFFSET,
1877
- metrics: tuple[float, float, float, float, float]):
1878
- """Renders a single glyph using D2D DrawGlyphRun"""
1879
- glyph_width, glyph_height, glyph_lsb, glyph_advance, glyph_bsb = metrics
1880
-
1881
- # Slicing an array turns it into a python object. Maybe a better way to keep it a ctypes value?
1882
- new_indice = (UINT16 * 1)(indice)
1883
- new_advance = (FLOAT * 1)(advance)
1884
-
1885
- run = self._get_single_glyph_run(
1886
- font_face,
1887
- self.font._real_size,
1888
- new_indice, # indice,
1889
- new_advance, # advance,
1890
- pointer(offset), # offset,
1891
- False,
1892
- 0,
1893
- )
1894
-
1895
- # If it's colored, return to render it using layout.
1896
- if self.draw_options & D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT and self.is_color_run(run):
1897
- return None
1898
-
1899
- # Use the glyph's advance as a width as bitmap width.
1900
- # Some characters have no glyph width at all, just use a 1x1
1901
- if glyph_width == 0 and glyph_height == 0:
1902
- render_width = 1
1903
- render_height = 1
1904
- else:
1905
- # Use the glyph width, or if the advance is larger, use that instead.
1906
- # Diacritics usually have no proper sizing, but instead have an advance.
1907
- render_width = int(math.ceil(max(glyph_width, glyph_advance) * self.font.font_scale_ratio))
1908
- render_height = int(math.ceil(self.font.max_glyph_height))
1909
-
1910
- render_offset_x = 0
1911
- if glyph_lsb < 0:
1912
- # Negative LSB: we shift the offset, otherwise the glyph will be cut off.
1913
- render_offset_x = glyph_lsb * self.font.font_scale_ratio
1914
-
1915
- # Create new bitmap.
1916
- # TODO: We can probably adjust bitmap/baseline to reduce the whitespace and save a lot of texture space.
1917
- # Note: Floating point precision makes this a giant headache, will need to be solved for this approach.
1918
- self._create_bitmap(render_width + 1, # Add 1, sometimes AA can add an extra pixel or so.
1919
- render_height + 1)
1920
-
1921
- # Glyphs are drawn at the baseline, and with LSB, so we need to offset it based on top left position.
1922
- baseline_offset = D2D_POINT_2F(-render_offset_x - offset.advanceOffset,
1923
- self.font.ascent + offset.ascenderOffset)
1924
-
1925
- self._render_target.BeginDraw()
1926
-
1927
- self._render_target.Clear(transparent)
1928
-
1929
- self._render_target.DrawGlyphRun(baseline_offset,
1930
- run,
1931
- self._brush,
1932
- self.measuring_mode)
1933
-
1934
- self._render_target.EndDraw(None, None)
1935
- image = wic_decoder.get_image(self._bitmap)
1936
-
1937
- glyph = self.font.create_glyph(image)
1938
-
1939
- glyph.set_bearings(-self.font.descent, render_offset_x,
1940
- advance,
1941
- offset.advanceOffset,
1942
- offset.ascenderOffset)
1943
-
1944
- return glyph
1945
-
1946
- def render_using_layout(self, text: str) -> Glyph | None:
1947
- """This will render text given the built-in DirectWrite layout.
1948
-
1949
- This process allows us to take advantage of color glyphs and fallback handling that is built into DirectWrite.
1950
- This can also handle shaping and many other features if you want to render directly to a texture.
1951
- """
1952
- text_layout = self.font.create_text_layout(text)
1953
-
1954
- layout_metrics = DWRITE_TEXT_METRICS()
1955
- text_layout.GetMetrics(byref(layout_metrics))
1956
-
1957
- width = int(math.ceil(layout_metrics.width))
1958
- height = int(math.ceil(layout_metrics.height))
1959
-
1960
- if width == 0 or height == 0:
1961
- return None
1962
-
1963
- self._create_bitmap(width, height)
1964
-
1965
- # This offsets the characters if needed.
1966
- point = D2D_POINT_2F(0, 0)
1967
-
1968
- self._render_target.BeginDraw()
1969
-
1970
- self._render_target.Clear(transparent)
1971
-
1972
- self._render_target.DrawTextLayout(point,
1973
- text_layout,
1974
- self._brush,
1975
- self.draw_options)
1976
-
1977
- self._render_target.EndDraw(None, None)
1978
-
1979
- image = wic_decoder.get_image(self._bitmap)
1980
-
1981
- glyph = self.font.create_glyph(image)
1982
- glyph.set_bearings(-self.font.descent, 0, int(math.ceil(layout_metrics.width)))
1983
- return glyph
1984
-
1985
- def create_zero_glyph(self) -> Glyph:
1986
- """Zero glyph is a 1x1 image that has a -1 advance.
1987
-
1988
- This is to fill in for ligature substitutions since font system requires 1 glyph per character in a string.
1989
- """
1990
- self._create_bitmap(1, 1)
1991
- image = wic_decoder.get_image(self._bitmap)
1992
-
1993
- glyph = self.font.create_glyph(image)
1994
- glyph.set_bearings(-self.font.descent, 0, -1)
1995
- return glyph
1996
-
1997
- def _create_bitmap(self, width: int, height: int) -> None:
1998
- """Creates a bitmap using Direct2D and WIC."""
1999
- # Create a new bitmap, try to re-use the bitmap as much as we can to minimize creations.
2000
- if self._bitmap_dimensions[0] != width or self._bitmap_dimensions[1] != height:
2001
- # If dimensions aren't the same, release bitmap to create new ones.
2002
- if self._bitmap:
2003
- self._bitmap.Release()
2004
-
2005
- self._bitmap = IWICBitmap()
2006
- wic_decoder._factory.CreateBitmap(width, height,
2007
- GUID_WICPixelFormat32bppPBGRA,
2008
- WICBitmapCacheOnDemand,
2009
- byref(self._bitmap))
2010
-
2011
- self._render_target = ID2D1RenderTarget()
2012
- d2d_factory.CreateWicBitmapRenderTarget(self._bitmap, default_target_properties, byref(self._render_target))
2013
-
2014
- # Font aliasing rendering quality.
2015
- self._render_target.SetTextAntialiasMode(self.antialias_mode)
2016
-
2017
- if not self._brush:
2018
- self._brush = ID2D1SolidColorBrush()
2019
- self._render_target.CreateSolidColorBrush(white, None, byref(self._brush))
2020
-
2021
-
2022
- class Win32DirectWriteFont(base.Font):
2023
- # To load fonts from files, we need to produce a custom collection.
2024
- _custom_collection = None
2025
-
2026
- # Shared loader values
2027
- _write_factory = None # Factory required to run any DirectWrite interfaces.
2028
- _font_loader = None
2029
-
2030
- # Windows 10 loader values.
2031
- _font_builder = None
2032
- _font_set = None
2033
-
2034
- # Legacy loader values
2035
- _font_collection_loader = None
2036
- _font_cache = []
2037
- _font_loader_key = None
2038
-
2039
- _default_name = "Segoe UI" # Default font for Win7/10.
2040
-
2041
- _glyph_renderer = None
2042
- _empty_glyph = None
2043
- _zero_glyph = None
2044
-
2045
- glyph_renderer_class = DirectWriteGlyphRenderer
2046
- texture_internalformat = pyglet.gl.GL_RGBA
2047
-
2048
- def __init__(self, name: str, size: float, weight: str = "normal", italic: bool | str = False,
2049
- stretch: bool | str = False, dpi: int | None = None, locale: str | None = None) -> None:
2050
- self._filename: str | None = None
2051
- self._advance_cache = {} # Stores glyph's by the indice and advance.
2052
-
2053
- super().__init__()
2054
-
2055
- if not name:
2056
- name = self._default_name
2057
-
2058
- self._name = name
2059
- self.weight = weight
2060
- self.size = size
2061
- self.italic = italic
2062
- self.stretch = stretch
2063
- self.dpi = dpi
2064
- self.locale = locale
2065
-
2066
- if self.locale is None:
2067
- self.locale = ""
2068
- self.rtl = False # Right to left should be handled by pyglet?
2069
- # TODO: Use system locale string?
2070
-
2071
- if self.dpi is None:
2072
- self.dpi = 96
2073
-
2074
- # From DPI to DIP (Device Independent Pixels) which is what the fonts rely on.
2075
- self._real_size = (self.size * self.dpi) // 72
2076
-
2077
- self._weight = name_to_weight[self.weight]
2078
-
2079
- if self.italic:
2080
- if isinstance(self.italic, str):
2081
- self._style = name_to_style[self.italic]
2082
- else:
2083
- self._style = DWRITE_FONT_STYLE_ITALIC
2084
- else:
2085
- self._style = DWRITE_FONT_STYLE_NORMAL
2086
-
2087
- if self.stretch:
2088
- if isinstance(self.stretch, str):
2089
- self._stretch = name_to_stretch[self.stretch]
2090
- else:
2091
- self._stretch = DWRITE_FONT_STRETCH_EXPANDED
2092
- else:
2093
- self._stretch = DWRITE_FONT_STRETCH_NORMAL
2094
-
2095
- self._font_index, self._collection = self.get_collection(name)
2096
- write_font = None
2097
- # If not font found, search all collections for legacy GDI naming.
2098
- if pyglet.options["dw_legacy_naming"]:
2099
- if self._font_index is None and self._collection is None:
2100
- write_font, self._collection = self.find_font_face(name, self._weight, self._style, self._stretch)
2101
-
2102
- assert self._collection is not None, f"Font: '{name}' not found in loaded or system font collection."
2103
-
2104
- if self._font_index is not None:
2105
- font_family = IDWriteFontFamily1()
2106
- self._collection.GetFontFamily(self._font_index, byref(font_family))
2107
-
2108
- write_font = IDWriteFont()
2109
- font_family.GetFirstMatchingFont(
2110
- self._weight,
2111
- self._stretch,
2112
- self._style,
2113
- byref(write_font),
2114
- )
2115
-
2116
- # Create the text format this font will use permanently.
2117
- # Could technically be recreated, but will keep to be inline with other font objects.
2118
- self._text_format = IDWriteTextFormat()
2119
- self._write_factory.CreateTextFormat(
2120
- self._name,
2121
- self._collection,
2122
- self._weight,
2123
- self._style,
2124
- self._stretch,
2125
- self._real_size,
2126
- create_unicode_buffer(self.locale),
2127
- byref(self._text_format),
2128
- )
2129
-
2130
- font_face = IDWriteFontFace()
2131
- write_font.CreateFontFace(byref(font_face))
2132
-
2133
- self.font_face = IDWriteFontFace1()
2134
- font_face.QueryInterface(IID_IDWriteFontFace1, byref(self.font_face))
2135
-
2136
- self._font_metrics = DWRITE_FONT_METRICS()
2137
- self.font_face.GetMetrics(byref(self._font_metrics))
2138
-
2139
- self.font_scale_ratio = (self._real_size / self._font_metrics.designUnitsPerEm)
2140
-
2141
- self.ascent = math.ceil(self._font_metrics.ascent * self.font_scale_ratio)
2142
- self.descent = -round(self._font_metrics.descent * self.font_scale_ratio)
2143
- self.max_glyph_height = (self._font_metrics.ascent + self._font_metrics.descent) * self.font_scale_ratio
2144
-
2145
- self.line_gap = self._font_metrics.lineGap * self.font_scale_ratio
2146
-
2147
- self._fallback = None
2148
- if WINDOWS_8_1_OR_GREATER:
2149
- self._fallback = IDWriteFontFallback()
2150
- self._write_factory.GetSystemFontFallback(byref(self._fallback))
2151
- else:
2152
- assert _debug_print("Windows 8.1+ is required for font fallback. Colored glyphs cannot be omitted.")
2153
-
2154
- @property
2155
- def filename(self) -> str:
2156
- """Returns a filename associated with the font face.
2157
-
2158
- Note: Capable of returning more than 1 file in the future, but will do just one for now.
2159
- """
2160
- if self._filename is not None:
2161
- return self._filename
2162
-
2163
- file_ct = UINT32()
2164
- self.font_face.GetFiles(byref(file_ct), None)
2165
-
2166
- font_files = (IDWriteFontFile * file_ct.value)()
2167
-
2168
- self.font_face.GetFiles(byref(file_ct), font_files)
2169
-
2170
- self._filename = "Not Available"
2171
-
2172
- pff = font_files[0]
2173
-
2174
- key_data = c_void_p()
2175
- ff_key_size = UINT32()
2176
-
2177
- pff.GetReferenceKey(byref(key_data), byref(ff_key_size))
2178
-
2179
- loader = IDWriteFontFileLoader()
2180
- pff.GetLoader(byref(loader))
2181
-
2182
- try:
2183
- local_loader = IDWriteLocalFontFileLoader()
2184
- loader.QueryInterface(IID_IDWriteLocalFontFileLoader, byref(local_loader))
2185
- except OSError: # E_NOTIMPL
2186
- loader.Release()
2187
- pff.Release()
2188
- return self._filename
2189
-
2190
- path_len = UINT32()
2191
- local_loader.GetFilePathLengthFromKey(key_data, ff_key_size, byref(path_len))
2192
-
2193
- buffer = create_unicode_buffer(path_len.value + 1)
2194
- local_loader.GetFilePathFromKey(key_data, ff_key_size, buffer, len(buffer))
2195
-
2196
- loader.Release()
2197
- local_loader.Release()
2198
- pff.Release()
2199
-
2200
- self._filename = pathlib.PureWindowsPath(buffer.value).as_posix() # Convert to forward slashes.
2201
- return self._filename
2202
-
2203
- @property
2204
- def name(self) -> str:
2205
- return self._name
2206
-
2207
- def render_to_image(self, text: str, width: int=10000, height: int=80) -> ImageData:
2208
- """This process takes Pyglet out of the equation and uses only DirectWrite to shape and render text.
2209
- This may allow more accurate fonts (bidi, rtl, etc) in very special circumstances at the cost of
2210
- additional texture space.
2211
- """
2212
- if not self._glyph_renderer:
2213
- self._glyph_renderer = self.glyph_renderer_class(self)
2214
-
2215
- return self._glyph_renderer.render_to_image(text, width, height)
2216
-
2217
- def copy_glyph(self, glyph: base.Glyph, advance: int, offset: DWRITE_GLYPH_OFFSET) -> base.Glyph:
2218
- """This takes the existing glyph texture and puts it into a new Glyph with a new advance.
2219
- Texture memory is shared between both glyphs.
2220
- """
2221
- new_glyph = base.Glyph(glyph.x, glyph.y, glyph.z, glyph.width, glyph.height, glyph.owner)
2222
- new_glyph.set_bearings(
2223
- glyph.baseline,
2224
- glyph.lsb,
2225
- advance,
2226
- offset.advanceOffset,
2227
- offset.ascenderOffset,
2228
- )
2229
- return new_glyph
2230
-
2231
- def _render_layout_glyph(self, text_buffer: str, i: int, clusters: list[DWRITE_CLUSTER_METRICS], check_color: bool=True):
2232
- # Some glyphs can be more than 1 char. We use the clusters to determine how many of an index exist.
2233
- text_length = clusters.count(i)
2234
-
2235
- # Amount of glyphs don't always match 1:1 with text as some can be substituted or omitted. Get
2236
- # actual text buffer index.
2237
- text_index = clusters.index(i)
2238
-
2239
- # Get actual text based on the index and length.
2240
- actual_text = text_buffer[text_index:text_index + text_length]
2241
-
2242
- # Since we can't store as indice 0 without overriding, we have to store as text
2243
- if actual_text not in self.glyphs:
2244
- glyph = self._glyph_renderer.render_using_layout(text_buffer[text_index:text_index + text_length])
2245
- if glyph:
2246
- if check_color and self._glyph_renderer.draw_options & D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT:
2247
- fb_ff = self._get_fallback_font_face(text_index, text_length)
2248
- if fb_ff:
2249
- glyph.colored = self.is_fallback_str_colored(fb_ff, actual_text)
2250
- else:
2251
- glyph = self._empty_glyph
2252
-
2253
- self.glyphs[actual_text] = glyph
2254
-
2255
- return self.glyphs[actual_text]
2256
-
2257
- def is_fallback_str_colored(self, font_face: IDWriteFontFace, text: str) -> bool:
2258
- indice = UINT16()
2259
- code_points = (UINT32 * len(text))(*[ord(c) for c in text])
2260
-
2261
- font_face.GetGlyphIndices(code_points, len(text), byref(indice))
2262
-
2263
- new_indice = (UINT16 * 1)(indice)
2264
- new_advance = (FLOAT * 1)(100) # dummy
2265
- offset = (DWRITE_GLYPH_OFFSET * 1)()
2266
-
2267
- run = self._glyph_renderer._get_single_glyph_run(
2268
- font_face,
2269
- self._real_size,
2270
- new_indice, # indice,
2271
- new_advance, # advance,
2272
- offset, # offset,
2273
- False,
2274
- False,
2275
- )
2276
-
2277
- return self._glyph_renderer.is_color_run(run)
2278
-
2279
- def _get_fallback_font_face(self, text_index: int, text_length: int) -> IDWriteFontFace | None:
2280
- if WINDOWS_8_1_OR_GREATER:
2281
- out_length = UINT32()
2282
- fb_font = IDWriteFont()
2283
- scale = FLOAT()
2284
-
2285
- self._fallback.MapCharacters(
2286
- self._glyph_renderer._text_analysis,
2287
- text_index,
2288
- text_length,
2289
- None,
2290
- None,
2291
- self._weight,
2292
- self._style,
2293
- self._stretch,
2294
- byref(out_length),
2295
- byref(fb_font),
2296
- byref(scale),
2297
- )
2298
-
2299
- if fb_font:
2300
- fb_font_face = IDWriteFontFace()
2301
- fb_font.CreateFontFace(byref(fb_font_face))
2302
-
2303
- return fb_font_face
2304
-
2305
- return None
2306
-
2307
- def get_glyphs_no_shape(self, text: str) -> list[Glyph]:
2308
- """This differs in that it does not attempt to shape the text at all. May be useful in cases where your font
2309
- has no special shaping requirements, spacing is the same, or some other reason where faster performance is
2310
- wanted and you can get away with this.
2311
- """
2312
- if not self._glyph_renderer:
2313
- self._glyph_renderer = self.glyph_renderer_class(self)
2314
- self._empty_glyph = self._glyph_renderer.render_using_layout(" ")
2315
-
2316
- glyphs = []
2317
- for c in text:
2318
- if c == "\t":
2319
- c = " "
2320
-
2321
- if c not in self.glyphs:
2322
- self.glyphs[c] = self._glyph_renderer.render_using_layout(c)
2323
- if not self.glyphs[c]:
2324
- self.glyphs[c] = self._empty_glyph
2325
-
2326
- glyphs.append(self.glyphs[c])
2327
-
2328
- return glyphs
2329
-
2330
- def get_glyphs(self, text: str) -> list[Glyph]:
2331
- if not self._glyph_renderer:
2332
- self._glyph_renderer = self.glyph_renderer_class(self)
2333
- self._empty_glyph = self._glyph_renderer.render_using_layout(" ")
2334
- self._zero_glyph = self._glyph_renderer.create_zero_glyph()
2335
-
2336
- text_buffer, actual_count, indices, advances, offsets, clusters = self._glyph_renderer.get_string_info(text,
2337
- self.font_face)
2338
-
2339
- metrics = self._glyph_renderer.get_glyph_metrics(self.font_face, indices, actual_count)
2340
-
2341
- formatted_clusters = list(clusters)
2342
-
2343
- # Convert to real sizes.
2344
- for i in range(actual_count):
2345
- advances[i] *= self.font_scale_ratio
2346
-
2347
- for i in range(actual_count):
2348
- offsets[i].advanceOffset *= self.font_scale_ratio
2349
- offsets[i].ascenderOffset *= self.font_scale_ratio
2350
-
2351
- glyphs = []
2352
-
2353
- # Pyglet expects 1 glyph for every string. However, ligatures can combine 1 or more glyphs, leading
2354
- # to issues with multilines producing wrong output.
2355
- substitutions = {}
2356
- for idx in clusters:
2357
- ct = formatted_clusters.count(idx)
2358
- if ct > 1:
2359
- substitutions[idx] = ct - 1
2360
-
2361
- for i in range(actual_count):
2362
- indice = indices[i]
2363
-
2364
- if indice == 0:
2365
- # If an indice is 0, it will return no glyph. In this case we attempt to render leveraging
2366
- # the built in text layout from MS. Which depending on version can use fallback fonts and other tricks
2367
- # to possibly get something of use.
2368
- glyph = self._render_layout_glyph(text_buffer, i, formatted_clusters)
2369
- glyphs.append(glyph)
2370
- else:
2371
- advance_key = (indice, advances[i], offsets[i].advanceOffset, offsets[i].ascenderOffset)
2372
-
2373
- # Glyphs can vary depending on shaping. We will cache it by indice, advance, and offset.
2374
- # Possible to just cache without offset and set them each time. This may be faster?
2375
- if indice in self.glyphs:
2376
- if advance_key in self._advance_cache:
2377
- glyph = self._advance_cache[advance_key]
2378
- else:
2379
- glyph = self.copy_glyph(self.glyphs[indice], advances[i], offsets[i])
2380
- self._advance_cache[advance_key] = glyph
2381
- else:
2382
- glyph = self._glyph_renderer.render_single_glyph(self.font_face, indice, advances[i], offsets[i],
2383
- metrics[i])
2384
- if glyph is None: # Will only return None if a color glyph is found. Use DW to render it directly.
2385
- glyph = self._render_layout_glyph(text_buffer, i, formatted_clusters, check_color=False)
2386
- glyph.colored = True
2387
-
2388
- self.glyphs[indice] = glyph
2389
- self._advance_cache[advance_key] = glyph
2390
-
2391
- glyphs.append(glyph)
2392
-
2393
- if i in substitutions:
2394
- for _ in range(substitutions[i]):
2395
- glyphs.append(self._zero_glyph)
2396
-
2397
- return glyphs
2398
-
2399
- def create_text_layout(self, text: str) -> IDWriteTextLayout:
2400
- text_buffer = create_unicode_buffer(text)
2401
-
2402
- text_layout = IDWriteTextLayout()
2403
- hr = self._write_factory.CreateTextLayout(text_buffer,
2404
- len(text_buffer),
2405
- self._text_format,
2406
- 10000, # Doesn't affect bitmap size.
2407
- 80,
2408
- byref(text_layout),
2409
- )
2410
-
2411
- return text_layout
2412
-
2413
- @classmethod
2414
- def _initialize_direct_write(cls: type[Win32DirectWriteFont]) -> None:
2415
- """All direct write fonts needs factory access as well as the loaders."""
2416
- if WINDOWS_10_CREATORS_UPDATE_OR_GREATER:
2417
- cls._write_factory = IDWriteFactory5()
2418
- guid = IID_IDWriteFactory5
2419
- elif WINDOWS_8_1_OR_GREATER:
2420
- cls._write_factory = IDWriteFactory2()
2421
- guid = IID_IDWriteFactory2
2422
- else:
2423
- cls._write_factory = IDWriteFactory()
2424
- guid = IID_IDWriteFactory
2425
-
2426
- DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, guid, byref(cls._write_factory))
2427
-
2428
- @classmethod
2429
- def _initialize_custom_loaders(cls: type[Win32DirectWriteFont]) -> None:
2430
- """Initialize the loaders needed to load custom fonts."""
2431
- if WINDOWS_10_CREATORS_UPDATE_OR_GREATER:
2432
- # Windows 10 finally has a built in loader that can take data and make a font out of it w/ COMs.
2433
- cls._font_loader = IDWriteInMemoryFontFileLoader()
2434
- cls._write_factory.CreateInMemoryFontFileLoader(byref(cls._font_loader))
2435
- cls._write_factory.RegisterFontFileLoader(cls._font_loader)
2436
-
2437
- # Used for grouping fonts together.
2438
- cls._font_builder = IDWriteFontSetBuilder1()
2439
- cls._write_factory.CreateFontSetBuilder1(byref(cls._font_builder))
2440
- else:
2441
- cls._font_loader = LegacyFontFileLoader()
2442
-
2443
- # Note: RegisterFontLoader takes a pointer. However, for legacy we implement our own callback interface.
2444
- # Therefore we need to pass to the actual pointer directly.
2445
- cls._write_factory.RegisterFontFileLoader(cls._font_loader.as_interface(IDWriteFontFileLoader_LI))
2446
-
2447
- cls._font_collection_loader = LegacyCollectionLoader(cls._write_factory, cls._font_loader)
2448
- cls._write_factory.RegisterFontCollectionLoader(cls._font_collection_loader)
2449
-
2450
- cls._font_loader_key = cast(create_unicode_buffer("legacy_font_loader"), c_void_p)
2451
-
2452
- @classmethod
2453
- def add_font_data(cls: type[Win32DirectWriteFont], data: BinaryIO) -> None:
2454
- if not cls._write_factory:
2455
- cls._initialize_direct_write()
2456
-
2457
- if not cls._font_loader:
2458
- cls._initialize_custom_loaders()
2459
-
2460
- if WINDOWS_10_CREATORS_UPDATE_OR_GREATER:
2461
- font_file = IDWriteFontFile()
2462
- hr = cls._font_loader.CreateInMemoryFontFileReference(cls._write_factory,
2463
- data,
2464
- len(data),
2465
- None,
2466
- byref(font_file))
2467
-
2468
- hr = cls._font_builder.AddFontFile(font_file)
2469
- if hr != 0:
2470
- raise Exception("This font file data is not not a font or unsupported.")
2471
-
2472
- # We have to rebuild collection everytime we add a font.
2473
- # No way to add fonts to the collection once the FontSet and Collection are created.
2474
- # Release old one and renew.
2475
- if cls._custom_collection:
2476
- cls._font_set.Release()
2477
- cls._custom_collection.Release()
2478
-
2479
- cls._font_set = IDWriteFontSet()
2480
- cls._font_builder.CreateFontSet(byref(cls._font_set))
2481
-
2482
- cls._custom_collection = IDWriteFontCollection1()
2483
- cls._write_factory.CreateFontCollectionFromFontSet(cls._font_set, byref(cls._custom_collection))
2484
-
2485
- else:
2486
- cls._font_cache.append(data)
2487
-
2488
- # If a collection exists, we need to completely remake the collection, delete everything and start over.
2489
- if cls._custom_collection:
2490
- cls._custom_collection = None
2491
-
2492
- cls._write_factory.UnregisterFontCollectionLoader(cls._font_collection_loader)
2493
- cls._write_factory.UnregisterFontFileLoader(cls._font_loader)
2494
-
2495
- cls._font_loader = LegacyFontFileLoader()
2496
- cls._font_collection_loader = LegacyCollectionLoader(cls._write_factory, cls._font_loader)
2497
-
2498
- cls._write_factory.RegisterFontCollectionLoader(cls._font_collection_loader)
2499
- cls._write_factory.RegisterFontFileLoader(cls._font_loader.as_interface(IDWriteFontFileLoader_LI))
2500
-
2501
- cls._font_collection_loader.AddFontData(cls._font_cache)
2502
-
2503
- cls._custom_collection = IDWriteFontCollection()
2504
-
2505
- cls._write_factory.CreateCustomFontCollection(cls._font_collection_loader,
2506
- cls._font_loader_key,
2507
- sizeof(cls._font_loader_key),
2508
- byref(cls._custom_collection))
2509
-
2510
- @classmethod
2511
- def get_collection(cls: type[Win32DirectWriteFont], font_name: str) -> tuple[int | None, IDWriteFontCollection1 | None]:
2512
- """Returns which collection this font belongs to (system or custom collection), as well as its index in the
2513
- collection.
2514
- """
2515
- if not cls._write_factory:
2516
- cls._initialize_direct_write()
2517
-
2518
- """Returns a collection the font_name belongs to."""
2519
- font_index = UINT()
2520
- font_exists = BOOL()
2521
-
2522
- # Check custom loaded font collections.
2523
- if cls._custom_collection:
2524
- cls._custom_collection.FindFamilyName(create_unicode_buffer(font_name),
2525
- byref(font_index),
2526
- byref(font_exists))
2527
-
2528
- if font_exists.value:
2529
- return font_index.value, cls._custom_collection
2530
-
2531
- # Check if font is in the system collection.
2532
- # Do not cache these values permanently as system font collection can be updated during runtime.
2533
- sys_collection = IDWriteFontCollection()
2534
- if not font_exists.value:
2535
- cls._write_factory.GetSystemFontCollection(byref(sys_collection), 1)
2536
- sys_collection.FindFamilyName(create_unicode_buffer(font_name),
2537
- byref(font_index),
2538
- byref(font_exists))
2539
-
2540
- if font_exists.value:
2541
- return font_index.value, sys_collection
2542
-
2543
- return None, None
2544
-
2545
- @classmethod
2546
- def find_font_face(cls, font_name: str, weight: str, italic: bool | str, stretch: bool | str) -> tuple[
2547
- IDWriteFont | None, IDWriteFontCollection | None]:
2548
- """This will search font collections for legacy RBIZ names. However, matching to weight, italic, stretch is
2549
- problematic in that there are many values. We parse the font name looking for matches to the name database,
2550
- and attempt to pick the closest match.
2551
- This will search all fonts on the system and custom loaded, and all of their font faces. Returns a collection
2552
- and IDWriteFont if successful.
2553
- """
2554
- p_weight, p_italic, p_stretch = cls.parse_name(font_name, weight, italic, stretch)
2555
-
2556
- _debug_print(f"directwrite: '{font_name}' not found. Attempting legacy name lookup in all collections.")
2557
- if cls._custom_collection:
2558
- collection_idx = cls.find_legacy_font(cls._custom_collection, font_name, p_weight, p_italic, p_stretch)
2559
- if collection_idx is not None:
2560
- return collection_idx, cls._custom_collection
2561
-
2562
- sys_collection = IDWriteFontCollection()
2563
- cls._write_factory.GetSystemFontCollection(byref(sys_collection), 1)
2564
-
2565
- collection_idx = cls.find_legacy_font(sys_collection, font_name, p_weight, p_italic, p_stretch)
2566
- if collection_idx is not None:
2567
- return collection_idx, sys_collection
2568
-
2569
- return None, None
2570
-
2571
- @classmethod
2572
- def have_font(cls: type[Win32DirectWriteFont], name: str) -> bool:
2573
- if cls.get_collection(name)[0] is not None:
2574
- return True
2575
-
2576
- return False
2577
-
2578
- @staticmethod
2579
- def parse_name(font_name: str, weight: str, style: int, stretch: int) -> tuple[str, int, int]:
2580
- """Attempt at parsing any special names in a font for legacy checks. Takes the first found."""
2581
- font_name = font_name.lower()
2582
- split_name = font_name.split(" ")
2583
-
2584
- found_weight = weight
2585
- found_style = style
2586
- found_stretch = stretch
2587
-
2588
- # Only search if name is split more than once.
2589
- if len(split_name) > 1:
2590
- for name, value in name_to_weight.items():
2591
- if name in split_name:
2592
- found_weight = value
2593
- break
2594
-
2595
- for name, value in name_to_style.items():
2596
- if name in split_name:
2597
- found_style = value
2598
- break
2599
-
2600
- for name, value in name_to_stretch.items():
2601
- if name in split_name:
2602
- found_stretch = value
2603
- break
2604
-
2605
- return found_weight, found_style, found_stretch
2606
-
2607
- @staticmethod
2608
- def find_legacy_font(collection: IDWriteFontCollection, font_name: str, weight: str, italic: bool | str, stretch: bool | str, full_debug: bool=False) -> IDWriteFont | None:
2609
- coll_count = collection.GetFontFamilyCount()
2610
-
2611
- assert _debug_print(f"directwrite: Found {coll_count} fonts in collection.")
2612
-
2613
- locale = get_system_locale()
2614
-
2615
- for i in range(coll_count):
2616
- family = IDWriteFontFamily()
2617
- collection.GetFontFamily(i, byref(family))
2618
-
2619
- # Just check the first character in Family Names to reduce search time. Arial -> A's only.
2620
- family_name_str = IDWriteLocalizedStrings()
2621
- family.GetFamilyNames(byref(family_name_str))
2622
-
2623
- family_names = Win32DirectWriteFont.unpack_localized_string(family_name_str, locale)
2624
- family_name = family_names[0]
2625
-
2626
- if family_name[0] != font_name[0]:
2627
- family.Release()
2628
- continue
2629
-
2630
- assert _debug_print(f"directwrite: Inspecting family name: {family_name}")
2631
-
2632
- # Fonts in the family. Full search to search all font faces, typically the first will be good enough to tell
2633
- ft_ct = family.GetFontCount()
2634
-
2635
- face_names = []
2636
- matches = []
2637
- for j in range(ft_ct):
2638
- temp_ft = IDWriteFont()
2639
- family.GetFont(j, byref(temp_ft))
2640
-
2641
- if _debug_font and full_debug:
2642
- fc_str = IDWriteLocalizedStrings()
2643
- temp_ft.GetFaceNames(byref(fc_str))
2644
-
2645
- strings = Win32DirectWriteFont.unpack_localized_string(fc_str, locale)
2646
- face_names.extend(strings)
2647
-
2648
- print(f"directwrite: Face names found: {strings}")
2649
-
2650
- # Check for GDI compatibility name
2651
- compat_names = IDWriteLocalizedStrings()
2652
- exists = BOOL()
2653
- temp_ft.GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES,
2654
- byref(compat_names),
2655
- byref(exists))
2656
-
2657
- # Successful in finding GDI name.
2658
- match_found = False
2659
- if exists.value != 0:
2660
- for compat_name in Win32DirectWriteFont.unpack_localized_string(compat_names, locale):
2661
- if compat_name == font_name:
2662
- assert _debug_print(
2663
- f"Found legacy name '{font_name}' as '{family_name}' in font face '{j}' (collection "
2664
- f"id #{i}).")
2665
-
2666
- match_found = True
2667
- matches.append((temp_ft.GetWeight(), temp_ft.GetStyle(), temp_ft.GetStretch(), temp_ft))
2668
- break
2669
-
2670
- # Release resource if not a match.
2671
- if not match_found:
2672
- temp_ft.Release()
2673
-
2674
- family.Release()
2675
-
2676
- # If we have matches, we've already parsed through the proper family. Now try to match.
2677
- if matches:
2678
- write_font = Win32DirectWriteFont.match_closest_font(matches, weight, italic, stretch)
2679
-
2680
- # Cleanup other matches not used.
2681
- for match in matches:
2682
- if match[3] != write_font:
2683
- match[3].Release() # Release all other matches.
2684
-
2685
- return write_font
2686
-
2687
- return None
2688
-
2689
- @staticmethod
2690
- def match_closest_font(font_list: list[tuple[int, int, int, IDWriteFont]], weight: str, italic: int, stretch: int) -> IDWriteFont | None:
2691
- """Match the closest font to the parameters specified.
2692
-
2693
- If a full match is not found, a secondary match will be found based on similar features. This can probably
2694
- be improved, but it is possible you could get a different font style than expected.
2695
- """
2696
- closest = []
2697
- for match in font_list:
2698
- (f_weight, f_style, f_stretch, writefont) = match
2699
-
2700
- # Found perfect match, no need for the rest.
2701
- if f_weight == weight and f_style == italic and f_stretch == stretch:
2702
- _debug_print(
2703
- f"directwrite: full match found. (weight: {f_weight}, italic: {f_style}, stretch: {f_stretch})")
2704
- return writefont
2705
-
2706
- prop_match = 0
2707
- similar_match = 0
2708
- # Look for a full match, otherwise look for close enough.
2709
- # For example, Arial Black only has Oblique, not Italic, but good enough if you want slanted text.
2710
- if f_weight == weight:
2711
- prop_match += 1
2712
- elif weight != DWRITE_FONT_WEIGHT_NORMAL and f_weight != DWRITE_FONT_WEIGHT_NORMAL:
2713
- similar_match += 1
2714
-
2715
- if f_style == italic:
2716
- prop_match += 1
2717
- elif italic != DWRITE_FONT_STYLE_NORMAL and f_style != DWRITE_FONT_STYLE_NORMAL:
2718
- similar_match += 1
2719
-
2720
- if stretch == f_stretch:
2721
- prop_match += 1
2722
- elif stretch != DWRITE_FONT_STRETCH_NORMAL and f_stretch != DWRITE_FONT_STRETCH_NORMAL:
2723
- similar_match += 1
2724
-
2725
- closest.append((prop_match, similar_match, *match))
2726
-
2727
- # If we get here, no perfect match, sort by highest perfect match, to secondary matches.
2728
- closest.sort(key=lambda fts: (fts[0], fts[1]), reverse=True)
2729
-
2730
- if closest:
2731
- # Take the first match after sorting.
2732
- closest_match = closest[0]
2733
- _debug_print(f"directwrite: falling back to partial match. "
2734
- f"(weight: {closest_match[2]}, italic: {closest_match[3]}, stretch: {closest_match[4]})")
2735
- return closest_match[5]
2736
-
2737
- return None
2738
-
2739
- @staticmethod
2740
- def unpack_localized_string(local_string: IDWriteLocalizedStrings, locale: str) -> list[str]:
2741
- """Takes IDWriteLocalizedStrings and unpacks the strings inside of it into a list."""
2742
- str_array_len = local_string.GetCount()
2743
-
2744
- strings = []
2745
- for _ in range(str_array_len):
2746
- string_size = UINT32()
2747
-
2748
- idx = Win32DirectWriteFont.get_localized_index(local_string, locale)
2749
-
2750
- local_string.GetStringLength(idx, byref(string_size))
2751
-
2752
- buffer_size = string_size.value
2753
-
2754
- buffer = create_unicode_buffer(buffer_size + 1)
2755
-
2756
- local_string.GetString(idx, buffer, len(buffer))
2757
-
2758
- strings.append(buffer.value)
2759
-
2760
- local_string.Release()
2761
-
2762
- return strings
2763
-
2764
- @staticmethod
2765
- def get_localized_index(strings: IDWriteLocalizedStrings, locale: str) -> int:
2766
- idx = UINT32()
2767
- exists = BOOL()
2768
-
2769
- if locale:
2770
- strings.FindLocaleName(locale, byref(idx), byref(exists))
2771
-
2772
- if not exists.value:
2773
- # fallback to english.
2774
- strings.FindLocaleName("en-us", byref(idx), byref(exists))
2775
-
2776
- if not exists:
2777
- return 0
2778
-
2779
- return idx.value
2780
-
2781
- return 0
2782
-
2783
-
2784
- d2d_factory = ID2D1Factory()
2785
- hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, IID_ID2D1Factory, None, byref(d2d_factory))
2786
-
2787
- WICBitmapCreateCacheOption = UINT
2788
- WICBitmapNoCache = 0
2789
- WICBitmapCacheOnDemand = 0x1
2790
- WICBitmapCacheOnLoad = 0x2
2791
-
2792
- transparent = D2D1_COLOR_F(0.0, 0.0, 0.0, 0.0)
2793
- white = D2D1_COLOR_F(1.0, 1.0, 1.0, 1.0)
2794
- no_offset = D2D_POINT_2F(0, 0)
2795
-
2796
- # If we are not shaping, monkeypatch to no shape function.
2797
- if pyglet.options["win32_disable_shaping"]:
2798
- Win32DirectWriteFont.get_glyphs = Win32DirectWriteFont.get_glyphs_no_shape