pyglet 2.1.13__py3-none-any.whl → 3.0.dev1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. pyglet/__init__.py +67 -61
  2. pyglet/__init__.pyi +15 -8
  3. pyglet/app/__init__.py +22 -13
  4. pyglet/app/async_app.py +212 -0
  5. pyglet/app/base.py +2 -1
  6. pyglet/app/{xlib.py → linux.py} +3 -3
  7. pyglet/config/__init__.py +101 -0
  8. pyglet/config/gl/__init__.py +30 -0
  9. pyglet/config/gl/egl.py +120 -0
  10. pyglet/config/gl/macos.py +262 -0
  11. pyglet/config/gl/windows.py +267 -0
  12. pyglet/config/gl/x11.py +142 -0
  13. pyglet/customtypes.py +43 -2
  14. pyglet/display/__init__.py +8 -6
  15. pyglet/display/base.py +3 -63
  16. pyglet/display/cocoa.py +12 -17
  17. pyglet/display/emscripten.py +39 -0
  18. pyglet/display/headless.py +23 -30
  19. pyglet/display/wayland.py +157 -0
  20. pyglet/display/win32.py +5 -21
  21. pyglet/display/xlib.py +19 -27
  22. pyglet/display/xlib_vidmoderestore.py +2 -2
  23. pyglet/enums.py +183 -0
  24. pyglet/event.py +0 -1
  25. pyglet/experimental/geoshader_sprite.py +15 -13
  26. pyglet/experimental/hidraw.py +6 -15
  27. pyglet/experimental/multitexture_sprite.py +31 -19
  28. pyglet/experimental/particles.py +13 -35
  29. pyglet/font/__init__.py +251 -85
  30. pyglet/font/base.py +116 -61
  31. pyglet/font/dwrite/__init__.py +349 -204
  32. pyglet/font/dwrite/dwrite_lib.py +27 -5
  33. pyglet/font/fontconfig.py +14 -6
  34. pyglet/font/freetype.py +138 -87
  35. pyglet/font/freetype_lib.py +19 -0
  36. pyglet/font/group.py +179 -0
  37. pyglet/font/harfbuzz/__init__.py +3 -3
  38. pyglet/font/pyodide_js.py +310 -0
  39. pyglet/font/quartz.py +319 -126
  40. pyglet/font/ttf.py +45 -3
  41. pyglet/font/user.py +14 -19
  42. pyglet/font/win32.py +45 -21
  43. pyglet/graphics/__init__.py +8 -787
  44. pyglet/graphics/allocation.py +115 -1
  45. pyglet/graphics/api/__init__.py +77 -0
  46. pyglet/graphics/api/base.py +299 -0
  47. pyglet/graphics/api/gl/__init__.py +58 -0
  48. pyglet/graphics/api/gl/base.py +24 -0
  49. pyglet/graphics/{vertexbuffer.py → api/gl/buffer.py} +104 -159
  50. pyglet/graphics/api/gl/cocoa/context.py +76 -0
  51. pyglet/graphics/api/gl/context.py +391 -0
  52. pyglet/graphics/api/gl/default_shaders.py +0 -0
  53. pyglet/graphics/api/gl/draw.py +627 -0
  54. pyglet/graphics/api/gl/egl/__init__.py +0 -0
  55. pyglet/graphics/api/gl/egl/context.py +92 -0
  56. pyglet/graphics/api/gl/enums.py +76 -0
  57. pyglet/graphics/api/gl/framebuffer.py +315 -0
  58. pyglet/graphics/api/gl/gl.py +5463 -0
  59. pyglet/graphics/api/gl/gl_info.py +188 -0
  60. pyglet/graphics/api/gl/global_opengl.py +226 -0
  61. pyglet/{gl → graphics/api/gl}/lib.py +34 -18
  62. pyglet/graphics/api/gl/shader.py +1476 -0
  63. pyglet/graphics/api/gl/shapes.py +55 -0
  64. pyglet/graphics/api/gl/sprite.py +102 -0
  65. pyglet/graphics/api/gl/state.py +219 -0
  66. pyglet/graphics/api/gl/text.py +190 -0
  67. pyglet/graphics/api/gl/texture.py +1526 -0
  68. pyglet/graphics/{vertexarray.py → api/gl/vertexarray.py} +11 -13
  69. pyglet/graphics/api/gl/vertexdomain.py +751 -0
  70. pyglet/graphics/api/gl/win32/__init__.py +0 -0
  71. pyglet/graphics/api/gl/win32/context.py +108 -0
  72. pyglet/graphics/api/gl/win32/wgl_info.py +24 -0
  73. pyglet/graphics/api/gl/xlib/__init__.py +0 -0
  74. pyglet/graphics/api/gl/xlib/context.py +174 -0
  75. pyglet/{gl → graphics/api/gl/xlib}/glx_info.py +26 -31
  76. pyglet/graphics/api/gl1/__init__.py +0 -0
  77. pyglet/{gl → graphics/api/gl1}/gl_compat.py +3 -2
  78. pyglet/graphics/api/gl2/__init__.py +0 -0
  79. pyglet/graphics/api/gl2/buffer.py +320 -0
  80. pyglet/graphics/api/gl2/draw.py +600 -0
  81. pyglet/graphics/api/gl2/global_opengl.py +122 -0
  82. pyglet/graphics/api/gl2/shader.py +200 -0
  83. pyglet/graphics/api/gl2/shapes.py +51 -0
  84. pyglet/graphics/api/gl2/sprite.py +79 -0
  85. pyglet/graphics/api/gl2/text.py +175 -0
  86. pyglet/graphics/api/gl2/vertexdomain.py +364 -0
  87. pyglet/graphics/api/webgl/__init__.py +233 -0
  88. pyglet/graphics/api/webgl/buffer.py +302 -0
  89. pyglet/graphics/api/webgl/context.py +234 -0
  90. pyglet/graphics/api/webgl/draw.py +590 -0
  91. pyglet/graphics/api/webgl/enums.py +76 -0
  92. pyglet/graphics/api/webgl/framebuffer.py +360 -0
  93. pyglet/graphics/api/webgl/gl.py +1537 -0
  94. pyglet/graphics/api/webgl/gl_info.py +130 -0
  95. pyglet/graphics/api/webgl/shader.py +1346 -0
  96. pyglet/graphics/api/webgl/shapes.py +92 -0
  97. pyglet/graphics/api/webgl/sprite.py +102 -0
  98. pyglet/graphics/api/webgl/state.py +227 -0
  99. pyglet/graphics/api/webgl/text.py +187 -0
  100. pyglet/graphics/api/webgl/texture.py +1227 -0
  101. pyglet/graphics/api/webgl/vertexarray.py +54 -0
  102. pyglet/graphics/api/webgl/vertexdomain.py +616 -0
  103. pyglet/graphics/api/webgl/webgl_js.pyi +307 -0
  104. pyglet/{image → graphics}/atlas.py +33 -32
  105. pyglet/graphics/base.py +10 -0
  106. pyglet/graphics/buffer.py +245 -0
  107. pyglet/graphics/draw.py +578 -0
  108. pyglet/graphics/framebuffer.py +26 -0
  109. pyglet/graphics/instance.py +178 -69
  110. pyglet/graphics/shader.py +267 -1553
  111. pyglet/graphics/state.py +83 -0
  112. pyglet/graphics/texture.py +703 -0
  113. pyglet/graphics/vertexdomain.py +695 -538
  114. pyglet/gui/ninepatch.py +10 -10
  115. pyglet/gui/widgets.py +120 -10
  116. pyglet/image/__init__.py +20 -1973
  117. pyglet/image/animation.py +12 -12
  118. pyglet/image/base.py +730 -0
  119. pyglet/image/codecs/__init__.py +9 -0
  120. pyglet/image/codecs/bmp.py +53 -30
  121. pyglet/image/codecs/dds.py +53 -31
  122. pyglet/image/codecs/gdiplus.py +38 -14
  123. pyglet/image/codecs/gdkpixbuf2.py +0 -2
  124. pyglet/image/codecs/js_image.py +99 -0
  125. pyglet/image/codecs/ktx2.py +161 -0
  126. pyglet/image/codecs/pil.py +1 -1
  127. pyglet/image/codecs/png.py +1 -1
  128. pyglet/image/codecs/wic.py +11 -2
  129. pyglet/info.py +26 -24
  130. pyglet/input/__init__.py +8 -0
  131. pyglet/input/base.py +163 -105
  132. pyglet/input/controller.py +13 -19
  133. pyglet/input/controller_db.py +39 -24
  134. pyglet/input/emscripten/__init__.py +18 -0
  135. pyglet/input/emscripten/gamepad_js.py +397 -0
  136. pyglet/input/linux/__init__.py +11 -5
  137. pyglet/input/linux/evdev.py +10 -11
  138. pyglet/input/linux/x11_xinput.py +2 -2
  139. pyglet/input/linux/x11_xinput_tablet.py +1 -1
  140. pyglet/input/macos/__init__.py +7 -2
  141. pyglet/input/macos/darwin_gc.py +559 -0
  142. pyglet/input/win32/__init__.py +1 -1
  143. pyglet/input/win32/directinput.py +34 -29
  144. pyglet/input/win32/xinput.py +11 -61
  145. pyglet/lib.py +3 -3
  146. pyglet/libs/__init__.py +1 -1
  147. pyglet/{gl → libs/darwin}/agl.py +1 -1
  148. pyglet/libs/darwin/cocoapy/__init__.py +2 -2
  149. pyglet/libs/darwin/cocoapy/cocoahelpers.py +181 -0
  150. pyglet/libs/darwin/cocoapy/cocoalibs.py +31 -0
  151. pyglet/libs/darwin/cocoapy/cocoatypes.py +27 -0
  152. pyglet/libs/darwin/cocoapy/runtime.py +81 -45
  153. pyglet/libs/darwin/coreaudio.py +4 -4
  154. pyglet/{gl → libs/darwin}/lib_agl.py +9 -8
  155. pyglet/libs/darwin/quartzkey.py +1 -3
  156. pyglet/libs/egl/__init__.py +2 -0
  157. pyglet/libs/egl/egl_lib.py +576 -0
  158. pyglet/libs/egl/eglext.py +51 -5
  159. pyglet/libs/linux/__init__.py +0 -0
  160. pyglet/libs/linux/egl/__init__.py +0 -0
  161. pyglet/libs/linux/egl/eglext.py +22 -0
  162. pyglet/libs/linux/glx/__init__.py +0 -0
  163. pyglet/{gl → libs/linux/glx}/glx.py +13 -14
  164. pyglet/{gl → libs/linux/glx}/glxext_arb.py +408 -192
  165. pyglet/{gl → libs/linux/glx}/glxext_mesa.py +1 -1
  166. pyglet/{gl → libs/linux/glx}/glxext_nv.py +345 -164
  167. pyglet/{gl → libs/linux/glx}/lib_glx.py +3 -2
  168. pyglet/libs/linux/wayland/__init__.py +0 -0
  169. pyglet/libs/linux/wayland/client.py +1068 -0
  170. pyglet/libs/linux/wayland/lib_wayland.py +207 -0
  171. pyglet/libs/linux/wayland/wayland_egl.py +38 -0
  172. pyglet/libs/{wayland → linux/wayland}/xkbcommon.py +26 -0
  173. pyglet/libs/{x11 → linux/x11}/xf86vmode.py +4 -4
  174. pyglet/libs/{x11 → linux/x11}/xinerama.py +2 -2
  175. pyglet/libs/{x11 → linux/x11}/xinput.py +10 -10
  176. pyglet/libs/linux/x11/xrandr.py +0 -0
  177. pyglet/libs/{x11 → linux/x11}/xrender.py +1 -1
  178. pyglet/libs/shared/__init__.py +0 -0
  179. pyglet/libs/shared/spirv/__init__.py +0 -0
  180. pyglet/libs/shared/spirv/lib_shaderc.py +85 -0
  181. pyglet/libs/shared/spirv/lib_spirv_cross.py +126 -0
  182. pyglet/libs/win32/__init__.py +28 -8
  183. pyglet/libs/win32/constants.py +59 -48
  184. pyglet/libs/win32/context_managers.py +20 -3
  185. pyglet/libs/win32/dinput.py +105 -88
  186. pyglet/{gl → libs/win32}/lib_wgl.py +52 -26
  187. pyglet/libs/win32/types.py +58 -23
  188. pyglet/{gl → libs/win32}/wgl.py +32 -25
  189. pyglet/{gl → libs/win32}/wglext_arb.py +364 -2
  190. pyglet/media/__init__.py +9 -10
  191. pyglet/media/codecs/__init__.py +12 -1
  192. pyglet/media/codecs/base.py +99 -96
  193. pyglet/media/codecs/ffmpeg.py +2 -2
  194. pyglet/media/codecs/ffmpeg_lib/libavformat.py +3 -8
  195. pyglet/media/codecs/webaudio_pyodide.py +111 -0
  196. pyglet/media/drivers/__init__.py +9 -4
  197. pyglet/media/drivers/base.py +4 -4
  198. pyglet/media/drivers/openal/__init__.py +1 -1
  199. pyglet/media/drivers/openal/adaptation.py +3 -3
  200. pyglet/media/drivers/pulse/__init__.py +1 -1
  201. pyglet/media/drivers/pulse/adaptation.py +3 -3
  202. pyglet/media/drivers/pyodide_js/__init__.py +8 -0
  203. pyglet/media/drivers/pyodide_js/adaptation.py +288 -0
  204. pyglet/media/drivers/xaudio2/adaptation.py +3 -3
  205. pyglet/media/player.py +276 -193
  206. pyglet/media/player_worker_thread.py +1 -1
  207. pyglet/model/__init__.py +39 -29
  208. pyglet/model/codecs/base.py +4 -4
  209. pyglet/model/codecs/gltf.py +3 -3
  210. pyglet/model/codecs/obj.py +71 -43
  211. pyglet/resource.py +129 -78
  212. pyglet/shapes.py +154 -194
  213. pyglet/sprite.py +47 -164
  214. pyglet/text/__init__.py +44 -54
  215. pyglet/text/caret.py +12 -7
  216. pyglet/text/document.py +19 -17
  217. pyglet/text/formats/html.py +2 -2
  218. pyglet/text/formats/structured.py +10 -40
  219. pyglet/text/layout/__init__.py +20 -13
  220. pyglet/text/layout/base.py +176 -287
  221. pyglet/text/layout/incremental.py +9 -10
  222. pyglet/text/layout/scrolling.py +7 -95
  223. pyglet/window/__init__.py +183 -172
  224. pyglet/window/cocoa/__init__.py +62 -51
  225. pyglet/window/cocoa/pyglet_delegate.py +2 -25
  226. pyglet/window/cocoa/pyglet_view.py +9 -8
  227. pyglet/window/dialog/__init__.py +184 -0
  228. pyglet/window/dialog/base.py +99 -0
  229. pyglet/window/dialog/darwin.py +121 -0
  230. pyglet/window/dialog/linux.py +72 -0
  231. pyglet/window/dialog/windows.py +194 -0
  232. pyglet/window/emscripten/__init__.py +779 -0
  233. pyglet/window/headless/__init__.py +44 -28
  234. pyglet/window/key.py +2 -0
  235. pyglet/window/mouse.py +2 -2
  236. pyglet/window/wayland/__init__.py +377 -0
  237. pyglet/window/win32/__init__.py +101 -46
  238. pyglet/window/xlib/__init__.py +104 -66
  239. {pyglet-2.1.13.dist-info → pyglet-3.0.dev1.dist-info}/METADATA +2 -3
  240. pyglet-3.0.dev1.dist-info/RECORD +322 -0
  241. {pyglet-2.1.13.dist-info → pyglet-3.0.dev1.dist-info}/WHEEL +1 -1
  242. pyglet/gl/__init__.py +0 -208
  243. pyglet/gl/base.py +0 -499
  244. pyglet/gl/cocoa.py +0 -309
  245. pyglet/gl/gl.py +0 -4625
  246. pyglet/gl/gl.pyi +0 -2320
  247. pyglet/gl/gl_compat.pyi +0 -3097
  248. pyglet/gl/gl_info.py +0 -190
  249. pyglet/gl/headless.py +0 -166
  250. pyglet/gl/wgl_info.py +0 -36
  251. pyglet/gl/wglext_nv.py +0 -1096
  252. pyglet/gl/win32.py +0 -268
  253. pyglet/gl/xlib.py +0 -295
  254. pyglet/image/buffer.py +0 -274
  255. pyglet/image/codecs/s3tc.py +0 -354
  256. pyglet/libs/x11/xrandr.py +0 -166
  257. pyglet-2.1.13.dist-info/RECORD +0 -234
  258. /pyglet/{libs/wayland → graphics/api/gl/cocoa}/__init__.py +0 -0
  259. /pyglet/libs/{egl → linux/egl}/egl.py +0 -0
  260. /pyglet/libs/{egl → linux/egl}/lib.py +0 -0
  261. /pyglet/libs/{ioctl.py → linux/ioctl.py} +0 -0
  262. /pyglet/libs/{wayland → linux/wayland}/gbm.py +0 -0
  263. /pyglet/libs/{x11 → linux/x11}/__init__.py +0 -0
  264. /pyglet/libs/{x11 → linux/x11}/cursorfont.py +0 -0
  265. /pyglet/libs/{x11 → linux/x11}/xlib.py +0 -0
  266. /pyglet/libs/{x11 → linux/x11}/xsync.py +0 -0
  267. {pyglet-2.1.13.dist-info/licenses → pyglet-3.0.dev1.dist-info}/LICENSE +0 -0
