@sutton-signwriting/font-ttf
Version:
a javascript package for node that generates SVG and PNG images for individual symbols, complete signs, and structured text. The package covers the entire set of the International SignWritnig Alphabet 2010 (ISWA 2010).
4,414 lines (4,285 loc) • 147 kB
JavaScript
/**
* Sutton SignWriting TrueType Font Module v1.6.0 (https://github.com/sutton-signwriting/font-ttf)
* Author: Steve Slevinski (https://SteveSlevinski.me)
* index.mjs is released under the MIT License.
*/
/**
* Function that appends font-face CSS for the Sutton SignWriting fonts for system installed fonts, relative directory fonts, or content delivery network
* @function font.cssAppend
* @param {string} dir - an optional relative directory for font location
* @example
* font.cssAppend('./font/')
*/
const cssAppend = function (dir = '') {
const id = "SgnwFontCss";
if (!document.getElementById(id)) {
const style = document.createElement('style');
style.setAttribute("id", "SgnwFontCss");
style.appendChild(document.createTextNode(`
@font-face {
font-family: "SuttonSignWritingLine";
src:
local('SuttonSignWritingLine'),
${dir ? `url('${dir}SuttonSignWritingLine.ttf') format('truetype'),` : ""}
url('https://cdn.jsdelivr.net/npm/@sutton-signwriting/font-ttf@1.0.0/font/SuttonSignWritingLine.ttf') format('truetype');
}
@font-face {
font-family: "SuttonSignWritingFill";
src:
local('SuttonSignWritingFill'),
${dir ? `url('${dir}SuttonSignWritingFill.ttf') format('truetype'),` : ""}
url('https://cdn.jsdelivr.net/npm/@sutton-signwriting/font-ttf@1.0.0/font/SuttonSignWritingFill.ttf') format('truetype');
}
@font-face {
font-family: "SuttonSignWritingOneD";
src:
local('SuttonSignWritingOneD'),
${dir ? `url('${dir}SuttonSignWritingOneD.ttf') format('truetype'),` : ""}
url('https://cdn.jsdelivr.net/npm/@sutton-signwriting/font-ttf@1.0.0/font/SuttonSignWritingOneD.ttf') format('truetype');
}
`));
document.head.appendChild(style);
}
};
let sizes = {};
const zoom = 2;
const bound = 76 * zoom;
let context;
/**
* Function that returns the size of a symbol using an id
* @function font.symbolSize
* @param {number} id - a 16-bit number of a symbol
* @returns {number[]} width and height of symbol
* @example
* font.symbolSize(1)
*
* return [15,30]
*/
const symbolSize$2 = function (id) {
if (id in sizes) {
return [...sizes[id]];
}
if (!context) {
const canvaser = document.createElement("canvas");
canvaser.width = bound;
canvaser.height = bound;
context = canvaser.getContext("2d", {
willReadFrequently: true
});
}
context.clearRect(0, 0, bound, bound);
context.font = 30 * zoom + "px 'SuttonSignWritingLine'";
context.fillText(String.fromCodePoint(id + 0xF0000), 0, 0);
const imgData = context.getImageData(0, 0, bound, bound).data;
let w, h, i, s;
wloop: for (w = bound - 1; w >= 0; w--) {
for (h = 0; h < bound; h += 1) {
for (s = 0; s < 4; s += 1) {
i = w * 4 + h * 4 * bound + s;
if (imgData[i]) {
break wloop;
}
}
}
}
var width = w;
hloop: for (h = bound - 1; h >= 0; h--) {
for (w = 0; w < width; w += 1) {
for (s = 0; s < 4; s += 1) {
i = w * 4 + h * 4 * bound + s;
if (imgData[i]) {
break hloop;
}
}
}
}
var height = h + 1;
width = Math.ceil(width / zoom);
height = Math.ceil(height / zoom);
// Rounding error in chrome. Manual fixes.
if (14394 == id) {
width = 19;
}
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)) {
width = 20;
}
if (31921 == id) {
width = 22;
}
if (38460 == id) {
width = 23;
}
if ([20164, 20212].includes(id)) {
width = 25;
}
if (31894 == id) {
width = 28;
}
if (46698 == id) {
width = 29;
}
if (29606 == id) {
width = 30;
}
if (44855 == id) {
width = 40;
}
if (32667 == id) {
width = 50;
}
if ([11088, 11474, 11490, 11506].includes(id)) {
height = 20;
}
if (6285 == id) {
height = 21;
}
if (40804 == id) {
height = 31;
}
if (41475 == id) {
height = 36;
}
// Error in chrome. Manual fix.
// if (width==0 && height==0) {
if (width == 0 && height == 0) {
const sizefix = {
9: [15, 30],
10: [21, 30],
11: [30, 15],
12: [30, 21],
13: [15, 30],
14: [21, 30]
};
if (id in sizefix) {
width = sizefix[id][0];
height = sizefix[id][1];
}
}
if (width == 0 && height == 0) {
return undefined;
}
sizes[id] = [width, height];
return [width, height];
};
/**
* Function that returns a plane 15 character for a symbol line using an id
* @function font.symbolLine
* @param {number} id - a 16-bit number of a symbol
* @returns {string} character for symbol line
* @example
* font.symbolLine(1)
*
* return ''
*/
const symbolLine$2 = function (id) {
return String.fromCodePoint(id + 0xF0000);
};
/**
* Function that returns a plane 16 character for a symbol fill using an id
* @function font.symbolFill
* @param {number} id - a 16-bit number of a symbol
* @returns {string} character for symbol fill
* @example
* font.symbolFill(1)
*
* return ''
*/
const symbolFill$2 = function (id) {
return String.fromCodePoint(id + 0x100000);
};
/**
* Function that creates two text elements for a symbol using an id
* @function font.symbolText
* @param {number} id - a 16-bit number of a symbol
* @returns {string} SVG segment for line and fill
* @example
* font.symbolText(1)
*
* return ` <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>`
*/
const symbolText$2 = function (id) {
return ` <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">${symbolFill$2(id)}</text>
<text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">${symbolLine$2(id)}</text>`;
};
/**
* Function that executes a callback function once the Sutton SignWriiting Line and Fill fonts are ready to use
* @function font.cssLoaded
* @param {function} callback - a callback function to execute when fonts are ready
* @example
* const callback = () => {
* console.log("Sutton SignWriting Line and Fill fonts are ready to use")
* }
*
* font.cssLoaded( callback )
*/
const cssLoaded = function (callback) {
let lineReady = false;
let fillReady = false;
cssLoadedLine(() => {
lineReady = true;
});
cssLoadedFill(() => {
fillReady = true;
});
const cssCheck = setInterval(function () {
if (lineReady && fillReady) {
clearInterval(cssCheck);
callback();
}
}, 100);
};
/**
* Function that executes a callback function once the Sutton SignWriiting Line font is ready to use
* @function font.cssLoadedLine
* @param {function} callback - a callback function to execute when line font is ready
* @example
* const callback = () => {
* console.log("Sutton SignWriting Line font is ready to use")
* }
*
* font.cssLoadedLine( callback )
*/
const cssLoadedLine = function (callback) {
if (!symbolSize$2(1)) {
const cssCheck = setInterval(function () {
if (symbolSize$2(1)) {
clearInterval(cssCheck);
callback();
}
}, 100);
} else {
callback();
}
};
/**
* Function that executes a callback function once the Sutton SignWriiting Fill font is ready to use
* @function font.cssLoadedFill
* @param {function} callback - a callback function to execute when fill font is ready
* @example
* const callback = () => {
* console.log("Sutton SignWriting Fill font is ready to use")
* }
*
* font.cssLoadedFill( callback )
*/
const cssLoadedFill = function (callback) {
const fillReady = function () {
const canvaser = document.createElement("canvas");
canvaser.width = 15;
canvaser.height = 30;
const context = canvaser.getContext("2d");
context.font = "30px 'SuttonSignWritingFill'";
context.fillText(symbolFill$2(1), 0, 0);
const imgData = context.getImageData(0, 0, 15, 30).data;
return !imgData.every(item => item === 0);
};
if (!fillReady()) {
const cssCheck = setInterval(function () {
if (fillReady()) {
clearInterval(cssCheck);
callback();
}
}, 100);
} else {
callback();
}
};
/** The font module contains functions for handing the TrueType fonts.
* @module font
*/
var index$2 = /*#__PURE__*/Object.freeze({
__proto__: null,
cssAppend: cssAppend,
cssLoaded: cssLoaded,
cssLoadedLine: cssLoadedLine,
cssLoadedFill: cssLoadedFill,
symbolSize: symbolSize$2,
symbolLine: symbolLine$2,
symbolFill: symbolFill$2,
symbolText: symbolText$2
});
/**
* Sutton SignWriting Core Module v2.0.0 (https://github.com/sutton-signwriting/core)
* Author: Steve Slevinski (https://SteveSlevinski.me)
* convert.mjs is released under the MIT License.
*/
/**
* Function to convert a number to an SWU number character
* @function convert.num2swu
* @param {number} num - Integer value for number
* @returns {string} SWU number character
* @example
* convert.num2swu(500)
*
* return '𝤆'
*/
const num2swu$1 = num => String.fromCodePoint(0x1D80C + parseInt(num) - 250);
/**
* Function to convert an array of x,y integers to two SWU number characters
* @function convert.coord2swu
* @param {number[]} coord - Array of x,y integers
* @returns {string} Two SWU number character
* @example
* convert.coord2swu([500, 500])
*
* return '𝤆𝤆'
*/
const coord2swu$1 = coord => coord.map(num => num2swu$1(num)).join('');
/**
* Function to convert an SWU symbol character to a code point on plane 4
* @function convert.swu2code
* @param {string} swuSym - SWU symbol character
* @returns {number} Code point on plane 4
* @example
* convert.swu2code('')
*
* return 0x40001
*/
const swu2code$1 = swuSym => parseInt(swuSym.codePointAt(0));
/**
* Function to convert a code point on plane 4 to an SWU symbol character
* @function convert.code2swu
* @param {number} code - Code point on plane 4
* @returns {string} SWU symbol character
* @example
* convert.code2swu(0x40001)
*
* return ''
*/
const code2swu = code => String.fromCodePoint(code);
/**
* Function to convert an SWU symbol character to a 16-bit ID
* @function convert.swu2id
* @param {string} swuSym - SWU symbol character
* @returns {number} 16-bit ID
* @example
* convert.swu2id('')
*
* return 1
*/
const swu2id = swuSym => swu2code$1(swuSym) - 0x40000;
/**
* Function to convert an FSW symbol key to a 16-bit ID
* @function convert.key2id
* @param {string} key - FSW symbol key
* @returns {number} 16-bit ID
* @example
* convert.key2id('S10000')
*
* return 1
*/
const key2id = key => key === "S00000" ? 0 : 1 + (parseInt(key.slice(1, 4), 16) - 256) * 96 + parseInt(key.slice(4, 5), 16) * 16 + parseInt(key.slice(5, 6), 16);
/**
* Function to convert an SWU symbol character to an FSW symbol key
* @function convert.swu2key
* @param {string} swuSym - SWU symbol character
* @returns {string} FSW symbol key
* @example
* convert.swu2key('')
*
* return 'S10000'
*/
const swu2key = swuSym => {
if (swuSym === "") {
return "S00000";
}
const symcode = swu2code$1(swuSym) - 0x40001;
const base = parseInt(symcode / 96);
const fill = parseInt((symcode - base * 96) / 16);
const rotation = parseInt(symcode - base * 96 - fill * 16);
return 'S' + (base + 0x100).toString(16) + fill.toString(16) + rotation.toString(16);
};
/**
* Function to convert an FSW symbol key to an SWU symbol character
* @function convert.key2swu
* @param {string} key - FSW symbol key
* @returns {string} SWU symbol character
* @example
* convert.key2swu('S10000')
*
* return ''
*/
const key2swu = key => {
if (key === "S00000") {
return code2swu(0x40000);
}
return code2swu(0x40001 + (parseInt(key.slice(1, 4), 16) - 256) * 96 + parseInt(key.slice(4, 5), 16) * 16 + parseInt(key.slice(5, 6), 16));
};
/* support ongoing development */
/* https://patreon.com/signwriting */
/* https://donate.sutton-signwriting.io */
/**
* Sutton SignWriting Core Module v2.0.0 (https://github.com/sutton-signwriting/core)
* Author: Steve Slevinski (https://SteveSlevinski.me)
* fsw.mjs is released under the MIT License.
*/
/**
* Object of regular expressions for FSW strings
*
* @alias fsw.re
* @property {string} null - the null symbol
* @property {string} symbol - a symbol
* @property {string} nullorsymbol - null or a symbol
* @property {string} sort - the sorting marker
* @property {string} prefix - a sorting marker followed by one or more symbols with nulls
* @property {string} box - a signbox marker
* @property {string} coord - a coordinate
* @property {string} spatial - a symbol followed by a coordinate
* @property {string} signbox - a signbox marker, max coordinate and zero or more spatial symbols
* @property {string} sign - an optional prefix followed by a signbox
* @property {string} sortable - a mandatory prefix followed by a signbox
*/
let re$1$1 = {
'null': 'S00000',
'symbol': 'S[123][0-9a-f]{2}[0-5][0-9a-f]',
'coord': '[0-9]{3}x[0-9]{3}',
'sort': 'A',
'box': '[BLMR]'
};
re$1$1.nullorsymbol = `(?:${re$1$1.null}|${re$1$1.symbol})`;
re$1$1.prefix = `(?:${re$1$1.sort}${re$1$1.nullorsymbol}+)`;
re$1$1.spatial = `${re$1$1.symbol}${re$1$1.coord}`;
re$1$1.signbox = `${re$1$1.box}${re$1$1.coord}(?:${re$1$1.spatial})*`;
re$1$1.sign = `${re$1$1.prefix}?${re$1$1.signbox}`;
re$1$1.sortable = `${re$1$1.prefix}${re$1$1.signbox}`;
/**
* Object of regular expressions for style strings
*
* @alias style.re
* @type {object}
* @property {string} colorize - regular expression for colorize section
* @property {string} colorhex - regular expression for color hex values with 3 or 6 characters
* @property {string} colorname - regular expression for css color name
* @property {string} padding - regular expression for padding section
* @property {string} zoom - regular expression for zoom section
* @property {string} classbase - regular expression for class name definition
* @property {string} id - regular expression for id definition
* @property {string} colorbase - regular expression for color hex or color name
* @property {string} color - regular expression for single color entry
* @property {string} colors - regular expression for double color entry
* @property {string} background - regular expression for background section
* @property {string} detail - regular expression for color details for line and optional fill
* @property {string} detailsym - regular expression for color details for individual symbols
* @property {string} classes - regular expression for one or more class names
* @property {string} full - full regular expression for style string
*/
let re$3 = {
'colorize': 'C',
'colorhex': '(?:[0-9a-fA-F]{3}){1,2}',
'colorname': '[a-zA-Z]+',
'padding': 'P[0-9]{2}',
'zoom': 'Z(?:[0-9]+(?:\\.[0-9]+)?|x)',
'classbase': '-?[_a-zA-Z][_a-zA-Z0-9-]{0,100}',
'id': '[a-zA-Z][_a-zA-Z0-9-]{0,100}'
};
re$3.colorbase = `(?:${re$3.colorhex}|${re$3.colorname})`;
re$3.color = `_${re$3.colorbase}_`;
re$3.colors = `_${re$3.colorbase}(?:,${re$3.colorbase})?_`;
re$3.background = `G${re$3.color}`;
re$3.detail = `D${re$3.colors}`;
re$3.detailsym = `D[0-9]{2}${re$3.colors}`;
re$3.classes = `${re$3.classbase}(?: ${re$3.classbase})*`;
re$3.full = `-(${re$3.colorize})?(${re$3.padding})?(${re$3.background})?(${re$3.detail})?(${re$3.zoom})?(?:-((?:${re$3.detailsym})*))?(?:-(${re$3.classes})?!(?:(${re$3.id})!)?)?`;
const prefixColor$2 = color => {
const regex = new RegExp(`^${re$3.colorhex}$`);
return (regex.test(color) ? '#' : '') + color;
};
const definedProps$2 = obj => Object.fromEntries(Object.entries(obj).filter(([k, v]) => v !== undefined));
/**
* Function to parse style string to object
* @function style.parse
* @param {string} styleString - a style string
* @returns {StyleObject} elements of style string
* @example
* style.parse('-CP10G_blue_D_red,Cyan_')
*
* return {
* 'colorize': true,
* 'padding': 10,
* 'background': 'blue',
* 'detail': ['red', 'Cyan']
* }
*/
const parse$1$1 = styleString => {
const regex = `^${re$3.full}`;
const m = (typeof styleString === 'string' ? styleString.match(new RegExp(regex)) : []) || [];
return definedProps$2({
'colorize': !m[1] ? undefined : !!m[1],
'padding': !m[2] ? undefined : parseInt(m[2].slice(1)),
'background': !m[3] ? undefined : prefixColor$2(m[3].slice(2, -1)),
'detail': !m[4] ? undefined : m[4].slice(2, -1).split(',').map(prefixColor$2),
'zoom': !m[5] ? undefined : m[5] === 'Zx' ? 'x' : parseFloat(m[5].slice(1)),
'detailsym': !m[6] ? undefined : m[6].match(new RegExp(re$3.detailsym, 'g')).map(val => {
const parts = val.split('_');
const detail = parts[1].split(',').map(prefixColor$2);
return {
'index': parseInt(parts[0].slice(1)),
'detail': detail
};
}),
'classes': !m[7] ? undefined : m[7],
'id': !m[8] ? undefined : m[8]
});
};
/** 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.
* [Characters set definitions](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-09.html#name-characters)
* @module convert
*/
/**
* Function to convert an FSW coordinate string to an array of x,y integers
* @function convert.fsw2coord
* @param {string} fswCoord - An FSW coordinate string
* @returns {number[]} Array of x,y integers
* @example
* convert.fsw2coord('500x500')
*
* return [500, 500]
*/
const fsw2coord = fswCoord => fswCoord.split('x').map(num => parseInt(num));
const parse$3 = {
/**
* Function to parse an fsw symbol with optional coordinate and style string
* @function fsw.parse.symbol
* @param {string} fswSym - an fsw symbol
* @returns {SymbolObject} elements of fsw symbol
* @example
* fsw.parse.symbol('S10000500x500-C')
*
* return {
* 'symbol': 'S10000',
* 'coord': [500, 500],
* 'style': '-C'
* }
*/
symbol: fswSym => {
const regex = `^(${re$1$1.symbol})(${re$1$1.coord})?(${re$3.full})?`;
const symbol = typeof fswSym === 'string' ? fswSym.match(new RegExp(regex)) : undefined;
return {
'symbol': symbol ? symbol[1] : undefined,
'coord': symbol && symbol[2] ? fsw2coord(symbol[2]) : undefined,
'style': symbol ? symbol[3] : undefined
};
},
/**
* Function to parse an fsw sign with style string
* @function fsw.parse.sign
* @param {string} fswSign - an fsw sign
* @returns { SignObject } elements of fsw sign
* @example
* fsw.parse.sign('AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475-C')
*
* return {
* sequence: ['S10011', 'S10019', 'S2e704', 'S2e748'],
* box: 'M',
* max: [525, 535],
* spatials: [
* {
* symbol: 'S2e748',
* coord: [483, 510]
* },
* {
* symbol: 'S10011',
* coord: [501, 466]
* },
* {
* symbol: 'S2e704',
* coord: [510, 500]
* },
* {
* symbol: 'S10019',
* coord: [476, 475]
* }
* ],
* style: '-C'
* }
*/
sign: fswSign => {
const regex = `^(${re$1$1.prefix})?(${re$1$1.signbox})(${re$3.full})?`;
const sign = typeof fswSign === 'string' ? fswSign.match(new RegExp(regex)) : undefined;
if (sign) {
return {
'sequence': sign[1] ? sign[1].slice(1).match(/.{6}/g) : undefined,
'box': sign[2][0],
'max': fsw2coord(sign[2].slice(1, 8)),
'spatials': sign[2].length < 9 ? undefined : sign[2].slice(8).match(/(.{13})/g).map(m => {
return {
symbol: m.slice(0, 6),
coord: [parseInt(m.slice(6, 9)), parseInt(m.slice(10, 13))]
};
}),
'style': sign[3]
};
} else {
return {};
}
},
/**
* Function to parse an fsw text
* @function fsw.parse.text
* @param {string} fswText - an fsw text
* @returns {string[]} fsw signs and punctuations
* @example
* fsw.parse.text('AS14c20S27106M518x529S14c20481x471S27106503x489 AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468 S38800464x496')
*
* return [
* 'AS14c20S27106M518x529S14c20481x471S27106503x489',
* 'AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468',
* 'S38800464x496'
* ]
*/
text: fswText => {
if (typeof fswText !== 'string') return [];
const regex = `(${re$1$1.sign}(${re$3.full})?|${re$1$1.spatial}(${re$3.full})?)`;
const matches = fswText.match(new RegExp(regex, 'g'));
return matches ? [...matches] : [];
}
};
const compose$2 = {
/**
* Function to compose an fsw symbol with optional coordinate and style string
* @function fsw.compose.symbol
* @param {SymbolObject} fswSymObject - an fsw symbol object
* @returns {string} an fsw symbol string
* @example
* fsw.compose.symbol({
* 'symbol': 'S10000',
* 'coord': [480, 480],
* 'style': '-C'
* })
*
* return 'S10000480x480-C'
*/
symbol: fswSymObject => {
if (typeof fswSymObject.symbol === 'string') {
const symbol = (fswSymObject.symbol.match(re$1$1.symbol) || [''])[0];
if (symbol) {
const x = (fswSymObject.coord && fswSymObject.coord[0] || '').toString();
const y = (fswSymObject.coord && fswSymObject.coord[1] || '').toString();
const coord = ((x + 'x' + y).match(re$1$1.coord) || [''])[0] || '';
const styleStr = typeof fswSymObject.style === 'string' && (fswSymObject.style.match(re$3.full) || [''])[0] || '';
return symbol + coord + styleStr;
}
}
return undefined;
},
/**
* Function to compose an fsw sign with style string
* @function fsw.compose.sign
* @param {SignObject} fswSignObject - an fsw symbol object
* @returns {string} an fsw sign string
* @example
* fsw.compose.sign({
* sequence: ['S10011', 'S10019', 'S2e704', 'S2e748'],
* box: 'M',
* max: [525, 535],
* spatials: [
* {
* symbol: 'S2e748',
* coord: [483, 510]
* },
* {
* symbol: 'S10011',
* coord: [501, 466]
* },
* {
* symbol: 'S2e704',
* coord: [510, 500]
* },
* {
* symbol: 'S10019',
* coord: [476, 475]
* }
* ],
* style: '-C'
* })
*
* return 'AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475-C'
*/
sign: fswSignObject => {
let box = typeof fswSignObject.box !== 'string' ? 'M' : (fswSignObject.box + 'M').match(re$1$1.box);
const x = (fswSignObject.max && fswSignObject.max[0] || '').toString();
const y = (fswSignObject.max && fswSignObject.max[1] || '').toString();
const max = ((x + 'x' + y).match(re$1$1.coord) || [''])[0] || '';
if (!max) return undefined;
let prefix = '';
if (fswSignObject.sequence && Array.isArray(fswSignObject.sequence)) {
prefix = fswSignObject.sequence.map(key => (key.match(re$1$1.nullorsymbol) || [''])[0]).join('');
prefix = prefix ? 'A' + prefix : '';
}
let signbox = '';
if (fswSignObject.spatials && Array.isArray(fswSignObject.spatials)) {
signbox = fswSignObject.spatials.map(spatial => {
if (typeof spatial.symbol === 'string') {
const symbol = (spatial.symbol.match(re$1$1.symbol) || [''])[0];
if (symbol) {
const x = (spatial.coord && spatial.coord[0] || '').toString();
const y = (spatial.coord && spatial.coord[1] || '').toString();
const coord = ((x + 'x' + y).match(re$1$1.coord) || [''])[0] || '';
if (coord) {
return symbol + coord;
}
}
}
return '';
}).join('');
}
const styleStr = typeof fswSignObject.style === 'string' && (fswSignObject.style.match(re$3.full) || [''])[0] || '';
return prefix + box + max + signbox + styleStr;
}
};
/**
* Function to gather sizing information about an fsw sign or symbol
* @function fsw.info
* @param {string} fsw - an fsw sign or symbol
* @returns {SegmentInfo} information about the fsw string
* @example
* fsw.info('AS14c20S27106L518x529S14c20481x471S27106503x489-P10Z2')
*
* return {
* minX: 481,
* minY: 471,
* width: 37,
* height: 58,
* lane: -1,
* padding: 10,
* segment: 'sign',
* zoom: 2
* }
*/
const info$1 = fsw => {
let lanes = {
"B": 0,
"L": -1,
"M": 0,
"R": 1
};
let parsed = parse$3.sign(fsw);
let width, height, segment, x1, x2, y1, y2, lane;
if (parsed.spatials) {
x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
x2 = parsed.max[0];
width = x2 - x1;
y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
y2 = parsed.max[1];
height = y2 - y1;
segment = 'sign';
lane = parsed.box;
} else {
parsed = parse$3.symbol(fsw);
lane = "M";
if (parsed.coord) {
x1 = parsed.coord[0];
width = (500 - x1) * 2;
y1 = parsed.coord[1];
height = (500 - y1) * 2;
segment = 'symbol';
} else {
x1 = 490;
width = 20;
y1 = 490;
height = 20;
segment = 'none';
}
}
let style = parse$1$1(parsed.style);
let zoom = style.zoom || 1;
let padding = style.padding || 0;
return {
minX: x1,
minY: y1,
width: width,
height: height,
segment: segment,
lane: lanes[lane],
padding: padding,
zoom: zoom
};
};
const columnDefaults$1 = {
'height': 500,
'width': 150,
'offset': 50,
'pad': 20,
'margin': 5,
'dynamic': false,
'background': undefined,
'punctuation': {
'spacing': true,
'pad': 30,
'pull': true
},
'style': {
'detail': ['black', 'white'],
'zoom': 1
}
};
/**
* Function to an object of column options with default values
*
* @function fsw.columnDefaultsMerge
* @param {ColumnOptions} options - object of column options
* @returns {ColumnOptions} object of column options merged with column defaults
* @example
* fsw.columnDefaultsMerge({height: 500,width:150})
*
* return {
* "height": 500,
* "width": 150,
* "offset": 50,
* "pad": 20,
* "margin": 5,
* "dynamic": false,
* "punctuation": {
* "spacing": true,
* "pad": 30,
* "pull": true
* },
* "style": {
* "detail": [
* "black",
* "white"
* ],
* "zoom": 1
* }
* }
*/
const columnDefaultsMerge$1 = options => {
if (typeof options !== 'object') options = {};
return {
...columnDefaults$1,
...options,
punctuation: {
...columnDefaults$1.punctuation,
...options.punctuation
},
style: {
...columnDefaults$1.style,
...options.style
}
};
};
/**
* Function to transform an FSW text to an array of columns
*
* @function fsw.columns
* @param {string} fswText - FSW text of signs and punctuation
* @param {ColumnOptions} options - object of column options
* @returns {{options:ColumnOptions,widths:number[],columns:ColumnData}} object of column options, widths array, and column data
* @example
* fsw.columns('AS14c20S27106M518x529S14c20481x471S27106503x489 AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468 S38800464x496', {height: 500,width:150})
*
* return {
* "options": {
* "height": 500,
* "width": 150,
* "offset": 50,
* "pad": 20,
* "margin": 5,
* "dynamic": false,
* "punctuation": {
* "spacing": true,
* "pad": 30,
* "pull": true
* },
* "style": {
* "detail": [
* "black",
* "white"
* ],
* "zoom": 1
* }
* },
* "widths": [
* 150
* ],
* "columns": [
* [
* {
* "x": 56,
* "y": 20,
* "minX": 481,
* "minY": 471,
* "width": 37,
* "height": 58,
* "lane": 0,
* "padding": 0,
* "segment": "sign",
* "text": "AS14c20S27106M518x529S14c20481x471S27106503x489",
* "zoom": 1
* },
* {
* "x": 57,
* "y": 118,
* "minX": 482,
* "minY": 468,
* "width": 36,
* "height": 65,
* "lane": 0,
* "padding": 0,
* "segment": "sign",
* "text": "AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468",
* "zoom": 1
* },
* {
* "x": 39,
* "y": 203,
* "minX": 464,
* "minY": 496,
* "width": 72,
* "height": 8,
* "lane": 0,
* "padding": 0,
* "segment": "symbol",
* "text": "S38800464x496",
* "zoom": 1
* }
* ]
* ]
* }
*/
const columns$1 = (fswText, options) => {
if (typeof fswText !== 'string') return {};
const values = columnDefaultsMerge$1(options);
let input = parse$3.text(fswText);
let cursor = 0;
let cols = [];
let col = [];
let plus = 0;
let center = parseInt(values.width / 2);
let maxHeight = values.height - values.margin;
let pullable = true;
let finalize = false;
for (let val of input) {
let informed = info$1(val);
cursor += plus;
if (values.punctuation.spacing) {
cursor += informed.segment == 'sign' ? values.pad : 0;
} else {
cursor += values.pad;
}
finalize = cursor + informed.height > maxHeight;
if (finalize && informed.segment == 'symbol' && values.punctuation.pull && pullable) {
finalize = false;
pullable = false;
}
if (col.length == 0) {
finalize = false;
}
if (finalize) {
cursor = values.pad;
cols.push(col);
col = [];
pullable = true;
}
col.push(Object.assign(informed, {
x: center + values.offset * informed.lane - (500 - informed.minX) * informed.zoom * values.style.zoom,
y: cursor,
text: val
}));
cursor += informed.height * informed.zoom * values.style.zoom;
if (values.punctuation.spacing) {
plus = informed.segment == 'sign' ? values.pad : values.punctuation.pad;
} else {
plus = values.pad;
}
}
if (col.length) {
cols.push(col);
}
// over height issue when pulling punctuation
if (values.punctuation.pull) {
for (let col of cols) {
let last = col[col.length - 1];
let diff = last.y + last.height - (values.height - values.margin);
if (diff > 0) {
let adj = parseInt(diff / col.length) + 1;
for (let i in col) {
col[i].y -= adj * i + adj;
}
}
}
}
// contract, expand, adjust
let widths = [];
for (let col of cols) {
let min = [center - values.offset - values.pad];
let max = [center + values.offset + values.pad];
for (let item of col) {
min.push(item.x - values.pad);
max.push(item.x + item.width + values.pad);
}
min = Math.min(...min);
max = Math.max(...max);
let width = values.width;
let adj = 0;
if (!values.dynamic) {
adj = center - parseInt((min + max) / 2);
} else {
width = max - min;
adj = -min;
}
for (let item of col) {
item.x += adj;
}
widths.push(width);
}
return {
'options': values,
'widths': widths,
'columns': cols
};
};
/**
* Array of numbers for categories of symbols: hand, movement, dynamics, head, trunk & limb, location, and punctuation.
* @alias fsw.category
* @type {number[]}
*/
const category$1 = [0x100, 0x205, 0x2f7, 0x2ff, 0x36d, 0x37f, 0x387];
/**
* Object of symbol ranges with starting and ending numbers.
*
* { all, writing, hand, movement, dynamic, head, hcenter, vcenter, trunk, limb, location, punctuation }
* @alias fsw.ranges
* @type {object}
*/
const ranges$1 = {
'all': [0x100, 0x38b],
'writing': [0x100, 0x37e],
'hand': [0x100, 0x204],
'movement': [0x205, 0x2f6],
'dynamic': [0x2f7, 0x2fe],
'head': [0x2ff, 0x36c],
'hcenter': [0x2ff, 0x36c],
'vcenter': [0x2ff, 0x375],
'trunk': [0x36d, 0x375],
'limb': [0x376, 0x37e],
'location': [0x37f, 0x386],
'punctuation': [0x387, 0x38b]
};
/**
* Array of colors associated with the seven symbol categories.
* @alias fsw.colors
* @type {string[]}
*/
const colors$1 = ['#0000CC', '#CC0000', '#FF0099', '#006600', '#000000', '#884411', '#FF9900'];
/**
* Function that returns the standardized color for a symbol.
* @function fsw.colorize
* @param {string} key - an FSW symbol key
* @returns {string} name of standardized color for symbol
* @example
* fsw.colorize('S10000')
*
* return '#0000CC'
*/
const colorize$1 = key => {
const parsed = parse$3.symbol(key);
let color = '#000000';
if (parsed.symbol) {
const dec = parseInt(parsed.symbol.slice(1, 4), 16);
const index = category$1.findIndex(val => val > dec);
color = colors$1[index < 0 ? 6 : index - 1];
}
return color;
};
/* support ongoing development */
/* https://patreon.com/signwriting */
/* https://donate.sutton-signwriting.io */
/**
* Function that returns the size of a symbol using an FSW symbol key
* @function fsw.symbolSize
* @param {string} fsw - an FSW symbol key
* @returns {number[]} width and height of symbol
* @example
* fsw.symbolSize("S10000")
*
* return [15,30]
*/
const symbolSize$1 = function (fsw) {
const parsed = parse$3.symbol(fsw);
if (!parsed.symbol) {
return undefined;
}
return symbolSize$2(key2id(fsw));
};
/**
* Function that returns a plane 15 character for a symbol line using an FSW symbol key
* @function fsw.symbolLine
* @param {string} fsw - an FSW symbol key
* @returns {string} character for symbol line
* @example
* fsw.symbolLine('S10000')
*
* return ''
*/
const symbolLine$1 = function (fsw) {
return symbolLine$2(key2id(fsw));
};
/**
* Function that returns a plane 16 character for a symbol fill using an FSW symbol key
* @function fsw.symbolFill
* @param {string} fsw - an FSW symbol key
* @returns {string} character for symbol fill
* @example
* font.symbolFill('S10000')
*
* return ''
*/
const symbolFill$1 = function (fsw) {
return symbolFill$2(key2id(fsw));
};
/**
* Function that creates two text elements for a symbol using an FSW symbol key
* @function fsw.symbolText
* @param {string} fsw - an FSW symbol key
* @returns {string} svg segment for line and fill
* @example
* fsw.symbolText('S10000')
*
* return ` <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>`
*/
const symbolText$1 = function (fsw) {
return symbolText$2(key2id(fsw));
};
/**
* Sutton SignWriting Core Module v2.0.0 (https://github.com/sutton-signwriting/core)
* Author: Steve Slevinski (https://SteveSlevinski.me)
* style.mjs is released under the MIT License.
*/
/**
* Object of regular expressions for style strings
*
* @alias style.re
* @type {object}
* @property {string} colorize - regular expression for colorize section
* @property {string} colorhex - regular expression for color hex values with 3 or 6 characters
* @property {string} colorname - regular expression for css color name
* @property {string} padding - regular expression for padding section
* @property {string} zoom - regular expression for zoom section
* @property {string} classbase - regular expression for class name definition
* @property {string} id - regular expression for id definition
* @property {string} colorbase - regular expression for color hex or color name
* @property {string} color - regular expression for single color entry
* @property {string} colors - regular expression for double color entry
* @property {string} background - regular expression for background section
* @property {string} detail - regular expression for color details for line and optional fill
* @property {string} detailsym - regular expression for color details for individual symbols
* @property {string} classes - regular expression for one or more class names
* @property {string} full - full regular expression for style string
*/
let re$2 = {
'colorize': 'C',
'colorhex': '(?:[0-9a-fA-F]{3}){1,2}',
'colorname': '[a-zA-Z]+',
'padding': 'P[0-9]{2}',
'zoom': 'Z(?:[0-9]+(?:\\.[0-9]+)?|x)',
'classbase': '-?[_a-zA-Z][_a-zA-Z0-9-]{0,100}',
'id': '[a-zA-Z][_a-zA-Z0-9-]{0,100}'
};
re$2.colorbase = `(?:${re$2.colorhex}|${re$2.colorname})`;
re$2.color = `_${re$2.colorbase}_`;
re$2.colors = `_${re$2.colorbase}(?:,${re$2.colorbase})?_`;
re$2.background = `G${re$2.color}`;
re$2.detail = `D${re$2.colors}`;
re$2.detailsym = `D[0-9]{2}${re$2.colors}`;
re$2.classes = `${re$2.classbase}(?: ${re$2.classbase})*`;
re$2.full = `-(${re$2.colorize})?(${re$2.padding})?(${re$2.background})?(${re$2.detail})?(${re$2.zoom})?(?:-((?:${re$2.detailsym})*))?(?:-(${re$2.classes})?!(?:(${re$2.id})!)?)?`;
const prefixColor$1 = color => {
const regex = new RegExp(`^${re$2.colorhex}$`);
return (regex.test(color) ? '#' : '') + color;
};
const definedProps$1 = obj => Object.fromEntries(Object.entries(obj).filter(([k, v]) => v !== undefined));
/**
* Function to parse style string to object
* @function style.parse
* @param {string} styleString - a style string
* @returns {StyleObject} elements of style string
* @example
* style.parse('-CP10G_blue_D_red,Cyan_')
*
* return {
* 'colorize': true,
* 'padding': 10,
* 'background': 'blue',
* 'detail': ['red', 'Cyan']
* }
*/
const parse$2 = styleString => {
const regex = `^${re$2.full}`;
const m = (typeof styleString === 'string' ? styleString.match(new RegExp(regex)) : []) || [];
return definedProps$1({
'colorize': !m[1] ? undefined : !!m[1],
'padding': !m[2] ? undefined : parseInt(m[2].slice(1)),
'background': !m[3] ? undefined : prefixColor$1(m[3].slice(2, -1)),
'detail': !m[4] ? undefined : m[4].slice(2, -1).split(',').map(prefixColor$1),
'zoom': !m[5] ? undefined : m[5] === 'Zx' ? 'x' : parseFloat(m[5].slice(1)),
'detailsym': !m[6] ? undefined : m[6].match(new RegExp(re$2.detailsym, 'g')).map(val => {
const parts = val.split('_');
const detail = parts[1].split(',').map(prefixColor$1);
return {
'index': parseInt(parts[0].slice(1)),
'detail': detail
};
}),
'classes': !m[7] ? undefined : m[7],
'id': !m[8] ? undefined : m[8]
});
};
/**
* Function to compose style string from object
* @function style.compose
* @param {StyleObject} styleObject - an object of style options
* @returns {string} style string
* @example
* style.compose({
* 'colorize': true,
* 'padding': 10,
* 'background': 'blue',
* 'detail': ['red', 'Cyan'],
* 'zoom': 1.1,
* 'detailsym': [
* {
* 'index': 1,
* 'detail': ['#ff00ff']
* },
* {
* 'index': 2,
* 'detail': ['yellow', 'green']
* }
* ],
* 'classes': 'primary blinking',
* 'id': 'cursor'
* })
*
* return '-CP10G_blue_D_red,Cyan_Z1.1-D01_ff00ff_D02_yellow,green_-primary blinking!cursor!'
*/
const compose$1 = styleObject => {
if (typeof styleObject !== 'object' || styleObject === null) return undefined;
// three sections
let style1 = '-';
style1 += !styleObject.colorize ? '' : 'C';
const padding = parseInt(styleObject.padding);
style1 += !padding || padding <= 0 || padding > 99 ? '' : 'P' + (padding > 9 ? padding : '0' + padding);
const background = !styleObject.background || !(typeof styleObject.background === 'string') ? undefined : styleObject.background.match(re$2.colorbase)[0];
style1 += !background ? '' : 'G_' + background + '_';
const detail1 = !styleObject.detail || !styleObject.detail[0] || !(typeof styleObject.detail[0] === 'string') ? undefined : styleObject.detail[0].match(re$2.colorbase)[0];
const detail2 = !styleObject.detail || !styleObject.detail[1] || !(typeof styleObject.detail[1] === 'string') ? undefined : styleObject.detail[1].match(re$2.colorbase)[0];
if (detail1) {
style1 += 'D_' + detail1;
if (detail2) {
style1 += ',' + detail2;
}
style1 += '_';
}
const zoom = styleObject.zoom === 'x' ? 'x' : parseFloat(styleObject.zoom);
style1 += !zoom || zoom <= 0 ? '' : 'Z' + zoom;
let style2 = '';
const detailsym = !styleObject.detailsym || !Array.isArray(styleObject.detailsym) ? [] : styleObject.detailsym.map(styleObject => {
const index = parseInt(styleObject.index);
if (!index || index <= 0 || index > 99) return '';
let style = 'D' + (index > 9 ? index : '0' + index);
const detail1 = !styleObject.detail || !styleObject.detail[0] ? undefined : styleObject.detail[0].match(re$2.colorbase)[0];
const detail2 = !styleObject.detail || !styleObject.detail[1] ? undefined : styleObject.detail[1].match(re$2.colorbase)[0];
if (detail1) {
style += '_' + detail1;
if (detail2) {
style += ',' + detail2;
}
style += '_';
}
return style;
});
style2 += detailsym.join('');
let style3 = '';
const classes = !styleObject.classes || !(typeof styleObject.classes === 'string') ? undefined : styleObject.classes.match(re$2.classes)[0];
style3 += !classes ? '' : classes;
const id = !styleObject.id || !(typeof styleObject.id === 'string') ? undefined : styleObject.id.match(re$2.id)[0];
style3 += classes || id ? '!' : '';
style3 += !id ? '' : id + '!';
return style1 + (style2 || style3 ? '-' + style2 : '') + (style3 ? '-' + style3 : '');
};
/* support ongoing development */
/* https://patreon.com/signwriting */
/* https://donate.sutton-signwriting.io */
/**
* Function that creates an SVG image from an FSW symbol key with an optional style string
* @function fsw.symbolSvgBody
* @param {string} fswSym - an FSW symbol key with optional style string
* @returns {string} body of SVG for symbol
* @example
* fsw.symbolSvgBody('S10000')
*
* return `<text font-size="0">S10000</text>
* <g transform="translate(500,500)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>`
*/
const symbolSvgBody$1 = fswSym => {
const parsed = parse$3.symbol(fswSym);
const blank = '';
if (!parsed.symbol) return blank;
let styling = parse$2(parsed.style);
let x1, y1, x2, y2;
if (parsed.coord) {
x1 = parsed.coord[0];
y1 = parsed.coord[1];
x2 = 500 + (500 - x1);
y2 = 500 + (500 - y1);
} else {
let size = symbolSize$1(parsed.symbol);
if (!size) return blank;
x1 = 500 - parseInt((size[0] + 1) / 2);
y1 = 500 - parseInt((size[1] + 1) / 2);
x2 = 500 + (500 - x1);
y2 = 500 + (500 - y1);
}
let symSvg = symbolText$1(parsed.symbol);
symSvg = ` <g transform="translate(${x1},${y1})">
${symSvg}
</g>`;
let line;
if (styling.colorize) {
line = colorize$1(parsed.symbol);
} else if (styling.detail) {
line = styling.detail[0];
}
if (line) {
symSvg = symSvg.replace(/class="sym-line" fill="black"/, `class="sym-line" fill="${line}"`);
}
let fill = styling.detail && styling.detail[1];
if (fill) {
symSvg = symSvg.replace(/class="sym-fill" fill="white"/, `class="sym-fill" fill="${fill}"`);
}
let background = '';
if (styling.padding) {
x1 -= styling.padding;
y1 -= styling.padding;
x2 += styling.padding;
y2 += styling.padding;
}
if (styling.background) {
background = `\n <rect x="${x1}" y="${y1}" width="${x2 - x1}" height="${y2 - y1}" style="fill:${styling.background};" />`;
}
return ` <text font-size="0">${fswSym}</text>${background}
${symSvg}`;
};
/**
* Function that creates an SVG image from an FSW symbol key with an optional style string
* @function fsw.symbolSvg
* @param {string} fswSym - an FSW symbol key with optional style string
* @returns {string} SVG for symbol
* @example
* fsw.symbolSvg('S10000')
*
* return `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="15" height="30" viewBox="500 500 15 30">
* <text font-size="0">S10000</text>
* <g transform="translate(500,500)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* </svg>`
*/
const symbolSvg$1 = fswSym => {
const parsed = parse$3.symbol(fswSym);
const blank = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="1" height="1"></svg>';
if (!parsed.symbol) return blank;
let styling = parse$2(parsed.style);
let x1, y1, x2, y2;
if (parsed.coord) {
x1 = parsed.coord[0];
y1 = parsed.coord[1];
x2 = 500 + (500 - x1);
y2 = 500 + (500 - y1);
} else {
let size = symbolSize$1(parsed.symbol);
if (!size) return blank;
x1 = parseInt(500 - size[0] / 2);
y1 = parseInt(500 - size[1] / 2);
x2 = x1 + size[0];
y2 = y1 + size[1];
}
let classes = '';
if (styling.classes) {
classes = ` class="${styling.classes}"`;
}
let id = '';
if (styling.id) {
id = ` id="${styling.id}"`;
}
if (styling.padding) {
x1 -= styling.padding;
y1 -= styling.padding;
x2 += styling.padding;
y2 += styling.padding;
}
let sizing = '';
if (styling.zoom != 'x') {
sizing = ` width="${(x2 - x1) * (styling.zoom ? styling.zoom : 1)}" height="${(y2 - y1) * (styling.zoom ? styling.zoom : 1)}"`;
}
return `<svg${classes}${id} version="1.1" xmlns="http://www.w3.org/2000/svg"${sizing} viewBox="${x1} ${y1} ${x2 - x1} ${y2 - y1}">
${symbolSvgBody$1(fswSym)}
</svg>`;
};
const symbolCanvas$1 = function (fswSym) {
const parsed = parse$3.symbol(fswSym);
if (parsed.symbol) {
let size = symbolSize$1(parsed.symbol);
if (size) {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
let styling = parse$2(parsed.style);
let line = 'black';
if (styling.colorize) {
line = colorize$1(parsed.symbol);
} else if (styling.detail) {
line = styling.detail[0];
}
let fill = styling.detail && styling.detail[1] || 'white';
let x1 = 500;
let x2 = x1 + size[0];
let y1 = 500;
let y2 = y1 + size[1];
if (styling.padding) {
x1 -= styling.padding;
y1 -= styling.padding;
x2 += styling.padding;
y2 += styling.padding;
}
let sizing = 1;
if (styling.zoom != 'x') {
sizing = styling.zoom;
}
let w = (x2 - x1) * sizing;
let h = (y2 - y1) * sizing;
canvas.width = w ? w : 1;
canvas.height = h ? h : 1;
if (styling.background) {
context.rect(0, 0, w, h);
context.fillStyle = styling.background;
context.fill();
}
context.font = 30 * sizing + "px 'SuttonSignWritingFill'";
context.fillStyle = fill;
context.fillText(symbolFill$1(parsed.symbol), (500 - x1) * sizing, (500 - y1) * sizing);
context.font = 30 * sizing + "px 'SuttonSignWritingLine'";
context.fillStyle = line;
context.fillText(symbolLine$1(parsed.symbol), (500 - x1) * sizing, (500 - y1) * sizing);
return canvas;
}
}
};
/**
* Function that creates a PNG data url from an FSW symbol key with an optional style string
* @function fsw.symbolPng
* @param {string} fswSym - an FSW symbol key with optional style string
* @returns {string} png image for symbol as data url
* @example
* fsw.symbolPng('S10000')
*
* return 'data:image/png;base64,iVBORw...'
*/
const symbolPng$1 = fswSym => {
const canvas = symbolCanvas$1(fswSym);
const png = canvas.toDataURL("image/png");
canvas.remove();
return png;
};
const blank$1 = null;
/**
* Function that normalizes a symbol with a minimum coordinate for a center of 500,500
* @function fsw.symbolNormalize
* @param {string} fswSym - an FSW symbol key with optional coordinate and style string
* @returns {string} normalized FSW symbol
* @example
* fsw.symbolNormalize('S10000-CP10G_green_Z2')
*
* return 'S10000493x485-CP10G_green_Z2'
*/
const symbolNormalize$1 = fswSym => {
const parsed = parse$3.symbol(fswSym);
if (parsed.symbol) {
let size = symbolSize$1(parsed.symbol);
if (size) {
return `${parsed.symbol}${500 - parseInt((size[0] + 1) / 2)}x${500 - parseInt((size[1] + 1) / 2)}${parsed.style || ''}`;
}
} else {
return blank$1;
}
};
/**
* Function that mirrors a symbol
* @function fsw.symbolMirror
* @param {string} fswSym - an FSW symbol key with optional coordinate and style string
* @returns {string} mirrored FSW symbol
* @example
* fsw.symbolMirror('S10000')
*
* return 'S10008'
*/
const symbolMirror$1 = fswSym => {
let parsed = parse$3.symbol(fswSym);
if (!parsed.symbol) {
return fswSym;
}
const size = symbolSize$1(parsed.symbol);
if (!size) {
return fswSym;
}
const base = parsed.symbol.slice(0, 4);
let fill = parsed.symbol.slice(4, 5);
let rot = parseInt(parsed.symbol.slice(5, 6), 16);
const key1 = base + "08";
const key2 = base + "18";
var rAdd;
if (symbolSize$1(key1) || symbolSize$1(key2)) {
rAdd = 8;
} else {
if (rot === 0 || rot == 4) {
rAdd = 0;
}
if (rot == 1 || rot == 5) {
rAdd = 6;
}
if (rot == 2 || rot == 6) {
rAdd = 4;
}
if (rot == 3 || rot == 7) {
rAdd = 2;
}
}
let key = '';
while (!key || !symbolSize$1(key)) {
rot += rAdd;
if (rot > 7 && rAdd < 8) {
rot = rot - 8;
}
if (rot > 15) {
rot = rot - 16;
}
key = base + fill + rot.toString(16);
}
parsed.symbol = key;
return compose$2.symbol(parsed);
};
/**
* Function that inverts a symbol
* @function fsw.symbolInvert
* @param {string} fswSym - an FSW symbol key with optional coordinate and style string
* @returns {string} inverted FSW symbol
* @example
* fsw.symbolInvert('S10000')
*
* return 'S1000c'
*/
const symbolInvert$1 = fswSym => {
let parsed = parse$3.symbol(fswSym);
if (!parsed.symbol) {
return fswSym;
}
const size = symbolSize$1(parsed.symbol);
if (!size) {
return fswSym;
}
const base = parsed.symbol.slice(0, 4);
let fill = parsed.symbol.slice(4, 5);
let rot = parsed.symbol.slice(5, 6);
const key1 = base + "08";
const key2 = base + "18";
let map;
if (symbolSize$1(key1) || symbolSize$1(key2)) {
map = {
'0': 'c',
'1': 'd',
'2': 'e',
'3': 'f',
'4': '8',
'5': '9',
'6': 'a',
'7': 'b',
'c': '0',
'd': '1',
'e': '2',
'f': '3',
'8': '4',
'9': '5',
'a': '6',
'b': '7'
};
} else {
map = {
'0': '4',
'1': '3',
'2': '2',
'3': '1',
'4': '0',
'5': '7',
'6': '6',
'7': '5'
};
}
if (rot in map) {
const key = base + fill + map[rot];
if (symbolSize$1(key)) {
parsed.symbol = key;
}
}
return compose$2.symbol(parsed);
};
/**
* Function that rotates a symbol
* @function fsw.symbolRotate
* @param {string} fswSym - an FSW symbol key with optional coordinate and style string
* @param {boolean} [clockwise=true] - rotate the symbol clockwise
* @returns {string} rotated FSW symbol
* @example
* fsw.symbolRotate('S10000')
*
* return 'S10007'
*/
const symbolRotate$1 = (fswSym, clockwise = true) => {
let parsed = parse$3.symbol(fswSym);
if (!parsed.symbol) {
return fswSym;
}
const size = symbolSize$1(parsed.symbol);
if (!size) {
return fswSym;
}
const step = clockwise ? 1 : -1;
const base = parsed.symbol.slice(0, 4);
let fill = parsed.symbol.slice(4, 5);
let rot = parseInt(parsed.symbol.slice(5, 6), 16);
let key = '';
while (!key || !symbolSize$1(key)) {
if (rot > 7) {
rot += step;
if (rot > 15) {
rot = 8;
}
if (rot < 8) {
rot = 15;
}
key = base + fill + rot.toString(16);
} else {
rot -= step;
if (rot > 7) {
rot = 0;
}
if (rot < 0) {
rot = 7;
}
key = base + fill + rot;
}
}
parsed.symbol = key;
return compose$2.symbol(parsed);
};
/**
* Function that changes the fill of a symbol
* @function fsw.symbolFlop
* @param {string} fswSym - an FSW symbol key with optional coordinate and style string
* @param {boolean} [positive=true] - increase the symbol fill
* @returns {string} FSW symbol with changed fill
* @example
* fsw.symbolFlop('S10000')
*
* return 'S10010'
*/
const symbolFlop$1 = (fswSym, positive = true) => {
let parsed = parse$3.symbol(fswSym);
if (!parsed.symbol) {
return fswSym;
}
const size = symbolSize$1(parsed.symbol);
if (!size) {
return fswSym;
}
const step = positive ? 1 : -1;
const base = parsed.symbol.slice(0, 4);
let fill = parseInt(parsed.symbol.slice(4, 5));
let rot = parsed.symbol.slice(5, 6);
let key = '';
while (!key || !symbolSize$1(key)) {
fill += step;
if (fill > 5) {
fill = 0;
}
if (fill < 0) {
fill = 5;
}
key = base + fill + rot;
}
parsed.symbol = key;
return compose$2.symbol(parsed);
};
/**
* Function that changes the base of a symbol
* @function fsw.symbolScroll
* @param {string} fswSym - an FSW symbol key with optional coordinate and style string
* @param {boolean} [positive=true] - increase the symbol base
* @returns {string} FSW symbol with changed base
* @example
* fsw.symbolScroll('S10000')
*
* return 'S10100'
*/
const symbolScroll$1 = (fswSym, positive = true) => {
let parsed = parse$3.symbol(fswSym);
if (!parsed.symbol) {
return fswSym;
}
const size = symbolSize$1(parsed.symbol);
if (!size) {
return fswSym;
}
const step = positive ? 1 : -1;
const base = parseInt(parsed.symbol.slice(1, 4), 16) + step;
const fill = parsed.symbol.slice(4, 5);
const rot = parsed.symbol.slice(5, 6);
const key = 'S' + base.toString(16) + fill + rot;
if (symbolSize$1(key)) {
parsed.symbol = key;
}
return compose$2.symbol(parsed);
};
/**
* Function that creates an SVG image from an FSW sign with an optional style string
* @function fsw.signSvgBody
* @param {string} fswSign - an FSW sign with optional style string
* @returns {string} body of SVG for sign
* @example
* fsw.signSvgBody('M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475')
*
* return `<text font-size="0">M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475</text>
* <g transform="translate(483,510)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(501,466)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(510,500)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(476,475)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>`
*/
const signSvgBody$1 = fswSign => {
let parsed = parse$3.sign(fswSign);
const blank = '';
if (parsed.spatials) {
let styling = parse$2(parsed.style);
if (styling.detailsym) {
styling.detailsym.forEach(sym => {
if (parsed.spatials[sym.index - 1]) {
parsed.spatials[sym.index - 1].detail = sym.detail;
}
});
}
let x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
let y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
let x2 = parsed.max[0];
let y2 = parsed.max[1];
let background = '';
if (styling.padding) {
x1 -= styling.padding;
y1 -= styling.padding;
x2 += styling.padding;
y2 += styling.padding;
}
if (styling.background) {
background = `\n <rect x="${x1}" y="${y1}" width="${x2 - x1}" height="${y2 - y1}" style="fill:${styling.background};" />`;
}
let svg = ` <text font-size="0">${fswSign}</text>${background}`;
const line = styling.detail && styling.detail[0];
const fill = styling.detail && styling.detail[1];
svg += '\n' + parsed.spatials.map(spatial => {
let svg = symbolText$1(spatial.symbol);
let symLine = line;
if (spatial.detail) {
symLine = spatial.detail[0];
} else if (styling.colorize) {
symLine = colorize$1(spatial.symbol);
}
if (symLine) {
svg = svg.replace(/class="sym-line" fill="black"/, `class="sym-line" fill="${symLine}"`);
}
let symFill = fill;
if (spatial.detail && spatial.detail[1]) {
symFill = spatial.detail[1];
}
if (symFill) {
svg = svg.replace(/class="sym-fill" fill="white"/, `class="sym-fill" fill="${symFill}"`);
}
return ` <g transform="translate(${spatial.coord[0]},${spatial.coord[1]})">
${svg}
</g>`;
}).join('\n');
return svg;
}
return blank;
};
/**
* Function that creates an SVG image from an FSW sign with an optional style string
* @function fsw.signSvg
* @param {string} fswSign - an FSW sign with optional style string
* @returns {string} SVG for sign
* @example
* fsw.signSvg('M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475')
*
* return `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="49" height="69" viewBox="476 466 49 69">
* <text font-size="0">M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475</text>
* <g transform="translate(483,510)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(501,466)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(510,500)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(476,475)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* </svg>`
*/
const signSvg$1 = fswSign => {
let parsed = parse$3.sign(fswSign);
const blank = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="1" height="1"></svg>';
if (parsed.spatials) {
let styling = parse$2(parsed.style);
let x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
let y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
let x2 = parsed.max[0];
let y2 = parsed.max[1];
let classes = '';
if (styling.classes) {
classes = ` class="${styling.classes}"`;
}
let id = '';
if (styling.id) {
id = ` id="${styling.id}"`;
}
if (styling.padding) {
x1 -= styling.padding;
y1 -= styling.padding;
x2 += styling.padding;
y2 += styling.padding;
}
let sizing = '';
if (styling.zoom != 'x') {
sizing = ` width="${(x2 - x1) * (styling.zoom ? styling.zoom : 1)}" height="${(y2 - y1) * (styling.zoom ? styling.zoom : 1)}"`;
}
let svg = `<svg${classes}${id} version="1.1" xmlns="http://www.w3.org/2000/svg"${sizing} viewBox="${x1} ${y1} ${x2 - x1} ${y2 - y1}">
`;
svg += signSvgBody$1(fswSign);
svg += '\n</svg>';
return svg;
}
return blank;
};
const signCanvas$1 = function (fswSign) {
const parsed = parse$3.sign(fswSign);
if (parsed.spatials) {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
let styling = parse$2(parsed.style);
if (styling.detailsym) {
styling.detailsym.forEach(sym => {
if (parsed.spatials[sym.index - 1]) {
parsed.spatials[sym.index - 1].detail = sym.detail;
}
});
}
let x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
let y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
let x2 = parsed.max[0];
let y2 = parsed.max[1];
if (styling.padding) {
x1 -= styling.padding;
y1 -= styling.padding;
x2 += styling.padding;
y2 += styling.padding;
}
let sizing = 1;
if (styling.zoom != 'x') {
sizing = styling.zoom;
}
let w = (x2 - x1) * sizing;
let h = (y2 - y1) * sizing;
canvas.width = w ? w : 1;
canvas.height = h ? h : 1;
if (styling.background) {
context.rect(0, 0, w, h);
context.fillStyle = styling.background;
context.fill();
}
const line = styling.detail && styling.detail[0] || "black";
const fill = styling.detail && styling.detail[1] || "white";
parsed.spatials.forEach(spatial => {
let symLine = line;
if (spatial.detail) {
symLine = spatial.detail[0];
} else if (styling.colorize) {
symLine = colorize$1(spatial.symbol);
}
let symFill = fill;
if (spatial.detail && spatial.detail[1]) {
symFill = spatial.detail[1];
}
context.font = 30 * sizing + "px 'SuttonSignWritingFill'";
context.fillStyle = symFill;
context.fillText(symbolFill$1(spatial.symbol), (spatial.coord[0] - x1) * sizing, (spatial.coord[1] - y1) * sizing);
context.font = 30 * sizing + "px 'SuttonSignWritingLine'";
context.fillStyle = symLine;
context.fillText(symbolLine$1(spatial.symbol), (spatial.coord[0] - x1) * sizing, (spatial.coord[1] - y1) * sizing);
});
return canvas;
}
};
/**
* Function that creates a PNG data url from an FSW sign with an optional style string
* @function fsw.signPng
* @param {string} fswSign - an FSW sign with optional style string
* @returns {string} png image for sign as data url
* @example
* fsw.signPng('M525x535S2e748483x510S10011501x466S20544510x500S10019476x475')
*
* return 'data:image/png;base64,iVBORw...'
*/
const signPng$1 = fswSign => {
const canvas = signCanvas$1(fswSign);
const png = canvas.toDataURL("image/png");
canvas.remove();
return png;
};
/**
* Function that normalizes an FSW sign for a center of 500,500
* @function fsw.signNormalize
* @param {string} fswSign - an FSW sign with optional style string
* @returns {string} normalized FSW sign
* @example
* fsw.signNormalize('M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475')
*
* return 'M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475'
*/
const signNormalize$1 = fswSign => {
const parsed = parse$3.sign(fswSign);
if (parsed.spatials) {
const symbolsizes = parsed.spatials.reduce((output, spatial) => {
const size = symbolSize$1(spatial.symbol);
output[spatial.symbol] = {
width: size[0],
height: size[1]
};
return output;
}, {});
const bbox = symbols => {
const x1 = Math.min(...symbols.map(spatial => spatial.coord[0]));
const y1 = Math.min(...symbols.map(spatial => spatial.coord[1]));
const x2 = Math.max(...symbols.map(spatial => spatial.coord[0] + parseInt(symbolsizes[spatial.symbol].width)));
const y2 = Math.max(...symbols.map(spatial => spatial.coord[1] + parseInt(symbolsizes[spatial.symbol].height)));
return {
x1: x1,
y1: y1,
x2: x2,
y2: y2
};
};
const hrange = ranges$1['hcenter'];
const hsyms = parsed.spatials.filter(spatial => {
const dec = parseInt(spatial.symbol.slice(1, 4), 16);
return hrange[0] <= dec && hrange[1] >= dec;
});
const vrange = ranges$1['vcenter'];
const vsyms = parsed.spatials.filter(spatial => {
const dec = parseInt(spatial.symbol.slice(1, 4), 16);
return vrange[0] <= dec && vrange[1] >= dec;
});
let abox = bbox(parsed.spatials);
let max = [abox.x2, abox.y2];
if (hsyms.length) {
const hbox = bbox(hsyms);
abox.x1 = hbox.x1;
abox.x2 = hbox.x2;
}
if (vsyms.length) {
const vbox = bbox(vsyms);
abox.y1 = vbox.y1;
abox.y2 = vbox.y2;
}
const offset = [parseInt((abox.x2 + abox.x1) / 2) - 500, parseInt((abox.y2 + abox.y1) / 2) - 500];
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 || '');
return fswout;
}
};
/**
* Function that creates an SVG image for a column of FSW
* @function fsw.columnSvg
* @param {ColumnData} fswColumn - an array of objects with information about FSW signs and punctuation
* @param {ColumnOptions} options - an object of column options
* @returns {string} column svg
* @example
* fsw.columnSvg([
* {
* "x": 56,
* "y": 20,
* "minX": 481,
* "minY": 471,
* "width": 37,
* "height": 58,
* "lane": 0,
* "padding": 0,
* "segment": "sign",
* "text": "AS14c20S27106M518x529S14c20481x471S27106503x489",
* "zoom": 1
* },
* {
* "x": 57,
* "y": 118,
* "minX": 482,
* "minY": 468,
* "width": 36,
* "height": 65,
* "lane": 0,
* "padding": 0,
* "segment": "sign",
* "text": "AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468",
* "zoom": 1
* },
* {
* "x": 39,
* "y": 203,
* "minX": 464,
* "minY": 496,
* "width": 72,
* "height": 8,
* "lane": 0,
* "padding": 0,
* "segment": "symbol",
* "text": "S38800464x496",
* "zoom": 1
* }
* ],
* {
* "height": 250,
* "width": 150,
* })
*
* return `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="150" height="250" viewBox="0 0 150 250">
* <g transform="translate(56,20) scale(1) translate(-481,-471) ">
* <text font-size="0">AS14c20S27106M518x529S14c20481x471S27106503x489-D_black,white_Z1</text>
* <g transform="translate(481,471)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(503,489)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* </g>
* <g transform="translate(57,118) scale(1) translate(-482,-468) ">
* <text font-size="0">AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468-D_black,white_Z1</text>
* <g transform="translate(489,515)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(482,490)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(508,496)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(500,468)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* </g>
* <g transform="translate(39,203) scale(1) translate(-464,-496) ">
* <text font-size="0">S38800464x496-D_black,white_Z1</text>
* <g transform="translate(464,496)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* </g>
* </svg>`
*/
const columnSvg$1 = (fswColumn, options) => {
//if (typeof fswColumn !== 'array') return blank;
if (typeof options !== 'object') options = {};
const values = Object.assign(columnDefaults$1, options);
let x1 = 0;
let y1 = 0;
let x2 = values.width;
let y2 = values.height;
let background = '';
if (values.background) {
background = `\n <rect x="${x1}" y="${y1}" width="${x2 - x1}" height="${y2 - y1}" style="fill:${values.background};" />`;
}
let sizing = ` width="${values.width}" height="${values.height}"`;
let svg = `<svg version="1.1" xmlns="http://www.w3.org/2000/svg"${sizing} viewBox="${x1} ${y1} ${x2 - x1} ${y2 - y1}">
${background}`;
svg += fswColumn.map(item => {
const dash = item.text.indexOf('-');
if (dash > 0) {
const itemStyle = item.text.substring(dash);
const newStyle = {
...values.style,
...parse$2(itemStyle)
};
item.text = item.text.replace(itemStyle, compose$1(newStyle));
} else {
item.text += compose$1(values.style);
}
item.zoom = item.zoom * values.style.zoom;
return '<g transform="translate(' + item.x + ',' + item.y + ') scale(' + item.zoom + ') translate(' + -item.minX + ',' + -item.minY + ') ">' + (item.segment == "sign" ? signSvgBody$1(item.text) : symbolSvgBody$1(item.text)) + '</g>';
}).join('\n');
svg += '\n</svg>';
return svg;
};
/**
* Function that creates an array of SVG column images for an FSW text
* @function fsw.columnsSvg
* @param {string} fswText - a text of FSW signs and punctuation
* @param {ColumnOptions} options - an object of column options
* @returns {string[]} array of SVG columns
* @example
* fsw.columnsSvg('AS14c20S27106M518x529S14c20481x471S27106503x489 AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468 S38800464x496',{
* "height": 250,
* "width": 150,
* })
*
* return [`<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="150" height="250" viewBox="0 0 150 250">
* <g transform="translate(56,20) scale(1) translate(-481,-471) ">
* <text font-size="0">AS14c20S27106M518x529S14c20481x471S27106503x489-D_black,white_Z1</text>
* <g transform="translate(481,471)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(503,489)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* </g>
* <g transform="translate(57,118) scale(1) translate(-482,-468) ">
* <text font-size="0">AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468-D_black,white_Z1</text>
* <g transform="translate(489,515)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(482,490)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(508,496)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(500,468)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* </g>
* <g transform="translate(39,203) scale(1) translate(-464,-496) ">
* <text font-size="0">S38800464x496-D_black,white_Z1</text>
* <g transform="translate(464,496)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* </g>
* </svg>`]
*/
const columnsSvg$1 = function (fswText, options) {
if (typeof options !== 'object') options = {};
let values = columns$1(fswText, options);
let cols = values.columns.map((col, i) => {
return columnSvg$1(col, {
...values.options,
...{
width: values.widths[i]
}
});
});
return cols;
};
const columnCanvas$1 = function (fswColumn, options) {
if (typeof options !== 'object') options = {};
const values = Object.assign(columnDefaults$1, options);
const canvas = document.createElement('canvas');
canvas.width = values.width;
canvas.height = values.height;
const context = canvas.getContext('2d');
if (values.background) {
context.rect(0, 0, values.width, values.height);
context.fillStyle = values.background;
context.fill();
}
fswColumn.map(item => {
const dash = item.text.indexOf('-');
if (dash > 0) {
const itemStyle = item.text.substring(dash);
const newStyle = {
...values.style,
...parse$2(itemStyle)
};
item.text = item.text.replace(itemStyle, compose$1(newStyle));
} else {
item.text += compose$1(values.style);
}
item.zoom = item.zoom * values.style.zoom;
let parsed = {};
if (item.segment == "sign") {
parsed = parse$3.sign(item.text);
} else {
let sym = parse$3.symbol(item.text);
parsed.style = sym.style;
parsed.spatials = [sym];
}
let styling = parse$2(parsed.style);
if (styling.background) {
context.fillStyle = styling.background;
context.fillRect(item.x - styling.padding * item.zoom, item.y - styling.padding * item.zoom, (item.width + styling.padding * 2) * item.zoom, (item.height + styling.padding * 2) * item.zoom);
}
if (styling.detailsym) {
styling.detailsym.forEach(sym => {
if (parsed.spatials[sym.index - 1]) {
parsed.spatials[sym.index - 1].detail = sym.detail;
}
});
}
const line = styling.detail && styling.detail[0] || "black";
const fill = styling.detail && styling.detail[1] || "white";
parsed.spatials.forEach(spatial => {
let symLine = line;
if (spatial.detail) {
symLine = spatial.detail[0];
} else if (styling.colorize) {
symLine = colorize$1(spatial.symbol);
}
let symFill = fill;
if (spatial.detail && spatial.detail[1]) {
symFill = spatial.detail[1];
}
context.font = 30 * item.zoom + "px 'SuttonSignWritingFill'";
context.fillStyle = symFill;
context.fillText(symbolFill$1(spatial.symbol), item.x + (spatial.coord[0] - item.minX) * item.zoom, item.y + (spatial.coord[1] - item.minY) * item.zoom);
context.font = 30 * item.zoom + "px 'SuttonSignWritingLine'";
context.fillStyle = symLine;
context.fillText(symbolLine$1(spatial.symbol), item.x + (spatial.coord[0] - item.minX) * item.zoom, item.y + (spatial.coord[1] - item.minY) * item.zoom);
});
});
return canvas;
};
/**
* Function that creates a PNG data url for a column of FSW
* @function fsw.columnPng
* @param {ColumnData} fswColumn - an array of objects with information about FSW signs and punctuation
* @param {ColumnOptions} options - an object of column options
* @returns {string} column png data url
* @example
* fsw.columnPng([
* {
* "x": 56,
* "y": 20,
* "minX": 481,
* "minY": 471,
* "width": 37,
* "height": 58,
* "lane": 0,
* "padding": 0,
* "segment": "sign",
* "text": "AS14c20S27106M518x529S14c20481x471S27106503x489",
* "zoom": 1
* },
* {
* "x": 57,
* "y": 118,
* "minX": 482,
* "minY": 468,
* "width": 36,
* "height": 65,
* "lane": 0,
* "padding": 0,
* "segment": "sign",
* "text": "AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468",
* "zoom": 1
* },
* {
* "x": 39,
* "y": 203,
* "minX": 464,
* "minY": 496,
* "width": 72,
* "height": 8,
* "lane": 0,
* "padding": 0,
* "segment": "symbol",
* "text": "S38800464x496",
* "zoom": 1
* }
* ],
* {
* "height": 250,
* "width": 150,
* })
*
* return 'data:image/png;base64,iVBORw...'
*/
const columnPng$1 = (fswColumn, options) => {
const canvas = columnCanvas$1(fswColumn, options);
const png = canvas.toDataURL("image/png");
canvas.remove();
return png;
};
/**
* Function that creates an array of PNG data urls for an FSW text
* @function fsw.columnsPng
* @param {string} fswText - a text of FSW signs and punctuation
* @param {ColumnOptions} options - an object of column options
* @returns {string[]} array of PNG data urls
* @example
* fsw.columnsPng('AS14c20S27106M518x529S14c20481x471S27106503x489 AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468 S38800464x496',{
* "height": 250,
* "width": 150,
* })
*
* return ['data:image/png;base64,iVBORw...']
*/
const columnsPng$1 = function (fswText, options) {
if (typeof options !== 'object') options = {};
let values = columns$1(fswText, options);
let cols = values.columns.map((col, i) => {
return columnPng$1(col, {
...values.options,
...{
width: values.widths[i]
}
});
});
return cols;
};
/** The fsw module contains functions for handling Formal SignWriitng in ASCII (FSW) characters.
* [FSW characters definition](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-09.html#name-formal-signwriting-in-ascii)
* @module fsw
*/
var index$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
symbolSize: symbolSize$1,
symbolLine: symbolLine$1,
symbolFill: symbolFill$1,
symbolText: symbolText$1,
symbolSvgBody: symbolSvgBody$1,
symbolSvg: symbolSvg$1,
symbolPng: symbolPng$1,
symbolNormalize: symbolNormalize$1,
symbolMirror: symbolMirror$1,
symbolInvert: symbolInvert$1,
symbolRotate: symbolRotate$1,
symbolFlop: symbolFlop$1,
symbolScroll: symbolScroll$1,
signSvgBody: signSvgBody$1,
signSvg: signSvg$1,
signPng: signPng$1,
signNormalize: signNormalize$1,
columnSvg: columnSvg$1,
columnsSvg: columnsSvg$1,
columnPng: columnPng$1,
columnsPng: columnsPng$1
});
/**
* Sutton SignWriting Core Module v2.0.0 (https://github.com/sutton-signwriting/core)
* Author: Steve Slevinski (https://SteveSlevinski.me)
* swu.mjs is released under the MIT License.
*/
/**
* Object of regular expressions for SWU strings in UTF-16
*
* @alias swu.re
* @property {string} null - the null symbol
* @property {string} symbol - a symbol
* @property {string} nullorsymbol - null or a symbol
* @property {string} sort - the sorting marker
* @property {string} prefix - a sorting marker followed by one or more symbols with nulls
* @property {string} box - a signbox marker
* @property {string} coord - a coordinate
* @property {string} spatial - a symbol followed by a coordinate
* @property {string} signbox - a signbox marker, max coordinate and zero or more spatial symbols
* @property {string} sign - an optional prefix followed by a signbox
* @property {string} sortable - a mandatory prefix followed by a signbox
*/
let re$1 = {
'null': '\uD8C0\uDC00',
'symbol': '(?:(?:\uD8C0[\uDC01-\uDFFF])|(?:[\uD8C1-\uD8FC][\uDC00-\uDFFF])|(?:\uD8FD[\uDC00-\uDC80]))',
'coord': '(?:\uD836[\uDC0C-\uDDFF]){2}',
'sort': '\uD836\uDC00',
'box': '\uD836[\uDC01-\uDC04]'
};
re$1.nullorsymbol = `(?:${re$1.null}|${re$1.symbol})`;
re$1.prefix = `(?:${re$1.sort}(?:${re$1.nullorsymbol})+)`;
re$1.spatial = `${re$1.symbol}${re$1.coord}`;
re$1.signbox = `${re$1.box}${re$1.coord}(?:${re$1.spatial})*`;
re$1.sign = `${re$1.prefix}?${re$1.signbox}`;
re$1.sortable = `${re$1.prefix}${re$1.signbox}`;
/**
* Object of regular expressions for style strings
*
* @alias style.re
* @type {object}
* @property {string} colorize - regular expression for colorize section
* @property {string} colorhex - regular expression for color hex values with 3 or 6 characters
* @property {string} colorname - regular expression for css color name
* @property {string} padding - regular expression for padding section
* @property {string} zoom - regular expression for zoom section
* @property {string} classbase - regular expression for class name definition
* @property {string} id - regular expression for id definition
* @property {string} colorbase - regular expression for color hex or color name
* @property {string} color - regular expression for single color entry
* @property {string} colors - regular expression for double color entry
* @property {string} background - regular expression for background section
* @property {string} detail - regular expression for color details for line and optional fill
* @property {string} detailsym - regular expression for color details for individual symbols
* @property {string} classes - regular expression for one or more class names
* @property {string} full - full regular expression for style string
*/
let re = {
'colorize': 'C',
'colorhex': '(?:[0-9a-fA-F]{3}){1,2}',
'colorname': '[a-zA-Z]+',
'padding': 'P[0-9]{2}',
'zoom': 'Z(?:[0-9]+(?:\\.[0-9]+)?|x)',
'classbase': '-?[_a-zA-Z][_a-zA-Z0-9-]{0,100}',
'id': '[a-zA-Z][_a-zA-Z0-9-]{0,100}'
};
re.colorbase = `(?:${re.colorhex}|${re.colorname})`;
re.color = `_${re.colorbase}_`;
re.colors = `_${re.colorbase}(?:,${re.colorbase})?_`;
re.background = `G${re.color}`;
re.detail = `D${re.colors}`;
re.detailsym = `D[0-9]{2}${re.colors}`;
re.classes = `${re.classbase}(?: ${re.classbase})*`;
re.full = `-(${re.colorize})?(${re.padding})?(${re.background})?(${re.detail})?(${re.zoom})?(?:-((?:${re.detailsym})*))?(?:-(${re.classes})?!(?:(${re.id})!)?)?`;
const prefixColor = color => {
const regex = new RegExp(`^${re.colorhex}$`);
return (regex.test(color) ? '#' : '') + color;
};
const definedProps = obj => Object.fromEntries(Object.entries(obj).filter(([k, v]) => v !== undefined));
/**
* Function to parse style string to object
* @function style.parse
* @param {string} styleString - a style string
* @returns {StyleObject} elements of style string
* @example
* style.parse('-CP10G_blue_D_red,Cyan_')
*
* return {
* 'colorize': true,
* 'padding': 10,
* 'background': 'blue',
* 'detail': ['red', 'Cyan']
* }
*/
const parse$1 = styleString => {
const regex = `^${re.full}`;
const m = (typeof styleString === 'string' ? styleString.match(new RegExp(regex)) : []) || [];
return definedProps({
'colorize': !m[1] ? undefined : !!m[1],
'padding': !m[2] ? undefined : parseInt(m[2].slice(1)),
'background': !m[3] ? undefined : prefixColor(m[3].slice(2, -1)),
'detail': !m[4] ? undefined : m[4].slice(2, -1).split(',').map(prefixColor),
'zoom': !m[5] ? undefined : m[5] === 'Zx' ? 'x' : parseFloat(m[5].slice(1)),
'detailsym': !m[6] ? undefined : m[6].match(new RegExp(re.detailsym, 'g')).map(val => {
const parts = val.split('_');
const detail = parts[1].split(',').map(prefixColor);
return {
'index': parseInt(parts[0].slice(1)),
'detail': detail
};
}),
'classes': !m[7] ? undefined : m[7],
'id': !m[8] ? undefined : m[8]
});
};
/** 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.
* [Characters set definitions](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-09.html#name-characters)
* @module convert
*/
/**
* Function to convert an SWU number character to an integer
* @function convert.swu2num
* @param {string} swuNum - SWU number character
* @returns {number} Integer value for number
* @example
* convert.swu2num('𝤆')
*
* return 500
*/
const swu2num = swuNum => parseInt(swuNum.codePointAt(0)) - 0x1D80C + 250;
/**
* Function to convert a number to an SWU number character
* @function convert.num2swu
* @param {number} num - Integer value for number
* @returns {string} SWU number character
* @example
* convert.num2swu(500)
*
* return '𝤆'
*/
const num2swu = num => String.fromCodePoint(0x1D80C + parseInt(num) - 250);
/**
* Function to convert two SWU number characters to an array of x,y integers
* @function convert.swu2coord
* @param {string} swuCoord - Two SWU number character
* @returns {number[]} Array of x,y integers
* @example
* convert.swu2coord('𝤆𝤆')
*
* return [500, 500]
*/
const swu2coord = swuCoord => [swu2num(swuCoord.slice(0, 2)), swu2num(swuCoord.slice(2, 4))];
/**
* Function to convert an array of x,y integers to two SWU number characters
* @function convert.coord2swu
* @param {number[]} coord - Array of x,y integers
* @returns {string} Two SWU number character
* @example
* convert.coord2swu([500, 500])
*
* return '𝤆𝤆'
*/
const coord2swu = coord => coord.map(num => num2swu(num)).join('');
/**
* Function to convert an SWU symbol character to a code point on plane 4
* @function convert.swu2code
* @param {string} swuSym - SWU symbol character
* @returns {number} Code point on plane 4
* @example
* convert.swu2code('')
*
* return 0x40001
*/
const swu2code = swuSym => parseInt(swuSym.codePointAt(0));
const parse = {
/**
* Function to parse an swu symbol with optional coordinate and style string
* @function swu.parse.symbol
* @param {string} swuSym - an swu symbol
* @returns {SymbolObject} elements of swu symbol
* @example
* swu.parse.symbol('𝤆𝤆-C')
*
* return {
* 'symbol': '',
* 'coord': [500, 500],
* 'style': '-C'
* }
*/
symbol: swuSym => {
const regex = `^(${re$1.symbol})(${re$1.coord})?(${re.full})?`;
const symbol = typeof swuSym === 'string' ? swuSym.match(new RegExp(regex)) : undefined;
return {
'symbol': symbol ? symbol[1] : undefined,
'coord': symbol && symbol[2] ? swu2coord(symbol[2]) : undefined,
'style': symbol ? symbol[3] : undefined
};
},
/**
* Function to parse an swu sign with style string
* @function swu.parse.sign
* @param {string} swuSign - an swu sign
* @returns {SignObject} elements of swu sign
* @example
* swu.parse.sign('𝠀𝠃𝤟𝤩𝣵𝤐𝤇𝣤𝤐𝤆𝣮𝣭-C')
*
* return {
* sequence: ['','','',''],
* box: '𝠃',
* max: [525, 535],
* spatials: [
* {
* symbol: '',
* coord: [483, 510]
* },
* {
* symbol: '',
* coord: [501, 466]
* },
* {
* symbol: '',
* coord: [510, 500]
* },
* {
* symbol: '',
* coord: [476, 475]
* }
* ],
* style: '-C'
* }
*/
sign: swuSign => {
const regex = `^(${re$1.prefix})?(${re$1.signbox})(${re.full})?`;
const sign = typeof swuSign === 'string' ? swuSign.match(new RegExp(regex)) : undefined;
if (sign) {
return {
'sequence': sign[1] ? sign[1].slice(2).match(/.{2}/g) : undefined,
'box': sign[2].slice(0, 2),
'max': swu2coord(sign[2].slice(2, 6)),
'spatials': sign[2].length < 7 ? undefined : sign[2].slice(6).match(/(.{6})/g).map(m => {
return {
symbol: m.slice(0, 2),
coord: swu2coord(m.slice(2))
};
}),
'style': sign[3]
};
} else {
return {};
}
},
/**
* Function to parse an swu text
* @function swu.parse.text
* @param {string} swuText - an swu text
* @returns {string[]} swu signs and punctuations
* @example
* swu.parse.text('𝠀𝠃𝤘𝤣𝣳𝣩𝤉𝣻 𝠀𝠃𝤘𝤧𝣻𝤕𝣴𝣼𝤎𝤂𝤆𝣦 𝣢𝤂')
*
* return [
* '𝠀𝠃𝤘𝤣𝣳𝣩𝤉𝣻',
* '𝠀𝠃𝤘𝤧𝣻𝤕𝣴𝣼𝤎𝤂𝤆𝣦',
* '𝣢𝤂'
* ]
*/
text: swuText => {
if (typeof swuText !== 'string') return [];
const regex = `(${re$1.sign}(${re.full})?|${re$1.spatial}(${re.full})?)`;
const matches = swuText.match(new RegExp(regex, 'g'));
return matches ? [...matches] : [];
}
};
const compose = {
/**
* Function to compose an swu symbol with optional coordinate and style string
* @function swu.compose.symbol
* @param {SymbolObject} swuSymObject - an swu symbol object
* @returns {string} an swu symbol string
* @example
* swu.compose.symbol({
* 'symbol': '',
* 'coord': [500, 500],
* 'style': '-C'
* })
*
* return '𝤆𝤆-C'
*/
symbol: swuSymObject => {
if (typeof swuSymObject !== 'object' || swuSymObject === null) return undefined;
if (typeof swuSymObject.symbol === 'string') {
const symbol = (swuSymObject.symbol.match(re$1.symbol) || [''])[0];
if (symbol) {
const x = swuSymObject.coord && swuSymObject.coord[0] || '';
const y = swuSymObject.coord && swuSymObject.coord[1] || '';
const coord = x && y ? coord2swu([x, y]) : '';
const styleStr = typeof swuSymObject.style === 'string' && (swuSymObject.style.match(re.full) || [''])[0] || '';
return symbol + coord + styleStr;
}
}
return undefined;
},
/**
* Function to compose an swu sign with style string
* @function swu.compose.sign
* @param {SignObject} swuSignObject - an swu sign object
* @returns {string} an swu sign string
* @example
* swu.compose.sign({
* sequence: ['','','',''],
* box: '𝠃',
* max: [525, 535],
* spatials: [
* {
* symbol: '',
* coord: [483, 510]
* },
* {
* symbol: '',
* coord: [501, 466]
* },
* {
* symbol: '',
* coord: [510, 500]
* },
* {
* symbol: '',
* coord: [476, 475]
* }
* ],
* style: '-C'
* })
*
* return '𝠀𝠃𝤟𝤩𝣵𝤐𝤇𝣤𝤐𝤆𝣮𝣭-C'
*/
sign: swuSignObject => {
if (typeof swuSignObject !== 'object' || swuSignObject === null) return undefined;
let box = typeof swuSignObject.box !== 'string' ? '𝠃' : (swuSignObject.box + '𝠃').match(re$1.box);
const x = swuSignObject.max && swuSignObject.max[0] || '';
const y = swuSignObject.max && swuSignObject.max[1] || '';
const max = x && y ? coord2swu([x, y]) : undefined;
if (!max) return undefined;
let prefix = '';
if (swuSignObject.sequence && Array.isArray(swuSignObject.sequence)) {
prefix = swuSignObject.sequence.map(key => (key.match(re$1.nullorsymbol) || [''])[0]).join('');
prefix = prefix ? '𝠀' + prefix : '';
}
let signbox = '';
if (swuSignObject.spatials && Array.isArray(swuSignObject.spatials)) {
signbox = swuSignObject.spatials.map(spatial => {
if (typeof spatial.symbol === 'string') {
const symbol = (spatial.symbol.match(re$1.symbol) || [''])[0];
if (symbol) {
const x = spatial.coord && spatial.coord[0] || '';
const y = spatial.coord && spatial.coord[1] || '';
const coord = x && y ? coord2swu([x, y]) : '';
if (coord) {
return symbol + coord;
}
}
}
return '';
}).join('');
}
const styleStr = typeof swuSignObject.style === 'string' && (swuSignObject.style.match(re.full) || [''])[0] || '';
return prefix + box + max + signbox + styleStr;
}
};
/**
* Function to gather sizing information about an swu sign or symbol
* @function swu.info
* @param {string} swu - an swu sign or symbol
* @returns {SegmentInfo} information about the swu string
* @example
* swu.info('𝠀𝠂𝤘𝤣𝣳𝣩𝤉𝣻-P10Z2')
*
* return {
* minX: 481,
* minY: 471,
* width: 37,
* height: 58,
* lane: -1,
* padding: 10,
* segment: 'sign',
* zoom: 2
* }
*/
const info = swu => {
let lanes = {
'𝠁': 0,
'𝠂': -1,
'𝠃': 0,
'𝠄': 1
};
let parsed = parse.sign(swu);
let width, height, segment, x1, x2, y1, y2, lane;
if (parsed.spatials) {
x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
x2 = parsed.max[0];
width = x2 - x1;
y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
y2 = parsed.max[1];
height = y2 - y1;
segment = 'sign';
lane = parsed.box;
} else {
parsed = parse.symbol(swu);
lane = "𝠃";
if (parsed.coord) {
x1 = parsed.coord[0];
width = (500 - x1) * 2;
y1 = parsed.coord[1];
height = (500 - y1) * 2;
segment = 'symbol';
} else {
x1 = 490;
width = 20;
y1 = 490;
height = 20;
segment = 'none';
}
}
let style = parse$1(parsed.style);
let zoom = style.zoom || 1;
let padding = style.padding || 0;
return {
minX: x1,
minY: y1,
width: width,
height: height,
segment: segment,
lane: lanes[lane],
padding: padding,
zoom: zoom
};
};
const columnDefaults = {
'height': 500,
'width': 150,
'offset': 50,
'pad': 20,
'margin': 5,
'dynamic': false,
'background': undefined,
'punctuation': {
'spacing': true,
'pad': 30,
'pull': true
},
'style': {
'detail': ['black', 'white'],
'zoom': 1
}
};
/**
* Function to an object of column options with default values
*
* @function swu.columnDefaultsMerge
* @param {ColumnOptions} options - object of column options
* @returns {ColumnOptions} object of column options merged with column defaults
* @example
* swu.columnDefaultsMerge({height: 500,width:150})
*
* return {
* "height": 500,
* "width": 150,
* "offset": 50,
* "pad": 20,
* "margin": 5,
* "dynamic": false,
* "punctuation": {
* "spacing": true,
* "pad": 30,
* "pull": true
* },
* "style": {
* "detail": [
* "black",
* "white"
* ],
* "zoom": 1
* }
* }
*/
const columnDefaultsMerge = options => {
if (typeof options !== 'object') options = {};
return {
...columnDefaults,
...options,
punctuation: {
...columnDefaults.punctuation,
...options.punctuation
},
style: {
...columnDefaults.style,
...options.style
}
};
};
/**
* Function to transform an SWU text to an array of columns
*
* @function swu.columns
* @param {string} swuText - SWU text of signs and punctuation
* @param {ColumnOptions} options - object of column options
* @returns {{options:ColumnOptions,widths:number[],columns:ColumnData}} object of column options, widths array, and column data
* @example
* swu.columns('𝠀𝠃𝤘𝤣𝣳𝣩𝤉𝣻 𝠀𝠃𝤘𝤧𝣻𝤕𝣴𝣼𝤎𝤂𝤆𝣦 𝣢𝤂', {height: 500,width:150})
*
* return {
* "options": {
* "height": 500,
* "width": 150,
* "offset": 50,
* "pad": 20,
* "margin": 5,
* "dynamic": false,
* "punctuation": {
* "spacing": true,
* "pad": 30,
* "pull": true
* },
* "style": {
* "detail": [
* "black",
* "white"
* ],
* "zoom": 1
* }
* },
* "widths": [
* 150
* ],
* "columns": [
* [
* {
* "x": 56,
* "y": 20,
* "minX": 481,
* "minY": 471,
* "width": 37,
* "height": 58,
* "lane": 0,
* "padding": 0,
* "segment": "sign",
* "text": "𝠀𝠃𝤘𝤣𝣳𝣩𝤉𝣻",
* "zoom": 1
* },
* {
* "x": 57,
* "y": 118,
* "minX": 482,
* "minY": 468,
* "width": 36,
* "height": 65,
* "lane": 0,
* "padding": 0,
* "segment": "sign",
* "text": "𝠀𝠃𝤘𝤧𝣻𝤕𝣴𝣼𝤎𝤂𝤆𝣦",
* "zoom": 1
* },
* {
* "x": 39,
* "y": 203,
* "minX": 464,
* "minY": 496,
* "width": 72,
* "height": 8,
* "lane": 0,
* "padding": 0,
* "segment": "symbol",
* "text": "𝣢𝤂",
* "zoom": 1
* }
* ]
* ]
* }
*/
const columns = (swuText, options) => {
if (typeof swuText !== 'string') return {};
const values = columnDefaultsMerge(options);
let input = parse.text(swuText);
let cursor = 0;
let cols = [];
let col = [];
let plus = 0;
let center = parseInt(values.width / 2);
let maxHeight = values.height - values.margin;
let pullable = true;
let finalize = false;
for (let val of input) {
let informed = info(val);
cursor += plus;
if (values.punctuation.spacing) {
cursor += informed.segment == 'sign' ? values.pad : 0;
} else {
cursor += values.pad;
}
finalize = cursor + informed.height > maxHeight;
if (finalize && informed.segment == 'symbol' && values.punctuation.pull && pullable) {
finalize = false;
pullable = false;
}
if (col.length == 0) {
finalize = false;
}
if (finalize) {
cursor = values.pad;
cols.push(col);
col = [];
pullable = true;
}
col.push(Object.assign(informed, {
x: center + values.offset * informed.lane - (500 - informed.minX) * informed.zoom * values.style.zoom,
y: cursor,
text: val
}));
cursor += informed.height * informed.zoom * values.style.zoom;
if (values.punctuation.spacing) {
plus = informed.segment == 'sign' ? values.pad : values.punctuation.pad;
} else {
plus = values.pad;
}
}
if (col.length) {
cols.push(col);
}
// over height issue when pulling punctuation
if (values.punctuation.pull) {
for (let col of cols) {
let last = col[col.length - 1];
let diff = last.y + last.height - (values.height - values.margin);
if (diff > 0) {
let adj = parseInt(diff / col.length) + 1;
for (let i in col) {
col[i].y -= adj * i + adj;
}
}
}
}
// contract, expand, adjust
let widths = [];
for (let col of cols) {
let min = [center - values.offset - values.pad];
let max = [center + values.offset + values.pad];
for (let item of col) {
min.push(item.x - values.pad);
max.push(item.x + item.width + values.pad);
}
min = Math.min(...min);
max = Math.max(...max);
let width = values.width;
let adj = 0;
if (!values.dynamic) {
adj = center - parseInt((min + max) / 2);
} else {
width = max - min;
adj = -min;
}
for (let item of col) {
item.x += adj;
}
widths.push(width);
}
return {
'options': values,
'widths': widths,
'columns': cols
};
};
/**
* Array of plane 4 code points for categories of symbols: hand, movement, dynamics, head, trunk & limb, location, and punctuation.
* @alias swu.category
* @type {array}
*/
const category = [0x40001, 0x461e1, 0x4bca1, 0x4bfa1, 0x4e8e1, 0x4efa1, 0x4f2a1];
/**
* Object of symbol ranges with starting and ending code points on plane 4.
*
* { all, writing, hand, movement, dynamic, head, hcenter, vcenter, trunk, limb, location, punctuation }
* @alias swu.ranges
* @type {object}
*/
const ranges = {
'all': [0x40001, 0x4f480],
'writing': [0x40001, 0x4efa0],
'hand': [0x40001, 0x461e0],
'movement': [0x461e1, 0x4bca0],
'dynamic': [0x4bca1, 0x4bfa0],
'head': [0x4bfa1, 0x4e8e0],
'hcenter': [0x4bfa1, 0x4e8e0],
'vcenter': [0x4bfa1, 0x4ec40],
'trunk': [0x4e8e1, 0x4ec40],
'limb': [0x4ec41, 0x4efa0],
'location': [0x4efa1, 0x4f2a0],
'punctuation': [0x4f2a1, 0x4f480]
};
/**
* Array of colors associated with the seven symbol categories.
* @alias swu.colors
* @type {array}
*/
const colors = ['#0000CC', '#CC0000', '#FF0099', '#006600', '#000000', '#884411', '#FF9900'];
/**
* Function that returns the standardized color for a symbol.
* @function swu.colorize
* @param {string} swuSym - an SWU symbol character
* @returns {string} name of standardized color for symbol
* @example
* swu.colorize('')
*
* return '#0000CC'
*/
const colorize = swuSym => {
const parsed = parse.symbol(swuSym);
let color = '#000000';
if (parsed.symbol) {
const code = swu2code(parsed.symbol);
const index = category.findIndex(val => val > code);
color = colors[index < 0 ? 6 : index - 1];
}
return color;
};
/* support ongoing development */
/* https://patreon.com/signwriting */
/* https://donate.sutton-signwriting.io */
/**
* Function that returns the size of a symbol using an SWU symbol character
* @function swu.symbolSize
* @param {string} swu - an SWU symbol character
* @returns {number[]} width and height of symbol
* @example
* swu.symbolSize("")
*
* return [15,30]
*/
const symbolSize = function (swu) {
const parsed = parse.symbol(swu);
if (!parsed.symbol) {
return undefined;
}
return symbolSize$2(swu2id(swu));
};
/**
* Function that returns a plane 15 character for a symbol line using an SWU symbol character
* @function swu.symbolLine
* @param {string} swu - an SWU symbol character
* @returns {string} character for symbol line
* @example
* swu.symbolLine('')
*
* return ''
*/
const symbolLine = function (swu) {
return symbolLine$2(swu2id(swu));
};
/**
* Function that returns a plane 165 character for a symbol fill using an SWU symbol character
* @function swu.symbolFill
* @param {string} swu - an SWU symbol character
* @returns {string} character for symbol fill
* @example
* swu.symbolFill('')
*
* return ''
*/
const symbolFill = function (swu) {
return symbolFill$2(swu2id(swu));
};
/**
* Function that creates two text elements for a symbol using an SWU symbol character
* @function swu.symbolText
* @param {string} swu - an SWU symbol character
* @returns {string} svg segment for line and fill
* @example
* swu.symbolText('')
*
* return ` <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>`
*/
const symbolText = function (swu) {
return symbolText$2(swu2id(swu));
};
/**
* Function that creates an SVG image from an SWU symbol key with an optional style string
* @function swu.symbolSvgBody
* @param {string} swuSym - an SWU symbol key with optional style string
* @returns {string} body of SVG for symbol
* @example
* swu.symbolSvgBody('S10000')
*
* return `<text font-size="0">S10000</text>
* <g transform="translate(500,500)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>`
*/
const symbolSvgBody = swuSym => {
const parsed = parse.symbol(swuSym);
const blank = '';
if (!parsed.symbol) return blank;
let styling = parse$2(parsed.style);
let x1, y1, x2, y2;
if (parsed.coord) {
x1 = parsed.coord[0];
y1 = parsed.coord[1];
x2 = 500 + (500 - x1);
y2 = 500 + (500 - y1);
} else {
let size = symbolSize(parsed.symbol);
if (!size) return blank;
x1 = 500 - parseInt((size[0] + 1) / 2);
y1 = 500 - parseInt((size[1] + 1) / 2);
x2 = 500 + (500 - x1);
y2 = 500 + (500 - y1);
}
let symSvg = symbolText(parsed.symbol);
symSvg = ` <g transform="translate(${x1},${y1})">
${symSvg}
</g>`;
let line;
if (styling.colorize) {
line = colorize(parsed.symbol);
} else if (styling.detail) {
line = styling.detail[0];
}
if (line) {
symSvg = symSvg.replace(/class="sym-line" fill="black"/, `class="sym-line" fill="${line}"`);
}
let fill = styling.detail && styling.detail[1];
if (fill) {
symSvg = symSvg.replace(/class="sym-fill" fill="white"/, `class="sym-fill" fill="${fill}"`);
}
let background = '';
if (styling.padding) {
x1 -= styling.padding;
y1 -= styling.padding;
x2 += styling.padding;
y2 += styling.padding;
}
if (styling.background) {
background = `\n <rect x="${x1}" y="${y1}" width="${x2 - x1}" height="${y2 - y1}" style="fill:${styling.background};" />`;
}
return ` <text font-size="0">${swuSym}</text>${background}
${symSvg}`;
};
/**
* Function that creates an SVG image from an SWU symbol key with an optional style string
* @function swu.symbolSvg
* @param {string} swuSym - an SWU symbol key with optional style string
* @returns {string} SVG for symbol
* @example
* swu.symbolSvg('S10000')
*
* return `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="15" height="30" viewBox="500 500 15 30">
* <text font-size="0">S10000</text>
* <g transform="translate(500,500)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* </svg>`
*/
const symbolSvg = swuSym => {
const parsed = parse.symbol(swuSym);
const blank = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="1" height="1"></svg>';
if (!parsed.symbol) return blank;
let styling = parse$2(parsed.style);
let x1, y1, x2, y2;
if (parsed.coord) {
x1 = parsed.coord[0];
y1 = parsed.coord[1];
x2 = 500 + (500 - x1);
y2 = 500 + (500 - y1);
} else {
let size = symbolSize(parsed.symbol);
if (!size) return blank;
x1 = parseInt(500 - size[0] / 2);
y1 = parseInt(500 - size[1] / 2);
x2 = x1 + size[0];
y2 = y1 + size[1];
}
let classes = '';
if (styling.classes) {
classes = ` class="${styling.classes}"`;
}
let id = '';
if (styling.id) {
id = ` id="${styling.id}"`;
}
if (styling.padding) {
x1 -= styling.padding;
y1 -= styling.padding;
x2 += styling.padding;
y2 += styling.padding;
}
let sizing = '';
if (styling.zoom != 'x') {
sizing = ` width="${(x2 - x1) * (styling.zoom ? styling.zoom : 1)}" height="${(y2 - y1) * (styling.zoom ? styling.zoom : 1)}"`;
}
return `<svg${classes}${id} version="1.1" xmlns="http://www.w3.org/2000/svg"${sizing} viewBox="${x1} ${y1} ${x2 - x1} ${y2 - y1}">
${symbolSvgBody(swuSym)}
</svg>`;
};
const symbolCanvas = function (swuSym) {
const parsed = parse.symbol(swuSym);
if (parsed.symbol) {
let size = symbolSize(parsed.symbol);
if (size) {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
let styling = parse$2(parsed.style);
let line = 'black';
if (styling.colorize) {
line = colorize(parsed.symbol);
} else if (styling.detail) {
line = styling.detail[0];
}
let fill = styling.detail && styling.detail[1] || 'white';
let x1 = 500;
let x2 = x1 + size[0];
let y1 = 500;
let y2 = y1 + size[1];
if (styling.padding) {
x1 -= styling.padding;
y1 -= styling.padding;
x2 += styling.padding;
y2 += styling.padding;
}
let sizing = 1;
if (styling.zoom != 'x') {
sizing = styling.zoom;
}
let w = (x2 - x1) * sizing;
let h = (y2 - y1) * sizing;
canvas.width = w ? w : 1;
canvas.height = h ? h : 1;
if (styling.background) {
context.rect(0, 0, w, h);
context.fillStyle = styling.background;
context.fill();
}
context.font = 30 * sizing + "px 'SuttonSignWritingFill'";
context.fillStyle = fill;
context.fillText(symbolFill(parsed.symbol), (500 - x1) * sizing, (500 - y1) * sizing);
context.font = 30 * sizing + "px 'SuttonSignWritingLine'";
context.fillStyle = line;
context.fillText(symbolLine(parsed.symbol), (500 - x1) * sizing, (500 - y1) * sizing);
return canvas;
}
}
};
/**
* Function that creates a PNG data url from an SWU symbol character with an optional style string
* @function swu.symbolPng
* @param {string} swuSym - an SWU symbol character with optional style string
* @returns {string} png image for symbol as data url
* @example
* swu.symbolPng('-CP10G_green_Z2')
*
* return 'data:image/png;base64,iVBORw...'
*/
const symbolPng = swuSym => {
const canvas = symbolCanvas(swuSym);
const png = canvas.toDataURL("image/png");
canvas.remove();
return png;
};
const blank = null;
/**
* Function that normalizes a symbol with a minimum coordinate for a center of 500,500
* @function swu.symbolNormalize
* @param {string} swuSym - an SWU symbol character with optional coordinate and style string
* @returns {string} normalized SWU symbol
* @example
* swu.symbolNormalize('')
*
* return '𝣿𝣷'
*/
const symbolNormalize = swuSym => {
const parsed = parse.symbol(swuSym);
if (parsed.symbol) {
let size = symbolSize(parsed.symbol);
if (size) {
return `${parsed.symbol}${coord2swu$1([500 - parseInt((size[0] + 1) / 2), 500 - parseInt((size[1] + 1) / 2)])}${parsed.style || ''}`;
}
} else {
return blank;
}
};
/**
* Function that mirrors a symbol
* @function swu.symbolMirror
* @param {string} swuSym - an SWU symbol with optional coordinate and style string
* @returns {string} mirrored SWU symbol
* @example
* swu.symbolMirror('')
*
* return ''
*/
const symbolMirror = swuSym => {
let parsed = parse.symbol(swuSym);
if (!parsed.symbol) {
return swuSym;
}
const size = symbolSize(parsed.symbol);
if (!size) {
return swuSym;
}
parsed.symbol = swu2key(parsed.symbol);
const base = parsed.symbol.slice(0, 4);
let fill = parsed.symbol.slice(4, 5);
let rot = parseInt(parsed.symbol.slice(5, 6), 16);
const key1 = base + "08";
const key2 = base + "18";
var rAdd;
if (symbolSize(key2swu(key1)) || symbolSize(key2swu(key2))) {
rAdd = 8;
} else {
if (rot === 0 || rot == 4) {
rAdd = 0;
}
if (rot == 1 || rot == 5) {
rAdd = 6;
}
if (rot == 2 || rot == 6) {
rAdd = 4;
}
if (rot == 3 || rot == 7) {
rAdd = 2;
}
}
let key = '';
while (!key || !symbolSize(key2swu(key))) {
rot += rAdd;
if (rot > 7 && rAdd < 8) {
rot = rot - 8;
}
if (rot > 15) {
rot = rot - 16;
}
key = base + fill + rot.toString(16);
}
parsed.symbol = key2swu(key);
return compose.symbol(parsed);
};
/**
* Function that inverts a symbol
* @function swu.symbolInvert
* @param {string} swuSym - an SWU symbol with optional coordinate and style string
* @returns {string} inverted SWU symbol
* @example
* swu.symbolInvert('')
*
* return ''
*/
const symbolInvert = swuSym => {
let parsed = parse.symbol(swuSym);
if (!parsed.symbol) {
return swuSym;
}
const size = symbolSize(parsed.symbol);
if (!size) {
return swuSym;
}
parsed.symbol = swu2key(parsed.symbol);
const base = parsed.symbol.slice(0, 4);
let fill = parsed.symbol.slice(4, 5);
let rot = parsed.symbol.slice(5, 6);
const key1 = base + "08";
const key2 = base + "18";
let map;
if (symbolSize(key2swu(key1)) || symbolSize(key2swu(key2))) {
map = {
'0': 'c',
'1': 'd',
'2': 'e',
'3': 'f',
'4': '8',
'5': '9',
'6': 'a',
'7': 'b',
'c': '0',
'd': '1',
'e': '2',
'f': '3',
'8': '4',
'9': '5',
'a': '6',
'b': '7'
};
} else {
map = {
'0': '4',
'1': '3',
'2': '2',
'3': '1',
'4': '0',
'5': '7',
'6': '6',
'7': '5'
};
}
if (rot in map) {
const key = base + fill + map[rot];
if (symbolSize(key2swu(key))) {
parsed.symbol = key2swu(key);
}
}
return compose.symbol(parsed);
};
/**
* Function that rotates a symbol
* @function swu.symbolRotate
* @param {string} swuSym - an SWU symbol with optional coordinate and style string
* @param {boolean} [clockwise=true] - rotate the symbol clockwise
* @returns {string} rotated SWU symbol
* @example
* swu.symbolRotate('')
*
* return ''
*/
const symbolRotate = (swuSym, clockwise = true) => {
let parsed = parse.symbol(swuSym);
if (!parsed.symbol) {
return swuSym;
}
const size = symbolSize(parsed.symbol);
if (!size) {
return swuSym;
}
parsed.symbol = swu2key(parsed.symbol);
const step = clockwise ? 1 : -1;
const base = parsed.symbol.slice(0, 4);
let fill = parsed.symbol.slice(4, 5);
let rot = parseInt(parsed.symbol.slice(5, 6), 16);
let key = '';
while (!key || !symbolSize(key2swu(key))) {
if (rot > 7) {
rot += step;
if (rot > 15) {
rot = 8;
}
if (rot < 8) {
rot = 15;
}
key = base + fill + rot.toString(16);
} else {
rot -= step;
if (rot > 7) {
rot = 0;
}
if (rot < 0) {
rot = 7;
}
key = base + fill + rot;
}
}
parsed.symbol = key2swu(key);
return compose.symbol(parsed);
};
/**
* Function that changes the fill of a symbol
* @function swu.symbolFlop
* @param {string} swuSym - an SWU symbol with optional coordinate and style string
* @param {boolean} [positive=true] - increase the symbol fill
* @returns {string} SWU symbol with changed fill
* @example
* swu.symbolFlop('')
*
* return ''
*/
const symbolFlop = (swuSym, positive = true) => {
let parsed = parse.symbol(swuSym);
if (!parsed.symbol) {
return swuSym;
}
const size = symbolSize(parsed.symbol);
if (!size) {
return swuSym;
}
parsed.symbol = swu2key(parsed.symbol);
const step = positive ? 1 : -1;
const base = parsed.symbol.slice(0, 4);
let fill = parseInt(parsed.symbol.slice(4, 5));
let rot = parsed.symbol.slice(5, 6);
let key = '';
while (!key || !symbolSize(key2swu(key))) {
fill += step;
if (fill > 5) {
fill = 0;
}
if (fill < 0) {
fill = 5;
}
key = base + fill + rot;
}
parsed.symbol = key2swu(key);
return compose.symbol(parsed);
};
/**
* Function that changes the base of a symbol
* @function swu.symbolScroll
* @param {string} swuSym - an SWU symbol with optional coordinate and style string
* @param {boolean} [positive=true] - increase the symbol base
* @returns {string} SWU symbol with changed base
* @example
* swu.symbolScroll('')
*
* return ''
*/
const symbolScroll = (swuSym, positive = true) => {
let parsed = parse.symbol(swuSym);
if (!parsed.symbol) {
return swuSym;
}
const size = symbolSize(parsed.symbol);
if (!size) {
return swuSym;
}
parsed.symbol = swu2key(parsed.symbol);
const step = positive ? 1 : -1;
const base = parseInt(parsed.symbol.slice(1, 4), 16) + step;
const fill = parsed.symbol.slice(4, 5);
const rot = parsed.symbol.slice(5, 6);
const key = 'S' + base.toString(16) + fill + rot;
if (key.length == 6 && symbolSize(key2swu(key))) {
parsed.symbol = key;
}
parsed.symbol = key2swu(parsed.symbol);
return compose.symbol(parsed);
};
/**
* Function that creates an SVG image from an SWU sign with an optional style string
* @function swu.signSvgBody
* @param {string} swuSign - an SWU sign with optional style string
* @returns {string} body of SVG for sign
* @example
* swu.signSvgBody('M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475')
*
* return `<text font-size="0">M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475</text>
* <g transform="translate(483,510)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(501,466)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(510,500)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(476,475)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>`
*/
const signSvgBody = swuSign => {
let parsed = parse.sign(swuSign);
const blank = '';
if (parsed.spatials) {
let styling = parse$2(parsed.style);
if (styling.detailsym) {
styling.detailsym.forEach(sym => {
if (parsed.spatials[sym.index - 1]) {
parsed.spatials[sym.index - 1].detail = sym.detail;
}
});
}
let x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
let y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
let x2 = parsed.max[0];
let y2 = parsed.max[1];
let background = '';
if (styling.padding) {
x1 -= styling.padding;
y1 -= styling.padding;
x2 += styling.padding;
y2 += styling.padding;
}
if (styling.background) {
background = `\n <rect x="${x1}" y="${y1}" width="${x2 - x1}" height="${y2 - y1}" style="fill:${styling.background};" />`;
}
let svg = ` <text font-size="0">${swuSign}</text>${background}`;
const line = styling.detail && styling.detail[0];
const fill = styling.detail && styling.detail[1];
svg += '\n' + parsed.spatials.map(spatial => {
let svg = symbolText(spatial.symbol);
let symLine = line;
if (spatial.detail) {
symLine = spatial.detail[0];
} else if (styling.colorize) {
symLine = colorize(spatial.symbol);
}
if (symLine) {
svg = svg.replace(/class="sym-line" fill="black"/, `class="sym-line" fill="${symLine}"`);
}
let symFill = fill;
if (spatial.detail && spatial.detail[1]) {
symFill = spatial.detail[1];
}
if (symFill) {
svg = svg.replace(/class="sym-fill" fill="white"/, `class="sym-fill" fill="${symFill}"`);
}
return ` <g transform="translate(${spatial.coord[0]},${spatial.coord[1]})">
${svg}
</g>`;
}).join('\n');
return svg;
}
return blank;
};
/**
* Function that creates an SVG image from an SWU sign with an optional style string
* @function swu.signSvg
* @param {string} swuSign - an SWU sign with optional style string
* @returns {string} SVG for sign
* @example
* swu.signSvg('M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475')
*
* return `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="49" height="69" viewBox="476 466 49 69">
* <text font-size="0">M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475</text>
* <g transform="translate(483,510)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(501,466)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(510,500)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(476,475)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* </svg>`
*/
const signSvg = swuSign => {
let parsed = parse.sign(swuSign);
const blank = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="1" height="1"></svg>';
if (parsed.spatials) {
let styling = parse$2(parsed.style);
let x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
let y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
let x2 = parsed.max[0];
let y2 = parsed.max[1];
let classes = '';
if (styling.classes) {
classes = ` class="${styling.classes}"`;
}
let id = '';
if (styling.id) {
id = ` id="${styling.id}"`;
}
if (styling.padding) {
x1 -= styling.padding;
y1 -= styling.padding;
x2 += styling.padding;
y2 += styling.padding;
}
let sizing = '';
if (styling.zoom != 'x') {
sizing = ` width="${(x2 - x1) * (styling.zoom ? styling.zoom : 1)}" height="${(y2 - y1) * (styling.zoom ? styling.zoom : 1)}"`;
}
let svg = `<svg${classes}${id} version="1.1" xmlns="http://www.w3.org/2000/svg"${sizing} viewBox="${x1} ${y1} ${x2 - x1} ${y2 - y1}">
`;
svg += signSvgBody(swuSign);
svg += '\n</svg>';
return svg;
}
return blank;
};
const signCanvas = function (swuSign) {
const parsed = parse.sign(swuSign);
if (parsed.spatials) {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
let styling = parse$2(parsed.style);
if (styling.detailsym) {
styling.detailsym.forEach(sym => {
if (parsed.spatials[sym.index - 1]) {
parsed.spatials[sym.index - 1].detail = sym.detail;
}
});
}
let x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
let y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
let x2 = parsed.max[0];
let y2 = parsed.max[1];
if (styling.padding) {
x1 -= styling.padding;
y1 -= styling.padding;
x2 += styling.padding;
y2 += styling.padding;
}
let sizing = 1;
if (styling.zoom != 'x') {
sizing = styling.zoom;
}
let w = (x2 - x1) * sizing;
let h = (y2 - y1) * sizing;
canvas.width = w ? w : 1;
canvas.height = h ? h : 1;
if (styling.background) {
context.rect(0, 0, w, h);
context.fillStyle = styling.background;
context.fill();
}
const line = styling.detail && styling.detail[0] || "black";
const fill = styling.detail && styling.detail[1] || "white";
parsed.spatials.forEach(spatial => {
let symLine = line;
if (spatial.detail) {
symLine = spatial.detail[0];
} else if (styling.colorize) {
symLine = colorize(spatial.symbol);
}
let symFill = fill;
if (spatial.detail && spatial.detail[1]) {
symFill = spatial.detail[1];
}
context.font = 30 * sizing + "px 'SuttonSignWritingFill'";
context.fillStyle = symFill;
context.fillText(symbolFill(spatial.symbol), (spatial.coord[0] - x1) * sizing, (spatial.coord[1] - y1) * sizing);
context.font = 30 * sizing + "px 'SuttonSignWritingLine'";
context.fillStyle = symLine;
context.fillText(symbolLine(spatial.symbol), (spatial.coord[0] - x1) * sizing, (spatial.coord[1] - y1) * sizing);
});
return canvas;
}
};
/**
* Function that creates a PNG data url from an SWU sign with an optional style string
* @function swu.signPng
* @param {string} swuSign - an SWU sign with optional style string
* @returns {string} png image for sign as data url
* @example
* swu.signPng('𝠀𝠃𝤟𝤩𝣵𝤐𝤇𝣤𝤐𝤆𝣮𝣭')
*
* return 'data:image/png;base64,iVBORw...'
*/
const signPng = swuSign => {
const canvas = signCanvas(swuSign);
const png = canvas.toDataURL("image/png");
canvas.remove();
return png;
};
/**
* Function that normalizes an SWU sign for a center of 500,500
* @function swu.signNormalize
* @param {string} swuSign - an SWU sign with optional style string
* @returns {string} normalized SWU sign
* @example
* swu.signNormalize('𝠀𝠃𝤟𝤩𝣵𝤐𝤇𝣤𝤐𝤆𝣮𝣭')
*
* return '𝠀𝠃𝤟𝤩𝣵𝤐𝤇𝣤𝤐𝤆𝣮𝣭'
*/
const signNormalize = swuSign => {
const parsed = parse.sign(swuSign);
if (parsed.spatials) {
const symbolsizes = parsed.spatials.reduce((output, spatial) => {
const size = symbolSize(spatial.symbol);
output[spatial.symbol] = {
width: size[0],
height: size[1]
};
return output;
}, {});
const bbox = symbols => {
const x1 = Math.min(...symbols.map(spatial => spatial.coord[0]));
const y1 = Math.min(...symbols.map(spatial => spatial.coord[1]));
const x2 = Math.max(...symbols.map(spatial => spatial.coord[0] + parseInt(symbolsizes[spatial.symbol].width)));
const y2 = Math.max(...symbols.map(spatial => spatial.coord[1] + parseInt(symbolsizes[spatial.symbol].height)));
return {
x1: x1,
y1: y1,
x2: x2,
y2: y2
};
};
const hrange = ranges['hcenter'];
const hsyms = parsed.spatials.filter(spatial => {
const dec = parseInt(spatial.symbol.slice(1, 4), 16);
return hrange[0] <= dec && hrange[1] >= dec;
});
const vrange = ranges['vcenter'];
const vsyms = parsed.spatials.filter(spatial => {
const dec = parseInt(spatial.symbol.slice(1, 4), 16);
return vrange[0] <= dec && vrange[1] >= dec;
});
let abox = bbox(parsed.spatials);
let max = [abox.x2, abox.y2];
if (hsyms.length) {
const hbox = bbox(hsyms);
abox.x1 = hbox.x1;
abox.x2 = hbox.x2;
}
if (vsyms.length) {
const vbox = bbox(vsyms);
abox.y1 = vbox.y1;
abox.y2 = vbox.y2;
}
const offset = [parseInt((abox.x2 + abox.x1) / 2) - 500, parseInt((abox.y2 + abox.y1) / 2) - 500];
const swuout = (parsed.sequence ? '𝠀' + parsed.sequence.join('') : '') + parsed.box + coord2swu$1([max[0] - offset[0], max[1] - offset[1]]) + parsed.spatials.map(spatial => spatial.symbol + coord2swu$1([spatial.coord[0] - offset[0], spatial.coord[1] - offset[1]])).join('') + (parsed.style || '');
return swuout;
}
};
/**
* Function that creates an SVG image for a column of SWU
* @function swu.columnSvg
* @param {ColumnData} swuColumn - an array of objects with information about FSW signs and punctuation
* @param {ColumnOptions} options - an object of column options
* @returns {string} column svg
* @example
* swu.columnSvg([
* {
* "x": 56,
* "y": 20,
* "minX": 481,
* "minY": 471,
* "width": 37,
* "height": 58,
* "lane": 0,
* "padding": 0,
* "segment": "sign",
* "text": "𝠀𝠃𝤘𝤣𝣳𝣩𝤉𝣻",
* "zoom": 1
* },
* {
* "x": 57,
* "y": 118,
* "minX": 482,
* "minY": 468,
* "width": 36,
* "height": 65,
* "lane": 0,
* "padding": 0,
* "segment": "sign",
* "text": "𝠀𝠃𝤘𝤧𝣻𝤕𝣴𝣼𝤎𝤂𝤆𝣦",
* "zoom": 1
* },
* {
* "x": 39,
* "y": 203,
* "minX": 464,
* "minY": 496,
* "width": 72,
* "height": 8,
* "lane": 0,
* "padding": 0,
* "segment": "symbol",
* "text": "𝣢𝤂",
* "zoom": 1
* }
* ],
* {
* "height": 250,
* "width": 150,
* })
*
* return `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="150" height="250" viewBox="0 0 150 250">
* <g transform="translate(56,20) scale(1) translate(-481,-471) ">
* <text font-size="0">𝠀𝠃𝤘𝤣𝣳𝣩𝤉𝣻-D_black,white_Z1</text>
* <g transform="translate(481,471)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(503,489)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* </g>
* <g transform="translate(57,118) scale(1) translate(-482,-468) ">
* <text font-size="0">𝠀𝠃𝤘𝤧𝣻𝤕𝣴𝣼𝤎𝤂𝤆𝣦-D_black,white_Z1</text>
* <g transform="translate(489,515)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(482,490)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(508,496)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(500,468)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* </g>
* <g transform="translate(39,203) scale(1) translate(-464,-496) ">
* <text font-size="0">𝣢𝤂-D_black,white_Z1</text>
* <g transform="translate(464,496)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* </g>
* </svg>`
*/
const columnSvg = (swuColumn, options) => {
//if (typeof swuColumn !== 'array') return blank;
if (typeof options !== 'object') options = {};
const values = Object.assign(columnDefaults, options);
let x1 = 0;
let y1 = 0;
let x2 = values.width;
let y2 = values.height;
let background = '';
if (values.background) {
background = `\n <rect x="${x1}" y="${y1}" width="${x2 - x1}" height="${y2 - y1}" style="fill:${values.background};" />`;
}
let sizing = ` width="${values.width}" height="${values.height}"`;
let svg = `<svg version="1.1" xmlns="http://www.w3.org/2000/svg"${sizing} viewBox="${x1} ${y1} ${x2 - x1} ${y2 - y1}">
<text font-size="0">${x1}</text>${background}`;
svg += swuColumn.map(item => {
const dash = item.text.indexOf('-');
if (dash > 0) {
const itemStyle = item.text.substring(dash);
const newStyle = {
...values.style,
...parse$2(itemStyle)
};
item.text = item.text.replace(itemStyle, compose$1(newStyle));
} else {
item.text += compose$1(values.style);
}
item.zoom = item.zoom * values.style.zoom;
return '<g transform="translate(' + item.x + ',' + item.y + ') scale(' + item.zoom + ') translate(' + -item.minX + ',' + -item.minY + ') ">' + (item.segment == "sign" ? signSvgBody(item.text) : symbolSvgBody(item.text)) + '</g>';
}).join('\n');
svg += '\n</svg>';
return svg;
};
/**
* Function that creates an array of SVG column images for an SWU text
* @function swu.columnsSvg
* @param {string} swuText - a text of SWU signs and punctuation
* @param {ColumnOptions} options - an object of column options
* @returns {string[]} array of SVG columns
* @example
* swu.columnsSvg('𝠀𝠃𝤘𝤣𝣳𝣩𝤉𝣻 𝠀𝠃𝤘𝤧𝣻𝤕𝣴𝣼𝤎𝤂𝤆𝣦 𝣢𝤂',{
* "height": 250,
* "width": 150,
* })
*
* return [`<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="150" height="250" viewBox="0 0 150 250">
* <g transform="translate(56,20) scale(1) translate(-481,-471) ">
* <text font-size="0">𝠀𝠃𝤘𝤣𝣳𝣩𝤉𝣻-D_black,white_Z1</text>
* <g transform="translate(481,471)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(503,489)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* </g>
* <g transform="translate(57,118) scale(1) translate(-482,-468) ">
* <text font-size="0">𝠀𝠃𝤘𝤧𝣻𝤕𝣴𝣼𝤎𝤂𝤆𝣦-D_black,white_Z1</text>
* <g transform="translate(489,515)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(482,490)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(508,496)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* <g transform="translate(500,468)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* </g>
* <g transform="translate(39,203) scale(1) translate(-464,-496) ">
* <text font-size="0">𝣢𝤂-D_black,white_Z1</text>
* <g transform="translate(464,496)">
* <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;"></text>
* <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;"></text>
* </g>
* </g>
* </svg>`]
*/
const columnsSvg = function (swuText, options) {
if (typeof options !== 'object') options = {};
let values = columns(swuText, options);
let cols = values.columns.map((col, i) => {
return columnSvg(col, {
...values.options,
...{
width: values.widths[i]
}
});
});
return cols;
};
const columnCanvas = function (swuColumn, options) {
if (typeof options !== 'object') options = {};
const values = Object.assign(columnDefaults, options);
const canvas = document.createElement('canvas');
canvas.width = values.width;
canvas.height = values.height;
const context = canvas.getContext('2d');
if (values.background) {
context.rect(0, 0, values.width, values.height);
context.fillStyle = values.background;
context.fill();
}
swuColumn.map(item => {
const dash = item.text.indexOf('-');
if (dash > 0) {
const itemStyle = item.text.substring(dash);
const newStyle = {
...values.style,
...parse$2(itemStyle)
};
item.text = item.text.replace(itemStyle, compose$1(newStyle));
} else {
item.text += compose$1(values.style);
}
item.zoom = item.zoom * values.style.zoom;
let parsed = {};
if (item.segment == "sign") {
parsed = parse.sign(item.text);
} else {
let sym = parse.symbol(item.text);
parsed.style = sym.style;
parsed.spatials = [sym];
}
let styling = parse$2(parsed.style);
if (styling.background) {
context.fillStyle = styling.background;
context.fillRect(item.x - styling.padding * item.zoom, item.y - styling.padding * item.zoom, (item.width + styling.padding * 2) * item.zoom, (item.height + styling.padding * 2) * item.zoom);
}
if (styling.detailsym) {
styling.detailsym.forEach(sym => {
if (parsed.spatials[sym.index - 1]) {
parsed.spatials[sym.index - 1].detail = sym.detail;
}
});
}
const line = styling.detail && styling.detail[0] || "black";
const fill = styling.detail && styling.detail[1] || "white";
parsed.spatials.forEach(spatial => {
let symLine = line;
if (spatial.detail) {
symLine = spatial.detail[0];
} else if (styling.colorize) {
symLine = colorize(spatial.symbol);
}
let symFill = fill;
if (spatial.detail && spatial.detail[1]) {
symFill = spatial.detail[1];
}
context.font = 30 * item.zoom + "px 'SuttonSignWritingFill'";
context.fillStyle = symFill;
context.fillText(symbolFill(spatial.symbol), item.x + (spatial.coord[0] - item.minX) * item.zoom, item.y + (spatial.coord[1] - item.minY) * item.zoom);
context.font = 30 * item.zoom + "px 'SuttonSignWritingLine'";
context.fillStyle = symLine;
context.fillText(symbolLine(spatial.symbol), item.x + (spatial.coord[0] - item.minX) * item.zoom, item.y + (spatial.coord[1] - item.minY) * item.zoom);
});
});
return canvas;
};
/**
* Function that creates a PNG data url for a column of SWU
* @function swu.columnPng
* @param {ColumnData} swuColumn - an array of SWU signs and punctuation with coordinates
* @param {ColumnOptions} options - an object of column options
* @returns {string} column png data url
* @example
* swu.columnPng([
* {
* "x": 56,
* "y": 20,
* "minX": 481,
* "minY": 471,
* "width": 37,
* "height": 58,
* "lane": 0,
* "padding": 0,
* "segment": "sign",
* "text": "𝠀𝠃𝤘𝤣𝣳𝣩𝤉𝣻",
* "zoom": 1
* },
* {
* "x": 57,
* "y": 118,
* "minX": 482,
* "minY": 468,
* "width": 36,
* "height": 65,
* "lane": 0,
* "padding": 0,
* "segment": "sign",
* "text": "𝠀𝠃𝤘𝤧𝣻𝤕𝣴𝣼𝤎𝤂𝤆𝣦",
* "zoom": 1
* },
* {
* "x": 39,
* "y": 203,
* "minX": 464,
* "minY": 496,
* "width": 72,
* "height": 8,
* "lane": 0,
* "padding": 0,
* "segment": "symbol",
* "text": "𝣢𝤂",
* "zoom": 1
* }
* ],
* {
* "height": 250,
* "width": 150,
* })
*
* return 'data:image/png;base64,iVBORw...'
*/
const columnPng = (swuColumn, options) => {
const canvas = columnCanvas(swuColumn, options);
const png = canvas.toDataURL("image/png");
canvas.remove();
return png;
};
/**
* Function that creates an SVG image for a column of SWU
* @function swu.columnsPng
* @param {string} swuText - an array of SWU signs and punctuation with coordinates
* @param {ColumnOptions} options - an object of column options
* @returns {string[]} array of PNG data urls
* @example
* swu.columnsPng('𝠀𝠃𝤘𝤣𝣳𝣩𝤉𝣻 𝠀𝠃𝤘𝤧𝣻𝤕𝣴𝣼𝤎𝤂𝤆𝣦 𝣢𝤂',{
* "height": 250,
* "width": 150,
* })
*
* return ['data:image/png;base64,iVBORw...']
*/
const columnsPng = function (swuText, options) {
if (typeof options !== 'object') options = {};
let values = columns(swuText, options);
let cols = values.columns.map((col, i) => {
return columnPng(col, {
...values.options,
...{
width: values.widths[i]
}
});
});
return cols;
};
/** The swu module contains functions for handling SignWriting in Unicode (SWU) characters.
* [SWU characters definition](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-09.html#name-signwriting-in-unicode-swu)
* @module swu
*/
var index = /*#__PURE__*/Object.freeze({
__proto__: null,
symbolSize: symbolSize,
symbolLine: symbolLine,
symbolFill: symbolFill,
symbolText: symbolText,
symbolSvgBody: symbolSvgBody,
symbolSvg: symbolSvg,
symbolPng: symbolPng,
symbolNormalize: symbolNormalize,
symbolMirror: symbolMirror,
symbolInvert: symbolInvert,
symbolRotate: symbolRotate,
symbolFlop: symbolFlop,
symbolScroll: symbolScroll,
signSvgBody: signSvgBody,
signSvg: signSvg,
signPng: signPng,
signNormalize: signNormalize,
columnSvg: columnSvg,
columnsSvg: columnsSvg,
columnPng: columnPng,
columnsPng: columnsPng
});
export { index$2 as font, index$1 as fsw, index as swu };
/* support ongoing development on https://patreon.com/signwriting */