xbintsc 0.5.45 → 0.5.52

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 (286) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +5 -4
  3. package/README.zh-CN.md +4 -3
  4. package/dist/src/cli/extensions.d.ts +24 -0
  5. package/dist/src/cli/extensions.js +62 -0
  6. package/dist/src/cli/extensions.js.map +1 -0
  7. package/dist/src/cli/help.d.ts +1 -1
  8. package/dist/src/cli/help.js +3 -1
  9. package/dist/src/cli/help.js.map +1 -1
  10. package/dist/src/cli/hints.js +8 -3
  11. package/dist/src/cli/hints.js.map +1 -1
  12. package/dist/src/cli/main.js +21 -28
  13. package/dist/src/cli/main.js.map +1 -1
  14. package/dist/src/diagnostics/diagnostic.d.ts +3 -1
  15. package/dist/src/diagnostics/diagnostic.js +2 -0
  16. package/dist/src/diagnostics/diagnostic.js.map +1 -1
  17. package/dist/src/driver/build-key.d.ts +2 -0
  18. package/dist/src/driver/build-key.js +1 -0
  19. package/dist/src/driver/build-key.js.map +1 -1
  20. package/dist/src/driver/compiler.js +61 -8
  21. package/dist/src/driver/compiler.js.map +1 -1
  22. package/dist/src/driver/config.d.ts +4 -0
  23. package/dist/src/driver/config.js +2 -0
  24. package/dist/src/driver/config.js.map +1 -1
  25. package/dist/src/driver/link-features.d.ts +37 -0
  26. package/dist/src/driver/link-features.js +123 -0
  27. package/dist/src/driver/link-features.js.map +1 -0
  28. package/dist/src/driver/options.d.ts +10 -0
  29. package/dist/src/driver/runtime-objects.d.ts +5 -0
  30. package/dist/src/driver/runtime-objects.js +20 -1
  31. package/dist/src/driver/runtime-objects.js.map +1 -1
  32. package/dist/src/driver/usage.d.ts +23 -0
  33. package/dist/src/driver/usage.js +353 -0
  34. package/dist/src/driver/usage.js.map +1 -0
  35. package/dist/src/driver/version.d.ts +1 -1
  36. package/dist/src/driver/version.js +1 -1
  37. package/dist/src/extensions/catalog.js +1 -2
  38. package/dist/src/extensions/catalog.js.map +1 -1
  39. package/dist/src/extensions/gui/features.d.ts +34 -0
  40. package/dist/src/extensions/gui/features.js +165 -0
  41. package/dist/src/extensions/gui/features.js.map +1 -0
  42. package/dist/src/extensions/gui/index.js +10 -4
  43. package/dist/src/extensions/gui/index.js.map +1 -1
  44. package/dist/src/extensions/link-feature.d.ts +70 -0
  45. package/dist/src/extensions/link-feature.js +18 -0
  46. package/dist/src/extensions/link-feature.js.map +1 -0
  47. package/dist/src/extensions/registry.d.ts +24 -4
  48. package/dist/src/extensions/registry.js +26 -3
  49. package/dist/src/extensions/registry.js.map +1 -1
  50. package/dist/src/index.d.ts +3 -2
  51. package/dist/src/index.js +0 -1
  52. package/dist/src/index.js.map +1 -1
  53. package/dist/tests/cli/hints.test.js +5 -7
  54. package/dist/tests/cli/hints.test.js.map +1 -1
  55. package/dist/tests/driver/link-features.test.js +105 -0
  56. package/dist/tests/driver/link-features.test.js.map +1 -0
  57. package/dist/tests/driver/usage.test.d.ts +1 -0
  58. package/dist/tests/driver/usage.test.js +88 -0
  59. package/dist/tests/driver/usage.test.js.map +1 -0
  60. package/dist/tests/e2e/gui-audio.test.js +4 -4
  61. package/dist/tests/e2e/gui-audio.test.js.map +1 -1
  62. package/dist/tests/e2e/gui-css-coverage.test.d.ts +9 -0
  63. package/dist/tests/e2e/gui-css-coverage.test.js +333 -0
  64. package/dist/tests/e2e/gui-css-coverage.test.js.map +1 -0
  65. package/dist/tests/e2e/gui-css-features.test.d.ts +6 -0
  66. package/dist/tests/e2e/gui-css-features.test.js +270 -0
  67. package/dist/tests/e2e/gui-css-features.test.js.map +1 -0
  68. package/dist/tests/e2e/gui-effects.test.js +92 -11
  69. package/dist/tests/e2e/gui-effects.test.js.map +1 -1
  70. package/dist/tests/e2e/gui-feature-linking.test.d.ts +9 -0
  71. package/dist/tests/e2e/gui-feature-linking.test.js +68 -0
  72. package/dist/tests/e2e/gui-feature-linking.test.js.map +1 -0
  73. package/dist/tests/e2e/gui-font-face.test.d.ts +10 -0
  74. package/dist/tests/e2e/gui-font-face.test.js +142 -0
  75. package/dist/tests/e2e/gui-font-face.test.js.map +1 -0
  76. package/dist/tests/e2e/gui-fonts.test.d.ts +10 -0
  77. package/dist/tests/e2e/gui-fonts.test.js +150 -0
  78. package/dist/tests/e2e/gui-fonts.test.js.map +1 -0
  79. package/dist/tests/e2e/gui-helpers.d.ts +10 -1
  80. package/dist/tests/e2e/gui-helpers.js +6 -0
  81. package/dist/tests/e2e/gui-helpers.js.map +1 -1
  82. package/dist/tests/e2e/gui-layout.test.js +7 -3
  83. package/dist/tests/e2e/gui-layout.test.js.map +1 -1
  84. package/dist/tests/e2e/gui-media-disabled.test.d.ts +5 -4
  85. package/dist/tests/e2e/gui-media-disabled.test.js +7 -6
  86. package/dist/tests/e2e/gui-media-disabled.test.js.map +1 -1
  87. package/dist/tests/e2e/gui-media.test.d.ts +5 -4
  88. package/dist/tests/e2e/gui-media.test.js +9 -9
  89. package/dist/tests/e2e/gui-media.test.js.map +1 -1
  90. package/dist/tests/e2e/gui-paint.test.js +5 -2
  91. package/dist/tests/e2e/gui-paint.test.js.map +1 -1
  92. package/dist/tests/e2e/gui-shaping.test.d.ts +5 -0
  93. package/dist/tests/e2e/gui-shaping.test.js +47 -0
  94. package/dist/tests/e2e/gui-shaping.test.js.map +1 -0
  95. package/dist/tests/e2e/gui-track-sizing.test.d.ts +6 -0
  96. package/dist/tests/e2e/gui-track-sizing.test.js +173 -0
  97. package/dist/tests/e2e/gui-track-sizing.test.js.map +1 -0
  98. package/dist/tests/e2e/gui-transform.test.js +3 -2
  99. package/dist/tests/e2e/gui-transform.test.js.map +1 -1
  100. package/dist/tests/e2e/gui-webgl-pipeline-cache.test.d.ts +18 -0
  101. package/dist/tests/e2e/gui-webgl-pipeline-cache.test.js +120 -0
  102. package/dist/tests/e2e/gui-webgl-pipeline-cache.test.js.map +1 -0
  103. package/dist/tests/extensions/gui-features.test.d.ts +1 -0
  104. package/dist/tests/extensions/gui-features.test.js +175 -0
  105. package/dist/tests/extensions/gui-features.test.js.map +1 -0
  106. package/doc/ai/cli.md +5 -1
  107. package/doc/ai/extensions.md +56 -26
  108. package/doc/ai/troubleshooting.md +10 -7
  109. package/doc/ai/zh-CN/cli.md +5 -1
  110. package/doc/ai/zh-CN/extensions.md +48 -22
  111. package/doc/ai/zh-CN/troubleshooting.md +6 -5
  112. package/doc/gui/animation.md +39 -9
  113. package/doc/gui/api.md +2 -1
  114. package/doc/gui/build.md +137 -19
  115. package/doc/gui/canvas.md +1 -1
  116. package/doc/gui/dom.md +20 -4
  117. package/doc/gui/known-issues.md +63 -26
  118. package/doc/gui/layout.md +31 -8
  119. package/doc/gui/media.md +13 -7
  120. package/doc/gui/rendering.md +114 -39
  121. package/doc/gui/runtime.md +5 -0
  122. package/doc/gui/webgl.md +24 -6
  123. package/doc/gui.md +110 -47
  124. package/doc/implemented/cli.md +2 -0
  125. package/doc/implemented/extensions.md +9 -3
  126. package/doc/npm-package.md +4 -4
  127. package/doc/xbintsc.config.schema.json +10 -0
  128. package/doc/zh-CN/gui/animation.md +33 -10
  129. package/doc/zh-CN/gui/api.md +2 -1
  130. package/doc/zh-CN/gui/build.md +110 -16
  131. package/doc/zh-CN/gui/canvas.md +1 -1
  132. package/doc/zh-CN/gui/dom.md +16 -3
  133. package/doc/zh-CN/gui/known-issues.md +45 -15
  134. package/doc/zh-CN/gui/layout.md +23 -6
  135. package/doc/zh-CN/gui/media.md +11 -6
  136. package/doc/zh-CN/gui/rendering.md +84 -26
  137. package/doc/zh-CN/gui/runtime.md +3 -0
  138. package/doc/zh-CN/gui/webgl.md +3 -3
  139. package/doc/zh-CN/gui.md +92 -41
  140. package/doc/zh-CN/implemented/cli.md +2 -0
  141. package/doc/zh-CN/implemented/extensions.md +9 -3
  142. package/doc/zh-CN/npm-package.md +4 -3
  143. package/package.json +7 -7
  144. package/runtime/ext_gui/animation_values.cpp +425 -0
  145. package/runtime/ext_gui/animation_values.h +64 -0
  146. package/runtime/ext_gui/browser.cpp +22 -15
  147. package/runtime/ext_gui/browser_loop.cpp +13 -1
  148. package/runtime/ext_gui/canvas.h +1 -1
  149. package/runtime/ext_gui/canvas_raster.cpp +1 -1
  150. package/runtime/ext_gui/canvas_text.cpp +1 -1
  151. package/runtime/ext_gui/css.cpp +172 -21
  152. package/runtime/ext_gui/css.h +75 -4
  153. package/runtime/ext_gui/css_match.cpp +4 -0
  154. package/runtime/ext_gui/css_sheet.cpp +218 -2
  155. package/runtime/ext_gui/document.cpp +42 -192
  156. package/runtime/ext_gui/document.h +25 -12
  157. package/runtime/ext_gui/document_animation.cpp +428 -0
  158. package/runtime/ext_gui/dom_api_canvas.cpp +15 -8
  159. package/runtime/ext_gui/dom_api_canvas_extra.cpp +44 -20
  160. package/runtime/ext_gui/dom_api_canvas_image.cpp +3 -1
  161. package/runtime/ext_gui/dom_api_node_read.cpp +10 -0
  162. package/runtime/ext_gui/dom_api_offscreen.cpp +3 -1
  163. package/runtime/ext_gui/dom_api_style.cpp +7 -0
  164. package/runtime/ext_gui/dom_api_webgl_internal.h +4 -0
  165. package/runtime/ext_gui/dom_api_webgl_source.cpp +5 -2
  166. package/runtime/ext_gui/effect_values.cpp +245 -21
  167. package/runtime/ext_gui/gpu_bridge.c +28 -0
  168. package/runtime/ext_gui/gpu_feature.cpp +30 -0
  169. package/runtime/ext_gui/gpu_shader.cpp +22 -0
  170. package/runtime/ext_gui/gpu_shader_api.h +50 -0
  171. package/runtime/ext_gui/gradient_values.cpp +24 -0
  172. package/runtime/ext_gui/grid_values.cpp +23 -0
  173. package/runtime/ext_gui/gui.cpp +6 -37
  174. package/runtime/ext_gui/gui_canvas.cpp +14 -6
  175. package/runtime/ext_gui/gui_cursor.cpp +50 -0
  176. package/runtime/ext_gui/gui_engine.h +1 -18
  177. package/runtime/ext_gui/gui_events.cpp +19 -6
  178. package/runtime/ext_gui/gui_internal.h +3 -0
  179. package/runtime/ext_gui/gui_webgl.cpp +25 -10
  180. package/runtime/ext_gui/image_api.cpp +18 -0
  181. package/runtime/ext_gui/image_api.h +43 -0
  182. package/runtime/ext_gui/image_bitmap_browser.cpp +20 -11
  183. package/runtime/ext_gui/image_bridge.c +22 -0
  184. package/runtime/ext_gui/image_feature.cpp +107 -0
  185. package/runtime/ext_gui/inline_svg.cpp +95 -8
  186. package/runtime/ext_gui/inline_svg.h +5 -1
  187. package/runtime/ext_gui/layout.cpp +15 -9
  188. package/runtime/ext_gui/layout.h +8 -2
  189. package/runtime/ext_gui/layout_flex.cpp +134 -29
  190. package/runtime/ext_gui/layout_flow.cpp +58 -7
  191. package/runtime/ext_gui/layout_flow_internal.h +39 -4
  192. package/runtime/ext_gui/layout_grid.cpp +31 -0
  193. package/runtime/ext_gui/layout_inline.cpp +26 -2
  194. package/runtime/ext_gui/layout_vertical.cpp +196 -0
  195. package/runtime/ext_gui/media.cpp +4 -2
  196. package/runtime/ext_gui/media.h +3 -3
  197. package/runtime/ext_gui/media_api.h +7 -6
  198. package/runtime/ext_gui/paint.cpp +67 -9
  199. package/runtime/ext_gui/paint.h +61 -8
  200. package/runtime/ext_gui/paint_backdrop.cpp +211 -0
  201. package/runtime/ext_gui/paint_background.cpp +175 -97
  202. package/runtime/ext_gui/paint_effects.cpp +12 -6
  203. package/runtime/ext_gui/paint_internal.h +27 -0
  204. package/runtime/ext_gui/paint_outline.cpp +44 -0
  205. package/runtime/ext_gui/paint_shadow.cpp +368 -0
  206. package/runtime/ext_gui/renderer.cpp +15 -7
  207. package/runtime/ext_gui/renderer.h +11 -2
  208. package/runtime/ext_gui/renderer_backdrop.cpp +374 -0
  209. package/runtime/ext_gui/renderer_bitmap.cpp +82 -0
  210. package/runtime/ext_gui/renderer_gradient.cpp +51 -17
  211. package/runtime/ext_gui/renderer_image.cpp +19 -7
  212. package/runtime/ext_gui/renderer_internal.h +21 -0
  213. package/runtime/ext_gui/renderer_layer.cpp +37 -6
  214. package/runtime/ext_gui/renderer_text.cpp +2 -1
  215. package/runtime/ext_gui/stb_truetype.h +5077 -0
  216. package/runtime/ext_gui/style.cpp +62 -8
  217. package/runtime/ext_gui/style.h +111 -4
  218. package/runtime/ext_gui/style_cascade.cpp +83 -0
  219. package/runtime/ext_gui/style_internal.h +13 -1
  220. package/runtime/ext_gui/style_length.cpp +92 -0
  221. package/runtime/ext_gui/style_print.cpp +83 -2
  222. package/runtime/ext_gui/style_svg.cpp +40 -0
  223. package/runtime/ext_gui/style_text.cpp +302 -0
  224. package/runtime/ext_gui/style_ui.cpp +150 -0
  225. package/runtime/ext_gui/style_values.cpp +76 -62
  226. package/runtime/ext_gui/style_values_internal.h +23 -0
  227. package/runtime/ext_gui/text.cpp +27 -499
  228. package/runtime/ext_gui/text.h +18 -9
  229. package/runtime/ext_gui/text_api.h +45 -0
  230. package/runtime/ext_gui/text_bridge.c +23 -0
  231. package/runtime/ext_gui/text_cache.cpp +12 -3
  232. package/runtime/ext_gui/text_font_db.cpp +427 -0
  233. package/runtime/ext_gui/text_font_paths.cpp +250 -0
  234. package/runtime/ext_gui/text_font_paths.h +39 -0
  235. package/runtime/ext_gui/text_font_sfnt.cpp +396 -0
  236. package/runtime/ext_gui/text_font_sfnt.h +44 -0
  237. package/runtime/ext_gui/text_fonts.cpp +348 -0
  238. package/runtime/ext_gui/text_fonts.h +78 -0
  239. package/runtime/ext_gui/text_fonts_internal.h +77 -0
  240. package/runtime/ext_gui/text_glyph_cache.cpp +2 -2
  241. package/runtime/ext_gui/text_hb.cpp +438 -0
  242. package/runtime/ext_gui/text_stb.cpp +369 -0
  243. package/runtime/ext_gui/transform.cpp +107 -9
  244. package/runtime/ext_gui/transform.h +5 -0
  245. package/runtime/ext_gui/webgl_api.cpp +19 -0
  246. package/runtime/ext_gui/webgl_api.h +53 -0
  247. package/runtime/ext_gui/webgl_bridge.c +25 -0
  248. package/runtime/ext_gui/webgl_draw_setup.cpp +42 -1
  249. package/runtime/ext_gui/webgl_feature.cpp +82 -0
  250. package/runtime/ext_gui/webgl_feature.h +28 -0
  251. package/runtime/ext_gui/webgl_msl.cpp +41 -0
  252. package/runtime/ext_gui/webgl_msl.h +6 -0
  253. package/runtime/ext_gui/webgl_shader.cpp +40 -32
  254. package/runtime/ext_gui/window.cpp +7 -7
  255. package/runtime/ext_media/decode_audio.cpp +1 -1
  256. package/runtime/ext_media/media.cpp +1 -1
  257. package/runtime/ext_media/media_bridge.c +7 -7
  258. package/scripts/build-gui.ts +97 -98
  259. package/scripts/check-file-length.ts +1 -0
  260. package/scripts/gui-build-options.ts +122 -0
  261. package/src/cli/extensions.ts +72 -0
  262. package/src/cli/help.ts +3 -1
  263. package/src/cli/hints.ts +9 -3
  264. package/src/cli/main.ts +21 -27
  265. package/src/diagnostics/diagnostic.ts +2 -0
  266. package/src/driver/build-key.ts +3 -0
  267. package/src/driver/compiler.ts +63 -8
  268. package/src/driver/config.ts +6 -0
  269. package/src/driver/link-features.ts +163 -0
  270. package/src/driver/options.ts +10 -0
  271. package/src/driver/runtime-objects.ts +26 -1
  272. package/src/driver/usage.ts +376 -0
  273. package/src/driver/version.ts +1 -1
  274. package/src/extensions/catalog.ts +1 -2
  275. package/src/extensions/gui/features.ts +166 -0
  276. package/src/extensions/gui/index.ts +10 -4
  277. package/src/extensions/link-feature.ts +72 -0
  278. package/src/extensions/registry.ts +45 -4
  279. package/src/index.ts +3 -2
  280. package/dist/src/extensions/media/index.d.ts +0 -28
  281. package/dist/src/extensions/media/index.js +0 -59
  282. package/dist/src/extensions/media/index.js.map +0 -1
  283. package/dist/tests/extensions/media.test.js +0 -63
  284. package/dist/tests/extensions/media.test.js.map +0 -1
  285. package/src/extensions/media/index.ts +0 -65
  286. /package/dist/tests/{extensions/media.test.d.ts → driver/link-features.test.d.ts} +0 -0