@@ -168,6 +168,28 @@ DWRITE_FONT_FACE_TYPE_UNKNOWN = 6
168
168
  DWRITE_FONT_FACE_TYPE_RAW_CFF = 7
169
169
  DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION = 8
170
170
 
171
+ DWRITE_FONT_PROPERTY_ID = UINT
172
+ DWRITE_FONT_PROPERTY_ID_NONE = 0
173
+ DWRITE_FONT_PROPERTY_ID_WEIGHT_STRETCH_STYLE_FAMILY_NAME = 1
174
+ DWRITE_FONT_PROPERTY_ID_TYPOGRAPHIC_FAMILY_NAME = 2
175
+ DWRITE_FONT_PROPERTY_ID_WEIGHT_STRETCH_STYLE_FACE_NAME = 3
176
+ DWRITE_FONT_PROPERTY_ID_FULL_NAME = 4
177
+ DWRITE_FONT_PROPERTY_ID_WIN32_FAMILY_NAME = 5
178
+ DWRITE_FONT_PROPERTY_ID_POSTSCRIPT_NAME = 6
179
+ DWRITE_FONT_PROPERTY_ID_DESIGN_SCRIPT_LANGUAGE_TAG = 7
180
+ DWRITE_FONT_PROPERTY_ID_SUPPORTED_SCRIPT_LANGUAGE_TAG = 8
181
+ DWRITE_FONT_PROPERTY_ID_SEMANTIC_TAG = 9
182
+ DWRITE_FONT_PROPERTY_ID_WEIGHT = 10
183
+ DWRITE_FONT_PROPERTY_ID_STRETCH = 11
184
+ DWRITE_FONT_PROPERTY_ID_STYLE = 12
185
+ DWRITE_FONT_PROPERTY_ID_TYPOGRAPHIC_FACE_NAME = 13
186
+ DWRITE_FONT_PROPERTY_ID_TOTAL = 14
187
+ DWRITE_FONT_PROPERTY_ID_TOTAL_RS3 = 15
188
+ DWRITE_FONT_PROPERTY_ID_PREFERRED_FAMILY_NAME = 16
189
+ DWRITE_FONT_PROPERTY_ID_FAMILY_NAME = 17
190
+ DWRITE_FONT_PROPERTY_ID_FACE_NAME = 18
191
+
192
+
171
193
  def repr_func(self):
