UNPKG

66.3 kBJavaScriptView Raw
1/**
2* Sutton SignWriting TrueType Font Module v1.3.2 (https://github.com/sutton-signwriting/font-ttf)
3* Author: Steve Slevinski (https://SteveSlevinski.me)
4* swu.mjs is released under the MIT License.
5*/
6
7/**
8* Sutton SignWriting Core Module v1.4.2 (https://github.com/sutton-signwriting/core)
9* Author: Steve Slevinski (https://SteveSlevinski.me)
10* convert.mjs is released under the MIT License.
11*/
12/**
13 * Function to convert a number to an SWU number character
14 * @function convert.num2swu
15 * @param {number} num - Integer value for number
16 * @returns {string} SWU number character
17 * @example
18 * convert.num2swu(500)
19 *
20 * return '𝤆'
21 */
22
23
24const num2swu = num => String.fromCodePoint(0x1D80C + parseInt(num) - 250);
25/**
26 * Function to convert an array of x,y integers to two SWU number characters
27 * @function convert.coord2swu
28 * @param {number[]} coord - Array of x,y integers
29 * @returns {string} Two SWU number character
30 * @example
31 * convert.coord2swu([500, 500])
32 *
33 * return '𝤆𝤆'
34 */
35
36
37const coord2swu = coord => coord.map(num => num2swu(num)).join('');
38/**
39 * Function to convert an SWU symbol character to a code point on plane 4
40 * @function convert.swu2code
41 * @param {string} swuSym - SWU symbol character
42 * @returns {number} Code point on plane 4
43 * @example
44 * convert.swu2code('񀀁')
45 *
46 * return 0x40001
47 */
48
49
50const swu2code$1 = swuSym => parseInt(swuSym.codePointAt(0));
51/**
52 * Function to convert an SWU symbol character to a 16-bit ID
53 * @function convert.swu2id
54 * @param {string} swuSym - SWU symbol character
55 * @returns {number} 16-bit ID
56 * @example
57 * convert.swu2id('񀀁')
58 *
59 * return 1
60 */
61
62
63const swu2id = swuSym => swu2code$1(swuSym) - 0x40000;
64/* support ongoing development on https://patreon.com/signwriting */
65
66let sizes = {};
67const zoom = 2;
68const bound = 76 * zoom;
69let context;
70/**
71 * Function that returns the size of a symbol using an id
72 * @function font.symbolSize
73 * @param {number} id - a 16-bit number of a symbol
74 * @example
75 * font.symbolSize(1)
76 *
77 * return [15,30]
78 */
79
80const symbolSize$1 = function (id) {
81 if (id in sizes) {
82 return [...sizes[id]];
83 }
84
85 if (!context) {
86 const canvaser = document.createElement("canvas");
87 canvaser.width = bound;
88 canvaser.height = bound;
89 context = canvaser.getContext("2d");
90 }
91
92 context.clearRect(0, 0, bound, bound);
93 context.font = 30 * zoom + "px 'SuttonSignWritingLine'";
94 context.fillText(String.fromCodePoint(id + 0xF0000), 0, 0);
95 const imgData = context.getImageData(0, 0, bound, bound).data;
96 let w, h, i, s;
97
98 wloop: for (w = bound - 1; w >= 0; w--) {
99 for (h = 0; h < bound; h += 1) {
100 for (s = 0; s < 4; s += 1) {
101 i = w * 4 + h * 4 * bound + s;
102
103 if (imgData[i]) {
104 break wloop;
105 }
106 }
107 }
108 }
109
110 var width = w;
111
112 hloop: for (h = bound - 1; h >= 0; h--) {
113 for (w = 0; w < width; w += 1) {
114 for (s = 0; s < 4; s += 1) {
115 i = w * 4 + h * 4 * bound + s;
116
117 if (imgData[i]) {
118 break hloop;
119 }
120 }
121 }
122 }
123
124 var height = h + 1;
125 width = Math.ceil(width / zoom);
126 height = Math.ceil(height / zoom); // Rounding error in chrome. Manual fixes.
127
128 if (14394 == id) {
129 width = 19;
130 }
131
132 if ([10468, 10480, 10496, 10512, 10500, 10532, 10548, 10862, 10878, 10894, 11058, 11074, 11476, 11488, 11492, 11504, 11508, 11520, 10516, 10910, 10926, 11042, 11082, 10942].includes(id)) {
133 width = 20;
134 }
135
136 if (31921 == id) {
137 width = 22;
138 }
139
140 if (38460 == id) {
141 width = 23;
142 }
143
144 if ([20164, 20212].includes(id)) {
145 width = 25;
146 }
147
148 if (31894 == id) {
149 width = 28;
150 }
151
152 if (46698 == id) {
153 width = 29;
154 }
155
156 if (29606 == id) {
157 width = 30;
158 }
159
160 if (44855 == id) {
161 width = 40;
162 }
163
164 if (32667 == id) {
165 width = 50;
166 }
167
168 if ([11088, 11474, 11490, 11506].includes(id)) {
169 height = 20;
170 }
171
172 if (6285 == id) {
173 height = 21;
174 }
175
176 if (40804 == id) {
177 height = 31;
178 }
179
180 if (41475 == id) {
181 height = 36;
182 } // Error in chrome. Manual fix.
183 // if (width==0 && height==0) {
184
185
186 if (width == 0 && height == 0) {
187 const sizefix = {
188 9: [15, 30],
189 10: [21, 30],
190 11: [30, 15],
191 12: [30, 21],
192 13: [15, 30],
193 14: [21, 30]
194 };
195
196 if (id in sizefix) {
197 width = sizefix[id][0];
198 height = sizefix[id][1];
199 }
200 }
201
202 if (width == 0 && height == 0) {
203 return undefined;
204 }
205
206 sizes[id] = [width, height];
207 return [width, height];
208};
209
210/**
211 * Function that returns the size of a symbol using an SWU symbol character
212 * @function swu.symbolSize
213 * @param {string} swu - an SWU symbol character
214 * @example
215 * swu.symbolSize("񀀁")
216 *
217 * return [15,30]
218 */
219
220const symbolSize = function (swu) {
221 return symbolSize$1(swu2id(swu));
222};
223
224/**
225 * Function that returns a plane 15 character for a symbol line using an id
226 * @function font.symbolLine
227 * @param {number} id - a 16-bit number of a symbol
228 * @example
229 * font.symbolLine(1)
230 *
231 * return '󰀁'
232 */
233const symbolLine$1 = function (id) {
234 return String.fromCodePoint(id + 0xF0000);
235};
236/**
237 * Function that returns a plane 16 character for a symbol fill using an id
238 * @function font.symbolFill
239 * @param {number} id - a 16-bit number of a symbol
240 * @example
241 * font.symbolFill(1)
242 *
243 * return '􀀁'
244 */
245
246
247const symbolFill$1 = function (id) {
248 return String.fromCodePoint(id + 0x100000);
249};
250/**
251 * Function that creates two text elements for a symbol using an id
252 * @function font.symbolText
253 * @param {number} id - a 16-bit number of a symbol
254 * @example
255 * font.symbolText(1)
256 *
257 * return ` <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀁</text>
258 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀁</text>`
259 */
260
261
262const symbolText$1 = function (id) {
263 return ` <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">${symbolFill$1(id)}</text>
264 <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">${symbolLine$1(id)}</text>`;
265};
266
267/**
268 * Function that returns a plane 15 character for a symbol line using an SWU symbol character
269 * @function swu.symbolLine
270 * @param {string} swu - an SWU symbol character
271 * @example
272 * swu.symbolLine('񀀁')
273 *
274 * return '󰀁'
275 */
276
277const symbolLine = function (swu) {
278 return symbolLine$1(swu2id(swu));
279};
280/**
281 * Function that returns a plane 165 character for a symbol fill using an SWU symbol character
282 * @function swu.symbolFill
283 * @param {string} swu - an SWU symbol character
284 * @example
285 * swu.symbolFill('񀀁')
286 *
287 * return '􀀁'
288 */
289
290
291const symbolFill = function (swu) {
292 return symbolFill$1(swu2id(swu));
293};
294/**
295 * Function that creates two text elements for a symbol using an SWU symbol character
296 * @function swu.symbolText
297 * @param {string} swu - an SWU symbol character
298 * @example
299 * swu.symbolText('񀀁')
300 *
301 * return ` <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀁</text>
302 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀁</text>`
303 */
304
305
306const symbolText = function (swu) {
307 return symbolText$1(swu2id(swu));
308};
309
310/**
311* Sutton SignWriting Core Module v1.4.2 (https://github.com/sutton-signwriting/core)
312* Author: Steve Slevinski (https://SteveSlevinski.me)
313* style.mjs is released under the MIT License.
314*/
315
316/**
317 * Object of regular expressions for style strings
318 *
319 * @alias style.re
320 * @type {object}
321 * @property {string} colorize - regular expression for colorize section
322 * @property {string} colorhex - regular expression for color hex values with 3 or 6 characters
323 * @property {string} colorname - regular expression for css color name
324 * @property {string} padding - regular expression for padding section
325 * @property {string} zoom - regular expression for zoom section
326 * @property {string} classbase - regular expression for class name definition
327 * @property {string} id - regular expression for id definition
328 * @property {string} colorbase - regular expression for color hex or color name
329 * @property {string} color - regular expression for single color entry
330 * @property {string} colors - regular expression for double color entry
331 * @property {string} background - regular expression for background section
332 * @property {string} detail - regular expression for color details for line and optional fill
333 * @property {string} detailsym - regular expression for color details for individual symbols
334 * @property {string} classes - regular expression for one or more class names
335 * @property {string} full - full regular expression for style string
336 */
337let re$2 = {
338 'colorize': 'C',
339 'colorhex': '(?:[0-9a-fA-F]{3}){1,2}',
340 'colorname': '[a-zA-Z]+',
341 'padding': 'P[0-9]{2}',
342 'zoom': 'Z(?:[0-9]+(?:\\.[0-9]+)?|x)',
343 'classbase': '-?[_a-zA-Z][_a-zA-Z0-9-]{0,100}',
344 'id': '[a-zA-Z][_a-zA-Z0-9-]{0,100}'
345};
346re$2.colorbase = `(?:${re$2.colorhex}|${re$2.colorname})`;
347re$2.color = `_${re$2.colorbase}_`;
348re$2.colors = `_${re$2.colorbase}(?:,${re$2.colorbase})?_`;
349re$2.background = `G${re$2.color}`;
350re$2.detail = `D${re$2.colors}`;
351re$2.detailsym = `D[0-9]{2}${re$2.colors}`;
352re$2.classes = `${re$2.classbase}(?: ${re$2.classbase})*`;
353re$2.full = `-(${re$2.colorize})?(${re$2.padding})?(${re$2.background})?(${re$2.detail})?(${re$2.zoom})?(?:-((?:${re$2.detailsym})*))?(?:-(${re$2.classes})?!(?:(${re$2.id})!)?)?`;
354
355const prefixColor$1 = color => {
356 const regex = new RegExp(`^${re$2.colorhex}$`);
357 return (regex.test(color) ? '#' : '') + color;
358};
359
360const definedProps$1 = obj => Object.fromEntries(Object.entries(obj).filter(([k, v]) => v !== undefined));
361/**
362 * Function to parse style string to object
363 * @function style.parse
364 * @param {string} styleString - a style string
365 * @returns {StyleObject} elements of style string
366 * @example
367 * style.parse('-CP10G_blue_D_red,Cyan_')
368 *
369 * return {
370 * 'colorize': true,
371 * 'padding': 10,
372 * 'background': 'blue',
373 * 'detail': ['red', 'Cyan']
374 * }
375 */
376
377
378const parse$2 = styleString => {
379 const regex = `^${re$2.full}`;
380 const m = (typeof styleString === 'string' ? styleString.match(new RegExp(regex)) : []) || [];
381 return definedProps$1({
382 'colorize': !m[1] ? undefined : !!m[1],
383 'padding': !m[2] ? undefined : parseInt(m[2].slice(1)),
384 'background': !m[3] ? undefined : prefixColor$1(m[3].slice(2, -1)),
385 'detail': !m[4] ? undefined : m[4].slice(2, -1).split(',').map(prefixColor$1),
386 'zoom': !m[5] ? undefined : m[5] === 'Zx' ? 'x' : parseFloat(m[5].slice(1)),
387 'detailsym': !m[6] ? undefined : m[6].match(new RegExp(re$2.detailsym, 'g')).map(val => {
388 const parts = val.split('_');
389 const detail = parts[1].split(',').map(prefixColor$1);
390 return {
391 'index': parseInt(parts[0].slice(1)),
392 'detail': detail
393 };
394 }),
395 'classes': !m[7] ? undefined : m[7],
396 'id': !m[8] ? undefined : m[8]
397 });
398};
399/**
400 * Function to compose style string from object
401 * @function style.compose
402 * @param {StyleObject} styleObject - an object of style options
403 * @returns {string} style string
404 * @example
405 * style.compose({
406 * 'colorize': true,
407 * 'padding': 10,
408 * 'background': 'blue',
409 * 'detail': ['red', 'Cyan'],
410 * 'zoom': 1.1,
411 * 'detailsym': [
412 * {
413 * 'index': 1,
414 * 'detail': ['#ff00ff']
415 * },
416 * {
417 * 'index': 2,
418 * 'detail': ['yellow', 'green']
419 * }
420 * ],
421 * 'classes': 'primary blinking',
422 * 'id': 'cursor'
423 * })
424 *
425 * return '-CP10G_blue_D_red,Cyan_Z1.1-D01_ff00ff_D02_yellow,green_-primary blinking!cursor!'
426 */
427
428
429const compose = styleObject => {
430 if (typeof styleObject !== 'object' || styleObject === null) return undefined; // three sections
431
432 let style1 = '-';
433 style1 += !styleObject.colorize ? '' : 'C';
434 const padding = parseInt(styleObject.padding);
435 style1 += !padding || padding <= 0 || padding > 99 ? '' : 'P' + (padding > 9 ? padding : '0' + padding);
436 const background = !styleObject.background || !(typeof styleObject.background === 'string') ? undefined : styleObject.background.match(re$2.colorbase)[0];
437 style1 += !background ? '' : 'G_' + background + '_';
438 const detail1 = !styleObject.detail || !styleObject.detail[0] || !(typeof styleObject.detail[0] === 'string') ? undefined : styleObject.detail[0].match(re$2.colorbase)[0];
439 const detail2 = !styleObject.detail || !styleObject.detail[1] || !(typeof styleObject.detail[1] === 'string') ? undefined : styleObject.detail[1].match(re$2.colorbase)[0];
440
441 if (detail1) {
442 style1 += 'D_' + detail1;
443
444 if (detail2) {
445 style1 += ',' + detail2;
446 }
447
448 style1 += '_';
449 }
450
451 const zoom = styleObject.zoom === 'x' ? 'x' : parseFloat(styleObject.zoom);
452 style1 += !zoom || zoom <= 0 ? '' : 'Z' + zoom;
453 let style2 = '';
454 const detailsym = !styleObject.detailsym || !Array.isArray(styleObject.detailsym) ? [] : styleObject.detailsym.map(styleObject => {
455 const index = parseInt(styleObject.index);
456 if (!index || index <= 0 || index > 99) return '';
457 let style = 'D' + (index > 9 ? index : '0' + index);
458 const detail1 = !styleObject.detail || !styleObject.detail[0] ? undefined : styleObject.detail[0].match(re$2.colorbase)[0];
459 const detail2 = !styleObject.detail || !styleObject.detail[1] ? undefined : styleObject.detail[1].match(re$2.colorbase)[0];
460
461 if (detail1) {
462 style += '_' + detail1;
463
464 if (detail2) {
465 style += ',' + detail2;
466 }
467
468 style += '_';
469 }
470
471 return style;
472 });
473 style2 += detailsym.join('');
474 let style3 = '';
475 const classes = !styleObject.classes || !(typeof styleObject.classes === 'string') ? undefined : styleObject.classes.match(re$2.classes)[0];
476 style3 += !classes ? '' : classes;
477 const id = !styleObject.id || !(typeof styleObject.id === 'string') ? undefined : styleObject.id.match(re$2.id)[0];
478 style3 += classes || id ? '!' : '';
479 style3 += !id ? '' : id + '!';
480 return style1 + (style2 || style3 ? '-' + style2 : '') + (style3 ? '-' + style3 : '');
481};
482/* support ongoing development on https://patreon.com/signwriting */
483
484/**
485* Sutton SignWriting Core Module v1.4.2 (https://github.com/sutton-signwriting/core)
486* Author: Steve Slevinski (https://SteveSlevinski.me)
487* swu.mjs is released under the MIT License.
488*/
489
490/**
491 * Object of regular expressions for SWU strings in UTF-16
492 *
493 * @alias swu.re
494 * @property {string} symbol - regular expressions for a symbol
495 * @property {string} coord - regular expressions for a coordinate
496 * @property {string} sort - regular expressions for the sorting marker
497 * @property {string} box - regular expression for a signbox marker
498 * @property {string} prefix - regular expression for a sorting marker followed by one or more symbols
499 * @property {string} spatial - regular expression for a symbol followed by a coordinate
500 * @property {string} signbox - regular expression for a signbox marker, max coordinate and zero or more spatial symbols
501 * @property {string} sign - regular expression for an optional prefix followed by a signbox
502 * @property {string} sortable - regular expression for a mandatory prefix followed by a signbox
503 */
504let re$1 = {
505 'symbol': '(?:(?:\uD8C0[\uDC01-\uDFFF])|(?:[\uD8C1-\uD8FC][\uDC00-\uDFFF])|(?:\uD8FD[\uDC00-\uDC80]))',
506 'coord': '(?:\uD836[\uDC0C-\uDDFF]){2}',
507 'sort': '\uD836\uDC00',
508 'box': '\uD836[\uDC01-\uDC04]'
509};
510re$1.prefix = `(?:${re$1.sort}(?:${re$1.symbol})+)`;
511re$1.spatial = `${re$1.symbol}${re$1.coord}`;
512re$1.signbox = `${re$1.box}${re$1.coord}(?:${re$1.spatial})*`;
513re$1.sign = `${re$1.prefix}?${re$1.signbox}`;
514re$1.sortable = `${re$1.prefix}${re$1.signbox}`;
515/**
516 * Object of regular expressions for style strings
517 *
518 * @alias style.re
519 * @type {object}
520 * @property {string} colorize - regular expression for colorize section
521 * @property {string} colorhex - regular expression for color hex values with 3 or 6 characters
522 * @property {string} colorname - regular expression for css color name
523 * @property {string} padding - regular expression for padding section
524 * @property {string} zoom - regular expression for zoom section
525 * @property {string} classbase - regular expression for class name definition
526 * @property {string} id - regular expression for id definition
527 * @property {string} colorbase - regular expression for color hex or color name
528 * @property {string} color - regular expression for single color entry
529 * @property {string} colors - regular expression for double color entry
530 * @property {string} background - regular expression for background section
531 * @property {string} detail - regular expression for color details for line and optional fill
532 * @property {string} detailsym - regular expression for color details for individual symbols
533 * @property {string} classes - regular expression for one or more class names
534 * @property {string} full - full regular expression for style string
535 */
536
537let re = {
538 'colorize': 'C',
539 'colorhex': '(?:[0-9a-fA-F]{3}){1,2}',
540 'colorname': '[a-zA-Z]+',
541 'padding': 'P[0-9]{2}',
542 'zoom': 'Z(?:[0-9]+(?:\\.[0-9]+)?|x)',
543 'classbase': '-?[_a-zA-Z][_a-zA-Z0-9-]{0,100}',
544 'id': '[a-zA-Z][_a-zA-Z0-9-]{0,100}'
545};
546re.colorbase = `(?:${re.colorhex}|${re.colorname})`;
547re.color = `_${re.colorbase}_`;
548re.colors = `_${re.colorbase}(?:,${re.colorbase})?_`;
549re.background = `G${re.color}`;
550re.detail = `D${re.colors}`;
551re.detailsym = `D[0-9]{2}${re.colors}`;
552re.classes = `${re.classbase}(?: ${re.classbase})*`;
553re.full = `-(${re.colorize})?(${re.padding})?(${re.background})?(${re.detail})?(${re.zoom})?(?:-((?:${re.detailsym})*))?(?:-(${re.classes})?!(?:(${re.id})!)?)?`;
554
555const prefixColor = color => {
556 const regex = new RegExp(`^${re.colorhex}$`);
557 return (regex.test(color) ? '#' : '') + color;
558};
559
560const definedProps = obj => Object.fromEntries(Object.entries(obj).filter(([k, v]) => v !== undefined));
561/**
562 * Function to parse style string to object
563 * @function style.parse
564 * @param {string} styleString - a style string
565 * @returns {StyleObject} elements of style string
566 * @example
567 * style.parse('-CP10G_blue_D_red,Cyan_')
568 *
569 * return {
570 * 'colorize': true,
571 * 'padding': 10,
572 * 'background': 'blue',
573 * 'detail': ['red', 'Cyan']
574 * }
575 */
576
577
578const parse$1 = styleString => {
579 const regex = `^${re.full}`;
580 const m = (typeof styleString === 'string' ? styleString.match(new RegExp(regex)) : []) || [];
581 return definedProps({
582 'colorize': !m[1] ? undefined : !!m[1],
583 'padding': !m[2] ? undefined : parseInt(m[2].slice(1)),
584 'background': !m[3] ? undefined : prefixColor(m[3].slice(2, -1)),
585 'detail': !m[4] ? undefined : m[4].slice(2, -1).split(',').map(prefixColor),
586 'zoom': !m[5] ? undefined : m[5] === 'Zx' ? 'x' : parseFloat(m[5].slice(1)),
587 'detailsym': !m[6] ? undefined : m[6].match(new RegExp(re.detailsym, 'g')).map(val => {
588 const parts = val.split('_');
589 const detail = parts[1].split(',').map(prefixColor);
590 return {
591 'index': parseInt(parts[0].slice(1)),
592 'detail': detail
593 };
594 }),
595 'classes': !m[7] ? undefined : m[7],
596 'id': !m[8] ? undefined : m[8]
597 });
598};
599/** The convert module contains functions to convert between Formal SignWriitng in ASCII (FSW) and SignWriting in Unicode (SWU) characters, along with other types of data.
600 * [Characters set definitions](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-09.html#name-characters)
601 * @module convert
602 */
603
604/**
605 * Function to convert an SWU number character to an integer
606 * @function convert.swu2num
607 * @param {string} swuNum - SWU number character
608 * @returns {number} Integer value for number
609 * @example
610 * convert.swu2num('𝤆')
611 *
612 * return 500
613 */
614
615
616const swu2num = swuNum => parseInt(swuNum.codePointAt(0)) - 0x1D80C + 250;
617/**
618 * Function to convert two SWU number characters to an array of x,y integers
619 * @function convert.swu2coord
620 * @param {string} swuCoord - Two SWU number character
621 * @returns {number[]} Array of x,y integers
622 * @example
623 * convert.swu2coord('𝤆𝤆')
624 *
625 * return [500, 500]
626 */
627
628
629const swu2coord = swuCoord => [swu2num(swuCoord.slice(0, 2)), swu2num(swuCoord.slice(2, 4))];
630/**
631 * Function to convert an SWU symbol character to a code point on plane 4
632 * @function convert.swu2code
633 * @param {string} swuSym - SWU symbol character
634 * @returns {number} Code point on plane 4
635 * @example
636 * convert.swu2code('񀀁')
637 *
638 * return 0x40001
639 */
640
641
642const swu2code = swuSym => parseInt(swuSym.codePointAt(0));
643
644const parse = {
645 /**
646 * Function to parse an swu symbol with optional coordinate and style string
647 * @function swu.parse.symbol
648 * @param {string} swuSym - an swu symbol
649 * @returns {object} elements of swu symbol
650 * @example
651 * swu.parse.symbol('񀀁𝤆𝤆-C')
652 *
653 * return {
654 * 'symbol': '񀀁',
655 * 'coord': [500, 500],
656 * 'style': '-C'
657 * }
658 */
659 symbol: swuSym => {
660 const regex = `^(${re$1.symbol})(${re$1.coord})?(${re.full})?`;
661 const symbol = typeof swuSym === 'string' ? swuSym.match(new RegExp(regex)) : undefined;
662 return {
663 'symbol': symbol ? symbol[1] : undefined,
664 'coord': symbol && symbol[2] ? swu2coord(symbol[2]) : undefined,
665 'style': symbol ? symbol[3] : undefined
666 };
667 },
668
669 /**
670 * Function to parse an swu sign with style string
671 * @function swu.parse.sign
672 * @param {string} swuSign - an swu sign
673 * @returns {object} elements of swu sign
674 * @example
675 * swu.parse.sign('𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭-C')
676 *
677 * return {
678 * sequence: ['񀀒','񀀚','񋚥','񋛩'],
679 * box: '𝠃',
680 * max: [525, 535],
681 * spatials: [
682 * {
683 * symbol: '񋛩',
684 * coord: [483, 510]
685 * },
686 * {
687 * symbol: '񀀒',
688 * coord: [501, 466]
689 * },
690 * {
691 * symbol: '񋚥',
692 * coord: [510, 500]
693 * },
694 * {
695 * symbol: '񀀚',
696 * coord: [476, 475]
697 * }
698 * ],
699 * style: '-C'
700 * }
701 */
702 sign: swuSign => {
703 const regex = `^(${re$1.prefix})?(${re$1.signbox})(${re.full})?`;
704 const sign = typeof swuSign === 'string' ? swuSign.match(new RegExp(regex)) : undefined;
705
706 if (sign) {
707 return {
708 'sequence': sign[1] ? sign[1].slice(2).match(/.{2}/g) : undefined,
709 'box': sign[2].slice(0, 2),
710 'max': swu2coord(sign[2].slice(2, 6)),
711 'spatials': sign[2].length < 7 ? undefined : sign[2].slice(6).match(/(.{6})/g).map(m => {
712 return {
713 symbol: m.slice(0, 2),
714 coord: swu2coord(m.slice(2))
715 };
716 }),
717 'style': sign[3]
718 };
719 } else {
720 return {};
721 }
722 },
723
724 /**
725 * Function to parse an swu text
726 * @function swu.parse.text
727 * @param {string} swuText - an swu text
728 * @returns {array} swu signs and punctuations
729 * @example
730 * swu.parse.text('𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻 𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦 񏌁𝣢𝤂')
731 *
732 * return [
733 * '𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻',
734 * '𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦',
735 * '񏌁𝣢𝤂'
736 * ]
737 */
738 text: swuText => {
739 if (typeof swuText !== 'string') return [];
740 const regex = `(${re$1.sign}(${re.full})?|${re$1.spatial}(${re.full})?)`;
741 const matches = swuText.match(new RegExp(regex, 'g'));
742 return matches ? [...matches] : [];
743 }
744};
745/**
746 * Function to gather sizing information about an swu sign or symbol
747 * @function swu.info
748 * @param {string} swu - an swu sign or symbol
749 * @returns {object} information about the swu string
750 * @example
751 * swu.info('𝠀񁲡񈩧𝠂𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻-P10Z2')
752 *
753 * return {
754 * minX: 481,
755 * minY: 471,
756 * width: 37,
757 * height: 58,
758 * segment: 'sign',
759 * lane: -1
760 * padding: 10,
761 * zoom: 2
762 * }
763 */
764
765const info = swu => {
766 let lanes = {
767 '𝠁': 0,
768 '𝠂': -1,
769 '𝠃': 0,
770 '𝠄': 1
771 };
772 let parsed = parse.sign(swu);
773 let width, height, segment, x1, x2, y1, y2, lane;
774
775 if (parsed.spatials) {
776 x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
777 x2 = parsed.max[0];
778 width = x2 - x1;
779 y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
780 y2 = parsed.max[1];
781 height = y2 - y1;
782 segment = 'sign';
783 lane = parsed.box;
784 } else {
785 parsed = parse.symbol(swu);
786 lane = "𝠃";
787
788 if (parsed.coord) {
789 x1 = parsed.coord[0];
790 width = (500 - x1) * 2;
791 y1 = parsed.coord[1];
792 height = (500 - y1) * 2;
793 segment = 'symbol';
794 } else {
795 x1 = 490;
796 width = 20;
797 y1 = 490;
798 height = 20;
799 segment = 'none';
800 }
801 }
802
803 let style = parse$1(parsed.style);
804 let zoom = style.zoom || 1;
805 let padding = style.padding || 0;
806 return {
807 minX: x1,
808 minY: y1,
809 width: width,
810 height: height,
811 segment: segment,
812 lane: lanes[lane],
813 padding: padding,
814 zoom: zoom
815 };
816};
817
818const columnDefaults = {
819 'height': 500,
820 'width': 150,
821 'offset': 50,
822 'pad': 20,
823 'margin': 5,
824 'dynamic': false,
825 'background': undefined,
826 'punctuation': {
827 'spacing': true,
828 'pad': 30,
829 'pull': true
830 },
831 'style': {
832 'detail': ['black', 'white'],
833 'zoom': 1
834 }
835};
836/**
837 * Function to an object of column options with default values
838 *
839 * @function swu.columnDefaultsMerge
840 * @param {ColumnOptions} options - object of column options
841 * @returns {ColumnOptions} object of column options merged with column defaults
842 * @example
843 * swu.columnDefaultsMerge({height: 500,width:150})
844 *
845 * return {
846 * "height": 500,
847 * "width": 150,
848 * "offset": 50,
849 * "pad": 20,
850 * "margin": 5,
851 * "dynamic": false,
852 * "punctuation": {
853 * "spacing": true,
854 * "pad": 30,
855 * "pull": true
856 * },
857 * "style": {
858 * "detail": [
859 * "black",
860 * "white"
861 * ],
862 * "zoom": 1
863 * }
864 * }
865 */
866
867const columnDefaultsMerge = options => {
868 if (typeof options !== 'object') options = {};
869 return { ...columnDefaults,
870 ...options,
871 punctuation: { ...columnDefaults.punctuation,
872 ...options.punctuation
873 },
874 style: { ...columnDefaults.style,
875 ...options.style
876 }
877 };
878};
879/**
880 * Function to transform an SWU text to an array of columns
881 *
882 * @function swu.columns
883 * @param {string} swuText - SWU text of signs and punctuation
884 * @param {ColumnOptions} options - object of column options
885 * @returns {{options:ColumnOptions,widths:number[],columns:ColumnData}} object of column options, widths array, and column data
886 * @example
887 * swu.columns('𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻 𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦 񏌁𝣢𝤂', {height: 500,width:150})
888 *
889 * return {
890 * "options": {
891 * "height": 500,
892 * "width": 150,
893 * "offset": 50,
894 * "pad": 20,
895 * "margin": 5,
896 * "dynamic": false,
897 * "punctuation": {
898 * "spacing": true,
899 * "pad": 30,
900 * "pull": true
901 * },
902 * "style": {
903 * "detail": [
904 * "black",
905 * "white"
906 * ],
907 * "zoom": 1
908 * }
909 * },
910 * "widths": [
911 * 150
912 * ],
913 * "columns": [
914 * [
915 * {
916 * "x": 56,
917 * "y": 20,
918 * "minX": 481,
919 * "minY": 471,
920 * "width": 37,
921 * "height": 58,
922 * "lane": 0,
923 * "padding": 0,
924 * "segment": "sign",
925 * "text": "𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻",
926 * "zoom": 1
927 * },
928 * {
929 * "x": 57,
930 * "y": 118,
931 * "minX": 482,
932 * "minY": 468,
933 * "width": 36,
934 * "height": 65,
935 * "lane": 0,
936 * "padding": 0,
937 * "segment": "sign",
938 * "text": "𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦",
939 * "zoom": 1
940 * },
941 * {
942 * "x": 39,
943 * "y": 203,
944 * "minX": 464,
945 * "minY": 496,
946 * "width": 72,
947 * "height": 8,
948 * "lane": 0,
949 * "padding": 0,
950 * "segment": "symbol",
951 * "text": "񏌁𝣢𝤂",
952 * "zoom": 1
953 * }
954 * ]
955 * ]
956 * }
957 */
958
959
960const columns = (swuText, options) => {
961 if (typeof swuText !== 'string') return {};
962 const values = columnDefaultsMerge(options);
963 let input = parse.text(swuText);
964 let cursor = 0;
965 let cols = [];
966 let col = [];
967 let plus = 0;
968 let center = parseInt(values.width / 2);
969 let maxHeight = values.height - values.margin;
970 let pullable = true;
971 let finalize = false;
972
973 for (let val of input) {
974 let informed = info(val);
975 cursor += plus;
976
977 if (values.punctuation.spacing) {
978 cursor += informed.segment == 'sign' ? values.pad : 0;
979 } else {
980 cursor += values.pad;
981 }
982
983 finalize = cursor + informed.height > maxHeight;
984
985 if (finalize && informed.segment == 'symbol' && values.punctuation.pull && pullable) {
986 finalize = false;
987 pullable = false;
988 }
989
990 if (col.length == 0) {
991 finalize = false;
992 }
993
994 if (finalize) {
995 cursor = values.pad;
996 cols.push(col);
997 col = [];
998 pullable = true;
999 }
1000
1001 col.push(Object.assign(informed, {
1002 x: center + values.offset * informed.lane - (500 - informed.minX) * informed.zoom * values.style.zoom,
1003 y: cursor,
1004 text: val
1005 }));
1006 cursor += informed.height * informed.zoom * values.style.zoom;
1007
1008 if (values.punctuation.spacing) {
1009 plus = informed.segment == 'sign' ? values.pad : values.punctuation.pad;
1010 } else {
1011 plus = values.pad;
1012 }
1013 }
1014
1015 if (col.length) {
1016 cols.push(col);
1017 } // over height issue when pulling punctuation
1018
1019
1020 if (values.punctuation.pull) {
1021 for (let col of cols) {
1022 let last = col[col.length - 1];
1023 let diff = last.y + last.height - (values.height - values.margin);
1024
1025 if (diff > 0) {
1026 let adj = parseInt(diff / col.length) + 1;
1027
1028 for (let i in col) {
1029 col[i].y -= adj * i + adj;
1030 }
1031 }
1032 }
1033 } // contract, expand, adjust
1034
1035
1036 let widths = [];
1037
1038 for (let col of cols) {
1039 let min = [center - values.offset - values.pad];
1040 let max = [center + values.offset + values.pad];
1041
1042 for (let item of col) {
1043 min.push(item.x - values.pad);
1044 max.push(item.x + item.width + values.pad);
1045 }
1046
1047 min = Math.min(...min);
1048 max = Math.max(...max);
1049 let width = values.width;
1050 let adj = 0;
1051
1052 if (!values.dynamic) {
1053 adj = center - parseInt((min + max) / 2);
1054 } else {
1055 width = max - min;
1056 adj = -min;
1057 }
1058
1059 for (let item of col) {
1060 item.x += adj;
1061 }
1062
1063 widths.push(width);
1064 }
1065
1066 return {
1067 'options': values,
1068 'widths': widths,
1069 'columns': cols
1070 };
1071};
1072/**
1073 * Array of plane 4 code points for categories of symbols: hand, movement, dynamics, head, trunk & limb, location, and punctuation.
1074 * @alias swu.category
1075 * @type {array}
1076 */
1077
1078const category = [0x40001, 0x461e1, 0x4bca1, 0x4bfa1, 0x4e8e1, 0x4efa1, 0x4f2a1];
1079/**
1080 * Object of symbol ranges with starting and ending code points on plane 4.
1081 *
1082 * { all, writing, hand, movement, dynamic, head, hcenter, vcenter, trunk, limb, location, punctuation }
1083 * @alias swu.ranges
1084 * @type {object}
1085 */
1086
1087const ranges = {
1088 'all': [0x40001, 0x4f480],
1089 'writing': [0x40001, 0x4efa0],
1090 'hand': [0x40001, 0x461e0],
1091 'movement': [0x461e1, 0x4bca0],
1092 'dynamic': [0x4bca1, 0x4bfa0],
1093 'head': [0x4bfa1, 0x4e8e0],
1094 'hcenter': [0x4bfa1, 0x4e8e0],
1095 'vcenter': [0x4bfa1, 0x4ec40],
1096 'trunk': [0x4e8e1, 0x4ec40],
1097 'limb': [0x4ec41, 0x4efa0],
1098 'location': [0x4efa1, 0x4f2a0],
1099 'punctuation': [0x4f2a1, 0x4f480]
1100};
1101/**
1102 * Array of colors associated with the seven symbol categories.
1103 * @alias swu.colors
1104 * @type {array}
1105 */
1106
1107
1108const colors = ['#0000CC', '#CC0000', '#FF0099', '#006600', '#000000', '#884411', '#FF9900'];
1109/**
1110 * Function that returns the standardized color for a symbol.
1111 * @function swu.colorize
1112 * @param {string} swuSym - an SWU symbol character
1113 * @returns {string} name of standardized color for symbol
1114 * @example
1115 * swu.colorize('񀀁')
1116 *
1117 * return '#0000CC'
1118 */
1119
1120const colorize = swuSym => {
1121 const parsed = parse.symbol(swuSym);
1122 let color = '#000000';
1123
1124 if (parsed.symbol) {
1125 const code = swu2code(parsed.symbol);
1126 const index = category.findIndex(val => val > code);
1127 color = colors[index < 0 ? 6 : index - 1];
1128 }
1129
1130 return color;
1131};
1132/* support ongoing development on https://patreon.com/signwriting */
1133
1134/**
1135 * Function that creates an SVG image from an SWU symbol key with an optional style string
1136 * @function swu.symbolSvgBody
1137 * @param {string} swuSym - an SWU symbol key with optional style string
1138 * @example
1139 * swu.symbolSvgBody('S10000')
1140 *
1141 * return `<text font-size="0">S10000</text>
1142 * <g transform="translate(500,500)">
1143 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀁</text>
1144 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀁</text>
1145 * </g>`
1146 */
1147
1148const symbolSvgBody = swuSym => {
1149 const parsed = parse.symbol(swuSym);
1150 const blank = '';
1151 if (!parsed.symbol) return blank;
1152 let styling = parse$2(parsed.style);
1153 let x1, y1, x2, y2;
1154
1155 if (parsed.coord) {
1156 x1 = parsed.coord[0];
1157 y1 = parsed.coord[1];
1158 x2 = 500 + (500 - x1);
1159 y2 = 500 + (500 - y1);
1160 } else {
1161 let size = symbolSize(parsed.symbol);
1162 if (!size) return blank;
1163 x1 = 500 - parseInt((size[0] + 1) / 2);
1164 y1 = 500 - parseInt((size[1] + 1) / 2);
1165 x2 = 500 + (500 - x1);
1166 y2 = 500 + (500 - y1);
1167 }
1168
1169 let symSvg = symbolText(parsed.symbol);
1170 symSvg = ` <g transform="translate(${x1},${y1})">
1171${symSvg}
1172 </g>`;
1173 let line;
1174
1175 if (styling.colorize) {
1176 line = colorize(parsed.symbol);
1177 } else if (styling.detail) {
1178 line = styling.detail[0];
1179 }
1180
1181 if (line) {
1182 symSvg = symSvg.replace(/class="sym-line" fill="black"/, `class="sym-line" fill="${line}"`);
1183 }
1184
1185 let fill = styling.detail && styling.detail[1];
1186
1187 if (fill) {
1188 symSvg = symSvg.replace(/class="sym-fill" fill="white"/, `class="sym-fill" fill="${fill}"`);
1189 }
1190
1191 let background = '';
1192
1193 if (styling.padding) {
1194 x1 -= styling.padding;
1195 y1 -= styling.padding;
1196 x2 += styling.padding;
1197 y2 += styling.padding;
1198 }
1199
1200 if (styling.background) {
1201 background = `\n <rect x="${x1}" y="${y1}" width="${x2 - x1}" height="${y2 - y1}" style="fill:${styling.background};" />`;
1202 }
1203
1204 return ` <text font-size="0">${swuSym}</text>${background}
1205${symSvg}`;
1206};
1207/**
1208 * Function that creates an SVG image from an SWU symbol key with an optional style string
1209 * @function swu.symbolSvg
1210 * @param {string} swuSym - an SWU symbol key with optional style string
1211 * @example
1212 * swu.symbolSvg('S10000')
1213 *
1214 * return `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="15" height="30" viewBox="500 500 15 30">
1215 * <text font-size="0">S10000</text>
1216 * <g transform="translate(500,500)">
1217 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀁</text>
1218 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀁</text>
1219 * </g>
1220 * </svg>`
1221 */
1222
1223
1224const symbolSvg = swuSym => {
1225 const parsed = parse.symbol(swuSym);
1226 const blank = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="1" height="1"></svg>';
1227 if (!parsed.symbol) return blank;
1228 let styling = parse$2(parsed.style);
1229 let x1, y1, x2, y2;
1230
1231 if (parsed.coord) {
1232 x1 = parsed.coord[0];
1233 y1 = parsed.coord[1];
1234 x2 = 500 + (500 - x1);
1235 y2 = 500 + (500 - y1);
1236 } else {
1237 let size = symbolSize(parsed.symbol);
1238 if (!size) return blank;
1239 x1 = parseInt(500 - size[0] / 2);
1240 y1 = parseInt(500 - size[1] / 2);
1241 x2 = x1 + size[0];
1242 y2 = y1 + size[1];
1243 }
1244
1245 let classes = '';
1246
1247 if (styling.classes) {
1248 classes = ` class="${styling.classes}"`;
1249 }
1250
1251 let id = '';
1252
1253 if (styling.id) {
1254 id = ` id="${styling.id}"`;
1255 }
1256
1257 if (styling.padding) {
1258 x1 -= styling.padding;
1259 y1 -= styling.padding;
1260 x2 += styling.padding;
1261 y2 += styling.padding;
1262 }
1263
1264 let sizing = '';
1265
1266 if (styling.zoom != 'x') {
1267 sizing = ` width="${(x2 - x1) * (styling.zoom ? styling.zoom : 1)}" height="${(y2 - y1) * (styling.zoom ? styling.zoom : 1)}"`;
1268 }
1269
1270 return `<svg${classes}${id} version="1.1" xmlns="http://www.w3.org/2000/svg"${sizing} viewBox="${x1} ${y1} ${x2 - x1} ${y2 - y1}">
1271${symbolSvgBody(swuSym)}
1272</svg>`;
1273};
1274
1275const symbolCanvas = function (swuSym) {
1276 const parsed = parse.symbol(swuSym);
1277
1278 if (parsed.symbol) {
1279 let size = symbolSize(parsed.symbol);
1280
1281 if (size) {
1282 const canvas = document.createElement('canvas');
1283 const context = canvas.getContext('2d');
1284 let styling = parse$2(parsed.style);
1285 let line = 'black';
1286
1287 if (styling.colorize) {
1288 line = colorize(parsed.symbol);
1289 } else if (styling.detail) {
1290 line = styling.detail[0];
1291 }
1292
1293 let fill = styling.detail && styling.detail[1] || 'white';
1294 let x1 = 500;
1295 let x2 = x1 + size[0];
1296 let y1 = 500;
1297 let y2 = y1 + size[1];
1298
1299 if (styling.padding) {
1300 x1 -= styling.padding;
1301 y1 -= styling.padding;
1302 x2 += styling.padding;
1303 y2 += styling.padding;
1304 }
1305
1306 let sizing = 1;
1307
1308 if (styling.zoom != 'x') {
1309 sizing = styling.zoom;
1310 }
1311
1312 let w = (x2 - x1) * sizing;
1313 let h = (y2 - y1) * sizing;
1314 canvas.width = w ? w : 1;
1315 canvas.height = h ? h : 1;
1316
1317 if (styling.background) {
1318 context.rect(0, 0, w, h);
1319 context.fillStyle = styling.background;
1320 context.fill();
1321 }
1322
1323 context.font = 30 * sizing + "px 'SuttonSignWritingFill'";
1324 context.fillStyle = fill;
1325 context.fillText(symbolFill(parsed.symbol), (500 - x1) * sizing, (500 - y1) * sizing);
1326 context.font = 30 * sizing + "px 'SuttonSignWritingLine'";
1327 context.fillStyle = line;
1328 context.fillText(symbolLine(parsed.symbol), (500 - x1) * sizing, (500 - y1) * sizing);
1329 return canvas;
1330 }
1331 }
1332};
1333/**
1334 * Function that creates a PNG data url from an SWU symbol character with an optional style string
1335 * @function swu.symbolPng
1336 * @param {string} swuSym - an SWU symbol character with optional style string
1337 * @example
1338 * swu.symbolPng('񀀁-CP10G_green_Z2')
1339 *
1340 * return 'data:image/png;base64,iVBORw...'
1341 */
1342
1343
1344const symbolPng = swuSym => {
1345 const canvas = symbolCanvas(swuSym);
1346 const png = canvas.toDataURL("image/png");
1347 canvas.remove();
1348 return png;
1349};
1350
1351const blank = null;
1352/**
1353 * Function that normalizes a symbol with a minimum coordinate for a center of 500,500
1354 * @function swu.symbolNormalize
1355 * @param {string} swuSym - an SWU symbol character with optional coordinate and style string
1356 * @example
1357 * swu.symbolNormalize('񀀁')
1358 *
1359 * return '񀀁𝣿𝣷'
1360 */
1361
1362const symbolNormalize = swuSym => {
1363 const parsed = parse.symbol(swuSym);
1364
1365 if (parsed.symbol) {
1366 let size = symbolSize(parsed.symbol);
1367
1368 if (size) {
1369 return `${parsed.symbol}${coord2swu([500 - parseInt((size[0] + 1) / 2), 500 - parseInt((size[1] + 1) / 2)])}${parsed.style || ''}`;
1370 }
1371 } else {
1372 return blank;
1373 }
1374};
1375
1376/**
1377 * Function that creates an SVG image from an SWU sign with an optional style string
1378 * @function swu.signSvgBody
1379 * @param {string} swuSign - an SWU sign with optional style string
1380 * @example
1381 * swu.signSvgBody('M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475')
1382 *
1383 * return `<text font-size="0">M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475</text>
1384 * <g transform="translate(483,510)">
1385 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􋛩</text>
1386 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󻛩</text>
1387 * </g>
1388 * <g transform="translate(501,466)">
1389 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀒</text>
1390 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀒</text>
1391 * </g>
1392 * <g transform="translate(510,500)">
1393 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􋚥</text>
1394 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󻚥</text>
1395 * </g>
1396 * <g transform="translate(476,475)">
1397 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀚</text>
1398 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀚</text>
1399 * </g>`
1400 */
1401
1402const signSvgBody = swuSign => {
1403 let parsed = parse.sign(swuSign);
1404 const blank = '';
1405
1406 if (parsed.spatials) {
1407 let styling = parse$2(parsed.style);
1408
1409 if (styling.detailsym) {
1410 styling.detailsym.forEach(sym => {
1411 if (parsed.spatials[sym.index - 1]) {
1412 parsed.spatials[sym.index - 1].detail = sym.detail;
1413 }
1414 });
1415 }
1416
1417 let x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
1418 let y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
1419 let x2 = parsed.max[0];
1420 let y2 = parsed.max[1];
1421 let background = '';
1422
1423 if (styling.padding) {
1424 x1 -= styling.padding;
1425 y1 -= styling.padding;
1426 x2 += styling.padding;
1427 y2 += styling.padding;
1428 }
1429
1430 if (styling.background) {
1431 background = `\n <rect x="${x1}" y="${y1}" width="${x2 - x1}" height="${y2 - y1}" style="fill:${styling.background};" />`;
1432 }
1433
1434 let svg = ` <text font-size="0">${swuSign}</text>${background}`;
1435 const line = styling.detail && styling.detail[0];
1436 const fill = styling.detail && styling.detail[1];
1437 svg += '\n' + parsed.spatials.map(spatial => {
1438 let svg = symbolText(spatial.symbol);
1439 let symLine = line;
1440
1441 if (spatial.detail) {
1442 symLine = spatial.detail[0];
1443 } else if (styling.colorize) {
1444 symLine = colorize(spatial.symbol);
1445 }
1446
1447 if (symLine) {
1448 svg = svg.replace(/class="sym-line" fill="black"/, `class="sym-line" fill="${symLine}"`);
1449 }
1450
1451 let symFill = fill;
1452
1453 if (spatial.detail && spatial.detail[1]) {
1454 symFill = spatial.detail[1];
1455 }
1456
1457 if (symFill) {
1458 svg = svg.replace(/class="sym-fill" fill="white"/, `class="sym-fill" fill="${symFill}"`);
1459 }
1460
1461 return ` <g transform="translate(${spatial.coord[0]},${spatial.coord[1]})">
1462${svg}
1463 </g>`;
1464 }).join('\n');
1465 return svg;
1466 }
1467
1468 return blank;
1469};
1470/**
1471 * Function that creates an SVG image from an SWU sign with an optional style string
1472 * @function swu.signSvg
1473 * @param {string} swuSign - an SWU sign with optional style string
1474 * @example
1475 * swu.signSvg('M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475')
1476 *
1477 * return `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="49" height="69" viewBox="476 466 49 69">
1478 * <text font-size="0">M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475</text>
1479 * <g transform="translate(483,510)">
1480 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􋛩</text>
1481 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󻛩</text>
1482 * </g>
1483 * <g transform="translate(501,466)">
1484 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀒</text>
1485 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀒</text>
1486 * </g>
1487 * <g transform="translate(510,500)">
1488 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􋚥</text>
1489 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󻚥</text>
1490 * </g>
1491 * <g transform="translate(476,475)">
1492 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀚</text>
1493 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀚</text>
1494 * </g>
1495 * </svg>`
1496 */
1497
1498
1499const signSvg = swuSign => {
1500 let parsed = parse.sign(swuSign);
1501 const blank = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="1" height="1"></svg>';
1502
1503 if (parsed.spatials) {
1504 let styling = parse$2(parsed.style);
1505 let x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
1506 let y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
1507 let x2 = parsed.max[0];
1508 let y2 = parsed.max[1];
1509 let classes = '';
1510
1511 if (styling.classes) {
1512 classes = ` class="${styling.classes}"`;
1513 }
1514
1515 let id = '';
1516
1517 if (styling.id) {
1518 id = ` id="${styling.id}"`;
1519 }
1520
1521 if (styling.padding) {
1522 x1 -= styling.padding;
1523 y1 -= styling.padding;
1524 x2 += styling.padding;
1525 y2 += styling.padding;
1526 }
1527
1528 let sizing = '';
1529
1530 if (styling.zoom != 'x') {
1531 sizing = ` width="${(x2 - x1) * (styling.zoom ? styling.zoom : 1)}" height="${(y2 - y1) * (styling.zoom ? styling.zoom : 1)}"`;
1532 }
1533
1534 let svg = `<svg${classes}${id} version="1.1" xmlns="http://www.w3.org/2000/svg"${sizing} viewBox="${x1} ${y1} ${x2 - x1} ${y2 - y1}">
1535`;
1536 svg += signSvgBody(swuSign);
1537 svg += '\n</svg>';
1538 return svg;
1539 }
1540
1541 return blank;
1542};
1543
1544const signCanvas = function (swuSign) {
1545 const parsed = parse.sign(swuSign);
1546
1547 if (parsed.spatials) {
1548 const canvas = document.createElement('canvas');
1549 const context = canvas.getContext('2d');
1550 let styling = parse$2(parsed.style);
1551
1552 if (styling.detailsym) {
1553 styling.detailsym.forEach(sym => {
1554 if (parsed.spatials[sym.index - 1]) {
1555 parsed.spatials[sym.index - 1].detail = sym.detail;
1556 }
1557 });
1558 }
1559
1560 let x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
1561 let y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
1562 let x2 = parsed.max[0];
1563 let y2 = parsed.max[1];
1564
1565 if (styling.padding) {
1566 x1 -= styling.padding;
1567 y1 -= styling.padding;
1568 x2 += styling.padding;
1569 y2 += styling.padding;
1570 }
1571
1572 let sizing = 1;
1573
1574 if (styling.zoom != 'x') {
1575 sizing = styling.zoom;
1576 }
1577
1578 let w = (x2 - x1) * sizing;
1579 let h = (y2 - y1) * sizing;
1580 canvas.width = w ? w : 1;
1581 canvas.height = h ? h : 1;
1582
1583 if (styling.background) {
1584 context.rect(0, 0, w, h);
1585 context.fillStyle = styling.background;
1586 context.fill();
1587 }
1588
1589 const line = styling.detail && styling.detail[0] || "black";
1590 const fill = styling.detail && styling.detail[1] || "white";
1591 parsed.spatials.forEach(spatial => {
1592 let symLine = line;
1593
1594 if (spatial.detail) {
1595 symLine = spatial.detail[0];
1596 } else if (styling.colorize) {
1597 symLine = colorize(spatial.symbol);
1598 }
1599
1600 let symFill = fill;
1601
1602 if (spatial.detail && spatial.detail[1]) {
1603 symFill = spatial.detail[1];
1604 }
1605
1606 context.font = 30 * sizing + "px 'SuttonSignWritingFill'";
1607 context.fillStyle = symFill;
1608 context.fillText(symbolFill(spatial.symbol), (spatial.coord[0] - x1) * sizing, (spatial.coord[1] - y1) * sizing);
1609 context.font = 30 * sizing + "px 'SuttonSignWritingLine'";
1610 context.fillStyle = symLine;
1611 context.fillText(symbolLine(spatial.symbol), (spatial.coord[0] - x1) * sizing, (spatial.coord[1] - y1) * sizing);
1612 });
1613 return canvas;
1614 }
1615};
1616/**
1617 * Function that creates a PNG data url from an SWU sign with an optional style string
1618 * @function swu.signPng
1619 * @param {string} swuSign - an SWU sign with optional style string
1620 * @example
1621 * swu.signPng('𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭')
1622 *
1623 * return 'data:image/png;base64,iVBORw...'
1624 */
1625
1626
1627const signPng = swuSign => {
1628 const canvas = signCanvas(swuSign);
1629 const png = canvas.toDataURL("image/png");
1630 canvas.remove();
1631 return png;
1632};
1633
1634/**
1635 * Function that normalizes an SWU sign for a center of 500,500
1636 * @function swu.signNormalize
1637 * @param {string} swuSign - an SWU sign with optional style string
1638 * @example
1639 * swu.signNormalize('𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭')
1640 *
1641 * return '𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭'
1642 */
1643
1644const signNormalize = swuSign => {
1645 const parsed = parse.sign(swuSign);
1646
1647 if (parsed.spatials) {
1648 const symbolsizes = parsed.spatials.reduce((output, spatial) => {
1649 const size = symbolSize(spatial.symbol);
1650 output[spatial.symbol] = {
1651 width: size[0],
1652 height: size[1]
1653 };
1654 return output;
1655 }, {});
1656
1657 const bbox = symbols => {
1658 const x1 = Math.min(...symbols.map(spatial => spatial.coord[0]));
1659 const y1 = Math.min(...symbols.map(spatial => spatial.coord[1]));
1660 const x2 = Math.max(...symbols.map(spatial => spatial.coord[0] + parseInt(symbolsizes[spatial.symbol].width)));
1661 const y2 = Math.max(...symbols.map(spatial => spatial.coord[1] + parseInt(symbolsizes[spatial.symbol].height)));
1662 return {
1663 x1: x1,
1664 y1: y1,
1665 x2: x2,
1666 y2: y2
1667 };
1668 };
1669
1670 const hrange = ranges['hcenter'];
1671 const hsyms = parsed.spatials.filter(spatial => {
1672 const dec = parseInt(spatial.symbol.slice(1, 4), 16);
1673 return hrange[0] <= dec && hrange[1] >= dec;
1674 });
1675 const vrange = ranges['vcenter'];
1676 const vsyms = parsed.spatials.filter(spatial => {
1677 const dec = parseInt(spatial.symbol.slice(1, 4), 16);
1678 return vrange[0] <= dec && vrange[1] >= dec;
1679 });
1680 let abox = bbox(parsed.spatials);
1681 let max = [abox.x2, abox.y2];
1682
1683 if (hsyms.length) {
1684 const hbox = bbox(hsyms);
1685 abox.x1 = hbox.x1;
1686 abox.x2 = hbox.x2;
1687 }
1688
1689 if (vsyms.length) {
1690 const vbox = bbox(vsyms);
1691 abox.y1 = vbox.y1;
1692 abox.y2 = vbox.y2;
1693 }
1694
1695 const offset = [parseInt((abox.x2 + abox.x1) / 2) - 500, parseInt((abox.y2 + abox.y1) / 2) - 500];
1696 const swuout = (parsed.sequence ? '𝠀' + parsed.sequence.join('') : '') + parsed.box + coord2swu([max[0] - offset[0], max[1] - offset[1]]) + parsed.spatials.map(spatial => spatial.symbol + coord2swu([spatial.coord[0] - offset[0], spatial.coord[1] - offset[1]])).join('') + (parsed.style || '');
1697 return swuout;
1698 }
1699};
1700
1701/**
1702 * Function that creates an SVG image for a column of SWU
1703 * @function swu.columnSvg
1704 * @param {ColumnData} swuColumn - an array of objects with information about FSW signs and punctuation
1705 * @param {ColumnOptions} options - an object of column options
1706 * @returns {string} column svg
1707 * @example
1708 * swu.columnSvg([
1709 * {
1710 * "x": 56,
1711 * "y": 20,
1712 * "minX": 481,
1713 * "minY": 471,
1714 * "width": 37,
1715 * "height": 58,
1716 * "lane": 0,
1717 * "padding": 0,
1718 * "segment": "sign",
1719 * "text": "𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻",
1720 * "zoom": 1
1721 * },
1722 * {
1723 * "x": 57,
1724 * "y": 118,
1725 * "minX": 482,
1726 * "minY": 468,
1727 * "width": 36,
1728 * "height": 65,
1729 * "lane": 0,
1730 * "padding": 0,
1731 * "segment": "sign",
1732 * "text": "𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦",
1733 * "zoom": 1
1734 * },
1735 * {
1736 * "x": 39,
1737 * "y": 203,
1738 * "minX": 464,
1739 * "minY": 496,
1740 * "width": 72,
1741 * "height": 8,
1742 * "lane": 0,
1743 * "padding": 0,
1744 * "segment": "symbol",
1745 * "text": "񏌁𝣢𝤂",
1746 * "zoom": 1
1747 * }
1748 * ],
1749 * {
1750 * "height": 250,
1751 * "width": 150,
1752 * })
1753 *
1754 * return `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="150" height="250" viewBox="0 0 150 250">
1755 * <g transform="translate(56,20) scale(1) translate(-481,-471) ">
1756 * <text font-size="0">𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻-D_black,white_Z1</text>
1757 * <g transform="translate(481,471)">
1758 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􁲡</text>
1759 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󱲡</text>
1760 * </g>
1761 * <g transform="translate(503,489)">
1762 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􈩧</text>
1763 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󸩧</text>
1764 * </g>
1765 * </g>
1766 * <g transform="translate(57,118) scale(1) translate(-482,-468) ">
1767 * <text font-size="0">𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦-D_black,white_Z1</text>
1768 * <g transform="translate(489,515)">
1769 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􃊫</text>
1770 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󳊫</text>
1771 * </g>
1772 * <g transform="translate(482,490)">
1773 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􃊢</text>
1774 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󳊢</text>
1775 * </g>
1776 * <g transform="translate(508,496)">
1777 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􆇡</text>
1778 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󶇡</text>
1779 * </g>
1780 * <g transform="translate(500,468)">
1781 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􋛕</text>
1782 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󻛕</text>
1783 * </g>
1784 * </g>
1785 * <g transform="translate(39,203) scale(1) translate(-464,-496) ">
1786 * <text font-size="0">񏌁𝣢𝤂-D_black,white_Z1</text>
1787 * <g transform="translate(464,496)">
1788 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􏌁</text>
1789 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󿌁</text>
1790 * </g>
1791 * </g>
1792 * </svg>`
1793 */
1794
1795const columnSvg = (swuColumn, options) => {
1796
1797 if (typeof options !== 'object') options = {};
1798 const values = Object.assign(columnDefaults, options);
1799 let x1 = 0;
1800 let y1 = 0;
1801 let x2 = values.width;
1802 let y2 = values.height;
1803 let background = '';
1804
1805 if (values.background) {
1806 background = `\n <rect x="${x1}" y="${y1}" width="${x2 - x1}" height="${y2 - y1}" style="fill:${values.background};" />`;
1807 }
1808
1809 let sizing = ` width="${values.width}" height="${values.height}"`;
1810 let svg = `<svg version="1.1" xmlns="http://www.w3.org/2000/svg"${sizing} viewBox="${x1} ${y1} ${x2 - x1} ${y2 - y1}">
1811 <text font-size="0">${x1}</text>${background}`;
1812 svg += swuColumn.map(item => {
1813 const dash = item.text.indexOf('-');
1814
1815 if (dash > 0) {
1816 const itemStyle = item.text.substring(dash);
1817 const newStyle = { ...values.style,
1818 ...parse$2(itemStyle)
1819 };
1820 item.text = item.text.replace(itemStyle, compose(newStyle));
1821 } else {
1822 item.text += compose(values.style);
1823 }
1824
1825 item.zoom = item.zoom * values.style.zoom;
1826 return '<g transform="translate(' + item.x + ',' + item.y + ') scale(' + item.zoom + ') translate(' + -item.minX + ',' + -item.minY + ') ">' + (item.segment == "sign" ? signSvgBody(item.text) : symbolSvgBody(item.text)) + '</g>';
1827 }).join('\n');
1828 svg += '\n</svg>';
1829 return svg;
1830};
1831
1832/**
1833 * Function that creates an array of SVG column images for an SWU text
1834 * @function swu.columnsSvg
1835 * @param {string} swuText - a text of SWU signs and punctuation
1836 * @param {ColumnOptions} options - an object of column options
1837 * @returns {string[]} array of SVG columns
1838 * @example
1839 * swu.columnsSvg('𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻 𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦 񏌁𝣢𝤂',{
1840 * "height": 250,
1841 * "width": 150,
1842 * })
1843 *
1844 * return [`<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="150" height="250" viewBox="0 0 150 250">
1845 * <g transform="translate(56,20) scale(1) translate(-481,-471) ">
1846 * <text font-size="0">𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻-D_black,white_Z1</text>
1847 * <g transform="translate(481,471)">
1848 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􁲡</text>
1849 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󱲡</text>
1850 * </g>
1851 * <g transform="translate(503,489)">
1852 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􈩧</text>
1853 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󸩧</text>
1854 * </g>
1855 * </g>
1856 * <g transform="translate(57,118) scale(1) translate(-482,-468) ">
1857 * <text font-size="0">𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦-D_black,white_Z1</text>
1858 * <g transform="translate(489,515)">
1859 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􃊫</text>
1860 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󳊫</text>
1861 * </g>
1862 * <g transform="translate(482,490)">
1863 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􃊢</text>
1864 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󳊢</text>
1865 * </g>
1866 * <g transform="translate(508,496)">
1867 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􆇡</text>
1868 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󶇡</text>
1869 * </g>
1870 * <g transform="translate(500,468)">
1871 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􋛕</text>
1872 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󻛕</text>
1873 * </g>
1874 * </g>
1875 * <g transform="translate(39,203) scale(1) translate(-464,-496) ">
1876 * <text font-size="0">񏌁𝣢𝤂-D_black,white_Z1</text>
1877 * <g transform="translate(464,496)">
1878 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􏌁</text>
1879 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󿌁</text>
1880 * </g>
1881 * </g>
1882 * </svg>`]
1883 */
1884
1885const columnsSvg = function (swuText, options) {
1886 if (typeof options !== 'object') options = {};
1887 let values = columns(swuText, options);
1888 let cols = values.columns.map((col, i) => {
1889 return columnSvg(col, { ...values.options,
1890 ...{
1891 width: values.widths[i]
1892 }
1893 });
1894 });
1895 return cols;
1896};
1897
1898const columnCanvas = function (swuColumn, options) {
1899 if (typeof options !== 'object') options = {};
1900 const values = Object.assign(columnDefaults, options);
1901 const canvas = document.createElement('canvas');
1902 canvas.width = values.width;
1903 canvas.height = values.height;
1904 const context = canvas.getContext('2d');
1905
1906 if (values.background) {
1907 context.rect(0, 0, values.width, values.height);
1908 context.fillStyle = values.background;
1909 context.fill();
1910 }
1911
1912 swuColumn.map(item => {
1913 const dash = item.text.indexOf('-');
1914
1915 if (dash > 0) {
1916 const itemStyle = item.text.substring(dash);
1917 const newStyle = { ...values.style,
1918 ...parse$2(itemStyle)
1919 };
1920 item.text = item.text.replace(itemStyle, compose(newStyle));
1921 } else {
1922 item.text += compose(values.style);
1923 }
1924
1925 item.zoom = item.zoom * values.style.zoom;
1926 let parsed = {};
1927
1928 if (item.segment == "sign") {
1929 parsed = parse.sign(item.text);
1930 } else {
1931 let sym = parse.symbol(item.text);
1932 parsed.style = sym.style;
1933 parsed.spatials = [sym];
1934 }
1935
1936 let styling = parse$2(parsed.style);
1937
1938 if (styling.background) {
1939 context.fillStyle = styling.background;
1940 context.fillRect(item.x - styling.padding * item.zoom, item.y - styling.padding * item.zoom, (item.width + styling.padding * 2) * item.zoom, (item.height + styling.padding * 2) * item.zoom);
1941 }
1942
1943 if (styling.detailsym) {
1944 styling.detailsym.forEach(sym => {
1945 if (parsed.spatials[sym.index - 1]) {
1946 parsed.spatials[sym.index - 1].detail = sym.detail;
1947 }
1948 });
1949 }
1950
1951 const line = styling.detail && styling.detail[0] || "black";
1952 const fill = styling.detail && styling.detail[1] || "white";
1953 parsed.spatials.forEach(spatial => {
1954 let symLine = line;
1955
1956 if (spatial.detail) {
1957 symLine = spatial.detail[0];
1958 } else if (styling.colorize) {
1959 symLine = colorize(spatial.symbol);
1960 }
1961
1962 let symFill = fill;
1963
1964 if (spatial.detail && spatial.detail[1]) {
1965 symFill = spatial.detail[1];
1966 }
1967
1968 context.font = 30 * item.zoom + "px 'SuttonSignWritingFill'";
1969 context.fillStyle = symFill;
1970 context.fillText(symbolFill(spatial.symbol), item.x + (spatial.coord[0] - item.minX) * item.zoom, item.y + (spatial.coord[1] - item.minY) * item.zoom);
1971 context.font = 30 * item.zoom + "px 'SuttonSignWritingLine'";
1972 context.fillStyle = symLine;
1973 context.fillText(symbolLine(spatial.symbol), item.x + (spatial.coord[0] - item.minX) * item.zoom, item.y + (spatial.coord[1] - item.minY) * item.zoom);
1974 });
1975 });
1976 return canvas;
1977};
1978/**
1979 * Function that creates a PNG data url for a column of SWU
1980 * @function swu.columnPng
1981 * @param {ColumnData} swuColumn - an array of SWU signs and punctuation with coordinates
1982 * @param {ColumnOptions} options - an object of column options
1983 * @returns {string} column png data url
1984 * @example
1985 * swu.columnPng([
1986 * {
1987 * "x": 56,
1988 * "y": 20,
1989 * "minX": 481,
1990 * "minY": 471,
1991 * "width": 37,
1992 * "height": 58,
1993 * "lane": 0,
1994 * "padding": 0,
1995 * "segment": "sign",
1996 * "text": "𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻",
1997 * "zoom": 1
1998 * },
1999 * {
2000 * "x": 57,
2001 * "y": 118,
2002 * "minX": 482,
2003 * "minY": 468,
2004 * "width": 36,
2005 * "height": 65,
2006 * "lane": 0,
2007 * "padding": 0,
2008 * "segment": "sign",
2009 * "text": "𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦",
2010 * "zoom": 1
2011 * },
2012 * {
2013 * "x": 39,
2014 * "y": 203,
2015 * "minX": 464,
2016 * "minY": 496,
2017 * "width": 72,
2018 * "height": 8,
2019 * "lane": 0,
2020 * "padding": 0,
2021 * "segment": "symbol",
2022 * "text": "񏌁𝣢𝤂",
2023 * "zoom": 1
2024 * }
2025 * ],
2026 * {
2027 * "height": 250,
2028 * "width": 150,
2029 * })
2030 *
2031 * return 'data:image/png;base64,iVBORw...'
2032 */
2033
2034
2035const columnPng = (swuColumn, options) => {
2036 const canvas = columnCanvas(swuColumn, options);
2037 const png = canvas.toDataURL("image/png");
2038 canvas.remove();
2039 return png;
2040};
2041
2042/**
2043 * Function that creates an SVG image for a column of SWU
2044 * @function swu.columnsPng
2045 * @param {string} swuText - an array of SWU signs and punctuation with coordinates
2046 * @param {ColumnOptions} options - an object of column options
2047 * @returns {string[]} array of PNG data urls
2048 * @example
2049 * swu.columnsPng('𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻 𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦 񏌁𝣢𝤂',{
2050 * "height": 250,
2051 * "width": 150,
2052 * })
2053 *
2054 * return ['data:image/png;base64,iVBORw...']
2055 */
2056
2057const columnsPng = function (swuText, options) {
2058 if (typeof options !== 'object') options = {};
2059 let values = columns(swuText, options);
2060 let cols = values.columns.map((col, i) => {
2061 return columnPng(col, { ...values.options,
2062 ...{
2063 width: values.widths[i]
2064 }
2065 });
2066 });
2067 return cols;
2068};
2069
2070export { columnPng, columnSvg, columnsPng, columnsSvg, signNormalize, signPng, signSvg, signSvgBody, symbolFill, symbolLine, symbolNormalize, symbolPng, symbolSize, symbolSvg, symbolSvgBody, symbolText };
2071
2072/* support ongoing development on https://patreon.com/signwriting */