@@ -0,0 +1,250 @@
1
+ /*
2
+ * xbintsc GUI engine — platform font locations (see text_font_paths.h).
3
+ */
4
+
5
+ #include "text_font_paths.h"
6
+
7
+ #include <cstdlib>
8
+
9
+ namespace xtgui {
10
+ namespace {
11
+
12
+ const std::vector<std::string> &sansPaths() {
13
+ static const std::vector<std::string> paths = {
14
+ /* macOS: CJK-capable faces first, then the Latin families. */
15
+ "/System/Library/Fonts/PingFang.ttc",
16
+ "/System/Library/Fonts/STHeiti Light.ttc",
17
+ "/System/Library/Fonts/STHeiti Medium.ttc",
18
+ "/System/Library/Fonts/Hiragino Sans GB.ttc",
19
+ "/System/Library/Fonts/Helvetica.ttc",
20
+ "/System/Library/Fonts/HelveticaNeue.ttc",
21
+ "/System/Library/Fonts/Supplemental/Arial Unicode.ttf",
22
+ "/System/Library/Fonts/Supplemental/Arial.ttf",
23
+ "/System/Library/Fonts/Supplemental/Arial Bold.ttf",
24
+ "/System/Library/Fonts/Supplemental/Arial Italic.ttf",
25
+ "/System/Library/Fonts/Supplemental/Arial Bold Italic.ttf",
26
+ "/System/Library/Fonts/Supplemental/Verdana.ttf",
27
+ "/System/Library/Fonts/Supplemental/Verdana Bold.ttf",
28
+ "/System/Library/Fonts/Supplemental/Verdana Italic.ttf",
29
+ "/System/Library/Fonts/Supplemental/Verdana Bold Italic.ttf",
30
+ "/System/Library/Fonts/SFNS.ttf",
31
+ "/System/Library/Fonts/SFNSItalic.ttf",
32
+ "/Library/Fonts/Arial.ttf",
33
+ /* Windows. */
34
+ "C:\\Windows\\Fonts\\msyh.ttc",
35
+ "C:\\Windows\\Fonts\\msyhbd.ttc",
36
+ "C:\\Windows\\Fonts\\msyhl.ttc",
37
+ "C:\\Windows\\Fonts\\msyh.ttf",
38
+ "C:\\Windows\\Fonts\\Deng.ttf",
39
+ "C:\\Windows\\Fonts\\Dengb.ttf",
40
+ "C:\\Windows\\Fonts\\simsun.ttc",
41
+ "C:\\Windows\\Fonts\\segoeui.ttf",
42
+ "C:\\Windows\\Fonts\\segoeuib.ttf",
43
+ "C:\\Windows\\Fonts\\segoeuii.ttf",
44
+ "C:\\Windows\\Fonts\\segoeuiz.ttf",
45
+ "C:\\Windows\\Fonts\\tahoma.ttf",
46
+ "C:\\Windows\\Fonts\\tahomabd.ttf",
47
+ "C:\\Windows\\Fonts\\arial.ttf",
48
+ "C:\\Windows\\Fonts\\arialbd.ttf",
49
+ "C:\\Windows\\Fonts\\ariali.ttf",
50
+ "C:\\Windows\\Fonts\\arialbi.ttf",
51
+ /* Linux. */
52
+ "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
53
+ "/usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc",
54
+ "/usr/share/fonts/opentype/noto/NotoSansCJK-Light.ttc",
55
+ "/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc",
56
+ "/usr/share/fonts/truetype/noto/NotoSansCJK-Bold.ttc",
57
+ "/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Regular.ttc",
58
+ "/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Bold.ttc",
59
+ "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc",
60
+ "/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc",
61
+ "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
62
+ "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
63
+ "/usr/share/fonts/truetype/dejavu/DejaVuSans-Oblique.ttf",
64
+ "/usr/share/fonts/truetype/dejavu/DejaVuSans-BoldOblique.ttf",
65
+ "/usr/share/fonts/TTF/DejaVuSans.ttf",
66
+ "/usr/share/fonts/dejavu/DejaVuSans.ttf",
67
+ "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf",
68
+ "/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf",
69
+ "/usr/share/fonts/truetype/liberation/LiberationSans-Italic.ttf",
70
+ "/usr/share/fonts/truetype/liberation/LiberationSans-BoldItalic.ttf",
71
+ "/usr/share/fonts/truetype/liberation2/LiberationSans-Regular.ttf",
72
+ "/usr/share/fonts/truetype/liberation2/LiberationSans-Bold.ttf",
73
+ };
74
+ return paths;
75
+ }
76
+
77
+ const std::vector<std::string> &serifPaths() {
78
+ static const std::vector<std::string> paths = {
79
+ "/System/Library/Fonts/Supplemental/Times New Roman.ttf",
80
+ "/System/Library/Fonts/Supplemental/Times New Roman Bold.ttf",
81
+ "/System/Library/Fonts/Supplemental/Times New Roman Italic.ttf",
82
+ "/System/Library/Fonts/Supplemental/Times New Roman Bold Italic.ttf",
83
+ "/System/Library/Fonts/Supplemental/Georgia.ttf",
84
+ "/System/Library/Fonts/Supplemental/Georgia Bold.ttf",
85
+ "/System/Library/Fonts/Supplemental/Georgia Italic.ttf",
86
+ "/System/Library/Fonts/Supplemental/Georgia Bold Italic.ttf",
87
+ "/System/Library/Fonts/Palatino.ttc",
88
+ "/System/Library/Fonts/NewYork.ttf",
89
+ "/System/Library/Fonts/NewYorkItalic.ttf",
90
+ "/System/Library/Fonts/Songti.ttc",
91
+ "C:\\Windows\\Fonts\\times.ttf",
92
+ "C:\\Windows\\Fonts\\timesbd.ttf",
93
+ "C:\\Windows\\Fonts\\timesi.ttf",
94
+ "C:\\Windows\\Fonts\\timesbi.ttf",
95
+ "C:\\Windows\\Fonts\\georgia.ttf",
96
+ "C:\\Windows\\Fonts\\georgiab.ttf",
97
+ "C:\\Windows\\Fonts\\georgiai.ttf",
98
+ "C:\\Windows\\Fonts\\georgiaz.ttf",
99
+ "C:\\Windows\\Fonts\\constan.ttf",
100
+ "C:\\Windows\\Fonts\\constanb.ttf",
101
+ "C:\\Windows\\Fonts\\simsun.ttc",
102
+ "/usr/share/fonts/truetype/dejavu/DejaVuSerif.ttf",
103
+ "/usr/share/fonts/truetype/dejavu/DejaVuSerif-Bold.ttf",
104
+ "/usr/share/fonts/truetype/dejavu/DejaVuSerif-Italic.ttf",
105
+ "/usr/share/fonts/truetype/dejavu/DejaVuSerif-BoldItalic.ttf",
106
+ "/usr/share/fonts/TTF/DejaVuSerif.ttf",
107
+ "/usr/share/fonts/dejavu/DejaVuSerif.ttf",
108
+ "/usr/share/fonts/truetype/liberation/LiberationSerif-Regular.ttf",
109
+ "/usr/share/fonts/truetype/liberation/LiberationSerif-Bold.ttf",
110
+ "/usr/share/fonts/truetype/liberation/LiberationSerif-Italic.ttf",
111
+ "/usr/share/fonts/truetype/liberation/LiberationSerif-BoldItalic.ttf",
112
+ "/usr/share/fonts/opentype/noto/NotoSerifCJK-Regular.ttc",
113
+ "/usr/share/fonts/opentype/noto/NotoSerifCJK-Bold.ttc",
114
+ "/usr/share/fonts/truetype/noto/NotoSerifCJK-Regular.ttc",
115
+ "/usr/share/fonts/truetype/noto/NotoSerifCJK-Bold.ttc",
116
+ };
117
+ return paths;
118
+ }
119
+
120
+ const std::vector<std::string> &monoPaths() {
121
+ static const std::vector<std::string> paths = {
122
+ "/System/Library/Fonts/Menlo.ttc",
123
+ "/System/Library/Fonts/Courier.ttc",
124
+ "/System/Library/Fonts/Supplemental/Courier New.ttf",
125
+ "/System/Library/Fonts/Supplemental/Courier New Bold.ttf",
126
+ "/System/Library/Fonts/Supplemental/Courier New Italic.ttf",
127
+ "/System/Library/Fonts/Supplemental/Courier New Bold Italic.ttf",
128
+ "/System/Library/Fonts/SFNSMono.ttf",
129
+ "/Library/Fonts/Courier New.ttf",
130
+ "C:\\Windows\\Fonts\\consola.ttf",
131
+ "C:\\Windows\\Fonts\\consolab.ttf",
132
+ "C:\\Windows\\Fonts\\consolai.ttf",
133
+ "C:\\Windows\\Fonts\\consolaz.ttf",
134
+ "C:\\Windows\\Fonts\\cour.ttf",
135
+ "C:\\Windows\\Fonts\\courbd.ttf",
136
+ "C:\\Windows\\Fonts\\couri.ttf",
137
+ "C:\\Windows\\Fonts\\courbi.ttf",
138
+ "C:\\Windows\\Fonts\\lucon.ttf",
139
+ "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf",
140
+ "/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Bold.ttf",
141
+ "/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Oblique.ttf",
142
+ "/usr/share/fonts/truetype/dejavu/DejaVuSansMono-BoldOblique.ttf",
143
+ "/usr/share/fonts/TTF/DejaVuSansMono.ttf",
144
+ "/usr/share/fonts/dejavu/DejaVuSansMono.ttf",
145
+ "/usr/share/fonts/truetype/liberation/LiberationMono-Regular.ttf",
146
+ "/usr/share/fonts/truetype/liberation/LiberationMono-Bold.ttf",
147
+ "/usr/share/fonts/truetype/liberation/LiberationMono-Italic.ttf",
148
+ "/usr/share/fonts/truetype/liberation/LiberationMono-BoldItalic.ttf",
149
+ "/usr/share/fonts/truetype/liberation2/LiberationMono-Regular.ttf",
150
+ "/usr/share/fonts/truetype/noto/NotoSansMono-Regular.ttf",
151
+ "/usr/share/fonts/truetype/noto/NotoSansMono-Bold.ttf",
152
+ };
153
+ return paths;
154
+ }
155
+
156
+ const std::vector<std::string> &cursivePaths() {
157
+ static const std::vector<std::string> paths = {
158
+ "/System/Library/Fonts/Supplemental/Apple Chancery.ttf",
159
+ "/System/Library/Fonts/Supplemental/SnellRoundhand.ttc",
160
+ "C:\\Windows\\Fonts\\segoesc.ttf",
161
+ "C:\\Windows\\Fonts\\comic.ttf",
162
+ "C:\\Windows\\Fonts\\comicbd.ttf",
163
+ "/usr/share/fonts/truetype/urw-base35/Z003-MediumItalic.ttf",
164
+ "/usr/share/fonts/type1/urw-base35/Z003-MediumItalic.t1",
165
+ };
166
+ return paths;
167
+ }
168
+
169
+ const std::vector<std::string> &fantasyPaths() {
170
+ static const std::vector<std::string> paths = {
171
+ "/System/Library/Fonts/Supplemental/Papyrus.ttc",
172
+ "/System/Library/Fonts/Supplemental/Chalkduster.ttf",
173
+ "/System/Library/Fonts/Supplemental/Impact.ttf",
174
+ "C:\\Windows\\Fonts\\papyrus.ttf",
175
+ "C:\\Windows\\Fonts\\impact.ttf",
176
+ "C:\\Windows\\Fonts\\comic.ttf",
177
+ "/usr/share/fonts/truetype/urw-base35/Z003-MediumItalic.ttf",
178
+ };
179
+ return paths;
180
+ }
181
+
182
+ } // namespace
183
+
184
+ const std::vector<std::string> &xt_font_default_paths(FontGeneric kind) {
185
+ switch (kind) {
186
+ case FontGeneric::Serif:
187
+ return serifPaths();
188
+ case FontGeneric::Monospace:
189
+ return monoPaths();
190
+ case FontGeneric::Cursive:
191
+ return cursivePaths();
192
+ case FontGeneric::Fantasy:
193
+ return fantasyPaths();
194
+ case FontGeneric::Sans:
195
+ break;
196
+ }
197
+ return sansPaths();
198
+ }
199
+
200
+ const std::vector<std::string> &xt_font_symbol_paths() {
201
+ static const std::vector<std::string> paths = {
202
+ "C:\\Windows\\Fonts\\seguisym.ttf",
203
+ "C:\\Windows\\Fonts\\arialuni.ttf",
204
+ "/System/Library/Fonts/Apple Symbols.ttf",
205
+ "/System/Library/Fonts/CJKSymbolsFallback.ttc",
206
+ "/System/Library/Fonts/Supplemental/Arial Unicode.ttf",
207
+ "/usr/share/fonts/truetype/noto/NotoSansSymbols2-Regular.ttf",
208
+ "/usr/share/fonts/truetype/noto/NotoSansSymbols-Regular.ttf",
209
+ "/usr/share/fonts/truetype/ancient-scripts/Symbola_hint.ttf",
210
+ "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
211
+ };
212
+ return paths;
213
+ }
214
+
215
+ std::vector<std::string> xt_font_search_directories() {
216
+ std::vector<std::string> dirs;
217
+ auto add = [&dirs](const std::string &path) {
218
+ if (path.empty()) return;
219
+ for (const std::string &entry : dirs) {
220
+ if (entry == path) return;
221
+ }
222
+ dirs.push_back(path);
223
+ };
224
+ const char *home = std::getenv("HOME");
225
+ if (home == nullptr || home[0] == '\0') home = std::getenv("USERPROFILE");
226
+
227
+ if (std::getenv("WINDIR") != nullptr || std::getenv("SystemRoot") != nullptr) {
228
+ const char *windir = std::getenv("WINDIR");
229
+ if (windir == nullptr || windir[0] == '\0') windir = std::getenv("SystemRoot");
230
+ if (windir != nullptr && windir[0] != '\0') add(std::string(windir) + "\\Fonts");
231
+ const char *local = std::getenv("LOCALAPPDATA");
232
+ if (local != nullptr && local[0] != '\0') add(std::string(local) + "\\Microsoft\\Windows\\Fonts");
233
+ } else {
234
+ add("/System/Library/Fonts");
235
+ add("/System/Library/Fonts/Supplemental");
236
+ add("/Library/Fonts");
237
+ if (home != nullptr && home[0] != '\0') add(std::string(home) + "/Library/Fonts");
238
+ add("/usr/share/fonts");
239
+ add("/usr/local/share/fonts");
240
+ if (home != nullptr && home[0] != '\0') {
241
+ add(std::string(home) + "/.fonts");
242
+ add(std::string(home) + "/.local/share/fonts");
243
+ }
244
+ const char *xdg = std::getenv("XDG_DATA_HOME");
245
+ if (xdg != nullptr && xdg[0] != '\0') add(std::string(xdg) + "/fonts");
246
+ }
247
+ return dirs;
248
+ }
249
+
250
+ } // namespace xtgui
@@ -0,0 +1,39 @@
1
+ /*
2
+ * xbintsc GUI engine — platform font locations and default families.
3
+ *
4
+ * The candidate lists are shared by the font database and its fallback chain so
5
+ * the default sans/serif/monospace families are chosen in one place. Paths for
6
+ * every platform are listed; only the ones that exist on the host are used, so
7
+ * the same binary code covers macOS, Windows and Linux without per-platform
8
+ * font-name tables in the text backends.
9
+ */
10
+ #ifndef XT_GUI_TEXT_FONT_PATHS_H
11
+ #define XT_GUI_TEXT_FONT_PATHS_H
12
+
13
+ #include <string>
14
+ #include <vector>
15
+
16
+ namespace xtgui {
17
+
18
+ enum class FontGeneric {
19
+ Sans,
20
+ Serif,
21
+ Monospace,
22
+ Cursive,
23
+ Fantasy,
24
+ };
25
+
26
+ /** Candidate files for a generic CSS family, in preference order. A family is
27
+ * taken from the first file that exists (and can be parsed); the rest of that
28
+ * family's weight/style faces are then found across these files. */
29
+ const std::vector<std::string> &xt_font_default_paths(FontGeneric kind);
30
+
31
+ /** Faces used for codepoints the requested family does not cover. */
32
+ const std::vector<std::string> &xt_font_symbol_paths();
33
+
34
+ /** Directories scanned for arbitrary `font-family` names, recursively. */
35
+ std::vector<std::string> xt_font_search_directories();
36
+
37
+ } // namespace xtgui
38
+
39
+ #endif /* XT_GUI_TEXT_FONT_PATHS_H */
@@ -0,0 +1,396 @@
1
+ /*
2
+ * xbintsc GUI engine — sfnt metadata reader (see text_font_sfnt.h).
3
+ */
4
+
5
+ #include "text_font_sfnt.h"
6
+
7
+ #include <algorithm>
8
+ #include <cstdint>
9
+ #include <cstdio>
10
+ #include <string>
11
+ #include <vector>
12
+
13
+ namespace xtgui {
14
+ namespace {
15
+
16
+ constexpr uint32_t tagOf(char a, char b, char c, char d) {
17
+ return ((uint32_t)(unsigned char)a << 24) | ((uint32_t)(unsigned char)b << 16) |
18
+ ((uint32_t)(unsigned char)c << 8) | (uint32_t)(unsigned char)d;
19
+ }
20
+
21
+ constexpr uint32_t kTagTtcf = tagOf('t', 't', 'c', 'f');
22
+ constexpr uint32_t kTagOtto = tagOf('O', 'T', 'T', 'O');
23
+ constexpr uint32_t kTagName = tagOf('n', 'a', 'm', 'e');
24
+ constexpr uint32_t kTagOs2 = tagOf('O', 'S', '/', '2');
25
+ constexpr uint32_t kTagHead = tagOf('h', 'e', 'a', 'd');
26
+ constexpr uint32_t kTagPost = tagOf('p', 'o', 's', 't');
27
+
28
+ class FileReader {
29
+ public:
30
+ FileReader() = default;
31
+ ~FileReader() {
32
+ if (file_ != nullptr) std::fclose(file_);
33
+ }
34
+ FileReader(const FileReader &) = delete;
35
+ FileReader &operator=(const FileReader &) = delete;
36
+
37
+ bool open(const std::string &path) {
38
+ file_ = std::fopen(path.c_str(), "rb");
39
+ if (file_ == nullptr) return false;
40
+ if (std::fseek(file_, 0, SEEK_END) != 0) return false;
41
+ const long length = std::ftell(file_);
42
+ if (length <= 0) return false;
43
+ size_ = (uint64_t)length;
44
+ return true;
45
+ }
46
+
47
+ uint64_t size() const { return size_; }
48
+
49
+ bool read(uint64_t offset, void *destination, size_t length) const {
50
+ if (offset > size_ || length > size_ - offset) return false;
51
+ if (std::fseek(file_, (long)offset, SEEK_SET) != 0) return false;
52
+ return std::fread(destination, 1, length, file_) == length;
53
+ }
54
+
55
+ bool u16(uint64_t offset, uint16_t *out) const {
56
+ unsigned char bytes[2];
57
+ if (!read(offset, bytes, sizeof(bytes))) return false;
58
+ *out = (uint16_t)((uint16_t)bytes[0] << 8 | bytes[1]);
59
+ return true;
60
+ }
61
+
62
+ bool u32(uint64_t offset, uint32_t *out) const {
63
+ unsigned char bytes[4];
64
+ if (!read(offset, bytes, sizeof(bytes))) return false;
65
+ *out = ((uint32_t)bytes[0] << 24) | ((uint32_t)bytes[1] << 16) | ((uint32_t)bytes[2] << 8) |
66
+ (uint32_t)bytes[3];
67
+ return true;
68
+ }
69
+
70
+ private:
71
+ FILE *file_ = nullptr;
72
+ uint64_t size_ = 0;
73
+ };
74
+
75
+ struct TableRecord {
76
+ uint32_t tag = 0;
77
+ uint32_t offset = 0;
78
+ uint32_t length = 0;
79
+ };
80
+
81
+ bool readTableRecords(const FileReader &reader, uint64_t face_offset, uint32_t *sfnt_tag,
82
+ std::vector<TableRecord> *tables) {
83
+ if (!reader.u32(face_offset, sfnt_tag)) return false;
84
+ uint16_t count = 0;
85
+ if (!reader.u16(face_offset + 4, &count)) return false;
86
+ if (count == 0 || count > 4096) return false;
87
+ const uint64_t directory_size = (uint64_t)count * 16;
88
+ if (face_offset > reader.size() || directory_size > reader.size() - face_offset ||
89
+ 12 + directory_size > reader.size() - face_offset) {
90
+ return false;
91
+ }
92
+ std::vector<unsigned char> bytes((size_t)directory_size);
93
+ if (!reader.read(face_offset + 12, bytes.data(), bytes.size())) return false;
94
+ tables->clear();
95
+ tables->reserve(count);
96
+ for (uint16_t i = 0; i < count; i++) {
97
+ const unsigned char *entry = bytes.data() + (size_t)i * 16;
98
+ TableRecord record;
99
+ record.tag = ((uint32_t)entry[0] << 24) | ((uint32_t)entry[1] << 16) |
100
+ ((uint32_t)entry[2] << 8) | (uint32_t)entry[3];
101
+ record.offset = ((uint32_t)entry[8] << 24) | ((uint32_t)entry[9] << 16) |
102
+ ((uint32_t)entry[10] << 8) | (uint32_t)entry[11];
103
+ record.length = ((uint32_t)entry[12] << 24) | ((uint32_t)entry[13] << 16) |
104
+ ((uint32_t)entry[14] << 8) | (uint32_t)entry[15];
105
+ if (record.offset > reader.size() || record.length > reader.size() - record.offset) {
106
+ continue;
107
+ }
108
+ tables->push_back(record);
109
+ }
110
+ return true;
111
+ }
112
+
113
+ const TableRecord *findTable(const std::vector<TableRecord> &tables, uint32_t tag) {
114
+ for (const TableRecord &record : tables) {
115
+ if (record.tag == tag && record.length > 0) return &record;
116
+ }
117
+ return nullptr;
118
+ }
119
+
120
+ void appendUtf8(std::string *out, uint32_t code_point) {
121
+ if (code_point <= 0x7f) {
122
+ out->push_back((char)code_point);
123
+ } else if (code_point <= 0x7ff) {
124
+ out->push_back((char)(0xc0 | (code_point >> 6)));
125
+ out->push_back((char)(0x80 | (code_point & 0x3f)));
126
+ } else if (code_point <= 0xffff) {
127
+ out->push_back((char)(0xe0 | (code_point >> 12)));
128
+ out->push_back((char)(0x80 | ((code_point >> 6) & 0x3f)));
129
+ out->push_back((char)(0x80 | (code_point & 0x3f)));
130
+ } else {
131
+ out->push_back((char)(0xf0 | (code_point >> 18)));
132
+ out->push_back((char)(0x80 | ((code_point >> 12) & 0x3f)));
133
+ out->push_back((char)(0x80 | ((code_point >> 6) & 0x3f)));
134
+ out->push_back((char)(0x80 | (code_point & 0x3f)));
135
+ }
136
+ }
137
+
138
+ std::string decodeUtf16Be(const unsigned char *bytes, size_t length) {
139
+ std::string out;
140
+ out.reserve(length / 2);
141
+ for (size_t i = 0; i + 1 < length; i += 2) {
142
+ uint32_t unit = ((uint32_t)bytes[i] << 8) | bytes[i + 1];
143
+ if (unit >= 0xd800 && unit <= 0xdbff && i + 3 < length) {
144
+ const uint32_t low = ((uint32_t)bytes[i + 2] << 8) | bytes[i + 3];
145
+ if (low >= 0xdc00 && low <= 0xdfff) {
146
+ appendUtf8(&out, 0x10000 + ((unit - 0xd800) << 10) + (low - 0xdc00));
147
+ i += 2;
148
+ continue;
149
+ }
150
+ }
151
+ if (unit == 0) break;
152
+ appendUtf8(&out, unit >= 0xd800 && unit <= 0xdfff ? 0xfffd : unit);
153
+ }
154
+ return out;
155
+ }
156
+
157
+ std::string decodeNameBytes(const unsigned char *bytes, size_t length, uint16_t platform,
158
+ uint16_t encoding) {
159
+ if (platform == 0 || platform == 3 || (platform == 2 && encoding == 1)) {
160
+ return decodeUtf16Be(bytes, length);
161
+ }
162
+ /* Macintosh/legacy encodings are mostly ASCII for family names. Decode the
163
+ * rest as Latin-1 so a name is still usable for exact matching. */
164
+ std::string out;
165
+ out.reserve(length);
166
+ for (size_t i = 0; i < length; i++) {
167
+ unsigned char c = bytes[i];
168
+ if (c == 0) break;
169
+ if (c < 0x80) {
170
+ out.push_back((char)c);
171
+ } else {
172
+ appendUtf8(&out, c);
173
+ }
174
+ }
175
+ return out;
176
+ }
177
+
178
+ std::string trim(const std::string &text) {
179
+ size_t first = 0;
180
+ size_t last = text.size();
181
+ auto space = [](unsigned char c) {
182
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f';
183
+ };
184
+ while (first < last && space((unsigned char)text[first])) first++;
185
+ while (last > first && space((unsigned char)text[last - 1])) last--;
186
+ return text.substr(first, last - first);
187
+ }
188
+
189
+ struct NameSet {
190
+ std::string values[18];
191
+ int priorities[18];
192
+
193
+ NameSet() {
194
+ for (int &priority : priorities) priority = 100;
195
+ }
196
+
197
+ void add(uint16_t id, const std::string &value, int priority) {
198
+ if (id >= 18 || value.empty()) return;
199
+ if (priority >= priorities[id]) return;
200
+ values[id] = value;
201
+ priorities[id] = priority;
202
+ }
203
+ };
204
+
205
+ int namePriority(uint16_t platform, uint16_t encoding, uint16_t language) {
206
+ if (platform == 3) {
207
+ if (language == 0x0409) return 0;
208
+ if (language == 0x0809 || language == 0x0c09 || language == 0x1009 || language == 0x1409) {
209
+ return 1;
210
+ }
211
+ if (encoding == 1 || encoding == 10) return 4;
212
+ return 6;
213
+ }
214
+ if (platform == 0) return 2;
215
+ if (platform == 1 && language == 0) return 3;
216
+ return 5;
217
+ }
218
+
219
+ bool readNames(const FileReader &reader, const TableRecord &table, NameSet *names) {
220
+ uint16_t format = 0;
221
+ uint16_t count = 0;
222
+ uint16_t string_offset = 0;
223
+ if (!reader.u16(table.offset, &format) || !reader.u16(table.offset + 2, &count) ||
224
+ !reader.u16(table.offset + 4, &string_offset)) {
225
+ return false;
226
+ }
227
+ if ((format != 0 && format != 1) || count == 0 || count > 10000) return false;
228
+ if ((uint64_t)count * 12 > table.length) return false;
229
+ std::vector<unsigned char> records((size_t)count * 12);
230
+ if (!reader.read(table.offset + 6, records.data(), records.size())) return false;
231
+ if (string_offset > table.length) return false;
232
+ for (uint16_t i = 0; i < count; i++) {
233
+ const unsigned char *entry = records.data() + (size_t)i * 12;
234
+ const uint16_t platform = (uint16_t)((uint16_t)entry[0] << 8 | entry[1]);
235
+ const uint16_t encoding = (uint16_t)((uint16_t)entry[2] << 8 | entry[3]);
236
+ const uint16_t language = (uint16_t)((uint16_t)entry[4] << 8 | entry[5]);
237
+ const uint16_t name_id = (uint16_t)((uint16_t)entry[6] << 8 | entry[7]);
238
+ const uint16_t length = (uint16_t)((uint16_t)entry[8] << 8 | entry[9]);
239
+ const uint16_t offset = (uint16_t)((uint16_t)entry[10] << 8 | entry[11]);
240
+ if (name_id >= 18 || length == 0) continue;
241
+ const uint64_t start = (uint64_t)table.offset + string_offset + offset;
242
+ if (start > reader.size() || length > reader.size() - start) continue;
243
+ std::vector<unsigned char> bytes(length);
244
+ if (!reader.read(start, bytes.data(), bytes.size())) continue;
245
+ names->add(name_id, trim(decodeNameBytes(bytes.data(), bytes.size(), platform, encoding)),
246
+ namePriority(platform, encoding, language));
247
+ }
248
+ return true;
249
+ }
250
+
251
+ std::string lowerAscii(const std::string &text) {
252
+ std::string out = text;
253
+ for (char &c : out) {
254
+ if (c >= 'A' && c <= 'Z') c = (char)(c - 'A' + 'a');
255
+ }
256
+ return out;
257
+ }
258
+
259
+ int inferWeight(const std::string &style) {
260
+ const std::string text = lowerAscii(style);
261
+ if (text.find("thin") != std::string::npos) return 100;
262
+ if (text.find("extralight") != std::string::npos ||
263
+ text.find("ultralight") != std::string::npos) {
264
+ return 200;
265
+ }
266
+ if (text.find("semilight") != std::string::npos ||
267
+ text.find("demilight") != std::string::npos) {
268
+ return 350;
269
+ }
270
+ if (text.find("light") != std::string::npos) return 300;
271
+ if (text.find("extrabold") != std::string::npos ||
272
+ text.find("ultrabold") != std::string::npos) {
273
+ return 800;
274
+ }
275
+ if (text.find("semibold") != std::string::npos ||
276
+ text.find("demibold") != std::string::npos) {
277
+ return 600;
278
+ }
279
+ if (text.find("black") != std::string::npos || text.find("heavy") != std::string::npos ||
280
+ text.find("ultra") != std::string::npos) {
281
+ return 900;
282
+ }
283
+ if (text.find("bold") != std::string::npos) return 700;
284
+ if (text.find("medium") != std::string::npos) return 500;
285
+ return 400;
286
+ }
287
+
288
+ bool looksMonospace(const std::string &family, const std::string &style) {
289
+ const std::string text = lowerAscii(family + " " + style);
290
+ return text.find("mono") != std::string::npos || text.find("courier") != std::string::npos ||
291
+ text.find("consol") != std::string::npos || text.find("menlo") != std::string::npos ||
292
+ text.find("monaco") != std::string::npos || text.find("code") != std::string::npos ||
293
+ text.find("fixed") != std::string::npos || text.find("terminal") != std::string::npos;
294
+ }
295
+
296
+ std::vector<std::string> uniqueNames(const std::vector<std::string> &names) {
297
+ std::vector<std::string> out;
298
+ for (const std::string &name : names) {
299
+ const std::string trimmed = trim(name);
300
+ if (trimmed.empty()) continue;
301
+ if (std::find(out.begin(), out.end(), trimmed) == out.end()) out.push_back(trimmed);
302
+ }
303
+ return out;
304
+ }
305
+
306
+ bool parseFace(const FileReader &reader, uint64_t face_offset, int face_index, SfntFace *face) {
307
+ uint32_t sfnt_tag = 0;
308
+ std::vector<TableRecord> tables;
309
+ if (!readTableRecords(reader, face_offset, &sfnt_tag, &tables)) return false;
310
+
311
+ NameSet names;
312
+ if (const TableRecord *name_table = findTable(tables, kTagName)) {
313
+ readNames(reader, *name_table, &names);
314
+ }
315
+
316
+ const std::string legacy_family = names.values[1];
317
+ const std::string legacy_style = names.values[2];
318
+ const std::string full_name = names.values[4];
319
+ const std::string typographic_family = names.values[16];
320
+ const std::string typographic_style = names.values[17];
321
+
322
+ std::vector<std::string> candidates;
323
+ if (!typographic_family.empty()) candidates.push_back(typographic_family);
324
+ if (!legacy_family.empty()) candidates.push_back(legacy_family);
325
+ if (candidates.empty() && !full_name.empty()) candidates.push_back(full_name);
326
+ face->families = uniqueNames(candidates);
327
+ if (!face->families.empty()) face->primary_family = face->families.front();
328
+ face->index = face_index;
329
+ face->style = typographic_family.empty() ? legacy_style : typographic_style;
330
+ if (face->style.empty()) face->style = legacy_style;
331
+ face->truetype = sfnt_tag != kTagOtto;
332
+
333
+ face->weight = inferWeight(face->style);
334
+ if (const TableRecord *os2 = findTable(tables, kTagOs2)) {
335
+ uint16_t weight = 0;
336
+ if (os2->length >= 6 && reader.u16(os2->offset + 4, &weight) && weight >= 1 && weight <= 1000) {
337
+ face->weight = weight;
338
+ }
339
+ }
340
+
341
+ bool italic = false;
342
+ if (const TableRecord *os2 = findTable(tables, kTagOs2)) {
343
+ uint16_t selection = 0;
344
+ if (os2->length >= 64 && reader.u16(os2->offset + 62, &selection)) {
345
+ italic = (selection & 0x0001) != 0;
346
+ }
347
+ }
348
+ if (const TableRecord *head = findTable(tables, kTagHead)) {
349
+ uint16_t mac_style = 0;
350
+ if (head->length >= 46 && reader.u16(head->offset + 44, &mac_style)) {
351
+ italic = italic || (mac_style & 0x0002) != 0;
352
+ }
353
+ }
354
+ const std::string style_lower = lowerAscii(face->style);
355
+ face->italic = italic || style_lower.find("italic") != std::string::npos ||
356
+ style_lower.find("oblique") != std::string::npos;
357
+
358
+ bool fixed_pitch = false;
359
+ if (const TableRecord *post = findTable(tables, kTagPost)) {
360
+ uint32_t value = 0;
361
+ if (post->length >= 16 && reader.u32(post->offset + 12, &value)) fixed_pitch = value != 0;
362
+ }
363
+ face->monospace = fixed_pitch || looksMonospace(face->primary_family, face->style);
364
+ return true;
365
+ }
366
+
367
+ } // namespace
368
+
369
+ bool xt_sfnt_read_faces(const std::string &path, std::vector<SfntFace> &out) {
370
+ FileReader reader;
371
+ if (!reader.open(path)) return false;
372
+ uint32_t first_tag = 0;
373
+ if (!reader.u32(0, &first_tag)) return false;
374
+ if (first_tag != kTagTtcf) {
375
+ SfntFace face;
376
+ if (!parseFace(reader, 0, 0, &face)) return false;
377
+ out.push_back(std::move(face));
378
+ return true;
379
+ }
380
+
381
+ uint32_t count = 0;
382
+ if (!reader.u32(8, &count) || count == 0 || count > 4096) return false;
383
+ if (12 + (uint64_t)count * 4 > reader.size()) return false;
384
+ bool any = false;
385
+ for (uint32_t i = 0; i < count; i++) {
386
+ uint32_t offset = 0;
387
+ if (!reader.u32(12 + (uint64_t)i * 4, &offset)) continue;
388
+ SfntFace face;
389
+ if (!parseFace(reader, offset, (int)i, &face)) continue;
390
+ out.push_back(std::move(face));
391
+ any = true;
392
+ }
393
+ return any;
394
+ }
395
+
396
+ } // namespace xtgui