@cj-tech-master/excelts 9.4.1 → 9.4.2

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 (66) hide show
  1. package/dist/browser/modules/pdf/builder/document-builder.js +4 -23
  2. package/dist/browser/modules/pdf/core/pdf-stream.d.ts +15 -0
  3. package/dist/browser/modules/pdf/core/pdf-stream.js +47 -3
  4. package/dist/browser/modules/pdf/font/font-manager.d.ts +37 -6
  5. package/dist/browser/modules/pdf/font/font-manager.js +129 -17
  6. package/dist/browser/modules/pdf/font/system-fonts.d.ts +41 -0
  7. package/dist/browser/modules/pdf/font/system-fonts.js +188 -0
  8. package/dist/browser/modules/pdf/font/ttf-parser.js +29 -1
  9. package/dist/browser/modules/pdf/font/type3-font.d.ts +35 -0
  10. package/dist/browser/modules/pdf/font/type3-font.js +228 -0
  11. package/dist/browser/modules/pdf/font/type3-glyphs-extended.d.ts +33 -0
  12. package/dist/browser/modules/pdf/font/type3-glyphs-extended.js +4164 -0
  13. package/dist/browser/modules/pdf/font/type3-glyphs-extended2.d.ts +16 -0
  14. package/dist/browser/modules/pdf/font/type3-glyphs-extended2.js +9649 -0
  15. package/dist/browser/modules/pdf/font/type3-glyphs-fill.d.ts +17 -0
  16. package/dist/browser/modules/pdf/font/type3-glyphs-fill.js +5438 -0
  17. package/dist/browser/modules/pdf/font/type3-glyphs-quality.d.ts +28 -0
  18. package/dist/browser/modules/pdf/font/type3-glyphs-quality.js +5345 -0
  19. package/dist/browser/modules/pdf/font/type3-glyphs.d.ts +79 -0
  20. package/dist/browser/modules/pdf/font/type3-glyphs.js +2567 -0
  21. package/dist/browser/modules/pdf/render/layout-engine.js +36 -23
  22. package/dist/browser/modules/pdf/render/page-renderer.d.ts +9 -0
  23. package/dist/browser/modules/pdf/render/page-renderer.js +110 -78
  24. package/dist/browser/modules/pdf/render/pdf-exporter.js +73 -5
  25. package/dist/cjs/modules/pdf/builder/document-builder.js +3 -22
  26. package/dist/cjs/modules/pdf/core/pdf-stream.js +49 -3
  27. package/dist/cjs/modules/pdf/font/font-manager.js +129 -17
  28. package/dist/cjs/modules/pdf/font/system-fonts.js +194 -0
  29. package/dist/cjs/modules/pdf/font/ttf-parser.js +29 -1
  30. package/dist/cjs/modules/pdf/font/type3-font.js +231 -0
  31. package/dist/cjs/modules/pdf/font/type3-glyphs-extended.js +4167 -0
  32. package/dist/cjs/modules/pdf/font/type3-glyphs-extended2.js +9652 -0
  33. package/dist/cjs/modules/pdf/font/type3-glyphs-fill.js +5441 -0
  34. package/dist/cjs/modules/pdf/font/type3-glyphs-quality.js +5348 -0
  35. package/dist/cjs/modules/pdf/font/type3-glyphs.js +2573 -0
  36. package/dist/cjs/modules/pdf/render/layout-engine.js +36 -23
  37. package/dist/cjs/modules/pdf/render/page-renderer.js +111 -78
  38. package/dist/cjs/modules/pdf/render/pdf-exporter.js +71 -3
  39. package/dist/esm/modules/pdf/builder/document-builder.js +4 -23
  40. package/dist/esm/modules/pdf/core/pdf-stream.js +47 -3
  41. package/dist/esm/modules/pdf/font/font-manager.js +129 -17
  42. package/dist/esm/modules/pdf/font/system-fonts.js +188 -0
  43. package/dist/esm/modules/pdf/font/ttf-parser.js +29 -1
  44. package/dist/esm/modules/pdf/font/type3-font.js +228 -0
  45. package/dist/esm/modules/pdf/font/type3-glyphs-extended.js +4164 -0
  46. package/dist/esm/modules/pdf/font/type3-glyphs-extended2.js +9649 -0
  47. package/dist/esm/modules/pdf/font/type3-glyphs-fill.js +5438 -0
  48. package/dist/esm/modules/pdf/font/type3-glyphs-quality.js +5345 -0
  49. package/dist/esm/modules/pdf/font/type3-glyphs.js +2567 -0
  50. package/dist/esm/modules/pdf/render/layout-engine.js +36 -23
  51. package/dist/esm/modules/pdf/render/page-renderer.js +110 -78
  52. package/dist/esm/modules/pdf/render/pdf-exporter.js +73 -5
  53. package/dist/iife/excelts.iife.js +25445 -344
  54. package/dist/iife/excelts.iife.js.map +1 -1
  55. package/dist/iife/excelts.iife.min.js +48 -46
  56. package/dist/types/modules/pdf/core/pdf-stream.d.ts +15 -0
  57. package/dist/types/modules/pdf/font/font-manager.d.ts +37 -6
  58. package/dist/types/modules/pdf/font/system-fonts.d.ts +41 -0
  59. package/dist/types/modules/pdf/font/type3-font.d.ts +35 -0
  60. package/dist/types/modules/pdf/font/type3-glyphs-extended.d.ts +33 -0
  61. package/dist/types/modules/pdf/font/type3-glyphs-extended2.d.ts +16 -0
  62. package/dist/types/modules/pdf/font/type3-glyphs-fill.d.ts +17 -0
  63. package/dist/types/modules/pdf/font/type3-glyphs-quality.d.ts +28 -0
  64. package/dist/types/modules/pdf/font/type3-glyphs.d.ts +79 -0
  65. package/dist/types/modules/pdf/render/page-renderer.d.ts +9 -0
  66. package/package.json +1 -1