172
194
  field_values = []
173
195
  for field_value in self._fields_:
@@ -368,7 +390,7 @@ class IDWriteFontFace(com.pIUnknown):
368
390
  ("GetIndex",
369
391
  com.METHOD(UINT32)),
370
392
  ("GetSimulations",
371
- com.STDMETHOD()),
393
+ com.METHOD(UINT32)),
372
394
  ("IsSymbolFont",
373
395
  com.METHOD(BOOL)),
374
396
  ("GetMetrics",
@@ -682,7 +704,7 @@ class IDWriteFont(com.pIUnknown):
682
704
  ("GetInformationalStrings",
683
705
  com.STDMETHOD(DWRITE_INFORMATIONAL_STRING_ID, POINTER(IDWriteLocalizedStrings), POINTER(BOOL))),
684
706
  ("GetSimulations",
685
- com.STDMETHOD()),
707
+ com.METHOD(UINT32)),
686
708
  ("GetMetrics",
687
709
  com.STDMETHOD()),
688
710
  ("HasCharacter",
@@ -1155,11 +1177,11 @@ class IDWriteFontSet(com.pIUnknown):
1155
1177
  ("FindFontFace",
1156
1178
  com.STDMETHOD()),
1157
1179
  ("GetPropertyValues__",
1158
- com.STDMETHOD()),
1180
+ com.STDMETHOD(UINT32, DWRITE_FONT_PROPERTY_ID, POINTER(BOOL), POINTER(IDWriteLocalizedStrings))),
1159
1181
  ("GetPropertyValues_",
1160
- com.STDMETHOD()),
1182
+ com.STDMETHOD(DWRITE_FONT_PROPERTY_ID, c_wchar_p, c_void_p)),
1161
1183
  ("GetPropertyValues",
1162
- com.STDMETHOD()),
1184
+ com.STDMETHOD(DWRITE_FONT_PROPERTY_ID, c_void_p)),
1163
1185
  ("GetPropertyOccurrenceCount",
1164
1186
  com.STDMETHOD()),
