UNPKG

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