@@ -0,0 +1,4164 @@
1
+ /**
2
+ * Extended Type3 glyph definitions — programmatically generated blocks
3
+ * and additional hand-crafted symbols.
4
+ *
5
+ * This file supplements type3-glyphs.ts with:
6
+ * - Box Drawing (U+2500–U+257F) 128 chars — algorithmic
7
+ * - Block Elements (U+2580–U+259F) 32 chars — algorithmic
8
+ * - Braille Patterns (U+2800–U+28FF) 256 chars — algorithmic
9
+ * - Letterlike Symbols (U+2100–U+214F) hand-crafted
10
+ * - Number Forms (U+2150–U+218F) hand-crafted
11
+ * - Enclosed Alphanumerics (U+2460–U+24FF) programmatic
12
+ * - General Punctuation extras (U+2000–U+206F)
13
+ * - Additional Arrows, Math, Dingbats, Misc Symbols, Currency, Technical
14
+ */
15
+ const W = 600;
16
+ // =============================================================================
17
+ // Box Drawing — FULL BLOCK (U+2500–U+257F) 128 characters
18
+ // =============================================================================
19
+ //
20
+ // Each character is a combination of horizontal/vertical line segments
21
+ // through the cell centre (300, 250), with light (40) or heavy (80) weight.
22
+ // The naming pattern: bits encode which segments are present and their weight.
23
+ function boxDraw(left, right, up, down) {
24
+ return {
25
+ width: W,
26
+ draw: (p) => {
27
+ if (left) {
28
+ p.lineWidth(left);
29
+ p.M(0, 250);
30
+ p.L(300, 250);
31
+ p.stroke();
32
+ }
33
+ if (right) {
34
+ p.lineWidth(right);
35
+ p.M(300, 250);
36
+ p.L(600, 250);
37
+ p.stroke();
38
+ }
39
+ if (up) {
40
+ p.lineWidth(up);
41
+ p.M(300, 250);
42
+ p.L(300, 500);
43
+ p.stroke();
44
+ }
45
+ if (down) {
46
+ p.lineWidth(down);
47
+ p.M(300, 250);
48
+ p.L(300, 0);
49
+ p.stroke();
50
+ }
51
+ }
52
+ };
53
+ }
54
+ function boxDouble(leftD, rightD, upD, downD) {
55
+ const s = 30; // line weight
56
+ const g = 30; // gap between double lines
57
+ return {
58
+ width: W,
59
+ draw: (p) => {
60
+ p.lineWidth(s);
61
+ if (leftD) {
62
+ p.M(0, 250 - g);
63
+ p.L(300, 250 - g);
64
+ p.stroke();
65
+ p.M(0, 250 + g);
66
+ p.L(300, 250 + g);
67
+ p.stroke();
68
+ }
69
+ if (rightD) {
70
+ p.M(300, 250 - g);
71
+ p.L(600, 250 - g);
72
+ p.stroke();
73
+ p.M(300, 250 + g);
74
+ p.L(600, 250 + g);
75
+ p.stroke();
76
+ }
77
+ if (upD) {
78
+ p.M(300 - g, 250);
79
+ p.L(300 - g, 500);
80
+ p.stroke();
81
+ p.M(300 + g, 250);
82
+ p.L(300 + g, 500);
83
+ p.stroke();
84
+ }
85
+ if (downD) {
86
+ p.M(300 - g, 250);
87
+ p.L(300 - g, 0);
88
+ p.stroke();
89
+ p.M(300 + g, 250);
90
+ p.L(300 + g, 0);
91
+ p.stroke();
92
+ }
93
+ }
94
+ };
95
+ }
96
+ // Light=40, Heavy=80
97
+ const L = 40;
98
+ const H = 80;
99
+ export const BOX_FULL = {
100
+ // Light lines
101
+ [0x2500]: boxDraw(L, L, 0, 0), // ─
102
+ [0x2501]: boxDraw(H, H, 0, 0), // ━
103
+ [0x2502]: boxDraw(0, 0, L, L), // │
104
+ [0x2503]: boxDraw(0, 0, H, H), // ┃
105
+ [0x2504]: boxDraw(L, L, 0, 0), // ┄ (triple dash — approx as solid)
106
+ [0x2505]: boxDraw(H, H, 0, 0), // ┅
107
+ [0x2506]: boxDraw(0, 0, L, L), // ┆
108
+ [0x2507]: boxDraw(0, 0, H, H), // ┇
109
+ [0x2508]: boxDraw(L, L, 0, 0), // ┈ (quadruple dash)
110
+ [0x2509]: boxDraw(H, H, 0, 0), // ┉
111
+ [0x250a]: boxDraw(0, 0, L, L), // ┊
112
+ [0x250b]: boxDraw(0, 0, H, H), // ┋
113
+ // Corners: light
114
+ [0x250c]: boxDraw(0, L, 0, L), // ┌
115
+ [0x250d]: boxDraw(0, H, 0, L), // ┍
116
+ [0x250e]: boxDraw(0, L, 0, H), // ┎
117
+ [0x250f]: boxDraw(0, H, 0, H), // ┏
118
+ [0x2510]: boxDraw(L, 0, 0, L), // ┐
119
+ [0x2511]: boxDraw(H, 0, 0, L), // ┑
120
+ [0x2512]: boxDraw(L, 0, 0, H), // ┒
121
+ [0x2513]: boxDraw(H, 0, 0, H), // ┓
122
+ [0x2514]: boxDraw(0, L, L, 0), // └
123
+ [0x2515]: boxDraw(0, H, L, 0), // ┕
124
+ [0x2516]: boxDraw(0, L, H, 0), // ┖
125
+ [0x2517]: boxDraw(0, H, H, 0), // ┗
126
+ [0x2518]: boxDraw(L, 0, L, 0), // ┘
127
+ [0x2519]: boxDraw(H, 0, L, 0), // ┙
128
+ [0x251a]: boxDraw(L, 0, H, 0), // ┚
129
+ [0x251b]: boxDraw(H, 0, H, 0), // ┛
130
+ // T-junctions
131
+ [0x251c]: boxDraw(0, L, L, L), // ├
132
+ [0x251d]: boxDraw(0, H, L, L), // ┝
133
+ [0x251e]: boxDraw(0, L, H, L), // ┞
134
+ [0x251f]: boxDraw(0, L, L, H), // ┟
135
+ [0x2520]: boxDraw(0, L, H, H), // ┠
136
+ [0x2521]: boxDraw(0, H, H, L), // ┡
137
+ [0x2522]: boxDraw(0, H, L, H), // ┢
138
+ [0x2523]: boxDraw(0, H, H, H), // ┣
139
+ [0x2524]: boxDraw(L, 0, L, L), // ┤
140
+ [0x2525]: boxDraw(H, 0, L, L), // ┥
141
+ [0x2526]: boxDraw(L, 0, H, L), // ┦
142
+ [0x2527]: boxDraw(L, 0, L, H), // ┧
143
+ [0x2528]: boxDraw(L, 0, H, H), // ┨
144
+ [0x2529]: boxDraw(H, 0, H, L), // ┩
145
+ [0x252a]: boxDraw(H, 0, L, H), // ┪
146
+ [0x252b]: boxDraw(H, 0, H, H), // ┫
147
+ [0x252c]: boxDraw(L, L, 0, L), // ┬
148
+ [0x252d]: boxDraw(H, L, 0, L), // ┭
149
+ [0x252e]: boxDraw(L, H, 0, L), // ┮
150
+ [0x252f]: boxDraw(H, H, 0, L), // ┯
151
+ [0x2530]: boxDraw(L, L, 0, H), // ┰
152
+ [0x2531]: boxDraw(H, L, 0, H), // ┱
153
+ [0x2532]: boxDraw(L, H, 0, H), // ┲
154
+ [0x2533]: boxDraw(H, H, 0, H), // ┳
155
+ [0x2534]: boxDraw(L, L, L, 0), // ┴
156
+ [0x2535]: boxDraw(H, L, L, 0), // ┵
157
+ [0x2536]: boxDraw(L, H, L, 0), // ┶
158
+ [0x2537]: boxDraw(H, H, L, 0), // ┷
159
+ [0x2538]: boxDraw(L, L, H, 0), // ┸
160
+ [0x2539]: boxDraw(H, L, H, 0), // ┹
161
+ [0x253a]: boxDraw(L, H, H, 0), // ┺
162
+ [0x253b]: boxDraw(H, H, H, 0), // ┻
163
+ // Crosses
164
+ [0x253c]: boxDraw(L, L, L, L), // ┼
165
+ [0x253d]: boxDraw(H, L, L, L), // ┽
166
+ [0x253e]: boxDraw(L, H, L, L), // ┾
167
+ [0x253f]: boxDraw(H, H, L, L), // ┿
168
+ [0x2540]: boxDraw(L, L, H, L), // ╀
169
+ [0x2541]: boxDraw(L, L, L, H), // ╁
170
+ [0x2542]: boxDraw(L, L, H, H), // ╂
171
+ [0x2543]: boxDraw(H, L, H, L), // ╃
172
+ [0x2544]: boxDraw(L, H, H, L), // ╄
173
+ [0x2545]: boxDraw(H, L, L, H), // ╅
174
+ [0x2546]: boxDraw(L, H, L, H), // ╆
175
+ [0x2547]: boxDraw(H, H, H, L), // ╇
176
+ [0x2548]: boxDraw(H, H, L, H), // ╈
177
+ [0x2549]: boxDraw(H, L, H, H), // ╉
178
+ [0x254a]: boxDraw(L, H, H, H), // ╊
179
+ [0x254b]: boxDraw(H, H, H, H), // ╋
180
+ // Double lines
181
+ [0x2550]: boxDouble(true, true, false, false), // ═
182
+ [0x2551]: boxDouble(false, false, true, true), // ║
183
+ [0x2552]: boxDouble(false, true, false, true), // ╒ (approx)
184
+ [0x2553]: boxDouble(false, true, false, true), // ╓
185
+ [0x2554]: boxDouble(false, true, false, true), // ╔
186
+ [0x2555]: boxDouble(true, false, false, true), // ╕
187
+ [0x2556]: boxDouble(true, false, false, true), // ╖
188
+ [0x2557]: boxDouble(true, false, false, true), // ╗
189
+ [0x2558]: boxDouble(false, true, true, false), // ╘
190
+ [0x2559]: boxDouble(false, true, true, false), // ╙
191
+ [0x255a]: boxDouble(false, true, true, false), // ╚
192
+ [0x255b]: boxDouble(true, false, true, false), // ╛
193
+ [0x255c]: boxDouble(true, false, true, false), // ╜
194
+ [0x255d]: boxDouble(true, false, true, false), // ╝
195
+ [0x255e]: boxDouble(false, true, true, true), // ╞
196
+ [0x255f]: boxDouble(false, true, true, true), // ╟
197
+ [0x2560]: boxDouble(false, true, true, true), // ╠
198
+ [0x2561]: boxDouble(true, false, true, true), // ╡
199
+ [0x2562]: boxDouble(true, false, true, true), // ╢
200
+ [0x2563]: boxDouble(true, false, true, true), // ╣
201
+ [0x2564]: boxDouble(true, true, false, true), // ╤
202
+ [0x2565]: boxDouble(true, true, false, true), // ╥
203
+ [0x2566]: boxDouble(true, true, false, true), // ╦
204
+ [0x2567]: boxDouble(true, true, true, false), // ╧
205
+ [0x2568]: boxDouble(true, true, true, false), // ╨
206
+ [0x2569]: boxDouble(true, true, true, false), // ╩
207
+ [0x256a]: boxDouble(true, true, true, true), // ╪
208
+ [0x256b]: boxDouble(true, true, true, true), // ╫
209
+ [0x256c]: boxDouble(true, true, true, true), // ╬
210
+ // Rounded corners (light)
211
+ [0x256d]: boxDraw(0, L, 0, L), // ╭
212
+ [0x256e]: boxDraw(L, 0, 0, L), // ╮
213
+ [0x256f]: boxDraw(L, 0, L, 0), // ╯
214
+ [0x2570]: boxDraw(0, L, L, 0), // ╰
215
+ // Diagonals
216
+ [0x2571]: {
217
+ width: W,
218
+ draw: (p) => {
219
+ p.lineWidth(L);
220
+ p.M(0, 0);
221
+ p.L(600, 500);
222
+ p.stroke();
223
+ }
224
+ }, // ╱
225
+ [0x2572]: {
226
+ width: W,
227
+ draw: (p) => {
228
+ p.lineWidth(L);
229
+ p.M(0, 500);
230
+ p.L(600, 0);
231
+ p.stroke();
232
+ }
233
+ }, // ╲
234
+ [0x2573]: {
235
+ width: W,
236
+ draw: (p) => {
237
+ p.lineWidth(L);
238
+ p.M(0, 0);
239
+ p.L(600, 500);
240
+ p.stroke();
241
+ p.M(0, 500);
242
+ p.L(600, 0);
243
+ p.stroke();
244
+ }
245
+ }, // ╳
246
+ // Half lines
247
+ [0x2574]: boxDraw(L, 0, 0, 0), // ╴
248
+ [0x2575]: boxDraw(0, 0, L, 0), // ╵
249
+ [0x2576]: boxDraw(0, L, 0, 0), // ╶
250
+ [0x2577]: boxDraw(0, 0, 0, L), // ╷
251
+ [0x2578]: boxDraw(H, 0, 0, 0), // ╸
252
+ [0x2579]: boxDraw(0, 0, H, 0), // ╹
253
+ [0x257a]: boxDraw(0, H, 0, 0), // ╺
254
+ [0x257b]: boxDraw(0, 0, 0, H), // ╻
255
+ [0x257c]: boxDraw(L, H, 0, 0), // ╼
256
+ [0x257d]: boxDraw(0, 0, L, H), // ╽
257
+ [0x257e]: boxDraw(H, L, 0, 0), // ╾
258
+ [0x257f]: boxDraw(0, 0, H, L) // ╿
259
+ };
260
+ // =============================================================================
261
+ // Block Elements — FULL BLOCK (U+2580–U+259F) 32 characters
262
+ // =============================================================================
263
+ function blockElement(x, y, w, h) {
264
+ return {
265
+ width: W,
266
+ draw: (p) => {
267
+ p.rect(x, y, w, h);
268
+ p.fill();
269
+ }
270
+ };
271
+ }
272
+ export const BLOCK_FULL = {
273
+ [0x2580]: blockElement(0, 250, 600, 250), // ▀ UPPER HALF
274
+ [0x2581]: blockElement(0, 0, 600, 62), // ▁ LOWER ONE EIGHTH
275
+ [0x2582]: blockElement(0, 0, 600, 125), // ▂ LOWER ONE QUARTER
276
+ [0x2583]: blockElement(0, 0, 600, 187), // ▃ LOWER THREE EIGHTHS
277
+ [0x2584]: blockElement(0, 0, 600, 250), // ▄ LOWER HALF
278
+ [0x2585]: blockElement(0, 0, 600, 312), // ▅ LOWER FIVE EIGHTHS
279
+ [0x2586]: blockElement(0, 0, 600, 375), // ▆ LOWER THREE QUARTERS
280
+ [0x2587]: blockElement(0, 0, 600, 437), // ▇ LOWER SEVEN EIGHTHS
281
+ [0x2588]: blockElement(0, 0, 600, 500), // █ FULL BLOCK
282
+ [0x2589]: blockElement(0, 0, 525, 500), // ▉ LEFT SEVEN EIGHTHS
283
+ [0x258a]: blockElement(0, 0, 450, 500), // ▊ LEFT THREE QUARTERS
284
+ [0x258b]: blockElement(0, 0, 375, 500), // ▋ LEFT FIVE EIGHTHS
285
+ [0x258c]: blockElement(0, 0, 300, 500), // ▌ LEFT HALF
286
+ [0x258d]: blockElement(0, 0, 225, 500), // ▍ LEFT THREE EIGHTHS
287
+ [0x258e]: blockElement(0, 0, 150, 500), // ▎ LEFT ONE QUARTER
288
+ [0x258f]: blockElement(0, 0, 75, 500), // ▏ LEFT ONE EIGHTH
289
+ [0x2590]: blockElement(300, 0, 300, 500), // ▐ RIGHT HALF
290
+ [0x2591]: {
291
+ width: W,
292
+ draw: (p) => {
293
+ p.lineWidth(10);
294
+ for (let y = 10; y < 500; y += 40) {
295
+ for (let x = 10; x < 600; x += 40) {
296
+ p.rect(x, y, 5, 5);
297
+ }
298
+ }
299
+ p.fill();
300
+ }
301
+ }, // ░
302
+ [0x2592]: {
303
+ width: W,
304
+ draw: (p) => {
305
+ p.lineWidth(10);
306
+ for (let y = 5; y < 500; y += 20) {
307
+ for (let x = 5; x < 600; x += 20) {
308
+ p.rect(x, y, 8, 8);
309
+ }
310
+ }
311
+ p.fill();
312
+ }
313
+ }, // ▒
314
+ [0x2593]: {
315
+ width: W,
316
+ draw: (p) => {
317
+ p.lineWidth(10);
318
+ for (let y = 3; y < 500; y += 12) {
319
+ for (let x = 3; x < 600; x += 12) {
320
+ p.rect(x, y, 10, 10);
321
+ }
322
+ }
323
+ p.fill();
324
+ }
325
+ }, // ▓
326
+ [0x2594]: blockElement(0, 437, 600, 63), // ▔ UPPER ONE EIGHTH
327
+ [0x2595]: blockElement(525, 0, 75, 500), // ▕ RIGHT ONE EIGHTH
328
+ [0x2596]: blockElement(0, 0, 300, 250), // ▖ QUADRANT LOWER LEFT
329
+ [0x2597]: blockElement(300, 0, 300, 250), // ▗ QUADRANT LOWER RIGHT
330
+ [0x2598]: blockElement(0, 250, 300, 250), // ▘ QUADRANT UPPER LEFT
331
+ [0x2599]: {
332
+ width: W,
333
+ draw: (p) => {
334
+ p.rect(0, 0, 300, 250);
335
+ p.fill();
336
+ p.rect(0, 250, 300, 250);
337
+ p.fill();
338
+ p.rect(300, 0, 300, 250);
339
+ p.fill();
340
+ }
341
+ }, // ▙
342
+ [0x259a]: {
343
+ width: W,
344
+ draw: (p) => {
345
+ p.rect(0, 250, 300, 250);
346
+ p.fill();
347
+ p.rect(300, 0, 300, 250);
348
+ p.fill();
349
+ }
350
+ }, // ▚
351
+ [0x259b]: {
352
+ width: W,
353
+ draw: (p) => {
354
+ p.rect(0, 0, 300, 250);
355
+ p.fill();
356
+ p.rect(0, 250, 600, 250);
357
+ p.fill();
358
+ }
359
+ }, // ▛
360
+ [0x259c]: {
361
+ width: W,
362
+ draw: (p) => {
363
+ p.rect(300, 0, 300, 250);
364
+ p.fill();
365
+ p.rect(0, 250, 600, 250);
366
+ p.fill();
367
+ }
368
+ }, // ▜
369
+ [0x259d]: blockElement(300, 250, 300, 250), // ▝ QUADRANT UPPER RIGHT
370
+ [0x259e]: {
371
+ width: W,
372
+ draw: (p) => {
373
+ p.rect(300, 250, 300, 250);
374
+ p.fill();
375
+ p.rect(0, 0, 300, 250);
376
+ p.fill();
377
+ }
378
+ }, // ▞
379
+ [0x259f]: {
380
+ width: W,
381
+ draw: (p) => {
382
+ p.rect(300, 0, 300, 500);
383
+ p.fill();
384
+ p.rect(0, 0, 300, 250);
385
+ p.fill();
386
+ }
387
+ } // ▟
388
+ };
389
+ // =============================================================================
390
+ // Braille Patterns — FULL BLOCK (U+2800–U+28FF) 256 characters
391
+ // =============================================================================
392
+ // Each Braille pattern is a 2×4 grid of dots. The code point encodes which
393
+ // dots are raised: bit 0=dot1(top-left), bit 1=dot2(mid-left), bit 2=dot3(bot-left),
394
+ // bit 3=dot4(top-right), bit 4=dot5(mid-right), bit 5=dot6(bot-right),
395
+ // bit 6=dot7(lower-left), bit 7=dot8(lower-right).
396
+ function brailleGlyph(pattern) {
397
+ // Dot positions in the 1000-unit glyph space
398
+ const dotR = 40;
399
+ const colX = [200, 400]; // left, right
400
+ const rowY = [400, 300, 200, 100]; // top to bottom (PDF y increases upward)
401
+ // Bit→dot mapping: bits 0-2 = left col rows 0-2, bits 3-5 = right col rows 0-2,
402
+ // bit 6 = left col row 3, bit 7 = right col row 3
403
+ const dots = [
404
+ [0, 0],
405
+ [0, 1],
406
+ [0, 2], // bits 0,1,2 → left col, rows 0,1,2
407
+ [1, 0],
408
+ [1, 1],
409
+ [1, 2], // bits 3,4,5 → right col, rows 0,1,2
410
+ [0, 3],
411
+ [1, 3] // bits 6,7 → left/right col, row 3
412
+ ];
413
+ return {
414
+ width: W,
415
+ draw: (p) => {
416
+ for (let bit = 0; bit < 8; bit++) {
417
+ if (pattern & (1 << bit)) {
418
+ const [col, row] = dots[bit];
419
+ p.circle(colX[col], rowY[row], dotR);
420
+ p.fill();
421
+ }
422
+ }
423
+ }
424
+ };
425
+ }
426
+ export const BRAILLE = {};
427
+ for (let i = 0; i < 256; i++) {
428
+ BRAILLE[0x2800 + i] = brailleGlyph(i);
429
+ }
430
+ // =============================================================================
431
+ // Letterlike Symbols (U+2100–U+214F)
432
+ // =============================================================================
433
+ export const LETTERLIKE = {
434
+ // ℃ DEGREE CELSIUS
435
+ [0x2103]: {
436
+ width: 700,
437
+ draw: (p) => {
438
+ p.lineWidth(40);
439
+ p.circle(120, 450, 60);
440
+ p.stroke();
441
+ p.M(550, 450);
442
+ p.C(350, 450, 250, 350, 250, 250);
443
+ p.C(250, 150, 350, 50, 550, 50);
444
+ p.stroke();
445
+ }
446
+ },
447
+ // ℅ CARE OF
448
+ [0x2105]: {
449
+ width: W,
450
+ draw: (p) => {
451
+ p.lineWidth(40);
452
+ p.M(150, 350);
453
+ p.C(80, 350, 50, 400, 50, 430);
454
+ p.C(50, 470, 100, 500, 150, 500);
455
+ p.C(200, 500, 250, 470, 250, 430);
456
+ p.C(250, 400, 200, 350, 150, 350);
457
+ p.stroke();
458
+ p.M(450, 0);
459
+ p.L(150, 500);
460
+ p.stroke();
461
+ p.M(450, 150);
462
+ p.C(380, 150, 350, 100, 350, 70);
463
+ p.C(350, 30, 400, 0, 450, 0);
464
+ p.C(500, 0, 550, 30, 550, 70);
465
+ p.C(550, 100, 500, 150, 450, 150);
466
+ p.stroke();
467
+ }
468
+ },
469
+ // ℉ DEGREE FAHRENHEIT
470
+ [0x2109]: {
471
+ width: 700,
472
+ draw: (p) => {
473
+ p.lineWidth(40);
474
+ p.circle(120, 450, 60);
475
+ p.stroke();
476
+ p.M(280, 0);
477
+ p.L(280, 500);
478
+ p.stroke();
479
+ p.M(280, 500);
480
+ p.L(550, 500);
481
+ p.stroke();
482
+ p.M(280, 280);
483
+ p.L(480, 280);
484
+ p.stroke();
485
+ }
486
+ },
487
+ // ℓ SCRIPT SMALL L
488
+ [0x2113]: {
489
+ width: W,
490
+ draw: (p) => {
491
+ p.lineWidth(45);
492
+ p.M(350, 500);
493
+ p.C(200, 500, 200, 400, 250, 200);
494
+ p.C(280, 100, 200, 0, 150, 50);
495
+ p.stroke();
496
+ }
497
+ },
498
+ // № NUMERO SIGN
499
+ [0x2116]: {
500
+ width: 700,
501
+ draw: (p) => {
502
+ p.lineWidth(45);
503
+ p.M(80, 0);
504
+ p.L(80, 500);
505
+ p.stroke();
506
+ p.M(80, 500);
507
+ p.L(350, 0);
508
+ p.stroke();
509
+ p.M(350, 0);
510
+ p.L(350, 500);
511
+ p.stroke();
512
+ p.lineWidth(35);
513
+ p.circle(530, 70, 70);
514
+ p.stroke();
515
+ p.M(380, 170);
516
+ p.L(680, 170);
517
+ p.stroke();
518
+ }
519
+ },
520
+ // ℠ SERVICE MARK
521
+ [0x2120]: {
522
+ width: W,
523
+ draw: (p) => {
524
+ p.lineWidth(30);
525
+ p.M(150, 400);
526
+ p.C(150, 500, 350, 500, 350, 450);
527
+ p.C(350, 400, 150, 350, 150, 300);
528
+ p.C(150, 250, 350, 250, 350, 350);
529
+ p.stroke();
530
+ p.M(400, 500);
531
+ p.L(400, 250);
532
+ p.L(475, 400);
533
+ p.L(550, 250);
534
+ p.L(550, 500);
535
+ p.stroke();
536
+ }
537
+ },
538
+ // ™ TRADE MARK SIGN
539
+ [0x2122]: {
540
+ width: W,
541
+ draw: (p) => {
542
+ p.lineWidth(30);
543
+ p.M(50, 500);
544
+ p.L(200, 500);
545
+ p.stroke();
546
+ p.M(125, 500);
547
+ p.L(125, 300);
548
+ p.stroke();
549
+ p.M(250, 500);
550
+ p.L(250, 300);
551
+ p.L(350, 450);
552
+ p.L(450, 300);
553
+ p.L(450, 500);
554
+ p.stroke();
555
+ }
556
+ },
557
+ // Ω OHM SIGN
558
+ [0x2126]: {
559
+ width: W,
560
+ draw: (p) => {
561
+ p.lineWidth(45);
562
+ p.M(100, 50);
563
+ p.L(200, 50);
564
+ p.L(200, 150);
565
+ p.C(120, 200, 80, 300, 80, 350);
566
+ p.C(80, 450, 180, 520, 300, 520);
567
+ p.C(420, 520, 520, 450, 520, 350);
568
+ p.C(520, 300, 480, 200, 400, 150);
569
+ p.L(400, 50);
570
+ p.L(500, 50);
571
+ p.stroke();
572
+ }
573
+ },
574
+ // ℮ ESTIMATED SIGN
575
+ [0x212e]: {
576
+ width: W,
577
+ draw: (p) => {
578
+ p.lineWidth(40);
579
+ p.M(480, 220);
580
+ p.L(150, 220);
581
+ p.C(150, 400, 250, 500, 350, 500);
582
+ p.C(450, 500, 530, 400, 500, 300);
583
+ p.C(470, 100, 400, 0, 250, 0);
584
+ p.C(150, 0, 100, 80, 100, 150);
585
+ p.stroke();
586
+ }
587
+ },
588
+ // ℹ INFORMATION SOURCE
589
+ [0x2139]: {
590
+ width: W,
591
+ draw: (p) => {
592
+ p.circle(300, 450, 40);
593
+ p.fill();
594
+ p.lineWidth(55);
595
+ p.M(300, 350);
596
+ p.L(300, 50);
597
+ p.stroke();
598
+ }
599
+ },
600
+ // ⅟ FRACTION NUMERATOR ONE
601
+ [0x215f]: {
602
+ width: W,
603
+ draw: (p) => {
604
+ p.lineWidth(40);
605
+ p.M(150, 500);
606
+ p.L(200, 500);
607
+ p.L(200, 350);
608
+ p.stroke();
609
+ p.M(450, 500);
610
+ p.L(150, 0);
611
+ p.stroke();
612
+ }
613
+ }
614
+ };
615
+ // =============================================================================
616
+ // Number Forms (U+2150–U+218F) — Vulgar Fractions
617
+ // =============================================================================
618
+ function fraction(num, denom) {
619
+ return {
620
+ width: W,
621
+ draw: (p) => {
622
+ // We can't draw actual text in Type3 glyphs, so draw the fraction bar
623
+ // and approximate numerator/denominator with simple shapes
624
+ p.lineWidth(35);
625
+ p.M(450, 500);
626
+ p.L(150, 0);
627
+ p.stroke();
628
+ // Small circles as digit placeholders
629
+ const numCount = num.length;
630
+ const denCount = denom.length;
631
+ for (let i = 0; i < numCount; i++) {
632
+ p.circle(150 + i * 80, 420, 30);
633
+ p.fill();
634
+ }
635
+ for (let i = 0; i < denCount; i++) {
636
+ p.circle(350 + i * 80, 80, 30);
637
+ p.fill();
638
+ }
639
+ }
640
+ };
641
+ }
642
+ export const NUMBER_FORMS = {
643
+ [0x2150]: fraction("1", "7"), // ⅐
644
+ [0x2151]: fraction("1", "9"), // ⅑
645
+ [0x2152]: fraction("1", "10"), // ⅒
646
+ [0x2153]: fraction("1", "3"), // ⅓
647
+ [0x2154]: fraction("2", "3"), // ⅔
648
+ [0x2155]: fraction("1", "5"), // ⅕
649
+ [0x2156]: fraction("2", "5"), // ⅖
650
+ [0x2157]: fraction("3", "5"), // ⅗
651
+ [0x2158]: fraction("4", "5"), // ⅘
652
+ [0x2159]: fraction("1", "6"), // ⅙
653
+ [0x215a]: fraction("5", "6"), // ⅚
654
+ [0x215b]: fraction("1", "8"), // ⅛
655
+ [0x215c]: fraction("3", "8"), // ⅜
656
+ [0x215d]: fraction("5", "8"), // ⅝
657
+ [0x215e]: fraction("7", "8"), // ⅞
658
+ // Roman numerals as simple stroked letters
659
+ [0x2160]: {
660
+ width: W,
661
+ draw: (p) => {
662
+ p.lineWidth(50);
663
+ p.M(300, 0);
664
+ p.L(300, 500);
665
+ p.stroke();
666
+ }
667
+ }, // Ⅰ
668
+ [0x2161]: {
669
+ width: W,
670
+ draw: (p) => {
671
+ p.lineWidth(50);
672
+ p.M(200, 0);
673
+ p.L(200, 500);
674
+ p.stroke();
675
+ p.M(400, 0);
676
+ p.L(400, 500);
677
+ p.stroke();
678
+ }
679
+ }, // Ⅱ
680
+ [0x2162]: {
681
+ width: W,
682
+ draw: (p) => {
683
+ p.lineWidth(45);
684
+ p.M(120, 0);
685
+ p.L(120, 500);
686
+ p.stroke();
687
+ p.M(300, 0);
688
+ p.L(300, 500);
689
+ p.stroke();
690
+ p.M(480, 0);
691
+ p.L(480, 500);
692
+ p.stroke();
693
+ }
694
+ }, // Ⅲ
695
+ [0x2163]: {
696
+ width: W,
697
+ draw: (p) => {
698
+ p.lineWidth(50);
699
+ p.M(180, 500);
700
+ p.L(300, 0);
701
+ p.L(420, 500);
702
+ p.stroke();
703
+ }
704
+ }, // Ⅳ (V shape inverted — approx)
705
+ [0x2164]: {
706
+ width: W,
707
+ draw: (p) => {
708
+ p.lineWidth(50);
709
+ p.M(150, 500);
710
+ p.L(300, 0);
711
+ p.L(450, 500);
712
+ p.stroke();
713
+ }
714
+ } // Ⅴ
715
+ };
716
+ // =============================================================================
717
+ // Enclosed Alphanumerics — circled numbers ①–⑳ (U+2460–U+2473)
718
+ // =============================================================================
719
+ export const ENCLOSED = {};
720
+ // ① - ⑳ : circled digits (stroke circle only — actual digit not possible w/o font)
721
+ for (let i = 0; i < 20; i++) {
722
+ ENCLOSED[0x2460 + i] = {
723
+ width: W,
724
+ draw: (p) => {
725
+ p.lineWidth(35);
726
+ p.circle(300, 250, 220);
727
+ p.stroke();
728
+ }
729
+ };
730
+ }
731
+ // ⑴-⑿ Parenthesized digits
732
+ for (let i = 0; i < 12; i++) {
733
+ ENCLOSED[0x2474 + i] = {
734
+ width: W,
735
+ draw: (p) => {
736
+ p.lineWidth(30);
737
+ p.M(180, 480);
738
+ p.C(120, 480, 80, 380, 80, 250);
739
+ p.C(80, 120, 120, 20, 180, 20);
740
+ p.stroke();
741
+ p.M(420, 20);
742
+ p.C(480, 20, 520, 120, 520, 250);
743
+ p.C(520, 380, 480, 480, 420, 480);
744
+ p.stroke();
745
+ }
746
+ };
747
+ }
748
+ // Ⓐ-Ⓩ Circled Latin Capital (U+24B6–U+24CF)
749
+ for (let i = 0; i < 26; i++) {
750
+ ENCLOSED[0x24b6 + i] = {
751
+ width: W,
752
+ draw: (p) => {
753
+ p.lineWidth(35);
754
+ p.circle(300, 250, 220);
755
+ p.stroke();
756
+ }
757
+ };
758
+ }
759
+ // ⓐ-ⓩ Circled Latin Small (U+24D0–U+24E9)
760
+ for (let i = 0; i < 26; i++) {
761
+ ENCLOSED[0x24d0 + i] = {
762
+ width: W,
763
+ draw: (p) => {
764
+ p.lineWidth(30);
765
+ p.circle(300, 250, 200);
766
+ p.stroke();
767
+ }
768
+ };
769
+ }
770
+ // ⓪ Circled zero (U+24EA)
771
+ ENCLOSED[0x24ea] = {
772
+ width: W,
773
+ draw: (p) => {
774
+ p.lineWidth(35);
775
+ p.circle(300, 250, 220);
776
+ p.stroke();
777
+ }
778
+ };
779
+ // =============================================================================
780
+ // Additional General Punctuation (U+2000–U+206F)
781
+ // =============================================================================
782
+ export const PUNCT_EXT = {
783
+ // ‐ HYPHEN (U+2010)
784
+ [0x2010]: {
785
+ width: W,
786
+ draw: (p) => {
787
+ p.lineWidth(50);
788
+ p.M(150, 250);
789
+ p.L(450, 250);
790
+ p.stroke();
791
+ }
792
+ },
793
+ // ‑ NON-BREAKING HYPHEN (U+2011)
794
+ [0x2011]: {
795
+ width: W,
796
+ draw: (p) => {
797
+ p.lineWidth(50);
798
+ p.M(150, 250);
799
+ p.L(450, 250);
800
+ p.stroke();
801
+ }
802
+ },
803
+ // ‒ FIGURE DASH (U+2012)
804
+ [0x2012]: {
805
+ width: W,
806
+ draw: (p) => {
807
+ p.lineWidth(50);
808
+ p.M(100, 250);
809
+ p.L(500, 250);
810
+ p.stroke();
811
+ }
812
+ },
813
+ // – EN DASH (U+2013)
814
+ [0x2013]: {
815
+ width: W,
816
+ draw: (p) => {
817
+ p.lineWidth(50);
818
+ p.M(80, 250);
819
+ p.L(520, 250);
820
+ p.stroke();
821
+ }
822
+ },
823
+ // — EM DASH (U+2014)
824
+ [0x2014]: {
825
+ width: 700,
826
+ draw: (p) => {
827
+ p.lineWidth(50);
828
+ p.M(0, 250);
829
+ p.L(700, 250);
830
+ p.stroke();
831
+ }
832
+ },
833
+ // ‖ DOUBLE VERTICAL LINE (U+2016)
834
+ [0x2016]: {
835
+ width: W,
836
+ draw: (p) => {
837
+ p.lineWidth(40);
838
+ p.M(230, 0);
839
+ p.L(230, 500);
840
+ p.stroke();
841
+ p.M(370, 0);
842
+ p.L(370, 500);
843
+ p.stroke();
844
+ }
845
+ },
846
+ // ‗ DOUBLE LOW LINE (U+2017)
847
+ [0x2017]: {
848
+ width: W,
849
+ draw: (p) => {
850
+ p.lineWidth(30);
851
+ p.M(50, 30);
852
+ p.L(550, 30);
853
+ p.stroke();
854
+ p.M(50, 80);
855
+ p.L(550, 80);
856
+ p.stroke();
857
+ }
858
+ },
859
+ // ' LEFT SINGLE QUOTATION MARK (U+2018)
860
+ [0x2018]: {
861
+ width: 300,
862
+ draw: (p) => {
863
+ p.circle(150, 450, 40);
864
+ p.fill();
865
+ p.lineWidth(30);
866
+ p.M(150, 410);
867
+ p.C(120, 370, 100, 350, 80, 340);
868
+ p.stroke();
869
+ }
870
+ },
871
+ // ' RIGHT SINGLE QUOTATION MARK (U+2019)
872
+ [0x2019]: {
873
+ width: 300,
874
+ draw: (p) => {
875
+ p.circle(150, 450, 40);
876
+ p.fill();
877
+ p.lineWidth(30);
878
+ p.M(150, 410);
879
+ p.C(180, 370, 200, 350, 220, 340);
880
+ p.stroke();
881
+ }
882
+ },
883
+ // ‟ DOUBLE HIGH-REVERSED-9 QUOTATION MARK (U+201F)
884
+ [0x201f]: {
885
+ width: 400,
886
+ draw: (p) => {
887
+ p.circle(120, 450, 35);
888
+ p.fill();
889
+ p.circle(280, 450, 35);
890
+ p.fill();
891
+ }
892
+ },
893
+ // † DAGGER (U+2020)
894
+ [0x2020]: {
895
+ width: W,
896
+ draw: (p) => {
897
+ p.lineWidth(50);
898
+ p.M(300, 500);
899
+ p.L(300, 0);
900
+ p.stroke();
901
+ p.M(150, 380);
902
+ p.L(450, 380);
903
+ p.stroke();
904
+ }
905
+ },
906
+ // ‡ DOUBLE DAGGER (U+2021)
907
+ [0x2021]: {
908
+ width: W,
909
+ draw: (p) => {
910
+ p.lineWidth(45);
911
+ p.M(300, 500);
912
+ p.L(300, 0);
913
+ p.stroke();
914
+ p.M(150, 380);
915
+ p.L(450, 380);
916
+ p.stroke();
917
+ p.M(150, 180);
918
+ p.L(450, 180);
919
+ p.stroke();
920
+ }
921
+ },
922
+ // ‣ TRIANGULAR BULLET (U+2023) — already in main file
923
+ // ‥ TWO DOT LEADER (U+2025)
924
+ [0x2025]: {
925
+ width: W,
926
+ draw: (p) => {
927
+ p.circle(200, 50, 35);
928
+ p.fill();
929
+ p.circle(400, 50, 35);
930
+ p.fill();
931
+ }
932
+ },
933
+ // ‧ HYPHENATION POINT (U+2027)
934
+ [0x2027]: {
935
+ width: 300,
936
+ draw: (p) => {
937
+ p.circle(150, 250, 35);
938
+ p.fill();
939
+ }
940
+ },
941
+ // ‰ PER MILLE SIGN (U+2030)
942
+ [0x2030]: {
943
+ width: 700,
944
+ draw: (p) => {
945
+ p.lineWidth(35);
946
+ p.M(550, 0);
947
+ p.L(150, 500);
948
+ p.stroke();
949
+ p.circle(180, 400, 70);
950
+ p.stroke();
951
+ p.circle(400, 100, 60);
952
+ p.stroke();
953
+ p.circle(560, 100, 60);
954
+ p.stroke();
955
+ }
956
+ },
957
+ // ‱ PER TEN THOUSAND SIGN (U+2031)
958
+ [0x2031]: {
959
+ width: 700,
960
+ draw: (p) => {
961
+ p.lineWidth(35);
962
+ p.M(550, 0);
963
+ p.L(150, 500);
964
+ p.stroke();
965
+ p.circle(180, 400, 60);
966
+ p.stroke();
967
+ p.circle(350, 100, 50);
968
+ p.stroke();
969
+ p.circle(470, 100, 50);
970
+ p.stroke();
971
+ p.circle(590, 100, 50);
972
+ p.stroke();
973
+ }
974
+ },
975
+ // ′ PRIME (U+2032)
976
+ [0x2032]: {
977
+ width: 300,
978
+ draw: (p) => {
979
+ p.lineWidth(40);
980
+ p.M(180, 500);
981
+ p.L(140, 350);
982
+ p.stroke();
983
+ }
984
+ },
985
+ // ″ DOUBLE PRIME (U+2033)
986
+ [0x2033]: {
987
+ width: 400,
988
+ draw: (p) => {
989
+ p.lineWidth(40);
990
+ p.M(150, 500);
991
+ p.L(110, 350);
992
+ p.stroke();
993
+ p.M(300, 500);
994
+ p.L(260, 350);
995
+ p.stroke();
996
+ }
997
+ },
998
+ // ‹ SINGLE LEFT-POINTING ANGLE QUOTATION MARK (U+2039)
999
+ [0x2039]: {
1000
+ width: 350,
1001
+ draw: (p) => {
1002
+ p.lineWidth(40);
1003
+ p.M(250, 400);
1004
+ p.L(100, 250);
1005
+ p.L(250, 100);
1006
+ p.stroke();
1007
+ }
1008
+ },
1009
+ // › SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (U+203A)
1010
+ [0x203a]: {
1011
+ width: 350,
1012
+ draw: (p) => {
1013
+ p.lineWidth(40);
1014
+ p.M(100, 400);
1015
+ p.L(250, 250);
1016
+ p.L(100, 100);
1017
+ p.stroke();
1018
+ }
1019
+ },
1020
+ // ‼ DOUBLE EXCLAMATION MARK (U+203C)
1021
+ [0x203c]: {
1022
+ width: W,
1023
+ draw: (p) => {
1024
+ p.lineWidth(50);
1025
+ p.M(200, 500);
1026
+ p.L(200, 130);
1027
+ p.stroke();
1028
+ p.circle(200, 40, 30);
1029
+ p.fill();
1030
+ p.M(400, 500);
1031
+ p.L(400, 130);
1032
+ p.stroke();
1033
+ p.circle(400, 40, 30);
1034
+ p.fill();
1035
+ }
1036
+ },
1037
+ // ⁄ FRACTION SLASH (U+2044)
1038
+ [0x2044]: {
1039
+ width: W,
1040
+ draw: (p) => {
1041
+ p.lineWidth(40);
1042
+ p.M(450, 500);
1043
+ p.L(150, 0);
1044
+ p.stroke();
1045
+ }
1046
+ },
1047
+ // ⁏ REVERSED SEMICOLON (U+204F)
1048
+ [0x204f]: {
1049
+ width: 300,
1050
+ draw: (p) => {
1051
+ p.circle(150, 350, 35);
1052
+ p.fill();
1053
+ p.circle(150, 130, 35);
1054
+ p.fill();
1055
+ p.lineWidth(30);
1056
+ p.M(150, 95);
1057
+ p.C(180, 50, 200, 20, 220, 0);
1058
+ p.stroke();
1059
+ }
1060
+ }
1061
+ };
1062
+ // =============================================================================
1063
+ // Additional Arrows (U+2190–U+21FF)
1064
+ // =============================================================================
1065
+ export const ARROWS_EXT = {
1066
+ // ↚ LEFTWARDS ARROW WITH STROKE
1067
+ [0x219a]: {
1068
+ width: W,
1069
+ draw: (p) => {
1070
+ p.lineWidth(45);
1071
+ p.M(500, 250);
1072
+ p.L(100, 250);
1073
+ p.stroke();
1074
+ p.M(100, 250);
1075
+ p.L(250, 380);
1076
+ p.L(250, 120);
1077
+ p.Z();
1078
+ p.fill();
1079
+ p.lineWidth(50);
1080
+ p.M(350, 120);
1081
+ p.L(250, 380);
1082
+ p.stroke();
1083
+ }
1084
+ },
1085
+ // ↛ RIGHTWARDS ARROW WITH STROKE
1086
+ [0x219b]: {
1087
+ width: W,
1088
+ draw: (p) => {
1089
+ p.lineWidth(45);
1090
+ p.M(100, 250);
1091
+ p.L(500, 250);
1092
+ p.stroke();
1093
+ p.M(500, 250);
1094
+ p.L(350, 380);
1095
+ p.L(350, 120);
1096
+ p.Z();
1097
+ p.fill();
1098
+ p.lineWidth(50);
1099
+ p.M(250, 120);
1100
+ p.L(350, 380);
1101
+ p.stroke();
1102
+ }
1103
+ },
1104
+ // ↝ RIGHTWARDS WAVE ARROW
1105
+ [0x219d]: {
1106
+ width: W,
1107
+ draw: (p) => {
1108
+ p.lineWidth(45);
1109
+ p.M(100, 250);
1110
+ p.C(200, 350, 300, 150, 400, 250);
1111
+ p.L(500, 250);
1112
+ p.stroke();
1113
+ p.M(500, 250);
1114
+ p.L(400, 330);
1115
+ p.L(400, 170);
1116
+ p.Z();
1117
+ p.fill();
1118
+ }
1119
+ },
1120
+ // ↞ LEFTWARDS TWO HEADED ARROW
1121
+ [0x219e]: {
1122
+ width: W,
1123
+ draw: (p) => {
1124
+ p.lineWidth(45);
1125
+ p.M(500, 250);
1126
+ p.L(100, 250);
1127
+ p.stroke();
1128
+ p.M(100, 250);
1129
+ p.L(220, 370);
1130
+ p.L(220, 130);
1131
+ p.Z();
1132
+ p.fill();
1133
+ p.M(200, 250);
1134
+ p.L(320, 370);
1135
+ p.L(320, 130);
1136
+ p.Z();
1137
+ p.fill();
1138
+ }
1139
+ },
1140
+ // ↟ UPWARDS TWO HEADED ARROW
1141
+ [0x219f]: {
1142
+ width: W,
1143
+ draw: (p) => {
1144
+ p.lineWidth(45);
1145
+ p.M(300, 50);
1146
+ p.L(300, 470);
1147
+ p.stroke();
1148
+ p.M(300, 470);
1149
+ p.L(180, 350);
1150
+ p.L(420, 350);
1151
+ p.Z();
1152
+ p.fill();
1153
+ p.M(300, 370);
1154
+ p.L(180, 250);
1155
+ p.L(420, 250);
1156
+ p.Z();
1157
+ p.fill();
1158
+ }
1159
+ },
1160
+ // ↠ RIGHTWARDS TWO HEADED ARROW
1161
+ [0x21a0]: {
1162
+ width: W,
1163
+ draw: (p) => {
1164
+ p.lineWidth(45);
1165
+ p.M(100, 250);
1166
+ p.L(500, 250);
1167
+ p.stroke();
1168
+ p.M(500, 250);
1169
+ p.L(380, 370);
1170
+ p.L(380, 130);
1171
+ p.Z();
1172
+ p.fill();
1173
+ p.M(400, 250);
1174
+ p.L(280, 370);
1175
+ p.L(280, 130);
1176
+ p.Z();
1177
+ p.fill();
1178
+ }
1179
+ },
1180
+ // ↡ DOWNWARDS TWO HEADED ARROW
1181
+ [0x21a1]: {
1182
+ width: W,
1183
+ draw: (p) => {
1184
+ p.lineWidth(45);
1185
+ p.M(300, 450);
1186
+ p.L(300, 30);
1187
+ p.stroke();
1188
+ p.M(300, 30);
1189
+ p.L(180, 150);
1190
+ p.L(420, 150);
1191
+ p.Z();
1192
+ p.fill();
1193
+ p.M(300, 130);
1194
+ p.L(180, 250);
1195
+ p.L(420, 250);
1196
+ p.Z();
1197
+ p.fill();
1198
+ }
1199
+ },
1200
+ // ↤ LEFTWARDS ARROW FROM BAR
1201
+ [0x21a4]: {
1202
+ width: W,
1203
+ draw: (p) => {
1204
+ p.lineWidth(45);
1205
+ p.M(480, 250);
1206
+ p.L(130, 250);
1207
+ p.stroke();
1208
+ p.M(130, 250);
1209
+ p.L(260, 370);
1210
+ p.L(260, 130);
1211
+ p.Z();
1212
+ p.fill();
1213
+ p.M(500, 100);
1214
+ p.L(500, 400);
1215
+ p.stroke();
1216
+ }
1217
+ },
1218
+ // ↥ UPWARDS ARROW FROM BAR
1219
+ [0x21a5]: {
1220
+ width: W,
1221
+ draw: (p) => {
1222
+ p.lineWidth(45);
1223
+ p.M(300, 30);
1224
+ p.L(300, 420);
1225
+ p.stroke();
1226
+ p.M(300, 420);
1227
+ p.L(180, 290);
1228
+ p.L(420, 290);
1229
+ p.Z();
1230
+ p.fill();
1231
+ p.M(150, 30);
1232
+ p.L(450, 30);
1233
+ p.stroke();
1234
+ }
1235
+ },
1236
+ // ↦ RIGHTWARDS ARROW FROM BAR
1237
+ [0x21a6]: {
1238
+ width: W,
1239
+ draw: (p) => {
1240
+ p.lineWidth(45);
1241
+ p.M(120, 250);
1242
+ p.L(470, 250);
1243
+ p.stroke();
1244
+ p.M(470, 250);
1245
+ p.L(340, 370);
1246
+ p.L(340, 130);
1247
+ p.Z();
1248
+ p.fill();
1249
+ p.M(100, 100);
1250
+ p.L(100, 400);
1251
+ p.stroke();
1252
+ }
1253
+ },
1254
+ // ↧ DOWNWARDS ARROW FROM BAR
1255
+ [0x21a7]: {
1256
+ width: W,
1257
+ draw: (p) => {
1258
+ p.lineWidth(45);
1259
+ p.M(300, 470);
1260
+ p.L(300, 80);
1261
+ p.stroke();
1262
+ p.M(300, 80);
1263
+ p.L(180, 210);
1264
+ p.L(420, 210);
1265
+ p.Z();
1266
+ p.fill();
1267
+ p.M(150, 470);
1268
+ p.L(450, 470);
1269
+ p.stroke();
1270
+ }
1271
+ },
1272
+ // ↰ UPWARDS ARROW WITH TIP LEFTWARDS
1273
+ [0x21b0]: {
1274
+ width: W,
1275
+ draw: (p) => {
1276
+ p.lineWidth(45);
1277
+ p.M(400, 50);
1278
+ p.L(400, 350);
1279
+ p.L(150, 350);
1280
+ p.stroke();
1281
+ p.M(400, 450);
1282
+ p.L(280, 330);
1283
+ p.L(520, 330);
1284
+ p.Z();
1285
+ p.fill();
1286
+ }
1287
+ },
1288
+ // ↱ UPWARDS ARROW WITH TIP RIGHTWARDS
1289
+ [0x21b1]: {
1290
+ width: W,
1291
+ draw: (p) => {
1292
+ p.lineWidth(45);
1293
+ p.M(200, 50);
1294
+ p.L(200, 350);
1295
+ p.L(450, 350);
1296
+ p.stroke();
1297
+ p.M(200, 450);
1298
+ p.L(80, 330);
1299
+ p.L(320, 330);
1300
+ p.Z();
1301
+ p.fill();
1302
+ }
1303
+ },
1304
+ // ↲ DOWNWARDS ARROW WITH TIP LEFTWARDS
1305
+ [0x21b2]: {
1306
+ width: W,
1307
+ draw: (p) => {
1308
+ p.lineWidth(45);
1309
+ p.M(400, 450);
1310
+ p.L(400, 150);
1311
+ p.L(150, 150);
1312
+ p.stroke();
1313
+ p.M(400, 50);
1314
+ p.L(280, 170);
1315
+ p.L(520, 170);
1316
+ p.Z();
1317
+ p.fill();
1318
+ }
1319
+ },
1320
+ // ↳ DOWNWARDS ARROW WITH TIP RIGHTWARDS
1321
+ [0x21b3]: {
1322
+ width: W,
1323
+ draw: (p) => {
1324
+ p.lineWidth(45);
1325
+ p.M(200, 450);
1326
+ p.L(200, 150);
1327
+ p.L(450, 150);
1328
+ p.stroke();
1329
+ p.M(200, 50);
1330
+ p.L(80, 170);
1331
+ p.L(320, 170);
1332
+ p.Z();
1333
+ p.fill();
1334
+ }
1335
+ },
1336
+ // ↵ DOWNWARDS ARROW WITH CORNER LEFTWARDS (return key symbol)
1337
+ [0x21b5]: {
1338
+ width: W,
1339
+ draw: (p) => {
1340
+ p.lineWidth(45);
1341
+ p.M(450, 450);
1342
+ p.L(450, 150);
1343
+ p.L(150, 150);
1344
+ p.stroke();
1345
+ p.M(150, 150);
1346
+ p.L(280, 260);
1347
+ p.L(280, 40);
1348
+ p.Z();
1349
+ p.fill();
1350
+ }
1351
+ },
1352
+ // ↺ ANTICLOCKWISE OPEN CIRCLE ARROW
1353
+ [0x21ba]: {
1354
+ width: W,
1355
+ draw: (p) => {
1356
+ p.lineWidth(45);
1357
+ p.M(160, 350);
1358
+ p.C(160, 450, 300, 500, 400, 450);
1359
+ p.C(500, 400, 520, 250, 450, 150);
1360
+ p.C(380, 50, 200, 50, 160, 150);
1361
+ p.stroke();
1362
+ p.M(160, 350);
1363
+ p.L(80, 250);
1364
+ p.L(250, 270);
1365
+ p.Z();
1366
+ p.fill();
1367
+ }
1368
+ },
1369
+ // ↻ CLOCKWISE OPEN CIRCLE ARROW
1370
+ [0x21bb]: {
1371
+ width: W,
1372
+ draw: (p) => {
1373
+ p.lineWidth(45);
1374
+ p.M(440, 350);
1375
+ p.C(440, 450, 300, 500, 200, 450);
1376
+ p.C(100, 400, 80, 250, 150, 150);
1377
+ p.C(220, 50, 400, 50, 440, 150);
1378
+ p.stroke();
1379
+ p.M(440, 350);
1380
+ p.L(520, 250);
1381
+ p.L(350, 270);
1382
+ p.Z();
1383
+ p.fill();
1384
+ }
1385
+ }
1386
+ };
1387
+ // =============================================================================
1388
+ // Additional Math Operators (U+2200–U+22FF)
1389
+ // =============================================================================
1390
+ export const MATH_EXT = {
1391
+ // ∓ MINUS-OR-PLUS
1392
+ [0x2213]: {
1393
+ width: W,
1394
+ draw: (p) => {
1395
+ p.lineWidth(50);
1396
+ p.M(120, 350);
1397
+ p.L(480, 350);
1398
+ p.stroke();
1399
+ p.M(300, 200);
1400
+ p.L(300, 0);
1401
+ p.stroke();
1402
+ p.M(120, 100);
1403
+ p.L(480, 100);
1404
+ p.stroke();
1405
+ }
1406
+ },
1407
+ // ∙ BULLET OPERATOR
1408
+ [0x2219]: {
1409
+ width: W,
1410
+ draw: (p) => {
1411
+ p.circle(300, 250, 50);
1412
+ p.fill();
1413
+ }
1414
+ },
1415
+ // ∝ PROPORTIONAL TO
1416
+ [0x221d]: {
1417
+ width: W,
1418
+ draw: (p) => {
1419
+ p.lineWidth(45);
1420
+ p.M(80, 250);
1421
+ p.C(80, 450, 250, 450, 300, 250);
1422
+ p.C(350, 50, 520, 50, 520, 250);
1423
+ p.stroke();
1424
+ }
1425
+ },
1426
+ // ∟ RIGHT ANGLE
1427
+ [0x221f]: {
1428
+ width: W,
1429
+ draw: (p) => {
1430
+ p.lineWidth(50);
1431
+ p.M(120, 450);
1432
+ p.L(120, 50);
1433
+ p.L(480, 50);
1434
+ p.stroke();
1435
+ }
1436
+ },
1437
+ // ∣ DIVIDES
1438
+ [0x2223]: {
1439
+ width: W,
1440
+ draw: (p) => {
1441
+ p.lineWidth(50);
1442
+ p.M(300, 0);
1443
+ p.L(300, 500);
1444
+ p.stroke();
1445
+ }
1446
+ },
1447
+ // ∤ DOES NOT DIVIDE
1448
+ [0x2224]: {
1449
+ width: W,
1450
+ draw: (p) => {
1451
+ p.lineWidth(50);
1452
+ p.M(300, 0);
1453
+ p.L(300, 500);
1454
+ p.stroke();
1455
+ p.M(200, 100);
1456
+ p.L(400, 400);
1457
+ p.stroke();
1458
+ }
1459
+ },
1460
+ // ∥ PARALLEL TO
1461
+ [0x2225]: {
1462
+ width: W,
1463
+ draw: (p) => {
1464
+ p.lineWidth(45);
1465
+ p.M(220, 0);
1466
+ p.L(220, 500);
1467
+ p.stroke();
1468
+ p.M(380, 0);
1469
+ p.L(380, 500);
1470
+ p.stroke();
1471
+ }
1472
+ },
1473
+ // ∦ NOT PARALLEL TO
1474
+ [0x2226]: {
1475
+ width: W,
1476
+ draw: (p) => {
1477
+ p.lineWidth(45);
1478
+ p.M(220, 0);
1479
+ p.L(220, 500);
1480
+ p.stroke();
1481
+ p.M(380, 0);
1482
+ p.L(380, 500);
1483
+ p.stroke();
1484
+ p.M(150, 100);
1485
+ p.L(450, 400);
1486
+ p.stroke();
1487
+ }
1488
+ },
1489
+ // ∘ RING OPERATOR
1490
+ [0x2218]: {
1491
+ width: W,
1492
+ draw: (p) => {
1493
+ p.lineWidth(35);
1494
+ p.circle(300, 250, 70);
1495
+ p.stroke();
1496
+ }
1497
+ },
1498
+ // ∴ THEREFORE
1499
+ [0x2234]: {
1500
+ width: W,
1501
+ draw: (p) => {
1502
+ p.circle(300, 400, 40);
1503
+ p.fill();
1504
+ p.circle(180, 150, 40);
1505
+ p.fill();
1506
+ p.circle(420, 150, 40);
1507
+ p.fill();
1508
+ }
1509
+ },
1510
+ // ∵ BECAUSE
1511
+ [0x2235]: {
1512
+ width: W,
1513
+ draw: (p) => {
1514
+ p.circle(300, 100, 40);
1515
+ p.fill();
1516
+ p.circle(180, 350, 40);
1517
+ p.fill();
1518
+ p.circle(420, 350, 40);
1519
+ p.fill();
1520
+ }
1521
+ },
1522
+ // ∶ RATIO
1523
+ [0x2236]: {
1524
+ width: 300,
1525
+ draw: (p) => {
1526
+ p.circle(150, 350, 40);
1527
+ p.fill();
1528
+ p.circle(150, 150, 40);
1529
+ p.fill();
1530
+ }
1531
+ },
1532
+ // ∷ PROPORTION
1533
+ [0x2237]: {
1534
+ width: W,
1535
+ draw: (p) => {
1536
+ p.circle(200, 350, 35);
1537
+ p.fill();
1538
+ p.circle(200, 150, 35);
1539
+ p.fill();
1540
+ p.circle(400, 350, 35);
1541
+ p.fill();
1542
+ p.circle(400, 150, 35);
1543
+ p.fill();
1544
+ }
1545
+ },
1546
+ // ≌ ALL EQUAL TO
1547
+ [0x224c]: {
1548
+ width: W,
1549
+ draw: (p) => {
1550
+ p.lineWidth(40);
1551
+ p.M(120, 400);
1552
+ p.C(200, 460, 400, 460, 480, 400);
1553
+ p.stroke();
1554
+ p.M(120, 280);
1555
+ p.C(200, 340, 400, 340, 480, 280);
1556
+ p.stroke();
1557
+ p.M(120, 160);
1558
+ p.L(480, 160);
1559
+ p.stroke();
1560
+ }
1561
+ },
1562
+ // ≅ APPROXIMATELY EQUAL TO
1563
+ [0x2245]: {
1564
+ width: W,
1565
+ draw: (p) => {
1566
+ p.lineWidth(45);
1567
+ p.M(120, 350);
1568
+ p.C(200, 430, 400, 430, 480, 350);
1569
+ p.stroke();
1570
+ p.M(120, 180);
1571
+ p.L(480, 180);
1572
+ p.stroke();
1573
+ }
1574
+ },
1575
+ // ≜ DELTA EQUAL TO
1576
+ [0x225c]: {
1577
+ width: W,
1578
+ draw: (p) => {
1579
+ p.lineWidth(40);
1580
+ p.M(120, 200);
1581
+ p.L(480, 200);
1582
+ p.stroke();
1583
+ p.M(120, 120);
1584
+ p.L(480, 120);
1585
+ p.stroke();
1586
+ p.M(300, 450);
1587
+ p.L(200, 280);
1588
+ p.L(400, 280);
1589
+ p.Z();
1590
+ p.stroke();
1591
+ }
1592
+ },
1593
+ // ≪ MUCH LESS-THAN
1594
+ [0x226a]: {
1595
+ width: W,
1596
+ draw: (p) => {
1597
+ p.lineWidth(45);
1598
+ p.M(350, 420);
1599
+ p.L(100, 250);
1600
+ p.L(350, 80);
1601
+ p.stroke();
1602
+ p.M(500, 420);
1603
+ p.L(250, 250);
1604
+ p.L(500, 80);
1605
+ p.stroke();
1606
+ }
1607
+ },
1608
+ // ≫ MUCH GREATER-THAN
1609
+ [0x226b]: {
1610
+ width: W,
1611
+ draw: (p) => {
1612
+ p.lineWidth(45);
1613
+ p.M(100, 420);
1614
+ p.L(350, 250);
1615
+ p.L(100, 80);
1616
+ p.stroke();
1617
+ p.M(250, 420);
1618
+ p.L(500, 250);
1619
+ p.L(250, 80);
1620
+ p.stroke();
1621
+ }
1622
+ },
1623
+ // ⊄ NOT A SUBSET OF
1624
+ [0x2284]: {
1625
+ width: W,
1626
+ draw: (p) => {
1627
+ p.lineWidth(45);
1628
+ p.M(470, 450);
1629
+ p.C(200, 450, 130, 350, 130, 250);
1630
+ p.C(130, 150, 200, 50, 470, 50);
1631
+ p.stroke();
1632
+ p.M(200, 50);
1633
+ p.L(400, 450);
1634
+ p.stroke();
1635
+ }
1636
+ },
1637
+ // ⊅ NOT A SUPERSET OF
1638
+ [0x2285]: {
1639
+ width: W,
1640
+ draw: (p) => {
1641
+ p.lineWidth(45);
1642
+ p.M(130, 450);
1643
+ p.C(400, 450, 470, 350, 470, 250);
1644
+ p.C(470, 150, 400, 50, 130, 50);
1645
+ p.stroke();
1646
+ p.M(200, 50);
1647
+ p.L(400, 450);
1648
+ p.stroke();
1649
+ }
1650
+ },
1651
+ // ⊆ SUBSET OF OR EQUAL TO
1652
+ [0x2286]: {
1653
+ width: W,
1654
+ draw: (p) => {
1655
+ p.lineWidth(45);
1656
+ p.M(470, 420);
1657
+ p.C(200, 420, 130, 340, 130, 250);
1658
+ p.C(130, 160, 200, 80, 470, 80);
1659
+ p.stroke();
1660
+ p.M(130, 30);
1661
+ p.L(470, 30);
1662
+ p.stroke();
1663
+ }
1664
+ },
1665
+ // ⊇ SUPERSET OF OR EQUAL TO
1666
+ [0x2287]: {
1667
+ width: W,
1668
+ draw: (p) => {
1669
+ p.lineWidth(45);
1670
+ p.M(130, 420);
1671
+ p.C(400, 420, 470, 340, 470, 250);
1672
+ p.C(470, 160, 400, 80, 130, 80);
1673
+ p.stroke();
1674
+ p.M(130, 30);
1675
+ p.L(470, 30);
1676
+ p.stroke();
1677
+ }
1678
+ },
1679
+ // ⊥ UP TACK (perpendicular)
1680
+ [0x22a5]: {
1681
+ width: W,
1682
+ draw: (p) => {
1683
+ p.lineWidth(50);
1684
+ p.M(120, 50);
1685
+ p.L(480, 50);
1686
+ p.stroke();
1687
+ p.M(300, 50);
1688
+ p.L(300, 450);
1689
+ p.stroke();
1690
+ }
1691
+ },
1692
+ // ⊿ RIGHT TRIANGLE
1693
+ [0x22bf]: {
1694
+ width: W,
1695
+ draw: (p) => {
1696
+ p.lineWidth(45);
1697
+ p.M(120, 50);
1698
+ p.L(480, 50);
1699
+ p.L(120, 450);
1700
+ p.Z();
1701
+ p.stroke();
1702
+ }
1703
+ }
1704
+ };
1705
+ // =============================================================================
1706
+ // Additional Misc Symbols (U+2600–U+26FF)
1707
+ // =============================================================================
1708
+ export const MISC_EXT = {
1709
+ // ☁ CLOUD
1710
+ [0x2601]: {
1711
+ width: W,
1712
+ draw: (p) => {
1713
+ p.lineWidth(40);
1714
+ p.M(120, 200);
1715
+ p.C(120, 350, 200, 400, 280, 400);
1716
+ p.C(300, 470, 400, 470, 430, 400);
1717
+ p.C(500, 400, 520, 300, 480, 200);
1718
+ p.L(120, 200);
1719
+ p.stroke();
1720
+ }
1721
+ },
1722
+ // ☎ BLACK TELEPHONE — simplified
1723
+ [0x260e]: {
1724
+ width: W,
1725
+ draw: (p) => {
1726
+ p.lineWidth(45);
1727
+ p.M(120, 100);
1728
+ p.C(120, 50, 480, 50, 480, 100);
1729
+ p.stroke();
1730
+ p.M(150, 100);
1731
+ p.C(150, 350, 200, 450, 250, 450);
1732
+ p.stroke();
1733
+ p.M(450, 100);
1734
+ p.C(450, 350, 400, 450, 350, 450);
1735
+ p.stroke();
1736
+ }
1737
+ },
1738
+ // ☮ PEACE SYMBOL
1739
+ [0x262e]: {
1740
+ width: W,
1741
+ draw: (p) => {
1742
+ p.lineWidth(45);
1743
+ p.circle(300, 250, 220);
1744
+ p.stroke();
1745
+ p.M(300, 470);
1746
+ p.L(300, 30);
1747
+ p.stroke();
1748
+ p.M(300, 250);
1749
+ p.L(145, 95);
1750
+ p.stroke();
1751
+ p.M(300, 250);
1752
+ p.L(455, 95);
1753
+ p.stroke();
1754
+ }
1755
+ },
1756
+ // ☯ YIN YANG
1757
+ [0x262f]: {
1758
+ width: W,
1759
+ draw: (p) => {
1760
+ p.lineWidth(40);
1761
+ p.circle(300, 250, 220);
1762
+ p.stroke();
1763
+ p.circle(300, 350, 20);
1764
+ p.fill();
1765
+ p.circle(300, 150, 20);
1766
+ p.fill();
1767
+ }
1768
+ },
1769
+ // ☹ WHITE FROWNING FACE
1770
+ [0x2639]: {
1771
+ width: W,
1772
+ draw: (p) => {
1773
+ p.lineWidth(40);
1774
+ p.circle(300, 250, 220);
1775
+ p.stroke();
1776
+ p.circle(210, 330, 25);
1777
+ p.fill();
1778
+ p.circle(390, 330, 25);
1779
+ p.fill();
1780
+ p.M(200, 130);
1781
+ p.C(250, 80, 350, 80, 400, 130);
1782
+ p.stroke();
1783
+ }
1784
+ },
1785
+ // ☺ WHITE SMILING FACE
1786
+ [0x263a]: {
1787
+ width: W,
1788
+ draw: (p) => {
1789
+ p.lineWidth(40);
1790
+ p.circle(300, 250, 220);
1791
+ p.stroke();
1792
+ p.circle(210, 330, 25);
1793
+ p.fill();
1794
+ p.circle(390, 330, 25);
1795
+ p.fill();
1796
+ p.M(190, 170);
1797
+ p.C(220, 110, 380, 110, 410, 170);
1798
+ p.stroke();
1799
+ }
1800
+ },
1801
+ // ☻ BLACK SMILING FACE
1802
+ [0x263b]: {
1803
+ width: W,
1804
+ draw: (p) => {
1805
+ p.circle(300, 250, 230);
1806
+ p.fill();
1807
+ }
1808
+ },
1809
+ // ⚐ WHITE FLAG
1810
+ [0x2690]: {
1811
+ width: W,
1812
+ draw: (p) => {
1813
+ p.lineWidth(40);
1814
+ p.M(150, 0);
1815
+ p.L(150, 500);
1816
+ p.stroke();
1817
+ p.M(150, 500);
1818
+ p.L(480, 400);
1819
+ p.L(150, 300);
1820
+ p.stroke();
1821
+ }
1822
+ },
1823
+ // ⚑ BLACK FLAG
1824
+ [0x2691]: {
1825
+ width: W,
1826
+ draw: (p) => {
1827
+ p.lineWidth(40);
1828
+ p.M(150, 0);
1829
+ p.L(150, 500);
1830
+ p.stroke();
1831
+ p.M(150, 500);
1832
+ p.L(480, 400);
1833
+ p.L(150, 300);
1834
+ p.Z();
1835
+ p.fill();
1836
+ }
1837
+ },
1838
+ // ⚠ WARNING SIGN
1839
+ [0x26a0]: {
1840
+ width: W,
1841
+ draw: (p) => {
1842
+ p.lineWidth(45);
1843
+ p.M(300, 480);
1844
+ p.L(80, 50);
1845
+ p.L(520, 50);
1846
+ p.Z();
1847
+ p.stroke();
1848
+ p.lineWidth(40);
1849
+ p.M(300, 350);
1850
+ p.L(300, 180);
1851
+ p.stroke();
1852
+ p.circle(300, 110, 25);
1853
+ p.fill();
1854
+ }
1855
+ },
1856
+ // ⚡ HIGH VOLTAGE SIGN
1857
+ [0x26a1]: {
1858
+ width: W,
1859
+ draw: (p) => {
1860
+ p.M(350, 500);
1861
+ p.L(200, 280);
1862
+ p.L(310, 280);
1863
+ p.L(250, 0);
1864
+ p.L(420, 220);
1865
+ p.L(300, 220);
1866
+ p.L(350, 500);
1867
+ p.Z();
1868
+ p.fill();
1869
+ }
1870
+ },
1871
+ // ⚪ MEDIUM WHITE CIRCLE
1872
+ [0x26aa]: {
1873
+ width: W,
1874
+ draw: (p) => {
1875
+ p.lineWidth(40);
1876
+ p.circle(300, 250, 180);
1877
+ p.stroke();
1878
+ }
1879
+ },
1880
+ // ⚫ MEDIUM BLACK CIRCLE
1881
+ [0x26ab]: {
1882
+ width: W,
1883
+ draw: (p) => {
1884
+ p.circle(300, 250, 180);
1885
+ p.fill();
1886
+ }
1887
+ }
1888
+ };
1889
+ // =============================================================================
1890
+ // Additional Dingbats (U+2700–U+27BF)
1891
+ // =============================================================================
1892
+ export const DING_EXT = {
1893
+ // ✁ UPPER BLADE SCISSORS
1894
+ [0x2701]: {
1895
+ width: W,
1896
+ draw: (p) => {
1897
+ p.lineWidth(35);
1898
+ p.circle(180, 150, 80);
1899
+ p.stroke();
1900
+ p.circle(180, 350, 80);
1901
+ p.stroke();
1902
+ p.M(250, 200);
1903
+ p.L(500, 400);
1904
+ p.stroke();
1905
+ p.M(250, 300);
1906
+ p.L(500, 100);
1907
+ p.stroke();
1908
+ }
1909
+ },
1910
+ // ✂ BLACK SCISSORS
1911
+ [0x2702]: {
1912
+ width: W,
1913
+ draw: (p) => {
1914
+ p.lineWidth(40);
1915
+ p.circle(170, 130, 80);
1916
+ p.stroke();
1917
+ p.circle(170, 370, 80);
1918
+ p.stroke();
1919
+ p.M(240, 180);
1920
+ p.L(520, 420);
1921
+ p.stroke();
1922
+ p.M(240, 320);
1923
+ p.L(520, 80);
1924
+ p.stroke();
1925
+ }
1926
+ },
1927
+ // ✆ TELEPHONE LOCATION SIGN
1928
+ [0x2706]: {
1929
+ width: W,
1930
+ draw: (p) => {
1931
+ p.lineWidth(40);
1932
+ p.circle(300, 250, 220);
1933
+ p.stroke();
1934
+ p.M(200, 150);
1935
+ p.C(200, 100, 400, 100, 400, 150);
1936
+ p.stroke();
1937
+ p.M(220, 150);
1938
+ p.C(220, 350, 250, 400, 280, 400);
1939
+ p.stroke();
1940
+ p.M(380, 150);
1941
+ p.C(380, 350, 350, 400, 320, 400);
1942
+ p.stroke();
1943
+ }
1944
+ },
1945
+ // ✇ TAPE DRIVE — simplified square with circle
1946
+ [0x2707]: {
1947
+ width: W,
1948
+ draw: (p) => {
1949
+ p.lineWidth(35);
1950
+ p.rect(80, 50, 440, 400);
1951
+ p.stroke();
1952
+ p.circle(300, 250, 120);
1953
+ p.stroke();
1954
+ }
1955
+ },
1956
+ // ✈ AIRPLANE — simplified
1957
+ [0x2708]: {
1958
+ width: W,
1959
+ draw: (p) => {
1960
+ p.M(300, 480);
1961
+ p.L(260, 300);
1962
+ p.L(80, 250);
1963
+ p.L(260, 210);
1964
+ p.L(260, 80);
1965
+ p.L(220, 30);
1966
+ p.L(380, 30);
1967
+ p.L(340, 80);
1968
+ p.L(340, 210);
1969
+ p.L(520, 250);
1970
+ p.L(340, 300);
1971
+ p.Z();
1972
+ p.fill();
1973
+ }
1974
+ },
1975
+ // ✉ ENVELOPE
1976
+ [0x2709]: {
1977
+ width: W,
1978
+ draw: (p) => {
1979
+ p.lineWidth(35);
1980
+ p.rect(80, 80, 440, 340);
1981
+ p.stroke();
1982
+ p.M(80, 420);
1983
+ p.L(300, 220);
1984
+ p.L(520, 420);
1985
+ p.stroke();
1986
+ }
1987
+ },
1988
+ // ✎ LOWER RIGHT PENCIL
1989
+ [0x270e]: {
1990
+ width: W,
1991
+ draw: (p) => {
1992
+ p.lineWidth(40);
1993
+ p.M(100, 50);
1994
+ p.L(480, 430);
1995
+ p.stroke();
1996
+ p.M(100, 50);
1997
+ p.L(130, 130);
1998
+ p.stroke();
1999
+ }
2000
+ },
2001
+ // ✏ PENCIL
2002
+ [0x270f]: {
2003
+ width: W,
2004
+ draw: (p) => {
2005
+ p.M(130, 30);
2006
+ p.L(100, 80);
2007
+ p.L(420, 470);
2008
+ p.L(480, 470);
2009
+ p.L(480, 430);
2010
+ p.L(160, 30);
2011
+ p.Z();
2012
+ p.fill();
2013
+ }
2014
+ },
2015
+ // ✐ UPPER RIGHT PENCIL
2016
+ [0x2710]: {
2017
+ width: W,
2018
+ draw: (p) => {
2019
+ p.lineWidth(40);
2020
+ p.M(100, 450);
2021
+ p.L(480, 70);
2022
+ p.stroke();
2023
+ p.M(100, 450);
2024
+ p.L(130, 370);
2025
+ p.stroke();
2026
+ }
2027
+ },
2028
+ // ✝ LATIN CROSS
2029
+ [0x271d]: {
2030
+ width: W,
2031
+ draw: (p) => {
2032
+ p.lineWidth(55);
2033
+ p.M(300, 0);
2034
+ p.L(300, 500);
2035
+ p.stroke();
2036
+ p.M(140, 370);
2037
+ p.L(460, 370);
2038
+ p.stroke();
2039
+ }
2040
+ },
2041
+ // ✞ SHADOWED WHITE LATIN CROSS
2042
+ [0x271e]: {
2043
+ width: W,
2044
+ draw: (p) => {
2045
+ p.lineWidth(65);
2046
+ p.M(300, 0);
2047
+ p.L(300, 500);
2048
+ p.stroke();
2049
+ p.M(130, 370);
2050
+ p.L(470, 370);
2051
+ p.stroke();
2052
+ }
2053
+ },
2054
+ // ✦ BLACK FOUR POINTED STAR
2055
+ [0x2726]: {
2056
+ width: W,
2057
+ draw: (p) => {
2058
+ p.M(300, 480);
2059
+ p.L(230, 280);
2060
+ p.L(80, 250);
2061
+ p.L(230, 220);
2062
+ p.L(300, 20);
2063
+ p.L(370, 220);
2064
+ p.L(520, 250);
2065
+ p.L(370, 280);
2066
+ p.Z();
2067
+ p.fill();
2068
+ }
2069
+ },
2070
+ // ✧ WHITE FOUR POINTED STAR
2071
+ [0x2727]: {
2072
+ width: W,
2073
+ draw: (p) => {
2074
+ p.lineWidth(35);
2075
+ p.M(300, 480);
2076
+ p.L(230, 280);
2077
+ p.L(80, 250);
2078
+ p.L(230, 220);
2079
+ p.L(300, 20);
2080
+ p.L(370, 220);
2081
+ p.L(520, 250);
2082
+ p.L(370, 280);
2083
+ p.Z();
2084
+ p.stroke();
2085
+ }
2086
+ },
2087
+ // ✰ SHADOWED WHITE STAR
2088
+ [0x2730]: {
2089
+ width: W,
2090
+ draw: (p) => {
2091
+ p.lineWidth(40);
2092
+ const cx = 300;
2093
+ const cy = 260;
2094
+ const R = 220;
2095
+ const r = 100;
2096
+ for (let i = 0; i < 5; i++) {
2097
+ const a1 = ((i * 72 - 90) * Math.PI) / 180;
2098
+ const a2 = ((i * 72 + 36 - 90) * Math.PI) / 180;
2099
+ if (i === 0) {
2100
+ p.M(cx + R * Math.cos(a1), cy + R * Math.sin(a1));
2101
+ }
2102
+ else {
2103
+ p.L(cx + R * Math.cos(a1), cy + R * Math.sin(a1));
2104
+ }
2105
+ p.L(cx + r * Math.cos(a2), cy + r * Math.sin(a2));
2106
+ }
2107
+ p.Z();
2108
+ p.stroke();
2109
+ }
2110
+ },
2111
+ // ➔ HEAVY WIDE-HEADED RIGHTWARDS ARROW
2112
+ [0x2794]: {
2113
+ width: W,
2114
+ draw: (p) => {
2115
+ p.M(500, 250);
2116
+ p.L(300, 430);
2117
+ p.L(300, 310);
2118
+ p.L(80, 310);
2119
+ p.L(80, 190);
2120
+ p.L(300, 190);
2121
+ p.L(300, 70);
2122
+ p.Z();
2123
+ p.fill();
2124
+ }
2125
+ },
2126
+ // ➜ HEAVY ROUND-TIPPED RIGHTWARDS ARROW
2127
+ [0x279c]: {
2128
+ width: W,
2129
+ draw: (p) => {
2130
+ p.M(520, 250);
2131
+ p.L(320, 430);
2132
+ p.L(320, 310);
2133
+ p.C(200, 310, 80, 290, 80, 250);
2134
+ p.C(80, 210, 200, 190, 320, 190);
2135
+ p.L(320, 70);
2136
+ p.Z();
2137
+ p.fill();
2138
+ }
2139
+ },
2140
+ // ➡ BLACK RIGHTWARDS ARROW
2141
+ [0x27a1]: {
2142
+ width: W,
2143
+ draw: (p) => {
2144
+ p.M(520, 250);
2145
+ p.L(350, 420);
2146
+ p.L(350, 300);
2147
+ p.L(80, 300);
2148
+ p.L(80, 200);
2149
+ p.L(350, 200);
2150
+ p.L(350, 80);
2151
+ p.Z();
2152
+ p.fill();
2153
+ }
2154
+ }
2155
+ };
2156
+ // =============================================================================
2157
+ // Additional Misc Technical (U+2300–U+23FF)
2158
+ // =============================================================================
2159
+ export const TECH_EXT = {
2160
+ // ⌐ REVERSED NOT SIGN
2161
+ [0x2310]: {
2162
+ width: W,
2163
+ draw: (p) => {
2164
+ p.lineWidth(45);
2165
+ p.M(480, 350);
2166
+ p.L(120, 350);
2167
+ p.L(120, 150);
2168
+ p.stroke();
2169
+ }
2170
+ },
2171
+ // ⌕ TELEPHONE RECORDER
2172
+ [0x2315]: {
2173
+ width: W,
2174
+ draw: (p) => {
2175
+ p.lineWidth(40);
2176
+ p.circle(300, 250, 200);
2177
+ p.stroke();
2178
+ p.circle(300, 250, 50);
2179
+ p.fill();
2180
+ }
2181
+ },
2182
+ // ⌚ WATCH
2183
+ [0x231a]: {
2184
+ width: W,
2185
+ draw: (p) => {
2186
+ p.lineWidth(35);
2187
+ p.rect(150, 50, 300, 400);
2188
+ p.stroke();
2189
+ p.M(300, 450);
2190
+ p.L(300, 500);
2191
+ p.stroke();
2192
+ p.M(300, 50);
2193
+ p.L(300, 0);
2194
+ p.stroke();
2195
+ p.lineWidth(30);
2196
+ p.M(300, 250);
2197
+ p.L(300, 350);
2198
+ p.stroke();
2199
+ p.M(300, 250);
2200
+ p.L(380, 250);
2201
+ p.stroke();
2202
+ }
2203
+ },
2204
+ // ⌨ KEYBOARD
2205
+ [0x2328]: {
2206
+ width: W,
2207
+ draw: (p) => {
2208
+ p.lineWidth(35);
2209
+ p.rect(50, 80, 500, 340);
2210
+ p.stroke();
2211
+ p.lineWidth(20);
2212
+ for (let x = 100; x <= 500; x += 80) {
2213
+ p.M(x, 340);
2214
+ p.L(x + 40, 340);
2215
+ p.L(x + 40, 300);
2216
+ p.L(x, 300);
2217
+ p.Z();
2218
+ p.stroke();
2219
+ }
2220
+ p.M(150, 200);
2221
+ p.L(450, 200);
2222
+ p.L(450, 160);
2223
+ p.L(150, 160);
2224
+ p.Z();
2225
+ p.stroke();
2226
+ }
2227
+ },
2228
+ // ⎛⎜⎝ LEFT PARENTHESIS UPPER/EXTENSION/LOWER — simplified
2229
+ [0x239b]: {
2230
+ width: 300,
2231
+ draw: (p) => {
2232
+ p.lineWidth(40);
2233
+ p.M(250, 500);
2234
+ p.C(100, 400, 100, 300, 150, 250);
2235
+ p.stroke();
2236
+ }
2237
+ },
2238
+ [0x239c]: {
2239
+ width: 300,
2240
+ draw: (p) => {
2241
+ p.lineWidth(40);
2242
+ p.M(150, 500);
2243
+ p.L(150, 0);
2244
+ p.stroke();
2245
+ }
2246
+ },
2247
+ [0x239d]: {
2248
+ width: 300,
2249
+ draw: (p) => {
2250
+ p.lineWidth(40);
2251
+ p.M(150, 250);
2252
+ p.C(100, 200, 100, 100, 250, 0);
2253
+ p.stroke();
2254
+ }
2255
+ },
2256
+ // ⏏ EJECT SYMBOL
2257
+ [0x23cf]: {
2258
+ width: W,
2259
+ draw: (p) => {
2260
+ p.M(120, 150);
2261
+ p.L(480, 150);
2262
+ p.L(300, 420);
2263
+ p.Z();
2264
+ p.fill();
2265
+ p.rect(120, 50, 360, 60);
2266
+ p.fill();
2267
+ }
2268
+ },
2269
+ // ⏩ BLACK RIGHT-POINTING DOUBLE TRIANGLE
2270
+ [0x23e9]: {
2271
+ width: W,
2272
+ draw: (p) => {
2273
+ p.M(80, 430);
2274
+ p.L(300, 250);
2275
+ p.L(80, 70);
2276
+ p.Z();
2277
+ p.fill();
2278
+ p.M(300, 430);
2279
+ p.L(520, 250);
2280
+ p.L(300, 70);
2281
+ p.Z();
2282
+ p.fill();
2283
+ }
2284
+ },
2285
+ // ⏪ BLACK LEFT-POINTING DOUBLE TRIANGLE
2286
+ [0x23ea]: {
2287
+ width: W,
2288
+ draw: (p) => {
2289
+ p.M(520, 430);
2290
+ p.L(300, 250);
2291
+ p.L(520, 70);
2292
+ p.Z();
2293
+ p.fill();
2294
+ p.M(300, 430);
2295
+ p.L(80, 250);
2296
+ p.L(300, 70);
2297
+ p.Z();
2298
+ p.fill();
2299
+ }
2300
+ },
2301
+ // ⏫ BLACK UP-POINTING DOUBLE TRIANGLE
2302
+ [0x23eb]: {
2303
+ width: W,
2304
+ draw: (p) => {
2305
+ p.M(100, 230);
2306
+ p.L(300, 450);
2307
+ p.L(500, 230);
2308
+ p.Z();
2309
+ p.fill();
2310
+ p.M(100, 30);
2311
+ p.L(300, 250);
2312
+ p.L(500, 30);
2313
+ p.Z();
2314
+ p.fill();
2315
+ }
2316
+ },
2317
+ // ⏬ BLACK DOWN-POINTING DOUBLE TRIANGLE
2318
+ [0x23ec]: {
2319
+ width: W,
2320
+ draw: (p) => {
2321
+ p.M(100, 270);
2322
+ p.L(300, 50);
2323
+ p.L(500, 270);
2324
+ p.Z();
2325
+ p.fill();
2326
+ p.M(100, 470);
2327
+ p.L(300, 250);
2328
+ p.L(500, 470);
2329
+ p.Z();
2330
+ p.fill();
2331
+ }
2332
+ },
2333
+ // ⏭ BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR
2334
+ [0x23ed]: {
2335
+ width: W,
2336
+ draw: (p) => {
2337
+ p.M(60, 430);
2338
+ p.L(250, 250);
2339
+ p.L(60, 70);
2340
+ p.Z();
2341
+ p.fill();
2342
+ p.M(250, 430);
2343
+ p.L(440, 250);
2344
+ p.L(250, 70);
2345
+ p.Z();
2346
+ p.fill();
2347
+ p.rect(460, 70, 50, 360);
2348
+ p.fill();
2349
+ }
2350
+ },
2351
+ // ⏮ BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR
2352
+ [0x23ee]: {
2353
+ width: W,
2354
+ draw: (p) => {
2355
+ p.M(540, 430);
2356
+ p.L(350, 250);
2357
+ p.L(540, 70);
2358
+ p.Z();
2359
+ p.fill();
2360
+ p.M(350, 430);
2361
+ p.L(160, 250);
2362
+ p.L(350, 70);
2363
+ p.Z();
2364
+ p.fill();
2365
+ p.rect(90, 70, 50, 360);
2366
+ p.fill();
2367
+ }
2368
+ },
2369
+ // ⏯ BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR
2370
+ [0x23ef]: {
2371
+ width: W,
2372
+ draw: (p) => {
2373
+ p.M(80, 430);
2374
+ p.L(330, 250);
2375
+ p.L(80, 70);
2376
+ p.Z();
2377
+ p.fill();
2378
+ p.rect(370, 70, 50, 360);
2379
+ p.fill();
2380
+ p.rect(460, 70, 50, 360);
2381
+ p.fill();
2382
+ }
2383
+ },
2384
+ // ⏱ STOPWATCH
2385
+ [0x23f1]: {
2386
+ width: W,
2387
+ draw: (p) => {
2388
+ p.lineWidth(40);
2389
+ p.circle(300, 230, 200);
2390
+ p.stroke();
2391
+ p.M(260, 470);
2392
+ p.L(340, 470);
2393
+ p.stroke();
2394
+ p.M(300, 430);
2395
+ p.L(300, 480);
2396
+ p.stroke();
2397
+ p.M(300, 230);
2398
+ p.L(300, 350);
2399
+ p.stroke();
2400
+ p.M(300, 230);
2401
+ p.L(400, 280);
2402
+ p.stroke();
2403
+ }
2404
+ },
2405
+ // ⏲ TIMER CLOCK
2406
+ [0x23f2]: {
2407
+ width: W,
2408
+ draw: (p) => {
2409
+ p.lineWidth(40);
2410
+ p.circle(300, 230, 200);
2411
+ p.stroke();
2412
+ p.M(300, 230);
2413
+ p.L(300, 370);
2414
+ p.stroke();
2415
+ p.M(300, 230);
2416
+ p.L(420, 230);
2417
+ p.stroke();
2418
+ p.M(430, 400);
2419
+ p.L(470, 440);
2420
+ p.stroke();
2421
+ }
2422
+ },
2423
+ // ⏸ DOUBLE VERTICAL BAR (pause)
2424
+ [0x23f8]: {
2425
+ width: W,
2426
+ draw: (p) => {
2427
+ p.rect(140, 60, 100, 380);
2428
+ p.fill();
2429
+ p.rect(360, 60, 100, 380);
2430
+ p.fill();
2431
+ }
2432
+ },
2433
+ // ⏹ BLACK SQUARE FOR STOP
2434
+ [0x23f9]: {
2435
+ width: W,
2436
+ draw: (p) => {
2437
+ p.rect(100, 50, 400, 400);
2438
+ p.fill();
2439
+ }
2440
+ },
2441
+ // ⏺ BLACK CIRCLE FOR RECORD
2442
+ [0x23fa]: {
2443
+ width: W,
2444
+ draw: (p) => {
2445
+ p.circle(300, 250, 200);
2446
+ p.fill();
2447
+ }
2448
+ }
2449
+ };
2450
+ // =============================================================================
2451
+ // Additional Currency Symbols (U+20A0–U+20CF)
2452
+ // =============================================================================
2453
+ export const CURRENCY_EXT = {
2454
+ // ₠ EURO-CURRENCY SIGN
2455
+ [0x20a0]: {
2456
+ width: W,
2457
+ draw: (p) => {
2458
+ p.lineWidth(45);
2459
+ p.M(480, 430);
2460
+ p.C(350, 480, 180, 430, 150, 300);
2461
+ p.C(120, 170, 200, 50, 400, 30);
2462
+ p.stroke();
2463
+ p.M(80, 320);
2464
+ p.L(380, 320);
2465
+ p.stroke();
2466
+ p.M(80, 220);
2467
+ p.L(380, 220);
2468
+ p.stroke();
2469
+ }
2470
+ },
2471
+ // ₡ COLON SIGN
2472
+ [0x20a1]: {
2473
+ width: W,
2474
+ draw: (p) => {
2475
+ p.lineWidth(45);
2476
+ p.M(450, 430);
2477
+ p.C(350, 480, 180, 430, 150, 250);
2478
+ p.C(150, 70, 350, 20, 450, 70);
2479
+ p.stroke();
2480
+ p.M(300, 520);
2481
+ p.L(300, -20);
2482
+ p.stroke();
2483
+ }
2484
+ },
2485
+ // ₢ CRUZEIRO SIGN
2486
+ [0x20a2]: {
2487
+ width: W,
2488
+ draw: (p) => {
2489
+ p.lineWidth(45);
2490
+ p.M(450, 430);
2491
+ p.C(350, 480, 180, 430, 150, 250);
2492
+ p.C(150, 70, 350, 20, 450, 70);
2493
+ p.stroke();
2494
+ p.M(380, 0);
2495
+ p.L(250, 500);
2496
+ p.stroke();
2497
+ }
2498
+ },
2499
+ // ₣ FRENCH FRANC SIGN
2500
+ [0x20a3]: {
2501
+ width: W,
2502
+ draw: (p) => {
2503
+ p.lineWidth(50);
2504
+ p.M(200, 0);
2505
+ p.L(200, 500);
2506
+ p.stroke();
2507
+ p.M(200, 500);
2508
+ p.L(480, 500);
2509
+ p.stroke();
2510
+ p.M(200, 300);
2511
+ p.L(400, 300);
2512
+ p.stroke();
2513
+ p.M(120, 200);
2514
+ p.L(350, 200);
2515
+ p.stroke();
2516
+ }
2517
+ },
2518
+ // ₤ LIRA SIGN
2519
+ [0x20a4]: {
2520
+ width: W,
2521
+ draw: (p) => {
2522
+ p.lineWidth(50);
2523
+ p.M(200, 0);
2524
+ p.C(200, 0, 400, 0, 400, 0);
2525
+ p.stroke();
2526
+ p.M(200, 0);
2527
+ p.L(200, 500);
2528
+ p.C(200, 500, 150, 520, 120, 500);
2529
+ p.stroke();
2530
+ p.M(120, 300);
2531
+ p.L(380, 300);
2532
+ p.stroke();
2533
+ p.M(120, 180);
2534
+ p.L(380, 180);
2535
+ p.stroke();
2536
+ }
2537
+ },
2538
+ // ₥ MILL SIGN
2539
+ [0x20a5]: {
2540
+ width: W,
2541
+ draw: (p) => {
2542
+ p.lineWidth(40);
2543
+ p.M(100, 0);
2544
+ p.L(100, 350);
2545
+ p.L(200, 200);
2546
+ p.L(300, 350);
2547
+ p.L(300, 0);
2548
+ p.stroke();
2549
+ p.M(300, 500);
2550
+ p.L(300, -30);
2551
+ p.stroke();
2552
+ }
2553
+ },
2554
+ // ₦ NAIRA SIGN
2555
+ [0x20a6]: {
2556
+ width: W,
2557
+ draw: (p) => {
2558
+ p.lineWidth(45);
2559
+ p.M(130, 0);
2560
+ p.L(130, 500);
2561
+ p.stroke();
2562
+ p.M(130, 500);
2563
+ p.L(470, 0);
2564
+ p.stroke();
2565
+ p.M(470, 0);
2566
+ p.L(470, 500);
2567
+ p.stroke();
2568
+ p.M(80, 350);
2569
+ p.L(520, 350);
2570
+ p.stroke();
2571
+ p.M(80, 200);
2572
+ p.L(520, 200);
2573
+ p.stroke();
2574
+ }
2575
+ },
2576
+ // ₧ PESETA SIGN
2577
+ [0x20a7]: {
2578
+ width: W,
2579
+ draw: (p) => {
2580
+ p.lineWidth(45);
2581
+ p.M(150, 0);
2582
+ p.L(150, 500);
2583
+ p.stroke();
2584
+ p.M(150, 500);
2585
+ p.L(350, 500);
2586
+ p.C(450, 500, 480, 400, 350, 350);
2587
+ p.L(150, 350);
2588
+ p.stroke();
2589
+ p.M(350, 0);
2590
+ p.L(500, 0);
2591
+ p.stroke();
2592
+ }
2593
+ },
2594
+ // ₨ RUPEE SIGN
2595
+ [0x20a8]: {
2596
+ width: W,
2597
+ draw: (p) => {
2598
+ p.lineWidth(45);
2599
+ p.M(150, 0);
2600
+ p.L(150, 500);
2601
+ p.stroke();
2602
+ p.M(150, 500);
2603
+ p.L(350, 500);
2604
+ p.C(450, 500, 480, 400, 350, 350);
2605
+ p.L(150, 350);
2606
+ p.stroke();
2607
+ p.M(280, 350);
2608
+ p.L(450, 0);
2609
+ p.stroke();
2610
+ }
2611
+ },
2612
+ // ₩ WON SIGN
2613
+ [0x20a9]: {
2614
+ width: W,
2615
+ draw: (p) => {
2616
+ p.lineWidth(40);
2617
+ p.M(60, 500);
2618
+ p.L(180, 0);
2619
+ p.L(300, 350);
2620
+ p.L(420, 0);
2621
+ p.L(540, 500);
2622
+ p.stroke();
2623
+ p.M(80, 250);
2624
+ p.L(520, 250);
2625
+ p.stroke();
2626
+ p.M(80, 150);
2627
+ p.L(520, 150);
2628
+ p.stroke();
2629
+ }
2630
+ },
2631
+ // ₪ NEW SHEQEL SIGN
2632
+ [0x20aa]: {
2633
+ width: W,
2634
+ draw: (p) => {
2635
+ p.lineWidth(45);
2636
+ p.M(150, 500);
2637
+ p.L(150, 100);
2638
+ p.C(150, 30, 250, 0, 300, 50);
2639
+ p.stroke();
2640
+ p.M(450, 0);
2641
+ p.L(450, 400);
2642
+ p.C(450, 470, 350, 500, 300, 450);
2643
+ p.stroke();
2644
+ }
2645
+ },
2646
+ // ₫ DONG SIGN
2647
+ [0x20ab]: {
2648
+ width: W,
2649
+ draw: (p) => {
2650
+ p.lineWidth(45);
2651
+ p.M(250, 50);
2652
+ p.L(250, 500);
2653
+ p.stroke();
2654
+ p.M(250, 400);
2655
+ p.L(350, 400);
2656
+ p.C(470, 400, 470, 200, 350, 200);
2657
+ p.L(250, 200);
2658
+ p.stroke();
2659
+ p.M(150, 50);
2660
+ p.L(400, 50);
2661
+ p.stroke();
2662
+ p.M(170, 120);
2663
+ p.L(420, 120);
2664
+ p.stroke();
2665
+ }
2666
+ },
2667
+ // ₮ TUGRIK SIGN
2668
+ [0x20ae]: {
2669
+ width: W,
2670
+ draw: (p) => {
2671
+ p.lineWidth(50);
2672
+ p.M(120, 500);
2673
+ p.L(480, 500);
2674
+ p.stroke();
2675
+ p.M(300, 500);
2676
+ p.L(300, 0);
2677
+ p.stroke();
2678
+ p.M(120, 350);
2679
+ p.L(480, 350);
2680
+ p.stroke();
2681
+ p.M(120, 200);
2682
+ p.L(480, 200);
2683
+ p.stroke();
2684
+ }
2685
+ },
2686
+ // ₱ PESO SIGN
2687
+ [0x20b1]: {
2688
+ width: W,
2689
+ draw: (p) => {
2690
+ p.lineWidth(45);
2691
+ p.M(150, 0);
2692
+ p.L(150, 500);
2693
+ p.stroke();
2694
+ p.M(150, 500);
2695
+ p.L(350, 500);
2696
+ p.C(480, 500, 480, 300, 350, 300);
2697
+ p.L(150, 300);
2698
+ p.stroke();
2699
+ p.M(100, 420);
2700
+ p.L(450, 420);
2701
+ p.stroke();
2702
+ }
2703
+ },
2704
+ // ₲ GUARANI SIGN
2705
+ [0x20b2]: {
2706
+ width: W,
2707
+ draw: (p) => {
2708
+ p.lineWidth(45);
2709
+ p.M(450, 400);
2710
+ p.C(400, 480, 200, 480, 150, 350);
2711
+ p.C(100, 200, 200, 50, 400, 50);
2712
+ p.L(400, 250);
2713
+ p.L(250, 250);
2714
+ p.stroke();
2715
+ p.M(300, 520);
2716
+ p.L(300, 0);
2717
+ p.stroke();
2718
+ }
2719
+ },
2720
+ // ₳ AUSTRAL SIGN
2721
+ [0x20b3]: {
2722
+ width: W,
2723
+ draw: (p) => {
2724
+ p.lineWidth(45);
2725
+ p.M(100, 0);
2726
+ p.L(300, 500);
2727
+ p.L(500, 0);
2728
+ p.stroke();
2729
+ p.M(160, 180);
2730
+ p.L(440, 180);
2731
+ p.stroke();
2732
+ p.M(180, 280);
2733
+ p.L(420, 280);
2734
+ p.stroke();
2735
+ }
2736
+ },
2737
+ // ₴ HRYVNIA SIGN
2738
+ [0x20b4]: {
2739
+ width: W,
2740
+ draw: (p) => {
2741
+ p.lineWidth(45);
2742
+ p.M(420, 450);
2743
+ p.C(380, 500, 200, 500, 180, 400);
2744
+ p.C(160, 300, 400, 250, 420, 150);
2745
+ p.C(440, 50, 250, 0, 180, 50);
2746
+ p.stroke();
2747
+ p.M(100, 330);
2748
+ p.L(480, 330);
2749
+ p.stroke();
2750
+ p.M(100, 220);
2751
+ p.L(480, 220);
2752
+ p.stroke();
2753
+ }
2754
+ },
2755
+ // ₶ LIVRE TOURNOIS SIGN
2756
+ [0x20b6]: {
2757
+ width: W,
2758
+ draw: (p) => {
2759
+ p.lineWidth(45);
2760
+ p.M(250, 0);
2761
+ p.L(250, 480);
2762
+ p.C(250, 500, 200, 520, 180, 500);
2763
+ p.stroke();
2764
+ p.M(250, 350);
2765
+ p.L(450, 350);
2766
+ p.stroke();
2767
+ }
2768
+ },
2769
+ // ₸ TENGE SIGN
2770
+ [0x20b8]: {
2771
+ width: W,
2772
+ draw: (p) => {
2773
+ p.lineWidth(50);
2774
+ p.M(100, 500);
2775
+ p.L(500, 500);
2776
+ p.stroke();
2777
+ p.M(100, 400);
2778
+ p.L(500, 400);
2779
+ p.stroke();
2780
+ p.M(300, 400);
2781
+ p.L(300, 0);
2782
+ p.stroke();
2783
+ }
2784
+ }
2785
+ };
2786
+ // =============================================================================
2787
+ // Misc Math Symbols-A (U+27C0–U+27EF)
2788
+ // =============================================================================
2789
+ export const MATH_SYM_A = {
2790
+ // ⟨ MATHEMATICAL LEFT ANGLE BRACKET
2791
+ [0x27e8]: {
2792
+ width: 350,
2793
+ draw: (p) => {
2794
+ p.lineWidth(40);
2795
+ p.M(280, 500);
2796
+ p.L(100, 250);
2797
+ p.L(280, 0);
2798
+ p.stroke();
2799
+ }
2800
+ },
2801
+ // ⟩ MATHEMATICAL RIGHT ANGLE BRACKET
2802
+ [0x27e9]: {
2803
+ width: 350,
2804
+ draw: (p) => {
2805
+ p.lineWidth(40);
2806
+ p.M(100, 500);
2807
+ p.L(280, 250);
2808
+ p.L(100, 0);
2809
+ p.stroke();
2810
+ }
2811
+ },
2812
+ // ⟪ MATHEMATICAL LEFT DOUBLE ANGLE BRACKET
2813
+ [0x27ea]: {
2814
+ width: 450,
2815
+ draw: (p) => {
2816
+ p.lineWidth(35);
2817
+ p.M(250, 500);
2818
+ p.L(70, 250);
2819
+ p.L(250, 0);
2820
+ p.stroke();
2821
+ p.M(380, 500);
2822
+ p.L(200, 250);
2823
+ p.L(380, 0);
2824
+ p.stroke();
2825
+ }
2826
+ },
2827
+ // ⟫ MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET
2828
+ [0x27eb]: {
2829
+ width: 450,
2830
+ draw: (p) => {
2831
+ p.lineWidth(35);
2832
+ p.M(70, 500);
2833
+ p.L(250, 250);
2834
+ p.L(70, 0);
2835
+ p.stroke();
2836
+ p.M(200, 500);
2837
+ p.L(380, 250);
2838
+ p.L(200, 0);
2839
+ p.stroke();
2840
+ }
2841
+ }
2842
+ };
2843
+ // =============================================================================
2844
+ // Supplemental Arrows-A (U+27F0–U+27FF)
2845
+ // =============================================================================
2846
+ export const SUP_ARROWS_A = {
2847
+ // ⟵ LONG LEFTWARDS ARROW
2848
+ [0x27f5]: {
2849
+ width: 700,
2850
+ draw: (p) => {
2851
+ p.lineWidth(45);
2852
+ p.M(650, 250);
2853
+ p.L(100, 250);
2854
+ p.stroke();
2855
+ p.M(100, 250);
2856
+ p.L(250, 380);
2857
+ p.L(250, 120);
2858
+ p.Z();
2859
+ p.fill();
2860
+ }
2861
+ },
2862
+ // ⟶ LONG RIGHTWARDS ARROW
2863
+ [0x27f6]: {
2864
+ width: 700,
2865
+ draw: (p) => {
2866
+ p.lineWidth(45);
2867
+ p.M(50, 250);
2868
+ p.L(600, 250);
2869
+ p.stroke();
2870
+ p.M(600, 250);
2871
+ p.L(450, 380);
2872
+ p.L(450, 120);
2873
+ p.Z();
2874
+ p.fill();
2875
+ }
2876
+ },
2877
+ // ⟷ LONG LEFT RIGHT ARROW
2878
+ [0x27f7]: {
2879
+ width: 700,
2880
+ draw: (p) => {
2881
+ p.lineWidth(40);
2882
+ p.M(150, 250);
2883
+ p.L(550, 250);
2884
+ p.stroke();
2885
+ p.M(100, 250);
2886
+ p.L(230, 370);
2887
+ p.L(230, 130);
2888
+ p.Z();
2889
+ p.fill();
2890
+ p.M(600, 250);
2891
+ p.L(470, 370);
2892
+ p.L(470, 130);
2893
+ p.Z();
2894
+ p.fill();
2895
+ }
2896
+ },
2897
+ // ⟸ LONG LEFTWARDS DOUBLE ARROW
2898
+ [0x27f8]: {
2899
+ width: 700,
2900
+ draw: (p) => {
2901
+ p.M(80, 250);
2902
+ p.L(250, 430);
2903
+ p.L(250, 310);
2904
+ p.L(620, 310);
2905
+ p.L(620, 190);
2906
+ p.L(250, 190);
2907
+ p.L(250, 70);
2908
+ p.Z();
2909
+ p.fill();
2910
+ }
2911
+ },
2912
+ // ⟹ LONG RIGHTWARDS DOUBLE ARROW
2913
+ [0x27f9]: {
2914
+ width: 700,
2915
+ draw: (p) => {
2916
+ p.M(620, 250);
2917
+ p.L(450, 430);
2918
+ p.L(450, 310);
2919
+ p.L(80, 310);
2920
+ p.L(80, 190);
2921
+ p.L(450, 190);
2922
+ p.L(450, 70);
2923
+ p.Z();
2924
+ p.fill();
2925
+ }
2926
+ },
2927
+ // ⟺ LONG LEFT RIGHT DOUBLE ARROW
2928
+ [0x27fa]: {
2929
+ width: 700,
2930
+ draw: (p) => {
2931
+ p.M(80, 250);
2932
+ p.L(200, 400);
2933
+ p.L(200, 300);
2934
+ p.L(500, 300);
2935
+ p.L(500, 400);
2936
+ p.L(620, 250);
2937
+ p.L(500, 100);
2938
+ p.L(500, 200);
2939
+ p.L(200, 200);
2940
+ p.L(200, 100);
2941
+ p.Z();
2942
+ p.fill();
2943
+ }
2944
+ }
2945
+ };
2946
+ // =============================================================================
2947
+ // Extended Geometric Shapes (U+25A7–U+25FF) — remaining characters
2948
+ // =============================================================================
2949
+ // Square bounds used throughout: x=100 y=50 w=400 h=400
2950
+ const SQ_X = 100;
2951
+ const SQ_Y = 50;
2952
+ const SQ_W = 400;
2953
+ const SQ_H = 400;
2954
+ const SQ_R = SQ_X + SQ_W; // 500
2955
+ const SQ_T = SQ_Y + SQ_H; // 450
2956
+ export const GEOMETRIC_EXT = {
2957
+ // 0x25A7: SQUARE WITH UPPER LEFT TO LOWER RIGHT FILL
2958
+ [0x25a7]: {
2959
+ width: W,
2960
+ draw: (p) => {
2961
+ p.lineWidth(35);
2962
+ p.rect(SQ_X, SQ_Y, SQ_W, SQ_H);
2963
+ p.stroke();
2964
+ p.lineWidth(20);
2965
+ for (let i = -3; i <= 3; i++) {
2966
+ const off = i * 100;
2967
+ p.M(SQ_X + off, SQ_T);
2968
+ p.L(SQ_R + off, SQ_Y);
2969
+ p.stroke();
2970
+ }
2971
+ }
2972
+ },
2973
+ // 0x25A9: SQUARE WITH DIAGONAL CROSSHATCH
2974
+ [0x25a9]: {
2975
+ width: W,
2976
+ draw: (p) => {
2977
+ p.lineWidth(35);
2978
+ p.rect(SQ_X, SQ_Y, SQ_W, SQ_H);
2979
+ p.stroke();
2980
+ p.lineWidth(15);
2981
+ for (let i = -3; i <= 3; i++) {
2982
+ const off = i * 100;
2983
+ p.M(SQ_X + off, SQ_T);
2984
+ p.L(SQ_R + off, SQ_Y);
2985
+ p.stroke();
2986
+ p.M(SQ_X + off, SQ_Y);
2987
+ p.L(SQ_R + off, SQ_T);
2988
+ p.stroke();
2989
+ }
2990
+ }
2991
+ },
2992
+ // 0x25B1: WHITE PARALLELOGRAM
2993
+ [0x25b1]: {
2994
+ width: W,
2995
+ draw: (p) => {
2996
+ p.lineWidth(40);
2997
+ p.M(180, 50);
2998
+ p.L(500, 50);
2999
+ p.L(420, 450);
3000
+ p.L(100, 450);
3001
+ p.Z();
3002
+ p.stroke();
3003
+ }
3004
+ },
3005
+ // 0x25B9: WHITE RIGHT-POINTING SMALL TRIANGLE
3006
+ [0x25b9]: {
3007
+ width: W,
3008
+ draw: (p) => {
3009
+ p.lineWidth(35);
3010
+ p.M(200, 380);
3011
+ p.L(430, 250);
3012
+ p.L(200, 120);
3013
+ p.Z();
3014
+ p.stroke();
3015
+ }
3016
+ },
3017
+ // 0x25BA: BLACK RIGHT-POINTING POINTER
3018
+ [0x25ba]: {
3019
+ width: W,
3020
+ draw: (p) => {
3021
+ p.M(150, 420);
3022
+ p.L(480, 250);
3023
+ p.L(150, 80);
3024
+ p.Z();
3025
+ p.fill();
3026
+ }
3027
+ },
3028
+ // 0x25BB: WHITE RIGHT-POINTING POINTER
3029
+ [0x25bb]: {
3030
+ width: W,
3031
+ draw: (p) => {
3032
+ p.lineWidth(40);
3033
+ p.M(150, 420);
3034
+ p.L(480, 250);
3035
+ p.L(150, 80);
3036
+ p.Z();
3037
+ p.stroke();
3038
+ }
3039
+ },
3040
+ // 0x25BF: WHITE DOWN-POINTING SMALL TRIANGLE
3041
+ [0x25bf]: {
3042
+ width: W,
3043
+ draw: (p) => {
3044
+ p.lineWidth(35);
3045
+ p.M(180, 380);
3046
+ p.L(420, 380);
3047
+ p.L(300, 120);
3048
+ p.Z();
3049
+ p.stroke();
3050
+ }
3051
+ },
3052
+ // 0x25C3: WHITE LEFT-POINTING SMALL TRIANGLE
3053
+ [0x25c3]: {
3054
+ width: W,
3055
+ draw: (p) => {
3056
+ p.lineWidth(35);
3057
+ p.M(400, 380);
3058
+ p.L(170, 250);
3059
+ p.L(400, 120);
3060
+ p.Z();
3061
+ p.stroke();
3062
+ }
3063
+ },
3064
+ // 0x25C4: BLACK LEFT-POINTING POINTER
3065
+ [0x25c4]: {
3066
+ width: W,
3067
+ draw: (p) => {
3068
+ p.M(450, 420);
3069
+ p.L(120, 250);
3070
+ p.L(450, 80);
3071
+ p.Z();
3072
+ p.fill();
3073
+ }
3074
+ },
3075
+ // 0x25C5: WHITE LEFT-POINTING POINTER
3076
+ [0x25c5]: {
3077
+ width: W,
3078
+ draw: (p) => {
3079
+ p.lineWidth(40);
3080
+ p.M(450, 420);
3081
+ p.L(120, 250);
3082
+ p.L(450, 80);
3083
+ p.Z();
3084
+ p.stroke();
3085
+ }
3086
+ },
3087
+ // 0x25E2: BLACK LOWER RIGHT TRIANGLE
3088
+ [0x25e2]: {
3089
+ width: W,
3090
+ draw: (p) => {
3091
+ p.M(SQ_R, SQ_T);
3092
+ p.L(SQ_R, SQ_Y);
3093
+ p.L(SQ_X, SQ_Y);
3094
+ p.Z();
3095
+ p.fill();
3096
+ }
3097
+ },
3098
+ // 0x25E3: BLACK LOWER LEFT TRIANGLE
3099
+ [0x25e3]: {
3100
+ width: W,
3101
+ draw: (p) => {
3102
+ p.M(SQ_X, SQ_T);
3103
+ p.L(SQ_X, SQ_Y);
3104
+ p.L(SQ_R, SQ_Y);
3105
+ p.Z();
3106
+ p.fill();
3107
+ }
3108
+ },
3109
+ // 0x25E4: BLACK UPPER LEFT TRIANGLE
3110
+ [0x25e4]: {
3111
+ width: W,
3112
+ draw: (p) => {
3113
+ p.M(SQ_X, SQ_Y);
3114
+ p.L(SQ_X, SQ_T);
3115
+ p.L(SQ_R, SQ_T);
3116
+ p.Z();
3117
+ p.fill();
3118
+ }
3119
+ },
3120
+ // 0x25E5: BLACK UPPER RIGHT TRIANGLE
3121
+ [0x25e5]: {
3122
+ width: W,
3123
+ draw: (p) => {
3124
+ p.M(SQ_R, SQ_Y);
3125
+ p.L(SQ_R, SQ_T);
3126
+ p.L(SQ_X, SQ_T);
3127
+ p.Z();
3128
+ p.fill();
3129
+ }
3130
+ },
3131
+ // 0x25E7: SQUARE WITH LEFT HALF BLACK
3132
+ [0x25e7]: {
3133
+ width: W,
3134
+ draw: (p) => {
3135
+ p.lineWidth(35);
3136
+ p.rect(SQ_X, SQ_Y, SQ_W, SQ_H);
3137
+ p.stroke();
3138
+ p.rect(SQ_X, SQ_Y, SQ_W / 2, SQ_H);
3139
+ p.fill();
3140
+ }
3141
+ },
3142
+ // 0x25E8: SQUARE WITH RIGHT HALF BLACK
3143
+ [0x25e8]: {
3144
+ width: W,
3145
+ draw: (p) => {
3146
+ p.lineWidth(35);
3147
+ p.rect(SQ_X, SQ_Y, SQ_W, SQ_H);
3148
+ p.stroke();
3149
+ p.rect(SQ_X + SQ_W / 2, SQ_Y, SQ_W / 2, SQ_H);
3150
+ p.fill();
3151
+ }
3152
+ },
3153
+ // 0x25E9: SQUARE WITH UPPER LEFT DIAGONAL HALF BLACK
3154
+ [0x25e9]: {
3155
+ width: W,
3156
+ draw: (p) => {
3157
+ p.lineWidth(35);
3158
+ p.rect(SQ_X, SQ_Y, SQ_W, SQ_H);
3159
+ p.stroke();
3160
+ p.M(SQ_X, SQ_Y);
3161
+ p.L(SQ_X, SQ_T);
3162
+ p.L(SQ_R, SQ_T);
3163
+ p.Z();
3164
+ p.fill();
3165
+ }
3166
+ },
3167
+ // 0x25EA: SQUARE WITH LOWER RIGHT DIAGONAL HALF BLACK
3168
+ [0x25ea]: {
3169
+ width: W,
3170
+ draw: (p) => {
3171
+ p.lineWidth(35);
3172
+ p.rect(SQ_X, SQ_Y, SQ_W, SQ_H);
3173
+ p.stroke();
3174
+ p.M(SQ_R, SQ_T);
3175
+ p.L(SQ_R, SQ_Y);
3176
+ p.L(SQ_X, SQ_Y);
3177
+ p.Z();
3178
+ p.fill();
3179
+ }
3180
+ },
3181
+ // 0x25EB: WHITE SQUARE WITH VERTICAL BISECTING LINE
3182
+ [0x25eb]: {
3183
+ width: W,
3184
+ draw: (p) => {
3185
+ p.lineWidth(35);
3186
+ p.rect(SQ_X, SQ_Y, SQ_W, SQ_H);
3187
+ p.stroke();
3188
+ p.M(300, SQ_Y);
3189
+ p.L(300, SQ_T);
3190
+ p.stroke();
3191
+ }
3192
+ },
3193
+ // 0x25EC: WHITE UP-POINTING TRIANGLE WITH DOT
3194
+ [0x25ec]: {
3195
+ width: W,
3196
+ draw: (p) => {
3197
+ p.lineWidth(40);
3198
+ p.M(300, 470);
3199
+ p.L(100, 50);
3200
+ p.L(500, 50);
3201
+ p.Z();
3202
+ p.stroke();
3203
+ p.circle(300, 190, 30);
3204
+ p.fill();
3205
+ }
3206
+ },
3207
+ // 0x25ED: UP-POINTING TRIANGLE WITH LEFT HALF BLACK
3208
+ [0x25ed]: {
3209
+ width: W,
3210
+ draw: (p) => {
3211
+ p.lineWidth(40);
3212
+ p.M(300, 470);
3213
+ p.L(100, 50);
3214
+ p.L(500, 50);
3215
+ p.Z();
3216
+ p.stroke();
3217
+ p.M(300, 470);
3218
+ p.L(100, 50);
3219
+ p.L(300, 50);
3220
+ p.Z();
3221
+ p.fill();
3222
+ }
3223
+ },
3224
+ // 0x25EE: UP-POINTING TRIANGLE WITH RIGHT HALF BLACK
3225
+ [0x25ee]: {
3226
+ width: W,
3227
+ draw: (p) => {
3228
+ p.lineWidth(40);
3229
+ p.M(300, 470);
3230
+ p.L(100, 50);
3231
+ p.L(500, 50);
3232
+ p.Z();
3233
+ p.stroke();
3234
+ p.M(300, 470);
3235
+ p.L(500, 50);
3236
+ p.L(300, 50);
3237
+ p.Z();
3238
+ p.fill();
3239
+ }
3240
+ },
3241
+ // 0x25F0: WHITE SQUARE WITH UPPER LEFT QUADRANT
3242
+ [0x25f0]: {
3243
+ width: W,
3244
+ draw: (p) => {
3245
+ p.lineWidth(35);
3246
+ p.rect(SQ_X, SQ_Y, SQ_W, SQ_H);
3247
+ p.stroke();
3248
+ p.rect(SQ_X, SQ_Y + SQ_H / 2, SQ_W / 2, SQ_H / 2);
3249
+ p.fill();
3250
+ }
3251
+ },
3252
+ // 0x25F1: WHITE SQUARE WITH LOWER LEFT QUADRANT
3253
+ [0x25f1]: {
3254
+ width: W,
3255
+ draw: (p) => {
3256
+ p.lineWidth(35);
3257
+ p.rect(SQ_X, SQ_Y, SQ_W, SQ_H);
3258
+ p.stroke();
3259
+ p.rect(SQ_X, SQ_Y, SQ_W / 2, SQ_H / 2);
3260
+ p.fill();
3261
+ }
3262
+ },
3263
+ // 0x25F2: WHITE SQUARE WITH LOWER RIGHT QUADRANT
3264
+ [0x25f2]: {
3265
+ width: W,
3266
+ draw: (p) => {
3267
+ p.lineWidth(35);
3268
+ p.rect(SQ_X, SQ_Y, SQ_W, SQ_H);
3269
+ p.stroke();
3270
+ p.rect(SQ_X + SQ_W / 2, SQ_Y, SQ_W / 2, SQ_H / 2);
3271
+ p.fill();
3272
+ }
3273
+ },
3274
+ // 0x25F3: WHITE SQUARE WITH UPPER RIGHT QUADRANT
3275
+ [0x25f3]: {
3276
+ width: W,
3277
+ draw: (p) => {
3278
+ p.lineWidth(35);
3279
+ p.rect(SQ_X, SQ_Y, SQ_W, SQ_H);
3280
+ p.stroke();
3281
+ p.rect(SQ_X + SQ_W / 2, SQ_Y + SQ_H / 2, SQ_W / 2, SQ_H / 2);
3282
+ p.fill();
3283
+ }
3284
+ },
3285
+ // 0x25F4: WHITE CIRCLE WITH UPPER LEFT QUADRANT
3286
+ [0x25f4]: {
3287
+ width: W,
3288
+ draw: (p) => {
3289
+ p.lineWidth(35);
3290
+ p.circle(300, 250, 220);
3291
+ p.stroke();
3292
+ // Fill upper-left quadrant using a wedge path
3293
+ p.M(300, 250);
3294
+ p.L(300, 470);
3295
+ p.C(179, 470, 80, 371, 80, 250);
3296
+ p.L(300, 250);
3297
+ p.Z();
3298
+ p.fill();
3299
+ }
3300
+ },
3301
+ // 0x25F5: WHITE CIRCLE WITH LOWER LEFT QUADRANT
3302
+ [0x25f5]: {
3303
+ width: W,
3304
+ draw: (p) => {
3305
+ p.lineWidth(35);
3306
+ p.circle(300, 250, 220);
3307
+ p.stroke();
3308
+ p.M(300, 250);
3309
+ p.L(80, 250);
3310
+ p.C(80, 129, 179, 30, 300, 30);
3311
+ p.L(300, 250);
3312
+ p.Z();
3313
+ p.fill();
3314
+ }
3315
+ },
3316
+ // 0x25F6: WHITE CIRCLE WITH LOWER RIGHT QUADRANT
3317
+ [0x25f6]: {
3318
+ width: W,
3319
+ draw: (p) => {
3320
+ p.lineWidth(35);
3321
+ p.circle(300, 250, 220);
3322
+ p.stroke();
3323
+ p.M(300, 250);
3324
+ p.L(300, 30);
3325
+ p.C(421, 30, 520, 129, 520, 250);
3326
+ p.L(300, 250);
3327
+ p.Z();
3328
+ p.fill();
3329
+ }
3330
+ },
3331
+ // 0x25F7: WHITE CIRCLE WITH UPPER RIGHT QUADRANT
3332
+ [0x25f7]: {
3333
+ width: W,
3334
+ draw: (p) => {
3335
+ p.lineWidth(35);
3336
+ p.circle(300, 250, 220);
3337
+ p.stroke();
3338
+ p.M(300, 250);
3339
+ p.L(520, 250);
3340
+ p.C(520, 371, 421, 470, 300, 470);
3341
+ p.L(300, 250);
3342
+ p.Z();
3343
+ p.fill();
3344
+ }
3345
+ },
3346
+ // 0x25F8: UPPER LEFT TRIANGLE (outline)
3347
+ [0x25f8]: {
3348
+ width: W,
3349
+ draw: (p) => {
3350
+ p.lineWidth(35);
3351
+ p.M(SQ_X, SQ_T);
3352
+ p.L(SQ_R, SQ_T);
3353
+ p.L(SQ_X, SQ_Y);
3354
+ p.Z();
3355
+ p.stroke();
3356
+ }
3357
+ },
3358
+ // 0x25F9: UPPER RIGHT TRIANGLE (outline)
3359
+ [0x25f9]: {
3360
+ width: W,
3361
+ draw: (p) => {
3362
+ p.lineWidth(35);
3363
+ p.M(SQ_X, SQ_T);
3364
+ p.L(SQ_R, SQ_T);
3365
+ p.L(SQ_R, SQ_Y);
3366
+ p.Z();
3367
+ p.stroke();
3368
+ }
3369
+ },
3370
+ // 0x25FA: LOWER LEFT TRIANGLE (outline)
3371
+ [0x25fa]: {
3372
+ width: W,
3373
+ draw: (p) => {
3374
+ p.lineWidth(35);
3375
+ p.M(SQ_X, SQ_T);
3376
+ p.L(SQ_X, SQ_Y);
3377
+ p.L(SQ_R, SQ_Y);
3378
+ p.Z();
3379
+ p.stroke();
3380
+ }
3381
+ },
3382
+ // 0x25FB: WHITE MEDIUM SQUARE
3383
+ [0x25fb]: {
3384
+ width: W,
3385
+ draw: (p) => {
3386
+ p.lineWidth(35);
3387
+ p.rect(130, 80, 340, 340);
3388
+ p.stroke();
3389
+ }
3390
+ },
3391
+ // 0x25FC: BLACK MEDIUM SQUARE
3392
+ [0x25fc]: {
3393
+ width: W,
3394
+ draw: (p) => {
3395
+ p.rect(130, 80, 340, 340);
3396
+ p.fill();
3397
+ }
3398
+ },
3399
+ // 0x25FD: WHITE MEDIUM SMALL SQUARE
3400
+ [0x25fd]: {
3401
+ width: W,
3402
+ draw: (p) => {
3403
+ p.lineWidth(30);
3404
+ p.rect(170, 120, 260, 260);
3405
+ p.stroke();
3406
+ }
3407
+ },
3408
+ // 0x25FE: BLACK MEDIUM SMALL SQUARE
3409
+ [0x25fe]: {
3410
+ width: W,
3411
+ draw: (p) => {
3412
+ p.rect(170, 120, 260, 260);
3413
+ p.fill();
3414
+ }
3415
+ },
3416
+ // 0x25FF: LOWER RIGHT TRIANGLE (outline)
3417
+ [0x25ff]: {
3418
+ width: W,
3419
+ draw: (p) => {
3420
+ p.lineWidth(35);
3421
+ p.M(SQ_R, SQ_T);
3422
+ p.L(SQ_R, SQ_Y);
3423
+ p.L(SQ_X, SQ_Y);
3424
+ p.Z();
3425
+ p.stroke();
3426
+ }
3427
+ }
3428
+ };
3429
+ // =============================================================================
3430
+ // Roman Numerals (U+2165–U+2183)
3431
+ // =============================================================================
3432
+ // Uses simple stroke approximations: vertical lines for I, V-shapes for V,
3433
+ // X-shapes for X, etc. These are distinctive but not actual letter shapes.
3434
+ /** Single vertical stroke at given x position. */
3435
+ function romanI(p, x, bot, top) {
3436
+ p.M(x, bot);
3437
+ p.L(x, top);
3438
+ p.stroke();
3439
+ }
3440
+ /** V-shape centred at cx. */
3441
+ function romanV(p, cx, bot, top) {
3442
+ const hw = 80;
3443
+ p.M(cx - hw, top);
3444
+ p.L(cx, bot);
3445
+ p.L(cx + hw, top);
3446
+ p.stroke();
3447
+ }
3448
+ /** X-shape centred at cx. */
3449
+ function romanX(p, cx, bot, top) {
3450
+ const hw = 70;
3451
+ p.M(cx - hw, bot);
3452
+ p.L(cx + hw, top);
3453
+ p.stroke();
3454
+ p.M(cx + hw, bot);
3455
+ p.L(cx - hw, top);
3456
+ p.stroke();
3457
+ }
3458
+ /** Horizontal line (for L and C approximation). */
3459
+ function romanHL(p, x1, x2, y) {
3460
+ p.M(x1, y);
3461
+ p.L(x2, y);
3462
+ p.stroke();
3463
+ }
3464
+ // Capital roman numeral helpers — draws the appropriate symbol pattern
3465
+ function capitalRoman(value) {
3466
+ return {
3467
+ width: W,
3468
+ draw: (p) => {
3469
+ p.lineWidth(45);
3470
+ const bot = 0;
3471
+ const top = 500;
3472
+ if (value <= 3) {
3473
+ // I, II, III — evenly spaced vertical strokes
3474
+ const n = value;
3475
+ const gap = 120;
3476
+ const startX = 300 - ((n - 1) * gap) / 2;
3477
+ for (let i = 0; i < n; i++) {
3478
+ romanI(p, startX + i * gap, bot, top);
3479
+ }
3480
+ }
3481
+ else if (value === 4) {
3482
+ // IV
3483
+ romanI(p, 200, bot, top);
3484
+ romanV(p, 380, bot, top);
3485
+ }
3486
+ else if (value === 5) {
3487
+ // V
3488
+ romanV(p, 300, bot, top);
3489
+ }
3490
+ else if (value === 6) {
3491
+ // VI
3492
+ romanV(p, 200, bot, top);
3493
+ romanI(p, 400, bot, top);
3494
+ }
3495
+ else if (value === 7) {
3496
+ // VII
3497
+ romanV(p, 160, bot, top);
3498
+ romanI(p, 340, bot, top);
3499
+ romanI(p, 460, bot, top);
3500
+ }
3501
+ else if (value === 8) {
3502
+ // VIII
3503
+ p.lineWidth(40);
3504
+ romanV(p, 140, bot, top);
3505
+ romanI(p, 300, bot, top);
3506
+ romanI(p, 400, bot, top);
3507
+ romanI(p, 500, bot, top);
3508
+ }
3509
+ else if (value === 9) {
3510
+ // IX
3511
+ romanI(p, 200, bot, top);
3512
+ romanX(p, 400, bot, top);
3513
+ }
3514
+ else if (value === 10) {
3515
+ // X
3516
+ romanX(p, 300, bot, top);
3517
+ }
3518
+ else if (value === 11) {
3519
+ // XI
3520
+ romanX(p, 220, bot, top);
3521
+ romanI(p, 430, bot, top);
3522
+ }
3523
+ else if (value === 12) {
3524
+ // XII
3525
+ romanX(p, 180, bot, top);
3526
+ romanI(p, 370, bot, top);
3527
+ romanI(p, 480, bot, top);
3528
+ }
3529
+ else if (value === 50) {
3530
+ // L — vertical + horizontal base
3531
+ romanI(p, 200, bot, top);
3532
+ romanHL(p, 200, 450, bot);
3533
+ }
3534
+ else if (value === 100) {
3535
+ // C — open arc approximation (two horizontal + vertical)
3536
+ romanHL(p, 180, 450, top);
3537
+ romanI(p, 180, bot, top);
3538
+ romanHL(p, 180, 450, bot);
3539
+ }
3540
+ else if (value === 500) {
3541
+ // D — vertical + curved right side
3542
+ romanI(p, 180, bot, top);
3543
+ p.M(180, top);
3544
+ p.C(480, top, 480, bot, 180, bot);
3545
+ p.stroke();
3546
+ }
3547
+ else if (value === 1000) {
3548
+ // M — two V-shapes pointing down
3549
+ romanI(p, 100, bot, top);
3550
+ p.M(100, top);
3551
+ p.L(230, bot + 200);
3552
+ p.L(300, top);
3553
+ p.stroke();
3554
+ p.M(300, top);
3555
+ p.L(370, bot + 200);
3556
+ p.L(500, top);
3557
+ p.stroke();
3558
+ romanI(p, 500, bot, top);
3559
+ }
3560
+ }
3561
+ };
3562
+ }
3563
+ // Small roman numeral — same patterns but shorter (70% height, shifted up a bit)
3564
+ function smallRoman(value) {
3565
+ return {
3566
+ width: W,
3567
+ draw: (p) => {
3568
+ p.lineWidth(40);
3569
+ const bot = 0;
3570
+ const top = 350;
3571
+ if (value <= 3) {
3572
+ const n = value;
3573
+ const gap = 100;
3574
+ const startX = 300 - ((n - 1) * gap) / 2;
3575
+ for (let i = 0; i < n; i++) {
3576
+ romanI(p, startX + i * gap, bot, top);
3577
+ }
3578
+ }
3579
+ else if (value === 4) {
3580
+ romanI(p, 200, bot, top);
3581
+ romanV(p, 380, bot, top);
3582
+ }
3583
+ else if (value === 5) {
3584
+ romanV(p, 300, bot, top);
3585
+ }
3586
+ else if (value === 6) {
3587
+ romanV(p, 200, bot, top);
3588
+ romanI(p, 400, bot, top);
3589
+ }
3590
+ else if (value === 7) {
3591
+ romanV(p, 160, bot, top);
3592
+ romanI(p, 340, bot, top);
3593
+ romanI(p, 450, bot, top);
3594
+ }
3595
+ else if (value === 8) {
3596
+ p.lineWidth(35);
3597
+ romanV(p, 130, bot, top);
3598
+ romanI(p, 280, bot, top);
3599
+ romanI(p, 380, bot, top);
3600
+ romanI(p, 480, bot, top);
3601
+ }
3602
+ else if (value === 9) {
3603
+ romanI(p, 200, bot, top);
3604
+ romanX(p, 400, bot, top);
3605
+ }
3606
+ else if (value === 10) {
3607
+ romanX(p, 300, bot, top);
3608
+ }
3609
+ else if (value === 11) {
3610
+ romanX(p, 220, bot, top);
3611
+ romanI(p, 430, bot, top);
3612
+ }
3613
+ else if (value === 12) {
3614
+ romanX(p, 180, bot, top);
3615
+ romanI(p, 370, bot, top);
3616
+ romanI(p, 480, bot, top);
3617
+ }
3618
+ else if (value === 50) {
3619
+ romanI(p, 200, bot, top);
3620
+ romanHL(p, 200, 450, bot);
3621
+ }
3622
+ else if (value === 100) {
3623
+ romanHL(p, 180, 450, top);
3624
+ romanI(p, 180, bot, top);
3625
+ romanHL(p, 180, 450, bot);
3626
+ }
3627
+ else if (value === 500) {
3628
+ romanI(p, 180, bot, top);
3629
+ p.M(180, top);
3630
+ p.C(480, top, 480, bot, 180, bot);
3631
+ p.stroke();
3632
+ }
3633
+ else if (value === 1000) {
3634
+ romanI(p, 100, bot, top);
3635
+ p.M(100, top);
3636
+ p.L(230, bot + 140);
3637
+ p.L(300, top);
3638
+ p.stroke();
3639
+ p.M(300, top);
3640
+ p.L(370, bot + 140);
3641
+ p.L(500, top);
3642
+ p.stroke();
3643
+ romanI(p, 500, bot, top);
3644
+ }
3645
+ }
3646
+ };
3647
+ }
3648
+ // Values for U+2165–U+216F (Ⅵ–Ⅿ): index→value
3649
+ const CAPITAL_VALUES = [
3650
+ [0x2165, 6],
3651
+ [0x2166, 7],
3652
+ [0x2167, 8],
3653
+ [0x2168, 9],
3654
+ [0x2169, 10],
3655
+ [0x216a, 11],
3656
+ [0x216b, 12],
3657
+ [0x216c, 50],
3658
+ [0x216d, 100],
3659
+ [0x216e, 500],
3660
+ [0x216f, 1000]
3661
+ ];
3662
+ // Values for U+2170–U+217F (ⅰ–ⅿ)
3663
+ const SMALL_VALUES = [
3664
+ [0x2170, 1],
3665
+ [0x2171, 2],
3666
+ [0x2172, 3],
3667
+ [0x2173, 4],
3668
+ [0x2174, 5],
3669
+ [0x2175, 6],
3670
+ [0x2176, 7],
3671
+ [0x2177, 8],
3672
+ [0x2178, 9],
3673
+ [0x2179, 10],
3674
+ [0x217a, 11],
3675
+ [0x217b, 12],
3676
+ [0x217c, 50],
3677
+ [0x217d, 100],
3678
+ [0x217e, 500],
3679
+ [0x217f, 1000]
3680
+ ];
3681
+ export const ROMAN_NUMERALS = {};
3682
+ for (const [cp, val] of CAPITAL_VALUES) {
3683
+ ROMAN_NUMERALS[cp] = capitalRoman(val);
3684
+ }
3685
+ for (const [cp, val] of SMALL_VALUES) {
3686
+ ROMAN_NUMERALS[cp] = smallRoman(val);
3687
+ }
3688
+ // U+2180: ROMAN NUMERAL ONE THOUSAND C D — approx as large circle
3689
+ ROMAN_NUMERALS[0x2180] = {
3690
+ width: W,
3691
+ draw: (p) => {
3692
+ p.lineWidth(45);
3693
+ p.circle(300, 250, 220);
3694
+ p.stroke();
3695
+ p.M(300, 470);
3696
+ p.L(300, 30);
3697
+ p.stroke();
3698
+ }
3699
+ };
3700
+ // U+2181: ROMAN NUMERAL FIVE THOUSAND — approx as large D shape
3701
+ ROMAN_NUMERALS[0x2181] = {
3702
+ width: W,
3703
+ draw: (p) => {
3704
+ p.lineWidth(45);
3705
+ p.M(180, 500);
3706
+ p.L(180, 0);
3707
+ p.stroke();
3708
+ p.M(180, 500);
3709
+ p.C(520, 500, 520, 0, 180, 0);
3710
+ p.stroke();
3711
+ p.M(300, 500);
3712
+ p.L(300, 0);
3713
+ p.stroke();
3714
+ }
3715
+ };
3716
+ // U+2182: ROMAN NUMERAL TEN THOUSAND — approx as double circle
3717
+ ROMAN_NUMERALS[0x2182] = {
3718
+ width: W,
3719
+ draw: (p) => {
3720
+ p.lineWidth(40);
3721
+ p.circle(300, 250, 220);
3722
+ p.stroke();
3723
+ p.circle(300, 250, 150);
3724
+ p.stroke();
3725
+ }
3726
+ };
3727
+ // U+2183: ROMAN NUMERAL REVERSED ONE HUNDRED (Ↄ) — reversed C
3728
+ ROMAN_NUMERALS[0x2183] = {
3729
+ width: W,
3730
+ draw: (p) => {
3731
+ p.lineWidth(45);
3732
+ p.M(150, 450);
3733
+ p.C(450, 450, 450, 50, 150, 50);
3734
+ p.stroke();
3735
+ }
3736
+ };
3737
+ // =============================================================================
3738
+ // Extended Enclosed Alphanumerics (U+2480–U+24FF)
3739
+ // =============================================================================
3740
+ export const ENCLOSED_EXT = {};
3741
+ const EXT_DIGIT_PATHS = [
3742
+ // 0
3743
+ (p, cx, cy, s) => {
3744
+ p.ellipse(cx, cy, s * 0.28, s * 0.42);
3745
+ p.stroke();
3746
+ },
3747
+ // 1
3748
+ (p, cx, cy, s) => {
3749
+ p.M(cx, cy + s * 0.42);
3750
+ p.L(cx, cy - s * 0.42);
3751
+ p.stroke();
3752
+ },
3753
+ // 2
3754
+ (p, cx, cy, s) => {
3755
+ p.M(cx - s * 0.25, cy + s * 0.25);
3756
+ p.C(cx - s * 0.25, cy + s * 0.45, cx + s * 0.25, cy + s * 0.45, cx + s * 0.25, cy + s * 0.15);
3757
+ p.L(cx - s * 0.25, cy - s * 0.42);
3758
+ p.L(cx + s * 0.25, cy - s * 0.42);
3759
+ p.stroke();
3760
+ },
3761
+ // 3
3762
+ (p, cx, cy, s) => {
3763
+ p.M(cx - s * 0.22, cy + s * 0.42);
3764
+ p.L(cx + s * 0.22, cy + s * 0.42);
3765
+ p.L(cx + s * 0.22, cy);
3766
+ p.L(cx - s * 0.1, cy);
3767
+ p.stroke();
3768
+ p.M(cx + s * 0.22, cy);
3769
+ p.L(cx + s * 0.22, cy - s * 0.42);
3770
+ p.L(cx - s * 0.22, cy - s * 0.42);
3771
+ p.stroke();
3772
+ },
3773
+ // 4
3774
+ (p, cx, cy, s) => {
3775
+ p.M(cx + s * 0.2, cy + s * 0.42);
3776
+ p.L(cx - s * 0.25, cy - s * 0.05);
3777
+ p.L(cx + s * 0.25, cy - s * 0.05);
3778
+ p.stroke();
3779
+ p.M(cx + s * 0.2, cy + s * 0.42);
3780
+ p.L(cx + s * 0.2, cy - s * 0.42);
3781
+ p.stroke();
3782
+ },
3783
+ // 5
3784
+ (p, cx, cy, s) => {
3785
+ p.M(cx + s * 0.22, cy + s * 0.42);
3786
+ p.L(cx - s * 0.22, cy + s * 0.42);
3787
+ p.L(cx - s * 0.22, cy + s * 0.05);
3788
+ p.L(cx + s * 0.22, cy + s * 0.05);
3789
+ p.C(cx + s * 0.35, cy + s * 0.05, cx + s * 0.35, cy - s * 0.42, cx - s * 0.22, cy - s * 0.42);
3790
+ p.stroke();
3791
+ },
3792
+ // 6
3793
+ (p, cx, cy, s) => {
3794
+ p.M(cx + s * 0.2, cy + s * 0.35);
3795
+ p.C(cx - s * 0.1, cy + s * 0.45, cx - s * 0.3, cy + s * 0.15, cx - s * 0.25, cy - s * 0.1);
3796
+ p.C(cx - s * 0.2, cy - s * 0.45, cx + s * 0.25, cy - s * 0.45, cx + s * 0.25, cy - s * 0.1);
3797
+ p.C(cx + s * 0.25, cy + s * 0.1, cx - s * 0.25, cy + s * 0.1, cx - s * 0.25, cy - s * 0.1);
3798
+ p.stroke();
3799
+ },
3800
+ // 7
3801
+ (p, cx, cy, s) => {
3802
+ p.M(cx - s * 0.22, cy + s * 0.42);
3803
+ p.L(cx + s * 0.22, cy + s * 0.42);
3804
+ p.L(cx - s * 0.05, cy - s * 0.42);
3805
+ p.stroke();
3806
+ },
3807
+ // 8
3808
+ (p, cx, cy, s) => {
3809
+ p.ellipse(cx, cy + s * 0.22, s * 0.2, s * 0.2);
3810
+ p.stroke();
3811
+ p.ellipse(cx, cy - s * 0.22, s * 0.22, s * 0.22);
3812
+ p.stroke();
3813
+ },
3814
+ // 9
3815
+ (p, cx, cy, s) => {
3816
+ p.M(cx - s * 0.2, cy - s * 0.35);
3817
+ p.C(cx + s * 0.1, cy - s * 0.45, cx + s * 0.3, cy - s * 0.15, cx + s * 0.25, cy + s * 0.1);
3818
+ p.C(cx + s * 0.2, cy + s * 0.45, cx - s * 0.25, cy + s * 0.45, cx - s * 0.25, cy + s * 0.1);
3819
+ p.C(cx - s * 0.25, cy - s * 0.1, cx + s * 0.25, cy - s * 0.1, cx + s * 0.25, cy + s * 0.1);
3820
+ p.stroke();
3821
+ }
3822
+ ];
3823
+ function extDrawDigit(p, d, cx, cy, s) {
3824
+ EXT_DIGIT_PATHS[d](p, cx, cy, s);
3825
+ }
3826
+ const EXT_LETTER_PATHS = [
3827
+ // a
3828
+ (p, cx, cy, s) => {
3829
+ p.M(cx + s * 0.25, cy - s * 0.3);
3830
+ p.C(cx + s * 0.1, cy - s * 0.45, cx - s * 0.25, cy - s * 0.35, cx - s * 0.25, cy - s * 0.1);
3831
+ p.C(cx - s * 0.25, cy + s * 0.1, cx + s * 0.25, cy + s * 0.1, cx + s * 0.25, cy - s * 0.1);
3832
+ p.L(cx + s * 0.25, cy - s * 0.35);
3833
+ p.stroke();
3834
+ },
3835
+ // b
3836
+ (p, cx, cy, s) => {
3837
+ p.M(cx - s * 0.2, cy + s * 0.45);
3838
+ p.L(cx - s * 0.2, cy - s * 0.45);
3839
+ p.stroke();
3840
+ p.M(cx - s * 0.2, cy - s * 0.05);
3841
+ p.C(cx - s * 0.2, cy - s * 0.45, cx + s * 0.25, cy - s * 0.45, cx + s * 0.25, cy - s * 0.15);
3842
+ p.C(cx + s * 0.25, cy + s * 0.1, cx - s * 0.2, cy + s * 0.1, cx - s * 0.2, cy - s * 0.05);
3843
+ p.stroke();
3844
+ },
3845
+ // c
3846
+ (p, cx, cy, s) => {
3847
+ p.M(cx + s * 0.2, cy - s * 0.25);
3848
+ p.C(cx + s * 0.05, cy - s * 0.45, cx - s * 0.25, cy - s * 0.35, cx - s * 0.25, cy - s * 0.1);
3849
+ p.C(cx - s * 0.25, cy + s * 0.15, cx + s * 0.05, cy + s * 0.15, cx + s * 0.2, cy + s * 0.05);
3850
+ p.stroke();
3851
+ },
3852
+ // d
3853
+ (p, cx, cy, s) => {
3854
+ p.M(cx + s * 0.2, cy + s * 0.45);
3855
+ p.L(cx + s * 0.2, cy - s * 0.45);
3856
+ p.stroke();
3857
+ p.M(cx + s * 0.2, cy - s * 0.05);
3858
+ p.C(cx + s * 0.2, cy - s * 0.45, cx - s * 0.25, cy - s * 0.45, cx - s * 0.25, cy - s * 0.15);
3859
+ p.C(cx - s * 0.25, cy + s * 0.1, cx + s * 0.2, cy + s * 0.1, cx + s * 0.2, cy - s * 0.05);
3860
+ p.stroke();
3861
+ },
3862
+ // e
3863
+ (p, cx, cy, s) => {
3864
+ p.M(cx - s * 0.25, cy - s * 0.08);
3865
+ p.L(cx + s * 0.25, cy - s * 0.08);
3866
+ p.C(cx + s * 0.25, cy + s * 0.15, cx - s * 0.25, cy + s * 0.15, cx - s * 0.25, cy - s * 0.08);
3867
+ p.C(cx - s * 0.25, cy - s * 0.4, cx + s * 0.25, cy - s * 0.4, cx + s * 0.25, cy - s * 0.25);
3868
+ p.stroke();
3869
+ },
3870
+ // f
3871
+ (p, cx, cy, s) => {
3872
+ p.M(cx + s * 0.1, cy + s * 0.4);
3873
+ p.C(cx + s * 0.1, cy + s * 0.5, cx - s * 0.1, cy + s * 0.5, cx - s * 0.1, cy + s * 0.4);
3874
+ p.L(cx - s * 0.1, cy - s * 0.4);
3875
+ p.stroke();
3876
+ p.M(cx - s * 0.2, cy + s * 0.15);
3877
+ p.L(cx + s * 0.15, cy + s * 0.15);
3878
+ p.stroke();
3879
+ },
3880
+ // g
3881
+ (p, cx, cy, s) => {
3882
+ p.M(cx + s * 0.22, cy + s * 0.1);
3883
+ p.C(cx + s * 0.22, cy + s * 0.4, cx - s * 0.22, cy + s * 0.4, cx - s * 0.22, cy + s * 0.1);
3884
+ p.C(cx - s * 0.22, cy - s * 0.2, cx + s * 0.22, cy - s * 0.2, cx + s * 0.22, cy + s * 0.1);
3885
+ p.L(cx + s * 0.22, cy - s * 0.3);
3886
+ p.C(cx + s * 0.22, cy - s * 0.5, cx - s * 0.22, cy - s * 0.5, cx - s * 0.22, cy - s * 0.35);
3887
+ p.stroke();
3888
+ },
3889
+ // h
3890
+ (p, cx, cy, s) => {
3891
+ p.M(cx - s * 0.2, cy + s * 0.45);
3892
+ p.L(cx - s * 0.2, cy - s * 0.45);
3893
+ p.stroke();
3894
+ p.M(cx - s * 0.2, cy + s * 0.05);
3895
+ p.C(cx - s * 0.2, cy + s * 0.25, cx + s * 0.2, cy + s * 0.25, cx + s * 0.2, cy + s * 0.05);
3896
+ p.L(cx + s * 0.2, cy - s * 0.45);
3897
+ p.stroke();
3898
+ },
3899
+ // i
3900
+ (p, cx, cy, s) => {
3901
+ p.M(cx, cy + s * 0.15);
3902
+ p.L(cx, cy - s * 0.35);
3903
+ p.stroke();
3904
+ p.circle(cx, cy + s * 0.3, s * 0.06);
3905
+ p.fill();
3906
+ },
3907
+ // j
3908
+ (p, cx, cy, s) => {
3909
+ p.M(cx + s * 0.05, cy + s * 0.15);
3910
+ p.L(cx + s * 0.05, cy - s * 0.35);
3911
+ p.C(cx + s * 0.05, cy - s * 0.5, cx - s * 0.15, cy - s * 0.5, cx - s * 0.15, cy - s * 0.35);
3912
+ p.stroke();
3913
+ p.circle(cx + s * 0.05, cy + s * 0.3, s * 0.06);
3914
+ p.fill();
3915
+ },
3916
+ // k
3917
+ (p, cx, cy, s) => {
3918
+ p.M(cx - s * 0.2, cy + s * 0.45);
3919
+ p.L(cx - s * 0.2, cy - s * 0.45);
3920
+ p.stroke();
3921
+ p.M(cx + s * 0.2, cy + s * 0.15);
3922
+ p.L(cx - s * 0.2, cy - s * 0.1);
3923
+ p.L(cx + s * 0.2, cy - s * 0.35);
3924
+ p.stroke();
3925
+ },
3926
+ // l
3927
+ (p, cx, cy, s) => {
3928
+ p.M(cx, cy + s * 0.45);
3929
+ p.L(cx, cy - s * 0.45);
3930
+ p.stroke();
3931
+ },
3932
+ // m
3933
+ (p, cx, cy, s) => {
3934
+ p.M(cx - s * 0.3, cy - s * 0.35);
3935
+ p.L(cx - s * 0.3, cy + s * 0.15);
3936
+ p.C(cx - s * 0.3, cy + s * 0.25, cx - s * 0.05, cy + s * 0.25, cx - s * 0.05, cy + s * 0.15);
3937
+ p.L(cx - s * 0.05, cy - s * 0.35);
3938
+ p.stroke();
3939
+ p.M(cx - s * 0.05, cy + s * 0.15);
3940
+ p.C(cx - s * 0.05, cy + s * 0.25, cx + s * 0.2, cy + s * 0.25, cx + s * 0.2, cy + s * 0.15);
3941
+ p.L(cx + s * 0.2, cy - s * 0.35);
3942
+ p.stroke();
3943
+ },
3944
+ // n
3945
+ (p, cx, cy, s) => {
3946
+ p.M(cx - s * 0.2, cy - s * 0.35);
3947
+ p.L(cx - s * 0.2, cy + s * 0.15);
3948
+ p.C(cx - s * 0.2, cy + s * 0.25, cx + s * 0.2, cy + s * 0.25, cx + s * 0.2, cy + s * 0.15);
3949
+ p.L(cx + s * 0.2, cy - s * 0.35);
3950
+ p.stroke();
3951
+ },
3952
+ // o
3953
+ (p, cx, cy, s) => {
3954
+ p.ellipse(cx, cy - s * 0.1, s * 0.22, s * 0.28);
3955
+ p.stroke();
3956
+ },
3957
+ // p
3958
+ (p, cx, cy, s) => {
3959
+ p.M(cx - s * 0.2, cy - s * 0.45);
3960
+ p.L(cx - s * 0.2, cy + s * 0.15);
3961
+ p.stroke();
3962
+ p.M(cx - s * 0.2, cy + s * 0.05);
3963
+ p.C(cx - s * 0.2, cy + s * 0.35, cx + s * 0.25, cy + s * 0.35, cx + s * 0.25, cy + s * 0.05);
3964
+ p.C(cx + s * 0.25, cy - s * 0.2, cx - s * 0.2, cy - s * 0.2, cx - s * 0.2, cy + s * 0.05);
3965
+ p.stroke();
3966
+ },
3967
+ // q
3968
+ (p, cx, cy, s) => {
3969
+ p.M(cx + s * 0.2, cy - s * 0.45);
3970
+ p.L(cx + s * 0.2, cy + s * 0.15);
3971
+ p.stroke();
3972
+ p.M(cx + s * 0.2, cy + s * 0.05);
3973
+ p.C(cx + s * 0.2, cy + s * 0.35, cx - s * 0.25, cy + s * 0.35, cx - s * 0.25, cy + s * 0.05);
3974
+ p.C(cx - s * 0.25, cy - s * 0.2, cx + s * 0.2, cy - s * 0.2, cx + s * 0.2, cy + s * 0.05);
3975
+ p.stroke();
3976
+ },
3977
+ // r
3978
+ (p, cx, cy, s) => {
3979
+ p.M(cx - s * 0.15, cy - s * 0.35);
3980
+ p.L(cx - s * 0.15, cy + s * 0.15);
3981
+ p.stroke();
3982
+ p.M(cx - s * 0.15, cy + s * 0.05);
3983
+ p.C(cx - s * 0.15, cy + s * 0.25, cx + s * 0.15, cy + s * 0.25, cx + s * 0.2, cy + s * 0.1);
3984
+ p.stroke();
3985
+ },
3986
+ // s
3987
+ (p, cx, cy, s) => {
3988
+ p.M(cx + s * 0.18, cy + s * 0.08);
3989
+ p.C(cx + s * 0.18, cy + s * 0.22, cx - s * 0.18, cy + s * 0.22, cx - s * 0.18, cy + s * 0.05);
3990
+ p.C(cx - s * 0.18, cy - s * 0.1, cx + s * 0.18, cy - s * 0.1, cx + s * 0.18, cy - s * 0.22);
3991
+ p.C(cx + s * 0.18, cy - s * 0.38, cx - s * 0.18, cy - s * 0.38, cx - s * 0.18, cy - s * 0.25);
3992
+ p.stroke();
3993
+ },
3994
+ // t
3995
+ (p, cx, cy, s) => {
3996
+ p.M(cx, cy + s * 0.35);
3997
+ p.L(cx, cy - s * 0.35);
3998
+ p.C(cx, cy - s * 0.5, cx + s * 0.15, cy - s * 0.5, cx + s * 0.15, cy - s * 0.4);
3999
+ p.stroke();
4000
+ p.M(cx - s * 0.15, cy + s * 0.15);
4001
+ p.L(cx + s * 0.15, cy + s * 0.15);
4002
+ p.stroke();
4003
+ },
4004
+ // u
4005
+ (p, cx, cy, s) => {
4006
+ p.M(cx - s * 0.2, cy + s * 0.15);
4007
+ p.L(cx - s * 0.2, cy - s * 0.15);
4008
+ p.C(cx - s * 0.2, cy - s * 0.4, cx + s * 0.2, cy - s * 0.4, cx + s * 0.2, cy - s * 0.15);
4009
+ p.L(cx + s * 0.2, cy + s * 0.15);
4010
+ p.L(cx + s * 0.2, cy - s * 0.35);
4011
+ p.stroke();
4012
+ },
4013
+ // v
4014
+ (p, cx, cy, s) => {
4015
+ p.M(cx - s * 0.22, cy + s * 0.15);
4016
+ p.L(cx, cy - s * 0.35);
4017
+ p.L(cx + s * 0.22, cy + s * 0.15);
4018
+ p.stroke();
4019
+ },
4020
+ // w
4021
+ (p, cx, cy, s) => {
4022
+ p.M(cx - s * 0.3, cy + s * 0.15);
4023
+ p.L(cx - s * 0.15, cy - s * 0.35);
4024
+ p.L(cx, cy);
4025
+ p.L(cx + s * 0.15, cy - s * 0.35);
4026
+ p.L(cx + s * 0.3, cy + s * 0.15);
4027
+ p.stroke();
4028
+ },
4029
+ // x
4030
+ (p, cx, cy, s) => {
4031
+ p.M(cx - s * 0.2, cy + s * 0.15);
4032
+ p.L(cx + s * 0.2, cy - s * 0.35);
4033
+ p.stroke();
4034
+ p.M(cx + s * 0.2, cy + s * 0.15);
4035
+ p.L(cx - s * 0.2, cy - s * 0.35);
4036
+ p.stroke();
4037
+ },
4038
+ // y
4039
+ (p, cx, cy, s) => {
4040
+ p.M(cx - s * 0.2, cy + s * 0.15);
4041
+ p.L(cx, cy - s * 0.1);
4042
+ p.L(cx + s * 0.2, cy + s * 0.15);
4043
+ p.stroke();
4044
+ p.M(cx, cy - s * 0.1);
4045
+ p.L(cx - s * 0.1, cy - s * 0.45);
4046
+ p.stroke();
4047
+ },
4048
+ // z
4049
+ (p, cx, cy, s) => {
4050
+ p.M(cx - s * 0.2, cy + s * 0.15);
4051
+ p.L(cx + s * 0.2, cy + s * 0.15);
4052
+ p.L(cx - s * 0.2, cy - s * 0.35);
4053
+ p.L(cx + s * 0.2, cy - s * 0.35);
4054
+ p.stroke();
4055
+ }
4056
+ ];
4057
+ function extDrawLetter(p, idx, cx, cy, s) {
4058
+ EXT_LETTER_PATHS[idx](p, cx, cy, s);
4059
+ }
4060
+ function drawParens(p) {
4061
+ p.M(160, 480);
4062
+ p.C(100, 480, 60, 380, 60, 250);
4063
+ p.C(60, 120, 100, 20, 160, 20);
4064
+ p.stroke();
4065
+ p.M(440, 20);
4066
+ p.C(500, 20, 540, 120, 540, 250);
4067
+ p.C(540, 380, 500, 480, 440, 480);
4068
+ p.stroke();
4069
+ }
4070
+ // 0x2474–0x2487: Parenthesized digits ⑴ 1 – ⒇ 20
4071
+ for (let i = 0; i < 20; i++) {
4072
+ const num = i + 1;
4073
+ ENCLOSED_EXT[0x2474 + i] = {
4074
+ width: W,
4075
+ draw: (p) => {
4076
+ p.lineWidth(25);
4077
+ drawParens(p);
4078
+ p.lineWidth(24);
4079
+ if (num <= 9) {
4080
+ extDrawDigit(p, num, 300, 250, 200);
4081
+ }
4082
+ else {
4083
+ const d1 = Math.floor(num / 10);
4084
+ const d2 = num % 10;
4085
+ extDrawDigit(p, d1, 240, 250, 150);
4086
+ extDrawDigit(p, d2, 380, 250, 150);
4087
+ }
4088
+ }
4089
+ };
4090
+ }
4091
+ // 0x2480–0x2487: Parenthesized digits ⒀ 13 – ⒇ 20
4092
+ // (these overlap with the loop above; the loop above covers 0x2474-0x2487 = 1-20,
4093
+ // so 0x2480-0x2487 = 13-20 are already covered)
4094
+ // 0x2488–0x249B: Digit period 1.–20. — digit + period dot
4095
+ for (let i = 0; i < 20; i++) {
4096
+ const num = i + 1;
4097
+ ENCLOSED_EXT[0x2488 + i] = {
4098
+ width: W,
4099
+ draw: (p) => {
4100
+ p.lineWidth(26);
4101
+ if (num <= 9) {
4102
+ extDrawDigit(p, num, 260, 250, 210);
4103
+ }
4104
+ else {
4105
+ const d1 = Math.floor(num / 10);
4106
+ const d2 = num % 10;
4107
+ extDrawDigit(p, d1, 200, 250, 160);
4108
+ extDrawDigit(p, d2, 340, 250, 160);
4109
+ }
4110
+ // Period dot
4111
+ p.circle(num <= 9 ? 400 : 460, 80, 28);
4112
+ p.fill();
4113
+ }
4114
+ };
4115
+ }
4116
+ // 0x249C–0x24B5: Parenthesized Latin small letters ⒜–⒵
4117
+ for (let i = 0; i < 26; i++) {
4118
+ ENCLOSED_EXT[0x249c + i] = {
4119
+ width: W,
4120
+ draw: (p) => {
4121
+ p.lineWidth(25);
4122
+ drawParens(p);
4123
+ p.lineWidth(24);
4124
+ extDrawLetter(p, i, 300, 250, 200);
4125
+ }
4126
+ };
4127
+ }
4128
+ // 0x24EB–0x24FF: Negative circled numbers 11–20 and others
4129
+ // Use thick circle outline + number (best approximation since we can't do white-on-black)
4130
+ for (let i = 0; i <= 0x24ff - 0x24eb; i++) {
4131
+ const num = i + 11; // 0x24EB = ⓫ (11), ..., 0x24F4 = ⓴ (20), 0x24F5+ are circled 1-10 again, 0x24FF = ⓿ (0)
4132
+ ENCLOSED_EXT[0x24eb + i] = {
4133
+ width: W,
4134
+ draw: (p) => {
4135
+ // Thick circle outline to suggest filled appearance
4136
+ p.lineWidth(55);
4137
+ p.circle(300, 250, 215);
4138
+ p.stroke();
4139
+ p.lineWidth(24);
4140
+ if (i <= 9) {
4141
+ // 11-20: two digits
4142
+ const d1 = Math.floor(num / 10);
4143
+ const d2 = num % 10;
4144
+ extDrawDigit(p, d1, 230, 250, 150);
4145
+ extDrawDigit(p, d2, 370, 250, 150);
4146
+ }
4147
+ else if (i <= 19) {
4148
+ // 0x24F5-0x24FE: double-circled 1-10
4149
+ const d = i - 9;
4150
+ if (d <= 9) {
4151
+ extDrawDigit(p, d, 300, 250, 170);
4152
+ }
4153
+ else {
4154
+ extDrawDigit(p, 1, 230, 250, 140);
4155
+ extDrawDigit(p, 0, 370, 250, 140);
4156
+ }
4157
+ }
4158
+ else {
4159
+ // 0x24FF: ⓿ negative circled zero
4160
+ extDrawDigit(p, 0, 300, 250, 170);
4161
+ }
4162
+ }
4163
+ };
4164
+ }