1165
1187
  ("GetMatchingFonts_",
pyglet/font/fontconfig.py CHANGED
@@ -5,6 +5,7 @@ from collections import OrderedDict
5
5
  from ctypes import CDLL, Structure, Union, byref, c_char_p, c_double, c_int, c_uint, c_void_p, POINTER
6
6
  from typing import TYPE_CHECKING
7
7
 
8
+ from pyglet.enums import Style
8
9
  from pyglet.font.base import FontException
9
10
  from pyglet.lib import load_library
10
11
  from pyglet.util import asbytes, asstr
@@ -200,7 +201,9 @@ class FontConfig:
200
201
  return result.weight, result.italic, result.stretch
201
202
 
202
203
  def find_font(self, name: str, size: float = 12, weight: str = "normal",
203
- italic: bool = False, stretch: str = "normal") -> FontConfigSearchResult:
204
+ italic: str = "normal", stretch: str = "normal") -> FontConfigSearchResult:
205
+ assert isinstance(weight, str)
206
+ assert isinstance(italic, str)
204
207
  if result := self._get_from_search_cache(name, size, weight, italic, stretch):
205
208
  return result
206
209
 
@@ -239,7 +242,7 @@ class FontConfig:
239
242
  self._search_cache.popitem(last=False)[1].dispose()
