@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,2567 @@
1
+ /**
2
+ * Type3 fallback glyph definitions.
3
+ *
4
+ * Each glyph is a function that writes PDF content-stream operators into
5
+ * a 1000×1000 unit glyph coordinate system (matching a FontMatrix of
6
+ * [0.001 0 0 0.001 0 0]).
7
+ *
8
+ * The drawing callbacks receive a simple `GlyphPen` API so they don't
9
+ * depend on `PdfContentStream` directly — this keeps the module
10
+ * self-contained and easy to test.
11
+ *
12
+ * Coverage strategy:
13
+ * 1. Geometric Shapes (U+25A0–U+25FF)
14
+ * 2. Miscellaneous Symbols (U+2600–U+26FF)
15
+ * 3. Dingbats / checkmarks (U+2700–U+27BF)
16
+ * 4. Arrows (U+2190–U+21FF)
17
+ * 5. Mathematical Operators (U+2200–U+22FF)
18
+ * 6. Box Drawing (U+2500–U+257F)
19
+ * 7. Block Elements (U+2580–U+259F)
20
+ * 8. Misc Technical (U+2300–U+23FF)
21
+ * 9. Enclosed Alphanumerics (U+2460–U+24FF)
22
+ * 10. Misc Symbols & Arrows (U+2B00–U+2BFF)
23
+ * 11. Ballot / checkbox symbols (U+2610–U+2612)
24
+ * 12. Supplemental Arrows (U+27F0–U+27FF)
25
+ * 13. Braille Patterns (U+2800–U+28FF)
26
+ * 14. Squared symbols etc. (U+29C0–U+29FF)
27
+ *
28
+ * Characters not in this table get a generic .notdef glyph (open rectangle).
29
+ */
30
+ // =============================================================================
31
+ // Helpers
32
+ // =============================================================================
33
+ /** Standard advance width for a "normal-width" symbol. */
34
+ const W = 600;
35
+ /** A filled square glyph (generic, reusable). */
36
+ function filledRect(x, y, w, h) {
37
+ return {
38
+ width: W,
39
+ draw: p => {
40
+ p.rect(x, y, w, h);
41
+ p.fill();
42
+ }
43
+ };
44
+ }
45
+ function strokedRect(x, y, w, h, lw = 50) {
46
+ return {
47
+ width: W,
48
+ draw: p => {
49
+ p.lineWidth(lw);
50
+ p.rect(x, y, w, h);
51
+ p.stroke();
52
+ }
53
+ };
54
+ }
55
+ function filledCircle(cx, cy, r) {
56
+ return {
57
+ width: W,
58
+ draw: p => {
59
+ p.circle(cx, cy, r);
60
+ p.fill();
61
+ }
62
+ };
63
+ }
64
+ function strokedCircle(cx, cy, r, lw = 50) {
65
+ return {
66
+ width: W,
67
+ draw: p => {
68
+ p.lineWidth(lw);
69
+ p.circle(cx, cy, r);
70
+ p.stroke();
71
+ }
72
+ };
73
+ }
74
+ /** .notdef: open rectangle — drawn for any character we don't have. */
75
+ export const NOTDEF_GLYPH = strokedRect(80, 0, 440, 700, 40);
76
+ // =============================================================================
77
+ // Geometric Shapes (U+25A0 – U+25FF)
78
+ // =============================================================================
79
+ const GEO = {
80
+ // ■ BLACK SQUARE
81
+ [0x25a0]: filledRect(100, 50, 400, 400),
82
+ // □ WHITE SQUARE
83
+ [0x25a1]: strokedRect(100, 50, 400, 400),
84
+ // ▢ WHITE SQUARE WITH ROUNDED CORNERS
85
+ [0x25a2]: {
86
+ width: W,
87
+ draw: p => {
88
+ p.lineWidth(50);
89
+ p.M(150, 50);
90
+ p.L(450, 50);
91
+ p.C(480, 50, 500, 70, 500, 100);
92
+ p.L(500, 400);
93
+ p.C(500, 430, 480, 450, 450, 450);
94
+ p.L(150, 450);
95
+ p.C(120, 450, 100, 430, 100, 400);
96
+ p.L(100, 100);
97
+ p.C(100, 70, 120, 50, 150, 50);
98
+ p.Z();
99
+ p.stroke();
100
+ }
101
+ },
102
+ // ▣ WHITE SQUARE CONTAINING BLACK SMALL SQUARE
103
+ [0x25a3]: {
104
+ width: W,
105
+ draw: p => {
106
+ p.lineWidth(40);
107
+ p.rect(100, 50, 400, 400);
108
+ p.stroke();
109
+ p.rect(200, 150, 200, 200);
110
+ p.fill();
111
+ }
112
+ },
113
+ // ▤ SQUARE WITH HORIZONTAL FILL
114
+ [0x25a4]: {
115
+ width: W,
116
+ draw: p => {
117
+ p.lineWidth(40);
118
+ p.rect(100, 50, 400, 400);
119
+ p.stroke();
120
+ p.lineWidth(30);
121
+ for (let y = 100; y <= 400; y += 80) {
122
+ p.M(100, y);
123
+ p.L(500, y);
124
+ }
125
+ p.stroke();
126
+ }
127
+ },
128
+ // ▥ SQUARE WITH VERTICAL FILL
129
+ [0x25a5]: {
130
+ width: W,
131
+ draw: p => {
132
+ p.lineWidth(40);
133
+ p.rect(100, 50, 400, 400);
134
+ p.stroke();
135
+ p.lineWidth(30);
136
+ for (let x = 150; x <= 450; x += 80) {
137
+ p.M(x, 50);
138
+ p.L(x, 450);
139
+ }
140
+ p.stroke();
141
+ }
142
+ },
143
+ // ▦ SQUARE WITH ORTHOGONAL CROSSHATCH
144
+ [0x25a6]: {
145
+ width: W,
146
+ draw: p => {
147
+ p.lineWidth(40);
148
+ p.rect(100, 50, 400, 400);
149
+ p.stroke();
150
+ p.lineWidth(20);
151
+ for (let y = 100; y <= 400; y += 80) {
152
+ p.M(100, y);
153
+ p.L(500, y);
154
+ }
155
+ for (let x = 150; x <= 450; x += 80) {
156
+ p.M(x, 50);
157
+ p.L(x, 450);
158
+ }
159
+ p.stroke();
160
+ }
161
+ },
162
+ // ▨ SQUARE WITH UPPER RIGHT TO LOWER LEFT FILL — simplified as rect
163
+ [0x25a8]: {
164
+ width: W,
165
+ draw: p => {
166
+ p.lineWidth(40);
167
+ p.rect(100, 50, 400, 400);
168
+ p.stroke();
169
+ p.lineWidth(20);
170
+ for (let i = -400; i <= 400; i += 80) {
171
+ p.M(Math.max(100, 100 + i), Math.min(450, 450 + i));
172
+ p.L(Math.min(500, 500 + i), Math.max(50, 50 + i));
173
+ }
174
+ p.stroke();
175
+ }
176
+ },
177
+ // ▪ BLACK SMALL SQUARE
178
+ [0x25aa]: filledRect(175, 125, 250, 250),
179
+ // ▫ WHITE SMALL SQUARE
180
+ [0x25ab]: strokedRect(175, 125, 250, 250, 40),
181
+ // ▬ BLACK RECTANGLE
182
+ [0x25ac]: filledRect(80, 150, 440, 200),
183
+ // ▭ WHITE RECTANGLE
184
+ [0x25ad]: strokedRect(80, 150, 440, 200),
185
+ // ▮ BLACK VERTICAL RECTANGLE
186
+ [0x25ae]: filledRect(175, 0, 250, 500),
187
+ // ▯ WHITE VERTICAL RECTANGLE
188
+ [0x25af]: strokedRect(175, 0, 250, 500),
189
+ // ▰ BLACK PARALLELOGRAM
190
+ [0x25b0]: {
191
+ width: W,
192
+ draw: p => {
193
+ p.M(150, 100);
194
+ p.L(520, 100);
195
+ p.L(450, 400);
196
+ p.L(80, 400);
197
+ p.Z();
198
+ p.fill();
199
+ }
200
+ },
201
+ // ▲ BLACK UP-POINTING TRIANGLE
202
+ [0x25b2]: {
203
+ width: W,
204
+ draw: p => {
205
+ p.M(300, 500);
206
+ p.L(500, 50);
207
+ p.L(100, 50);
208
+ p.Z();
209
+ p.fill();
210
+ }
211
+ },
212
+ // △ WHITE UP-POINTING TRIANGLE
213
+ [0x25b3]: {
214
+ width: W,
215
+ draw: p => {
216
+ p.lineWidth(40);
217
+ p.M(300, 500);
218
+ p.L(500, 50);
219
+ p.L(100, 50);
220
+ p.Z();
221
+ p.stroke();
222
+ }
223
+ },
224
+ // ▴ BLACK UP-POINTING SMALL TRIANGLE
225
+ [0x25b4]: {
226
+ width: W,
227
+ draw: p => {
228
+ p.M(300, 450);
229
+ p.L(450, 100);
230
+ p.L(150, 100);
231
+ p.Z();
232
+ p.fill();
233
+ }
234
+ },
235
+ // ▵ WHITE UP-POINTING SMALL TRIANGLE
236
+ [0x25b5]: {
237
+ width: W,
238
+ draw: p => {
239
+ p.lineWidth(35);
240
+ p.M(300, 450);
241
+ p.L(450, 100);
242
+ p.L(150, 100);
243
+ p.Z();
244
+ p.stroke();
245
+ }
246
+ },
247
+ // ▶ BLACK RIGHT-POINTING TRIANGLE
248
+ [0x25b6]: {
249
+ width: W,
250
+ draw: p => {
251
+ p.M(100, 500);
252
+ p.L(500, 250);
253
+ p.L(100, 0);
254
+ p.Z();
255
+ p.fill();
256
+ }
257
+ },
258
+ // ▷ WHITE RIGHT-POINTING TRIANGLE
259
+ [0x25b7]: {
260
+ width: W,
261
+ draw: p => {
262
+ p.lineWidth(40);
263
+ p.M(100, 500);
264
+ p.L(500, 250);
265
+ p.L(100, 0);
266
+ p.Z();
267
+ p.stroke();
268
+ }
269
+ },
270
+ // ▸ BLACK RIGHT-POINTING SMALL TRIANGLE
271
+ [0x25b8]: {
272
+ width: W,
273
+ draw: p => {
274
+ p.M(150, 450);
275
+ p.L(450, 250);
276
+ p.L(150, 50);
277
+ p.Z();
278
+ p.fill();
279
+ }
280
+ },
281
+ // ▼ BLACK DOWN-POINTING TRIANGLE
282
+ [0x25bc]: {
283
+ width: W,
284
+ draw: p => {
285
+ p.M(100, 500);
286
+ p.L(500, 500);
287
+ p.L(300, 50);
288
+ p.Z();
289
+ p.fill();
290
+ }
291
+ },
292
+ // ▽ WHITE DOWN-POINTING TRIANGLE
293
+ [0x25bd]: {
294
+ width: W,
295
+ draw: p => {
296
+ p.lineWidth(40);
297
+ p.M(100, 500);
298
+ p.L(500, 500);
299
+ p.L(300, 50);
300
+ p.Z();
301
+ p.stroke();
302
+ }
303
+ },
304
+ // ▾ BLACK DOWN-POINTING SMALL TRIANGLE
305
+ [0x25be]: {
306
+ width: W,
307
+ draw: p => {
308
+ p.M(150, 400);
309
+ p.L(450, 400);
310
+ p.L(300, 100);
311
+ p.Z();
312
+ p.fill();
313
+ }
314
+ },
315
+ // ◀ BLACK LEFT-POINTING TRIANGLE
316
+ [0x25c0]: {
317
+ width: W,
318
+ draw: p => {
319
+ p.M(500, 500);
320
+ p.L(100, 250);
321
+ p.L(500, 0);
322
+ p.Z();
323
+ p.fill();
324
+ }
325
+ },
326
+ // ◁ WHITE LEFT-POINTING TRIANGLE
327
+ [0x25c1]: {
328
+ width: W,
329
+ draw: p => {
330
+ p.lineWidth(40);
331
+ p.M(500, 500);
332
+ p.L(100, 250);
333
+ p.L(500, 0);
334
+ p.Z();
335
+ p.stroke();
336
+ }
337
+ },
338
+ // ◂ BLACK LEFT-POINTING SMALL TRIANGLE
339
+ [0x25c2]: {
340
+ width: W,
341
+ draw: p => {
342
+ p.M(450, 450);
343
+ p.L(150, 250);
344
+ p.L(450, 50);
345
+ p.Z();
346
+ p.fill();
347
+ }
348
+ },
349
+ // ◆ BLACK DIAMOND
350
+ [0x25c6]: {
351
+ width: W,
352
+ draw: p => {
353
+ p.M(300, 500);
354
+ p.L(530, 250);
355
+ p.L(300, 0);
356
+ p.L(70, 250);
357
+ p.Z();
358
+ p.fill();
359
+ }
360
+ },
361
+ // ◇ WHITE DIAMOND
362
+ [0x25c7]: {
363
+ width: W,
364
+ draw: p => {
365
+ p.lineWidth(40);
366
+ p.M(300, 500);
367
+ p.L(530, 250);
368
+ p.L(300, 0);
369
+ p.L(70, 250);
370
+ p.Z();
371
+ p.stroke();
372
+ }
373
+ },
374
+ // ◈ WHITE DIAMOND CONTAINING BLACK SMALL DIAMOND
375
+ [0x25c8]: {
376
+ width: W,
377
+ draw: p => {
378
+ p.lineWidth(40);
379
+ p.M(300, 500);
380
+ p.L(530, 250);
381
+ p.L(300, 0);
382
+ p.L(70, 250);
383
+ p.Z();
384
+ p.stroke();
385
+ p.M(300, 380);
386
+ p.L(410, 250);
387
+ p.L(300, 120);
388
+ p.L(190, 250);
389
+ p.Z();
390
+ p.fill();
391
+ }
392
+ },
393
+ // ◉ FISHEYE (big circle with smaller filled circle)
394
+ [0x25c9]: {
395
+ width: W,
396
+ draw: p => {
397
+ p.lineWidth(40);
398
+ p.circle(300, 250, 220);
399
+ p.stroke();
400
+ p.circle(300, 250, 120);
401
+ p.fill();
402
+ }
403
+ },
404
+ // ◊ LOZENGE
405
+ [0x25ca]: {
406
+ width: W,
407
+ draw: p => {
408
+ p.lineWidth(40);
409
+ p.M(300, 550);
410
+ p.L(500, 250);
411
+ p.L(300, -50);
412
+ p.L(100, 250);
413
+ p.Z();
414
+ p.stroke();
415
+ }
416
+ },
417
+ // ○ WHITE CIRCLE
418
+ [0x25cb]: strokedCircle(300, 250, 220),
419
+ // ◌ DOTTED CIRCLE — simplified as dashed circle
420
+ [0x25cc]: strokedCircle(300, 250, 220, 30),
421
+ // ◍ CIRCLE WITH UPPER HALF BLACK
422
+ [0x25cd]: {
423
+ width: W,
424
+ draw: p => {
425
+ p.lineWidth(40);
426
+ p.circle(300, 250, 220);
427
+ p.stroke();
428
+ /* upper half */ p.M(80, 250);
429
+ p.C(80, 371, 179, 470, 300, 470);
430
+ p.C(421, 470, 520, 371, 520, 250);
431
+ p.Z();
432
+ p.fill();
433
+ }
434
+ },
435
+ // ◎ BULLSEYE
436
+ [0x25ce]: {
437
+ width: W,
438
+ draw: p => {
439
+ p.lineWidth(35);
440
+ p.circle(300, 250, 220);
441
+ p.stroke();
442
+ p.circle(300, 250, 140);
443
+ p.stroke();
444
+ }
445
+ },
446
+ // ● BLACK CIRCLE
447
+ [0x25cf]: filledCircle(300, 250, 220),
448
+ // ◐ CIRCLE WITH LEFT HALF BLACK
449
+ [0x25d0]: {
450
+ width: W,
451
+ draw: p => {
452
+ p.lineWidth(40);
453
+ p.circle(300, 250, 220);
454
+ p.stroke();
455
+ /* left half */ p.M(300, 30);
456
+ p.C(179, 30, 80, 129, 80, 250);
457
+ p.C(80, 371, 179, 470, 300, 470);
458
+ p.Z();
459
+ p.fill();
460
+ }
461
+ },
462
+ // ◑ CIRCLE WITH RIGHT HALF BLACK
463
+ [0x25d1]: {
464
+ width: W,
465
+ draw: p => {
466
+ p.lineWidth(40);
467
+ p.circle(300, 250, 220);
468
+ p.stroke();
469
+ p.M(300, 470);
470
+ p.C(421, 470, 520, 371, 520, 250);
471
+ p.C(520, 129, 421, 30, 300, 30);
472
+ p.Z();
473
+ p.fill();
474
+ }
475
+ },
476
+ // ◒ CIRCLE WITH LOWER HALF BLACK
477
+ [0x25d2]: {
478
+ width: W,
479
+ draw: p => {
480
+ p.lineWidth(40);
481
+ p.circle(300, 250, 220);
482
+ p.stroke();
483
+ p.M(520, 250);
484
+ p.C(520, 129, 421, 30, 300, 30);
485
+ p.C(179, 30, 80, 129, 80, 250);
486
+ p.Z();
487
+ p.fill();
488
+ }
489
+ },
490
+ // ◓ CIRCLE WITH UPPER HALF BLACK
491
+ [0x25d3]: {
492
+ width: W,
493
+ draw: p => {
494
+ p.lineWidth(40);
495
+ p.circle(300, 250, 220);
496
+ p.stroke();
497
+ p.M(80, 250);
498
+ p.C(80, 371, 179, 470, 300, 470);
499
+ p.C(421, 470, 520, 371, 520, 250);
500
+ p.Z();
501
+ p.fill();
502
+ }
503
+ },
504
+ // ◔ CIRCLE WITH UPPER RIGHT QUADRANT BLACK
505
+ [0x25d4]: {
506
+ width: W,
507
+ draw: p => {
508
+ p.lineWidth(40);
509
+ p.circle(300, 250, 220);
510
+ p.stroke();
511
+ p.M(300, 250);
512
+ p.L(300, 470);
513
+ p.C(421, 470, 520, 371, 520, 250);
514
+ p.Z();
515
+ p.fill();
516
+ }
517
+ },
518
+ // ◕ CIRCLE WITH ALL BUT UPPER LEFT QUADRANT BLACK
519
+ [0x25d5]: {
520
+ width: W,
521
+ draw: p => {
522
+ p.lineWidth(40);
523
+ p.circle(300, 250, 220);
524
+ p.stroke();
525
+ p.M(300, 250);
526
+ p.L(300, 470);
527
+ p.C(179, 470, 80, 371, 80, 250);
528
+ p.Z();
529
+ p.fill();
530
+ p.M(300, 250);
531
+ p.L(520, 250);
532
+ p.C(520, 129, 421, 30, 300, 30);
533
+ p.C(179, 30, 80, 129, 80, 250);
534
+ p.Z();
535
+ p.fill();
536
+ }
537
+ },
538
+ // ◖ LEFT HALF BLACK CIRCLE
539
+ [0x25d6]: {
540
+ width: W,
541
+ draw: p => {
542
+ p.M(300, 30);
543
+ p.C(179, 30, 80, 129, 80, 250);
544
+ p.C(80, 371, 179, 470, 300, 470);
545
+ p.Z();
546
+ p.fill();
547
+ }
548
+ },
549
+ // ◗ RIGHT HALF BLACK CIRCLE
550
+ [0x25d7]: {
551
+ width: W,
552
+ draw: p => {
553
+ p.M(300, 470);
554
+ p.C(421, 470, 520, 371, 520, 250);
555
+ p.C(520, 129, 421, 30, 300, 30);
556
+ p.Z();
557
+ p.fill();
558
+ }
559
+ },
560
+ // ◘ INVERSE BULLET (filled square, white circle inside)
561
+ [0x25d8]: {
562
+ width: W,
563
+ draw: p => {
564
+ p.rect(80, 30, 440, 440);
565
+ p.fill();
566
+ }
567
+ },
568
+ // ◙ INVERSE WHITE CIRCLE (filled circle w/ white square)
569
+ [0x25d9]: {
570
+ width: W,
571
+ draw: p => {
572
+ p.circle(300, 250, 240);
573
+ p.fill();
574
+ }
575
+ },
576
+ // ◜◝◞◟ UPPER LEFT / UPPER RIGHT / LOWER RIGHT / LOWER LEFT QUADRANT CIRCULAR ARC
577
+ [0x25dc]: {
578
+ width: W,
579
+ draw: p => {
580
+ p.lineWidth(50);
581
+ p.M(300, 470);
582
+ p.C(179, 470, 80, 371, 80, 250);
583
+ p.stroke();
584
+ }
585
+ },
586
+ [0x25dd]: {
587
+ width: W,
588
+ draw: p => {
589
+ p.lineWidth(50);
590
+ p.M(520, 250);
591
+ p.C(520, 371, 421, 470, 300, 470);
592
+ p.stroke();
593
+ }
594
+ },
595
+ [0x25de]: {
596
+ width: W,
597
+ draw: p => {
598
+ p.lineWidth(50);
599
+ p.M(300, 30);
600
+ p.C(421, 30, 520, 129, 520, 250);
601
+ p.stroke();
602
+ }
603
+ },
604
+ [0x25df]: {
605
+ width: W,
606
+ draw: p => {
607
+ p.lineWidth(50);
608
+ p.M(80, 250);
609
+ p.C(80, 129, 179, 30, 300, 30);
610
+ p.stroke();
611
+ }
612
+ },
613
+ // ◠ UPPER HALF CIRCLE
614
+ [0x25e0]: {
615
+ width: W,
616
+ draw: p => {
617
+ p.lineWidth(50);
618
+ p.M(80, 250);
619
+ p.C(80, 371, 179, 470, 300, 470);
620
+ p.C(421, 470, 520, 371, 520, 250);
621
+ p.stroke();
622
+ }
623
+ },
624
+ // ◡ LOWER HALF CIRCLE
625
+ [0x25e1]: {
626
+ width: W,
627
+ draw: p => {
628
+ p.lineWidth(50);
629
+ p.M(520, 250);
630
+ p.C(520, 129, 421, 30, 300, 30);
631
+ p.C(179, 30, 80, 129, 80, 250);
632
+ p.stroke();
633
+ }
634
+ },
635
+ // ◦ WHITE BULLET
636
+ [0x25e6]: strokedCircle(300, 250, 100, 40),
637
+ // ◯ LARGE CIRCLE
638
+ [0x25ef]: strokedCircle(300, 250, 280, 40)
639
+ };
640
+ // =============================================================================
641
+ // Arrows (U+2190 – U+21FF)
642
+ // =============================================================================
643
+ const ARR = {
644
+ // ← LEFTWARDS ARROW
645
+ [0x2190]: {
646
+ width: W,
647
+ draw: p => {
648
+ p.lineWidth(50);
649
+ p.M(500, 250);
650
+ p.L(100, 250);
651
+ p.stroke();
652
+ p.M(100, 250);
653
+ p.L(250, 380);
654
+ p.L(250, 120);
655
+ p.Z();
656
+ p.fill();
657
+ }
658
+ },
659
+ // ↑ UPWARDS ARROW
660
+ [0x2191]: {
661
+ width: W,
662
+ draw: p => {
663
+ p.lineWidth(50);
664
+ p.M(300, 50);
665
+ p.L(300, 450);
666
+ p.stroke();
667
+ p.M(300, 450);
668
+ p.L(170, 300);
669
+ p.L(430, 300);
670
+ p.Z();
671
+ p.fill();
672
+ }
673
+ },
674
+ // → RIGHTWARDS ARROW
675
+ [0x2192]: {
676
+ width: W,
677
+ draw: p => {
678
+ p.lineWidth(50);
679
+ p.M(100, 250);
680
+ p.L(500, 250);
681
+ p.stroke();
682
+ p.M(500, 250);
683
+ p.L(350, 380);
684
+ p.L(350, 120);
685
+ p.Z();
686
+ p.fill();
687
+ }
688
+ },
689
+ // ↓ DOWNWARDS ARROW
690
+ [0x2193]: {
691
+ width: W,
692
+ draw: p => {
693
+ p.lineWidth(50);
694
+ p.M(300, 450);
695
+ p.L(300, 50);
696
+ p.stroke();
697
+ p.M(300, 50);
698
+ p.L(170, 200);
699
+ p.L(430, 200);
700
+ p.Z();
701
+ p.fill();
702
+ }
703
+ },
704
+ // ↔ LEFT RIGHT ARROW
705
+ [0x2194]: {
706
+ width: W,
707
+ draw: p => {
708
+ p.lineWidth(40);
709
+ p.M(150, 250);
710
+ p.L(450, 250);
711
+ p.stroke();
712
+ p.M(100, 250);
713
+ p.L(220, 360);
714
+ p.L(220, 140);
715
+ p.Z();
716
+ p.fill();
717
+ p.M(500, 250);
718
+ p.L(380, 360);
719
+ p.L(380, 140);
720
+ p.Z();
721
+ p.fill();
722
+ }
723
+ },
724
+ // ↕ UP DOWN ARROW
725
+ [0x2195]: {
726
+ width: W,
727
+ draw: p => {
728
+ p.lineWidth(40);
729
+ p.M(300, 100);
730
+ p.L(300, 400);
731
+ p.stroke();
732
+ p.M(300, 450);
733
+ p.L(190, 330);
734
+ p.L(410, 330);
735
+ p.Z();
736
+ p.fill();
737
+ p.M(300, 50);
738
+ p.L(190, 170);
739
+ p.L(410, 170);
740
+ p.Z();
741
+ p.fill();
742
+ }
743
+ },
744
+ // ↖ NORTH WEST ARROW
745
+ [0x2196]: {
746
+ width: W,
747
+ draw: p => {
748
+ p.lineWidth(50);
749
+ p.M(480, 50);
750
+ p.L(120, 410);
751
+ p.stroke();
752
+ p.M(120, 450);
753
+ p.L(120, 320);
754
+ p.L(250, 450);
755
+ p.Z();
756
+ p.fill();
757
+ }
758
+ },
759
+ // ↗ NORTH EAST ARROW
760
+ [0x2197]: {
761
+ width: W,
762
+ draw: p => {
763
+ p.lineWidth(50);
764
+ p.M(120, 50);
765
+ p.L(480, 410);
766
+ p.stroke();
767
+ p.M(480, 450);
768
+ p.L(350, 450);
769
+ p.L(480, 320);
770
+ p.Z();
771
+ p.fill();
772
+ }
773
+ },
774
+ // ↘ SOUTH EAST ARROW
775
+ [0x2198]: {
776
+ width: W,
777
+ draw: p => {
778
+ p.lineWidth(50);
779
+ p.M(120, 450);
780
+ p.L(480, 90);
781
+ p.stroke();
782
+ p.M(480, 50);
783
+ p.L(480, 180);
784
+ p.L(350, 50);
785
+ p.Z();
786
+ p.fill();
787
+ }
788
+ },
789
+ // ↙ SOUTH WEST ARROW
790
+ [0x2199]: {
791
+ width: W,
792
+ draw: p => {
793
+ p.lineWidth(50);
794
+ p.M(480, 450);
795
+ p.L(120, 90);
796
+ p.stroke();
797
+ p.M(120, 50);
798
+ p.L(250, 50);
799
+ p.L(120, 180);
800
+ p.Z();
801
+ p.fill();
802
+ }
803
+ },
804
+ // ↩ LEFTWARDS ARROW WITH HOOK
805
+ [0x21a9]: {
806
+ width: W,
807
+ draw: p => {
808
+ p.lineWidth(50);
809
+ p.M(500, 350);
810
+ p.L(200, 350);
811
+ p.C(120, 350, 80, 300, 80, 250);
812
+ p.C(80, 200, 120, 150, 200, 150);
813
+ p.L(350, 150);
814
+ p.stroke();
815
+ p.M(100, 350);
816
+ p.L(230, 430);
817
+ p.L(230, 270);
818
+ p.Z();
819
+ p.fill();
820
+ }
821
+ },
822
+ // ↪ RIGHTWARDS ARROW WITH HOOK
823
+ [0x21aa]: {
824
+ width: W,
825
+ draw: p => {
826
+ p.lineWidth(50);
827
+ p.M(100, 350);
828
+ p.L(400, 350);
829
+ p.C(480, 350, 520, 300, 520, 250);
830
+ p.C(520, 200, 480, 150, 400, 150);
831
+ p.L(250, 150);
832
+ p.stroke();
833
+ p.M(500, 350);
834
+ p.L(370, 430);
835
+ p.L(370, 270);
836
+ p.Z();
837
+ p.fill();
838
+ }
839
+ },
840
+ // ⇐ LEFTWARDS DOUBLE ARROW
841
+ [0x21d0]: {
842
+ width: W,
843
+ draw: p => {
844
+ p.M(100, 250);
845
+ p.L(280, 420);
846
+ p.L(280, 300);
847
+ p.L(500, 300);
848
+ p.L(500, 200);
849
+ p.L(280, 200);
850
+ p.L(280, 80);
851
+ p.Z();
852
+ p.fill();
853
+ }
854
+ },
855
+ // ⇑ UPWARDS DOUBLE ARROW
856
+ [0x21d1]: {
857
+ width: W,
858
+ draw: p => {
859
+ p.M(300, 480);
860
+ p.L(480, 280);
861
+ p.L(370, 280);
862
+ p.L(370, 30);
863
+ p.L(230, 30);
864
+ p.L(230, 280);
865
+ p.L(120, 280);
866
+ p.Z();
867
+ p.fill();
868
+ }
869
+ },
870
+ // ⇒ RIGHTWARDS DOUBLE ARROW
871
+ [0x21d2]: {
872
+ width: W,
873
+ draw: p => {
874
+ p.M(500, 250);
875
+ p.L(320, 420);
876
+ p.L(320, 300);
877
+ p.L(100, 300);
878
+ p.L(100, 200);
879
+ p.L(320, 200);
880
+ p.L(320, 80);
881
+ p.Z();
882
+ p.fill();
883
+ }
884
+ },
885
+ // ⇓ DOWNWARDS DOUBLE ARROW
886
+ [0x21d3]: {
887
+ width: W,
888
+ draw: p => {
889
+ p.M(300, 20);
890
+ p.L(120, 220);
891
+ p.L(230, 220);
892
+ p.L(230, 470);
893
+ p.L(370, 470);
894
+ p.L(370, 220);
895
+ p.L(480, 220);
896
+ p.Z();
897
+ p.fill();
898
+ }
899
+ },
900
+ // ⇔ LEFT RIGHT DOUBLE ARROW
901
+ [0x21d4]: {
902
+ width: W,
903
+ draw: p => {
904
+ p.M(80, 250);
905
+ p.L(220, 400);
906
+ p.L(220, 300);
907
+ p.L(380, 300);
908
+ p.L(380, 400);
909
+ p.L(520, 250);
910
+ p.L(380, 100);
911
+ p.L(380, 200);
912
+ p.L(220, 200);
913
+ p.L(220, 100);
914
+ p.Z();
915
+ p.fill();
916
+ }
917
+ }
918
+ };
919
+ // =============================================================================
920
+ // Mathematical Operators (U+2200 – U+22FF)
921
+ // =============================================================================
922
+ const MATH = {
923
+ // ∀ FOR ALL
924
+ [0x2200]: {
925
+ width: W,
926
+ draw: p => {
927
+ p.lineWidth(50);
928
+ p.M(100, 500);
929
+ p.L(300, 0);
930
+ p.L(500, 500);
931
+ p.stroke();
932
+ p.M(170, 200);
933
+ p.L(430, 200);
934
+ p.stroke();
935
+ }
936
+ },
937
+ // ∂ PARTIAL DIFFERENTIAL
938
+ [0x2202]: {
939
+ width: W,
940
+ draw: p => {
941
+ p.lineWidth(50);
942
+ p.circle(300, 200, 150);
943
+ p.stroke();
944
+ p.M(450, 200);
945
+ p.L(450, 450);
946
+ p.C(450, 500, 400, 520, 350, 500);
947
+ p.stroke();
948
+ }
949
+ },
950
+ // ∃ THERE EXISTS
951
+ [0x2203]: {
952
+ width: W,
953
+ draw: p => {
954
+ p.lineWidth(50);
955
+ p.M(450, 500);
956
+ p.L(150, 500);
957
+ p.L(150, 0);
958
+ p.L(450, 0);
959
+ p.stroke();
960
+ p.M(150, 250);
961
+ p.L(400, 250);
962
+ p.stroke();
963
+ }
964
+ },
965
+ // ∅ EMPTY SET
966
+ [0x2205]: {
967
+ width: W,
968
+ draw: p => {
969
+ p.lineWidth(50);
970
+ p.ellipse(300, 250, 180, 220);
971
+ p.stroke();
972
+ p.M(150, 50);
973
+ p.L(450, 450);
974
+ p.stroke();
975
+ }
976
+ },
977
+ // ∆ INCREMENT (triangle)
978
+ [0x2206]: {
979
+ width: W,
980
+ draw: p => {
981
+ p.lineWidth(50);
982
+ p.M(300, 500);
983
+ p.L(100, 0);
984
+ p.L(500, 0);
985
+ p.Z();
986
+ p.stroke();
987
+ }
988
+ },
989
+ // ∇ NABLA
990
+ [0x2207]: {
991
+ width: W,
992
+ draw: p => {
993
+ p.lineWidth(50);
994
+ p.M(100, 500);
995
+ p.L(500, 500);
996
+ p.L(300, 0);
997
+ p.Z();
998
+ p.stroke();
999
+ }
1000
+ },
1001
+ // ∈ ELEMENT OF
1002
+ [0x2208]: {
1003
+ width: W,
1004
+ draw: p => {
1005
+ p.lineWidth(50);
1006
+ p.M(480, 450);
1007
+ p.C(250, 450, 120, 380, 120, 250);
1008
+ p.C(120, 120, 250, 50, 480, 50);
1009
+ p.stroke();
1010
+ p.M(120, 250);
1011
+ p.L(430, 250);
1012
+ p.stroke();
1013
+ }
1014
+ },
1015
+ // ∉ NOT AN ELEMENT OF
1016
+ [0x2209]: {
1017
+ width: W,
1018
+ draw: p => {
1019
+ p.lineWidth(50);
1020
+ p.M(480, 450);
1021
+ p.C(250, 450, 120, 380, 120, 250);
1022
+ p.C(120, 120, 250, 50, 480, 50);
1023
+ p.stroke();
1024
+ p.M(120, 250);
1025
+ p.L(430, 250);
1026
+ p.stroke();
1027
+ p.M(160, 50);
1028
+ p.L(440, 450);
1029
+ p.stroke();
1030
+ }
1031
+ },
1032
+ // ∋ CONTAINS AS MEMBER
1033
+ [0x220b]: {
1034
+ width: W,
1035
+ draw: p => {
1036
+ p.lineWidth(50);
1037
+ p.M(120, 450);
1038
+ p.C(350, 450, 480, 380, 480, 250);
1039
+ p.C(480, 120, 350, 50, 120, 50);
1040
+ p.stroke();
1041
+ p.M(170, 250);
1042
+ p.L(480, 250);
1043
+ p.stroke();
1044
+ }
1045
+ },
1046
+ // ∎ END OF PROOF
1047
+ [0x220e]: filledRect(150, 50, 300, 400),
1048
+ // ∏ N-ARY PRODUCT
1049
+ [0x220f]: {
1050
+ width: W,
1051
+ draw: p => {
1052
+ p.lineWidth(50);
1053
+ p.M(120, 500);
1054
+ p.L(480, 500);
1055
+ p.stroke();
1056
+ p.M(170, 500);
1057
+ p.L(170, 0);
1058
+ p.stroke();
1059
+ p.M(430, 500);
1060
+ p.L(430, 0);
1061
+ p.stroke();
1062
+ }
1063
+ },
1064
+ // ∑ N-ARY SUMMATION
1065
+ [0x2211]: {
1066
+ width: W,
1067
+ draw: p => {
1068
+ p.lineWidth(50);
1069
+ p.M(480, 500);
1070
+ p.L(120, 500);
1071
+ p.L(300, 250);
1072
+ p.L(120, 0);
1073
+ p.L(480, 0);
1074
+ p.stroke();
1075
+ }
1076
+ },
1077
+ // − MINUS SIGN
1078
+ [0x2212]: {
1079
+ width: W,
1080
+ draw: p => {
1081
+ p.lineWidth(60);
1082
+ p.M(120, 250);
1083
+ p.L(480, 250);
1084
+ p.stroke();
1085
+ }
1086
+ },
1087
+ // ∓ MINUS-OR-PLUS SIGN
1088
+ [0x2213]: {
1089
+ width: W,
1090
+ draw: p => {
1091
+ p.lineWidth(50);
1092
+ p.M(120, 350);
1093
+ p.L(480, 350);
1094
+ p.stroke();
1095
+ p.M(300, 200);
1096
+ p.L(300, 0);
1097
+ p.stroke();
1098
+ p.M(120, 100);
1099
+ p.L(480, 100);
1100
+ p.stroke();
1101
+ }
1102
+ },
1103
+ // ∗ ASTERISK OPERATOR
1104
+ [0x2217]: {
1105
+ width: W,
1106
+ draw: p => {
1107
+ p.lineWidth(50);
1108
+ p.M(300, 400);
1109
+ p.L(300, 100);
1110
+ p.stroke();
1111
+ p.M(140, 350);
1112
+ p.L(460, 150);
1113
+ p.stroke();
1114
+ p.M(140, 150);
1115
+ p.L(460, 350);
1116
+ p.stroke();
1117
+ }
1118
+ },
1119
+ // √ SQUARE ROOT
1120
+ [0x221a]: {
1121
+ width: W,
1122
+ draw: p => {
1123
+ p.lineWidth(50);
1124
+ p.M(60, 250);
1125
+ p.L(150, 200);
1126
+ p.L(250, 0);
1127
+ p.L(540, 500);
1128
+ p.stroke();
1129
+ }
1130
+ },
1131
+ // ∞ INFINITY
1132
+ [0x221e]: {
1133
+ width: 700,
1134
+ draw: p => {
1135
+ p.lineWidth(50);
1136
+ p.M(350, 250);
1137
+ p.C(350, 400, 500, 450, 550, 350);
1138
+ p.C(600, 250, 550, 100, 500, 100);
1139
+ p.C(450, 100, 350, 250, 350, 250);
1140
+ p.C(350, 250, 250, 400, 200, 400);
1141
+ p.C(150, 400, 100, 250, 150, 150);
1142
+ p.C(200, 50, 350, 100, 350, 250);
1143
+ p.stroke();
1144
+ }
1145
+ },
1146
+ // ∠ ANGLE
1147
+ [0x2220]: {
1148
+ width: W,
1149
+ draw: p => {
1150
+ p.lineWidth(50);
1151
+ p.M(480, 50);
1152
+ p.L(120, 50);
1153
+ p.L(380, 450);
1154
+ p.stroke();
1155
+ }
1156
+ },
1157
+ // ∧ LOGICAL AND
1158
+ [0x2227]: {
1159
+ width: W,
1160
+ draw: p => {
1161
+ p.lineWidth(50);
1162
+ p.M(100, 50);
1163
+ p.L(300, 450);
1164
+ p.L(500, 50);
1165
+ p.stroke();
1166
+ }
1167
+ },
1168
+ // ∨ LOGICAL OR
1169
+ [0x2228]: {
1170
+ width: W,
1171
+ draw: p => {
1172
+ p.lineWidth(50);
1173
+ p.M(100, 450);
1174
+ p.L(300, 50);
1175
+ p.L(500, 450);
1176
+ p.stroke();
1177
+ }
1178
+ },
1179
+ // ∩ INTERSECTION
1180
+ [0x2229]: {
1181
+ width: W,
1182
+ draw: p => {
1183
+ p.lineWidth(50);
1184
+ p.M(120, 50);
1185
+ p.L(120, 300);
1186
+ p.C(120, 470, 250, 500, 300, 500);
1187
+ p.C(350, 500, 480, 470, 480, 300);
1188
+ p.L(480, 50);
1189
+ p.stroke();
1190
+ }
1191
+ },
1192
+ // ∪ UNION
1193
+ [0x222a]: {
1194
+ width: W,
1195
+ draw: p => {
1196
+ p.lineWidth(50);
1197
+ p.M(120, 450);
1198
+ p.L(120, 200);
1199
+ p.C(120, 30, 250, 0, 300, 0);
1200
+ p.C(350, 0, 480, 30, 480, 200);
1201
+ p.L(480, 450);
1202
+ p.stroke();
1203
+ }
1204
+ },
1205
+ // ∫ INTEGRAL
1206
+ [0x222b]: {
1207
+ width: W,
1208
+ draw: p => {
1209
+ p.lineWidth(50);
1210
+ p.M(380, 530);
1211
+ p.C(350, 530, 300, 500, 300, 450);
1212
+ p.L(300, 50);
1213
+ p.C(300, 0, 250, -30, 220, -30);
1214
+ p.stroke();
1215
+ }
1216
+ },
1217
+ // ≈ ALMOST EQUAL TO
1218
+ [0x2248]: {
1219
+ width: W,
1220
+ draw: p => {
1221
+ p.lineWidth(50);
1222
+ p.M(120, 350);
1223
+ p.C(200, 430, 350, 430, 480, 350);
1224
+ p.stroke();
1225
+ p.M(120, 200);
1226
+ p.C(200, 280, 350, 280, 480, 200);
1227
+ p.stroke();
1228
+ }
1229
+ },
1230
+ // ≠ NOT EQUAL TO
1231
+ [0x2260]: {
1232
+ width: W,
1233
+ draw: p => {
1234
+ p.lineWidth(50);
1235
+ p.M(120, 330);
1236
+ p.L(480, 330);
1237
+ p.stroke();
1238
+ p.M(120, 170);
1239
+ p.L(480, 170);
1240
+ p.stroke();
1241
+ p.M(380, 450);
1242
+ p.L(220, 50);
1243
+ p.stroke();
1244
+ }
1245
+ },
1246
+ // ≡ IDENTICAL TO
1247
+ [0x2261]: {
1248
+ width: W,
1249
+ draw: p => {
1250
+ p.lineWidth(50);
1251
+ p.M(120, 380);
1252
+ p.L(480, 380);
1253
+ p.stroke();
1254
+ p.M(120, 250);
1255
+ p.L(480, 250);
1256
+ p.stroke();
1257
+ p.M(120, 120);
1258
+ p.L(480, 120);
1259
+ p.stroke();
1260
+ }
1261
+ },
1262
+ // ≤ LESS-THAN OR EQUAL TO
1263
+ [0x2264]: {
1264
+ width: W,
1265
+ draw: p => {
1266
+ p.lineWidth(50);
1267
+ p.M(480, 420);
1268
+ p.L(120, 250);
1269
+ p.L(480, 80);
1270
+ p.stroke();
1271
+ p.M(120, 30);
1272
+ p.L(480, 30);
1273
+ p.stroke();
1274
+ }
1275
+ },
1276
+ // ≥ GREATER-THAN OR EQUAL TO
1277
+ [0x2265]: {
1278
+ width: W,
1279
+ draw: p => {
1280
+ p.lineWidth(50);
1281
+ p.M(120, 420);
1282
+ p.L(480, 250);
1283
+ p.L(120, 80);
1284
+ p.stroke();
1285
+ p.M(120, 30);
1286
+ p.L(480, 30);
1287
+ p.stroke();
1288
+ }
1289
+ },
1290
+ // ⊂ SUBSET OF
1291
+ [0x2282]: {
1292
+ width: W,
1293
+ draw: p => {
1294
+ p.lineWidth(50);
1295
+ p.M(470, 450);
1296
+ p.C(200, 450, 130, 350, 130, 250);
1297
+ p.C(130, 150, 200, 50, 470, 50);
1298
+ p.stroke();
1299
+ }
1300
+ },
1301
+ // ⊃ SUPERSET OF
1302
+ [0x2283]: {
1303
+ width: W,
1304
+ draw: p => {
1305
+ p.lineWidth(50);
1306
+ p.M(130, 450);
1307
+ p.C(400, 450, 470, 350, 470, 250);
1308
+ p.C(470, 150, 400, 50, 130, 50);
1309
+ p.stroke();
1310
+ }
1311
+ },
1312
+ // ⊕ CIRCLED PLUS
1313
+ [0x2295]: {
1314
+ width: W,
1315
+ draw: p => {
1316
+ p.lineWidth(45);
1317
+ p.circle(300, 250, 200);
1318
+ p.stroke();
1319
+ p.M(100, 250);
1320
+ p.L(500, 250);
1321
+ p.stroke();
1322
+ p.M(300, 50);
1323
+ p.L(300, 450);
1324
+ p.stroke();
1325
+ }
1326
+ },
1327
+ // ⊖ CIRCLED MINUS
1328
+ [0x2296]: {
1329
+ width: W,
1330
+ draw: p => {
1331
+ p.lineWidth(45);
1332
+ p.circle(300, 250, 200);
1333
+ p.stroke();
1334
+ p.M(100, 250);
1335
+ p.L(500, 250);
1336
+ p.stroke();
1337
+ }
1338
+ },
1339
+ // ⊗ CIRCLED TIMES
1340
+ [0x2297]: {
1341
+ width: W,
1342
+ draw: p => {
1343
+ p.lineWidth(45);
1344
+ p.circle(300, 250, 200);
1345
+ p.stroke();
1346
+ p.M(160, 110);
1347
+ p.L(440, 390);
1348
+ p.stroke();
1349
+ p.M(160, 390);
1350
+ p.L(440, 110);
1351
+ p.stroke();
1352
+ }
1353
+ },
1354
+ // ⊘ CIRCLED DIVISION SLASH
1355
+ [0x2298]: {
1356
+ width: W,
1357
+ draw: p => {
1358
+ p.lineWidth(45);
1359
+ p.circle(300, 250, 200);
1360
+ p.stroke();
1361
+ p.M(160, 100);
1362
+ p.L(440, 400);
1363
+ p.stroke();
1364
+ }
1365
+ },
1366
+ // ⊙ CIRCLED DOT OPERATOR
1367
+ [0x2299]: {
1368
+ width: W,
1369
+ draw: p => {
1370
+ p.lineWidth(45);
1371
+ p.circle(300, 250, 200);
1372
+ p.stroke();
1373
+ p.circle(300, 250, 40);
1374
+ p.fill();
1375
+ }
1376
+ }
1377
+ };
1378
+ // =============================================================================
1379
+ // Dingbats / Checkmarks / Misc Symbols (U+2600–U+27BF)
1380
+ // =============================================================================
1381
+ const DING = {
1382
+ // ☀ BLACK SUN WITH RAYS — simplified as circle with lines
1383
+ [0x2600]: {
1384
+ width: W,
1385
+ draw: p => {
1386
+ p.circle(300, 250, 130);
1387
+ p.fill();
1388
+ p.lineWidth(40);
1389
+ for (let a = 0; a < 360; a += 45) {
1390
+ const r1 = 160;
1391
+ const r2 = 240;
1392
+ const rad = (a * Math.PI) / 180;
1393
+ p.M(300 + r1 * Math.cos(rad), 250 + r1 * Math.sin(rad));
1394
+ p.L(300 + r2 * Math.cos(rad), 250 + r2 * Math.sin(rad));
1395
+ }
1396
+ p.stroke();
1397
+ }
1398
+ },
1399
+ // ★ BLACK STAR
1400
+ [0x2605]: {
1401
+ width: W,
1402
+ draw: p => {
1403
+ const cx = 300;
1404
+ const cy = 260;
1405
+ const R = 230;
1406
+ const r = 90;
1407
+ for (let i = 0; i < 5; i++) {
1408
+ const a1 = ((i * 72 - 90) * Math.PI) / 180;
1409
+ const a2 = ((i * 72 + 36 - 90) * Math.PI) / 180;
1410
+ if (i === 0) {
1411
+ p.M(cx + R * Math.cos(a1), cy + R * Math.sin(a1));
1412
+ }
1413
+ else {
1414
+ p.L(cx + R * Math.cos(a1), cy + R * Math.sin(a1));
1415
+ }
1416
+ p.L(cx + r * Math.cos(a2), cy + r * Math.sin(a2));
1417
+ }
1418
+ p.Z();
1419
+ p.fill();
1420
+ }
1421
+ },
1422
+ // ☆ WHITE STAR
1423
+ [0x2606]: {
1424
+ width: W,
1425
+ draw: p => {
1426
+ p.lineWidth(35);
1427
+ const cx = 300;
1428
+ const cy = 260;
1429
+ const R = 230;
1430
+ const r = 90;
1431
+ for (let i = 0; i < 5; i++) {
1432
+ const a1 = ((i * 72 - 90) * Math.PI) / 180;
1433
+ const a2 = ((i * 72 + 36 - 90) * Math.PI) / 180;
1434
+ if (i === 0) {
1435
+ p.M(cx + R * Math.cos(a1), cy + R * Math.sin(a1));
1436
+ }
1437
+ else {
1438
+ p.L(cx + R * Math.cos(a1), cy + R * Math.sin(a1));
1439
+ }
1440
+ p.L(cx + r * Math.cos(a2), cy + r * Math.sin(a2));
1441
+ }
1442
+ p.Z();
1443
+ p.stroke();
1444
+ }
1445
+ },
1446
+ // ☐ BALLOT BOX
1447
+ [0x2610]: strokedRect(100, 30, 400, 400, 45),
1448
+ // ☑ BALLOT BOX WITH CHECK
1449
+ [0x2611]: {
1450
+ width: W,
1451
+ draw: p => {
1452
+ p.lineWidth(45);
1453
+ p.rect(100, 30, 400, 400);
1454
+ p.stroke();
1455
+ p.lineWidth(55);
1456
+ p.M(180, 230);
1457
+ p.L(280, 130);
1458
+ p.L(430, 350);
1459
+ p.stroke();
1460
+ }
1461
+ },
1462
+ // ☒ BALLOT BOX WITH X
1463
+ [0x2612]: {
1464
+ width: W,
1465
+ draw: p => {
1466
+ p.lineWidth(45);
1467
+ p.rect(100, 30, 400, 400);
1468
+ p.stroke();
1469
+ p.lineWidth(50);
1470
+ p.M(170, 100);
1471
+ p.L(430, 360);
1472
+ p.stroke();
1473
+ p.M(170, 360);
1474
+ p.L(430, 100);
1475
+ p.stroke();
1476
+ }
1477
+ },
1478
+ // ☓ SALTIRE (X)
1479
+ [0x2613]: {
1480
+ width: W,
1481
+ draw: p => {
1482
+ p.lineWidth(55);
1483
+ p.M(120, 50);
1484
+ p.L(480, 450);
1485
+ p.stroke();
1486
+ p.M(120, 450);
1487
+ p.L(480, 50);
1488
+ p.stroke();
1489
+ }
1490
+ },
1491
+ // ☛ BLACK RIGHT POINTING INDEX — simplified arrow
1492
+ [0x261b]: {
1493
+ width: W,
1494
+ draw: p => {
1495
+ p.M(500, 250);
1496
+ p.L(300, 400);
1497
+ p.L(300, 300);
1498
+ p.L(100, 300);
1499
+ p.L(100, 200);
1500
+ p.L(300, 200);
1501
+ p.L(300, 100);
1502
+ p.Z();
1503
+ p.fill();
1504
+ }
1505
+ },
1506
+ // ☞ WHITE RIGHT POINTING INDEX
1507
+ [0x261e]: {
1508
+ width: W,
1509
+ draw: p => {
1510
+ p.lineWidth(40);
1511
+ p.M(500, 250);
1512
+ p.L(300, 400);
1513
+ p.L(300, 300);
1514
+ p.L(100, 300);
1515
+ p.L(100, 200);
1516
+ p.L(300, 200);
1517
+ p.L(300, 100);
1518
+ p.Z();
1519
+ p.stroke();
1520
+ }
1521
+ },
1522
+ // ♀ FEMALE SIGN
1523
+ [0x2640]: {
1524
+ width: W,
1525
+ draw: p => {
1526
+ p.lineWidth(50);
1527
+ p.circle(300, 330, 150);
1528
+ p.stroke();
1529
+ p.M(300, 180);
1530
+ p.L(300, 0);
1531
+ p.stroke();
1532
+ p.M(220, 90);
1533
+ p.L(380, 90);
1534
+ p.stroke();
1535
+ }
1536
+ },
1537
+ // ♂ MALE SIGN
1538
+ [0x2642]: {
1539
+ width: W,
1540
+ draw: p => {
1541
+ p.lineWidth(50);
1542
+ p.circle(250, 220, 150);
1543
+ p.stroke();
1544
+ p.M(360, 330);
1545
+ p.L(500, 470);
1546
+ p.stroke();
1547
+ p.M(400, 470);
1548
+ p.L(500, 470);
1549
+ p.L(500, 370);
1550
+ p.stroke();
1551
+ }
1552
+ },
1553
+ // ♠ BLACK SPADE SUIT
1554
+ [0x2660]: {
1555
+ width: W,
1556
+ draw: p => {
1557
+ p.M(300, 500);
1558
+ p.C(300, 500, 80, 350, 80, 200);
1559
+ p.C(80, 100, 200, 50, 300, 180);
1560
+ p.C(400, 50, 520, 100, 520, 200);
1561
+ p.C(520, 350, 300, 500, 300, 500);
1562
+ p.Z();
1563
+ p.fill();
1564
+ p.M(250, 0);
1565
+ p.L(350, 0);
1566
+ p.L(320, 150);
1567
+ p.L(280, 150);
1568
+ p.Z();
1569
+ p.fill();
1570
+ }
1571
+ },
1572
+ // ♣ BLACK CLUB SUIT
1573
+ [0x2663]: {
1574
+ width: W,
1575
+ draw: p => {
1576
+ p.circle(300, 370, 110);
1577
+ p.fill();
1578
+ p.circle(190, 230, 100);
1579
+ p.fill();
1580
+ p.circle(410, 230, 100);
1581
+ p.fill();
1582
+ p.rect(270, 0, 60, 200);
1583
+ p.fill();
1584
+ }
1585
+ },
1586
+ // ♥ BLACK HEART SUIT
1587
+ [0x2665]: {
1588
+ width: W,
1589
+ draw: p => {
1590
+ p.M(300, 80);
1591
+ p.C(300, 80, 80, 200, 80, 340);
1592
+ p.C(80, 440, 180, 490, 300, 490);
1593
+ p.C(420, 490, 520, 440, 520, 340);
1594
+ p.C(520, 200, 300, 80, 300, 80);
1595
+ p.Z();
1596
+ p.fill();
1597
+ }
1598
+ },
1599
+ // ♦ BLACK DIAMOND SUIT
1600
+ [0x2666]: {
1601
+ width: W,
1602
+ draw: p => {
1603
+ p.M(300, 500);
1604
+ p.L(500, 250);
1605
+ p.L(300, 0);
1606
+ p.L(100, 250);
1607
+ p.Z();
1608
+ p.fill();
1609
+ }
1610
+ },
1611
+ // ♩ QUARTER NOTE
1612
+ [0x2669]: {
1613
+ width: W,
1614
+ draw: p => {
1615
+ p.ellipse(230, 80, 100, 70);
1616
+ p.fill();
1617
+ p.lineWidth(40);
1618
+ p.M(330, 80);
1619
+ p.L(330, 480);
1620
+ p.stroke();
1621
+ }
1622
+ },
1623
+ // ♪ EIGHTH NOTE
1624
+ [0x266a]: {
1625
+ width: W,
1626
+ draw: p => {
1627
+ p.ellipse(200, 80, 100, 70);
1628
+ p.fill();
1629
+ p.lineWidth(35);
1630
+ p.M(300, 80);
1631
+ p.L(300, 480);
1632
+ p.stroke();
1633
+ p.M(300, 480);
1634
+ p.C(350, 450, 430, 400, 430, 350);
1635
+ p.stroke();
1636
+ }
1637
+ },
1638
+ // ♭ MUSIC FLAT SIGN
1639
+ [0x266d]: {
1640
+ width: W,
1641
+ draw: p => {
1642
+ p.lineWidth(40);
1643
+ p.M(200, 0);
1644
+ p.L(200, 500);
1645
+ p.stroke();
1646
+ p.M(200, 200);
1647
+ p.C(350, 200, 400, 100, 200, 0);
1648
+ p.stroke();
1649
+ }
1650
+ },
1651
+ // ♯ MUSIC SHARP SIGN
1652
+ [0x266f]: {
1653
+ width: W,
1654
+ draw: p => {
1655
+ p.lineWidth(40);
1656
+ p.M(220, 0);
1657
+ p.L(220, 500);
1658
+ p.stroke();
1659
+ p.M(380, 0);
1660
+ p.L(380, 500);
1661
+ p.stroke();
1662
+ p.lineWidth(50);
1663
+ p.M(140, 180);
1664
+ p.L(460, 240);
1665
+ p.stroke();
1666
+ p.M(140, 310);
1667
+ p.L(460, 370);
1668
+ p.stroke();
1669
+ }
1670
+ },
1671
+ // ✓ CHECK MARK
1672
+ [0x2713]: {
1673
+ width: W,
1674
+ draw: p => {
1675
+ p.lineWidth(65);
1676
+ p.M(100, 250);
1677
+ p.L(240, 80);
1678
+ p.L(500, 450);
1679
+ p.stroke();
1680
+ }
1681
+ },
1682
+ // ✔ HEAVY CHECK MARK
1683
+ [0x2714]: {
1684
+ width: W,
1685
+ draw: p => {
1686
+ p.lineWidth(85);
1687
+ p.M(100, 250);
1688
+ p.L(240, 80);
1689
+ p.L(500, 450);
1690
+ p.stroke();
1691
+ }
1692
+ },
1693
+ // ✕ MULTIPLICATION X
1694
+ [0x2715]: {
1695
+ width: W,
1696
+ draw: p => {
1697
+ p.lineWidth(55);
1698
+ p.M(140, 70);
1699
+ p.L(460, 430);
1700
+ p.stroke();
1701
+ p.M(140, 430);
1702
+ p.L(460, 70);
1703
+ p.stroke();
1704
+ }
1705
+ },
1706
+ // ✖ HEAVY MULTIPLICATION X
1707
+ [0x2716]: {
1708
+ width: W,
1709
+ draw: p => {
1710
+ p.lineWidth(75);
1711
+ p.M(140, 70);
1712
+ p.L(460, 430);
1713
+ p.stroke();
1714
+ p.M(140, 430);
1715
+ p.L(460, 70);
1716
+ p.stroke();
1717
+ }
1718
+ },
1719
+ // ✗ BALLOT X
1720
+ [0x2717]: {
1721
+ width: W,
1722
+ draw: p => {
1723
+ p.lineWidth(60);
1724
+ p.M(130, 60);
1725
+ p.L(470, 440);
1726
+ p.stroke();
1727
+ p.M(130, 440);
1728
+ p.L(470, 60);
1729
+ p.stroke();
1730
+ }
1731
+ },
1732
+ // ✘ HEAVY BALLOT X
1733
+ [0x2718]: {
1734
+ width: W,
1735
+ draw: p => {
1736
+ p.lineWidth(80);
1737
+ p.M(130, 60);
1738
+ p.L(470, 440);
1739
+ p.stroke();
1740
+ p.M(130, 440);
1741
+ p.L(470, 60);
1742
+ p.stroke();
1743
+ }
1744
+ },
1745
+ // ✚ HEAVY GREEK CROSS
1746
+ [0x271a]: {
1747
+ width: W,
1748
+ draw: p => {
1749
+ p.rect(210, 30, 180, 440);
1750
+ p.fill();
1751
+ p.rect(90, 160, 420, 180);
1752
+ p.fill();
1753
+ }
1754
+ },
1755
+ // ✜ HEAVY OPEN CENTRE CROSS — simplified
1756
+ [0x271c]: {
1757
+ width: W,
1758
+ draw: p => {
1759
+ p.lineWidth(60);
1760
+ p.M(300, 30);
1761
+ p.L(300, 470);
1762
+ p.stroke();
1763
+ p.M(90, 250);
1764
+ p.L(510, 250);
1765
+ p.stroke();
1766
+ }
1767
+ },
1768
+ // ✠ MALTESE CROSS — simplified
1769
+ [0x2720]: {
1770
+ width: W,
1771
+ draw: p => {
1772
+ p.M(300, 480);
1773
+ p.L(250, 320);
1774
+ p.L(100, 350);
1775
+ p.L(200, 250);
1776
+ p.L(100, 150);
1777
+ p.L(250, 180);
1778
+ p.L(300, 20);
1779
+ p.L(350, 180);
1780
+ p.L(500, 150);
1781
+ p.L(400, 250);
1782
+ p.L(500, 350);
1783
+ p.L(350, 320);
1784
+ p.Z();
1785
+ p.fill();
1786
+ }
1787
+ },
1788
+ // ❤ HEAVY BLACK HEART
1789
+ [0x2764]: {
1790
+ width: W,
1791
+ draw: p => {
1792
+ p.M(300, 80);
1793
+ p.C(300, 80, 60, 220, 60, 350);
1794
+ p.C(60, 470, 180, 500, 300, 500);
1795
+ p.C(420, 500, 540, 470, 540, 350);
1796
+ p.C(540, 220, 300, 80, 300, 80);
1797
+ p.Z();
1798
+ p.fill();
1799
+ }
1800
+ },
1801
+ // ❶–❿ DINGBAT NEGATIVE CIRCLED (1–10)
1802
+ ...(() => {
1803
+ const r = {};
1804
+ for (let i = 0; i < 10; i++) {
1805
+ const cp = 0x2776 + i;
1806
+ r[cp] = {
1807
+ width: W,
1808
+ draw: p => {
1809
+ p.circle(300, 250, 240);
1810
+ p.fill(); /* digit rendered as glyph outline – too complex for paths; filled circle is best approximation */
1811
+ }
1812
+ };
1813
+ }
1814
+ return r;
1815
+ })(),
1816
+ // ①–⑩ CIRCLED DIGIT (U+2460–U+2469)
1817
+ ...(() => {
1818
+ const r = {};
1819
+ for (let i = 0; i < 10; i++) {
1820
+ r[0x2460 + i] = {
1821
+ width: W,
1822
+ draw: p => {
1823
+ p.lineWidth(40);
1824
+ p.circle(300, 250, 220);
1825
+ p.stroke();
1826
+ }
1827
+ };
1828
+ }
1829
+ return r;
1830
+ })()
1831
+ };
1832
+ // =============================================================================
1833
+ // Miscellaneous Technical (U+2300 – U+23FF)
1834
+ // =============================================================================
1835
+ const TECH = {
1836
+ // ⌀ DIAMETER SIGN
1837
+ [0x2300]: {
1838
+ width: W,
1839
+ draw: p => {
1840
+ p.lineWidth(45);
1841
+ p.circle(300, 250, 200);
1842
+ p.stroke();
1843
+ p.M(140, 90);
1844
+ p.L(460, 410);
1845
+ p.stroke();
1846
+ }
1847
+ },
1848
+ // ⌂ HOUSE
1849
+ [0x2302]: {
1850
+ width: W,
1851
+ draw: p => {
1852
+ p.lineWidth(45);
1853
+ p.M(300, 480);
1854
+ p.L(520, 250);
1855
+ p.L(520, 30);
1856
+ p.L(80, 30);
1857
+ p.L(80, 250);
1858
+ p.Z();
1859
+ p.stroke();
1860
+ }
1861
+ },
1862
+ // ⌘ PLACE OF INTEREST SIGN (command key) — simplified as looped square
1863
+ [0x2318]: {
1864
+ width: W,
1865
+ draw: p => {
1866
+ p.lineWidth(45);
1867
+ p.rect(180, 130, 240, 240);
1868
+ p.stroke();
1869
+ p.circle(180, 370, 70);
1870
+ p.stroke();
1871
+ p.circle(420, 370, 70);
1872
+ p.stroke();
1873
+ p.circle(180, 130, 70);
1874
+ p.stroke();
1875
+ p.circle(420, 130, 70);
1876
+ p.stroke();
1877
+ }
1878
+ },
1879
+ // ⌛ HOURGLASS
1880
+ [0x231b]: {
1881
+ width: W,
1882
+ draw: p => {
1883
+ p.lineWidth(45);
1884
+ p.M(120, 480);
1885
+ p.L(480, 480);
1886
+ p.stroke();
1887
+ p.M(120, 20);
1888
+ p.L(480, 20);
1889
+ p.stroke();
1890
+ p.M(150, 480);
1891
+ p.L(300, 250);
1892
+ p.L(450, 480);
1893
+ p.stroke();
1894
+ p.M(150, 20);
1895
+ p.L(300, 250);
1896
+ p.L(450, 20);
1897
+ p.stroke();
1898
+ }
1899
+ },
1900
+ // ⏎ RETURN SYMBOL
1901
+ [0x23ce]: {
1902
+ width: W,
1903
+ draw: p => {
1904
+ p.lineWidth(45);
1905
+ p.M(480, 420);
1906
+ p.L(480, 200);
1907
+ p.L(150, 200);
1908
+ p.stroke();
1909
+ p.M(150, 200);
1910
+ p.L(280, 310);
1911
+ p.L(280, 90);
1912
+ p.Z();
1913
+ p.fill();
1914
+ }
1915
+ }
1916
+ };
1917
+ // =============================================================================
1918
+ // Box Drawing (U+2500 – U+257F)
1919
+ // =============================================================================
1920
+ const BOX = {
1921
+ // ─ BOX DRAWINGS LIGHT HORIZONTAL
1922
+ [0x2500]: {
1923
+ width: W,
1924
+ draw: p => {
1925
+ p.lineWidth(40);
1926
+ p.M(0, 250);
1927
+ p.L(600, 250);
1928
+ p.stroke();
1929
+ }
1930
+ },
1931
+ // ━ BOX DRAWINGS HEAVY HORIZONTAL
1932
+ [0x2501]: {
1933
+ width: W,
1934
+ draw: p => {
1935
+ p.lineWidth(80);
1936
+ p.M(0, 250);
1937
+ p.L(600, 250);
1938
+ p.stroke();
1939
+ }
1940
+ },
1941
+ // │ BOX DRAWINGS LIGHT VERTICAL
1942
+ [0x2502]: {
1943
+ width: W,
1944
+ draw: p => {
1945
+ p.lineWidth(40);
1946
+ p.M(300, 0);
1947
+ p.L(300, 500);
1948
+ p.stroke();
1949
+ }
1950
+ },
1951
+ // ┃ BOX DRAWINGS HEAVY VERTICAL
1952
+ [0x2503]: {
1953
+ width: W,
1954
+ draw: p => {
1955
+ p.lineWidth(80);
1956
+ p.M(300, 0);
1957
+ p.L(300, 500);
1958
+ p.stroke();
1959
+ }
1960
+ },
1961
+ // ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT
1962
+ [0x250c]: {
1963
+ width: W,
1964
+ draw: p => {
1965
+ p.lineWidth(40);
1966
+ p.M(300, 0);
1967
+ p.L(300, 250);
1968
+ p.L(600, 250);
1969
+ p.stroke();
1970
+ }
1971
+ },
1972
+ // ┐ BOX DRAWINGS LIGHT DOWN AND LEFT
1973
+ [0x2510]: {
1974
+ width: W,
1975
+ draw: p => {
1976
+ p.lineWidth(40);
1977
+ p.M(300, 0);
1978
+ p.L(300, 250);
1979
+ p.L(0, 250);
1980
+ p.stroke();
1981
+ }
1982
+ },
1983
+ // └ BOX DRAWINGS LIGHT UP AND RIGHT
1984
+ [0x2514]: {
1985
+ width: W,
1986
+ draw: p => {
1987
+ p.lineWidth(40);
1988
+ p.M(300, 500);
1989
+ p.L(300, 250);
1990
+ p.L(600, 250);
1991
+ p.stroke();
1992
+ }
1993
+ },
1994
+ // ┘ BOX DRAWINGS LIGHT UP AND LEFT
1995
+ [0x2518]: {
1996
+ width: W,
1997
+ draw: p => {
1998
+ p.lineWidth(40);
1999
+ p.M(300, 500);
2000
+ p.L(300, 250);
2001
+ p.L(0, 250);
2002
+ p.stroke();
2003
+ }
2004
+ },
2005
+ // ├ BOX DRAWINGS LIGHT VERTICAL AND RIGHT
2006
+ [0x251c]: {
2007
+ width: W,
2008
+ draw: p => {
2009
+ p.lineWidth(40);
2010
+ p.M(300, 0);
2011
+ p.L(300, 500);
2012
+ p.stroke();
2013
+ p.M(300, 250);
2014
+ p.L(600, 250);
2015
+ p.stroke();
2016
+ }
2017
+ },
2018
+ // ┤ BOX DRAWINGS LIGHT VERTICAL AND LEFT
2019
+ [0x2524]: {
2020
+ width: W,
2021
+ draw: p => {
2022
+ p.lineWidth(40);
2023
+ p.M(300, 0);
2024
+ p.L(300, 500);
2025
+ p.stroke();
2026
+ p.M(300, 250);
2027
+ p.L(0, 250);
2028
+ p.stroke();
2029
+ }
2030
+ },
2031
+ // ┬ BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
2032
+ [0x252c]: {
2033
+ width: W,
2034
+ draw: p => {
2035
+ p.lineWidth(40);
2036
+ p.M(0, 250);
2037
+ p.L(600, 250);
2038
+ p.stroke();
2039
+ p.M(300, 250);
2040
+ p.L(300, 0);
2041
+ p.stroke();
2042
+ }
2043
+ },
2044
+ // ┴ BOX DRAWINGS LIGHT UP AND HORIZONTAL
2045
+ [0x2534]: {
2046
+ width: W,
2047
+ draw: p => {
2048
+ p.lineWidth(40);
2049
+ p.M(0, 250);
2050
+ p.L(600, 250);
2051
+ p.stroke();
2052
+ p.M(300, 250);
2053
+ p.L(300, 500);
2054
+ p.stroke();
2055
+ }
2056
+ },
2057
+ // ┼ BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
2058
+ [0x253c]: {
2059
+ width: W,
2060
+ draw: p => {
2061
+ p.lineWidth(40);
2062
+ p.M(0, 250);
2063
+ p.L(600, 250);
2064
+ p.stroke();
2065
+ p.M(300, 0);
2066
+ p.L(300, 500);
2067
+ p.stroke();
2068
+ }
2069
+ },
2070
+ // ╔ BOX DRAWINGS DOUBLE DOWN AND RIGHT
2071
+ [0x2554]: {
2072
+ width: W,
2073
+ draw: p => {
2074
+ p.lineWidth(30);
2075
+ p.M(270, 0);
2076
+ p.L(270, 270);
2077
+ p.L(600, 270);
2078
+ p.stroke();
2079
+ p.M(330, 0);
2080
+ p.L(330, 230);
2081
+ p.L(600, 230);
2082
+ p.stroke();
2083
+ }
2084
+ },
2085
+ // ╗ BOX DRAWINGS DOUBLE DOWN AND LEFT
2086
+ [0x2557]: {
2087
+ width: W,
2088
+ draw: p => {
2089
+ p.lineWidth(30);
2090
+ p.M(330, 0);
2091
+ p.L(330, 270);
2092
+ p.L(0, 270);
2093
+ p.stroke();
2094
+ p.M(270, 0);
2095
+ p.L(270, 230);
2096
+ p.L(0, 230);
2097
+ p.stroke();
2098
+ }
2099
+ },
2100
+ // ╚ BOX DRAWINGS DOUBLE UP AND RIGHT
2101
+ [0x255a]: {
2102
+ width: W,
2103
+ draw: p => {
2104
+ p.lineWidth(30);
2105
+ p.M(270, 500);
2106
+ p.L(270, 230);
2107
+ p.L(600, 230);
2108
+ p.stroke();
2109
+ p.M(330, 500);
2110
+ p.L(330, 270);
2111
+ p.L(600, 270);
2112
+ p.stroke();
2113
+ }
2114
+ },
2115
+ // ╝ BOX DRAWINGS DOUBLE UP AND LEFT
2116
+ [0x255d]: {
2117
+ width: W,
2118
+ draw: p => {
2119
+ p.lineWidth(30);
2120
+ p.M(330, 500);
2121
+ p.L(330, 230);
2122
+ p.L(0, 230);
2123
+ p.stroke();
2124
+ p.M(270, 500);
2125
+ p.L(270, 270);
2126
+ p.L(0, 270);
2127
+ p.stroke();
2128
+ }
2129
+ },
2130
+ // ═ BOX DRAWINGS DOUBLE HORIZONTAL
2131
+ [0x2550]: {
2132
+ width: W,
2133
+ draw: p => {
2134
+ p.lineWidth(30);
2135
+ p.M(0, 270);
2136
+ p.L(600, 270);
2137
+ p.stroke();
2138
+ p.M(0, 230);
2139
+ p.L(600, 230);
2140
+ p.stroke();
2141
+ }
2142
+ },
2143
+ // ║ BOX DRAWINGS DOUBLE VERTICAL
2144
+ [0x2551]: {
2145
+ width: W,
2146
+ draw: p => {
2147
+ p.lineWidth(30);
2148
+ p.M(270, 0);
2149
+ p.L(270, 500);
2150
+ p.stroke();
2151
+ p.M(330, 0);
2152
+ p.L(330, 500);
2153
+ p.stroke();
2154
+ }
2155
+ }
2156
+ };
2157
+ // =============================================================================
2158
+ // Block Elements (U+2580 – U+259F)
2159
+ // =============================================================================
2160
+ const BLOCK = {
2161
+ // ▀ UPPER HALF BLOCK
2162
+ [0x2580]: filledRect(0, 250, 600, 250),
2163
+ // ▄ LOWER HALF BLOCK
2164
+ [0x2584]: filledRect(0, 0, 600, 250),
2165
+ // █ FULL BLOCK
2166
+ [0x2588]: filledRect(0, 0, 600, 500),
2167
+ // ▌ LEFT HALF BLOCK
2168
+ [0x258c]: filledRect(0, 0, 300, 500),
2169
+ // ▐ RIGHT HALF BLOCK
2170
+ [0x2590]: filledRect(300, 0, 300, 500),
2171
+ // ░ LIGHT SHADE — simplified as rect with lines
2172
+ [0x2591]: {
2173
+ width: W,
2174
+ draw: p => {
2175
+ p.lineWidth(15);
2176
+ p.rect(0, 0, 600, 500);
2177
+ p.stroke();
2178
+ for (let y = 0; y <= 500; y += 60) {
2179
+ p.M(0, y);
2180
+ p.L(600, y);
2181
+ }
2182
+ p.stroke();
2183
+ }
2184
+ },
2185
+ // ▒ MEDIUM SHADE
2186
+ [0x2592]: {
2187
+ width: W,
2188
+ draw: p => {
2189
+ p.lineWidth(25);
2190
+ p.rect(0, 0, 600, 500);
2191
+ p.stroke();
2192
+ for (let y = 0; y <= 500; y += 50) {
2193
+ p.M(0, y);
2194
+ p.L(600, y);
2195
+ }
2196
+ for (let x = 0; x <= 600; x += 60) {
2197
+ p.M(x, 0);
2198
+ p.L(x, 500);
2199
+ }
2200
+ p.stroke();
2201
+ }
2202
+ },
2203
+ // ▓ DARK SHADE
2204
+ [0x2593]: {
2205
+ width: W,
2206
+ draw: p => {
2207
+ p.lineWidth(35);
2208
+ p.rect(0, 0, 600, 500);
2209
+ p.fill();
2210
+ }
2211
+ }
2212
+ };
2213
+ // =============================================================================
2214
+ // Misc Symbols & Arrows (U+2B00 – U+2BFF)
2215
+ // =============================================================================
2216
+ const MISC_ARROWS = {
2217
+ // ⬆ UPWARDS BLACK ARROW
2218
+ [0x2b06]: {
2219
+ width: W,
2220
+ draw: p => {
2221
+ p.M(300, 500);
2222
+ p.L(100, 280);
2223
+ p.L(230, 280);
2224
+ p.L(230, 0);
2225
+ p.L(370, 0);
2226
+ p.L(370, 280);
2227
+ p.L(500, 280);
2228
+ p.Z();
2229
+ p.fill();
2230
+ }
2231
+ },
2232
+ // ⬇ DOWNWARDS BLACK ARROW
2233
+ [0x2b07]: {
2234
+ width: W,
2235
+ draw: p => {
2236
+ p.M(300, 0);
2237
+ p.L(100, 220);
2238
+ p.L(230, 220);
2239
+ p.L(230, 500);
2240
+ p.L(370, 500);
2241
+ p.L(370, 220);
2242
+ p.L(500, 220);
2243
+ p.Z();
2244
+ p.fill();
2245
+ }
2246
+ },
2247
+ // ⬅ LEFTWARDS BLACK ARROW
2248
+ [0x2b05]: {
2249
+ width: W,
2250
+ draw: p => {
2251
+ p.M(0, 250);
2252
+ p.L(220, 430);
2253
+ p.L(220, 320);
2254
+ p.L(550, 320);
2255
+ p.L(550, 180);
2256
+ p.L(220, 180);
2257
+ p.L(220, 70);
2258
+ p.Z();
2259
+ p.fill();
2260
+ }
2261
+ },
2262
+ // ➡ BLACK RIGHTWARDS ARROW (U+27A1 not here, but mapped)
2263
+ // ⬛ BLACK LARGE SQUARE
2264
+ [0x2b1b]: filledRect(60, 10, 480, 480),
2265
+ // ⬜ WHITE LARGE SQUARE
2266
+ [0x2b1c]: strokedRect(60, 10, 480, 480, 45),
2267
+ // ⭐ WHITE MEDIUM STAR — same as ☆
2268
+ [0x2b50]: {
2269
+ width: W,
2270
+ draw: p => {
2271
+ p.lineWidth(35);
2272
+ const cx = 300;
2273
+ const cy = 260;
2274
+ const R = 230;
2275
+ const r = 90;
2276
+ for (let i = 0; i < 5; i++) {
2277
+ const a1 = ((i * 72 - 90) * Math.PI) / 180;
2278
+ const a2 = ((i * 72 + 36 - 90) * Math.PI) / 180;
2279
+ if (i === 0) {
2280
+ p.M(cx + R * Math.cos(a1), cy + R * Math.sin(a1));
2281
+ }
2282
+ else {
2283
+ p.L(cx + R * Math.cos(a1), cy + R * Math.sin(a1));
2284
+ }
2285
+ p.L(cx + r * Math.cos(a2), cy + r * Math.sin(a2));
2286
+ }
2287
+ p.Z();
2288
+ p.stroke();
2289
+ }
2290
+ }
2291
+ };
2292
+ // =============================================================================
2293
+ // Squared / circled symbols from Misc Math Symbols-B (U+29C0–U+29FF)
2294
+ // =============================================================================
2295
+ const SQUARED = {
2296
+ // ⧀ CIRCLED LESS-THAN
2297
+ [0x29c0]: {
2298
+ width: W,
2299
+ draw: p => {
2300
+ p.lineWidth(40);
2301
+ p.circle(300, 250, 220);
2302
+ p.stroke();
2303
+ p.lineWidth(45);
2304
+ p.M(400, 380);
2305
+ p.L(200, 250);
2306
+ p.L(400, 120);
2307
+ p.stroke();
2308
+ }
2309
+ },
2310
+ // ⧁ CIRCLED GREATER-THAN
2311
+ [0x29c1]: {
2312
+ width: W,
2313
+ draw: p => {
2314
+ p.lineWidth(40);
2315
+ p.circle(300, 250, 220);
2316
+ p.stroke();
2317
+ p.lineWidth(45);
2318
+ p.M(200, 380);
2319
+ p.L(400, 250);
2320
+ p.L(200, 120);
2321
+ p.stroke();
2322
+ }
2323
+ },
2324
+ // ⧇ SQUARED SMALL CIRCLE (key character from the issue!)
2325
+ [0x29c7]: {
2326
+ width: W,
2327
+ draw: p => {
2328
+ p.lineWidth(40);
2329
+ p.rect(80, 30, 440, 440);
2330
+ p.stroke();
2331
+ p.circle(300, 250, 130);
2332
+ p.stroke();
2333
+ }
2334
+ },
2335
+ // ⧈ SQUARED SQUARE
2336
+ [0x29c8]: {
2337
+ width: W,
2338
+ draw: p => {
2339
+ p.lineWidth(40);
2340
+ p.rect(80, 30, 440, 440);
2341
+ p.stroke();
2342
+ p.rect(190, 140, 220, 220);
2343
+ p.stroke();
2344
+ }
2345
+ },
2346
+ // ⧉ TWO JOINED SQUARES
2347
+ [0x29c9]: {
2348
+ width: W,
2349
+ draw: p => {
2350
+ p.lineWidth(35);
2351
+ p.rect(80, 80, 300, 300);
2352
+ p.stroke();
2353
+ p.rect(220, 120, 300, 300);
2354
+ p.stroke();
2355
+ }
2356
+ }
2357
+ };
2358
+ // =============================================================================
2359
+ // Currency Symbols (U+20A0 – U+20CF)
2360
+ // =============================================================================
2361
+ const CURRENCY = {
2362
+ // ₹ INDIAN RUPEE SIGN
2363
+ [0x20b9]: {
2364
+ width: W,
2365
+ draw: p => {
2366
+ p.lineWidth(50);
2367
+ p.M(130, 480);
2368
+ p.L(470, 480);
2369
+ p.stroke();
2370
+ p.M(130, 380);
2371
+ p.L(470, 380);
2372
+ p.stroke();
2373
+ p.M(230, 480);
2374
+ p.C(350, 480, 420, 400, 420, 350);
2375
+ p.C(420, 280, 350, 200, 230, 200);
2376
+ p.stroke();
2377
+ p.M(200, 380);
2378
+ p.L(400, 0);
2379
+ p.stroke();
2380
+ }
2381
+ },
2382
+ // ₺ TURKISH LIRA SIGN
2383
+ [0x20ba]: {
2384
+ width: W,
2385
+ draw: p => {
2386
+ p.lineWidth(50);
2387
+ p.M(350, 480);
2388
+ p.L(350, 50);
2389
+ p.C(350, 0, 200, 0, 150, 50);
2390
+ p.stroke();
2391
+ p.M(150, 330);
2392
+ p.L(450, 250);
2393
+ p.stroke();
2394
+ p.M(150, 230);
2395
+ p.L(450, 150);
2396
+ p.stroke();
2397
+ }
2398
+ },
2399
+ // ₽ RUBLE SIGN
2400
+ [0x20bd]: {
2401
+ width: W,
2402
+ draw: p => {
2403
+ p.lineWidth(50);
2404
+ p.M(180, 0);
2405
+ p.L(180, 500);
2406
+ p.stroke();
2407
+ p.M(180, 500);
2408
+ p.L(350, 500);
2409
+ p.C(480, 500, 480, 350, 350, 300);
2410
+ p.L(180, 300);
2411
+ p.stroke();
2412
+ p.M(120, 200);
2413
+ p.L(400, 200);
2414
+ p.stroke();
2415
+ }
2416
+ }
2417
+ };
2418
+ // =============================================================================
2419
+ // General Punctuation & Misc (assorted useful ones)
2420
+ // =============================================================================
2421
+ const PUNCT = {
2422
+ // • BULLET (U+2022)
2423
+ [0x2022]: filledCircle(300, 250, 100),
2424
+ // … HORIZONTAL ELLIPSIS (U+2026)
2425
+ [0x2026]: {
2426
+ width: W,
2427
+ draw: p => {
2428
+ p.circle(120, 100, 40);
2429
+ p.fill();
2430
+ p.circle(300, 100, 40);
2431
+ p.fill();
2432
+ p.circle(480, 100, 40);
2433
+ p.fill();
2434
+ }
2435
+ },
2436
+ // ‣ TRIANGULAR BULLET (U+2023)
2437
+ [0x2023]: {
2438
+ width: W,
2439
+ draw: p => {
2440
+ p.M(150, 400);
2441
+ p.L(450, 250);
2442
+ p.L(150, 100);
2443
+ p.Z();
2444
+ p.fill();
2445
+ }
2446
+ },
2447
+ // ⁃ HYPHEN BULLET (U+2043)
2448
+ [0x2043]: {
2449
+ width: W,
2450
+ draw: p => {
2451
+ p.lineWidth(60);
2452
+ p.M(150, 250);
2453
+ p.L(450, 250);
2454
+ p.stroke();
2455
+ }
2456
+ },
2457
+ // ⁄ FRACTION SLASH (U+2044)
2458
+ [0x2044]: {
2459
+ width: W,
2460
+ draw: p => {
2461
+ p.lineWidth(40);
2462
+ p.M(450, 500);
2463
+ p.L(150, 0);
2464
+ p.stroke();
2465
+ }
2466
+ }
2467
+ };
2468
+ // =============================================================================
2469
+ // Master lookup table
2470
+ // =============================================================================
2471
+ import { BOX_FULL, BLOCK_FULL, BRAILLE, LETTERLIKE, NUMBER_FORMS, ENCLOSED, PUNCT_EXT, ARROWS_EXT, MATH_EXT, MISC_EXT, DING_EXT, TECH_EXT, CURRENCY_EXT, MATH_SYM_A, SUP_ARROWS_A, GEOMETRIC_EXT, ROMAN_NUMERALS, ENCLOSED_EXT } from "./type3-glyphs-extended.js";
2472
+ import { ARROWS_HARPOONS, DINGBATS_FULL, MISC_SYMBOLS_FULL, MISC_TECHNICAL_EXT, SUP_ARROWS_B, MISC_MATH_A_EXT, MISC_SYM_ARROWS_EXT } from "./type3-glyphs-extended2.js";
2473
+ import { DINGBATS_FILL, MISC_SYM_FILL, MISC_SYM_ARR_FILL, SUP_ARROWS_FILL, GEN_PUNCT_FILL, NUM_FORMS_FILL, FINAL_FILL } from "./type3-glyphs-fill.js";
2474
+ import { SPACES, CIRCLED_DIGITS, CIRCLED_LETTERS, CIRCLED_SMALL_LETTERS, NEG_CIRCLED_DIGITS, REFINED_SYMBOLS, SUPP_MATH_OP, MISC_MATH_B, MATH_OP_FULL, LETTERLIKE_FULL, CURRENCY_REMAINING, ARROWS_REMAINING } from "./type3-glyphs-quality.js";
2475
+ const ALL_TABLES = [
2476
+ GEO,
2477
+ ARR,
2478
+ MATH,
2479
+ DING,
2480
+ TECH,
2481
+ BOX,
2482
+ BLOCK,
2483
+ MISC_ARROWS,
2484
+ SQUARED,
2485
+ CURRENCY,
2486
+ PUNCT,
2487
+ // Extended tables
2488
+ BOX_FULL,
2489
+ BLOCK_FULL,
2490
+ BRAILLE,
2491
+ LETTERLIKE,
2492
+ NUMBER_FORMS,
2493
+ ENCLOSED,
2494
+ PUNCT_EXT,
2495
+ ARROWS_EXT,
2496
+ MATH_EXT,
2497
+ MISC_EXT,
2498
+ DING_EXT,
2499
+ TECH_EXT,
2500
+ CURRENCY_EXT,
2501
+ MATH_SYM_A,
2502
+ SUP_ARROWS_A,
2503
+ GEOMETRIC_EXT,
2504
+ ROMAN_NUMERALS,
2505
+ ENCLOSED_EXT,
2506
+ // Extended2 tables
2507
+ ARROWS_HARPOONS,
2508
+ DINGBATS_FULL,
2509
+ MISC_SYMBOLS_FULL,
2510
+ MISC_TECHNICAL_EXT,
2511
+ SUP_ARROWS_B,
2512
+ MISC_MATH_A_EXT,
2513
+ MISC_SYM_ARROWS_EXT,
2514
+ // Fill tables (gap coverage)
2515
+ DINGBATS_FILL,
2516
+ MISC_SYM_FILL,
2517
+ MISC_SYM_ARR_FILL,
2518
+ SUP_ARROWS_FILL,
2519
+ GEN_PUNCT_FILL,
2520
+ NUM_FORMS_FILL,
2521
+ FINAL_FILL,
2522
+ // Quality overrides + new coverage (must be LAST to override earlier entries)
2523
+ SPACES,
2524
+ SUPP_MATH_OP,
2525
+ MISC_MATH_B,
2526
+ MATH_OP_FULL,
2527
+ LETTERLIKE_FULL,
2528
+ CURRENCY_REMAINING,
2529
+ ARROWS_REMAINING,
2530
+ CIRCLED_DIGITS,
2531
+ CIRCLED_LETTERS,
2532
+ CIRCLED_SMALL_LETTERS,
2533
+ NEG_CIRCLED_DIGITS,
2534
+ REFINED_SYMBOLS
2535
+ ];
2536
+ /** Merged lookup table (built once on first access). */
2537
+ let _merged = null;
2538
+ function getMerged() {
2539
+ if (!_merged) {
2540
+ _merged = new Map();
2541
+ for (const table of ALL_TABLES) {
2542
+ for (const [cp, def] of Object.entries(table)) {
2543
+ _merged.set(Number(cp), def);
2544
+ }
2545
+ }
2546
+ }
2547
+ return _merged;
2548
+ }
2549
+ /**
2550
+ * Look up a glyph definition for a Unicode code point.
2551
+ * Returns the glyph def, or `undefined` if no specific drawing is available.
2552
+ */
2553
+ export function lookupGlyph(codePoint) {
2554
+ return getMerged().get(codePoint);
2555
+ }
2556
+ /**
2557
+ * Check whether a specific code point has a dedicated vector glyph.
2558
+ */
2559
+ export function hasGlyph(codePoint) {
2560
+ return getMerged().has(codePoint);
2561
+ }
2562
+ /**
2563
+ * Return the total number of defined glyphs (for diagnostics / testing).
2564
+ */
2565
+ export function glyphCount() {
2566
+ return getMerged().size;
2567
+ }