UNPKG

125 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* index.mjs is released under the MIT License.
5*/
6
7/**
8 * Function that appends font-face CSS for the Sutton SignWriting fonts for system installed fonts, relative directory fonts, or content delivery network
9 * @function font.cssAppend
10 * @param {string} dir - an optional relative directory for font location
11 * @example
12 * font.cssAppend('./font/')
13 */
14const cssAppend = function (dir = '') {
15 const id = "SgnwFontCss";
16
17 if (!document.getElementById(id)) {
18 const style = document.createElement('style');
19 style.setAttribute("id", "SgnwFontCss");
20 style.appendChild(document.createTextNode(`
21 @font-face {
22 font-family: "SuttonSignWritingLine";
23 src:
24 local('SuttonSignWritingLine'),
25 ${dir ? `url('${dir}SuttonSignWritingLine.ttf') format('truetype'),` : ""}
26 url('https://cdn.jsdelivr.net/npm/@sutton-signwriting/font-ttf@1.0.0/font/SuttonSignWritingLine.ttf') format('truetype');
27 }
28 @font-face {
29 font-family: "SuttonSignWritingFill";
30 src:
31 local('SuttonSignWritingFill'),
32 ${dir ? `url('${dir}SuttonSignWritingFill.ttf') format('truetype'),` : ""}
33 url('https://cdn.jsdelivr.net/npm/@sutton-signwriting/font-ttf@1.0.0/font/SuttonSignWritingFill.ttf') format('truetype');
34 }
35 @font-face {
36 font-family: "SuttonSignWritingOneD";
37 src:
38 local('SuttonSignWritingOneD'),
39 ${dir ? `url('${dir}SuttonSignWritingOneD.ttf') format('truetype'),` : ""}
40 url('https://cdn.jsdelivr.net/npm/@sutton-signwriting/font-ttf@1.0.0/font/SuttonSignWritingOneD.ttf') format('truetype');
41 }
42 `));
43 document.head.appendChild(style);
44 }
45};
46
47let sizes = {};
48const zoom = 2;
49const bound = 76 * zoom;
50let context;
51/**
52 * Function that returns the size of a symbol using an id
53 * @function font.symbolSize
54 * @param {number} id - a 16-bit number of a symbol
55 * @example
56 * font.symbolSize(1)
57 *
58 * return [15,30]
59 */
60
61const symbolSize$2 = function (id) {
62 if (id in sizes) {
63 return [...sizes[id]];
64 }
65
66 if (!context) {
67 const canvaser = document.createElement("canvas");
68 canvaser.width = bound;
69 canvaser.height = bound;
70 context = canvaser.getContext("2d");
71 }
72
73 context.clearRect(0, 0, bound, bound);
74 context.font = 30 * zoom + "px 'SuttonSignWritingLine'";
75 context.fillText(String.fromCodePoint(id + 0xF0000), 0, 0);
76 const imgData = context.getImageData(0, 0, bound, bound).data;
77 let w, h, i, s;
78
79 wloop: for (w = bound - 1; w >= 0; w--) {
80 for (h = 0; h < bound; h += 1) {
81 for (s = 0; s < 4; s += 1) {
82 i = w * 4 + h * 4 * bound + s;
83
84 if (imgData[i]) {
85 break wloop;
86 }
87 }
88 }
89 }
90
91 var width = w;
92
93 hloop: for (h = bound - 1; h >= 0; h--) {
94 for (w = 0; w < width; w += 1) {
95 for (s = 0; s < 4; s += 1) {
96 i = w * 4 + h * 4 * bound + s;
97
98 if (imgData[i]) {
99 break hloop;
100 }
101 }
102 }
103 }
104
105 var height = h + 1;
106 width = Math.ceil(width / zoom);
107 height = Math.ceil(height / zoom); // Rounding error in chrome. Manual fixes.
108
109 if (14394 == id) {
110 width = 19;
111 }
112
113 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)) {
114 width = 20;
115 }
116
117 if (31921 == id) {
118 width = 22;
119 }
120
121 if (38460 == id) {
122 width = 23;
123 }
124
125 if ([20164, 20212].includes(id)) {
126 width = 25;
127 }
128
129 if (31894 == id) {
130 width = 28;
131 }
132
133 if (46698 == id) {
134 width = 29;
135 }
136
137 if (29606 == id) {
138 width = 30;
139 }
140
141 if (44855 == id) {
142 width = 40;
143 }
144
145 if (32667 == id) {
146 width = 50;
147 }
148
149 if ([11088, 11474, 11490, 11506].includes(id)) {
150 height = 20;
151 }
152
153 if (6285 == id) {
154 height = 21;
155 }
156
157 if (40804 == id) {
158 height = 31;
159 }
160
161 if (41475 == id) {
162 height = 36;
163 } // Error in chrome. Manual fix.
164 // if (width==0 && height==0) {
165
166
167 if (width == 0 && height == 0) {
168 const sizefix = {
169 9: [15, 30],
170 10: [21, 30],
171 11: [30, 15],
172 12: [30, 21],
173 13: [15, 30],
174 14: [21, 30]
175 };
176
177 if (id in sizefix) {
178 width = sizefix[id][0];
179 height = sizefix[id][1];
180 }
181 }
182
183 if (width == 0 && height == 0) {
184 return undefined;
185 }
186
187 sizes[id] = [width, height];
188 return [width, height];
189};
190
191/**
192 * Function that returns a plane 15 character for a symbol line using an id
193 * @function font.symbolLine
194 * @param {number} id - a 16-bit number of a symbol
195 * @example
196 * font.symbolLine(1)
197 *
198 * return '󰀁'
199 */
200const symbolLine$2 = function (id) {
201 return String.fromCodePoint(id + 0xF0000);
202};
203/**
204 * Function that returns a plane 16 character for a symbol fill using an id
205 * @function font.symbolFill
206 * @param {number} id - a 16-bit number of a symbol
207 * @example
208 * font.symbolFill(1)
209 *
210 * return '􀀁'
211 */
212
213
214const symbolFill$2 = function (id) {
215 return String.fromCodePoint(id + 0x100000);
216};
217/**
218 * Function that creates two text elements for a symbol using an id
219 * @function font.symbolText
220 * @param {number} id - a 16-bit number of a symbol
221 * @example
222 * font.symbolText(1)
223 *
224 * return ` <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀁</text>
225 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀁</text>`
226 */
227
228
229const symbolText$2 = function (id) {
230 return ` <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">${symbolFill$2(id)}</text>
231 <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">${symbolLine$2(id)}</text>`;
232};
233
234/**
235 * Function that executes a callback function once the Sutton SignWriiting Line and Fill fonts are ready to use
236 * @function font.cssLoaded
237 * @param {function} callback - a callback function to execute when fonts are ready
238 * @example
239 * const callback = () => {
240 * console.log("Sutton SignWriting Line and Fill fonts are ready to use")
241 * }
242 *
243 * font.cssLoaded( callback )
244 */
245
246const cssLoaded = function (callback) {
247 let lineReady = false;
248 let fillReady = false;
249 cssLoadedLine(() => {
250 lineReady = true;
251 });
252 cssLoadedFill(() => {
253 fillReady = true;
254 });
255 const cssCheck = setInterval(function () {
256 if (lineReady && fillReady) {
257 clearInterval(cssCheck);
258 callback();
259 }
260 }, 100);
261};
262/**
263 * Function that executes a callback function once the Sutton SignWriiting Line font is ready to use
264 * @function font.cssLoadedLine
265 * @param {function} callback - a callback function to execute when line font is ready
266 * @example
267 * const callback = () => {
268 * console.log("Sutton SignWriting Line font is ready to use")
269 * }
270 *
271 * font.cssLoadedLine( callback )
272 */
273
274
275const cssLoadedLine = function (callback) {
276 if (!symbolSize$2(1)) {
277 const cssCheck = setInterval(function () {
278 if (symbolSize$2(1)) {
279 clearInterval(cssCheck);
280 callback();
281 }
282 }, 100);
283 } else {
284 callback();
285 }
286};
287/**
288 * Function that executes a callback function once the Sutton SignWriiting Fill font is ready to use
289 * @function font.cssLoadedFill
290 * @param {function} callback - a callback function to execute when fill font is ready
291 * @example
292 * const callback = () => {
293 * console.log("Sutton SignWriting Fill font is ready to use")
294 * }
295 *
296 * font.cssLoadedFill( callback )
297 */
298
299
300const cssLoadedFill = function (callback) {
301 const fillReady = function () {
302 const canvaser = document.createElement("canvas");
303 canvaser.width = 15;
304 canvaser.height = 30;
305 const context = canvaser.getContext("2d");
306 context.font = "30px 'SuttonSignWritingFill'";
307 context.fillText(symbolFill$2(1), 0, 0);
308 const imgData = context.getImageData(0, 0, 15, 30).data;
309 return !imgData.every(item => item === 0);
310 };
311
312 if (!fillReady()) {
313 const cssCheck = setInterval(function () {
314 if (fillReady()) {
315 clearInterval(cssCheck);
316 callback();
317 }
318 }, 100);
319 } else {
320 callback();
321 }
322};
323
324/** The font module contains functions for handing the TrueType fonts.
325 * @module font
326 */
327
328var index$2 = /*#__PURE__*/Object.freeze({
329 __proto__: null,
330 cssAppend: cssAppend,
331 cssLoaded: cssLoaded,
332 cssLoadedLine: cssLoadedLine,
333 cssLoadedFill: cssLoadedFill,
334 symbolSize: symbolSize$2,
335 symbolLine: symbolLine$2,
336 symbolFill: symbolFill$2,
337 symbolText: symbolText$2
338});
339
340/**
341* Sutton SignWriting Core Module v1.4.2 (https://github.com/sutton-signwriting/core)
342* Author: Steve Slevinski (https://SteveSlevinski.me)
343* convert.mjs is released under the MIT License.
344*/
345/**
346 * Function to convert a number to an SWU number character
347 * @function convert.num2swu
348 * @param {number} num - Integer value for number
349 * @returns {string} SWU number character
350 * @example
351 * convert.num2swu(500)
352 *
353 * return '𝤆'
354 */
355
356
357const num2swu = num => String.fromCodePoint(0x1D80C + parseInt(num) - 250);
358/**
359 * Function to convert an array of x,y integers to two SWU number characters
360 * @function convert.coord2swu
361 * @param {number[]} coord - Array of x,y integers
362 * @returns {string} Two SWU number character
363 * @example
364 * convert.coord2swu([500, 500])
365 *
366 * return '𝤆𝤆'
367 */
368
369
370const coord2swu = coord => coord.map(num => num2swu(num)).join('');
371/**
372 * Function to convert an SWU symbol character to a code point on plane 4
373 * @function convert.swu2code
374 * @param {string} swuSym - SWU symbol character
375 * @returns {number} Code point on plane 4
376 * @example
377 * convert.swu2code('񀀁')
378 *
379 * return 0x40001
380 */
381
382
383const swu2code$1 = swuSym => parseInt(swuSym.codePointAt(0));
384/**
385 * Function to convert an SWU symbol character to a 16-bit ID
386 * @function convert.swu2id
387 * @param {string} swuSym - SWU symbol character
388 * @returns {number} 16-bit ID
389 * @example
390 * convert.swu2id('񀀁')
391 *
392 * return 1
393 */
394
395
396const swu2id = swuSym => swu2code$1(swuSym) - 0x40000;
397/**
398 * Function to convert an FSW symbol key to a 16-bit ID
399 * @function convert.key2id
400 * @param {string} key - FSW symbol key
401 * @returns {number} 16-bit ID
402 * @example
403 * convert.key2id('S10000')
404 *
405 * return 1
406 */
407
408
409const key2id = key => 1 + (parseInt(key.slice(1, 4), 16) - 256) * 96 + parseInt(key.slice(4, 5), 16) * 16 + parseInt(key.slice(5, 6), 16);
410/* support ongoing development on https://patreon.com/signwriting */
411
412/**
413 * Function that returns the size of a symbol using an FSW symbol key
414 * @function fsw.symbolSize
415 * @param {string} fsw - an FSW symbol key
416 * @example
417 * fsw.symbolSize("S10000")
418 *
419 * return [15,30]
420 */
421
422const symbolSize$1 = function (fsw) {
423 return symbolSize$2(key2id(fsw));
424};
425
426/**
427 * Function that returns a plane 15 character for a symbol line using an FSW symbol key
428 * @function fsw.symbolLine
429 * @param {string} fsw - an FSW symbol key
430 * @example
431 * fsw.symbolLine('S10000')
432 *
433 * return '󰀁'
434 */
435
436const symbolLine$1 = function (fsw) {
437 return symbolLine$2(key2id(fsw));
438};
439/**
440 * Function that returns a plane 16 character for a symbol fill using an FSW symbol key
441 * @function fsw.symbolFill
442 * @param {string} fsw - an FSW symbol key
443 * @example
444 * font.symbolFill('S10000')
445 *
446 * return '􀀁'
447 */
448
449
450const symbolFill$1 = function (fsw) {
451 return symbolFill$2(key2id(fsw));
452};
453/**
454 * Function that creates two text elements for a symbol using an FSW symbol key
455 * @function fsw.symbolText
456 * @param {string} fsw - an FSW symbol key
457 * @example
458 * fsw.symbolText('S10000')
459 *
460 * return ` <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀁</text>
461 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀁</text>`
462 */
463
464
465const symbolText$1 = function (fsw) {
466 return symbolText$2(key2id(fsw));
467};
468
469/**
470* Sutton SignWriting Core Module v1.4.2 (https://github.com/sutton-signwriting/core)
471* Author: Steve Slevinski (https://SteveSlevinski.me)
472* style.mjs is released under the MIT License.
473*/
474
475/**
476 * Object of regular expressions for style strings
477 *
478 * @alias style.re
479 * @type {object}
480 * @property {string} colorize - regular expression for colorize section
481 * @property {string} colorhex - regular expression for color hex values with 3 or 6 characters
482 * @property {string} colorname - regular expression for css color name
483 * @property {string} padding - regular expression for padding section
484 * @property {string} zoom - regular expression for zoom section
485 * @property {string} classbase - regular expression for class name definition
486 * @property {string} id - regular expression for id definition
487 * @property {string} colorbase - regular expression for color hex or color name
488 * @property {string} color - regular expression for single color entry
489 * @property {string} colors - regular expression for double color entry
490 * @property {string} background - regular expression for background section
491 * @property {string} detail - regular expression for color details for line and optional fill
492 * @property {string} detailsym - regular expression for color details for individual symbols
493 * @property {string} classes - regular expression for one or more class names
494 * @property {string} full - full regular expression for style string
495 */
496let re$3 = {
497 'colorize': 'C',
498 'colorhex': '(?:[0-9a-fA-F]{3}){1,2}',
499 'colorname': '[a-zA-Z]+',
500 'padding': 'P[0-9]{2}',
501 'zoom': 'Z(?:[0-9]+(?:\\.[0-9]+)?|x)',
502 'classbase': '-?[_a-zA-Z][_a-zA-Z0-9-]{0,100}',
503 'id': '[a-zA-Z][_a-zA-Z0-9-]{0,100}'
504};
505re$3.colorbase = `(?:${re$3.colorhex}|${re$3.colorname})`;
506re$3.color = `_${re$3.colorbase}_`;
507re$3.colors = `_${re$3.colorbase}(?:,${re$3.colorbase})?_`;
508re$3.background = `G${re$3.color}`;
509re$3.detail = `D${re$3.colors}`;
510re$3.detailsym = `D[0-9]{2}${re$3.colors}`;
511re$3.classes = `${re$3.classbase}(?: ${re$3.classbase})*`;
512re$3.full = `-(${re$3.colorize})?(${re$3.padding})?(${re$3.background})?(${re$3.detail})?(${re$3.zoom})?(?:-((?:${re$3.detailsym})*))?(?:-(${re$3.classes})?!(?:(${re$3.id})!)?)?`;
513
514const prefixColor$2 = color => {
515 const regex = new RegExp(`^${re$3.colorhex}$`);
516 return (regex.test(color) ? '#' : '') + color;
517};
518
519const definedProps$2 = obj => Object.fromEntries(Object.entries(obj).filter(([k, v]) => v !== undefined));
520/**
521 * Function to parse style string to object
522 * @function style.parse
523 * @param {string} styleString - a style string
524 * @returns {StyleObject} elements of style string
525 * @example
526 * style.parse('-CP10G_blue_D_red,Cyan_')
527 *
528 * return {
529 * 'colorize': true,
530 * 'padding': 10,
531 * 'background': 'blue',
532 * 'detail': ['red', 'Cyan']
533 * }
534 */
535
536
537const parse$3 = styleString => {
538 const regex = `^${re$3.full}`;
539 const m = (typeof styleString === 'string' ? styleString.match(new RegExp(regex)) : []) || [];
540 return definedProps$2({
541 'colorize': !m[1] ? undefined : !!m[1],
542 'padding': !m[2] ? undefined : parseInt(m[2].slice(1)),
543 'background': !m[3] ? undefined : prefixColor$2(m[3].slice(2, -1)),
544 'detail': !m[4] ? undefined : m[4].slice(2, -1).split(',').map(prefixColor$2),
545 'zoom': !m[5] ? undefined : m[5] === 'Zx' ? 'x' : parseFloat(m[5].slice(1)),
546 'detailsym': !m[6] ? undefined : m[6].match(new RegExp(re$3.detailsym, 'g')).map(val => {
547 const parts = val.split('_');
548 const detail = parts[1].split(',').map(prefixColor$2);
549 return {
550 'index': parseInt(parts[0].slice(1)),
551 'detail': detail
552 };
553 }),
554 'classes': !m[7] ? undefined : m[7],
555 'id': !m[8] ? undefined : m[8]
556 });
557};
558/**
559 * Function to compose style string from object
560 * @function style.compose
561 * @param {StyleObject} styleObject - an object of style options
562 * @returns {string} style string
563 * @example
564 * style.compose({
565 * 'colorize': true,
566 * 'padding': 10,
567 * 'background': 'blue',
568 * 'detail': ['red', 'Cyan'],
569 * 'zoom': 1.1,
570 * 'detailsym': [
571 * {
572 * 'index': 1,
573 * 'detail': ['#ff00ff']
574 * },
575 * {
576 * 'index': 2,
577 * 'detail': ['yellow', 'green']
578 * }
579 * ],
580 * 'classes': 'primary blinking',
581 * 'id': 'cursor'
582 * })
583 *
584 * return '-CP10G_blue_D_red,Cyan_Z1.1-D01_ff00ff_D02_yellow,green_-primary blinking!cursor!'
585 */
586
587
588const compose = styleObject => {
589 if (typeof styleObject !== 'object' || styleObject === null) return undefined; // three sections
590
591 let style1 = '-';
592 style1 += !styleObject.colorize ? '' : 'C';
593 const padding = parseInt(styleObject.padding);
594 style1 += !padding || padding <= 0 || padding > 99 ? '' : 'P' + (padding > 9 ? padding : '0' + padding);
595 const background = !styleObject.background || !(typeof styleObject.background === 'string') ? undefined : styleObject.background.match(re$3.colorbase)[0];
596 style1 += !background ? '' : 'G_' + background + '_';
597 const detail1 = !styleObject.detail || !styleObject.detail[0] || !(typeof styleObject.detail[0] === 'string') ? undefined : styleObject.detail[0].match(re$3.colorbase)[0];
598 const detail2 = !styleObject.detail || !styleObject.detail[1] || !(typeof styleObject.detail[1] === 'string') ? undefined : styleObject.detail[1].match(re$3.colorbase)[0];
599
600 if (detail1) {
601 style1 += 'D_' + detail1;
602
603 if (detail2) {
604 style1 += ',' + detail2;
605 }
606
607 style1 += '_';
608 }
609
610 const zoom = styleObject.zoom === 'x' ? 'x' : parseFloat(styleObject.zoom);
611 style1 += !zoom || zoom <= 0 ? '' : 'Z' + zoom;
612 let style2 = '';
613 const detailsym = !styleObject.detailsym || !Array.isArray(styleObject.detailsym) ? [] : styleObject.detailsym.map(styleObject => {
614 const index = parseInt(styleObject.index);
615 if (!index || index <= 0 || index > 99) return '';
616 let style = 'D' + (index > 9 ? index : '0' + index);
617 const detail1 = !styleObject.detail || !styleObject.detail[0] ? undefined : styleObject.detail[0].match(re$3.colorbase)[0];
618 const detail2 = !styleObject.detail || !styleObject.detail[1] ? undefined : styleObject.detail[1].match(re$3.colorbase)[0];
619
620 if (detail1) {
621 style += '_' + detail1;
622
623 if (detail2) {
624 style += ',' + detail2;
625 }
626
627 style += '_';
628 }
629
630 return style;
631 });
632 style2 += detailsym.join('');
633 let style3 = '';
634 const classes = !styleObject.classes || !(typeof styleObject.classes === 'string') ? undefined : styleObject.classes.match(re$3.classes)[0];
635 style3 += !classes ? '' : classes;
636 const id = !styleObject.id || !(typeof styleObject.id === 'string') ? undefined : styleObject.id.match(re$3.id)[0];
637 style3 += classes || id ? '!' : '';
638 style3 += !id ? '' : id + '!';
639 return style1 + (style2 || style3 ? '-' + style2 : '') + (style3 ? '-' + style3 : '');
640};
641/* support ongoing development on https://patreon.com/signwriting */
642
643/**
644* Sutton SignWriting Core Module v1.4.2 (https://github.com/sutton-signwriting/core)
645* Author: Steve Slevinski (https://SteveSlevinski.me)
646* fsw.mjs is released under the MIT License.
647*/
648
649/**
650 * Object of regular expressions for FSW strings
651 *
652 * @alias fsw.re
653 * @property {string} symbol - regular expressions for a symbol
654 * @property {string} coord - regular expressions for a coordinate
655 * @property {string} sort - regular expressions for the sorting marker
656 * @property {string} box - regular expression for a signbox marker
657 * @property {string} prefix - regular expression for a sorting marker followed by one or more symbols
658 * @property {string} spatial - regular expression for a symbol followed by a coordinate
659 * @property {string} signbox - regular expression for a signbox marker, max coordinate and zero or more spatial symbols
660 * @property {string} sign - regular expression for an optional prefix followed by a signbox
661 * @property {string} sortable - regular expression for a mandatory prefix followed by a signbox
662 */
663let re$1$1 = {
664 'symbol': 'S[123][0-9a-f]{2}[0-5][0-9a-f]',
665 'coord': '[0-9]{3}x[0-9]{3}',
666 'sort': 'A',
667 'box': '[BLMR]'
668};
669re$1$1.prefix = `(?:${re$1$1.sort}(?:${re$1$1.symbol})+)`;
670re$1$1.spatial = `${re$1$1.symbol}${re$1$1.coord}`;
671re$1$1.signbox = `${re$1$1.box}${re$1$1.coord}(?:${re$1$1.spatial})*`;
672re$1$1.sign = `${re$1$1.prefix}?${re$1$1.signbox}`;
673re$1$1.sortable = `${re$1$1.prefix}${re$1$1.signbox}`;
674/**
675 * Object of regular expressions for style strings
676 *
677 * @alias style.re
678 * @type {object}
679 * @property {string} colorize - regular expression for colorize section
680 * @property {string} colorhex - regular expression for color hex values with 3 or 6 characters
681 * @property {string} colorname - regular expression for css color name
682 * @property {string} padding - regular expression for padding section
683 * @property {string} zoom - regular expression for zoom section
684 * @property {string} classbase - regular expression for class name definition
685 * @property {string} id - regular expression for id definition
686 * @property {string} colorbase - regular expression for color hex or color name
687 * @property {string} color - regular expression for single color entry
688 * @property {string} colors - regular expression for double color entry
689 * @property {string} background - regular expression for background section
690 * @property {string} detail - regular expression for color details for line and optional fill
691 * @property {string} detailsym - regular expression for color details for individual symbols
692 * @property {string} classes - regular expression for one or more class names
693 * @property {string} full - full regular expression for style string
694 */
695
696let re$2 = {
697 'colorize': 'C',
698 'colorhex': '(?:[0-9a-fA-F]{3}){1,2}',
699 'colorname': '[a-zA-Z]+',
700 'padding': 'P[0-9]{2}',
701 'zoom': 'Z(?:[0-9]+(?:\\.[0-9]+)?|x)',
702 'classbase': '-?[_a-zA-Z][_a-zA-Z0-9-]{0,100}',
703 'id': '[a-zA-Z][_a-zA-Z0-9-]{0,100}'
704};
705re$2.colorbase = `(?:${re$2.colorhex}|${re$2.colorname})`;
706re$2.color = `_${re$2.colorbase}_`;
707re$2.colors = `_${re$2.colorbase}(?:,${re$2.colorbase})?_`;
708re$2.background = `G${re$2.color}`;
709re$2.detail = `D${re$2.colors}`;
710re$2.detailsym = `D[0-9]{2}${re$2.colors}`;
711re$2.classes = `${re$2.classbase}(?: ${re$2.classbase})*`;
712re$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})!)?)?`;
713
714const prefixColor$1 = color => {
715 const regex = new RegExp(`^${re$2.colorhex}$`);
716 return (regex.test(color) ? '#' : '') + color;
717};
718
719const definedProps$1 = obj => Object.fromEntries(Object.entries(obj).filter(([k, v]) => v !== undefined));
720/**
721 * Function to parse style string to object
722 * @function style.parse
723 * @param {string} styleString - a style string
724 * @returns {StyleObject} elements of style string
725 * @example
726 * style.parse('-CP10G_blue_D_red,Cyan_')
727 *
728 * return {
729 * 'colorize': true,
730 * 'padding': 10,
731 * 'background': 'blue',
732 * 'detail': ['red', 'Cyan']
733 * }
734 */
735
736
737const parse$1$1 = styleString => {
738 const regex = `^${re$2.full}`;
739 const m = (typeof styleString === 'string' ? styleString.match(new RegExp(regex)) : []) || [];
740 return definedProps$1({
741 'colorize': !m[1] ? undefined : !!m[1],
742 'padding': !m[2] ? undefined : parseInt(m[2].slice(1)),
743 'background': !m[3] ? undefined : prefixColor$1(m[3].slice(2, -1)),
744 'detail': !m[4] ? undefined : m[4].slice(2, -1).split(',').map(prefixColor$1),
745 'zoom': !m[5] ? undefined : m[5] === 'Zx' ? 'x' : parseFloat(m[5].slice(1)),
746 'detailsym': !m[6] ? undefined : m[6].match(new RegExp(re$2.detailsym, 'g')).map(val => {
747 const parts = val.split('_');
748 const detail = parts[1].split(',').map(prefixColor$1);
749 return {
750 'index': parseInt(parts[0].slice(1)),
751 'detail': detail
752 };
753 }),
754 'classes': !m[7] ? undefined : m[7],
755 'id': !m[8] ? undefined : m[8]
756 });
757};
758/** 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.
759 * [Characters set definitions](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-09.html#name-characters)
760 * @module convert
761 */
762
763/**
764 * Function to convert an FSW coordinate string to an array of x,y integers
765 * @function convert.fsw2coord
766 * @param {string} fswCoord - An FSW coordinate string
767 * @returns {number[]} Array of x,y integers
768 * @example
769 * convert.fsw2coord('500x500')
770 *
771 * return [500, 500]
772 */
773
774
775const fsw2coord = fswCoord => fswCoord.split('x').map(num => parseInt(num));
776
777const parse$2 = {
778 /**
779 * Function to parse an fsw symbol with optional coordinate and style string
780 * @function fsw.parse.symbol
781 * @param {string} fswSym - an fsw symbol
782 * @returns {object} elements of fsw symbol
783 * @example
784 * fsw.parse.symbol('S10000500x500-C')
785 *
786 * return {
787 * 'symbol': 'S10000',
788 * 'coord': [500, 500],
789 * 'style': '-C'
790 * }
791 */
792 symbol: fswSym => {
793 const regex = `^(${re$1$1.symbol})(${re$1$1.coord})?(${re$2.full})?`;
794 const symbol = typeof fswSym === 'string' ? fswSym.match(new RegExp(regex)) : undefined;
795 return {
796 'symbol': symbol ? symbol[1] : undefined,
797 'coord': symbol && symbol[2] ? fsw2coord(symbol[2]) : undefined,
798 'style': symbol ? symbol[3] : undefined
799 };
800 },
801
802 /**
803 * Function to parse an fsw sign with style string
804 * @function fsw.parse.sign
805 * @param {string} fswSign - an fsw sign
806 * @returns {object} elements of fsw sign
807 * @example
808 * fsw.parse.sign('AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475-C')
809 *
810 * return {
811 * sequence: ['S10011', 'S10019', 'S2e704', 'S2e748'],
812 * box: 'M',
813 * max: [525, 535],
814 * spatials: [
815 * {
816 * symbol: 'S2e748',
817 * coord: [483, 510]
818 * },
819 * {
820 * symbol: 'S10011',
821 * coord: [501, 466]
822 * },
823 * {
824 * symbol: 'S2e704',
825 * coord: [510, 500]
826 * },
827 * {
828 * symbol: 'S10019',
829 * coord: [476, 475]
830 * }
831 * ],
832 * style: '-C'
833 * }
834 */
835 sign: fswSign => {
836 const regex = `^(${re$1$1.prefix})?(${re$1$1.signbox})(${re$2.full})?`;
837 const sign = typeof fswSign === 'string' ? fswSign.match(new RegExp(regex)) : undefined;
838
839 if (sign) {
840 return {
841 'sequence': sign[1] ? sign[1].slice(1).match(/.{6}/g) : undefined,
842 'box': sign[2][0],
843 'max': fsw2coord(sign[2].slice(1, 8)),
844 'spatials': sign[2].length < 9 ? undefined : sign[2].slice(8).match(/(.{13})/g).map(m => {
845 return {
846 symbol: m.slice(0, 6),
847 coord: [parseInt(m.slice(6, 9)), parseInt(m.slice(10, 13))]
848 };
849 }),
850 'style': sign[3]
851 };
852 } else {
853 return {};
854 }
855 },
856
857 /**
858 * Function to parse an fsw text
859 * @function fsw.parse.text
860 * @param {string} fswText - an fsw text
861 * @returns {array} fsw signs and punctuations
862 * @example
863 * fsw.parse.text('AS14c20S27106M518x529S14c20481x471S27106503x489 AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468 S38800464x496')
864 *
865 * return [
866 * 'AS14c20S27106M518x529S14c20481x471S27106503x489',
867 * 'AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468',
868 * 'S38800464x496'
869 * ]
870 */
871 text: fswText => {
872 if (typeof fswText !== 'string') return [];
873 const regex = `(${re$1$1.sign}(${re$2.full})?|${re$1$1.spatial}(${re$2.full})?)`;
874 const matches = fswText.match(new RegExp(regex, 'g'));
875 return matches ? [...matches] : [];
876 }
877};
878/**
879 * Function to gather sizing information about an fsw sign or symbol
880 * @function fsw.info
881 * @param {string} fsw - an fsw sign or symbol
882 * @returns {object} information about the fsw string
883 * @example
884 * fsw.info('AS14c20S27106L518x529S14c20481x471S27106503x489-P10Z2')
885 *
886 * return {
887 * minX: 481,
888 * minY: 471,
889 * width: 37,
890 * height: 58,
891 * zoom: 2,
892 * padding: 10,
893 * segment: 'sign',
894 * lane: -1
895 * }
896 */
897
898const info$1 = fsw => {
899 let lanes = {
900 "B": 0,
901 "L": -1,
902 "M": 0,
903 "R": 1
904 };
905 let parsed = parse$2.sign(fsw);
906 let width, height, segment, x1, x2, y1, y2, lane;
907
908 if (parsed.spatials) {
909 x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
910 x2 = parsed.max[0];
911 width = x2 - x1;
912 y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
913 y2 = parsed.max[1];
914 height = y2 - y1;
915 segment = 'sign';
916 lane = parsed.box;
917 } else {
918 parsed = parse$2.symbol(fsw);
919 lane = "M";
920
921 if (parsed.coord) {
922 x1 = parsed.coord[0];
923 width = (500 - x1) * 2;
924 y1 = parsed.coord[1];
925 height = (500 - y1) * 2;
926 segment = 'symbol';
927 } else {
928 x1 = 490;
929 width = 20;
930 y1 = 490;
931 height = 20;
932 segment = 'none';
933 }
934 }
935
936 let style = parse$1$1(parsed.style);
937 let zoom = style.zoom || 1;
938 let padding = style.padding || 0;
939 return {
940 minX: x1,
941 minY: y1,
942 width: width,
943 height: height,
944 segment: segment,
945 lane: lanes[lane],
946 padding: padding,
947 zoom: zoom
948 };
949};
950
951const columnDefaults$1 = {
952 'height': 500,
953 'width': 150,
954 'offset': 50,
955 'pad': 20,
956 'margin': 5,
957 'dynamic': false,
958 'background': undefined,
959 'punctuation': {
960 'spacing': true,
961 'pad': 30,
962 'pull': true
963 },
964 'style': {
965 'detail': ['black', 'white'],
966 'zoom': 1
967 }
968};
969/**
970 * Function to an object of column options with default values
971 *
972 * @function fsw.columnDefaultsMerge
973 * @param {ColumnOptions} options - object of column options
974 * @returns {ColumnOptions} object of column options merged with column defaults
975 * @example
976 * fsw.columnDefaultsMerge({height: 500,width:150})
977 *
978 * return {
979 * "height": 500,
980 * "width": 150,
981 * "offset": 50,
982 * "pad": 20,
983 * "margin": 5,
984 * "dynamic": false,
985 * "punctuation": {
986 * "spacing": true,
987 * "pad": 30,
988 * "pull": true
989 * },
990 * "style": {
991 * "detail": [
992 * "black",
993 * "white"
994 * ],
995 * "zoom": 1
996 * }
997 * }
998 */
999
1000const columnDefaultsMerge$1 = options => {
1001 if (typeof options !== 'object') options = {};
1002 return { ...columnDefaults$1,
1003 ...options,
1004 punctuation: { ...columnDefaults$1.punctuation,
1005 ...options.punctuation
1006 },
1007 style: { ...columnDefaults$1.style,
1008 ...options.style
1009 }
1010 };
1011};
1012/**
1013 * Function to transform an FSW text to an array of columns
1014 *
1015 * @function fsw.columns
1016 * @param {string} fswText - FSW text of signs and punctuation
1017 * @param {ColumnOptions} options - object of column options
1018 * @returns {{options:ColumnOptions,widths:number[],columns:ColumnData}} object of column options, widths array, and column data
1019 * @example
1020 * fsw.columns('AS14c20S27106M518x529S14c20481x471S27106503x489 AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468 S38800464x496', {height: 500,width:150})
1021 *
1022 * return {
1023 * "options": {
1024 * "height": 500,
1025 * "width": 150,
1026 * "offset": 50,
1027 * "pad": 20,
1028 * "margin": 5,
1029 * "dynamic": false,
1030 * "punctuation": {
1031 * "spacing": true,
1032 * "pad": 30,
1033 * "pull": true
1034 * },
1035 * "style": {
1036 * "detail": [
1037 * "black",
1038 * "white"
1039 * ],
1040 * "zoom": 1
1041 * }
1042 * },
1043 * "widths": [
1044 * 150
1045 * ],
1046 * "columns": [
1047 * [
1048 * {
1049 * "x": 56,
1050 * "y": 20,
1051 * "minX": 481,
1052 * "minY": 471,
1053 * "width": 37,
1054 * "height": 58,
1055 * "lane": 0,
1056 * "padding": 0,
1057 * "segment": "sign",
1058 * "text": "AS14c20S27106M518x529S14c20481x471S27106503x489",
1059 * "zoom": 1
1060 * },
1061 * {
1062 * "x": 57,
1063 * "y": 118,
1064 * "minX": 482,
1065 * "minY": 468,
1066 * "width": 36,
1067 * "height": 65,
1068 * "lane": 0,
1069 * "padding": 0,
1070 * "segment": "sign",
1071 * "text": "AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468",
1072 * "zoom": 1
1073 * },
1074 * {
1075 * "x": 39,
1076 * "y": 203,
1077 * "minX": 464,
1078 * "minY": 496,
1079 * "width": 72,
1080 * "height": 8,
1081 * "lane": 0,
1082 * "padding": 0,
1083 * "segment": "symbol",
1084 * "text": "S38800464x496",
1085 * "zoom": 1
1086 * }
1087 * ]
1088 * ]
1089 * }
1090 */
1091
1092
1093const columns$1 = (fswText, options) => {
1094 if (typeof fswText !== 'string') return {};
1095 const values = columnDefaultsMerge$1(options);
1096 let input = parse$2.text(fswText);
1097 let cursor = 0;
1098 let cols = [];
1099 let col = [];
1100 let plus = 0;
1101 let center = parseInt(values.width / 2);
1102 let maxHeight = values.height - values.margin;
1103 let pullable = true;
1104 let finalize = false;
1105
1106 for (let val of input) {
1107 let informed = info$1(val);
1108 cursor += plus;
1109
1110 if (values.punctuation.spacing) {
1111 cursor += informed.segment == 'sign' ? values.pad : 0;
1112 } else {
1113 cursor += values.pad;
1114 }
1115
1116 finalize = cursor + informed.height > maxHeight;
1117
1118 if (finalize && informed.segment == 'symbol' && values.punctuation.pull && pullable) {
1119 finalize = false;
1120 pullable = false;
1121 }
1122
1123 if (col.length == 0) {
1124 finalize = false;
1125 }
1126
1127 if (finalize) {
1128 cursor = values.pad;
1129 cols.push(col);
1130 col = [];
1131 pullable = true;
1132 }
1133
1134 col.push(Object.assign(informed, {
1135 x: center + values.offset * informed.lane - (500 - informed.minX) * informed.zoom * values.style.zoom,
1136 y: cursor,
1137 text: val
1138 }));
1139 cursor += informed.height * informed.zoom * values.style.zoom;
1140
1141 if (values.punctuation.spacing) {
1142 plus = informed.segment == 'sign' ? values.pad : values.punctuation.pad;
1143 } else {
1144 plus = values.pad;
1145 }
1146 }
1147
1148 if (col.length) {
1149 cols.push(col);
1150 } // over height issue when pulling punctuation
1151
1152
1153 if (values.punctuation.pull) {
1154 for (let col of cols) {
1155 let last = col[col.length - 1];
1156 let diff = last.y + last.height - (values.height - values.margin);
1157
1158 if (diff > 0) {
1159 let adj = parseInt(diff / col.length) + 1;
1160
1161 for (let i in col) {
1162 col[i].y -= adj * i + adj;
1163 }
1164 }
1165 }
1166 } // contract, expand, adjust
1167
1168
1169 let widths = [];
1170
1171 for (let col of cols) {
1172 let min = [center - values.offset - values.pad];
1173 let max = [center + values.offset + values.pad];
1174
1175 for (let item of col) {
1176 min.push(item.x - values.pad);
1177 max.push(item.x + item.width + values.pad);
1178 }
1179
1180 min = Math.min(...min);
1181 max = Math.max(...max);
1182 let width = values.width;
1183 let adj = 0;
1184
1185 if (!values.dynamic) {
1186 adj = center - parseInt((min + max) / 2);
1187 } else {
1188 width = max - min;
1189 adj = -min;
1190 }
1191
1192 for (let item of col) {
1193 item.x += adj;
1194 }
1195
1196 widths.push(width);
1197 }
1198
1199 return {
1200 'options': values,
1201 'widths': widths,
1202 'columns': cols
1203 };
1204};
1205/**
1206 * Array of numbers for categories of symbols: hand, movement, dynamics, head, trunk & limb, location, and punctuation.
1207 * @alias fsw.category
1208 * @type {array}
1209 */
1210
1211const category$1 = [0x100, 0x205, 0x2f7, 0x2ff, 0x36d, 0x37f, 0x387];
1212/**
1213 * Object of symbol ranges with starting and ending numbers.
1214 *
1215 * { all, writing, hand, movement, dynamic, head, hcenter, vcenter, trunk, limb, location, punctuation }
1216 * @alias fsw.ranges
1217 * @type {object}
1218 */
1219
1220const ranges$1 = {
1221 'all': [0x100, 0x38b],
1222 'writing': [0x100, 0x37e],
1223 'hand': [0x100, 0x204],
1224 'movement': [0x205, 0x2f6],
1225 'dynamic': [0x2f7, 0x2fe],
1226 'head': [0x2ff, 0x36c],
1227 'hcenter': [0x2ff, 0x36c],
1228 'vcenter': [0x2ff, 0x375],
1229 'trunk': [0x36d, 0x375],
1230 'limb': [0x376, 0x37e],
1231 'location': [0x37f, 0x386],
1232 'punctuation': [0x387, 0x38b]
1233};
1234/**
1235 * Array of colors associated with the seven symbol categories.
1236 * @alias fsw.colors
1237 * @type {array}
1238 */
1239
1240
1241const colors$1 = ['#0000CC', '#CC0000', '#FF0099', '#006600', '#000000', '#884411', '#FF9900'];
1242/**
1243 * Function that returns the standardized color for a symbol.
1244 * @function fsw.colorize
1245 * @param {string} key - an FSW symbol key
1246 * @returns {string} name of standardized color for symbol
1247 * @example
1248 * fsw.colorize('S10000')
1249 *
1250 * return '#0000CC'
1251 */
1252
1253const colorize$1 = key => {
1254 const parsed = parse$2.symbol(key);
1255 let color = '#000000';
1256
1257 if (parsed.symbol) {
1258 const dec = parseInt(parsed.symbol.slice(1, 4), 16);
1259 const index = category$1.findIndex(val => val > dec);
1260 color = colors$1[index < 0 ? 6 : index - 1];
1261 }
1262
1263 return color;
1264};
1265/* support ongoing development on https://patreon.com/signwriting */
1266
1267/**
1268 * Function that creates an SVG image from an FSW symbol key with an optional style string
1269 * @function fsw.symbolSvgBody
1270 * @param {string} fswSym - an FSW symbol key with optional style string
1271 * @example
1272 * fsw.symbolSvgBody('S10000')
1273 *
1274 * return `<text font-size="0">S10000</text>
1275 * <g transform="translate(500,500)">
1276 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀁</text>
1277 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀁</text>
1278 * </g>`
1279 */
1280
1281const symbolSvgBody$1 = fswSym => {
1282 const parsed = parse$2.symbol(fswSym);
1283 const blank = '';
1284 if (!parsed.symbol) return blank;
1285 let styling = parse$3(parsed.style);
1286 let x1, y1, x2, y2;
1287
1288 if (parsed.coord) {
1289 x1 = parsed.coord[0];
1290 y1 = parsed.coord[1];
1291 x2 = 500 + (500 - x1);
1292 y2 = 500 + (500 - y1);
1293 } else {
1294 let size = symbolSize$1(parsed.symbol);
1295 if (!size) return blank;
1296 x1 = 500 - parseInt((size[0] + 1) / 2);
1297 y1 = 500 - parseInt((size[1] + 1) / 2);
1298 x2 = 500 + (500 - x1);
1299 y2 = 500 + (500 - y1);
1300 }
1301
1302 let symSvg = symbolText$1(parsed.symbol);
1303 symSvg = ` <g transform="translate(${x1},${y1})">
1304${symSvg}
1305 </g>`;
1306 let line;
1307
1308 if (styling.colorize) {
1309 line = colorize$1(parsed.symbol);
1310 } else if (styling.detail) {
1311 line = styling.detail[0];
1312 }
1313
1314 if (line) {
1315 symSvg = symSvg.replace(/class="sym-line" fill="black"/, `class="sym-line" fill="${line}"`);
1316 }
1317
1318 let fill = styling.detail && styling.detail[1];
1319
1320 if (fill) {
1321 symSvg = symSvg.replace(/class="sym-fill" fill="white"/, `class="sym-fill" fill="${fill}"`);
1322 }
1323
1324 let background = '';
1325
1326 if (styling.padding) {
1327 x1 -= styling.padding;
1328 y1 -= styling.padding;
1329 x2 += styling.padding;
1330 y2 += styling.padding;
1331 }
1332
1333 if (styling.background) {
1334 background = `\n <rect x="${x1}" y="${y1}" width="${x2 - x1}" height="${y2 - y1}" style="fill:${styling.background};" />`;
1335 }
1336
1337 return ` <text font-size="0">${fswSym}</text>${background}
1338${symSvg}`;
1339};
1340/**
1341 * Function that creates an SVG image from an FSW symbol key with an optional style string
1342 * @function fsw.symbolSvg
1343 * @param {string} fswSym - an FSW symbol key with optional style string
1344 * @example
1345 * fsw.symbolSvg('S10000')
1346 *
1347 * return `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="15" height="30" viewBox="500 500 15 30">
1348 * <text font-size="0">S10000</text>
1349 * <g transform="translate(500,500)">
1350 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀁</text>
1351 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀁</text>
1352 * </g>
1353 * </svg>`
1354 */
1355
1356
1357const symbolSvg$1 = fswSym => {
1358 const parsed = parse$2.symbol(fswSym);
1359 const blank = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="1" height="1"></svg>';
1360 if (!parsed.symbol) return blank;
1361 let styling = parse$3(parsed.style);
1362 let x1, y1, x2, y2;
1363
1364 if (parsed.coord) {
1365 x1 = parsed.coord[0];
1366 y1 = parsed.coord[1];
1367 x2 = 500 + (500 - x1);
1368 y2 = 500 + (500 - y1);
1369 } else {
1370 let size = symbolSize$1(parsed.symbol);
1371 if (!size) return blank;
1372 x1 = parseInt(500 - size[0] / 2);
1373 y1 = parseInt(500 - size[1] / 2);
1374 x2 = x1 + size[0];
1375 y2 = y1 + size[1];
1376 }
1377
1378 let classes = '';
1379
1380 if (styling.classes) {
1381 classes = ` class="${styling.classes}"`;
1382 }
1383
1384 let id = '';
1385
1386 if (styling.id) {
1387 id = ` id="${styling.id}"`;
1388 }
1389
1390 if (styling.padding) {
1391 x1 -= styling.padding;
1392 y1 -= styling.padding;
1393 x2 += styling.padding;
1394 y2 += styling.padding;
1395 }
1396
1397 let sizing = '';
1398
1399 if (styling.zoom != 'x') {
1400 sizing = ` width="${(x2 - x1) * (styling.zoom ? styling.zoom : 1)}" height="${(y2 - y1) * (styling.zoom ? styling.zoom : 1)}"`;
1401 }
1402
1403 return `<svg${classes}${id} version="1.1" xmlns="http://www.w3.org/2000/svg"${sizing} viewBox="${x1} ${y1} ${x2 - x1} ${y2 - y1}">
1404${symbolSvgBody$1(fswSym)}
1405</svg>`;
1406};
1407
1408const symbolCanvas$1 = function (fswSym) {
1409 const parsed = parse$2.symbol(fswSym);
1410
1411 if (parsed.symbol) {
1412 let size = symbolSize$1(parsed.symbol);
1413
1414 if (size) {
1415 const canvas = document.createElement('canvas');
1416 const context = canvas.getContext('2d');
1417 let styling = parse$3(parsed.style);
1418 let line = 'black';
1419
1420 if (styling.colorize) {
1421 line = colorize$1(parsed.symbol);
1422 } else if (styling.detail) {
1423 line = styling.detail[0];
1424 }
1425
1426 let fill = styling.detail && styling.detail[1] || 'white';
1427 let x1 = 500;
1428 let x2 = x1 + size[0];
1429 let y1 = 500;
1430 let y2 = y1 + size[1];
1431
1432 if (styling.padding) {
1433 x1 -= styling.padding;
1434 y1 -= styling.padding;
1435 x2 += styling.padding;
1436 y2 += styling.padding;
1437 }
1438
1439 let sizing = 1;
1440
1441 if (styling.zoom != 'x') {
1442 sizing = styling.zoom;
1443 }
1444
1445 let w = (x2 - x1) * sizing;
1446 let h = (y2 - y1) * sizing;
1447 canvas.width = w ? w : 1;
1448 canvas.height = h ? h : 1;
1449
1450 if (styling.background) {
1451 context.rect(0, 0, w, h);
1452 context.fillStyle = styling.background;
1453 context.fill();
1454 }
1455
1456 context.font = 30 * sizing + "px 'SuttonSignWritingFill'";
1457 context.fillStyle = fill;
1458 context.fillText(symbolFill$1(parsed.symbol), (500 - x1) * sizing, (500 - y1) * sizing);
1459 context.font = 30 * sizing + "px 'SuttonSignWritingLine'";
1460 context.fillStyle = line;
1461 context.fillText(symbolLine$1(parsed.symbol), (500 - x1) * sizing, (500 - y1) * sizing);
1462 return canvas;
1463 }
1464 }
1465};
1466/**
1467 * Function that creates a PNG data url from an FSW symbol key with an optional style string
1468 * @function fsw.symbolPng
1469 * @param {string} fswSym - an FSW symbol key with optional style string
1470 * @example
1471 * fsw.symbolPng('S10000')
1472 *
1473 * return 'data:image/png;base64,iVBORw...'
1474 */
1475
1476
1477const symbolPng$1 = fswSym => {
1478 const canvas = symbolCanvas$1(fswSym);
1479 const png = canvas.toDataURL("image/png");
1480 canvas.remove();
1481 return png;
1482};
1483
1484const blank$1 = null;
1485/**
1486 * Function that normalizes a symbol with a minimum coordinate for a center of 500,500
1487 * @function fsw.symbolNormalize
1488 * @param {string} fswSym - an FSW symbol key with optional coordinate and style string
1489 * @example
1490 * fsw.symbolNormalize('S10000-CP10G_green_Z2')
1491 *
1492 * return 'S10000493x485-CP10G_green_Z2'
1493 */
1494
1495const symbolNormalize$1 = fswSym => {
1496 const parsed = parse$2.symbol(fswSym);
1497
1498 if (parsed.symbol) {
1499 let size = symbolSize$1(parsed.symbol);
1500
1501 if (size) {
1502 return `${parsed.symbol}${500 - parseInt((size[0] + 1) / 2)}x${500 - parseInt((size[1] + 1) / 2)}${parsed.style || ''}`;
1503 }
1504 } else {
1505 return blank$1;
1506 }
1507};
1508
1509/**
1510 * Function that creates an SVG image from an FSW sign with an optional style string
1511 * @function fsw.signSvgBody
1512 * @param {string} fswSign - an FSW sign with optional style string
1513 * @example
1514 * fsw.signSvgBody('M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475')
1515 *
1516 * return `<text font-size="0">M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475</text>
1517 * <g transform="translate(483,510)">
1518 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􋛩</text>
1519 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󻛩</text>
1520 * </g>
1521 * <g transform="translate(501,466)">
1522 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀒</text>
1523 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀒</text>
1524 * </g>
1525 * <g transform="translate(510,500)">
1526 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􋚥</text>
1527 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󻚥</text>
1528 * </g>
1529 * <g transform="translate(476,475)">
1530 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀚</text>
1531 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀚</text>
1532 * </g>`
1533 */
1534
1535const signSvgBody$1 = fswSign => {
1536 let parsed = parse$2.sign(fswSign);
1537 const blank = '';
1538
1539 if (parsed.spatials) {
1540 let styling = parse$3(parsed.style);
1541
1542 if (styling.detailsym) {
1543 styling.detailsym.forEach(sym => {
1544 if (parsed.spatials[sym.index - 1]) {
1545 parsed.spatials[sym.index - 1].detail = sym.detail;
1546 }
1547 });
1548 }
1549
1550 let x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
1551 let y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
1552 let x2 = parsed.max[0];
1553 let y2 = parsed.max[1];
1554 let background = '';
1555
1556 if (styling.padding) {
1557 x1 -= styling.padding;
1558 y1 -= styling.padding;
1559 x2 += styling.padding;
1560 y2 += styling.padding;
1561 }
1562
1563 if (styling.background) {
1564 background = `\n <rect x="${x1}" y="${y1}" width="${x2 - x1}" height="${y2 - y1}" style="fill:${styling.background};" />`;
1565 }
1566
1567 let svg = ` <text font-size="0">${fswSign}</text>${background}`;
1568 const line = styling.detail && styling.detail[0];
1569 const fill = styling.detail && styling.detail[1];
1570 svg += '\n' + parsed.spatials.map(spatial => {
1571 let svg = symbolText$1(spatial.symbol);
1572 let symLine = line;
1573
1574 if (spatial.detail) {
1575 symLine = spatial.detail[0];
1576 } else if (styling.colorize) {
1577 symLine = colorize$1(spatial.symbol);
1578 }
1579
1580 if (symLine) {
1581 svg = svg.replace(/class="sym-line" fill="black"/, `class="sym-line" fill="${symLine}"`);
1582 }
1583
1584 let symFill = fill;
1585
1586 if (spatial.detail && spatial.detail[1]) {
1587 symFill = spatial.detail[1];
1588 }
1589
1590 if (symFill) {
1591 svg = svg.replace(/class="sym-fill" fill="white"/, `class="sym-fill" fill="${symFill}"`);
1592 }
1593
1594 return ` <g transform="translate(${spatial.coord[0]},${spatial.coord[1]})">
1595${svg}
1596 </g>`;
1597 }).join('\n');
1598 return svg;
1599 }
1600
1601 return blank;
1602};
1603/**
1604 * Function that creates an SVG image from an FSW sign with an optional style string
1605 * @function fsw.signSvg
1606 * @param {string} fswSign - an FSW sign with optional style string
1607 * @example
1608 * fsw.signSvg('M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475')
1609 *
1610 * return `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="49" height="69" viewBox="476 466 49 69">
1611 * <text font-size="0">M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475</text>
1612 * <g transform="translate(483,510)">
1613 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􋛩</text>
1614 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󻛩</text>
1615 * </g>
1616 * <g transform="translate(501,466)">
1617 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀒</text>
1618 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀒</text>
1619 * </g>
1620 * <g transform="translate(510,500)">
1621 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􋚥</text>
1622 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󻚥</text>
1623 * </g>
1624 * <g transform="translate(476,475)">
1625 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀚</text>
1626 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀚</text>
1627 * </g>
1628 * </svg>`
1629 */
1630
1631
1632const signSvg$1 = fswSign => {
1633 let parsed = parse$2.sign(fswSign);
1634 const blank = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="1" height="1"></svg>';
1635
1636 if (parsed.spatials) {
1637 let styling = parse$3(parsed.style);
1638 let x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
1639 let y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
1640 let x2 = parsed.max[0];
1641 let y2 = parsed.max[1];
1642 let classes = '';
1643
1644 if (styling.classes) {
1645 classes = ` class="${styling.classes}"`;
1646 }
1647
1648 let id = '';
1649
1650 if (styling.id) {
1651 id = ` id="${styling.id}"`;
1652 }
1653
1654 if (styling.padding) {
1655 x1 -= styling.padding;
1656 y1 -= styling.padding;
1657 x2 += styling.padding;
1658 y2 += styling.padding;
1659 }
1660
1661 let sizing = '';
1662
1663 if (styling.zoom != 'x') {
1664 sizing = ` width="${(x2 - x1) * (styling.zoom ? styling.zoom : 1)}" height="${(y2 - y1) * (styling.zoom ? styling.zoom : 1)}"`;
1665 }
1666
1667 let svg = `<svg${classes}${id} version="1.1" xmlns="http://www.w3.org/2000/svg"${sizing} viewBox="${x1} ${y1} ${x2 - x1} ${y2 - y1}">
1668`;
1669 svg += signSvgBody$1(fswSign);
1670 svg += '\n</svg>';
1671 return svg;
1672 }
1673
1674 return blank;
1675};
1676
1677const signCanvas$1 = function (fswSign) {
1678 const parsed = parse$2.sign(fswSign);
1679
1680 if (parsed.spatials) {
1681 const canvas = document.createElement('canvas');
1682 const context = canvas.getContext('2d');
1683 let styling = parse$3(parsed.style);
1684
1685 if (styling.detailsym) {
1686 styling.detailsym.forEach(sym => {
1687 if (parsed.spatials[sym.index - 1]) {
1688 parsed.spatials[sym.index - 1].detail = sym.detail;
1689 }
1690 });
1691 }
1692
1693 let x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
1694 let y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
1695 let x2 = parsed.max[0];
1696 let y2 = parsed.max[1];
1697
1698 if (styling.padding) {
1699 x1 -= styling.padding;
1700 y1 -= styling.padding;
1701 x2 += styling.padding;
1702 y2 += styling.padding;
1703 }
1704
1705 let sizing = 1;
1706
1707 if (styling.zoom != 'x') {
1708 sizing = styling.zoom;
1709 }
1710
1711 let w = (x2 - x1) * sizing;
1712 let h = (y2 - y1) * sizing;
1713 canvas.width = w ? w : 1;
1714 canvas.height = h ? h : 1;
1715
1716 if (styling.background) {
1717 context.rect(0, 0, w, h);
1718 context.fillStyle = styling.background;
1719 context.fill();
1720 }
1721
1722 const line = styling.detail && styling.detail[0] || "black";
1723 const fill = styling.detail && styling.detail[1] || "white";
1724 parsed.spatials.forEach(spatial => {
1725 let symLine = line;
1726
1727 if (spatial.detail) {
1728 symLine = spatial.detail[0];
1729 } else if (styling.colorize) {
1730 symLine = colorize$1(spatial.symbol);
1731 }
1732
1733 let symFill = fill;
1734
1735 if (spatial.detail && spatial.detail[1]) {
1736 symFill = spatial.detail[1];
1737 }
1738
1739 context.font = 30 * sizing + "px 'SuttonSignWritingFill'";
1740 context.fillStyle = symFill;
1741 context.fillText(symbolFill$1(spatial.symbol), (spatial.coord[0] - x1) * sizing, (spatial.coord[1] - y1) * sizing);
1742 context.font = 30 * sizing + "px 'SuttonSignWritingLine'";
1743 context.fillStyle = symLine;
1744 context.fillText(symbolLine$1(spatial.symbol), (spatial.coord[0] - x1) * sizing, (spatial.coord[1] - y1) * sizing);
1745 });
1746 return canvas;
1747 }
1748};
1749/**
1750 * Function that creates a PNG data url from an FSW sign with an optional style string
1751 * @function fsw.signPng
1752 * @param {string} fswSign - an FSW sign with optional style string
1753 * @example
1754 * fsw.signPng('M525x535S2e748483x510S10011501x466S20544510x500S10019476x475')
1755 *
1756 * return 'data:image/png;base64,iVBORw...'
1757 */
1758
1759
1760const signPng$1 = fswSign => {
1761 const canvas = signCanvas$1(fswSign);
1762 const png = canvas.toDataURL("image/png");
1763 canvas.remove();
1764 return png;
1765};
1766
1767/**
1768 * Function that normalizes an FSW sign for a center of 500,500
1769 * @function fsw.signNormalize
1770 * @param {string} fswSign - an FSW sign with optional style string
1771 * @example
1772 * fsw.signNormalize('M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475')
1773 *
1774 * return 'M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475'
1775 */
1776
1777const signNormalize$1 = fswSign => {
1778 const parsed = parse$2.sign(fswSign);
1779
1780 if (parsed.spatials) {
1781 const symbolsizes = parsed.spatials.reduce((output, spatial) => {
1782 const size = symbolSize$1(spatial.symbol);
1783 output[spatial.symbol] = {
1784 width: size[0],
1785 height: size[1]
1786 };
1787 return output;
1788 }, {});
1789
1790 const bbox = symbols => {
1791 const x1 = Math.min(...symbols.map(spatial => spatial.coord[0]));
1792 const y1 = Math.min(...symbols.map(spatial => spatial.coord[1]));
1793 const x2 = Math.max(...symbols.map(spatial => spatial.coord[0] + parseInt(symbolsizes[spatial.symbol].width)));
1794 const y2 = Math.max(...symbols.map(spatial => spatial.coord[1] + parseInt(symbolsizes[spatial.symbol].height)));
1795 return {
1796 x1: x1,
1797 y1: y1,
1798 x2: x2,
1799 y2: y2
1800 };
1801 };
1802
1803 const hrange = ranges$1['hcenter'];
1804 const hsyms = parsed.spatials.filter(spatial => {
1805 const dec = parseInt(spatial.symbol.slice(1, 4), 16);
1806 return hrange[0] <= dec && hrange[1] >= dec;
1807 });
1808 const vrange = ranges$1['vcenter'];
1809 const vsyms = parsed.spatials.filter(spatial => {
1810 const dec = parseInt(spatial.symbol.slice(1, 4), 16);
1811 return vrange[0] <= dec && vrange[1] >= dec;
1812 });
1813 let abox = bbox(parsed.spatials);
1814 let max = [abox.x2, abox.y2];
1815
1816 if (hsyms.length) {
1817 const hbox = bbox(hsyms);
1818 abox.x1 = hbox.x1;
1819 abox.x2 = hbox.x2;
1820 }
1821
1822 if (vsyms.length) {
1823 const vbox = bbox(vsyms);
1824 abox.y1 = vbox.y1;
1825 abox.y2 = vbox.y2;
1826 }
1827
1828 const offset = [parseInt((abox.x2 + abox.x1) / 2) - 500, parseInt((abox.y2 + abox.y1) / 2) - 500];
1829 const fswout = (parsed.sequence ? 'A' + parsed.sequence.join('') : '') + parsed.box + (max[0] - offset[0]) + 'x' + (max[1] - offset[1]) + parsed.spatials.map(spatial => spatial.symbol + (spatial.coord[0] - offset[0]) + 'x' + (spatial.coord[1] - offset[1])).join('') + (parsed.style || '');
1830 return fswout;
1831 }
1832};
1833
1834/**
1835 * Function that creates an SVG image for a column of FSW
1836 * @function fsw.columnSvg
1837 * @param {ColumnData} fswColumn - an array of objects with information about FSW signs and punctuation
1838 * @param {ColumnOptions} options - an object of column options
1839 * @returns {string} column svg
1840 * @example
1841 * fsw.columnSvg([
1842 * {
1843 * "x": 56,
1844 * "y": 20,
1845 * "minX": 481,
1846 * "minY": 471,
1847 * "width": 37,
1848 * "height": 58,
1849 * "lane": 0,
1850 * "padding": 0,
1851 * "segment": "sign",
1852 * "text": "AS14c20S27106M518x529S14c20481x471S27106503x489",
1853 * "zoom": 1
1854 * },
1855 * {
1856 * "x": 57,
1857 * "y": 118,
1858 * "minX": 482,
1859 * "minY": 468,
1860 * "width": 36,
1861 * "height": 65,
1862 * "lane": 0,
1863 * "padding": 0,
1864 * "segment": "sign",
1865 * "text": "AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468",
1866 * "zoom": 1
1867 * },
1868 * {
1869 * "x": 39,
1870 * "y": 203,
1871 * "minX": 464,
1872 * "minY": 496,
1873 * "width": 72,
1874 * "height": 8,
1875 * "lane": 0,
1876 * "padding": 0,
1877 * "segment": "symbol",
1878 * "text": "S38800464x496",
1879 * "zoom": 1
1880 * }
1881 * ],
1882 * {
1883 * "height": 250,
1884 * "width": 150,
1885 * })
1886 *
1887 * return `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="150" height="250" viewBox="0 0 150 250">
1888 * <g transform="translate(56,20) scale(1) translate(-481,-471) ">
1889 * <text font-size="0">AS14c20S27106M518x529S14c20481x471S27106503x489-D_black,white_Z1</text>
1890 * <g transform="translate(481,471)">
1891 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􁲡</text>
1892 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󱲡</text>
1893 * </g>
1894 * <g transform="translate(503,489)">
1895 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􈩧</text>
1896 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󸩧</text>
1897 * </g>
1898 * </g>
1899 * <g transform="translate(57,118) scale(1) translate(-482,-468) ">
1900 * <text font-size="0">AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468-D_black,white_Z1</text>
1901 * <g transform="translate(489,515)">
1902 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􃊫</text>
1903 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󳊫</text>
1904 * </g>
1905 * <g transform="translate(482,490)">
1906 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􃊢</text>
1907 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󳊢</text>
1908 * </g>
1909 * <g transform="translate(508,496)">
1910 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􆇡</text>
1911 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󶇡</text>
1912 * </g>
1913 * <g transform="translate(500,468)">
1914 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􋛕</text>
1915 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󻛕</text>
1916 * </g>
1917 * </g>
1918 * <g transform="translate(39,203) scale(1) translate(-464,-496) ">
1919 * <text font-size="0">S38800464x496-D_black,white_Z1</text>
1920 * <g transform="translate(464,496)">
1921 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􏌁</text>
1922 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󿌁</text>
1923 * </g>
1924 * </g>
1925 * </svg>`
1926 */
1927
1928const columnSvg$1 = (fswColumn, options) => {
1929
1930 if (typeof options !== 'object') options = {};
1931 const values = Object.assign(columnDefaults$1, options);
1932 let x1 = 0;
1933 let y1 = 0;
1934 let x2 = values.width;
1935 let y2 = values.height;
1936 let background = '';
1937
1938 if (values.background) {
1939 background = `\n <rect x="${x1}" y="${y1}" width="${x2 - x1}" height="${y2 - y1}" style="fill:${values.background};" />`;
1940 }
1941
1942 let sizing = ` width="${values.width}" height="${values.height}"`;
1943 let svg = `<svg version="1.1" xmlns="http://www.w3.org/2000/svg"${sizing} viewBox="${x1} ${y1} ${x2 - x1} ${y2 - y1}">
1944 ${background}`;
1945 svg += fswColumn.map(item => {
1946 const dash = item.text.indexOf('-');
1947
1948 if (dash > 0) {
1949 const itemStyle = item.text.substring(dash);
1950 const newStyle = { ...values.style,
1951 ...parse$3(itemStyle)
1952 };
1953 item.text = item.text.replace(itemStyle, compose(newStyle));
1954 } else {
1955 item.text += compose(values.style);
1956 }
1957
1958 item.zoom = item.zoom * values.style.zoom;
1959 return '<g transform="translate(' + item.x + ',' + item.y + ') scale(' + item.zoom + ') translate(' + -item.minX + ',' + -item.minY + ') ">' + (item.segment == "sign" ? signSvgBody$1(item.text) : symbolSvgBody$1(item.text)) + '</g>';
1960 }).join('\n');
1961 svg += '\n</svg>';
1962 return svg;
1963};
1964
1965/**
1966 * Function that creates an array of SVG column images for an FSW text
1967 * @function fsw.columnsSvg
1968 * @param {string} fswText - a text of FSW signs and punctuation
1969 * @param {ColumnOptions} options - an object of column options
1970 * @returns {string[]} array of SVG columns
1971 * @example
1972 * fsw.columnsSvg('AS14c20S27106M518x529S14c20481x471S27106503x489 AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468 S38800464x496',{
1973 * "height": 250,
1974 * "width": 150,
1975 * })
1976 *
1977 * return [`<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="150" height="250" viewBox="0 0 150 250">
1978 * <g transform="translate(56,20) scale(1) translate(-481,-471) ">
1979 * <text font-size="0">AS14c20S27106M518x529S14c20481x471S27106503x489-D_black,white_Z1</text>
1980 * <g transform="translate(481,471)">
1981 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􁲡</text>
1982 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󱲡</text>
1983 * </g>
1984 * <g transform="translate(503,489)">
1985 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􈩧</text>
1986 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󸩧</text>
1987 * </g>
1988 * </g>
1989 * <g transform="translate(57,118) scale(1) translate(-482,-468) ">
1990 * <text font-size="0">AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468-D_black,white_Z1</text>
1991 * <g transform="translate(489,515)">
1992 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􃊫</text>
1993 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󳊫</text>
1994 * </g>
1995 * <g transform="translate(482,490)">
1996 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􃊢</text>
1997 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󳊢</text>
1998 * </g>
1999 * <g transform="translate(508,496)">
2000 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􆇡</text>
2001 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󶇡</text>
2002 * </g>
2003 * <g transform="translate(500,468)">
2004 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􋛕</text>
2005 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󻛕</text>
2006 * </g>
2007 * </g>
2008 * <g transform="translate(39,203) scale(1) translate(-464,-496) ">
2009 * <text font-size="0">S38800464x496-D_black,white_Z1</text>
2010 * <g transform="translate(464,496)">
2011 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􏌁</text>
2012 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󿌁</text>
2013 * </g>
2014 * </g>
2015 * </svg>`]
2016 */
2017
2018const columnsSvg$1 = function (fswText, options) {
2019 if (typeof options !== 'object') options = {};
2020 let values = columns$1(fswText, options);
2021 let cols = values.columns.map((col, i) => {
2022 return columnSvg$1(col, { ...values.options,
2023 ...{
2024 width: values.widths[i]
2025 }
2026 });
2027 });
2028 return cols;
2029};
2030
2031const columnCanvas$1 = function (fswColumn, options) {
2032 if (typeof options !== 'object') options = {};
2033 const values = Object.assign(columnDefaults$1, options);
2034 const canvas = document.createElement('canvas');
2035 canvas.width = values.width;
2036 canvas.height = values.height;
2037 const context = canvas.getContext('2d');
2038
2039 if (values.background) {
2040 context.rect(0, 0, values.width, values.height);
2041 context.fillStyle = values.background;
2042 context.fill();
2043 }
2044
2045 fswColumn.map(item => {
2046 const dash = item.text.indexOf('-');
2047
2048 if (dash > 0) {
2049 const itemStyle = item.text.substring(dash);
2050 const newStyle = { ...values.style,
2051 ...parse$3(itemStyle)
2052 };
2053 item.text = item.text.replace(itemStyle, compose(newStyle));
2054 } else {
2055 item.text += compose(values.style);
2056 }
2057
2058 item.zoom = item.zoom * values.style.zoom;
2059 let parsed = {};
2060
2061 if (item.segment == "sign") {
2062 parsed = parse$2.sign(item.text);
2063 } else {
2064 let sym = parse$2.symbol(item.text);
2065 parsed.style = sym.style;
2066 parsed.spatials = [sym];
2067 }
2068
2069 let styling = parse$3(parsed.style);
2070
2071 if (styling.background) {
2072 context.fillStyle = styling.background;
2073 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);
2074 }
2075
2076 if (styling.detailsym) {
2077 styling.detailsym.forEach(sym => {
2078 if (parsed.spatials[sym.index - 1]) {
2079 parsed.spatials[sym.index - 1].detail = sym.detail;
2080 }
2081 });
2082 }
2083
2084 const line = styling.detail && styling.detail[0] || "black";
2085 const fill = styling.detail && styling.detail[1] || "white";
2086 parsed.spatials.forEach(spatial => {
2087 let symLine = line;
2088
2089 if (spatial.detail) {
2090 symLine = spatial.detail[0];
2091 } else if (styling.colorize) {
2092 symLine = colorize$1(spatial.symbol);
2093 }
2094
2095 let symFill = fill;
2096
2097 if (spatial.detail && spatial.detail[1]) {
2098 symFill = spatial.detail[1];
2099 }
2100
2101 context.font = 30 * item.zoom + "px 'SuttonSignWritingFill'";
2102 context.fillStyle = symFill;
2103 context.fillText(symbolFill$1(spatial.symbol), item.x + (spatial.coord[0] - item.minX) * item.zoom, item.y + (spatial.coord[1] - item.minY) * item.zoom);
2104 context.font = 30 * item.zoom + "px 'SuttonSignWritingLine'";
2105 context.fillStyle = symLine;
2106 context.fillText(symbolLine$1(spatial.symbol), item.x + (spatial.coord[0] - item.minX) * item.zoom, item.y + (spatial.coord[1] - item.minY) * item.zoom);
2107 });
2108 });
2109 return canvas;
2110};
2111/**
2112 * Function that creates a PNG data url for a column of FSW
2113 * @function fsw.columnPng
2114 * @param {ColumnData} fswColumn - an array of objects with information about FSW signs and punctuation
2115 * @param {ColumnOptions} options - an object of column options
2116 * @returns {string} column png data url
2117 * @example
2118 * fsw.columnPng([
2119 * {
2120 * "x": 56,
2121 * "y": 20,
2122 * "minX": 481,
2123 * "minY": 471,
2124 * "width": 37,
2125 * "height": 58,
2126 * "lane": 0,
2127 * "padding": 0,
2128 * "segment": "sign",
2129 * "text": "AS14c20S27106M518x529S14c20481x471S27106503x489",
2130 * "zoom": 1
2131 * },
2132 * {
2133 * "x": 57,
2134 * "y": 118,
2135 * "minX": 482,
2136 * "minY": 468,
2137 * "width": 36,
2138 * "height": 65,
2139 * "lane": 0,
2140 * "padding": 0,
2141 * "segment": "sign",
2142 * "text": "AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468",
2143 * "zoom": 1
2144 * },
2145 * {
2146 * "x": 39,
2147 * "y": 203,
2148 * "minX": 464,
2149 * "minY": 496,
2150 * "width": 72,
2151 * "height": 8,
2152 * "lane": 0,
2153 * "padding": 0,
2154 * "segment": "symbol",
2155 * "text": "S38800464x496",
2156 * "zoom": 1
2157 * }
2158 * ],
2159 * {
2160 * "height": 250,
2161 * "width": 150,
2162 * })
2163 *
2164 * return 'data:image/png;base64,iVBORw...'
2165 */
2166
2167
2168const columnPng$1 = (fswColumn, options) => {
2169 const canvas = columnCanvas$1(fswColumn, options);
2170 const png = canvas.toDataURL("image/png");
2171 canvas.remove();
2172 return png;
2173};
2174
2175/**
2176 * Function that creates an array of PNG data urls for an FSW text
2177 * @function fsw.columnsPng
2178 * @param {string} fswText - a text of FSW signs and punctuation
2179 * @param {ColumnOptions} options - an object of column options
2180 * @returns {string[]} array of PNG data urls
2181 * @example
2182 * fsw.columnsPng('AS14c20S27106M518x529S14c20481x471S27106503x489 AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468 S38800464x496',{
2183 * "height": 250,
2184 * "width": 150,
2185 * })
2186 *
2187 * return ['data:image/png;base64,iVBORw...']
2188 */
2189
2190const columnsPng$1 = function (fswText, options) {
2191 if (typeof options !== 'object') options = {};
2192 let values = columns$1(fswText, options);
2193 let cols = values.columns.map((col, i) => {
2194 return columnPng$1(col, { ...values.options,
2195 ...{
2196 width: values.widths[i]
2197 }
2198 });
2199 });
2200 return cols;
2201};
2202
2203/** The fsw module contains functions for handling Formal SignWriitng in ASCII (FSW) characters.
2204 * [FSW characters definition](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-09.html#name-formal-signwriting-in-ascii)
2205 * @module fsw
2206 */
2207
2208var index$1 = /*#__PURE__*/Object.freeze({
2209 __proto__: null,
2210 symbolSize: symbolSize$1,
2211 symbolLine: symbolLine$1,
2212 symbolFill: symbolFill$1,
2213 symbolText: symbolText$1,
2214 symbolSvgBody: symbolSvgBody$1,
2215 symbolSvg: symbolSvg$1,
2216 symbolPng: symbolPng$1,
2217 symbolNormalize: symbolNormalize$1,
2218 signSvgBody: signSvgBody$1,
2219 signSvg: signSvg$1,
2220 signPng: signPng$1,
2221 signNormalize: signNormalize$1,
2222 columnSvg: columnSvg$1,
2223 columnsSvg: columnsSvg$1,
2224 columnPng: columnPng$1,
2225 columnsPng: columnsPng$1
2226});
2227
2228/**
2229 * Function that returns the size of a symbol using an SWU symbol character
2230 * @function swu.symbolSize
2231 * @param {string} swu - an SWU symbol character
2232 * @example
2233 * swu.symbolSize("񀀁")
2234 *
2235 * return [15,30]
2236 */
2237
2238const symbolSize = function (swu) {
2239 return symbolSize$2(swu2id(swu));
2240};
2241
2242/**
2243 * Function that returns a plane 15 character for a symbol line using an SWU symbol character
2244 * @function swu.symbolLine
2245 * @param {string} swu - an SWU symbol character
2246 * @example
2247 * swu.symbolLine('񀀁')
2248 *
2249 * return '󰀁'
2250 */
2251
2252const symbolLine = function (swu) {
2253 return symbolLine$2(swu2id(swu));
2254};
2255/**
2256 * Function that returns a plane 165 character for a symbol fill using an SWU symbol character
2257 * @function swu.symbolFill
2258 * @param {string} swu - an SWU symbol character
2259 * @example
2260 * swu.symbolFill('񀀁')
2261 *
2262 * return '􀀁'
2263 */
2264
2265
2266const symbolFill = function (swu) {
2267 return symbolFill$2(swu2id(swu));
2268};
2269/**
2270 * Function that creates two text elements for a symbol using an SWU symbol character
2271 * @function swu.symbolText
2272 * @param {string} swu - an SWU symbol character
2273 * @example
2274 * swu.symbolText('񀀁')
2275 *
2276 * return ` <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀁</text>
2277 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀁</text>`
2278 */
2279
2280
2281const symbolText = function (swu) {
2282 return symbolText$2(swu2id(swu));
2283};
2284
2285/**
2286* Sutton SignWriting Core Module v1.4.2 (https://github.com/sutton-signwriting/core)
2287* Author: Steve Slevinski (https://SteveSlevinski.me)
2288* swu.mjs is released under the MIT License.
2289*/
2290
2291/**
2292 * Object of regular expressions for SWU strings in UTF-16
2293 *
2294 * @alias swu.re
2295 * @property {string} symbol - regular expressions for a symbol
2296 * @property {string} coord - regular expressions for a coordinate
2297 * @property {string} sort - regular expressions for the sorting marker
2298 * @property {string} box - regular expression for a signbox marker
2299 * @property {string} prefix - regular expression for a sorting marker followed by one or more symbols
2300 * @property {string} spatial - regular expression for a symbol followed by a coordinate
2301 * @property {string} signbox - regular expression for a signbox marker, max coordinate and zero or more spatial symbols
2302 * @property {string} sign - regular expression for an optional prefix followed by a signbox
2303 * @property {string} sortable - regular expression for a mandatory prefix followed by a signbox
2304 */
2305let re$1 = {
2306 'symbol': '(?:(?:\uD8C0[\uDC01-\uDFFF])|(?:[\uD8C1-\uD8FC][\uDC00-\uDFFF])|(?:\uD8FD[\uDC00-\uDC80]))',
2307 'coord': '(?:\uD836[\uDC0C-\uDDFF]){2}',
2308 'sort': '\uD836\uDC00',
2309 'box': '\uD836[\uDC01-\uDC04]'
2310};
2311re$1.prefix = `(?:${re$1.sort}(?:${re$1.symbol})+)`;
2312re$1.spatial = `${re$1.symbol}${re$1.coord}`;
2313re$1.signbox = `${re$1.box}${re$1.coord}(?:${re$1.spatial})*`;
2314re$1.sign = `${re$1.prefix}?${re$1.signbox}`;
2315re$1.sortable = `${re$1.prefix}${re$1.signbox}`;
2316/**
2317 * Object of regular expressions for style strings
2318 *
2319 * @alias style.re
2320 * @type {object}
2321 * @property {string} colorize - regular expression for colorize section
2322 * @property {string} colorhex - regular expression for color hex values with 3 or 6 characters
2323 * @property {string} colorname - regular expression for css color name
2324 * @property {string} padding - regular expression for padding section
2325 * @property {string} zoom - regular expression for zoom section
2326 * @property {string} classbase - regular expression for class name definition
2327 * @property {string} id - regular expression for id definition
2328 * @property {string} colorbase - regular expression for color hex or color name
2329 * @property {string} color - regular expression for single color entry
2330 * @property {string} colors - regular expression for double color entry
2331 * @property {string} background - regular expression for background section
2332 * @property {string} detail - regular expression for color details for line and optional fill
2333 * @property {string} detailsym - regular expression for color details for individual symbols
2334 * @property {string} classes - regular expression for one or more class names
2335 * @property {string} full - full regular expression for style string
2336 */
2337
2338let re = {
2339 'colorize': 'C',
2340 'colorhex': '(?:[0-9a-fA-F]{3}){1,2}',
2341 'colorname': '[a-zA-Z]+',
2342 'padding': 'P[0-9]{2}',
2343 'zoom': 'Z(?:[0-9]+(?:\\.[0-9]+)?|x)',
2344 'classbase': '-?[_a-zA-Z][_a-zA-Z0-9-]{0,100}',
2345 'id': '[a-zA-Z][_a-zA-Z0-9-]{0,100}'
2346};
2347re.colorbase = `(?:${re.colorhex}|${re.colorname})`;
2348re.color = `_${re.colorbase}_`;
2349re.colors = `_${re.colorbase}(?:,${re.colorbase})?_`;
2350re.background = `G${re.color}`;
2351re.detail = `D${re.colors}`;
2352re.detailsym = `D[0-9]{2}${re.colors}`;
2353re.classes = `${re.classbase}(?: ${re.classbase})*`;
2354re.full = `-(${re.colorize})?(${re.padding})?(${re.background})?(${re.detail})?(${re.zoom})?(?:-((?:${re.detailsym})*))?(?:-(${re.classes})?!(?:(${re.id})!)?)?`;
2355
2356const prefixColor = color => {
2357 const regex = new RegExp(`^${re.colorhex}$`);
2358 return (regex.test(color) ? '#' : '') + color;
2359};
2360
2361const definedProps = obj => Object.fromEntries(Object.entries(obj).filter(([k, v]) => v !== undefined));
2362/**
2363 * Function to parse style string to object
2364 * @function style.parse
2365 * @param {string} styleString - a style string
2366 * @returns {StyleObject} elements of style string
2367 * @example
2368 * style.parse('-CP10G_blue_D_red,Cyan_')
2369 *
2370 * return {
2371 * 'colorize': true,
2372 * 'padding': 10,
2373 * 'background': 'blue',
2374 * 'detail': ['red', 'Cyan']
2375 * }
2376 */
2377
2378
2379const parse$1 = styleString => {
2380 const regex = `^${re.full}`;
2381 const m = (typeof styleString === 'string' ? styleString.match(new RegExp(regex)) : []) || [];
2382 return definedProps({
2383 'colorize': !m[1] ? undefined : !!m[1],
2384 'padding': !m[2] ? undefined : parseInt(m[2].slice(1)),
2385 'background': !m[3] ? undefined : prefixColor(m[3].slice(2, -1)),
2386 'detail': !m[4] ? undefined : m[4].slice(2, -1).split(',').map(prefixColor),
2387 'zoom': !m[5] ? undefined : m[5] === 'Zx' ? 'x' : parseFloat(m[5].slice(1)),
2388 'detailsym': !m[6] ? undefined : m[6].match(new RegExp(re.detailsym, 'g')).map(val => {
2389 const parts = val.split('_');
2390 const detail = parts[1].split(',').map(prefixColor);
2391 return {
2392 'index': parseInt(parts[0].slice(1)),
2393 'detail': detail
2394 };
2395 }),
2396 'classes': !m[7] ? undefined : m[7],
2397 'id': !m[8] ? undefined : m[8]
2398 });
2399};
2400/** 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.
2401 * [Characters set definitions](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-09.html#name-characters)
2402 * @module convert
2403 */
2404
2405/**
2406 * Function to convert an SWU number character to an integer
2407 * @function convert.swu2num
2408 * @param {string} swuNum - SWU number character
2409 * @returns {number} Integer value for number
2410 * @example
2411 * convert.swu2num('𝤆')
2412 *
2413 * return 500
2414 */
2415
2416
2417const swu2num = swuNum => parseInt(swuNum.codePointAt(0)) - 0x1D80C + 250;
2418/**
2419 * Function to convert two SWU number characters to an array of x,y integers
2420 * @function convert.swu2coord
2421 * @param {string} swuCoord - Two SWU number character
2422 * @returns {number[]} Array of x,y integers
2423 * @example
2424 * convert.swu2coord('𝤆𝤆')
2425 *
2426 * return [500, 500]
2427 */
2428
2429
2430const swu2coord = swuCoord => [swu2num(swuCoord.slice(0, 2)), swu2num(swuCoord.slice(2, 4))];
2431/**
2432 * Function to convert an SWU symbol character to a code point on plane 4
2433 * @function convert.swu2code
2434 * @param {string} swuSym - SWU symbol character
2435 * @returns {number} Code point on plane 4
2436 * @example
2437 * convert.swu2code('񀀁')
2438 *
2439 * return 0x40001
2440 */
2441
2442
2443const swu2code = swuSym => parseInt(swuSym.codePointAt(0));
2444
2445const parse = {
2446 /**
2447 * Function to parse an swu symbol with optional coordinate and style string
2448 * @function swu.parse.symbol
2449 * @param {string} swuSym - an swu symbol
2450 * @returns {object} elements of swu symbol
2451 * @example
2452 * swu.parse.symbol('񀀁𝤆𝤆-C')
2453 *
2454 * return {
2455 * 'symbol': '񀀁',
2456 * 'coord': [500, 500],
2457 * 'style': '-C'
2458 * }
2459 */
2460 symbol: swuSym => {
2461 const regex = `^(${re$1.symbol})(${re$1.coord})?(${re.full})?`;
2462 const symbol = typeof swuSym === 'string' ? swuSym.match(new RegExp(regex)) : undefined;
2463 return {
2464 'symbol': symbol ? symbol[1] : undefined,
2465 'coord': symbol && symbol[2] ? swu2coord(symbol[2]) : undefined,
2466 'style': symbol ? symbol[3] : undefined
2467 };
2468 },
2469
2470 /**
2471 * Function to parse an swu sign with style string
2472 * @function swu.parse.sign
2473 * @param {string} swuSign - an swu sign
2474 * @returns {object} elements of swu sign
2475 * @example
2476 * swu.parse.sign('𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭-C')
2477 *
2478 * return {
2479 * sequence: ['񀀒','񀀚','񋚥','񋛩'],
2480 * box: '𝠃',
2481 * max: [525, 535],
2482 * spatials: [
2483 * {
2484 * symbol: '񋛩',
2485 * coord: [483, 510]
2486 * },
2487 * {
2488 * symbol: '񀀒',
2489 * coord: [501, 466]
2490 * },
2491 * {
2492 * symbol: '񋚥',
2493 * coord: [510, 500]
2494 * },
2495 * {
2496 * symbol: '񀀚',
2497 * coord: [476, 475]
2498 * }
2499 * ],
2500 * style: '-C'
2501 * }
2502 */
2503 sign: swuSign => {
2504 const regex = `^(${re$1.prefix})?(${re$1.signbox})(${re.full})?`;
2505 const sign = typeof swuSign === 'string' ? swuSign.match(new RegExp(regex)) : undefined;
2506
2507 if (sign) {
2508 return {
2509 'sequence': sign[1] ? sign[1].slice(2).match(/.{2}/g) : undefined,
2510 'box': sign[2].slice(0, 2),
2511 'max': swu2coord(sign[2].slice(2, 6)),
2512 'spatials': sign[2].length < 7 ? undefined : sign[2].slice(6).match(/(.{6})/g).map(m => {
2513 return {
2514 symbol: m.slice(0, 2),
2515 coord: swu2coord(m.slice(2))
2516 };
2517 }),
2518 'style': sign[3]
2519 };
2520 } else {
2521 return {};
2522 }
2523 },
2524
2525 /**
2526 * Function to parse an swu text
2527 * @function swu.parse.text
2528 * @param {string} swuText - an swu text
2529 * @returns {array} swu signs and punctuations
2530 * @example
2531 * swu.parse.text('𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻 𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦 񏌁𝣢𝤂')
2532 *
2533 * return [
2534 * '𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻',
2535 * '𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦',
2536 * '񏌁𝣢𝤂'
2537 * ]
2538 */
2539 text: swuText => {
2540 if (typeof swuText !== 'string') return [];
2541 const regex = `(${re$1.sign}(${re.full})?|${re$1.spatial}(${re.full})?)`;
2542 const matches = swuText.match(new RegExp(regex, 'g'));
2543 return matches ? [...matches] : [];
2544 }
2545};
2546/**
2547 * Function to gather sizing information about an swu sign or symbol
2548 * @function swu.info
2549 * @param {string} swu - an swu sign or symbol
2550 * @returns {object} information about the swu string
2551 * @example
2552 * swu.info('𝠀񁲡񈩧𝠂𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻-P10Z2')
2553 *
2554 * return {
2555 * minX: 481,
2556 * minY: 471,
2557 * width: 37,
2558 * height: 58,
2559 * segment: 'sign',
2560 * lane: -1
2561 * padding: 10,
2562 * zoom: 2
2563 * }
2564 */
2565
2566const info = swu => {
2567 let lanes = {
2568 '𝠁': 0,
2569 '𝠂': -1,
2570 '𝠃': 0,
2571 '𝠄': 1
2572 };
2573 let parsed = parse.sign(swu);
2574 let width, height, segment, x1, x2, y1, y2, lane;
2575
2576 if (parsed.spatials) {
2577 x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
2578 x2 = parsed.max[0];
2579 width = x2 - x1;
2580 y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
2581 y2 = parsed.max[1];
2582 height = y2 - y1;
2583 segment = 'sign';
2584 lane = parsed.box;
2585 } else {
2586 parsed = parse.symbol(swu);
2587 lane = "𝠃";
2588
2589 if (parsed.coord) {
2590 x1 = parsed.coord[0];
2591 width = (500 - x1) * 2;
2592 y1 = parsed.coord[1];
2593 height = (500 - y1) * 2;
2594 segment = 'symbol';
2595 } else {
2596 x1 = 490;
2597 width = 20;
2598 y1 = 490;
2599 height = 20;
2600 segment = 'none';
2601 }
2602 }
2603
2604 let style = parse$1(parsed.style);
2605 let zoom = style.zoom || 1;
2606 let padding = style.padding || 0;
2607 return {
2608 minX: x1,
2609 minY: y1,
2610 width: width,
2611 height: height,
2612 segment: segment,
2613 lane: lanes[lane],
2614 padding: padding,
2615 zoom: zoom
2616 };
2617};
2618
2619const columnDefaults = {
2620 'height': 500,
2621 'width': 150,
2622 'offset': 50,
2623 'pad': 20,
2624 'margin': 5,
2625 'dynamic': false,
2626 'background': undefined,
2627 'punctuation': {
2628 'spacing': true,
2629 'pad': 30,
2630 'pull': true
2631 },
2632 'style': {
2633 'detail': ['black', 'white'],
2634 'zoom': 1
2635 }
2636};
2637/**
2638 * Function to an object of column options with default values
2639 *
2640 * @function swu.columnDefaultsMerge
2641 * @param {ColumnOptions} options - object of column options
2642 * @returns {ColumnOptions} object of column options merged with column defaults
2643 * @example
2644 * swu.columnDefaultsMerge({height: 500,width:150})
2645 *
2646 * return {
2647 * "height": 500,
2648 * "width": 150,
2649 * "offset": 50,
2650 * "pad": 20,
2651 * "margin": 5,
2652 * "dynamic": false,
2653 * "punctuation": {
2654 * "spacing": true,
2655 * "pad": 30,
2656 * "pull": true
2657 * },
2658 * "style": {
2659 * "detail": [
2660 * "black",
2661 * "white"
2662 * ],
2663 * "zoom": 1
2664 * }
2665 * }
2666 */
2667
2668const columnDefaultsMerge = options => {
2669 if (typeof options !== 'object') options = {};
2670 return { ...columnDefaults,
2671 ...options,
2672 punctuation: { ...columnDefaults.punctuation,
2673 ...options.punctuation
2674 },
2675 style: { ...columnDefaults.style,
2676 ...options.style
2677 }
2678 };
2679};
2680/**
2681 * Function to transform an SWU text to an array of columns
2682 *
2683 * @function swu.columns
2684 * @param {string} swuText - SWU text of signs and punctuation
2685 * @param {ColumnOptions} options - object of column options
2686 * @returns {{options:ColumnOptions,widths:number[],columns:ColumnData}} object of column options, widths array, and column data
2687 * @example
2688 * swu.columns('𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻 𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦 񏌁𝣢𝤂', {height: 500,width:150})
2689 *
2690 * return {
2691 * "options": {
2692 * "height": 500,
2693 * "width": 150,
2694 * "offset": 50,
2695 * "pad": 20,
2696 * "margin": 5,
2697 * "dynamic": false,
2698 * "punctuation": {
2699 * "spacing": true,
2700 * "pad": 30,
2701 * "pull": true
2702 * },
2703 * "style": {
2704 * "detail": [
2705 * "black",
2706 * "white"
2707 * ],
2708 * "zoom": 1
2709 * }
2710 * },
2711 * "widths": [
2712 * 150
2713 * ],
2714 * "columns": [
2715 * [
2716 * {
2717 * "x": 56,
2718 * "y": 20,
2719 * "minX": 481,
2720 * "minY": 471,
2721 * "width": 37,
2722 * "height": 58,
2723 * "lane": 0,
2724 * "padding": 0,
2725 * "segment": "sign",
2726 * "text": "𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻",
2727 * "zoom": 1
2728 * },
2729 * {
2730 * "x": 57,
2731 * "y": 118,
2732 * "minX": 482,
2733 * "minY": 468,
2734 * "width": 36,
2735 * "height": 65,
2736 * "lane": 0,
2737 * "padding": 0,
2738 * "segment": "sign",
2739 * "text": "𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦",
2740 * "zoom": 1
2741 * },
2742 * {
2743 * "x": 39,
2744 * "y": 203,
2745 * "minX": 464,
2746 * "minY": 496,
2747 * "width": 72,
2748 * "height": 8,
2749 * "lane": 0,
2750 * "padding": 0,
2751 * "segment": "symbol",
2752 * "text": "񏌁𝣢𝤂",
2753 * "zoom": 1
2754 * }
2755 * ]
2756 * ]
2757 * }
2758 */
2759
2760
2761const columns = (swuText, options) => {
2762 if (typeof swuText !== 'string') return {};
2763 const values = columnDefaultsMerge(options);
2764 let input = parse.text(swuText);
2765 let cursor = 0;
2766 let cols = [];
2767 let col = [];
2768 let plus = 0;
2769 let center = parseInt(values.width / 2);
2770 let maxHeight = values.height - values.margin;
2771 let pullable = true;
2772 let finalize = false;
2773
2774 for (let val of input) {
2775 let informed = info(val);
2776 cursor += plus;
2777
2778 if (values.punctuation.spacing) {
2779 cursor += informed.segment == 'sign' ? values.pad : 0;
2780 } else {
2781 cursor += values.pad;
2782 }
2783
2784 finalize = cursor + informed.height > maxHeight;
2785
2786 if (finalize && informed.segment == 'symbol' && values.punctuation.pull && pullable) {
2787 finalize = false;
2788 pullable = false;
2789 }
2790
2791 if (col.length == 0) {
2792 finalize = false;
2793 }
2794
2795 if (finalize) {
2796 cursor = values.pad;
2797 cols.push(col);
2798 col = [];
2799 pullable = true;
2800 }
2801
2802 col.push(Object.assign(informed, {
2803 x: center + values.offset * informed.lane - (500 - informed.minX) * informed.zoom * values.style.zoom,
2804 y: cursor,
2805 text: val
2806 }));
2807 cursor += informed.height * informed.zoom * values.style.zoom;
2808
2809 if (values.punctuation.spacing) {
2810 plus = informed.segment == 'sign' ? values.pad : values.punctuation.pad;
2811 } else {
2812 plus = values.pad;
2813 }
2814 }
2815
2816 if (col.length) {
2817 cols.push(col);
2818 } // over height issue when pulling punctuation
2819
2820
2821 if (values.punctuation.pull) {
2822 for (let col of cols) {
2823 let last = col[col.length - 1];
2824 let diff = last.y + last.height - (values.height - values.margin);
2825
2826 if (diff > 0) {
2827 let adj = parseInt(diff / col.length) + 1;
2828
2829 for (let i in col) {
2830 col[i].y -= adj * i + adj;
2831 }
2832 }
2833 }
2834 } // contract, expand, adjust
2835
2836
2837 let widths = [];
2838
2839 for (let col of cols) {
2840 let min = [center - values.offset - values.pad];
2841 let max = [center + values.offset + values.pad];
2842
2843 for (let item of col) {
2844 min.push(item.x - values.pad);
2845 max.push(item.x + item.width + values.pad);
2846 }
2847
2848 min = Math.min(...min);
2849 max = Math.max(...max);
2850 let width = values.width;
2851 let adj = 0;
2852
2853 if (!values.dynamic) {
2854 adj = center - parseInt((min + max) / 2);
2855 } else {
2856 width = max - min;
2857 adj = -min;
2858 }
2859
2860 for (let item of col) {
2861 item.x += adj;
2862 }
2863
2864 widths.push(width);
2865 }
2866
2867 return {
2868 'options': values,
2869 'widths': widths,
2870 'columns': cols
2871 };
2872};
2873/**
2874 * Array of plane 4 code points for categories of symbols: hand, movement, dynamics, head, trunk & limb, location, and punctuation.
2875 * @alias swu.category
2876 * @type {array}
2877 */
2878
2879const category = [0x40001, 0x461e1, 0x4bca1, 0x4bfa1, 0x4e8e1, 0x4efa1, 0x4f2a1];
2880/**
2881 * Object of symbol ranges with starting and ending code points on plane 4.
2882 *
2883 * { all, writing, hand, movement, dynamic, head, hcenter, vcenter, trunk, limb, location, punctuation }
2884 * @alias swu.ranges
2885 * @type {object}
2886 */
2887
2888const ranges = {
2889 'all': [0x40001, 0x4f480],
2890 'writing': [0x40001, 0x4efa0],
2891 'hand': [0x40001, 0x461e0],
2892 'movement': [0x461e1, 0x4bca0],
2893 'dynamic': [0x4bca1, 0x4bfa0],
2894 'head': [0x4bfa1, 0x4e8e0],
2895 'hcenter': [0x4bfa1, 0x4e8e0],
2896 'vcenter': [0x4bfa1, 0x4ec40],
2897 'trunk': [0x4e8e1, 0x4ec40],
2898 'limb': [0x4ec41, 0x4efa0],
2899 'location': [0x4efa1, 0x4f2a0],
2900 'punctuation': [0x4f2a1, 0x4f480]
2901};
2902/**
2903 * Array of colors associated with the seven symbol categories.
2904 * @alias swu.colors
2905 * @type {array}
2906 */
2907
2908
2909const colors = ['#0000CC', '#CC0000', '#FF0099', '#006600', '#000000', '#884411', '#FF9900'];
2910/**
2911 * Function that returns the standardized color for a symbol.
2912 * @function swu.colorize
2913 * @param {string} swuSym - an SWU symbol character
2914 * @returns {string} name of standardized color for symbol
2915 * @example
2916 * swu.colorize('񀀁')
2917 *
2918 * return '#0000CC'
2919 */
2920
2921const colorize = swuSym => {
2922 const parsed = parse.symbol(swuSym);
2923 let color = '#000000';
2924
2925 if (parsed.symbol) {
2926 const code = swu2code(parsed.symbol);
2927 const index = category.findIndex(val => val > code);
2928 color = colors[index < 0 ? 6 : index - 1];
2929 }
2930
2931 return color;
2932};
2933/* support ongoing development on https://patreon.com/signwriting */
2934
2935/**
2936 * Function that creates an SVG image from an SWU symbol key with an optional style string
2937 * @function swu.symbolSvgBody
2938 * @param {string} swuSym - an SWU symbol key with optional style string
2939 * @example
2940 * swu.symbolSvgBody('S10000')
2941 *
2942 * return `<text font-size="0">S10000</text>
2943 * <g transform="translate(500,500)">
2944 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀁</text>
2945 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀁</text>
2946 * </g>`
2947 */
2948
2949const symbolSvgBody = swuSym => {
2950 const parsed = parse.symbol(swuSym);
2951 const blank = '';
2952 if (!parsed.symbol) return blank;
2953 let styling = parse$3(parsed.style);
2954 let x1, y1, x2, y2;
2955
2956 if (parsed.coord) {
2957 x1 = parsed.coord[0];
2958 y1 = parsed.coord[1];
2959 x2 = 500 + (500 - x1);
2960 y2 = 500 + (500 - y1);
2961 } else {
2962 let size = symbolSize(parsed.symbol);
2963 if (!size) return blank;
2964 x1 = 500 - parseInt((size[0] + 1) / 2);
2965 y1 = 500 - parseInt((size[1] + 1) / 2);
2966 x2 = 500 + (500 - x1);
2967 y2 = 500 + (500 - y1);
2968 }
2969
2970 let symSvg = symbolText(parsed.symbol);
2971 symSvg = ` <g transform="translate(${x1},${y1})">
2972${symSvg}
2973 </g>`;
2974 let line;
2975
2976 if (styling.colorize) {
2977 line = colorize(parsed.symbol);
2978 } else if (styling.detail) {
2979 line = styling.detail[0];
2980 }
2981
2982 if (line) {
2983 symSvg = symSvg.replace(/class="sym-line" fill="black"/, `class="sym-line" fill="${line}"`);
2984 }
2985
2986 let fill = styling.detail && styling.detail[1];
2987
2988 if (fill) {
2989 symSvg = symSvg.replace(/class="sym-fill" fill="white"/, `class="sym-fill" fill="${fill}"`);
2990 }
2991
2992 let background = '';
2993
2994 if (styling.padding) {
2995 x1 -= styling.padding;
2996 y1 -= styling.padding;
2997 x2 += styling.padding;
2998 y2 += styling.padding;
2999 }
3000
3001 if (styling.background) {
3002 background = `\n <rect x="${x1}" y="${y1}" width="${x2 - x1}" height="${y2 - y1}" style="fill:${styling.background};" />`;
3003 }
3004
3005 return ` <text font-size="0">${swuSym}</text>${background}
3006${symSvg}`;
3007};
3008/**
3009 * Function that creates an SVG image from an SWU symbol key with an optional style string
3010 * @function swu.symbolSvg
3011 * @param {string} swuSym - an SWU symbol key with optional style string
3012 * @example
3013 * swu.symbolSvg('S10000')
3014 *
3015 * return `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="15" height="30" viewBox="500 500 15 30">
3016 * <text font-size="0">S10000</text>
3017 * <g transform="translate(500,500)">
3018 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀁</text>
3019 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀁</text>
3020 * </g>
3021 * </svg>`
3022 */
3023
3024
3025const symbolSvg = swuSym => {
3026 const parsed = parse.symbol(swuSym);
3027 const blank = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="1" height="1"></svg>';
3028 if (!parsed.symbol) return blank;
3029 let styling = parse$3(parsed.style);
3030 let x1, y1, x2, y2;
3031
3032 if (parsed.coord) {
3033 x1 = parsed.coord[0];
3034 y1 = parsed.coord[1];
3035 x2 = 500 + (500 - x1);
3036 y2 = 500 + (500 - y1);
3037 } else {
3038 let size = symbolSize(parsed.symbol);
3039 if (!size) return blank;
3040 x1 = parseInt(500 - size[0] / 2);
3041 y1 = parseInt(500 - size[1] / 2);
3042 x2 = x1 + size[0];
3043 y2 = y1 + size[1];
3044 }
3045
3046 let classes = '';
3047
3048 if (styling.classes) {
3049 classes = ` class="${styling.classes}"`;
3050 }
3051
3052 let id = '';
3053
3054 if (styling.id) {
3055 id = ` id="${styling.id}"`;
3056 }
3057
3058 if (styling.padding) {
3059 x1 -= styling.padding;
3060 y1 -= styling.padding;
3061 x2 += styling.padding;
3062 y2 += styling.padding;
3063 }
3064
3065 let sizing = '';
3066
3067 if (styling.zoom != 'x') {
3068 sizing = ` width="${(x2 - x1) * (styling.zoom ? styling.zoom : 1)}" height="${(y2 - y1) * (styling.zoom ? styling.zoom : 1)}"`;
3069 }
3070
3071 return `<svg${classes}${id} version="1.1" xmlns="http://www.w3.org/2000/svg"${sizing} viewBox="${x1} ${y1} ${x2 - x1} ${y2 - y1}">
3072${symbolSvgBody(swuSym)}
3073</svg>`;
3074};
3075
3076const symbolCanvas = function (swuSym) {
3077 const parsed = parse.symbol(swuSym);
3078
3079 if (parsed.symbol) {
3080 let size = symbolSize(parsed.symbol);
3081
3082 if (size) {
3083 const canvas = document.createElement('canvas');
3084 const context = canvas.getContext('2d');
3085 let styling = parse$3(parsed.style);
3086 let line = 'black';
3087
3088 if (styling.colorize) {
3089 line = colorize(parsed.symbol);
3090 } else if (styling.detail) {
3091 line = styling.detail[0];
3092 }
3093
3094 let fill = styling.detail && styling.detail[1] || 'white';
3095 let x1 = 500;
3096 let x2 = x1 + size[0];
3097 let y1 = 500;
3098 let y2 = y1 + size[1];
3099
3100 if (styling.padding) {
3101 x1 -= styling.padding;
3102 y1 -= styling.padding;
3103 x2 += styling.padding;
3104 y2 += styling.padding;
3105 }
3106
3107 let sizing = 1;
3108
3109 if (styling.zoom != 'x') {
3110 sizing = styling.zoom;
3111 }
3112
3113 let w = (x2 - x1) * sizing;
3114 let h = (y2 - y1) * sizing;
3115 canvas.width = w ? w : 1;
3116 canvas.height = h ? h : 1;
3117
3118 if (styling.background) {
3119 context.rect(0, 0, w, h);
3120 context.fillStyle = styling.background;
3121 context.fill();
3122 }
3123
3124 context.font = 30 * sizing + "px 'SuttonSignWritingFill'";
3125 context.fillStyle = fill;
3126 context.fillText(symbolFill(parsed.symbol), (500 - x1) * sizing, (500 - y1) * sizing);
3127 context.font = 30 * sizing + "px 'SuttonSignWritingLine'";
3128 context.fillStyle = line;
3129 context.fillText(symbolLine(parsed.symbol), (500 - x1) * sizing, (500 - y1) * sizing);
3130 return canvas;
3131 }
3132 }
3133};
3134/**
3135 * Function that creates a PNG data url from an SWU symbol character with an optional style string
3136 * @function swu.symbolPng
3137 * @param {string} swuSym - an SWU symbol character with optional style string
3138 * @example
3139 * swu.symbolPng('񀀁-CP10G_green_Z2')
3140 *
3141 * return 'data:image/png;base64,iVBORw...'
3142 */
3143
3144
3145const symbolPng = swuSym => {
3146 const canvas = symbolCanvas(swuSym);
3147 const png = canvas.toDataURL("image/png");
3148 canvas.remove();
3149 return png;
3150};
3151
3152const blank = null;
3153/**
3154 * Function that normalizes a symbol with a minimum coordinate for a center of 500,500
3155 * @function swu.symbolNormalize
3156 * @param {string} swuSym - an SWU symbol character with optional coordinate and style string
3157 * @example
3158 * swu.symbolNormalize('񀀁')
3159 *
3160 * return '񀀁𝣿𝣷'
3161 */
3162
3163const symbolNormalize = swuSym => {
3164 const parsed = parse.symbol(swuSym);
3165
3166 if (parsed.symbol) {
3167 let size = symbolSize(parsed.symbol);
3168
3169 if (size) {
3170 return `${parsed.symbol}${coord2swu([500 - parseInt((size[0] + 1) / 2), 500 - parseInt((size[1] + 1) / 2)])}${parsed.style || ''}`;
3171 }
3172 } else {
3173 return blank;
3174 }
3175};
3176
3177/**
3178 * Function that creates an SVG image from an SWU sign with an optional style string
3179 * @function swu.signSvgBody
3180 * @param {string} swuSign - an SWU sign with optional style string
3181 * @example
3182 * swu.signSvgBody('M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475')
3183 *
3184 * return `<text font-size="0">M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475</text>
3185 * <g transform="translate(483,510)">
3186 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􋛩</text>
3187 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󻛩</text>
3188 * </g>
3189 * <g transform="translate(501,466)">
3190 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀒</text>
3191 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀒</text>
3192 * </g>
3193 * <g transform="translate(510,500)">
3194 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􋚥</text>
3195 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󻚥</text>
3196 * </g>
3197 * <g transform="translate(476,475)">
3198 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀚</text>
3199 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀚</text>
3200 * </g>`
3201 */
3202
3203const signSvgBody = swuSign => {
3204 let parsed = parse.sign(swuSign);
3205 const blank = '';
3206
3207 if (parsed.spatials) {
3208 let styling = parse$3(parsed.style);
3209
3210 if (styling.detailsym) {
3211 styling.detailsym.forEach(sym => {
3212 if (parsed.spatials[sym.index - 1]) {
3213 parsed.spatials[sym.index - 1].detail = sym.detail;
3214 }
3215 });
3216 }
3217
3218 let x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
3219 let y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
3220 let x2 = parsed.max[0];
3221 let y2 = parsed.max[1];
3222 let background = '';
3223
3224 if (styling.padding) {
3225 x1 -= styling.padding;
3226 y1 -= styling.padding;
3227 x2 += styling.padding;
3228 y2 += styling.padding;
3229 }
3230
3231 if (styling.background) {
3232 background = `\n <rect x="${x1}" y="${y1}" width="${x2 - x1}" height="${y2 - y1}" style="fill:${styling.background};" />`;
3233 }
3234
3235 let svg = ` <text font-size="0">${swuSign}</text>${background}`;
3236 const line = styling.detail && styling.detail[0];
3237 const fill = styling.detail && styling.detail[1];
3238 svg += '\n' + parsed.spatials.map(spatial => {
3239 let svg = symbolText(spatial.symbol);
3240 let symLine = line;
3241
3242 if (spatial.detail) {
3243 symLine = spatial.detail[0];
3244 } else if (styling.colorize) {
3245 symLine = colorize(spatial.symbol);
3246 }
3247
3248 if (symLine) {
3249 svg = svg.replace(/class="sym-line" fill="black"/, `class="sym-line" fill="${symLine}"`);
3250 }
3251
3252 let symFill = fill;
3253
3254 if (spatial.detail && spatial.detail[1]) {
3255 symFill = spatial.detail[1];
3256 }
3257
3258 if (symFill) {
3259 svg = svg.replace(/class="sym-fill" fill="white"/, `class="sym-fill" fill="${symFill}"`);
3260 }
3261
3262 return ` <g transform="translate(${spatial.coord[0]},${spatial.coord[1]})">
3263${svg}
3264 </g>`;
3265 }).join('\n');
3266 return svg;
3267 }
3268
3269 return blank;
3270};
3271/**
3272 * Function that creates an SVG image from an SWU sign with an optional style string
3273 * @function swu.signSvg
3274 * @param {string} swuSign - an SWU sign with optional style string
3275 * @example
3276 * swu.signSvg('M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475')
3277 *
3278 * return `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="49" height="69" viewBox="476 466 49 69">
3279 * <text font-size="0">M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475</text>
3280 * <g transform="translate(483,510)">
3281 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􋛩</text>
3282 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󻛩</text>
3283 * </g>
3284 * <g transform="translate(501,466)">
3285 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀒</text>
3286 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀒</text>
3287 * </g>
3288 * <g transform="translate(510,500)">
3289 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􋚥</text>
3290 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󻚥</text>
3291 * </g>
3292 * <g transform="translate(476,475)">
3293 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀚</text>
3294 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀚</text>
3295 * </g>
3296 * </svg>`
3297 */
3298
3299
3300const signSvg = swuSign => {
3301 let parsed = parse.sign(swuSign);
3302 const blank = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="1" height="1"></svg>';
3303
3304 if (parsed.spatials) {
3305 let styling = parse$3(parsed.style);
3306 let x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
3307 let y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
3308 let x2 = parsed.max[0];
3309 let y2 = parsed.max[1];
3310 let classes = '';
3311
3312 if (styling.classes) {
3313 classes = ` class="${styling.classes}"`;
3314 }
3315
3316 let id = '';
3317
3318 if (styling.id) {
3319 id = ` id="${styling.id}"`;
3320 }
3321
3322 if (styling.padding) {
3323 x1 -= styling.padding;
3324 y1 -= styling.padding;
3325 x2 += styling.padding;
3326 y2 += styling.padding;
3327 }
3328
3329 let sizing = '';
3330
3331 if (styling.zoom != 'x') {
3332 sizing = ` width="${(x2 - x1) * (styling.zoom ? styling.zoom : 1)}" height="${(y2 - y1) * (styling.zoom ? styling.zoom : 1)}"`;
3333 }
3334
3335 let svg = `<svg${classes}${id} version="1.1" xmlns="http://www.w3.org/2000/svg"${sizing} viewBox="${x1} ${y1} ${x2 - x1} ${y2 - y1}">
3336`;
3337 svg += signSvgBody(swuSign);
3338 svg += '\n</svg>';
3339 return svg;
3340 }
3341
3342 return blank;
3343};
3344
3345const signCanvas = function (swuSign) {
3346 const parsed = parse.sign(swuSign);
3347
3348 if (parsed.spatials) {
3349 const canvas = document.createElement('canvas');
3350 const context = canvas.getContext('2d');
3351 let styling = parse$3(parsed.style);
3352
3353 if (styling.detailsym) {
3354 styling.detailsym.forEach(sym => {
3355 if (parsed.spatials[sym.index - 1]) {
3356 parsed.spatials[sym.index - 1].detail = sym.detail;
3357 }
3358 });
3359 }
3360
3361 let x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
3362 let y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
3363 let x2 = parsed.max[0];
3364 let y2 = parsed.max[1];
3365
3366 if (styling.padding) {
3367 x1 -= styling.padding;
3368 y1 -= styling.padding;
3369 x2 += styling.padding;
3370 y2 += styling.padding;
3371 }
3372
3373 let sizing = 1;
3374
3375 if (styling.zoom != 'x') {
3376 sizing = styling.zoom;
3377 }
3378
3379 let w = (x2 - x1) * sizing;
3380 let h = (y2 - y1) * sizing;
3381 canvas.width = w ? w : 1;
3382 canvas.height = h ? h : 1;
3383
3384 if (styling.background) {
3385 context.rect(0, 0, w, h);
3386 context.fillStyle = styling.background;
3387 context.fill();
3388 }
3389
3390 const line = styling.detail && styling.detail[0] || "black";
3391 const fill = styling.detail && styling.detail[1] || "white";
3392 parsed.spatials.forEach(spatial => {
3393 let symLine = line;
3394
3395 if (spatial.detail) {
3396 symLine = spatial.detail[0];
3397 } else if (styling.colorize) {
3398 symLine = colorize(spatial.symbol);
3399 }
3400
3401 let symFill = fill;
3402
3403 if (spatial.detail && spatial.detail[1]) {
3404 symFill = spatial.detail[1];
3405 }
3406
3407 context.font = 30 * sizing + "px 'SuttonSignWritingFill'";
3408 context.fillStyle = symFill;
3409 context.fillText(symbolFill(spatial.symbol), (spatial.coord[0] - x1) * sizing, (spatial.coord[1] - y1) * sizing);
3410 context.font = 30 * sizing + "px 'SuttonSignWritingLine'";
3411 context.fillStyle = symLine;
3412 context.fillText(symbolLine(spatial.symbol), (spatial.coord[0] - x1) * sizing, (spatial.coord[1] - y1) * sizing);
3413 });
3414 return canvas;
3415 }
3416};
3417/**
3418 * Function that creates a PNG data url from an SWU sign with an optional style string
3419 * @function swu.signPng
3420 * @param {string} swuSign - an SWU sign with optional style string
3421 * @example
3422 * swu.signPng('𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭')
3423 *
3424 * return 'data:image/png;base64,iVBORw...'
3425 */
3426
3427
3428const signPng = swuSign => {
3429 const canvas = signCanvas(swuSign);
3430 const png = canvas.toDataURL("image/png");
3431 canvas.remove();
3432 return png;
3433};
3434
3435/**
3436 * Function that normalizes an SWU sign for a center of 500,500
3437 * @function swu.signNormalize
3438 * @param {string} swuSign - an SWU sign with optional style string
3439 * @example
3440 * swu.signNormalize('𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭')
3441 *
3442 * return '𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭'
3443 */
3444
3445const signNormalize = swuSign => {
3446 const parsed = parse.sign(swuSign);
3447
3448 if (parsed.spatials) {
3449 const symbolsizes = parsed.spatials.reduce((output, spatial) => {
3450 const size = symbolSize(spatial.symbol);
3451 output[spatial.symbol] = {
3452 width: size[0],
3453 height: size[1]
3454 };
3455 return output;
3456 }, {});
3457
3458 const bbox = symbols => {
3459 const x1 = Math.min(...symbols.map(spatial => spatial.coord[0]));
3460 const y1 = Math.min(...symbols.map(spatial => spatial.coord[1]));
3461 const x2 = Math.max(...symbols.map(spatial => spatial.coord[0] + parseInt(symbolsizes[spatial.symbol].width)));
3462 const y2 = Math.max(...symbols.map(spatial => spatial.coord[1] + parseInt(symbolsizes[spatial.symbol].height)));
3463 return {
3464 x1: x1,
3465 y1: y1,
3466 x2: x2,
3467 y2: y2
3468 };
3469 };
3470
3471 const hrange = ranges['hcenter'];
3472 const hsyms = parsed.spatials.filter(spatial => {
3473 const dec = parseInt(spatial.symbol.slice(1, 4), 16);
3474 return hrange[0] <= dec && hrange[1] >= dec;
3475 });
3476 const vrange = ranges['vcenter'];
3477 const vsyms = parsed.spatials.filter(spatial => {
3478 const dec = parseInt(spatial.symbol.slice(1, 4), 16);
3479 return vrange[0] <= dec && vrange[1] >= dec;
3480 });
3481 let abox = bbox(parsed.spatials);
3482 let max = [abox.x2, abox.y2];
3483
3484 if (hsyms.length) {
3485 const hbox = bbox(hsyms);
3486 abox.x1 = hbox.x1;
3487 abox.x2 = hbox.x2;
3488 }
3489
3490 if (vsyms.length) {
3491 const vbox = bbox(vsyms);
3492 abox.y1 = vbox.y1;
3493 abox.y2 = vbox.y2;
3494 }
3495
3496 const offset = [parseInt((abox.x2 + abox.x1) / 2) - 500, parseInt((abox.y2 + abox.y1) / 2) - 500];
3497 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 || '');
3498 return swuout;
3499 }
3500};
3501
3502/**
3503 * Function that creates an SVG image for a column of SWU
3504 * @function swu.columnSvg
3505 * @param {ColumnData} swuColumn - an array of objects with information about FSW signs and punctuation
3506 * @param {ColumnOptions} options - an object of column options
3507 * @returns {string} column svg
3508 * @example
3509 * swu.columnSvg([
3510 * {
3511 * "x": 56,
3512 * "y": 20,
3513 * "minX": 481,
3514 * "minY": 471,
3515 * "width": 37,
3516 * "height": 58,
3517 * "lane": 0,
3518 * "padding": 0,
3519 * "segment": "sign",
3520 * "text": "𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻",
3521 * "zoom": 1
3522 * },
3523 * {
3524 * "x": 57,
3525 * "y": 118,
3526 * "minX": 482,
3527 * "minY": 468,
3528 * "width": 36,
3529 * "height": 65,
3530 * "lane": 0,
3531 * "padding": 0,
3532 * "segment": "sign",
3533 * "text": "𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦",
3534 * "zoom": 1
3535 * },
3536 * {
3537 * "x": 39,
3538 * "y": 203,
3539 * "minX": 464,
3540 * "minY": 496,
3541 * "width": 72,
3542 * "height": 8,
3543 * "lane": 0,
3544 * "padding": 0,
3545 * "segment": "symbol",
3546 * "text": "񏌁𝣢𝤂",
3547 * "zoom": 1
3548 * }
3549 * ],
3550 * {
3551 * "height": 250,
3552 * "width": 150,
3553 * })
3554 *
3555 * return `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="150" height="250" viewBox="0 0 150 250">
3556 * <g transform="translate(56,20) scale(1) translate(-481,-471) ">
3557 * <text font-size="0">𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻-D_black,white_Z1</text>
3558 * <g transform="translate(481,471)">
3559 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􁲡</text>
3560 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󱲡</text>
3561 * </g>
3562 * <g transform="translate(503,489)">
3563 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􈩧</text>
3564 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󸩧</text>
3565 * </g>
3566 * </g>
3567 * <g transform="translate(57,118) scale(1) translate(-482,-468) ">
3568 * <text font-size="0">𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦-D_black,white_Z1</text>
3569 * <g transform="translate(489,515)">
3570 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􃊫</text>
3571 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󳊫</text>
3572 * </g>
3573 * <g transform="translate(482,490)">
3574 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􃊢</text>
3575 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󳊢</text>
3576 * </g>
3577 * <g transform="translate(508,496)">
3578 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􆇡</text>
3579 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󶇡</text>
3580 * </g>
3581 * <g transform="translate(500,468)">
3582 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􋛕</text>
3583 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󻛕</text>
3584 * </g>
3585 * </g>
3586 * <g transform="translate(39,203) scale(1) translate(-464,-496) ">
3587 * <text font-size="0">񏌁𝣢𝤂-D_black,white_Z1</text>
3588 * <g transform="translate(464,496)">
3589 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􏌁</text>
3590 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󿌁</text>
3591 * </g>
3592 * </g>
3593 * </svg>`
3594 */
3595
3596const columnSvg = (swuColumn, options) => {
3597
3598 if (typeof options !== 'object') options = {};
3599 const values = Object.assign(columnDefaults, options);
3600 let x1 = 0;
3601 let y1 = 0;
3602 let x2 = values.width;
3603 let y2 = values.height;
3604 let background = '';
3605
3606 if (values.background) {
3607 background = `\n <rect x="${x1}" y="${y1}" width="${x2 - x1}" height="${y2 - y1}" style="fill:${values.background};" />`;
3608 }
3609
3610 let sizing = ` width="${values.width}" height="${values.height}"`;
3611 let svg = `<svg version="1.1" xmlns="http://www.w3.org/2000/svg"${sizing} viewBox="${x1} ${y1} ${x2 - x1} ${y2 - y1}">
3612 <text font-size="0">${x1}</text>${background}`;
3613 svg += swuColumn.map(item => {
3614 const dash = item.text.indexOf('-');
3615
3616 if (dash > 0) {
3617 const itemStyle = item.text.substring(dash);
3618 const newStyle = { ...values.style,
3619 ...parse$3(itemStyle)
3620 };
3621 item.text = item.text.replace(itemStyle, compose(newStyle));
3622 } else {
3623 item.text += compose(values.style);
3624 }
3625
3626 item.zoom = item.zoom * values.style.zoom;
3627 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>';
3628 }).join('\n');
3629 svg += '\n</svg>';
3630 return svg;
3631};
3632
3633/**
3634 * Function that creates an array of SVG column images for an SWU text
3635 * @function swu.columnsSvg
3636 * @param {string} swuText - a text of SWU signs and punctuation
3637 * @param {ColumnOptions} options - an object of column options
3638 * @returns {string[]} array of SVG columns
3639 * @example
3640 * swu.columnsSvg('𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻 𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦 񏌁𝣢𝤂',{
3641 * "height": 250,
3642 * "width": 150,
3643 * })
3644 *
3645 * return [`<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="150" height="250" viewBox="0 0 150 250">
3646 * <g transform="translate(56,20) scale(1) translate(-481,-471) ">
3647 * <text font-size="0">𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻-D_black,white_Z1</text>
3648 * <g transform="translate(481,471)">
3649 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􁲡</text>
3650 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󱲡</text>
3651 * </g>
3652 * <g transform="translate(503,489)">
3653 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􈩧</text>
3654 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󸩧</text>
3655 * </g>
3656 * </g>
3657 * <g transform="translate(57,118) scale(1) translate(-482,-468) ">
3658 * <text font-size="0">𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦-D_black,white_Z1</text>
3659 * <g transform="translate(489,515)">
3660 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􃊫</text>
3661 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󳊫</text>
3662 * </g>
3663 * <g transform="translate(482,490)">
3664 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􃊢</text>
3665 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󳊢</text>
3666 * </g>
3667 * <g transform="translate(508,496)">
3668 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􆇡</text>
3669 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󶇡</text>
3670 * </g>
3671 * <g transform="translate(500,468)">
3672 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􋛕</text>
3673 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󻛕</text>
3674 * </g>
3675 * </g>
3676 * <g transform="translate(39,203) scale(1) translate(-464,-496) ">
3677 * <text font-size="0">񏌁𝣢𝤂-D_black,white_Z1</text>
3678 * <g transform="translate(464,496)">
3679 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􏌁</text>
3680 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󿌁</text>
3681 * </g>
3682 * </g>
3683 * </svg>`]
3684 */
3685
3686const columnsSvg = function (swuText, options) {
3687 if (typeof options !== 'object') options = {};
3688 let values = columns(swuText, options);
3689 let cols = values.columns.map((col, i) => {
3690 return columnSvg(col, { ...values.options,
3691 ...{
3692 width: values.widths[i]
3693 }
3694 });
3695 });
3696 return cols;
3697};
3698
3699const columnCanvas = function (swuColumn, options) {
3700 if (typeof options !== 'object') options = {};
3701 const values = Object.assign(columnDefaults, options);
3702 const canvas = document.createElement('canvas');
3703 canvas.width = values.width;
3704 canvas.height = values.height;
3705 const context = canvas.getContext('2d');
3706
3707 if (values.background) {
3708 context.rect(0, 0, values.width, values.height);
3709 context.fillStyle = values.background;
3710 context.fill();
3711 }
3712
3713 swuColumn.map(item => {
3714 const dash = item.text.indexOf('-');
3715
3716 if (dash > 0) {
3717 const itemStyle = item.text.substring(dash);
3718 const newStyle = { ...values.style,
3719 ...parse$3(itemStyle)
3720 };
3721 item.text = item.text.replace(itemStyle, compose(newStyle));
3722 } else {
3723 item.text += compose(values.style);
3724 }
3725
3726 item.zoom = item.zoom * values.style.zoom;
3727 let parsed = {};
3728
3729 if (item.segment == "sign") {
3730 parsed = parse.sign(item.text);
3731 } else {
3732 let sym = parse.symbol(item.text);
3733 parsed.style = sym.style;
3734 parsed.spatials = [sym];
3735 }
3736
3737 let styling = parse$3(parsed.style);
3738
3739 if (styling.background) {
3740 context.fillStyle = styling.background;
3741 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);
3742 }
3743
3744 if (styling.detailsym) {
3745 styling.detailsym.forEach(sym => {
3746 if (parsed.spatials[sym.index - 1]) {
3747 parsed.spatials[sym.index - 1].detail = sym.detail;
3748 }
3749 });
3750 }
3751
3752 const line = styling.detail && styling.detail[0] || "black";
3753 const fill = styling.detail && styling.detail[1] || "white";
3754 parsed.spatials.forEach(spatial => {
3755 let symLine = line;
3756
3757 if (spatial.detail) {
3758 symLine = spatial.detail[0];
3759 } else if (styling.colorize) {
3760 symLine = colorize(spatial.symbol);
3761 }
3762
3763 let symFill = fill;
3764
3765 if (spatial.detail && spatial.detail[1]) {
3766 symFill = spatial.detail[1];
3767 }
3768
3769 context.font = 30 * item.zoom + "px 'SuttonSignWritingFill'";
3770 context.fillStyle = symFill;
3771 context.fillText(symbolFill(spatial.symbol), item.x + (spatial.coord[0] - item.minX) * item.zoom, item.y + (spatial.coord[1] - item.minY) * item.zoom);
3772 context.font = 30 * item.zoom + "px 'SuttonSignWritingLine'";
3773 context.fillStyle = symLine;
3774 context.fillText(symbolLine(spatial.symbol), item.x + (spatial.coord[0] - item.minX) * item.zoom, item.y + (spatial.coord[1] - item.minY) * item.zoom);
3775 });
3776 });
3777 return canvas;
3778};
3779/**
3780 * Function that creates a PNG data url for a column of SWU
3781 * @function swu.columnPng
3782 * @param {ColumnData} swuColumn - an array of SWU signs and punctuation with coordinates
3783 * @param {ColumnOptions} options - an object of column options
3784 * @returns {string} column png data url
3785 * @example
3786 * swu.columnPng([
3787 * {
3788 * "x": 56,
3789 * "y": 20,
3790 * "minX": 481,
3791 * "minY": 471,
3792 * "width": 37,
3793 * "height": 58,
3794 * "lane": 0,
3795 * "padding": 0,
3796 * "segment": "sign",
3797 * "text": "𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻",
3798 * "zoom": 1
3799 * },
3800 * {
3801 * "x": 57,
3802 * "y": 118,
3803 * "minX": 482,
3804 * "minY": 468,
3805 * "width": 36,
3806 * "height": 65,
3807 * "lane": 0,
3808 * "padding": 0,
3809 * "segment": "sign",
3810 * "text": "𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦",
3811 * "zoom": 1
3812 * },
3813 * {
3814 * "x": 39,
3815 * "y": 203,
3816 * "minX": 464,
3817 * "minY": 496,
3818 * "width": 72,
3819 * "height": 8,
3820 * "lane": 0,
3821 * "padding": 0,
3822 * "segment": "symbol",
3823 * "text": "񏌁𝣢𝤂",
3824 * "zoom": 1
3825 * }
3826 * ],
3827 * {
3828 * "height": 250,
3829 * "width": 150,
3830 * })
3831 *
3832 * return 'data:image/png;base64,iVBORw...'
3833 */
3834
3835
3836const columnPng = (swuColumn, options) => {
3837 const canvas = columnCanvas(swuColumn, options);
3838 const png = canvas.toDataURL("image/png");
3839 canvas.remove();
3840 return png;
3841};
3842
3843/**
3844 * Function that creates an SVG image for a column of SWU
3845 * @function swu.columnsPng
3846 * @param {string} swuText - an array of SWU signs and punctuation with coordinates
3847 * @param {ColumnOptions} options - an object of column options
3848 * @returns {string[]} array of PNG data urls
3849 * @example
3850 * swu.columnsPng('𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻 𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦 񏌁𝣢𝤂',{
3851 * "height": 250,
3852 * "width": 150,
3853 * })
3854 *
3855 * return ['data:image/png;base64,iVBORw...']
3856 */
3857
3858const columnsPng = function (swuText, options) {
3859 if (typeof options !== 'object') options = {};
3860 let values = columns(swuText, options);
3861 let cols = values.columns.map((col, i) => {
3862 return columnPng(col, { ...values.options,
3863 ...{
3864 width: values.widths[i]
3865 }
3866 });
3867 });
3868 return cols;
3869};
3870
3871/** The swu module contains functions for handling SignWriting in Unicode (SWU) characters.
3872 * [SWU characters definition](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-09.html#name-signwriting-in-unicode-swu)
3873 * @module swu
3874 */
3875
3876var index = /*#__PURE__*/Object.freeze({
3877 __proto__: null,
3878 symbolSize: symbolSize,
3879 symbolLine: symbolLine,
3880 symbolFill: symbolFill,
3881 symbolText: symbolText,
3882 symbolSvgBody: symbolSvgBody,
3883 symbolSvg: symbolSvg,
3884 symbolPng: symbolPng,
3885 symbolNormalize: symbolNormalize,
3886 signSvgBody: signSvgBody,
3887 signSvg: signSvg,
3888 signPng: signPng,
3889 signNormalize: signNormalize,
3890 columnSvg: columnSvg,
3891 columnsSvg: columnsSvg,
3892 columnPng: columnPng,
3893 columnsPng: columnsPng
3894});
3895
3896export { index$2 as font, index$1 as fsw, index as swu };
3897
3898/* support ongoing development on https://patreon.com/signwriting */