240
243
 
241
244
  def _get_from_search_cache(self, name: str, size: float, weight: str,
242
- italic: bool, stretch: str) -> FontConfigSearchResult | None:
245
+ italic: str, stretch: str) -> FontConfigSearchResult | None:
243
246
  result = self._search_cache.get((name, size, weight, italic, stretch), None)
244
247
 
245
248
  if result and result.is_valid:
@@ -295,8 +298,13 @@ class FontConfigPattern:
295
298
  self._pattern = None
296
299
 
297
300
  @staticmethod
298
- def _italic_to_slant(italic: bool) -> int:
299
- return FC_SLANT_ITALIC if italic else FC_SLANT_ROMAN
301
+ def _italic_to_slant(italic: str) -> int:
302
+ if italic == Style.ITALIC:
303
+ return FC_SLANT_ITALIC
304
+ if italic == Style.OBLIQUE:
305
+ return FC_SLANT_OBLIQUE
306
+
307
+ return FC_SLANT_ROMAN
300
308
 
301
309
  def _set_string(self, name: bytes, value: str) -> None:
302
310
  assert self._pattern
@@ -377,7 +385,7 @@ class FontConfigPattern:
377
385
 
378
386
  class FontConfigSearchPattern(FontConfigPattern):
379
387
  size: float | None
380
- italic: bool
388
+ italic: str
381
389
  weight: str
382
390
  name: str | None
383
391
  stretch: str
@@ -387,7 +395,7 @@ class FontConfigSearchPattern(FontConfigPattern):
387
395
 
388
396
  self.name = None
389
397
  self.weight = "normal"
390
- self.italic = False
398
+ self.italic = "normal"
391
399
  self.size = None
392
400
  self.stretch = 'normal'
393
401
 
pyglet/font/freetype.py CHANGED
@@ -1,8 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import warnings
4
- from ctypes import POINTER, byref, c_ubyte, cast, memmove
5
- from typing import NamedTuple, Sequence
4
+ from ctypes import POINTER, byref, c_ubyte, cast, memmove, string_at, Array
5
+ from typing import NamedTuple, Sequence, TYPE_CHECKING
6
6
 
7
7
  import pyglet
8
8
  from pyglet import image
@@ -26,13 +26,31 @@ from pyglet.font.freetype_lib import (
26
26
  FT_Set_Char_Size,
27
27
  f26p6_to_float,
28
28
  float_to_f26p6,
29
- ft_get_library, FT_LOAD_TARGET_MONO, FT_LOAD_COLOR, FT_FACE_FLAG_SCALABLE, FT_FACE_FLAG_FIXED_SIZES,
30
- FT_FACE_FLAG_COLOR, FT_Select_Size, FT_PIXEL_MODE_BGRA, FT_Get_Char_Index, FT_FACE_FLAG_KERNING, FT_Vector,
31
- FT_Get_Kerning, FT_KERNING_DEFAULT, FT_LOAD_NO_BITMAP, FT_Load_Char
29
+ ft_get_library,
30
+ FT_LOAD_TARGET_MONO,
31
+ FT_LOAD_COLOR,
32
+ FT_FACE_FLAG_SCALABLE,
33
+ FT_FACE_FLAG_FIXED_SIZES,
34
+ FT_FACE_FLAG_COLOR,
35
+ FT_Select_Size,
36
+ FT_PIXEL_MODE_BGRA,
37
+ FT_Get_Char_Index,
38
+ FT_LOAD_NO_BITMAP,
39
+ FT_Load_Char,
40
+ FT_FACE_FLAG_SFNT,
41
+ FT_SfntName,
42
+ FT_Get_Sfnt_Name_Count,
43
+ FT_Get_Sfnt_Name,
44
+ TT_NAME_ID_FULL_NAME,
45
+ TT_NAME_ID_FONT_FAMILY,
32
46
  )
33
47
  from pyglet.font.harfbuzz import harfbuzz_available, get_resource_from_ft_font, get_harfbuzz_shaped_glyphs
34
- from pyglet.util import asbytes, asstr
48
+ from pyglet.util import asbytes, asstr, debug_print
35
49
 
50
+ _debug_font = debug_print("debug_font")
51
+
52
+ if TYPE_CHECKING:
53
+ from pyglet.font import FontManager
36
54
 
37
55
  class FreeTypeGlyphRenderer(base.GlyphRenderer):
38
56
  def __init__(self, font: FreeTypeFont) -> None:
@@ -70,7 +88,12 @@ class FreeTypeGlyphRenderer(base.GlyphRenderer):
70
88
  self._mode = self._glyph_slot.bitmap.pixel_mode
71
89
  self._pitch = self._glyph_slot.bitmap.pitch
72
90
 
73
- self._baseline = self._height - self._glyph_slot.bitmap_top
91
+ # Hack! Adjust baseline position depending backends for now.
92
+ if pyglet.options.backend == "vulkan":
93
+ self._baseline = self._glyph_slot.bitmap_top - self.font.descent - self.font.ascent
94
+ else:
95
+ self._baseline = self._height - self._glyph_slot.bitmap_top
96
+
74
97
  self._lsb = self._glyph_slot.bitmap_left
75
98
  self._advance_x = int(f26p6_to_float(self._glyph_slot.advance.x))
76
99
 
