UNPKG

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