UNPKG

34.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* swu.mjs is released under the MIT License.
5*/
6
7/**
8* Sutton SignWriting Core Module v1.2.0 (https://github.com/sutton-signwriting/core)
9* Author: Steve Slevinski (https://SteveSlevinski.me)
10* convert.mjs is released under the MIT License.
11*/
12/**
13 * Function to convert a number to an SWU number character
14 * @function convert.num2swu
15 * @param {number} num - Integer value for number
16 * @returns {string} SWU number character
17 * @example
18 * convert.num2swu(500)
19 *
20 * return 'ðĪ†'
21 */
22
23
24const num2swu = num => String.fromCodePoint(0x1D80C + parseInt(num) - 250);
25/**
26 * Function to convert an array of x,y integers to two SWU number characters
27 * @function convert.coord2swu
28 * @param {number[]} coord - Array of x,y integers
29 * @returns {string} Two SWU number character
30 * @example
31 * convert.coord2swu([500, 500])
32 *
33 * return 'ðĪ†ðĪ†'
34 */
35
36
37const coord2swu = coord => coord.map(num => num2swu(num)).join('');
38/**
39 * Function to convert an SWU symbol character to a code point on plane 4
40 * @function convert.swu2code
41 * @param {string} swuSym - SWU symbol character
42 * @returns {number} Code point on plane 4
43 * @example
44 * convert.swu2code('ņ€€')
45 *
46 * return 0x40001
47 */
48
49
50const swu2code = swuSym => parseInt(swuSym.codePointAt(0));
51/**
52 * Function to convert an SWU symbol character to a 16-bit ID
53 * @function convert.swu2id
54 * @param {string} swuSym - SWU symbol character
55 * @returns {number} 16-bit ID
56 * @example
57 * convert.swu2id('ņ€€')
58 *
59 * return 1
60 */
61
62
63const swu2id = swuSym => swu2code(swuSym) - 0x40000;
64
65/* support ongoing development on https://patreon.com/signwriting */
66
67let sizes = {};
68const zoom = 2;
69const bound = 76 * zoom;
70const canvaser = document.createElement("canvas");
71canvaser.width = bound;
72canvaser.height = bound;
73const context = canvaser.getContext("2d");
74/**
75 * Function that returns the size of a symbol using an id
76 * @function font.symbolSize
77 * @param {number} id - a 16-bit number of a symbol
78 * @example
79 * font.symbolSize(1)
80 *
81 * return [15,30]
82 */
83
84const symbolSize = function (id) {
85 if (id in sizes) {
86 return [...sizes[id]];
87 }
88
89 context.clearRect(0, 0, bound, bound);
90 context.font = 30 * zoom + "px 'SuttonSignWritingLine'";
91 context.fillText(String.fromCodePoint(id + 0xF0000), 0, 0);
92 const imgData = context.getImageData(0, 0, bound, bound).data;
93 let w, h, i, s;
94
95 wloop: for (w = bound - 1; w >= 0; w--) {
96 for (h = 0; h < bound; h += 1) {
97 for (s = 0; s < 4; s += 1) {
98 i = w * 4 + h * 4 * bound + s;
99
100 if (imgData[i]) {
101 break wloop;
102 }
103 }
104 }
105 }
106
107 var width = w;
108
109 hloop: for (h = bound - 1; h >= 0; h--) {
110 for (w = 0; w < width; w += 1) {
111 for (s = 0; s < 4; s += 1) {
112 i = w * 4 + h * 4 * bound + s;
113
114 if (imgData[i]) {
115 break hloop;
116 }
117 }
118 }
119 }
120
121 var height = h + 1;
122 width = Math.ceil(width / zoom);
123 height = Math.ceil(height / zoom); // Rounding error in chrome. Manual fixes.
124
125 if (14394 == id) {
126 width = 19;
127 }
128
129 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)) {
130 width = 20;
131 }
132
133 if (31921 == id) {
134 width = 22;
135 }
136
137 if (38460 == id) {
138 width = 23;
139 }
140
141 if ([20164, 20212].includes(id)) {
142 width = 25;
143 }
144
145 if (31894 == id) {
146 width = 28;
147 }
148
149 if (46698 == id) {
150 width = 29;
151 }
152
153 if (29606 == id) {
154 width = 30;
155 }
156
157 if (44855 == id) {
158 width = 40;
159 }
160
161 if (32667 == id) {
162 width = 50;
163 }
164
165 if ([11088, 11474, 11490, 11506].includes(id)) {
166 height = 20;
167 }
168
169 if (6285 == id) {
170 height = 21;
171 }
172
173 if (40804 == id) {
174 height = 31;
175 }
176
177 if (41475 == id) {
178 height = 36;
179 } // Error in chrome. Manual fix.
180 // if (width==0 && height==0) {
181
182
183 if (width == 0 && height == 0) {
184 const sizefix = {
185 9: [15, 30],
186 10: [21, 30],
187 11: [30, 15],
188 12: [30, 21],
189 13: [15, 30],
190 14: [21, 30]
191 };
192
193 if (id in sizefix) {
194 width = sizefix[id][0];
195 height = sizefix[id][1];
196 }
197 }
198
199 if (width == 0 && height == 0) {
200 return undefined;
201 }
202
203 sizes[id] = [width, height];
204 return [width, height];
205};
206
207/**
208 * Function that returns the size of a symbol using an SWU symbol character
209 * @function swu.symbolSize
210 * @param {string} swu - an SWU symbol character
211 * @example
212 * swu.symbolSize("ņ€€")
213 *
214 * return [15,30]
215 */
216
217const symbolSize$1 = function (swu) {
218 return symbolSize(swu2id(swu));
219};
220
221/**
222 * Function that returns a plane 15 character for a symbol line using an id
223 * @function font.symbolLine
224 * @param {number} id - a 16-bit number of a symbol
225 * @example
226 * font.symbolLine(1)
227 *
228 * return '󰀁'
229 */
230const symbolLine = function (id) {
231 return String.fromCodePoint(id + 0xF0000);
232};
233/**
234 * Function that returns a plane 16 character for a symbol fill using an id
235 * @function font.symbolFill
236 * @param {number} id - a 16-bit number of a symbol
237 * @example
238 * font.symbolFill(1)
239 *
240 * return '􀀁'
241 */
242
243
244const symbolFill = function (id) {
245 return String.fromCodePoint(id + 0x100000);
246};
247/**
248 * Function that creates two text elements for a symbol using an id
249 * @function font.symbolText
250 * @param {number} id - a 16-bit number of a symbol
251 * @example
252 * font.symbolText(1)
253 *
254 * return ` <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀁</text>
255 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀁</text>`
256 */
257
258
259const symbolText = function (id) {
260 return ` <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">${symbolFill(id)}</text>
261 <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">${symbolLine(id)}</text>`;
262};
263
264/**
265 * Function that returns a plane 15 character for a symbol line using an SWU symbol character
266 * @function swu.symbolLine
267 * @param {string} swu - an SWU symbol character
268 * @example
269 * swu.symbolLine('ņ€€')
270 *
271 * return '󰀁'
272 */
273
274const symbolLine$1 = function (swu) {
275 return symbolLine(swu2id(swu));
276};
277/**
278 * Function that returns a plane 165 character for a symbol fill using an SWU symbol character
279 * @function swu.symbolFill
280 * @param {string} swu - an SWU symbol character
281 * @example
282 * swu.symbolFill('ņ€€')
283 *
284 * return '􀀁'
285 */
286
287
288const symbolFill$1 = function (swu) {
289 return symbolFill(swu2id(swu));
290};
291/**
292 * Function that creates two text elements for a symbol using an SWU symbol character
293 * @function swu.symbolText
294 * @param {string} swu - an SWU symbol character
295 * @example
296 * swu.symbolText('ņ€€')
297 *
298 * return ` <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀁</text>
299 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀁</text>`
300 */
301
302
303const symbolText$1 = function (swu) {
304 return symbolText(swu2id(swu));
305};
306
307/**
308* Sutton SignWriting Core Module v1.2.0 (https://github.com/sutton-signwriting/core)
309* Author: Steve Slevinski (https://SteveSlevinski.me)
310* style.mjs is released under the MIT License.
311*/
312
313/**
314 * Object of regular expressions for style strings
315 *
316 * { colorize, colorhex, colorname, padding, zoom, zoomsym, classbase, id, colorbase, color, colors, background, detail, detailsym, classes, full }
317 * @alias style.re
318 * @type {object}
319 */
320let re = {
321 'colorize': 'C',
322 'colorhex': '(?:[0-9a-fA-F]{3}){1,2}',
323 'colorname': '[a-zA-Z]+',
324 'padding': 'P[0-9]{2}',
325 'zoom': 'Z(?:[0-9]+(?:\\.[0-9]+)?|x)',
326 'zoomsym': 'Z[0-9]{2},[0-9]+(?:\\.[0-9]+)?(?:,[0-9]{3}x[0-9]{3})?',
327 'classbase': '-?[_a-zA-Z][_a-zA-Z0-9-]{0,100}',
328 'id': '[a-zA-Z][_a-zA-Z0-9-]{0,100}'
329};
330re.colorbase = `(?:${re.colorhex}|${re.colorname})`;
331re.color = `_${re.colorbase}_`;
332re.colors = `_${re.colorbase}(?:,${re.colorbase})?_`;
333re.background = `G${re.color}`;
334re.detail = `D${re.colors}`;
335re.detailsym = `D[0-9]{2}${re.colors}`;
336re.classes = `${re.classbase}(?: ${re.classbase})*`;
337re.full = `-(${re.colorize})?(${re.padding})?(${re.background})?(${re.detail})?(${re.zoom})?(?:-((?:${re.detailsym})*)((?:${re.zoomsym})*))?(?:-(${re.classes})?!(?:(${re.id})!)?)?`;
338
339const prefixColor = color => {
340 const regex = new RegExp(`^${re.colorhex}$`);
341 return (regex.test(color) ? '#' : '') + color;
342};
343/**
344 * Function to parse style string to object
345 * @function style.parse
346 * @param {string} styleString - a style string
347 * @returns {object} elements of style string
348 * @example
349 * style.parse('-CP10G_blue_D_red,Cyan_')
350 *
351 * return {
352 * 'colorize': true,
353 * 'padding': 10,
354 * 'background': 'blue',
355 * 'detail': ['red', 'Cyan']
356 * }
357 */
358
359
360const parse = styleString => {
361 const regex = `^${re.full}`;
362 const m = (typeof styleString === 'string' ? styleString.match(new RegExp(regex)) : []) || [];
363 return {
364 'colorize': !m[1] ? undefined : !!m[1],
365 'padding': !m[2] ? undefined : parseInt(m[2].slice(1)),
366 'background': !m[3] ? undefined : prefixColor(m[3].slice(2, -1)),
367 'detail': !m[4] ? undefined : m[4].slice(2, -1).split(',').map(prefixColor),
368 'zoom': !m[5] ? undefined : m[5] === 'Zx' ? 'x' : parseFloat(m[5].slice(1)),
369 'detailsym': !m[6] ? undefined : m[6].match(new RegExp(re.detailsym, 'g')).map(val => {
370 const parts = val.split('_');
371 const detail = parts[1].split(',').map(prefixColor);
372 return {
373 'index': parseInt(parts[0].slice(1)),
374 'detail': detail
375 };
376 }),
377 'zoomsym': !m[7] ? undefined : m[7].match(new RegExp(re.zoomsym, 'g')).map(val => {
378 const parts = val.split(',');
379 return {
380 'index': parseInt(parts[0].slice(1)),
381 'zoom': parseFloat(parts[1]),
382 'offset': !parts[2] ? undefined : parts[2].split('x').map(val => parseInt(val) - 500)
383 };
384 }),
385 'classes': !m[8] ? undefined : m[8],
386 'id': !m[9] ? undefined : m[9]
387 };
388};
389
390/* support ongoing development on https://patreon.com/signwriting */
391
392/**
393* Sutton SignWriting Core Module v1.2.0 (https://github.com/sutton-signwriting/core)
394* Author: Steve Slevinski (https://SteveSlevinski.me)
395* swu.mjs is released under the MIT License.
396*/
397
398/**
399 * Object of regular expressions for SWU strings in UTF-16
400 *
401 * { symbol, coord, sort, box, prefix, spatial, signbox, sign, sortable }
402 * @alias swu.re
403 * @type {object}
404 */
405let re$1 = {
406 'symbol': '(?:(?:\uD8C0[\uDC01-\uDFFF])|(?:[\uD8C1-\uD8FC][\uDC00-\uDFFF])|(?:\uD8FD[\uDC00-\uDC80]))',
407 'coord': '(?:\uD836[\uDC0C-\uDDFF]){2}',
408 'sort': '\uD836\uDC00',
409 'box': '\uD836[\uDC01-\uDC04]'
410};
411re$1.prefix = `(?:${re$1.sort}(?:${re$1.symbol})+)`;
412re$1.spatial = `${re$1.symbol}${re$1.coord}`;
413re$1.signbox = `${re$1.box}${re$1.coord}(?:${re$1.spatial})*`;
414re$1.sign = `${re$1.prefix}?${re$1.signbox}`;
415re$1.sortable = `${re$1.prefix}${re$1.signbox}`;
416
417/**
418 * Object of regular expressions for style strings
419 *
420 * { colorize, colorhex, colorname, padding, zoom, zoomsym, classbase, id, colorbase, color, colors, background, detail, detailsym, classes, full }
421 * @alias style.re
422 * @type {object}
423 */
424let re$1$1 = {
425 'colorize': 'C',
426 'colorhex': '(?:[0-9a-fA-F]{3}){1,2}',
427 'colorname': '[a-zA-Z]+',
428 'padding': 'P[0-9]{2}',
429 'zoom': 'Z(?:[0-9]+(?:\\.[0-9]+)?|x)',
430 'zoomsym': 'Z[0-9]{2},[0-9]+(?:\\.[0-9]+)?(?:,[0-9]{3}x[0-9]{3})?',
431 'classbase': '-?[_a-zA-Z][_a-zA-Z0-9-]{0,100}',
432 'id': '[a-zA-Z][_a-zA-Z0-9-]{0,100}'
433};
434re$1$1.colorbase = `(?:${re$1$1.colorhex}|${re$1$1.colorname})`;
435re$1$1.color = `_${re$1$1.colorbase}_`;
436re$1$1.colors = `_${re$1$1.colorbase}(?:,${re$1$1.colorbase})?_`;
437re$1$1.background = `G${re$1$1.color}`;
438re$1$1.detail = `D${re$1$1.colors}`;
439re$1$1.detailsym = `D[0-9]{2}${re$1$1.colors}`;
440re$1$1.classes = `${re$1$1.classbase}(?: ${re$1$1.classbase})*`;
441re$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})!)?)?`;
442
443/** 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.
444 * [Characters set definitions](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-07.html#rfc.section.2.2)
445 * @module convert
446 */
447/**
448 * Function to convert an SWU number character to an integer
449 * @function convert.swu2num
450 * @param {string} swuNum - SWU number character
451 * @returns {number} Integer value for number
452 * @example
453 * convert.swu2num('ðĪ†')
454 *
455 * return 500
456 */
457
458
459const swu2num = swuNum => parseInt(swuNum.codePointAt(0)) - 0x1D80C + 250;
460/**
461 * Function to convert two SWU number characters to an array of x,y integers
462 * @function convert.swu2coord
463 * @param {string} swuCoord - Two SWU number character
464 * @returns {number[]} Array of x,y integers
465 * @example
466 * convert.swu2coord('ðĪ†ðĪ†')
467 *
468 * return [500, 500]
469 */
470
471
472const swu2coord = swuCoord => [swu2num(swuCoord.slice(0, 2)), swu2num(swuCoord.slice(2, 4))];
473/**
474 * Function to convert an SWU symbol character to a code point on plane 4
475 * @function convert.swu2code
476 * @param {string} swuSym - SWU symbol character
477 * @returns {number} Code point on plane 4
478 * @example
479 * convert.swu2code('ņ€€')
480 *
481 * return 0x40001
482 */
483
484
485const swu2code$1 = swuSym => parseInt(swuSym.codePointAt(0));
486
487const parse$1 = {
488 /**
489 * Function to parse an swu symbol with optional coordinate and style string
490 * @function swu.parse.symbol
491 * @param {string} swuSym - an swu symbol
492 * @returns {object} elements of swu symbol
493 * @example
494 * swu.parse.symbol('ņ€€ðĪ†ðĪ†-C')
495 *
496 * return {
497 * 'symbol': 'ņ€€',
498 * 'coord': [500, 500],
499 * 'style': '-C'
500 * }
501 */
502 symbol: swuSym => {
503 const regex = `^(${re$1.symbol})(${re$1.coord})?(${re$1$1.full})?`;
504 const symbol = typeof swuSym === 'string' ? swuSym.match(new RegExp(regex)) : undefined;
505 return {
506 'symbol': symbol ? symbol[1] : undefined,
507 'coord': symbol && symbol[2] ? swu2coord(symbol[2]) : undefined,
508 'style': symbol ? symbol[3] : undefined
509 };
510 },
511
512 /**
513 * Function to parse an swu sign with style string
514 * @function swu.parse.sign
515 * @param {string} swuSign - an swu sign
516 * @returns {object} elements of swu sign
517 * @example
518 * swu.parse.sign('𝠀ņ€€’ņ€€šņ‹šĨņ‹›Đ𝠃ðĪŸðĪĐņ‹›ĐðĢĩðĪņ€€’ðĪ‡ðĢĪņ‹šĨðĪðĪ†ņ€€šðĢŪðĢ­-C')
519 *
520 * return {
521 * sequence: ['ņ€€’','ņ€€š','ņ‹šĨ','ņ‹›Đ''],
522 * box: '𝠃',
523 * max: [525, 535],
524 * spatials: [
525 * {
526 * symbol: 'ņ‹›Đ',
527 * coord: [483, 510]
528 * },
529 * {
530 * symbol: 'ņ€€’',
531 * coord: [501, 466]
532 * },
533 * {
534 * symbol: 'ņ‹šĨ',
535 * coord: [510, 500]
536 * },
537 * {
538 * symbol: 'ņ€€š',
539 * coord: [476, 475]
540 * }
541 * ],
542 * style: '-C'
543 * }
544 */
545 sign: swuSign => {
546 const regex = `^(${re$1.prefix})?(${re$1.signbox})(${re$1$1.full})?`;
547 const sign = typeof swuSign === 'string' ? swuSign.match(new RegExp(regex)) : undefined;
548
549 if (sign) {
550 return {
551 'sequence': sign[1] ? sign[1].slice(2).match(/.{2}/g) : undefined,
552 'box': sign[2].slice(0, 2),
553 'max': swu2coord(sign[2].slice(2, 6)),
554 'spatials': sign[2].length < 7 ? undefined : sign[2].slice(6).match(/(.{6})/g).map(m => {
555 return {
556 symbol: m.slice(0, 2),
557 coord: swu2coord(m.slice(2))
558 };
559 }),
560 'style': sign[3]
561 };
562 } else {
563 return {};
564 }
565 }
566};
567/**
568 * Array of plane 4 code points for categories of symbols: hand, movement, dynamics, head, trunk & limb, location, and punctuation.
569 * @alias swu.category
570 * @type {array}
571 */
572
573const category = [0x40001, 0x461e1, 0x4bca1, 0x4bfa1, 0x4e8e1, 0x4efa1, 0x4f2a1];
574/**
575 * Object of symbol ranges with starting and ending code points on plane 4.
576 *
577 * { all, writing, hand, movement, dynamic, head, hcenter, vcenter, trunk, limb, location, punctuation }
578 * @alias swu.ranges
579 * @type {object}
580 */
581
582const ranges = {
583 'all': [0x40001, 0x4f480],
584 'writing': [0x40001, 0x4efa0],
585 'hand': [0x40001, 0x461e0],
586 'movement': [0x461e1, 0x4bca0],
587 'dynamic': [0x4bca1, 0x4bfa0],
588 'head': [0x4bfa1, 0x4e8e0],
589 'hcenter': [0x4bfa1, 0x4e8e0],
590 'vcenter': [0x4bfa1, 0x4ec40],
591 'trunk': [0x4e8e1, 0x4ec40],
592 'limb': [0x4ec41, 0x4efa0],
593 'location': [0x4efa1, 0x4f2a0],
594 'punctuation': [0x4f2a1, 0x4f480]
595};
596
597/**
598 * Array of colors associated with the seven symbol categories.
599 * @alias swu.colors
600 * @type {array}
601 */
602
603const colors = ['#0000CC', '#CC0000', '#FF0099', '#006600', '#000000', '#884411', '#FF9900'];
604/**
605 * Function that returns the standardized color for a symbol.
606 * @function swu.colorize
607 * @param {string} swuSym - an SWU symbol character
608 * @returns {string} name of standardized color for symbol
609 * @example
610 * swu.colorize('ņ€€')
611 *
612 * return '#0000CC'
613 */
614
615const colorize = swuSym => {
616 const parsed = parse$1.symbol(swuSym);
617 let color = '#000000';
618
619 if (parsed.symbol) {
620 const code = swu2code$1(parsed.symbol);
621 const index = category.findIndex(val => val > code);
622 color = colors[index < 0 ? 6 : index - 1];
623 }
624
625 return color;
626};
627
628/* support ongoing development on https://patreon.com/signwriting */
629
630/**
631 * Function that creates an SVG image from an SWU symbol character with an optional style string
632 * @function swu.symbolSvg
633 * @param {string} swuSym - an SWU symbol character with optional style string
634 * @example
635 * swu.symbolSvg('ņ€€-CP10G_green_Z2')
636 *
637 * return `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="70" height="100" viewBox="490 490 35 50">
638 * <text font-size="0">ņ€€-CP10G_green_Z2</text>
639 * <rect x="490" y="490" width="35" height="50" style="fill:green;" />
640 * <g transform="translate(500,500)">
641 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀁</text>
642 * <text class="sym-line" fill="#0000CC" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀁</text>
643 * </g>
644 * </svg>`
645 */
646
647const symbolSvg = swuSym => {
648 const parsed = parse$1.symbol(swuSym);
649 const blank = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="1" height="1"></svg>';
650
651 if (parsed.symbol) {
652 let size = symbolSize$1(parsed.symbol);
653
654 if (size) {
655 let styling = parse(parsed.style);
656 let line;
657 let symSvg = symbolText$1(parsed.symbol);
658 symSvg = ` <g transform="translate(500,500)">
659${symSvg}
660 </g>`;
661
662 if (styling.colorize) {
663 line = colorize(parsed.symbol);
664 } else if (styling.detail) {
665 line = styling.detail[0];
666 }
667
668 if (line) {
669 symSvg = symSvg.replace(/class="sym-line" fill="black"/, `class="sym-line" fill="${line}"`);
670 }
671
672 let fill = styling.detail && styling.detail[1];
673
674 if (fill) {
675 symSvg = symSvg.replace(/class="sym-fill" fill="white"/, `class="sym-fill" fill="${fill}"`);
676 }
677
678 let x1 = 500;
679 let y1 = 500;
680 let background = '';
681
682 if (styling.padding) {
683 x1 -= styling.padding;
684 y1 -= styling.padding;
685 size[0] += styling.padding * 2;
686 size[1] += styling.padding * 2;
687 }
688
689 if (styling.background) {
690 background = `\n <rect x="${x1}" y="${y1}" width="${size[0]}" height="${size[1]}" style="fill:${styling.background};" />`;
691 }
692
693 let sizing = '';
694
695 if (styling.zoom != 'x') {
696 sizing = ` width="${size[0] * (styling.zoom ? styling.zoom : 1)}" height="${size[1] * (styling.zoom ? styling.zoom : 1)}"`;
697 }
698
699 let classes = '';
700
701 if (styling.classes) {
702 classes = ` class="${styling.classes}"`;
703 }
704
705 let id = '';
706
707 if (styling.id) {
708 id = ` id="${styling.id}"`;
709 }
710
711 return `<svg${classes}${id} version="1.1" xmlns="http://www.w3.org/2000/svg"${sizing} viewBox="${x1} ${y1} ${size[0]} ${size[1]}">
712 <text font-size="0">${swuSym}</text>${background}
713${symSvg}
714</svg>`;
715 }
716 }
717
718 return blank;
719};
720
721const symbolCanvas = function (swuSym) {
722 const parsed = parse$1.symbol(swuSym);
723
724 if (parsed.symbol) {
725 let size = symbolSize$1(parsed.symbol);
726
727 if (size) {
728 const canvas = document.createElement('canvas');
729 const context = canvas.getContext('2d');
730 let styling = parse(parsed.style);
731 let line = 'black';
732
733 if (styling.colorize) {
734 line = colorize(parsed.symbol);
735 } else if (styling.detail) {
736 line = styling.detail[0];
737 }
738
739 let fill = styling.detail && styling.detail[1] || 'white';
740 let x1 = 500;
741 let x2 = x1 + size[0];
742 let y1 = 500;
743 let y2 = y1 + size[1];
744
745 if (styling.padding) {
746 x1 -= styling.padding;
747 y1 -= styling.padding;
748 x2 += styling.padding;
749 y2 += styling.padding;
750 }
751
752 let sizing = 1;
753
754 if (styling.zoom != 'x') {
755 sizing = styling.zoom;
756 }
757
758 let w = (x2 - x1) * sizing;
759 let h = (y2 - y1) * sizing;
760 canvas.width = w ? w : 1;
761 canvas.height = h ? h : 1;
762
763 if (styling.background) {
764 context.rect(0, 0, w, h);
765 context.fillStyle = styling.background;
766 context.fill();
767 }
768
769 context.font = 30 * sizing + "px 'SuttonSignWritingFill'";
770 context.fillStyle = fill;
771 context.fillText(symbolFill$1(parsed.symbol), (500 - x1) * sizing, (500 - y1) * sizing);
772 context.font = 30 * sizing + "px 'SuttonSignWritingLine'";
773 context.fillStyle = line;
774 context.fillText(symbolLine$1(parsed.symbol), (500 - x1) * sizing, (500 - y1) * sizing);
775 return canvas;
776 }
777 }
778};
779/**
780 * Function that creates a binary PNG image from an SWU symbol character with an optional stle string
781 * @function swu.symbolPng
782 * @param {string} swuSym - an SWU symbol character with optional style string
783 * @example
784 * swu.symbolPng('ņ€€-CP10G_green_Z2')
785 *
786 * return 'data:image/png;base64,iVBORw...'
787 */
788
789
790const symbolPng = swuSym => {
791 const canvas = symbolCanvas(swuSym);
792 const png = canvas.toDataURL("image/png");
793 canvas.remove();
794 return png;
795};
796
797const blank = null;
798/**
799 * Function that normalizes a symbol with a minimum coordinate for a center of 500,500
800 * @function swu.symbolNormalize
801 * @param {string} swuSym - an SWU symbol character with optional coordinate and style string
802 * @example
803 * swu.symbolNormalize('ņ€€')
804 *
805 * return 'ņ€€ðĢŋðĢ·'
806 */
807
808const symbolNormalize = swuSym => {
809 const parsed = parse$1.symbol(swuSym);
810
811 if (parsed.symbol) {
812 let size = symbolSize$1(parsed.symbol);
813
814 if (size) {
815 return `${parsed.symbol}${coord2swu([500 - parseInt(size[0] / 2), 500 - parseInt(size[1] / 2)])}${parsed.style || ''}`;
816 }
817 } else {
818 return blank;
819 }
820};
821
822/**
823 * Function that creates an SVG image from an SWU sign with an optional style string
824 * @function swu.signSvg
825 * @param {string} swuSign - an SWU sign with optional style string
826 * @example
827 * swu.signSvg('𝠀ņ€€’ņ€€šņ‹šĨņ‹›Đ𝠃ðĪŸðĪĐņ‹›ĐðĢĩðĪņ€€’ðĪ‡ðĢĪņ‹šĨðĪðĪ†ņ€€šðĢŪðĢ­-P10G_green_D_yellow,ff0ff0_Z2')
828 *
829 * return `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="138" height="178" viewBox="466 456 69 89">
830 * <text font-size="0">𝠀ņ€€’ņ€€šņ‹šĨņ‹›Đ𝠃ðĪŸðĪĐņ‹›ĐðĢĩðĪņ€€’ðĪ‡ðĢĪņ‹šĨðĪðĪ†ņ€€šðĢŪðĢ­-P10G_green_D_yellow,ff0ff0_Z2</text>
831 * <rect x="466" y="456" width="69" height="89" style="fill:green;" />
832 * <g transform="translate(483,510)">
833 * <text class="sym-fill" fill="#ff0ff0" style="pointer-events:none;font-family:\'SuttonSignWritingFill\';font-size:30px;">ô‹›Đ</text>
834 * <text class="sym-line" fill="yellow" style="pointer-events:none;font-family:\'SuttonSignWritingLine\';font-size:30px;">óŧ›Đ</text>
835 * </g>
836 * <g transform="translate(501,466)">
837 * <text class="sym-fill" fill="#ff0ff0" style="pointer-events:none;font-family:\'SuttonSignWritingFill\';font-size:30px;">􀀒</text>
838 * <text class="sym-line" fill="yellow" style="pointer-events:none;font-family:\'SuttonSignWritingLine\';font-size:30px;">󰀒</text>
839 * </g>
840 * <g transform="translate(510,500)">
841 * <text class="sym-fill" fill="#ff0ff0" style="pointer-events:none;font-family:\'SuttonSignWritingFill\';font-size:30px;">ô‹šĨ</text>
842 * <text class="sym-line" fill="yellow" style="pointer-events:none;font-family:\'SuttonSignWritingLine\';font-size:30px;">óŧšĨ</text>
843 * </g>
844 * <g transform="translate(476,475)">
845 * <text class="sym-fill" fill="#ff0ff0" style="pointer-events:none;font-family:\'SuttonSignWritingFill\';font-size:30px;">􀀚</text>
846 * <text class="sym-line" fill="yellow" style="pointer-events:none;font-family:\'SuttonSignWritingLine\';font-size:30px;">󰀚</text>
847 * </g>
848 * </svg>`
849 */
850
851const signSvg = swuSign => {
852 let parsed = parse$1.sign(swuSign);
853 const blank = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="1" height="1"></svg>';
854
855 if (parsed.spatials) {
856 let styling = parse(parsed.style);
857
858 if (styling.detailsym) {
859 styling.detailsym.forEach(sym => {
860 if (parsed.spatials[sym.index - 1]) {
861 parsed.spatials[sym.index - 1].detail = sym.detail;
862 }
863 });
864 }
865
866 let x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
867 let y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
868 let x2 = parsed.max[0];
869 let y2 = parsed.max[1];
870
871 if (styling.zoomsym) {
872 styling.zoomsym.forEach(sym => {
873 if (parsed.spatials[sym.index - 1]) {
874 parsed.spatials[sym.index - 1].zoom = sym.zoom;
875
876 if (sym.offset) {
877 parsed.spatials[sym.index - 1].coord[0] += sym.offset[0];
878 parsed.spatials[sym.index - 1].coord[1] += sym.offset[1];
879 }
880
881 let size = symbolSize$1(parsed.spatials[sym.index - 1].symbol);
882 x2 = Math.max(x2, parsed.spatials[sym.index - 1].coord[0] + size[0] * sym.zoom);
883 y2 = Math.max(y2, parsed.spatials[sym.index - 1].coord[1] + size[1] * sym.zoom);
884 }
885 });
886 }
887
888 let classes = '';
889
890 if (styling.classes) {
891 classes = ` class="${styling.classes}"`;
892 }
893
894 let id = '';
895
896 if (styling.id) {
897 id = ` id="${styling.id}"`;
898 }
899
900 let background = '';
901
902 if (styling.padding) {
903 x1 -= styling.padding;
904 y1 -= styling.padding;
905 x2 += styling.padding;
906 y2 += styling.padding;
907 }
908
909 if (styling.background) {
910 background = `\n <rect x="${x1}" y="${y1}" width="${x2 - x1}" height="${y2 - y1}" style="fill:${styling.background};" />`;
911 }
912
913 let sizing = '';
914
915 if (styling.zoom != 'x') {
916 sizing = ` width="${(x2 - x1) * (styling.zoom ? styling.zoom : 1)}" height="${(y2 - y1) * (styling.zoom ? styling.zoom : 1)}"`;
917 }
918
919 let svg = `<svg${classes}${id} version="1.1" xmlns="http://www.w3.org/2000/svg"${sizing} viewBox="${x1} ${y1} ${x2 - x1} ${y2 - y1}">
920 <text font-size="0">${swuSign}</text>${background}`;
921 const line = styling.detail && styling.detail[0];
922 const fill = styling.detail && styling.detail[1];
923 svg += '\n' + parsed.spatials.map(spatial => {
924 let svg = symbolText$1(spatial.symbol, spatial.coord);
925 let symLine = line;
926
927 if (spatial.detail) {
928 symLine = spatial.detail[0];
929 } else if (styling.colorize) {
930 symLine = colorize(spatial.symbol);
931 }
932
933 if (symLine) {
934 svg = svg.replace(/class="sym-line" fill="black"/, `class="sym-line" fill="${symLine}"`);
935 }
936
937 let symFill = fill;
938
939 if (spatial.detail && spatial.detail[1]) {
940 symFill = spatial.detail[1];
941 }
942
943 if (symFill) {
944 svg = svg.replace(/class="sym-fill" fill="white"/, `class="sym-fill" fill="${symFill}"`);
945 }
946
947 if (spatial.zoom) {
948 svg = `<g transform="scale(${spatial.zoom})">${svg}</g>`;
949 }
950
951 return ` <g transform="translate(${spatial.coord[0]},${spatial.coord[1]})">
952${svg}
953 </g>`;
954 }).join('\n');
955 svg += '\n</svg>';
956 return svg;
957 }
958
959 return blank;
960};
961
962const signCanvas = function (swuSign) {
963 const parsed = parse$1.sign(swuSign);
964
965 if (parsed.spatials) {
966 const canvas = document.createElement('canvas');
967 const context = canvas.getContext('2d');
968 let styling = parse(parsed.style);
969
970 if (styling.detailsym) {
971 styling.detailsym.forEach(sym => {
972 if (parsed.spatials[sym.index - 1]) {
973 parsed.spatials[sym.index - 1].detail = sym.detail;
974 }
975 });
976 }
977
978 let x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
979 let y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
980 let x2 = parsed.max[0];
981 let y2 = parsed.max[1];
982
983 if (styling.zoomsym) {
984 styling.zoomsym.forEach(sym => {
985 if (parsed.spatials[sym.index - 1]) {
986 parsed.spatials[sym.index - 1].zoom = sym.zoom;
987
988 if (sym.offset) {
989 parsed.spatials[sym.index - 1].coord[0] += sym.offset[0];
990 parsed.spatials[sym.index - 1].coord[1] += sym.offset[1];
991 }
992
993 let size = symbolSize$1(parsed.spatials[sym.index - 1].symbol);
994 x2 = Math.max(x2, parsed.spatials[sym.index - 1].coord[0] + size[0] * sym.zoom);
995 y2 = Math.max(y2, parsed.spatials[sym.index - 1].coord[1] + size[1] * sym.zoom);
996 }
997 });
998 }
999
1000 if (styling.padding) {
1001 x1 -= styling.padding;
1002 y1 -= styling.padding;
1003 x2 += styling.padding;
1004 y2 += styling.padding;
1005 }
1006
1007 let sizing = 1;
1008
1009 if (styling.zoom != 'x') {
1010 sizing = styling.zoom;
1011 }
1012
1013 let w = (x2 - x1) * sizing;
1014 let h = (y2 - y1) * sizing;
1015 canvas.width = w ? w : 1;
1016 canvas.height = h ? h : 1;
1017
1018 if (styling.background) {
1019 context.rect(0, 0, w, h);
1020 context.fillStyle = styling.background;
1021 context.fill();
1022 }
1023
1024 const line = styling.detail && styling.detail[0] || "black";
1025 const fill = styling.detail && styling.detail[1] || "white";
1026 parsed.spatials.forEach(spatial => {
1027 let symLine = line;
1028
1029 if (spatial.detail) {
1030 symLine = spatial.detail[0];
1031 } else if (styling.colorize) {
1032 symLine = colorize(spatial.symbol);
1033 }
1034
1035 let symFill = fill;
1036
1037 if (spatial.detail && spatial.detail[1]) {
1038 symFill = spatial.detail[1];
1039 }
1040
1041 let symZoom = spatial.zoom || 1;
1042 context.font = 30 * sizing * symZoom + "px 'SuttonSignWritingFill'";
1043 context.fillStyle = symFill;
1044 context.fillText(symbolFill$1(spatial.symbol), (spatial.coord[0] - x1) * sizing, (spatial.coord[1] - y1) * sizing);
1045 context.font = 30 * sizing * symZoom + "px 'SuttonSignWritingLine'";
1046 context.fillStyle = symLine;
1047 context.fillText(symbolLine$1(spatial.symbol), (spatial.coord[0] - x1) * sizing, (spatial.coord[1] - y1) * sizing);
1048 });
1049 return canvas;
1050 }
1051};
1052/**
1053 * Function that creates a binary PNG image from an SWU sign with an optional style string
1054 * @function swu.signPng
1055 * @param {string} swuSign - an SWU sign with optional style string
1056 * @example
1057 * swu.signPng('𝠀ņ€€’ņ€€šņ‹šĨņ‹›Đ𝠃ðĪŸðĪĐņ‹›ĐðĢĩðĪņ€€’ðĪ‡ðĢĪņ‹šĨðĪðĪ†ņ€€šðĢŪðĢ­')
1058 *
1059 * return 'data:image/png;base64,iVBORw...'
1060 */
1061
1062
1063const signPng = swuSign => {
1064 const canvas = signCanvas(swuSign);
1065 const png = canvas.toDataURL("image/png");
1066 canvas.remove();
1067 return png;
1068};
1069
1070/**
1071 * Function that normalizes an SWU sign for a center of 500,500
1072 * @function swu.signNormalize
1073 * @param {string} swuSign - an SWU sign with optional style string
1074 * @example
1075 * swu.signNormalize('𝠀ņ€€’ņ€€šņ‹šĨņ‹›Đ𝠃ðĪŸðĪĐņ‹›ĐðĢĩðĪņ€€’ðĪ‡ðĢĪņ‹šĨðĪðĪ†ņ€€šðĢŪðĢ­')
1076 *
1077 * return '𝠀ņ€€’ņ€€šņ‹šĨņ‹›Đ𝠃ðĪŸðĪĐņ‹›ĐðĢĩðĪņ€€’ðĪ‡ðĢĪņ‹šĨðĪðĪ†ņ€€šðĢŪðĢ­'
1078 */
1079
1080const signNormalize = swuSign => {
1081 const parsed = parse$1.sign(swuSign);
1082
1083 if (parsed.spatials) {
1084 const symbolsizes = parsed.spatials.reduce((output, spatial) => {
1085 const size = symbolSize$1(spatial.symbol);
1086 output[spatial.symbol] = {
1087 width: size[0],
1088 height: size[1]
1089 };
1090 return output;
1091 }, {});
1092
1093 const bbox = symbols => {
1094 const x1 = Math.min(...symbols.map(spatial => spatial.coord[0]));
1095 const y1 = Math.min(...symbols.map(spatial => spatial.coord[1]));
1096 const x2 = Math.max(...symbols.map(spatial => spatial.coord[0] + parseInt(symbolsizes[spatial.symbol].width)));
1097 const y2 = Math.max(...symbols.map(spatial => spatial.coord[1] + parseInt(symbolsizes[spatial.symbol].height)));
1098 return {
1099 x1: x1,
1100 y1: y1,
1101 x2: x2,
1102 y2: y2
1103 };
1104 };
1105
1106 const hrange = ranges['hcenter'];
1107 const hsyms = parsed.spatials.filter(spatial => {
1108 const dec = parseInt(spatial.symbol.slice(1, 4), 16);
1109 return hrange[0] <= dec && hrange[1] >= dec;
1110 });
1111 const vrange = ranges['vcenter'];
1112 const vsyms = parsed.spatials.filter(spatial => {
1113 const dec = parseInt(spatial.symbol.slice(1, 4), 16);
1114 return vrange[0] <= dec && vrange[1] >= dec;
1115 });
1116 let abox = bbox(parsed.spatials);
1117 let max = [abox.x2, abox.y2];
1118
1119 if (hsyms.length) {
1120 const hbox = bbox(hsyms);
1121 abox.x1 = hbox.x1;
1122 abox.x2 = hbox.x2;
1123 }
1124
1125 if (vsyms.length) {
1126 const vbox = bbox(vsyms);
1127 abox.y1 = vbox.y1;
1128 abox.y2 = vbox.y2;
1129 }
1130
1131 const offset = [parseInt((abox.x2 + abox.x1) / 2) - 500, parseInt((abox.y2 + abox.y1) / 2) - 500];
1132 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 || '');
1133 return swuout;
1134 }
1135};
1136
1137export { signNormalize, signPng, signSvg, symbolFill$1 as symbolFill, symbolLine$1 as symbolLine, symbolNormalize, symbolPng, symbolSize$1 as symbolSize, symbolSvg, symbolText$1 as symbolText };
1138
1139/* support ongoing development on https://patreon.com/signwriting */