@xterm/addon-webgl 0.20.0-beta.4 → 0.20.0-beta.5

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.
@@ -1,1026 +0,0 @@
1
- /**
2
- * Copyright (c) 2021 The xterm.js authors. All rights reserved.
3
- * @license MIT
4
- */
5
-
6
- /* eslint-disable @typescript-eslint/naming-convention */
7
-
8
- import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
9
-
10
- interface IBlockVector {
11
- x: number;
12
- y: number;
13
- w: number;
14
- h: number;
15
- }
16
-
17
- export const blockElementDefinitions: { [index: string]: IBlockVector[] | undefined } = {
18
- // Block elements (0x2580-0x2590)
19
- '▀': [{ x: 0, y: 0, w: 8, h: 4 }], // UPPER HALF BLOCK
20
- '▁': [{ x: 0, y: 7, w: 8, h: 1 }], // LOWER ONE EIGHTH BLOCK
21
- '▂': [{ x: 0, y: 6, w: 8, h: 2 }], // LOWER ONE QUARTER BLOCK
22
- '▃': [{ x: 0, y: 5, w: 8, h: 3 }], // LOWER THREE EIGHTHS BLOCK
23
- '▄': [{ x: 0, y: 4, w: 8, h: 4 }], // LOWER HALF BLOCK
24
- '▅': [{ x: 0, y: 3, w: 8, h: 5 }], // LOWER FIVE EIGHTHS BLOCK
25
- '▆': [{ x: 0, y: 2, w: 8, h: 6 }], // LOWER THREE QUARTERS BLOCK
26
- '▇': [{ x: 0, y: 1, w: 8, h: 7 }], // LOWER SEVEN EIGHTHS BLOCK
27
- '█': [{ x: 0, y: 0, w: 8, h: 8 }], // FULL BLOCK
28
- '▉': [{ x: 0, y: 0, w: 7, h: 8 }], // LEFT SEVEN EIGHTHS BLOCK
29
- '▊': [{ x: 0, y: 0, w: 6, h: 8 }], // LEFT THREE QUARTERS BLOCK
30
- '▋': [{ x: 0, y: 0, w: 5, h: 8 }], // LEFT FIVE EIGHTHS BLOCK
31
- '▌': [{ x: 0, y: 0, w: 4, h: 8 }], // LEFT HALF BLOCK
32
- '▍': [{ x: 0, y: 0, w: 3, h: 8 }], // LEFT THREE EIGHTHS BLOCK
33
- '▎': [{ x: 0, y: 0, w: 2, h: 8 }], // LEFT ONE QUARTER BLOCK
34
- '▏': [{ x: 0, y: 0, w: 1, h: 8 }], // LEFT ONE EIGHTH BLOCK
35
- '▐': [{ x: 4, y: 0, w: 4, h: 8 }], // RIGHT HALF BLOCK
36
-
37
- // Block elements (0x2594-0x2595)
38
- '▔': [{ x: 0, y: 0, w: 8, h: 1 }], // UPPER ONE EIGHTH BLOCK
39
- '▕': [{ x: 7, y: 0, w: 1, h: 8 }], // RIGHT ONE EIGHTH BLOCK
40
-
41
- // Terminal graphic characters (0x2596-0x259F)
42
- '▖': [{ x: 0, y: 4, w: 4, h: 4 }], // QUADRANT LOWER LEFT
43
- '▗': [{ x: 4, y: 4, w: 4, h: 4 }], // QUADRANT LOWER RIGHT
44
- '▘': [{ x: 0, y: 0, w: 4, h: 4 }], // QUADRANT UPPER LEFT
45
- '▙': [{ x: 0, y: 0, w: 4, h: 8 }, { x: 0, y: 4, w: 8, h: 4 }], // QUADRANT UPPER LEFT AND LOWER LEFT AND LOWER RIGHT
46
- '▚': [{ x: 0, y: 0, w: 4, h: 4 }, { x: 4, y: 4, w: 4, h: 4 }], // QUADRANT UPPER LEFT AND LOWER RIGHT
47
- '▛': [{ x: 0, y: 0, w: 4, h: 8 }, { x: 4, y: 0, w: 4, h: 4 }], // QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER LEFT
48
- '▜': [{ x: 0, y: 0, w: 8, h: 4 }, { x: 4, y: 0, w: 4, h: 8 }], // QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER RIGHT
49
- '▝': [{ x: 4, y: 0, w: 4, h: 4 }], // QUADRANT UPPER RIGHT
50
- '▞': [{ x: 4, y: 0, w: 4, h: 4 }, { x: 0, y: 4, w: 4, h: 4 }], // QUADRANT UPPER RIGHT AND LOWER LEFT
51
- '▟': [{ x: 4, y: 0, w: 4, h: 8 }, { x: 0, y: 4, w: 8, h: 4 }], // QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT
52
-
53
- // VERTICAL ONE EIGHTH BLOCK-2 through VERTICAL ONE EIGHTH BLOCK-7
54
- '\u{1FB70}': [{ x: 1, y: 0, w: 1, h: 8 }],
55
- '\u{1FB71}': [{ x: 2, y: 0, w: 1, h: 8 }],
56
- '\u{1FB72}': [{ x: 3, y: 0, w: 1, h: 8 }],
57
- '\u{1FB73}': [{ x: 4, y: 0, w: 1, h: 8 }],
58
- '\u{1FB74}': [{ x: 5, y: 0, w: 1, h: 8 }],
59
- '\u{1FB75}': [{ x: 6, y: 0, w: 1, h: 8 }],
60
-
61
- // HORIZONTAL ONE EIGHTH BLOCK-2 through HORIZONTAL ONE EIGHTH BLOCK-7
62
- '\u{1FB76}': [{ x: 0, y: 1, w: 8, h: 1 }],
63
- '\u{1FB77}': [{ x: 0, y: 2, w: 8, h: 1 }],
64
- '\u{1FB78}': [{ x: 0, y: 3, w: 8, h: 1 }],
65
- '\u{1FB79}': [{ x: 0, y: 4, w: 8, h: 1 }],
66
- '\u{1FB7A}': [{ x: 0, y: 5, w: 8, h: 1 }],
67
- '\u{1FB7B}': [{ x: 0, y: 6, w: 8, h: 1 }],
68
-
69
- // LEFT AND LOWER ONE EIGHTH BLOCK
70
- '\u{1FB7C}': [{ x: 0, y: 0, w: 1, h: 8 }, { x: 0, y: 7, w: 8, h: 1 }],
71
- // LEFT AND UPPER ONE EIGHTH BLOCK
72
- '\u{1FB7D}': [{ x: 0, y: 0, w: 1, h: 8 }, { x: 0, y: 0, w: 8, h: 1 }],
73
- // RIGHT AND UPPER ONE EIGHTH BLOCK
74
- '\u{1FB7E}': [{ x: 7, y: 0, w: 1, h: 8 }, { x: 0, y: 0, w: 8, h: 1 }],
75
- // RIGHT AND LOWER ONE EIGHTH BLOCK
76
- '\u{1FB7F}': [{ x: 7, y: 0, w: 1, h: 8 }, { x: 0, y: 7, w: 8, h: 1 }],
77
- // UPPER AND LOWER ONE EIGHTH BLOCK
78
- '\u{1FB80}': [{ x: 0, y: 0, w: 8, h: 1 }, { x: 0, y: 7, w: 8, h: 1 }],
79
- // HORIZONTAL ONE EIGHTH BLOCK-1358
80
- '\u{1FB81}': [{ x: 0, y: 0, w: 8, h: 1 }, { x: 0, y: 2, w: 8, h: 1 }, { x: 0, y: 4, w: 8, h: 1 }, { x: 0, y: 7, w: 8, h: 1 }],
81
-
82
- // UPPER ONE QUARTER BLOCK
83
- '\u{1FB82}': [{ x: 0, y: 0, w: 8, h: 2 }],
84
- // UPPER THREE EIGHTHS BLOCK
85
- '\u{1FB83}': [{ x: 0, y: 0, w: 8, h: 3 }],
86
- // UPPER FIVE EIGHTHS BLOCK
87
- '\u{1FB84}': [{ x: 0, y: 0, w: 8, h: 5 }],
88
- // UPPER THREE QUARTERS BLOCK
89
- '\u{1FB85}': [{ x: 0, y: 0, w: 8, h: 6 }],
90
- // UPPER SEVEN EIGHTHS BLOCK
91
- '\u{1FB86}': [{ x: 0, y: 0, w: 8, h: 7 }],
92
-
93
- // RIGHT ONE QUARTER BLOCK
94
- '\u{1FB87}': [{ x: 6, y: 0, w: 2, h: 8 }],
95
- // RIGHT THREE EIGHTHS B0OCK
96
- '\u{1FB88}': [{ x: 5, y: 0, w: 3, h: 8 }],
97
- // RIGHT FIVE EIGHTHS BL0CK
98
- '\u{1FB89}': [{ x: 3, y: 0, w: 5, h: 8 }],
99
- // RIGHT THREE QUARTERS 0LOCK
100
- '\u{1FB8A}': [{ x: 2, y: 0, w: 6, h: 8 }],
101
- // RIGHT SEVEN EIGHTHS B0OCK
102
- '\u{1FB8B}': [{ x: 1, y: 0, w: 7, h: 8 }],
103
-
104
- // CHECKER BOARD FILL
105
- '\u{1FB95}': [
106
- { x: 0, y: 0, w: 2, h: 2 }, { x: 4, y: 0, w: 2, h: 2 },
107
- { x: 2, y: 2, w: 2, h: 2 }, { x: 6, y: 2, w: 2, h: 2 },
108
- { x: 0, y: 4, w: 2, h: 2 }, { x: 4, y: 4, w: 2, h: 2 },
109
- { x: 2, y: 6, w: 2, h: 2 }, { x: 6, y: 6, w: 2, h: 2 }
110
- ],
111
- // INVERSE CHECKER BOARD FILL
112
- '\u{1FB96}': [
113
- { x: 2, y: 0, w: 2, h: 2 }, { x: 6, y: 0, w: 2, h: 2 },
114
- { x: 0, y: 2, w: 2, h: 2 }, { x: 4, y: 2, w: 2, h: 2 },
115
- { x: 2, y: 4, w: 2, h: 2 }, { x: 6, y: 4, w: 2, h: 2 },
116
- { x: 0, y: 6, w: 2, h: 2 }, { x: 4, y: 6, w: 2, h: 2 }
117
- ],
118
- // HEAVY HORIZONTAL FILL (upper middle and lower one quarter block)
119
- '\u{1FB97}': [{ x: 0, y: 2, w: 8, h: 2 }, { x: 0, y: 6, w: 8, h: 2 }]
120
- };
121
-
122
- /**
123
- * Generates a drawing function for sextant characters. Sextants are a 2x3 grid where each cell
124
- * can be on or off.
125
- * @param pattern A 6-bit pattern where bit 0 = top-left, bit 1 = top-right, bit 2 = middle-left,
126
- * bit 3 = middle-right, bit 4 = bottom-left, bit 5 = bottom-right
127
- */
128
- function sextant(pattern: number): DrawFunctionDefinition {
129
- return () => {
130
- // Sextant grid: 2 columns, 3 rows
131
- // Row heights in 8ths: top=3, middle=2, bottom=3
132
- // Column widths: left=4, right=4
133
- const rects: string[] = [];
134
- const colW = 0.5; // Each column is half width
135
- const rowH = [3 / 8, 2 / 8, 3 / 8]; // Row heights as fractions
136
- const rowY = [0, 3 / 8, 5 / 8]; // Row Y positions
137
-
138
- for (let row = 0; row < 3; row++) {
139
- const leftBit = (pattern >> (row * 2)) & 1;
140
- const rightBit = (pattern >> (row * 2 + 1)) & 1;
141
-
142
- if (leftBit && rightBit) {
143
- // Full row
144
- rects.push(`M0,${rowY[row]} L1,${rowY[row]} L1,${rowY[row] + rowH[row]} L0,${rowY[row] + rowH[row]} Z`);
145
- } else if (leftBit) {
146
- rects.push(`M0,${rowY[row]} L${colW},${rowY[row]} L${colW},${rowY[row] + rowH[row]} L0,${rowY[row] + rowH[row]} Z`);
147
- } else if (rightBit) {
148
- rects.push(`M${colW},${rowY[row]} L1,${rowY[row]} L1,${rowY[row] + rowH[row]} L${colW},${rowY[row] + rowH[row]} Z`);
149
- }
150
- }
151
- return rects.join(' ');
152
- };
153
- }
154
-
155
- export const symbolsForLegacyComputingDefinitions: { [index: string]: DrawFunctionDefinition | undefined } = {
156
- // Block sextants (0x1FB00-0x1FB3B)
157
- // Each sextant is a 2x3 grid of cells in an 8x8 block
158
- // Cell positions: bit 0=top-left, bit 1=top-right, bit 2=middle-left, bit 3=middle-right,
159
- // bit 4=bottom-left, bit 5=bottom-right
160
- // Patterns 0 (empty), 21 (left half), 42 (right half), 63 (full) are excluded as they exist
161
- // elsewhere
162
- '\u{1FB00}': sextant(0b000001), // BLOCK SEXTANT-1
163
- '\u{1FB01}': sextant(0b000010), // BLOCK SEXTANT-2
164
- '\u{1FB02}': sextant(0b000011), // BLOCK SEXTANT-12 (upper one third block)
165
- '\u{1FB03}': sextant(0b000100), // BLOCK SEXTANT-3
166
- '\u{1FB04}': sextant(0b000101), // BLOCK SEXTANT-13
167
- '\u{1FB05}': sextant(0b000110), // BLOCK SEXTANT-23
168
- '\u{1FB06}': sextant(0b000111), // BLOCK SEXTANT-123
169
- '\u{1FB07}': sextant(0b001000), // BLOCK SEXTANT-4
170
- '\u{1FB08}': sextant(0b001001), // BLOCK SEXTANT-14
171
- '\u{1FB09}': sextant(0b001010), // BLOCK SEXTANT-24
172
- '\u{1FB0A}': sextant(0b001011), // BLOCK SEXTANT-124
173
- '\u{1FB0B}': sextant(0b001100), // BLOCK SEXTANT-34 (middle one third block)
174
- '\u{1FB0C}': sextant(0b001101), // BLOCK SEXTANT-134
175
- '\u{1FB0D}': sextant(0b001110), // BLOCK SEXTANT-234
176
- '\u{1FB0E}': sextant(0b001111), // BLOCK SEXTANT-1234 (upper two thirds block)
177
- '\u{1FB0F}': sextant(0b010000), // BLOCK SEXTANT-5
178
- '\u{1FB10}': sextant(0b010001), // BLOCK SEXTANT-15
179
- '\u{1FB11}': sextant(0b010010), // BLOCK SEXTANT-25
180
- '\u{1FB12}': sextant(0b010011), // BLOCK SEXTANT-125
181
- '\u{1FB13}': sextant(0b010100), // BLOCK SEXTANT-35
182
- // Pattern 21 (0x15 = 0b010101) = left half block, skipped (exists as U+258C)
183
- '\u{1FB14}': sextant(0b010110), // BLOCK SEXTANT-235
184
- '\u{1FB15}': sextant(0b010111), // BLOCK SEXTANT-1235
185
- '\u{1FB16}': sextant(0b011000), // BLOCK SEXTANT-45
186
- '\u{1FB17}': sextant(0b011001), // BLOCK SEXTANT-145
187
- '\u{1FB18}': sextant(0b011010), // BLOCK SEXTANT-245
188
- '\u{1FB19}': sextant(0b011011), // BLOCK SEXTANT-1245
189
- '\u{1FB1A}': sextant(0b011100), // BLOCK SEXTANT-345
190
- '\u{1FB1B}': sextant(0b011101), // BLOCK SEXTANT-1345
191
- '\u{1FB1C}': sextant(0b011110), // BLOCK SEXTANT-2345
192
- '\u{1FB1D}': sextant(0b011111), // BLOCK SEXTANT-12345
193
- '\u{1FB1E}': sextant(0b100000), // BLOCK SEXTANT-6
194
- '\u{1FB1F}': sextant(0b100001), // BLOCK SEXTANT-16
195
- '\u{1FB20}': sextant(0b100010), // BLOCK SEXTANT-26
196
- '\u{1FB21}': sextant(0b100011), // BLOCK SEXTANT-126
197
- '\u{1FB22}': sextant(0b100100), // BLOCK SEXTANT-36
198
- '\u{1FB23}': sextant(0b100101), // BLOCK SEXTANT-136
199
- '\u{1FB24}': sextant(0b100110), // BLOCK SEXTANT-236
200
- '\u{1FB25}': sextant(0b100111), // BLOCK SEXTANT-1236
201
- '\u{1FB26}': sextant(0b101000), // BLOCK SEXTANT-46
202
- '\u{1FB27}': sextant(0b101001), // BLOCK SEXTANT-146
203
- // Pattern 42 (0x2A = 0b101010) = right half block, skipped (exists as U+2590)
204
- '\u{1FB28}': sextant(0b101011), // BLOCK SEXTANT-1246
205
- '\u{1FB29}': sextant(0b101100), // BLOCK SEXTANT-346
206
- '\u{1FB2A}': sextant(0b101101), // BLOCK SEXTANT-1346
207
- '\u{1FB2B}': sextant(0b101110), // BLOCK SEXTANT-2346
208
- '\u{1FB2C}': sextant(0b101111), // BLOCK SEXTANT-12346
209
- '\u{1FB2D}': sextant(0b110000), // BLOCK SEXTANT-56 (lower one third block)
210
- '\u{1FB2E}': sextant(0b110001), // BLOCK SEXTANT-156
211
- '\u{1FB2F}': sextant(0b110010), // BLOCK SEXTANT-256
212
- '\u{1FB30}': sextant(0b110011), // BLOCK SEXTANT-1256 (upper and lower one
213
- // third block)
214
- '\u{1FB31}': sextant(0b110100), // BLOCK SEXTANT-356
215
- '\u{1FB32}': sextant(0b110101), // BLOCK SEXTANT-1356
216
- '\u{1FB33}': sextant(0b110110), // BLOCK SEXTANT-2356
217
- '\u{1FB34}': sextant(0b110111), // BLOCK SEXTANT-12356
218
- '\u{1FB35}': sextant(0b111000), // BLOCK SEXTANT-456
219
- '\u{1FB36}': sextant(0b111001), // BLOCK SEXTANT-1456
220
- '\u{1FB37}': sextant(0b111010), // BLOCK SEXTANT-2456
221
- '\u{1FB38}': sextant(0b111011), // BLOCK SEXTANT-12456
222
- '\u{1FB39}': sextant(0b111100), // BLOCK SEXTANT-3456 (lower two thirds block)
223
- '\u{1FB3A}': sextant(0b111101), // BLOCK SEXTANT-13456
224
- '\u{1FB3B}': sextant(0b111110), // BLOCK SEXTANT-23456
225
- // Pattern 63 (0x3F = 0b111111) = full block, skipped (exists as U+2588)
226
-
227
- // Smooth mosaic terminal graphic characters (0x1FB3C-0x1FB6F)
228
- // These are triangular/diagonal shapes. "X BLOCK DIAGONAL A TO B" means the X region is filled,
229
- // with a diagonal edge from point A to point B.
230
- // Reference points: upper/lower = y (0/1), left/right = x (0/1), centre = x=0.5
231
- // Vertical uses sextant grid: upper-middle = y=1/3, lower-middle = y=2/3
232
-
233
- // LOWER LEFT BLOCK variants (1FB3C-1FB40) - filled region in lower-left
234
- '\u{1FB3C}': () => `M0,${2/3} L0,1 L0.5,1 Z`, // LOWER LEFT BLOCK DIAGONAL LOWER MIDDLE LEFT TO LOWER CENTRE
235
- '\u{1FB3D}': () => `M0,${2/3} L0,1 L1,1 Z`, // LOWER LEFT BLOCK DIAGONAL LOWER MIDDLE LEFT TO LOWER RIGHT
236
- '\u{1FB3E}': () => `M0,${1/3} L0,1 L0.5,1 Z`, // LOWER LEFT BLOCK DIAGONAL UPPER MIDDLE LEFT TO LOWER CENTRE
237
- '\u{1FB3F}': () => `M0,${1/3} L0,1 L1,1 Z`, // LOWER LEFT BLOCK DIAGONAL UPPER MIDDLE LEFT TO LOWER RIGHT
238
- '\u{1FB40}': () => 'M0,0 L0,1 L0.5,1 Z', // LOWER LEFT BLOCK DIAGONAL UPPER LEFT TO LOWER CENTRE
239
-
240
- // LOWER RIGHT BLOCK variants (1FB41-1FB4B) - filled region in lower-right
241
- '\u{1FB41}': () => `M0,${1/3} L0.5,0 L1,0 L1,1 L0,1 Z`, // LOWER RIGHT BLOCK DIAGONAL UPPER MIDDLE LEFT TO UPPER CENTRE
242
- '\u{1FB42}': () => `M0,${1/3} L1,0 L1,1 L0,1 Z`, // LOWER RIGHT BLOCK DIAGONAL UPPER MIDDLE LEFT TO UPPER RIGHT
243
- '\u{1FB43}': () => `M0,${2/3} L0.5,0 L1,0 L1,1 L0,1 Z`, // LOWER RIGHT BLOCK DIAGONAL LOWER MIDDLE LEFT TO UPPER CENTRE
244
- '\u{1FB44}': () => `M0,${2/3} L1,0 L1,1 L0,1 Z`, // LOWER RIGHT BLOCK DIAGONAL LOWER MIDDLE LEFT TO UPPER RIGHT
245
- '\u{1FB45}': () => 'M0,1 L0.5,0 L1,0 L1,1 Z', // LOWER RIGHT BLOCK DIAGONAL LOWER LEFT TO UPPER CENTRE
246
- '\u{1FB46}': () => `M0,${2/3} L1,${1/3} L1,1 L0,1 Z`, // LOWER RIGHT BLOCK DIAGONAL LOWER MIDDLE LEFT TO UPPER MIDDLE RIGHT
247
- '\u{1FB47}': () => `M0.5,1 L1,${2/3} L1,1 Z`, // LOWER RIGHT BLOCK DIAGONAL LOWER CENTRE TO LOWER MIDDLE RIGHT
248
- '\u{1FB48}': () => `M0,1 L1,${2/3} L1,1 Z`, // LOWER RIGHT BLOCK DIAGONAL LOWER LEFT TO LOWER MIDDLE RIGHT
249
- '\u{1FB49}': () => `M0.5,1 L1,${1/3} L1,1 Z`, // LOWER RIGHT BLOCK DIAGONAL LOWER CENTRE TO UPPER MIDDLE RIGHT
250
- '\u{1FB4A}': () => `M0,1 L1,${1/3} L1,1 Z`, // LOWER RIGHT BLOCK DIAGONAL LOWER LEFT TO UPPER MIDDLE RIGHT
251
- '\u{1FB4B}': () => 'M0.5,1 L1,0 L1,1 Z', // LOWER RIGHT BLOCK DIAGONAL LOWER CENTRE TO UPPER RIGHT
252
-
253
- // LOWER LEFT BLOCK variants continued (1FB4C-1FB51) - large fills with upper-right cut
254
- '\u{1FB4C}': () => `M0.5,0 L0,0 L0,1 L1,1 L1,${1/3} Z`, // LOWER LEFT BLOCK DIAGONAL UPPER CENTRE TO UPPER MIDDLE RIGHT
255
- '\u{1FB4D}': () => `M0,0 L0,1 L1,1 L1,${1/3} Z`, // LOWER LEFT BLOCK DIAGONAL UPPER LEFT TO UPPER MIDDLE RIGHT
256
- '\u{1FB4E}': () => `M0.5,0 L0,0 L0,1 L1,1 L1,${2/3} Z`, // LOWER LEFT BLOCK DIAGONAL UPPER CENTRE TO LOWER MIDDLE RIGHT
257
- '\u{1FB4F}': () => `M0,0 L0,1 L1,1 L1,${2/3} Z`, // LOWER LEFT BLOCK DIAGONAL UPPER LEFT TO LOWER MIDDLE RIGHT
258
- '\u{1FB50}': () => 'M0.5,0 L0,0 L0,1 L1,1 Z', // LOWER LEFT BLOCK DIAGONAL UPPER CENTRE TO LOWER RIGHT
259
- '\u{1FB51}': () => `M0,${1/3} L0,1 L1,1 L1,${2/3} Z`, // LOWER LEFT BLOCK DIAGONAL UPPER MIDDLE LEFT TO LOWER MIDDLE RIGHT
260
-
261
- // UPPER RIGHT BLOCK variants (1FB52-1FB56) - large fills with lower-left cut
262
- '\u{1FB52}': () => `M0,${2/3} L0.5,1 L1,1 L1,0 L0,0 Z`, // UPPER RIGHT BLOCK DIAGONAL LOWER MIDDLE LEFT TO LOWER CENTRE
263
- '\u{1FB53}': () => `M0,${2/3} L1,1 L1,0 L0,0 Z`, // UPPER RIGHT BLOCK DIAGONAL LOWER MIDDLE LEFT TO LOWER RIGHT
264
- '\u{1FB54}': () => `M0,${1/3} L0.5,1 L1,1 L1,0 L0,0 Z`, // UPPER RIGHT BLOCK DIAGONAL UPPER MIDDLE LEFT TO LOWER CENTRE
265
- '\u{1FB55}': () => `M0,${1/3} L1,1 L1,0 L0,0 Z`, // UPPER RIGHT BLOCK DIAGONAL UPPER MIDDLE LEFT TO LOWER RIGHT
266
- '\u{1FB56}': () => 'M0,0 L0.5,1 L1,1 L1,0 Z', // UPPER RIGHT BLOCK DIAGONAL UPPER LEFT TO LOWER CENTRE
267
-
268
- // UPPER LEFT BLOCK variants (1FB57-1FB61) - small to large fills in upper-left
269
- '\u{1FB57}': () => `M0,${1/3} L0,0 L0.5,0 Z`, // UPPER LEFT BLOCK DIAGONAL UPPER MIDDLE LEFT TO UPPER CENTRE
270
- '\u{1FB58}': () => `M0,${1/3} L0,0 L1,0 Z`, // UPPER LEFT BLOCK DIAGONAL UPPER MIDDLE LEFT TO UPPER RIGHT
271
- '\u{1FB59}': () => `M0,${2/3} L0,0 L0.5,0 Z`, // UPPER LEFT BLOCK DIAGONAL LOWER MIDDLE LEFT TO UPPER CENTRE
272
- '\u{1FB5A}': () => `M0,${2/3} L0,0 L1,0 Z`, // UPPER LEFT BLOCK DIAGONAL LOWER MIDDLE LEFT TO UPPER RIGHT
273
- '\u{1FB5B}': () => 'M0,1 L0,0 L0.5,0 Z', // UPPER LEFT BLOCK DIAGONAL LOWER LEFT TO UPPER CENTRE
274
- '\u{1FB5C}': () => `M0,${2/3} L0,0 L1,0 L1,${1/3} Z`, // UPPER LEFT BLOCK DIAGONAL LOWER MIDDLE LEFT TO UPPER MIDDLE RIGHT
275
- '\u{1FB5D}': () => `M0.5,1 L0,1 L0,0 L1,0 L1,${2/3} Z`, // UPPER LEFT BLOCK DIAGONAL LOWER CENTRE TO LOWER MIDDLE RIGHT
276
- '\u{1FB5E}': () => `M0,1 L0,0 L1,0 L1,${2/3} Z`, // UPPER LEFT BLOCK DIAGONAL LOWER LEFT TO LOWER MIDDLE RIGHT
277
- '\u{1FB5F}': () => `M0.5,1 L0,1 L0,0 L1,0 L1,${1/3} Z`, // UPPER LEFT BLOCK DIAGONAL LOWER CENTRE TO UPPER MIDDLE RIGHT
278
- '\u{1FB60}': () => `M0,1 L0,0 L1,0 L1,${1/3} Z`, // UPPER LEFT BLOCK DIAGONAL LOWER LEFT TO UPPER MIDDLE RIGHT
279
- '\u{1FB61}': () => 'M0.5,1 L0,1 L0,0 L1,0 Z', // UPPER LEFT BLOCK DIAGONAL LOWER CENTRE TO UPPER RIGHT
280
-
281
- // UPPER RIGHT BLOCK variants continued (1FB62-1FB67) - small to medium fills in upper-right
282
- '\u{1FB62}': () => `M0.5,0 L1,0 L1,${1/3} Z`, // UPPER RIGHT BLOCK DIAGONAL UPPER CENTRE TO UPPER MIDDLE RIGHT
283
- '\u{1FB63}': () => `M0,0 L1,0 L1,${1/3} Z`, // UPPER RIGHT BLOCK DIAGONAL UPPER LEFT TO UPPER MIDDLE RIGHT
284
- '\u{1FB64}': () => `M0.5,0 L1,0 L1,${2/3} Z`, // UPPER RIGHT BLOCK DIAGONAL UPPER CENTRE TO LOWER MIDDLE RIGHT
285
- '\u{1FB65}': () => `M0,0 L1,0 L1,${2/3} Z`, // UPPER RIGHT BLOCK DIAGONAL UPPER LEFT TO LOWER MIDDLE RIGHT
286
- '\u{1FB66}': () => 'M0.5,0 L1,0 L1,1 Z', // UPPER RIGHT BLOCK DIAGONAL UPPER CENTRE TO LOWER RIGHT
287
- '\u{1FB67}': () => `M0,${1/3} L1,${2/3} L1,0 L0,0 Z`, // UPPER RIGHT BLOCK DIAGONAL UPPER MIDDLE LEFT TO LOWER MIDDLE RIGHT
288
-
289
- // Triangular blocks (1FB68-1FB6F)
290
- // Three-quarter blocks: full block minus one triangular quarter pointing to center
291
- '\u{1FB68}': () => 'M0,0 L1,0 L1,1 L0,1 L0.5,0.5 Z', // UPPER AND RIGHT AND LOWER TRIANGULAR THREE QUARTERS BLOCK (missing left)
292
- '\u{1FB69}': () => 'M0,0 L0.5,0.5 L1,0 L1,1 L0,1 Z', // LEFT AND LOWER AND RIGHT TRIANGULAR THREE QUARTERS BLOCK (missing upper)
293
- '\u{1FB6A}': () => 'M0,0 L1,0 L0.5,0.5 L1,1 L0,1 Z', // UPPER AND LEFT AND LOWER TRIANGULAR THREE QUARTERS BLOCK (missing right)
294
- '\u{1FB6B}': () => 'M0,0 L1,0 L1,1 L0.5,0.5 L0,1 Z', // LEFT AND UPPER AND RIGHT TRIANGULAR THREE QUARTERS BLOCK (missing lower)
295
- '\u{1FB6C}': () => 'M0,0 L0.5,0.5 L0,1 Z', // LEFT TRIANGULAR ONE QUARTER BLOCK
296
- '\u{1FB6D}': () => 'M0,0 L1,0 L0.5,0.5 Z', // UPPER TRIANGULAR ONE QUARTER BLOCK
297
- '\u{1FB6E}': () => 'M1,0 L1,1 L0.5,0.5 Z', // RIGHT TRIANGULAR ONE QUARTER BLOCK
298
- '\u{1FB6F}': () => 'M0,1 L1,1 L0.5,0.5 Z' // LOWER TRIANGULAR ONE QUARTER BLOCK
299
- };
300
-
301
- /**
302
- * Rectangular shade characters - these use medium shade pattern with region bounds.
303
- * Pattern is a checkerboard that shifts 1px each row (same as medium shade).
304
- * Region is defined as [x, y, width, height] in 0-1 normalized coordinates.
305
- */
306
- export const rectangularShadeDefinitions: { [index: string]: [number, number, number, number] | undefined } = {
307
- '\u{1FB8C}': [0, 0, 0.5, 1], // LEFT HALF MEDIUM SHADE
308
- '\u{1FB8D}': [0.5, 0, 0.5, 1], // RIGHT HALF MEDIUM SHADE
309
- '\u{1FB8E}': [0, 0, 1, 0.5], // UPPER HALF MEDIUM SHADE
310
- '\u{1FB8F}': [0, 0.5, 1, 0.5], // LOWER HALF MEDIUM SHADE
311
- '\u{1FB90}': [0, 0, 1, 1] // INVERSE MEDIUM SHADE
312
- };
313
-
314
- /**
315
- * Block + inverse shade combo characters.
316
- * Each entry: [solidRegion, shadeRegion] where regions are [x, y, w, h] in 0-1 coords.
317
- * Shade region uses inverse medium shade pattern.
318
- */
319
- export const blockShadeComboDefinitions: { [index: string]: [[number, number, number, number], [number, number, number, number]] | undefined } = {
320
- '\u{1FB91}': [[0, 0, 1, 0.5], [0, 0.5, 1, 0.5]], // UPPER HALF BLOCK AND LOWER HALF INVERSE MEDIUM SHADE
321
- '\u{1FB92}': [[0, 0.5, 1, 0.5], [0, 0, 1, 0.5]], // UPPER HALF INVERSE MEDIUM SHADE AND LOWER HALF BLOCK
322
- // 1FB93 is reserved
323
- '\u{1FB94}': [[0.5, 0, 0.5, 1], [0, 0, 0.5, 1]] // LEFT HALF INVERSE MEDIUM SHADE AND RIGHT HALF BLOCK
324
- };
325
-
326
- type PatternDefinition = number[][];
327
-
328
- /**
329
- * Defines the repeating pattern used by special characters, the pattern is made up of a 2d array of
330
- * pixel values to be filled (1) or not filled (0).
331
- */
332
- const patternCharacterDefinitions: { [key: string]: PatternDefinition | undefined } = {
333
- // Shade characters (0x2591-0x2593)
334
- '░': [ // LIGHT SHADE (25%)
335
- [1, 0, 0, 0],
336
- [0, 0, 0, 0],
337
- [0, 0, 1, 0],
338
- [0, 0, 0, 0]
339
- ],
340
- '▒': [ // MEDIUM SHADE (50%)
341
- [1, 0],
342
- [0, 0],
343
- [0, 1],
344
- [0, 0]
345
- ],
346
- '▓': [ // DARK SHADE (75%)
347
- [0, 1],
348
- [1, 1],
349
- [1, 0],
350
- [1, 1]
351
- ]
352
- };
353
-
354
- const enum Shapes {
355
- /** │ */ TOP_TO_BOTTOM = 'M.5,0 L.5,1',
356
- /** ─ */ LEFT_TO_RIGHT = 'M0,.5 L1,.5',
357
-
358
- /** └ */ TOP_TO_RIGHT = 'M.5,0 L.5,.5 L1,.5',
359
- /** ┘ */ TOP_TO_LEFT = 'M.5,0 L.5,.5 L0,.5',
360
- /** ┐ */ LEFT_TO_BOTTOM = 'M0,.5 L.5,.5 L.5,1',
361
- /** ┌ */ RIGHT_TO_BOTTOM = 'M0.5,1 L.5,.5 L1,.5',
362
-
363
- /** ╵ */ MIDDLE_TO_TOP = 'M.5,.5 L.5,0',
364
- /** ╴ */ MIDDLE_TO_LEFT = 'M.5,.5 L0,.5',
365
- /** ╶ */ MIDDLE_TO_RIGHT = 'M.5,.5 L1,.5',
366
- /** ╷ */ MIDDLE_TO_BOTTOM = 'M.5,.5 L.5,1',
367
-
368
- /** ┴ */ T_TOP = 'M0,.5 L1,.5 M.5,.5 L.5,0',
369
- /** ┤ */ T_LEFT = 'M.5,0 L.5,1 M.5,.5 L0,.5',
370
- /** ├ */ T_RIGHT = 'M.5,0 L.5,1 M.5,.5 L1,.5',
371
- /** ┬ */ T_BOTTOM = 'M0,.5 L1,.5 M.5,.5 L.5,1',
372
-
373
- /** ┼ */ CROSS = 'M0,.5 L1,.5 M.5,0 L.5,1',
374
-
375
- /** ╌ */ TWO_DASHES_HORIZONTAL = 'M.1,.5 L.4,.5 M.6,.5 L.9,.5', // .2 empty, .3 filled
376
- /** ┄ */ THREE_DASHES_HORIZONTAL = 'M.0667,.5 L.2667,.5 M.4,.5 L.6,.5 M.7333,.5 L.9333,.5', // .1333 empty, .2 filled
377
- /** ┉ */ FOUR_DASHES_HORIZONTAL = 'M.05,.5 L.2,.5 M.3,.5 L.45,.5 M.55,.5 L.7,.5 M.8,.5 L.95,.5', // .1 empty, .15 filled
378
- /** ╎ */ TWO_DASHES_VERTICAL = 'M.5,.1 L.5,.4 M.5,.6 L.5,.9',
379
- /** ┆ */ THREE_DASHES_VERTICAL = 'M.5,.0667 L.5,.2667 M.5,.4 L.5,.6 M.5,.7333 L.5,.9333',
380
- /** ┊ */ FOUR_DASHES_VERTICAL = 'M.5,.05 L.5,.2 M.5,.3 L.5,.45 L.5,.55 M.5,.7 L.5,.95',
381
- }
382
-
383
- const enum Style {
384
- NORMAL = 1,
385
- BOLD = 3
386
- }
387
-
388
- /**
389
- * @param xp The percentage of 15% of the x axis.
390
- * @param yp The percentage of 15% of the x axis on the y axis.
391
- */
392
- type DrawFunctionDefinition = (xp: number, yp: number) => string;
393
-
394
- /**
395
- * This contains the definitions of all box drawing characters in the format of SVG paths (ie. the
396
- * svg d attribute).
397
- */
398
- export const boxDrawingDefinitions: { [character: string]: { [fontWeight: number]: string | DrawFunctionDefinition } | undefined } = {
399
- // Uniform normal and bold
400
- '─': { [Style.NORMAL]: Shapes.LEFT_TO_RIGHT },
401
- '━': { [Style.BOLD]: Shapes.LEFT_TO_RIGHT },
402
- '│': { [Style.NORMAL]: Shapes.TOP_TO_BOTTOM },
403
- '┃': { [Style.BOLD]: Shapes.TOP_TO_BOTTOM },
404
- '┌': { [Style.NORMAL]: Shapes.RIGHT_TO_BOTTOM },
405
- '┏': { [Style.BOLD]: Shapes.RIGHT_TO_BOTTOM },
406
- '┐': { [Style.NORMAL]: Shapes.LEFT_TO_BOTTOM },
407
- '┓': { [Style.BOLD]: Shapes.LEFT_TO_BOTTOM },
408
- '└': { [Style.NORMAL]: Shapes.TOP_TO_RIGHT },
409
- '┗': { [Style.BOLD]: Shapes.TOP_TO_RIGHT },
410
- '┘': { [Style.NORMAL]: Shapes.TOP_TO_LEFT },
411
- '┛': { [Style.BOLD]: Shapes.TOP_TO_LEFT },
412
- '├': { [Style.NORMAL]: Shapes.T_RIGHT },
413
- '┣': { [Style.BOLD]: Shapes.T_RIGHT },
414
- '┤': { [Style.NORMAL]: Shapes.T_LEFT },
415
- '┫': { [Style.BOLD]: Shapes.T_LEFT },
416
- '┬': { [Style.NORMAL]: Shapes.T_BOTTOM },
417
- '┳': { [Style.BOLD]: Shapes.T_BOTTOM },
418
- '┴': { [Style.NORMAL]: Shapes.T_TOP },
419
- '┻': { [Style.BOLD]: Shapes.T_TOP },
420
- '┼': { [Style.NORMAL]: Shapes.CROSS },
421
- '╋': { [Style.BOLD]: Shapes.CROSS },
422
- '╴': { [Style.NORMAL]: Shapes.MIDDLE_TO_LEFT },
423
- '╸': { [Style.BOLD]: Shapes.MIDDLE_TO_LEFT },
424
- '╵': { [Style.NORMAL]: Shapes.MIDDLE_TO_TOP },
425
- '╹': { [Style.BOLD]: Shapes.MIDDLE_TO_TOP },
426
- '╶': { [Style.NORMAL]: Shapes.MIDDLE_TO_RIGHT },
427
- '╺': { [Style.BOLD]: Shapes.MIDDLE_TO_RIGHT },
428
- '╷': { [Style.NORMAL]: Shapes.MIDDLE_TO_BOTTOM },
429
- '╻': { [Style.BOLD]: Shapes.MIDDLE_TO_BOTTOM },
430
-
431
- // Double border
432
- '═': { [Style.NORMAL]: (xp, yp) => `M0,${.5 - yp} L1,${.5 - yp} M0,${.5 + yp} L1,${.5 + yp}` },
433
- '║': { [Style.NORMAL]: (xp, yp) => `M${.5 - xp},0 L${.5 - xp},1 M${.5 + xp},0 L${.5 + xp},1` },
434
- '╒': { [Style.NORMAL]: (xp, yp) => `M.5,1 L.5,${.5 - yp} L1,${.5 - yp} M.5,${.5 + yp} L1,${.5 + yp}` },
435
- '╓': { [Style.NORMAL]: (xp, yp) => `M${.5 - xp},1 L${.5 - xp},.5 L1,.5 M${.5 + xp},.5 L${.5 + xp},1` },
436
- '╔': { [Style.NORMAL]: (xp, yp) => `M1,${.5 - yp} L${.5 - xp},${.5 - yp} L${.5 - xp},1 M1,${.5 + yp} L${.5 + xp},${.5 + yp} L${.5 + xp},1` },
437
- '╕': { [Style.NORMAL]: (xp, yp) => `M0,${.5 - yp} L.5,${.5 - yp} L.5,1 M0,${.5 + yp} L.5,${.5 + yp}` },
438
- '╖': { [Style.NORMAL]: (xp, yp) => `M${.5 + xp},1 L${.5 + xp},.5 L0,.5 M${.5 - xp},.5 L${.5 - xp},1` },
439
- '╗': { [Style.NORMAL]: (xp, yp) => `M0,${.5 + yp} L${.5 - xp},${.5 + yp} L${.5 - xp},1 M0,${.5 - yp} L${.5 + xp},${.5 - yp} L${.5 + xp},1` },
440
- '╘': { [Style.NORMAL]: (xp, yp) => `M.5,0 L.5,${.5 + yp} L1,${.5 + yp} M.5,${.5 - yp} L1,${.5 - yp}` },
441
- '╙': { [Style.NORMAL]: (xp, yp) => `M1,.5 L${.5 - xp},.5 L${.5 - xp},0 M${.5 + xp},.5 L${.5 + xp},0` },
442
- '╚': { [Style.NORMAL]: (xp, yp) => `M1,${.5 - yp} L${.5 + xp},${.5 - yp} L${.5 + xp},0 M1,${.5 + yp} L${.5 - xp},${.5 + yp} L${.5 - xp},0` },
443
- '╛': { [Style.NORMAL]: (xp, yp) => `M0,${.5 + yp} L.5,${.5 + yp} L.5,0 M0,${.5 - yp} L.5,${.5 - yp}` },
444
- '╜': { [Style.NORMAL]: (xp, yp) => `M0,.5 L${.5 + xp},.5 L${.5 + xp},0 M${.5 - xp},.5 L${.5 - xp},0` },
445
- '╝': { [Style.NORMAL]: (xp, yp) => `M0,${.5 - yp} L${.5 - xp},${.5 - yp} L${.5 - xp},0 M0,${.5 + yp} L${.5 + xp},${.5 + yp} L${.5 + xp},0` },
446
- '╞': { [Style.NORMAL]: (xp, yp) => `${Shapes.TOP_TO_BOTTOM} M.5,${.5 - yp} L1,${.5 - yp} M.5,${.5 + yp} L1,${.5 + yp}` },
447
- '╟': { [Style.NORMAL]: (xp, yp) => `M${.5 - xp},0 L${.5 - xp},1 M${.5 + xp},0 L${.5 + xp},1 M${.5 + xp},.5 L1,.5` },
448
- '╠': { [Style.NORMAL]: (xp, yp) => `M${.5 - xp},0 L${.5 - xp},1 M1,${.5 + yp} L${.5 + xp},${.5 + yp} L${.5 + xp},1 M1,${.5 - yp} L${.5 + xp},${.5 - yp} L${.5 + xp},0` },
449
- '╡': { [Style.NORMAL]: (xp, yp) => `${Shapes.TOP_TO_BOTTOM} M0,${.5 - yp} L.5,${.5 - yp} M0,${.5 + yp} L.5,${.5 + yp}` },
450
- '╢': { [Style.NORMAL]: (xp, yp) => `M0,.5 L${.5 - xp},.5 M${.5 - xp},0 L${.5 - xp},1 M${.5 + xp},0 L${.5 + xp},1` },
451
- '╣': { [Style.NORMAL]: (xp, yp) => `M${.5 + xp},0 L${.5 + xp},1 M0,${.5 + yp} L${.5 - xp},${.5 + yp} L${.5 - xp},1 M0,${.5 - yp} L${.5 - xp},${.5 - yp} L${.5 - xp},0` },
452
- '╤': { [Style.NORMAL]: (xp, yp) => `M0,${.5 - yp} L1,${.5 - yp} M0,${.5 + yp} L1,${.5 + yp} M.5,${.5 + yp} L.5,1` },
453
- '╥': { [Style.NORMAL]: (xp, yp) => `${Shapes.LEFT_TO_RIGHT} M${.5 - xp},.5 L${.5 - xp},1 M${.5 + xp},.5 L${.5 + xp},1` },
454
- '╦': { [Style.NORMAL]: (xp, yp) => `M0,${.5 - yp} L1,${.5 - yp} M0,${.5 + yp} L${.5 - xp},${.5 + yp} L${.5 - xp},1 M1,${.5 + yp} L${.5 + xp},${.5 + yp} L${.5 + xp},1` },
455
- '╧': { [Style.NORMAL]: (xp, yp) => `M.5,0 L.5,${.5 - yp} M0,${.5 - yp} L1,${.5 - yp} M0,${.5 + yp} L1,${.5 + yp}` },
456
- '╨': { [Style.NORMAL]: (xp, yp) => `${Shapes.LEFT_TO_RIGHT} M${.5 - xp},.5 L${.5 - xp},0 M${.5 + xp},.5 L${.5 + xp},0` },
457
- '╩': { [Style.NORMAL]: (xp, yp) => `M0,${.5 + yp} L1,${.5 + yp} M0,${.5 - yp} L${.5 - xp},${.5 - yp} L${.5 - xp},0 M1,${.5 - yp} L${.5 + xp},${.5 - yp} L${.5 + xp},0` },
458
- '╪': { [Style.NORMAL]: (xp, yp) => `${Shapes.TOP_TO_BOTTOM} M0,${.5 - yp} L1,${.5 - yp} M0,${.5 + yp} L1,${.5 + yp}` },
459
- '╫': { [Style.NORMAL]: (xp, yp) => `${Shapes.LEFT_TO_RIGHT} M${.5 - xp},0 L${.5 - xp},1 M${.5 + xp},0 L${.5 + xp},1` },
460
- '╬': { [Style.NORMAL]: (xp, yp) => `M0,${.5 + yp} L${.5 - xp},${.5 + yp} L${.5 - xp},1 M1,${.5 + yp} L${.5 + xp},${.5 + yp} L${.5 + xp},1 M0,${.5 - yp} L${.5 - xp},${.5 - yp} L${.5 - xp},0 M1,${.5 - yp} L${.5 + xp},${.5 - yp} L${.5 + xp},0` },
461
-
462
- // Diagonal
463
- '╱': { [Style.NORMAL]: 'M1,0 L0,1' },
464
- '╲': { [Style.NORMAL]: 'M0,0 L1,1' },
465
- '╳': { [Style.NORMAL]: 'M1,0 L0,1 M0,0 L1,1' },
466
-
467
- // Mixed weight
468
- '╼': { [Style.NORMAL]: Shapes.MIDDLE_TO_LEFT, [Style.BOLD]: Shapes.MIDDLE_TO_RIGHT },
469
- '╽': { [Style.NORMAL]: Shapes.MIDDLE_TO_TOP, [Style.BOLD]: Shapes.MIDDLE_TO_BOTTOM },
470
- '╾': { [Style.NORMAL]: Shapes.MIDDLE_TO_RIGHT, [Style.BOLD]: Shapes.MIDDLE_TO_LEFT },
471
- '╿': { [Style.NORMAL]: Shapes.MIDDLE_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_TOP },
472
- '┍': { [Style.NORMAL]: Shapes.MIDDLE_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_RIGHT },
473
- '┎': { [Style.NORMAL]: Shapes.MIDDLE_TO_RIGHT, [Style.BOLD]: Shapes.MIDDLE_TO_BOTTOM },
474
- '┑': { [Style.NORMAL]: Shapes.MIDDLE_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_LEFT },
475
- '┒': { [Style.NORMAL]: Shapes.MIDDLE_TO_LEFT, [Style.BOLD]: Shapes.MIDDLE_TO_BOTTOM },
476
- '┕': { [Style.NORMAL]: Shapes.MIDDLE_TO_TOP, [Style.BOLD]: Shapes.MIDDLE_TO_RIGHT },
477
- '┖': { [Style.NORMAL]: Shapes.MIDDLE_TO_RIGHT, [Style.BOLD]: Shapes.MIDDLE_TO_TOP },
478
- '┙': { [Style.NORMAL]: Shapes.MIDDLE_TO_TOP, [Style.BOLD]: Shapes.MIDDLE_TO_LEFT },
479
- '┚': { [Style.NORMAL]: Shapes.MIDDLE_TO_LEFT, [Style.BOLD]: Shapes.MIDDLE_TO_TOP },
480
- '┝': { [Style.NORMAL]: Shapes.TOP_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_RIGHT },
481
- '┞': { [Style.NORMAL]: Shapes.RIGHT_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_TOP },
482
- '┟': { [Style.NORMAL]: Shapes.TOP_TO_RIGHT, [Style.BOLD]: Shapes.MIDDLE_TO_BOTTOM },
483
- '┠': { [Style.NORMAL]: Shapes.MIDDLE_TO_RIGHT, [Style.BOLD]: Shapes.TOP_TO_BOTTOM },
484
- '┡': { [Style.NORMAL]: Shapes.MIDDLE_TO_BOTTOM, [Style.BOLD]: Shapes.TOP_TO_RIGHT },
485
- '┢': { [Style.NORMAL]: Shapes.MIDDLE_TO_TOP, [Style.BOLD]: Shapes.RIGHT_TO_BOTTOM },
486
- '┥': { [Style.NORMAL]: Shapes.TOP_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_LEFT },
487
- '┦': { [Style.NORMAL]: Shapes.LEFT_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_TOP },
488
- '┧': { [Style.NORMAL]: Shapes.TOP_TO_LEFT, [Style.BOLD]: Shapes.MIDDLE_TO_BOTTOM },
489
- '┨': { [Style.NORMAL]: Shapes.MIDDLE_TO_LEFT, [Style.BOLD]: Shapes.TOP_TO_BOTTOM },
490
- '┩': { [Style.NORMAL]: Shapes.MIDDLE_TO_BOTTOM, [Style.BOLD]: Shapes.TOP_TO_LEFT },
491
- '┪': { [Style.NORMAL]: Shapes.MIDDLE_TO_TOP, [Style.BOLD]: Shapes.LEFT_TO_BOTTOM },
492
- '┭': { [Style.NORMAL]: Shapes.RIGHT_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_LEFT },
493
- '┮': { [Style.NORMAL]: Shapes.LEFT_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_RIGHT },
494
- '┯': { [Style.NORMAL]: Shapes.MIDDLE_TO_BOTTOM, [Style.BOLD]: Shapes.LEFT_TO_RIGHT },
495
- '┰': { [Style.NORMAL]: Shapes.LEFT_TO_RIGHT, [Style.BOLD]: Shapes.MIDDLE_TO_BOTTOM },
496
- '┱': { [Style.NORMAL]: Shapes.MIDDLE_TO_RIGHT, [Style.BOLD]: Shapes.LEFT_TO_BOTTOM },
497
- '┲': { [Style.NORMAL]: Shapes.MIDDLE_TO_LEFT, [Style.BOLD]: Shapes.RIGHT_TO_BOTTOM },
498
- '┵': { [Style.NORMAL]: Shapes.TOP_TO_RIGHT, [Style.BOLD]: Shapes.MIDDLE_TO_LEFT },
499
- '┶': { [Style.NORMAL]: Shapes.TOP_TO_LEFT, [Style.BOLD]: Shapes.MIDDLE_TO_RIGHT },
500
- '┷': { [Style.NORMAL]: Shapes.MIDDLE_TO_TOP, [Style.BOLD]: Shapes.LEFT_TO_RIGHT },
501
- '┸': { [Style.NORMAL]: Shapes.LEFT_TO_RIGHT, [Style.BOLD]: Shapes.MIDDLE_TO_TOP },
502
- '┹': { [Style.NORMAL]: Shapes.MIDDLE_TO_RIGHT, [Style.BOLD]: Shapes.TOP_TO_LEFT },
503
- '┺': { [Style.NORMAL]: Shapes.MIDDLE_TO_LEFT, [Style.BOLD]: Shapes.TOP_TO_RIGHT },
504
- '┽': { [Style.NORMAL]: `${Shapes.TOP_TO_BOTTOM} ${Shapes.MIDDLE_TO_RIGHT}`, [Style.BOLD]: Shapes.MIDDLE_TO_LEFT },
505
- '┾': { [Style.NORMAL]: `${Shapes.TOP_TO_BOTTOM} ${Shapes.MIDDLE_TO_LEFT}`, [Style.BOLD]: Shapes.MIDDLE_TO_RIGHT },
506
- '┿': { [Style.NORMAL]: Shapes.TOP_TO_BOTTOM, [Style.BOLD]: Shapes.LEFT_TO_RIGHT },
507
- '╀': { [Style.NORMAL]: `${Shapes.LEFT_TO_RIGHT} ${Shapes.MIDDLE_TO_BOTTOM}`, [Style.BOLD]: Shapes.MIDDLE_TO_TOP },
508
- '╁': { [Style.NORMAL]: `${Shapes.MIDDLE_TO_TOP} ${Shapes.LEFT_TO_RIGHT}`, [Style.BOLD]: Shapes.MIDDLE_TO_BOTTOM },
509
- '╂': { [Style.NORMAL]: Shapes.LEFT_TO_RIGHT, [Style.BOLD]: Shapes.TOP_TO_BOTTOM },
510
- '╃': { [Style.NORMAL]: Shapes.RIGHT_TO_BOTTOM, [Style.BOLD]: Shapes.TOP_TO_LEFT },
511
- '╄': { [Style.NORMAL]: Shapes.LEFT_TO_BOTTOM, [Style.BOLD]: Shapes.TOP_TO_RIGHT },
512
- '╅': { [Style.NORMAL]: Shapes.TOP_TO_RIGHT, [Style.BOLD]: Shapes.LEFT_TO_BOTTOM },
513
- '╆': { [Style.NORMAL]: Shapes.TOP_TO_LEFT, [Style.BOLD]: Shapes.RIGHT_TO_BOTTOM },
514
- '╇': { [Style.NORMAL]: Shapes.MIDDLE_TO_BOTTOM, [Style.BOLD]: `${Shapes.MIDDLE_TO_TOP} ${Shapes.LEFT_TO_RIGHT}` },
515
- '╈': { [Style.NORMAL]: Shapes.MIDDLE_TO_TOP, [Style.BOLD]: `${Shapes.LEFT_TO_RIGHT} ${Shapes.MIDDLE_TO_BOTTOM}` },
516
- '╉': { [Style.NORMAL]: Shapes.MIDDLE_TO_RIGHT, [Style.BOLD]: `${Shapes.TOP_TO_BOTTOM} ${Shapes.MIDDLE_TO_LEFT}` },
517
- '╊': { [Style.NORMAL]: Shapes.MIDDLE_TO_LEFT, [Style.BOLD]: `${Shapes.TOP_TO_BOTTOM} ${Shapes.MIDDLE_TO_RIGHT}` },
518
-
519
- // Dashed
520
- '╌': { [Style.NORMAL]: Shapes.TWO_DASHES_HORIZONTAL },
521
- '╍': { [Style.BOLD]: Shapes.TWO_DASHES_HORIZONTAL },
522
- '┄': { [Style.NORMAL]: Shapes.THREE_DASHES_HORIZONTAL },
523
- '┅': { [Style.BOLD]: Shapes.THREE_DASHES_HORIZONTAL },
524
- '┈': { [Style.NORMAL]: Shapes.FOUR_DASHES_HORIZONTAL },
525
- '┉': { [Style.BOLD]: Shapes.FOUR_DASHES_HORIZONTAL },
526
- '╎': { [Style.NORMAL]: Shapes.TWO_DASHES_VERTICAL },
527
- '╏': { [Style.BOLD]: Shapes.TWO_DASHES_VERTICAL },
528
- '┆': { [Style.NORMAL]: Shapes.THREE_DASHES_VERTICAL },
529
- '┇': { [Style.BOLD]: Shapes.THREE_DASHES_VERTICAL },
530
- '┊': { [Style.NORMAL]: Shapes.FOUR_DASHES_VERTICAL },
531
- '┋': { [Style.BOLD]: Shapes.FOUR_DASHES_VERTICAL },
532
-
533
- // Curved
534
- '╭': { [Style.NORMAL]: (xp, yp) => `M.5,1 L.5,${.5 + (yp / .15 * .5)} C.5,${.5 + (yp / .15 * .5)},.5,.5,1,.5` },
535
- '╮': { [Style.NORMAL]: (xp, yp) => `M.5,1 L.5,${.5 + (yp / .15 * .5)} C.5,${.5 + (yp / .15 * .5)},.5,.5,0,.5` },
536
- '╯': { [Style.NORMAL]: (xp, yp) => `M.5,0 L.5,${.5 - (yp / .15 * .5)} C.5,${.5 - (yp / .15 * .5)},.5,.5,0,.5` },
537
- '╰': { [Style.NORMAL]: (xp, yp) => `M.5,0 L.5,${.5 - (yp / .15 * .5)} C.5,${.5 - (yp / .15 * .5)},.5,.5,1,.5` }
538
- };
539
-
540
- interface IVectorShape {
541
- d: string;
542
- type: VectorType;
543
- leftPadding?: number;
544
- rightPadding?: number;
545
- }
546
-
547
- const enum VectorType {
548
- FILL,
549
- STROKE
550
- }
551
-
552
- /**
553
- * This contains the definitions of the primarily used box drawing characters as vector shapes. The
554
- * reason these characters are defined specially is to avoid common problems if a user's font has
555
- * not been patched with powerline characters and also to get pixel perfect rendering as rendering
556
- * issues can occur around AA/SPAA.
557
- *
558
- * The line variants draw beyond the cell and get clipped to ensure the end of the line is not
559
- * visible.
560
- *
561
- * Original symbols defined in https://github.com/powerline/fontpatcher
562
- */
563
- export const powerlineDefinitions: { [index: string]: IVectorShape } = {
564
- // Git branch
565
- '\u{E0A0}': { d: 'M.3,1 L.03,1 L.03,.88 C.03,.82,.06,.78,.11,.73 C.15,.7,.2,.68,.28,.65 L.43,.6 C.49,.58,.53,.56,.56,.53 C.59,.5,.6,.47,.6,.43 L.6,.27 L.4,.27 L.69,.1 L.98,.27 L.78,.27 L.78,.46 C.78,.52,.76,.56,.72,.61 C.68,.66,.63,.67,.56,.7 L.48,.72 C.42,.74,.38,.76,.35,.78 C.32,.8,.31,.84,.31,.88 L.31,1 M.3,.5 L.03,.59 L.03,.09 L.3,.09 L.3,.655', type: VectorType.FILL },
566
- // L N
567
- '\u{E0A1}': { d: 'M.7,.4 L.7,.47 L.2,.47 L.2,.03 L.355,.03 L.355,.4 L.705,.4 M.7,.5 L.86,.5 L.86,.95 L.69,.95 L.44,.66 L.46,.86 L.46,.95 L.3,.95 L.3,.49 L.46,.49 L.71,.78 L.69,.565 L.69,.5', type: VectorType.FILL },
568
- // Lock
569
- '\u{E0A2}': { d: 'M.25,.94 C.16,.94,.11,.92,.11,.87 L.11,.53 C.11,.48,.15,.455,.23,.45 L.23,.3 C.23,.25,.26,.22,.31,.19 C.36,.16,.43,.15,.51,.15 C.59,.15,.66,.16,.71,.19 C.77,.22,.79,.26,.79,.3 L.79,.45 C.87,.45,.91,.48,.91,.53 L.91,.87 C.91,.92,.86,.94,.77,.94 L.24,.94 M.53,.2 C.49,.2,.45,.21,.42,.23 C.39,.25,.38,.27,.38,.3 L.38,.45 L.68,.45 L.68,.3 C.68,.27,.67,.25,.64,.23 C.61,.21,.58,.2,.53,.2 M.58,.82 L.58,.66 C.63,.65,.65,.63,.65,.6 C.65,.58,.64,.57,.61,.56 C.58,.55,.56,.54,.52,.54 C.48,.54,.46,.55,.43,.56 C.4,.57,.39,.59,.39,.6 C.39,.63,.41,.64,.46,.66 L.46,.82 L.57,.82', type: VectorType.FILL },
570
- // Right triangle solid
571
- '\u{E0B0}': { d: 'M0,0 L1,.5 L0,1', type: VectorType.FILL, rightPadding: 2 },
572
- // Right triangle line
573
- '\u{E0B1}': { d: 'M-1,-.5 L1,.5 L-1,1.5', type: VectorType.STROKE, leftPadding: 1, rightPadding: 1 },
574
- // Left triangle solid
575
- '\u{E0B2}': { d: 'M1,0 L0,.5 L1,1', type: VectorType.FILL, leftPadding: 2 },
576
- // Left triangle line
577
- '\u{E0B3}': { d: 'M2,-.5 L0,.5 L2,1.5', type: VectorType.STROKE, leftPadding: 1, rightPadding: 1 },
578
- // Right semi-circle solid
579
- '\u{E0B4}': { d: 'M0,0 L0,1 C0.552,1,1,0.776,1,.5 C1,0.224,0.552,0,0,0', type: VectorType.FILL, rightPadding: 1 },
580
- // Right semi-circle line
581
- '\u{E0B5}': { d: 'M.2,1 C.422,1,.8,.826,.78,.5 C.8,.174,0.422,0,.2,0', type: VectorType.STROKE, rightPadding: 1 },
582
- // Left semi-circle solid
583
- '\u{E0B6}': { d: 'M1,0 L1,1 C0.448,1,0,0.776,0,.5 C0,0.224,0.448,0,1,0', type: VectorType.FILL, leftPadding: 1 },
584
- // Left semi-circle line
585
- '\u{E0B7}': { d: 'M.8,1 C0.578,1,0.2,.826,.22,.5 C0.2,0.174,0.578,0,0.8,0', type: VectorType.STROKE, leftPadding: 1 },
586
- // Lower left triangle
587
- '\u{E0B8}': { d: 'M-.5,-.5 L1.5,1.5 L-.5,1.5', type: VectorType.FILL },
588
- // Backslash separator
589
- '\u{E0B9}': { d: 'M-.5,-.5 L1.5,1.5', type: VectorType.STROKE, leftPadding: 1, rightPadding: 1 },
590
- // Lower right triangle
591
- '\u{E0BA}': { d: 'M1.5,-.5 L-.5,1.5 L1.5,1.5', type: VectorType.FILL },
592
- // Upper left triangle
593
- '\u{E0BC}': { d: 'M1.5,-.5 L-.5,1.5 L-.5,-.5', type: VectorType.FILL },
594
- // Forward slash separator
595
- '\u{E0BD}': { d: 'M1.5,-.5 L-.5,1.5', type: VectorType.STROKE, leftPadding: 1, rightPadding: 1 },
596
- // Upper right triangle
597
- '\u{E0BE}': { d: 'M-.5,-.5 L1.5,1.5 L1.5,-.5', type: VectorType.FILL }
598
- };
599
- // Forward slash separator redundant
600
- powerlineDefinitions['\u{E0BB}'] = powerlineDefinitions['\u{E0BD}'];
601
- // Backslash separator redundant
602
- powerlineDefinitions['\u{E0BF}'] = powerlineDefinitions['\u{E0B9}'];
603
-
604
- /**
605
- * Try drawing a custom block element or box drawing character, returning whether it was
606
- * successfully drawn.
607
- */
608
- export function tryDrawCustomChar(
609
- ctx: CanvasRenderingContext2D,
610
- c: string,
611
- xOffset: number,
612
- yOffset: number,
613
- deviceCellWidth: number,
614
- deviceCellHeight: number,
615
- fontSize: number,
616
- devicePixelRatio: number
617
- ): boolean {
618
- const blockElementDefinition = blockElementDefinitions[c];
619
- if (blockElementDefinition) {
620
- drawBlockVectorChar(ctx, blockElementDefinition, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
621
- return true;
622
- }
623
-
624
- const symbolsForLegacyComputingDefinition = symbolsForLegacyComputingDefinitions[c];
625
- if (symbolsForLegacyComputingDefinition) {
626
- drawPathDefinitionCharacter(ctx, symbolsForLegacyComputingDefinition, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
627
- return true;
628
- }
629
-
630
- const rectangularShadeDefinition = rectangularShadeDefinitions[c];
631
- if (rectangularShadeDefinition) {
632
- drawRectangularShadeChar(ctx, rectangularShadeDefinition, xOffset, yOffset, deviceCellWidth, deviceCellHeight, c === '\u{1FB90}');
633
- return true;
634
- }
635
-
636
- const blockShadeComboDefinition = blockShadeComboDefinitions[c];
637
- if (blockShadeComboDefinition) {
638
- drawBlockShadeComboChar(ctx, blockShadeComboDefinition, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
639
- return true;
640
- }
641
-
642
- const patternDefinition = patternCharacterDefinitions[c];
643
- if (patternDefinition) {
644
- drawPatternChar(ctx, patternDefinition, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
645
- return true;
646
- }
647
-
648
- const boxDrawingDefinition = boxDrawingDefinitions[c];
649
- if (boxDrawingDefinition) {
650
- drawBoxDrawingChar(ctx, boxDrawingDefinition, xOffset, yOffset, deviceCellWidth, deviceCellHeight, devicePixelRatio);
651
- return true;
652
- }
653
-
654
- const powerlineDefinition = powerlineDefinitions[c];
655
- if (powerlineDefinition) {
656
- drawPowerlineChar(ctx, powerlineDefinition, xOffset, yOffset, deviceCellWidth, deviceCellHeight, fontSize, devicePixelRatio);
657
- return true;
658
- }
659
-
660
- return false;
661
- }
662
-
663
- function drawBlockVectorChar(
664
- ctx: CanvasRenderingContext2D,
665
- charDefinition: IBlockVector[],
666
- xOffset: number,
667
- yOffset: number,
668
- deviceCellWidth: number,
669
- deviceCellHeight: number
670
- ): void {
671
- for (let i = 0; i < charDefinition.length; i++) {
672
- const box = charDefinition[i];
673
- const xEighth = deviceCellWidth / 8;
674
- const yEighth = deviceCellHeight / 8;
675
- ctx.fillRect(
676
- xOffset + box.x * xEighth,
677
- yOffset + box.y * yEighth,
678
- box.w * xEighth,
679
- box.h * yEighth
680
- );
681
- }
682
- }
683
-
684
- function drawPathDefinitionCharacter(
685
- ctx: CanvasRenderingContext2D,
686
- charDefinition: DrawFunctionDefinition,
687
- xOffset: number,
688
- yOffset: number,
689
- deviceCellWidth: number,
690
- deviceCellHeight: number
691
- ): void {
692
- const instructions = charDefinition(0, 0);
693
- ctx.beginPath();
694
- for (const instruction of instructions.split(' ')) {
695
- const type = instruction[0];
696
- const args: string[] = instruction.substring(1).split(',');
697
- if (!args[0] || !args[1]) {
698
- if (type === 'Z') {
699
- ctx.closePath();
700
- }
701
- continue;
702
- }
703
- const translatedArgs = args.map((e, i) => {
704
- const val = parseFloat(e);
705
- return i % 2 === 0
706
- ? xOffset + val * deviceCellWidth
707
- : yOffset + val * deviceCellHeight;
708
- });
709
- if (type === 'M') {
710
- ctx.moveTo(translatedArgs[0], translatedArgs[1]);
711
- } else if (type === 'L') {
712
- ctx.lineTo(translatedArgs[0], translatedArgs[1]);
713
- }
714
- }
715
- ctx.fill();
716
- }
717
-
718
- const cachedPatterns: Map<PatternDefinition, Map</* fillStyle */string, CanvasPattern>> = new Map();
719
-
720
- function drawPatternChar(
721
- ctx: CanvasRenderingContext2D,
722
- charDefinition: number[][],
723
- xOffset: number,
724
- yOffset: number,
725
- deviceCellWidth: number,
726
- deviceCellHeight: number
727
- ): void {
728
- let patternSet = cachedPatterns.get(charDefinition);
729
- if (!patternSet) {
730
- patternSet = new Map();
731
- cachedPatterns.set(charDefinition, patternSet);
732
- }
733
- const fillStyle = ctx.fillStyle;
734
- if (typeof fillStyle !== 'string') {
735
- throw new Error(`Unexpected fillStyle type "${fillStyle}"`);
736
- }
737
- let pattern = patternSet.get(fillStyle);
738
- if (!pattern) {
739
- const width = charDefinition[0].length;
740
- const height = charDefinition.length;
741
- const tmpCanvas = ctx.canvas.ownerDocument.createElement('canvas');
742
- tmpCanvas.width = width;
743
- tmpCanvas.height = height;
744
- const tmpCtx = throwIfFalsy(tmpCanvas.getContext('2d'));
745
- const imageData = new ImageData(width, height);
746
-
747
- // Extract rgba from fillStyle
748
- let r: number;
749
- let g: number;
750
- let b: number;
751
- let a: number;
752
- if (fillStyle.startsWith('#')) {
753
- r = parseInt(fillStyle.slice(1, 3), 16);
754
- g = parseInt(fillStyle.slice(3, 5), 16);
755
- b = parseInt(fillStyle.slice(5, 7), 16);
756
- a = fillStyle.length > 7 && parseInt(fillStyle.slice(7, 9), 16) || 1;
757
- } else if (fillStyle.startsWith('rgba')) {
758
- ([r, g, b, a] = fillStyle.substring(5, fillStyle.length - 1).split(',').map(e => parseFloat(e)));
759
- } else {
760
- throw new Error(`Unexpected fillStyle color format "${fillStyle}" when drawing pattern glyph`);
761
- }
762
-
763
- for (let y = 0; y < height; y++) {
764
- for (let x = 0; x < width; x++) {
765
- imageData.data[(y * width + x) * 4 ] = r;
766
- imageData.data[(y * width + x) * 4 + 1] = g;
767
- imageData.data[(y * width + x) * 4 + 2] = b;
768
- imageData.data[(y * width + x) * 4 + 3] = charDefinition[y][x] * (a * 255);
769
- }
770
- }
771
- tmpCtx.putImageData(imageData, 0, 0);
772
- pattern = throwIfFalsy(ctx.createPattern(tmpCanvas, null));
773
- patternSet.set(fillStyle, pattern);
774
- }
775
- ctx.fillStyle = pattern;
776
- ctx.fillRect(xOffset, yOffset, deviceCellWidth, deviceCellHeight);
777
- }
778
-
779
- /**
780
- * Draws rectangular shade characters - medium shade pattern clipped to a region.
781
- * Uses a checkerboard pattern that shifts 1px each row (same as medium shade U+2592).
782
- */
783
- function drawRectangularShadeChar(
784
- ctx: CanvasRenderingContext2D,
785
- region: [number, number, number, number],
786
- xOffset: number,
787
- yOffset: number,
788
- deviceCellWidth: number,
789
- deviceCellHeight: number,
790
- isInverse: boolean
791
- ): void {
792
- const [rx, ry, rw, rh] = region;
793
- const regionX = Math.round(xOffset + rx * deviceCellWidth);
794
- const regionY = Math.round(yOffset + ry * deviceCellHeight);
795
- const regionW = Math.round(rw * deviceCellWidth);
796
- const regionH = Math.round(rh * deviceCellHeight);
797
-
798
- // For inverse medium shade, we use the opposite pattern (fill where medium shade doesn't)
799
- // Medium shade pattern: fills at (x+y) % 2 === 0, so inverse fills at (x+y) % 2 === 1
800
- const patternOffset = isInverse ? 1 : 0;
801
-
802
- for (let py = 0; py < regionH; py++) {
803
- // Calculate the absolute y position for pattern calculation
804
- const absY = regionY + py - yOffset;
805
- for (let px = 0; px < regionW; px++) {
806
- const absX = regionX + px - xOffset;
807
- // Checkerboard pattern: fill if (x + y) % 2 matches the offset
808
- if (((absX + absY) % 2) === patternOffset) {
809
- ctx.fillRect(regionX + px, regionY + py, 1, 1);
810
- }
811
- }
812
- }
813
- }
814
-
815
- /**
816
- * Draws block + inverse shade combo characters.
817
- * Fills the solid region completely, then draws inverse medium shade in the shade region.
818
- */
819
- function drawBlockShadeComboChar(
820
- ctx: CanvasRenderingContext2D,
821
- regions: [[number, number, number, number], [number, number, number, number]],
822
- xOffset: number,
823
- yOffset: number,
824
- deviceCellWidth: number,
825
- deviceCellHeight: number
826
- ): void {
827
- const [solidRegion, shadeRegion] = regions;
828
-
829
- // Draw solid block region
830
- const solidX = Math.round(xOffset + solidRegion[0] * deviceCellWidth);
831
- const solidY = Math.round(yOffset + solidRegion[1] * deviceCellHeight);
832
- const solidW = Math.round(solidRegion[2] * deviceCellWidth);
833
- const solidH = Math.round(solidRegion[3] * deviceCellHeight);
834
- ctx.fillRect(solidX, solidY, solidW, solidH);
835
-
836
- // Draw inverse medium shade region
837
- const shadeX = Math.round(xOffset + shadeRegion[0] * deviceCellWidth);
838
- const shadeY = Math.round(yOffset + shadeRegion[1] * deviceCellHeight);
839
- const shadeW = Math.round(shadeRegion[2] * deviceCellWidth);
840
- const shadeH = Math.round(shadeRegion[3] * deviceCellHeight);
841
-
842
- for (let py = 0; py < shadeH; py++) {
843
- const absY = shadeY + py - yOffset;
844
- for (let px = 0; px < shadeW; px++) {
845
- const absX = shadeX + px - xOffset;
846
- // Inverse checkerboard: fill at (x + y) % 2 === 1
847
- if (((absX + absY) % 2) === 1) {
848
- ctx.fillRect(shadeX + px, shadeY + py, 1, 1);
849
- }
850
- }
851
- }
852
- }
853
-
854
- /**
855
- * Draws the following box drawing characters by mapping a subset of SVG d attribute instructions to
856
- * canvas draw calls.
857
- *
858
- * Box styles: ┎┰┒┍┯┑╓╥╖╒╤╕ ┏┳┓┌┲┓┌┬┐┏┱┐
859
- * ┌─┬─┐ ┏━┳━┓ ╔═╦═╗ ┠╂┨┝┿┥╟╫╢╞╪╡ ┡╇┩├╊┫┢╈┪┣╉┤
860
- * │ │ │ ┃ ┃ ┃ ║ ║ ║ ┖┸┚┕┷┙╙╨╜╘╧╛ └┴┘└┺┛┗┻┛┗┹┘
861
- * ├─┼─┤ ┣━╋━┫ ╠═╬═╣ ┏┱┐┌┲┓┌┬┐┌┬┐ ┏┳┓┌┮┓┌┬┐┏┭┐
862
- * │ │ │ ┃ ┃ ┃ ║ ║ ║ ┡╃┤├╄┩├╆┪┢╅┤ ┞╀┦├┾┫┟╁┧┣┽┤
863
- * └─┴─┘ ┗━┻━┛ ╚═╩═╝ └┴┘└┴┘└┺┛┗┹┘ └┴┘└┶┛┗┻┛┗┵┘
864
- *
865
- * Other:
866
- * ╭─╮ ╲ ╱ ╷╻╎╏┆┇┊┋ ╺╾╴ ╌╌╌ ┄┄┄ ┈┈┈
867
- * │ │ ╳ ╽╿╎╏┆┇┊┋ ╶╼╸ ╍╍╍ ┅┅┅ ┉┉┉
868
- * ╰─╯ ╱ ╲ ╹╵╎╏┆┇┊┋
869
- *
870
- * All box drawing characters:
871
- * ─ ━ │ ┃ ┄ ┅ ┆ ┇ ┈ ┉ ┊ ┋ ┌ ┍ ┎ ┏
872
- * ┐ ┑ ┒ ┓ └ ┕ ┖ ┗ ┘ ┙ ┚ ┛ ├ ┝ ┞ ┟
873
- * ┠ ┡ ┢ ┣ ┤ ┥ ┦ ┧ ┨ ┩ ┪ ┫ ┬ ┭ ┮ ┯
874
- * ┰ ┱ ┲ ┳ ┴ ┵ ┶ ┷ ┸ ┹ ┺ ┻ ┼ ┽ ┾ ┿
875
- * ╀ ╁ ╂ ╃ ╄ ╅ ╆ ╇ ╈ ╉ ╊ ╋ ╌ ╍ ╎ ╏
876
- * ═ ║ ╒ ╓ ╔ ╕ ╖ ╗ ╘ ╙ ╚ ╛ ╜ ╝ ╞ ╟
877
- * ╠ ╡ ╢ ╣ ╤ ╥ ╦ ╧ ╨ ╩ ╪ ╫ ╬ ╭ ╮ ╯
878
- * ╰ ╱ ╲ ╳ ╴ ╵ ╶ ╷ ╸ ╹ ╺ ╻ ╼ ╽ ╾ ╿
879
- *
880
- * ---
881
- *
882
- * Box drawing alignment tests: █
883
- * ▉
884
- * ╔══╦══╗ ┌──┬──┐ ╭──┬──╮ ╭──┬──╮ ┏━━┳━━┓ ┎┒┏┑ ╷ ╻ ┏┯┓ ┌┰┐ ▊ ╱╲╱╲╳╳╳
885
- * ║┌─╨─┐║ │╔═╧═╗│ │╒═╪═╕│ │╓─╁─╖│ ┃┌─╂─┐┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ ┝╋┥ ▋ ╲╱╲╱╳╳╳
886
- * ║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╿ │┃ ┍╅╆┓ ╵ ╹ ┗┷┛ └┸┘ ▌ ╱╲╱╲╳╳╳
887
- * ╠╡ ╳ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳
888
- * ║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎
889
- * ║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏
890
- * ╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█
891
- *
892
- * Source: https://www.w3.org/2001/06/utf-8-test/UTF-8-demo.html
893
- */
894
- function drawBoxDrawingChar(
895
- ctx: CanvasRenderingContext2D,
896
- charDefinition: { [fontWeight: number]: string | ((xp: number, yp: number) => string) },
897
- xOffset: number,
898
- yOffset: number,
899
- deviceCellWidth: number,
900
- deviceCellHeight: number,
901
- devicePixelRatio: number
902
- ): void {
903
- ctx.strokeStyle = ctx.fillStyle;
904
- for (const [fontWeight, instructions] of Object.entries(charDefinition)) {
905
- ctx.beginPath();
906
- ctx.lineWidth = devicePixelRatio * Number.parseInt(fontWeight);
907
- let actualInstructions: string;
908
- if (typeof instructions === 'function') {
909
- const xp = .15;
910
- const yp = .15 / deviceCellHeight * deviceCellWidth;
911
- actualInstructions = instructions(xp, yp);
912
- } else {
913
- actualInstructions = instructions;
914
- }
915
- for (const instruction of actualInstructions.split(' ')) {
916
- const type = instruction[0];
917
- const f = svgToCanvasInstructionMap[type];
918
- if (!f) {
919
- console.error(`Could not find drawing instructions for "${type}"`);
920
- continue;
921
- }
922
- const args: string[] = instruction.substring(1).split(',');
923
- if (!args[0] || !args[1]) {
924
- continue;
925
- }
926
- f(ctx, translateArgs(args, deviceCellWidth, deviceCellHeight, xOffset, yOffset, true, devicePixelRatio));
927
- }
928
- ctx.stroke();
929
- ctx.closePath();
930
- }
931
- }
932
-
933
- function drawPowerlineChar(
934
- ctx: CanvasRenderingContext2D,
935
- charDefinition: IVectorShape,
936
- xOffset: number,
937
- yOffset: number,
938
- deviceCellWidth: number,
939
- deviceCellHeight: number,
940
- fontSize: number,
941
- devicePixelRatio: number
942
- ): void {
943
- // Clip the cell to make sure drawing doesn't occur beyond bounds
944
- const clipRegion = new Path2D();
945
- clipRegion.rect(xOffset, yOffset, deviceCellWidth, deviceCellHeight);
946
- ctx.clip(clipRegion);
947
-
948
- ctx.beginPath();
949
- // Scale the stroke with DPR and font size
950
- const cssLineWidth = fontSize / 12;
951
- ctx.lineWidth = devicePixelRatio * cssLineWidth;
952
- for (const instruction of charDefinition.d.split(' ')) {
953
- const type = instruction[0];
954
- const f = svgToCanvasInstructionMap[type];
955
- if (!f) {
956
- console.error(`Could not find drawing instructions for "${type}"`);
957
- continue;
958
- }
959
- const args: string[] = instruction.substring(1).split(',');
960
- if (!args[0] || !args[1]) {
961
- continue;
962
- }
963
- f(ctx, translateArgs(
964
- args,
965
- deviceCellWidth,
966
- deviceCellHeight,
967
- xOffset,
968
- yOffset,
969
- false,
970
- devicePixelRatio,
971
- (charDefinition.leftPadding ?? 0) * (cssLineWidth / 2),
972
- (charDefinition.rightPadding ?? 0) * (cssLineWidth / 2)
973
- ));
974
- }
975
- if (charDefinition.type === VectorType.STROKE) {
976
- ctx.strokeStyle = ctx.fillStyle;
977
- ctx.stroke();
978
- } else {
979
- ctx.fill();
980
- }
981
- ctx.closePath();
982
- }
983
-
984
- function clamp(value: number, max: number, min: number = 0): number {
985
- return Math.max(Math.min(value, max), min);
986
- }
987
-
988
- const svgToCanvasInstructionMap: { [index: string]: any } = {
989
- 'C': (ctx: CanvasRenderingContext2D, args: number[]) => ctx.bezierCurveTo(args[0], args[1], args[2], args[3], args[4], args[5]),
990
- 'L': (ctx: CanvasRenderingContext2D, args: number[]) => ctx.lineTo(args[0], args[1]),
991
- 'M': (ctx: CanvasRenderingContext2D, args: number[]) => ctx.moveTo(args[0], args[1])
992
- };
993
-
994
- function translateArgs(args: string[], cellWidth: number, cellHeight: number, xOffset: number, yOffset: number, doClamp: boolean, devicePixelRatio: number, leftPadding: number = 0, rightPadding: number = 0): number[] {
995
- const result = args.map(e => parseFloat(e) || parseInt(e));
996
-
997
- if (result.length < 2) {
998
- throw new Error('Too few arguments for instruction');
999
- }
1000
-
1001
- for (let x = 0; x < result.length; x += 2) {
1002
- // Translate from 0-1 to 0-cellWidth
1003
- result[x] *= cellWidth - (leftPadding * devicePixelRatio) - (rightPadding * devicePixelRatio);
1004
- // Ensure coordinate doesn't escape cell bounds and round to the nearest 0.5 to ensure a crisp
1005
- // line at 100% devicePixelRatio
1006
- if (doClamp && result[x] !== 0) {
1007
- result[x] = clamp(Math.round(result[x] + 0.5) - 0.5, cellWidth, 0);
1008
- }
1009
- // Apply the cell's offset (ie. x*cellWidth)
1010
- result[x] += xOffset + (leftPadding * devicePixelRatio);
1011
- }
1012
-
1013
- for (let y = 1; y < result.length; y += 2) {
1014
- // Translate from 0-1 to 0-cellHeight
1015
- result[y] *= cellHeight;
1016
- // Ensure coordinate doesn't escape cell bounds and round to the nearest 0.5 to ensure a crisp
1017
- // line at 100% devicePixelRatio
1018
- if (doClamp && result[y] !== 0) {
1019
- result[y] = clamp(Math.round(result[y] + 0.5) - 0.5, cellHeight, 0);
1020
- }
1021
- // Apply the cell's offset (ie. x*cellHeight)
1022
- result[y] += yOffset;
1023
- }
1024
-
1025
- return result;
1026
- }