@@ -137,6 +160,34 @@ class FreeTypeGlyphRenderer(base.GlyphRenderer):
137
160
  self._data = data
138
161
  self._pitch = self._width
139
162
 
163
+ def _expand_to_rgba(self, data: bytes, src_format: str, dst_format: str) -> bytes:
164
+ """Expands data type to RGBA with putting values into A.
165
+
166
+ Will re-evaluate on a better system later.
167
+ """
168
+ src_len = len(src_format)
169
+ dst_len = len(dst_format)
170
+
171
+ if src_len >= dst_len:
172
+ return data
173
+
174
+ expanded_data = bytearray(len(data) // src_len * dst_len)
175
+ mapping = {c: i for i, c in enumerate(src_format)}
176
+
177
+ for i in range(len(data) // src_len):
178
+ default_value = data[i * src_len + 0] if src_len > 0 else 0
179
+
180
+ for j, c in enumerate(dst_format):
181
+ if c in mapping:
182
+ expanded_data[i * dst_len + j] = 255
183
+ elif c == 'A':
184
+ # Default alpha to fully opaque
185
+ expanded_data[i * dst_len + j] = default_value
186
+ else:
187
+ expanded_data[i * dst_len + j] = 255
188
+
189
+ return bytes(expanded_data)
190
+
140
191
  def _create_glyph(self) -> base.Glyph:
141
192
  # Textures should be a minimum of 1x1.
142
193
  if self._width == 0 and self._height == 0:
@@ -190,21 +241,22 @@ class FreeTypeFontMetrics(NamedTuple):
190
241
 
191
242
 
192
243
  class MemoryFaceStore:
193
- _dict: dict[tuple[str, str, bool], FreeTypeMemoryFace]
244
+ _dict: dict[tuple[str, str, str, str], FreeTypeMemoryFace]
194
245
 
195
246
  def __init__(self) -> None:
196
247
  self._dict = {}
197
248
 
198
249
  def add(self, face: FreeTypeMemoryFace) -> None:
199
- self._dict[face.name.lower(), face.weight, face.italic] = face
250
+ self._dict[face.name, face.weight, face.style, face.stretch] = face
200
251
 
201
252
  def contains(self, name: str) -> bool:
202
- lname = name and name.lower() or ""
203
- return len([name for name, _, _ in self._dict if name == lname]) > 0
253
+ return len([fn_name for fn_name, _, _, _ in self._dict if name.lower() == fn_name.lower()]) > 0
254
+
255
+ def get(self, name: str, weight: str, style: str, stretch: str) -> FreeTypeMemoryFace | None:
256
+ return self._dict.get((name, weight, style, stretch), None)
204
257
 
205
- def get(self, name: str, weight: str, italic: bool) -> FreeTypeMemoryFace | None:
206
- lname = name and name.lower() or ""
207
- return self._dict.get((lname, weight, italic), None)
258
+ def all_keys(self) -> list[tuple[str, str, str, str]]:
259
+ return list(self._dict.keys())
208
260
 
209
261
 
210
262
  class FreeTypeFont(base.Font):
@@ -214,18 +266,12 @@ class FreeTypeFont(base.Font):
214
266
  # Map font (name, weight, italic) to FreeTypeMemoryFace
215
267
  _memory_faces = MemoryFaceStore()
216
268
  face: FreeTypeFace
217
- fallbacks: list[FreeTypeFont]
218
-
219
- def __init__(self, name: str, size: float, weight: str = "normal", italic: bool = False,
220
- stretch: bool | str = False, dpi: int | None = None) -> None:
221
- super().__init__()
222
- self._name = name
223
- self.size = size
224
- self.weight = weight
225
- self.italic = italic
226
- self.stretch = stretch
227
- self.dpi = dpi or 96
228
- self.pixel_size = (self.size * self.dpi) // 72
269
+
270
+ def __init__(self, name: str, size: float,
271
+ weight: str = "normal",
272
+ style: str = "normal",
273
+ stretch: str = "normal", dpi: int | None = None) -> None:
274
+ super().__init__(name, size, weight, style, stretch, dpi)
229
275
 
230
276
  self._load_font_face()
231
277
  self.metrics = self.face.get_font_metrics(self.size, self.dpi)
@@ -245,42 +291,22 @@ class FreeTypeFont(base.Font):
245
291
  def descent(self) -> int:
246
292
  return self.metrics.descent
247
293
 
248
- def add_fallback(self, font):
249
- self.fallbacks.append(font)
250
-
251
- def _get_slot_from_fallbacks(self, character: str) -> FT_GlyphSlot | None:
252
- """Checks all fallback fonts in order to find a valid glyph index."""
253
- # Check if fallback has this glyph, if so.
254
- for fallback_font in self.fallbacks:
255
- fb_index = fallback_font.face.get_character_index(character)
256
- if fb_index:
257
- fallback_font.face.set_char_size(self.size, self.dpi)
258
- return fallback_font.get_glyph_slot(character)
259
-
260
- return None
261
-
262
294
  def get_glyph_slot_index(self, glyph_index: int) -> FT_GlyphSlot:
263
295
  self.face.set_char_size(self.size, self.dpi)
264
296
  return self.face.get_glyph_slot(glyph_index)
265
297
 
266
298
  def get_glyph_slot(self, character: str) -> FT_GlyphSlot:
267
299
  glyph_index = self.face.get_character_index(character)
268
- # Glyph index does not exist, so check fallback fonts.
269
- if glyph_index == 0 and (self.name not in self.fallbacks):
270
- glyph_slot = self._get_slot_from_fallbacks(character)
271
- if glyph_slot is not None:
272
- return glyph_slot
273
-
274
300
  self.face.set_char_size(self.size, self.dpi)
275
301
  return self.face.get_glyph_slot(glyph_index)
276
302
 
277
303
  def _load_font_face(self) -> None:
278
- self.face = self._memory_faces.get(self._name, self.weight, self.italic)
304
+ self.face = self._memory_faces.get(self._name, self.weight, self.style, self.stretch)
279
305
  if self.face is None:
280
306
  self._load_font_face_from_system()
281
307
 
282
308
  def _load_font_face_from_system(self) -> None:
283
- match = get_fontconfig().find_font(self._name, self.size, self.weight, self.italic, self.stretch)
309
+ match = get_fontconfig().find_font(self._name, self.size, self.weight, self.style, self.stretch)
284
310
  if not match:
285
311
  msg = f"Could not match font '{self._name}'"
286
312
  raise base.FontException(msg)
@@ -295,7 +321,7 @@ class FreeTypeFont(base.Font):
295
321
  return get_fontconfig().have_font(name)
296
322
 
297
323
  @classmethod
298
- def add_font_data(cls: type[FreeTypeFont], data: bytes) -> None:
324
+ def add_font_data(cls: type[FreeTypeFont], data: bytes, manager: FontManager) -> None:
299
325
  font_data = (FT_Byte * len(data))()
300
326
  memmove(font_data, data, len(data))
301
327
 
@@ -308,7 +334,10 @@ class FreeTypeFont(base.Font):
308
334
  face = FreeTypeMemoryFace(font_data, i)
309
335
  cls._memory_faces.add(face)
310
336
 
311
- def render_glyph_indices(self, indices: Sequence[int]):
337
+ all_fonts = cls._memory_faces.all_keys()
338
+ manager._add_loaded_font(set(all_fonts)) # noqa: SLF001
339
+
340
+ def render_glyph_indices(self, indices: Sequence[int]) -> None:
312
341
  # Process any glyphs that have not been rendered.
313
342
  self._initialize_renderer()
314
343
 
@@ -321,7 +350,7 @@ class FreeTypeFont(base.Font):
321
350
  for glyph_indice in missing:
322
351
  self.glyphs[glyph_indice] = self._glyph_renderer.render_index(glyph_indice)
323
352
 
324
- def get_glyphs(self, text: str) -> tuple[list[base.Glyph], list[base.GlyphPosition]]:
353
+ def get_glyphs(self, text: str, shaping: bool) -> tuple[list[base.Glyph], list[base.GlyphPosition]]:
325
354
  """Create and return a list of Glyphs for `text`.
326
355
 
327
356
  If any characters do not have a known glyph representation in this
@@ -330,22 +359,24 @@ class FreeTypeFont(base.Font):
330
359
  Args:
331
360
  text:
332
361
  Text to render.
362
+ shaping:
363
+ If the text will be shaped using the global option.
333
364
  """
334
365
  self._initialize_renderer()
335
- if pyglet.options.text_shaping == "harfbuzz" and harfbuzz_available():
366
+
367
+ if shaping and pyglet.options.text_shaping == "harfbuzz" and harfbuzz_available():
336
368
  return get_harfbuzz_shaped_glyphs(self, text)
337
- else:
338
- glyphs = [] # glyphs that are committed.
339
- for idx, c in enumerate(text):
340
- # Get the glyph for 'c'. Hide tabs (Windows and Linux render
341
- # boxes)
342
- if c == "\t":
343
- c = " " # noqa: PLW2901
344
- if c not in self.glyphs:
345
- self.glyphs[c] = self._glyph_renderer.render(c)
346
- glyphs.append(self.glyphs[c])
347
-
348
- return glyphs, [GlyphPosition(0, 0, 0, 0)] * len(text)
369
+
370
+ glyphs = [] # glyphs that are committed.
371
+ for idx, c in enumerate(text):
372
+ # Get the glyph for 'c'. Hide tabs (Windows and Linux render boxes)
373
+ if c == "\t":
374
+ c = " " # noqa: PLW2901
375
+ if c not in self.glyphs:
376
+ self.glyphs[c] = self._glyph_renderer.render(c)
377
+ glyphs.append(self.glyphs[c])
378
+
379
+ return glyphs, [GlyphPosition(0, 0, 0, 0)] * len(text)
349
380
 
350
381
  def get_text_size(self, text: str) -> tuple[int, int]:
351
382
  width = 0
@@ -387,9 +418,11 @@ class FreeTypeFace:
387
418
  self._get_best_name()
388
419
 
389
420
  self._italic = self.style_flags & FT_STYLE_FLAG_ITALIC != 0
421
+ self._style = "italic" if self._italic else "normal"
390
422
  bold = self.style_flags & FT_STYLE_FLAG_BOLD != 0
391
423
  if bold:
392
424
  self._weight = "bold"
425
+ self._stretch = "normal"
393
426
  else:
394
427
  # Sometimes it may have a weight, but FT_STYLE_FLAG_BOLD is not accurate. Check the font config.
395
428
  config = get_fontconfig()
@@ -413,11 +446,11 @@ class FreeTypeFace:
413
446
  if match.face is not None:
414
447
  FT_Reference_Face(match.face)
415
448
  return cls(match.face)
416
- else:
417
- if not match.file:
418
- msg = f'No filename for "{match.name}"'
419
- raise base.FontException(msg)
420
- return cls.from_file(match.file)
449
+
450
+ if not match.file:
451
+ msg = f'No filename for "{match.name}"'
452
+ raise base.FontException(msg)
453
+ return cls.from_file(match.file)
421
454
 
422
455
  @property
423
456
  def name(self) -> str:
@@ -447,6 +480,14 @@ class FreeTypeFace:
447
480
  def italic(self) -> bool:
448
481
  return self._italic
449
482
 
483
+ @property
484
+ def stretch(self) -> str:
485
+ return self._stretch
486
+
487
+ @property
488
+ def style(self) -> str:
489
+ return self._style
490
+
450
491
  @property
451
492
  def face_flags(self) -> int:
452
493
  return self.ft_face.contents.face_flags
@@ -514,36 +555,46 @@ class FreeTypeFace:
514
555
  i = self.get_character_index("X")
515
556
  self.get_glyph_slot(i)
516
557
  ascent = self.ft_face.contents.available_sizes.contents.height
517
- return FreeTypeFontMetrics(ascent=ascent,
518
- descent=-ascent // 4) # arbitrary.
558
+ return FreeTypeFontMetrics(ascent=ascent, descent=-ascent // 4) # arbitrary.
519
559
 
520
560
  def _get_best_name(self) -> None:
521
561
  self._name = asstr(self.ft_face.contents.family_name)
522
- self._get_font_family_from_ttf()
523
562
 
524
- def _get_font_family_from_ttf(self) -> None:
525
563
  # Replace Freetype's generic family name with TTF/OpenType specific
526
564
  # name if we can find one; there are some instances where Freetype
527
565
  # gets it wrong.
566
+ if (ttf_name := self._get_ttf_string(TT_NAME_ID_FONT_FAMILY)) and ttf_name != self._name:
567
+ assert _debug_font(f"FreeType name: {self._name} does not match header name: {ttf_name}")
528
568
 
529
- return # FIXME: This is broken
530
-
569
+ def _get_ttf_string(self, name_id: int) -> str | None:
570
+ """Return the value from the TTF/OTF/AAT font table for the specified TT_NAME_ID."""
531
571
  if self.face_flags & FT_FACE_FLAG_SFNT:
532
- name = FT_SfntName()
533
- for i in range(FT_Get_Sfnt_Name_Count(self.ft_face)):
534
- try:
535
- FT_Get_Sfnt_Name(self.ft_face, i, name)
536
- if not (name.platform_id == TT_PLATFORM_MICROSOFT and
537
- name.encoding_id == TT_MS_ID_UNICODE_CS):
538
- continue
539
- # name.string is not 0 terminated! use name.string_len
540
- self._name = name.string.decode("utf-16be", "ignore")
541
- except:
542
- continue
572
+ name_count = FT_Get_Sfnt_Name_Count(self.ft_face)
573
+ for i in range(name_count):
574
+ name = FT_SfntName()
575
+ FT_Get_Sfnt_Name(self.ft_face, i, name)
576
+ if name.platform_id == name_id:
577
+ raw = string_at(name.string, name.string_len)
578
+
579
+ # First try decoding with UTF-16BE (used by Windows name table)
580
+ try:
581
+ full_name = raw.decode("utf-16-be")
582
+ except UnicodeDecodeError:
583
+ full_name = raw.decode("latin1", errors="replace")
584
+
585
+ return full_name
586
+
587
+ return None
588
+
589
+ def _get_full_name(self) -> str | None:
590
+ """Checks the font naming table."""
591
+ return self._get_ttf_string(TT_NAME_ID_FULL_NAME)
592
+
593
+
543
594
 
544
595
 
545
596
  class FreeTypeMemoryFace(FreeTypeFace):
546
- def __init__(self, data: bytes, face_index: int = 0) -> None:
597
+ def __init__(self, data: Array, face_index: int = 0) -> None:
547
598
  self.font_data = data
548
599
  ft_library = ft_get_library()
549
600
  ft_face = FT_Face()
@@ -672,7 +672,9 @@ FT_Get_Sfnt_Name = _get_function_with_error_handling("FT_Get_Sfnt_Name",
672
672
  [FT_Face, FT_UInt, POINTER(FT_SfntName)], FT_Error)
673
673
 
674
674
  TT_PLATFORM_MICROSOFT = 3
675
+
675
676
  TT_MS_ID_UNICODE_CS = 1
677
+
676
678
  TT_NAME_ID_COPYRIGHT = 0
677
679
  TT_NAME_ID_FONT_FAMILY = 1
678
680
  TT_NAME_ID_FONT_SUBFAMILY = 2
@@ -681,6 +683,8 @@ TT_NAME_ID_FULL_NAME = 4
681
683
  TT_NAME_ID_VERSION_STRING = 5
682
684
  TT_NAME_ID_PS_NAME = 6
683
685
  TT_NAME_ID_TRADEMARK = 7
686
+
687
+ # From OpenType:
684
688
  TT_NAME_ID_MANUFACTURER = 8
685
689
  TT_NAME_ID_DESIGNER = 9
686
690
  TT_NAME_ID_DESCRIPTION = 10
@@ -688,11 +692,26 @@ TT_NAME_ID_VENDOR_URL = 11
688
692
  TT_NAME_ID_DESIGNER_URL = 12
689
693
  TT_NAME_ID_LICENSE = 13
690
694
  TT_NAME_ID_LICENSE_URL = 14
695
+ # 15 is reserved.
691
696
  TT_NAME_ID_PREFERRED_FAMILY = 16
692
697
  TT_NAME_ID_PREFERRED_SUBFAMILY = 17
693
698
  TT_NAME_ID_MAC_FULL_NAME = 18
699
+ TT_NAME_ID_SAMPLE_TEXT = 19
700
+
701
+ # OpenType 1.3
694
702
  TT_NAME_ID_CID_FINDFONT_NAME = 20
695
703
 
704
+ # OpenType 1.5
705
+ TT_NAME_ID_WWS_FAMILY = 21
706
+ TT_NAME_ID_WWS_SUBFAMILY = 22
707
+
708
+ # OpenType 1.7
709
+ TT_NAME_ID_LIGHT_BACKGROUND = 23
710
+ TT_NAME_ID_DARK_BACKGROUND = 24
711
+
712
+ # OpenType 1.8
713
+ TT_NAME_ID_VARIATIONS_PREFIX = 25
714
+
696
715
  _library = None
697
716
 
698
717