UNPKG

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