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