pdfmake
Version:
Client/server side PDF printing in pure JavaScript
931 lines (924 loc) • 125 kB
JavaScript
var SVGtoPDF = function(doc, svg, x, y, options) {
"use strict";
const NamedColors = {aliceblue: [240,248,255], antiquewhite: [250,235,215], aqua: [0,255,255], aquamarine: [127,255,212], azure: [240,255,255], beige: [245,245,220], bisque: [255,228,196], black: [0,0,0], blanchedalmond: [255,235,205], blue: [0,0,255], blueviolet: [138,43,226], brown: [165,42,42], burlywood: [222,184,135], cadetblue: [95,158,160], chartreuse: [127,255,0],
chocolate: [210,105,30], coral: [255,127,80], cornflowerblue: [100,149,237], cornsilk: [255,248,220], crimson: [220,20,60], cyan: [0,255,255], darkblue: [0,0,139], darkcyan: [0,139,139], darkgoldenrod: [184,134,11], darkgray: [169,169,169], darkgrey: [169,169,169], darkgreen: [0,100,0], darkkhaki: [189,183,107], darkmagenta: [139,0,139], darkolivegreen: [85,107,47],
darkorange: [255,140,0], darkorchid: [153,50,204], darkred: [139,0,0], darksalmon: [233,150,122], darkseagreen: [143,188,143], darkslateblue: [72,61,139], darkslategray: [47,79,79], darkslategrey: [47,79,79], darkturquoise: [0,206,209], darkviolet: [148,0,211], deeppink: [255,20,147], deepskyblue: [0,191,255], dimgray: [105,105,105], dimgrey: [105,105,105],
dodgerblue: [30,144,255], firebrick: [178,34,34], floralwhite: [255,250,240], forestgreen: [34,139,34], fuchsia: [255,0,255], gainsboro: [220,220,220], ghostwhite: [248,248,255], gold: [255,215,0], goldenrod: [218,165,32], gray: [128,128,128], grey: [128,128,128], green: [0,128,0], greenyellow: [173,255,47], honeydew: [240,255,240], hotpink: [255,105,180],
indianred: [205,92,92], indigo: [75,0,130], ivory: [255,255,240], khaki: [240,230,140], lavender: [230,230,250], lavenderblush: [255,240,245], lawngreen: [124,252,0], lemonchiffon: [255,250,205], lightblue: [173,216,230], lightcoral: [240,128,128], lightcyan: [224,255,255], lightgoldenrodyellow: [250,250,210], lightgray: [211,211,211], lightgrey: [211,211,211],
lightgreen: [144,238,144], lightpink: [255,182,193], lightsalmon: [255,160,122], lightseagreen: [32,178,170], lightskyblue: [135,206,250], lightslategray: [119,136,153], lightslategrey: [119,136,153], lightsteelblue: [176,196,222], lightyellow: [255,255,224], lime: [0,255,0], limegreen: [50,205,50], linen: [250,240,230], magenta: [255,0,255], maroon: [128,0,0],
mediumaquamarine: [102,205,170], mediumblue: [0,0,205], mediumorchid: [186,85,211], mediumpurple: [147,112,219], mediumseagreen: [60,179,113], mediumslateblue: [123,104,238], mediumspringgreen: [0,250,154], mediumturquoise: [72,209,204], mediumvioletred: [199,21,133], midnightblue: [25,25,112], mintcream: [245,255,250], mistyrose: [255,228,225], moccasin: [255,228,181],
navajowhite: [255,222,173], navy: [0,0,128], oldlace: [253,245,230], olive: [128,128,0], olivedrab: [107,142,35], orange: [255,165,0], orangered: [255,69,0], orchid: [218,112,214], palegoldenrod: [238,232,170], palegreen: [152,251,152], paleturquoise: [175,238,238], palevioletred: [219,112,147], papayawhip: [255,239,213], peachpuff: [255,218,185], peru: [205,133,63],
pink: [255,192,203], plum: [221,160,221], powderblue: [176,224,230], purple: [128,0,128], rebeccapurple: [102,51,153], red: [255,0,0], rosybrown: [188,143,143], royalblue: [65,105,225], saddlebrown: [139,69,19], salmon: [250,128,114], sandybrown: [244,164,96], seagreen: [46,139,87], seashell: [255,245,238], sienna: [160,82,45], silver: [192,192,192], skyblue: [135,206,235],
slateblue: [106,90,205], slategray: [112,128,144], slategrey: [112,128,144], snow: [255,250,250], springgreen: [0,255,127], steelblue: [70,130,180], tan: [210,180,140], teal: [0,128,128], thistle: [216,191,216], tomato: [255,99,71], turquoise: [64,224,208], violet: [238,130,238], wheat: [245,222,179], white: [255,255,255], whitesmoke: [245,245,245], yellow: [255,255,0]};
const DefaultColors = {black: [NamedColors.black, 1], white: [NamedColors.white, 1], transparent: [NamedColors.black, 0]};
const Entities = {quot: 34, amp: 38, lt: 60, gt: 62, apos: 39, OElig: 338, oelig: 339, Scaron: 352, scaron: 353, Yuml: 376, circ: 710, tilde: 732, ensp: 8194, emsp: 8195, thinsp: 8201, zwnj: 8204, zwj: 8205, lrm: 8206, rlm: 8207, ndash: 8211, mdash: 8212, lsquo: 8216, rsquo: 8217, sbquo: 8218, ldquo: 8220, rdquo: 8221, bdquo: 8222, dagger: 8224, Dagger: 8225, permil: 8240, lsaquo: 8249,
rsaquo: 8250, euro: 8364, nbsp: 160, iexcl: 161, cent: 162, pound: 163, curren: 164, yen: 165, brvbar: 166, sect: 167, uml: 168, copy: 169, ordf: 170, laquo: 171, not: 172, shy: 173, reg: 174, macr: 175, deg: 176, plusmn: 177, sup2: 178, sup3: 179, acute: 180, micro: 181, para: 182, middot: 183, cedil: 184, sup1: 185, ordm: 186, raquo: 187, frac14: 188, frac12: 189, frac34: 190,
iquest: 191, Agrave: 192, Aacute: 193, Acirc: 194, Atilde: 195, Auml: 196, Aring: 197, AElig: 198, Ccedil: 199, Egrave: 200, Eacute: 201, Ecirc: 202, Euml: 203, Igrave: 204, Iacute: 205, Icirc: 206, Iuml: 207, ETH: 208, Ntilde: 209, Ograve: 210, Oacute: 211, Ocirc: 212, Otilde: 213, Ouml: 214, times: 215, Oslash: 216, Ugrave: 217, Uacute: 218, Ucirc: 219, Uuml: 220, Yacute: 221,
THORN: 222, szlig: 223, agrave: 224, aacute: 225, acirc: 226, atilde: 227, auml: 228, aring: 229, aelig: 230, ccedil: 231, egrave: 232, eacute: 233, ecirc: 234, euml: 235, igrave: 236, iacute: 237, icirc: 238, iuml: 239, eth: 240, ntilde: 241, ograve: 242, oacute: 243, ocirc: 244, otilde: 245, ouml: 246, divide: 247, oslash: 248, ugrave: 249, uacute: 250, ucirc: 251, uuml: 252,
yacute: 253, thorn: 254, yuml: 255, fnof: 402, Alpha: 913, Beta: 914, Gamma: 915, Delta: 916, Epsilon: 917, Zeta: 918, Eta: 919, Theta: 920, Iota: 921, Kappa: 922, Lambda: 923, Mu: 924, Nu: 925, Xi: 926, Omicron: 927, Pi: 928, Rho: 929, Sigma: 931, Tau: 932, Upsilon: 933, Phi: 934, Chi: 935, Psi: 936, Omega: 937, alpha: 945, beta: 946, gamma: 947, delta: 948, epsilon: 949,
zeta: 950, eta: 951, theta: 952, iota: 953, kappa: 954, lambda: 955, mu: 956, nu: 957, xi: 958, omicron: 959, pi: 960, rho: 961, sigmaf: 962, sigma: 963, tau: 964, upsilon: 965, phi: 966, chi: 967, psi: 968, omega: 969, thetasym: 977, upsih: 978, piv: 982, bull: 8226, hellip: 8230, prime: 8242, Prime: 8243, oline: 8254, frasl: 8260, weierp: 8472, image: 8465, real: 8476,
trade: 8482, alefsym: 8501, larr: 8592, uarr: 8593, rarr: 8594, darr: 8595, harr: 8596, crarr: 8629, lArr: 8656, uArr: 8657, rArr: 8658, dArr: 8659, hArr: 8660, forall: 8704, part: 8706, exist: 8707, empty: 8709, nabla: 8711, isin: 8712, notin: 8713, ni: 8715, prod: 8719, sum: 8721, minus: 8722, lowast: 8727, radic: 8730, prop: 8733, infin: 8734, ang: 8736, and: 8743, or: 8744,
cap: 8745, cup: 8746, int: 8747, there4: 8756, sim: 8764, cong: 8773, asymp: 8776, ne: 8800, equiv: 8801, le: 8804, ge: 8805, sub: 8834, sup: 8835, nsub: 8836, sube: 8838, supe: 8839, oplus: 8853, otimes: 8855, perp: 8869, sdot: 8901, lceil: 8968, rceil: 8969, lfloor: 8970, rfloor: 8971, lang: 9001, rang: 9002, loz: 9674, spades: 9824, clubs: 9827, hearts: 9829, diams: 9830};
const PathArguments = {A: 7, a: 7, C: 6, c: 6, H: 1, h: 1, L: 2, l: 2, M: 2, m: 2, Q: 4, q: 4, S: 4, s: 4, T: 2, t: 2, V: 1, v: 1, Z: 0, z: 0};
const PathFlags = {A3: true, A4: true, a3: true, a4: true};
const Properties = {
'color': {inherit: true, initial: undefined},
'visibility': {inherit: true, initial: 'visible', values: {'hidden': 'hidden', 'collapse': 'hidden', 'visible':'visible'}},
'fill': {inherit: true, initial: DefaultColors.black},
'stroke': {inherit: true, initial: 'none'},
'stop-color': {inherit: false, initial: DefaultColors.black},
'fill-opacity': {inherit: true, initial: 1},
'stroke-opacity': {inherit: true, initial: 1},
'stop-opacity': {inherit: false, initial: 1},
'fill-rule': {inherit: true, initial: 'nonzero', values: {'nonzero':'nonzero', 'evenodd':'evenodd'}},
'clip-rule': {inherit: true, initial: 'nonzero', values: {'nonzero':'nonzero', 'evenodd':'evenodd'}},
'stroke-width': {inherit: true, initial: 1},
'stroke-dasharray': {inherit: true, initial: []},
'stroke-dashoffset': {inherit: true, initial: 0},
'stroke-miterlimit': {inherit: true, initial: 4},
'stroke-linejoin': {inherit: true, initial: 'miter', values: {'miter':'miter', 'round':'round', 'bevel':'bevel'}},
'stroke-linecap': {inherit: true, initial: 'butt', values: {'butt':'butt', 'round':'round', 'square':'square'}},
'font-size': {inherit: true, initial: 16, values: {'xx-small':9, 'x-small':10, 'small':13, 'medium':16, 'large':18, 'x-large':24, 'xx-large':32}},
'font-family': {inherit: true, initial: 'sans-serif'},
'font-weight': {inherit: true, initial: 'normal', values: {'600':'bold', '700':'bold', '800':'bold', '900':'bold', 'bold':'bold', 'bolder':'bold', '500':'normal', '400':'normal', '300':'normal', '200':'normal', '100':'normal', 'normal':'normal', 'lighter':'normal'}},
'font-style': {inherit: true, initial: 'normal', values: {'italic':'italic', 'oblique':'italic', 'normal':'normal'}},
'text-anchor': {inherit: true, initial: 'start', values: {'start':'start', 'middle':'middle', 'end':'end'}},
'direction': {inherit: true, initial: 'ltr', values: {'ltr':'ltr', 'rtl':'rtl'}},
'dominant-baseline': {inherit: true, initial: 'baseline', values: {'auto':'baseline', 'baseline':'baseline', 'before-edge':'before-edge', 'text-before-edge':'before-edge', 'middle':'middle', 'central':'central', 'after-edge':'after-edge', 'text-after-edge':'after-edge', 'ideographic':'ideographic', 'alphabetic':'alphabetic', 'hanging':'hanging', 'mathematical':'mathematical'}},
'alignment-baseline': {inherit: false, initial: undefined, values: {'auto':'baseline', 'baseline':'baseline', 'before-edge':'before-edge', 'text-before-edge':'before-edge', 'middle':'middle', 'central':'central', 'after-edge':'after-edge', 'text-after-edge':'after-edge', 'ideographic':'ideographic', 'alphabetic':'alphabetic', 'hanging':'hanging', 'mathematical':'mathematical'}},
'baseline-shift': {inherit: true, initial: 'baseline', values: {'baseline':'baseline', 'sub':'sub', 'super':'super'}},
'word-spacing': {inherit: true, initial: 0, values: {normal:0}},
'letter-spacing': {inherit: true, initial: 0, values: {normal:0}},
'text-decoration': {inherit: false, initial: 'none', values: {'none':'none', 'underline':'underline', 'overline':'overline', 'line-through':'line-through'}},
'xml:space': {inherit: true, initial: 'default', css: 'white-space', values: {'preserve':'preserve', 'default':'default', 'pre':'preserve', 'pre-line':'preserve', 'pre-wrap':'preserve', 'nowrap': 'default'}},
'marker-start': {inherit: true, initial: 'none'},
'marker-mid': {inherit: true, initial: 'none'},
'marker-end': {inherit: true, initial: 'none'},
'opacity': {inherit: false, initial: 1},
'transform': {inherit: false, initial: [1, 0, 0, 1, 0, 0]},
'display': {inherit: false, initial: 'inline', values: {'none':'none', 'inline':'inline', 'block':'inline'}},
'clip-path': {inherit: false, initial: 'none'},
'mask': {inherit: false, initial: 'none'},
'overflow': {inherit: false, initial: 'hidden', values: {'hidden':'hidden', 'scroll':'hidden', 'visible':'visible'}}
};
function docBeginGroup(bbox) {
let group = new (function PDFGroup() {})();
group.name = 'G' + (doc._groupCount = (doc._groupCount || 0) + 1);
group.resources = doc.ref();
group.xobj = doc.ref({
Type: 'XObject',
Subtype: 'Form',
FormType: 1,
BBox: bbox,
Group: {S: 'Transparency', CS: 'DeviceRGB', I: true, K: false},
Resources: group.resources
});
group.xobj.write('');
group.savedMatrix = doc._ctm;
group.savedPage = doc.page;
groupStack.push(group);
doc._ctm = [1, 0, 0, 1, 0, 0];
doc.page = {
width: doc.page.width, height: doc.page.height,
write: function(data) {group.xobj.write(data);},
fonts: {}, xobjects: {}, ext_gstates: {}, patterns: {}
};
return group;
}
function docEndGroup(group) {
if (group !== groupStack.pop()) {throw('Group not matching');}
if (Object.keys(doc.page.fonts).length) {group.resources.data.Font = doc.page.fonts;}
if (Object.keys(doc.page.xobjects).length) {group.resources.data.XObject = doc.page.xobjects;}
if (Object.keys(doc.page.ext_gstates).length) {group.resources.data.ExtGState = doc.page.ext_gstates;}
if (Object.keys(doc.page.patterns).length) {group.resources.data.Pattern = doc.page.patterns;}
group.resources.end();
group.xobj.end();
doc._ctm = group.savedMatrix;
doc.page = group.savedPage;
}
function docInsertGroup(group) {
doc.page.xobjects[group.name] = group.xobj;
doc.addContent('/' + group.name + ' Do');
}
function docApplyMask(group, clip) {
let name = 'M' + (doc._maskCount = (doc._maskCount || 0) + 1);
let gstate = doc.ref({
Type: 'ExtGState', CA: 1, ca: 1, BM: 'Normal',
SMask: {S: 'Luminosity', G: group.xobj, BC: (clip ? [0, 0, 0] : [1, 1, 1])}
});
gstate.end();
doc.page.ext_gstates[name] = gstate;
doc.addContent('/' + name + ' gs');
}
function docCreatePattern(group, dx, dy, matrix) {
let pattern = new (function PDFPattern() {})();
pattern.group = group;
pattern.dx = dx;
pattern.dy = dy;
pattern.matrix = matrix || [1, 0, 0, 1, 0, 0];
return pattern;
}
function docUsePattern(pattern, stroke) {
let name = 'P' + (doc._patternCount = (doc._patternCount || 0) + 1);
let ref = doc.ref({
Type: 'Pattern', PatternType: 1, PaintType: 1, TilingType: 2,
BBox: [0, 0, pattern.dx, pattern.dy], XStep: pattern.dx, YStep: pattern.dy,
Matrix: multiplyMatrix(doc._ctm, pattern.matrix),
Resources: {
ProcSet: ['PDF', 'Text', 'ImageB', 'ImageC', 'ImageI'],
XObject: (function() {let temp = {}; temp[pattern.group.name] = pattern.group.xobj; return temp;})()
}
});
ref.write('/' + pattern.group.name + ' Do');
ref.end();
doc.page.patterns[name] = ref;
if (stroke) {
doc.addContent('/Pattern CS');
doc.addContent('/' + name + ' SCN');
} else {
doc.addContent('/Pattern cs');
doc.addContent('/' + name + ' scn');
}
}
function docBeginText(font, size) {
if (!doc.page.fonts[font.id]) {doc.page.fonts[font.id] = font.ref();}
doc.addContent('BT').addContent('/' + font.id + ' ' + size + ' Tf');
}
function docSetTextMatrix(a, b, c, d, e, f) {
doc.addContent(validateNumber(a) + ' ' + validateNumber(b) + ' ' + validateNumber(-c) + ' ' + validateNumber(-d) + ' ' + validateNumber(e) + ' ' + validateNumber(f) + ' Tm');
}
function docSetTextMode(fill, stroke) {
let mode = fill && stroke ? 2 : stroke ? 1 : fill ? 0 : 3;
doc.addContent(mode + ' Tr');
}
function docWriteGlyph(glyph) {
doc.addContent('<' + glyph + '> Tj');
}
function docEndText() {
doc.addContent('ET');
}
function docFillColor(color) {
if (color[0].constructor.name === 'PDFPattern') {
doc.fillOpacity(color[1]);
docUsePattern(color[0], false);
} else {
doc.fillColor(color[0], color[1]);
}
}
function docStrokeColor(color) {
if (color[0].constructor.name === 'PDFPattern') {
doc.strokeOpacity(color[1]);
docUsePattern(color[0], true);
} else {
doc.strokeColor(color[0], color[1]);
}
}
function docInsertLink(x, y, w, h, url) {
let ref = doc.ref({
Type: 'Annot',
Subtype: 'Link',
Rect: [x, y, w, h],
Border: [0, 0, 0],
A: {
S: 'URI',
URI: new String(url)
}
});
ref.end();
links.push(ref);
}
function parseXml(xml) {
let SvgNode = function(tag, type, value, error) {
this.error = error;
this.nodeName = tag;
this.nodeValue = value;
this.nodeType = type;
this.attributes = Object.create(null);
this.childNodes = [];
this.parentNode = null;
this.id = '';
this.textContent = '';
this.classList = [];
};
SvgNode.prototype.getAttribute = function(attr) {
return this.attributes[attr] != null ? this.attributes[attr] : null;
};
SvgNode.prototype.getElementById = function(id) {
let result = null;
(function recursive(node) {
if (result) {return;}
if (node.nodeType === 1) {
if (node.id === id) {result = node;}
for (let i = 0; i < node.childNodes.length; i++) {
recursive(node.childNodes[i]);
}
}
})(this);
return result;
};
SvgNode.prototype.getElementsByTagName = function(tag) {
let result = [];
(function recursive(node) {
if (node.nodeType === 1) {
if (node.nodeName === tag) {result.push(node);}
for (let i = 0; i < node.childNodes.length; i++) {
recursive(node.childNodes[i]);
}
}
})(this);
return result;
};
let parser = new StringParser(xml.trim()), result, child, error = false;
let recursive = function() {
let temp, child;
if (temp = parser.match(/^<([\w:.-]+)\s*/, true)) { // Opening tag
let node = new SvgNode(temp[1], 1, null, error);
while (temp = parser.match(/^([\w:.-]+)(?:\s*=\s*"([^"]*)"|\s*=\s*'([^']*)')?\s*/, true)) { // Attribute
let attr = temp[1], value = decodeEntities(temp[2] || temp[3] || '');
if (!node.attributes[attr]) {
node.attributes[attr] = value;
if (attr === 'id') {node.id = value;}
if (attr === 'class') {node.classList = value.split(' ');}
} else {
warningCallback('parseXml: duplicate attribute "' + attr + '"');
error = true;
}
}
if (parser.match(/^>/)) { // End of opening tag
while (child = recursive()) {
node.childNodes.push(child);
child.parentNode = node;
node.textContent += (child.nodeType === 3 || child.nodeType === 4 ? child.nodeValue : child.textContent);
}
if (temp = parser.match(/^<\/([\w:.-]+)\s*>/, true)) { // Closing tag
if (temp[1] === node.nodeName) {
return node;
} else {
warningCallback('parseXml: tag not matching, opening "' + node.nodeName + '" & closing "' + temp[1] + '"');
error = true;
return node;
}
} else {
warningCallback('parseXml: tag not matching, opening "' + node.nodeName + '" & not closing');
error = true;
return node;
}
} else if (parser.match(/^\/>/)) { // Self-closing tag
return node;
} else {
warningCallback('parseXml: tag could not be parsed "' + node.nodeName + '"');
error = true;
}
} else if (temp = parser.match(/^<!--[\s\S]*?-->/)) { // Comment
return new SvgNode(null, 8, temp, error);
} else if (temp = parser.match(/^<\?[\s\S]*?\?>/)) { // Processing instructions
return new SvgNode(null, 7, temp, error);
} else if (temp = parser.match(/^<!DOCTYPE\s*([\s\S]*?)>/)) { // Doctype
return new SvgNode(null, 10, temp, error);
} else if (temp = parser.match(/^<!\[CDATA\[([\s\S]*?)\]\]>/, true)) { // Cdata node
return new SvgNode('#cdata-section', 4, temp[1], error);
} else if (temp = parser.match(/^([^<]+)/, true)) { // Text node
return new SvgNode('#text', 3, decodeEntities(temp[1]), error);
}
};
while (child = recursive()) {
if (child.nodeType === 1 && !result) {
result = child;
} else if (child.nodeType === 1 || (child.nodeType === 3 && child.nodeValue.trim() !== '')) {
warningCallback('parseXml: data after document end has been discarded');
}
}
if (parser.matchAll()) {
warningCallback('parseXml: parsing error');
}
return result;
};
function decodeEntities(str) {
return(str.replace(/&(?:#([0-9]+)|#[xX]([0-9A-Fa-f]+)|([0-9A-Za-z]+));/g, function(mt, m0, m1, m2) {
if (m0) {return String.fromCharCode(parseInt(m0, 10));}
else if (m1) {return String.fromCharCode(parseInt(m1, 16));}
else if (m2 && Entities[m2]) {return String.fromCharCode(Entities[m2]);}
else {return mt;}
}));
}
function parseColor(raw) {
let temp, result;
raw = (raw || '').trim();
if (temp = NamedColors[raw]) {
result = [temp.slice(), 1];
} else if (temp = raw.match(/^rgba\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9.]+)\s*\)$/i)) {
temp[1] = parseInt(temp[1]); temp[2] = parseInt(temp[2]); temp[3] = parseInt(temp[3]); temp[4] = parseFloat(temp[4]);
if (temp[1] < 256 && temp[2] < 256 && temp[3] < 256 && temp[4] <= 1) {
result = [temp.slice(1, 4), temp[4]];
}
} else if (temp = raw.match(/^rgb\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)$/i)) {
temp[1] = parseInt(temp[1]); temp[2] = parseInt(temp[2]); temp[3] = parseInt(temp[3]);
if (temp[1] < 256 && temp[2] < 256 && temp[3] < 256) {
result = [temp.slice(1, 4), 1];
}
} else if (temp = raw.match(/^rgb\(\s*([0-9.]+)%\s*,\s*([0-9.]+)%\s*,\s*([0-9.]+)%\s*\)$/i)) {
temp[1] = 2.55 * parseFloat(temp[1]); temp[2] = 2.55 * parseFloat(temp[2]); temp[3] = 2.55 * parseFloat(temp[3]);
if (temp[1] < 256 && temp[2] < 256 && temp[3] < 256) {
result = [temp.slice(1, 4), 1];
}
} else if (temp = raw.match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i)) {
result = [[parseInt(temp[1], 16), parseInt(temp[2], 16), parseInt(temp[3], 16)], 1];
} else if (temp = raw.match(/^#([0-9a-f])([0-9a-f])([0-9a-f])$/i)) {
result = [[0x11 * parseInt(temp[1], 16), 0x11 * parseInt(temp[2], 16), 0x11 * parseInt(temp[3], 16)], 1];
}
return colorCallback ? colorCallback(result, raw) : result;
}
function opacityToColor(color, opacity, isMask) {
let newColor = color[0].slice(),
newOpacity = color[1] * opacity;
if (isMask) {
for (let i = 0; i < color.length; i++) {
newColor[i] *= newOpacity;
}
return [newColor, 1];
} else {
return [newColor, newOpacity];
}
}
function multiplyMatrix() {
function multiply(a, b) {
return [ a[0]*b[0]+a[2]*b[1], a[1]*b[0]+a[3]*b[1], a[0]*b[2]+a[2]*b[3],
a[1]*b[2]+a[3]*b[3], a[0]*b[4]+a[2]*b[5]+a[4], a[1]*b[4]+a[3]*b[5]+a[5] ];
}
let result = arguments[0];
for (let i = 1; i < arguments.length; i++) {
result = multiply(result, arguments[i]);
}
return result;
}
function transformPoint(p, m) {
return [m[0] * p[0] + m[2] * p[1] + m[4], m[1] * p[0] + m[3] * p[1] + m[5]];
}
function getGlobalMatrix() {
let ctm = doc._ctm;
for (let i = groupStack.length - 1; i >= 0; i--) {
ctm = multiplyMatrix(groupStack[i].savedMatrix, ctm);
}
return ctm;
}
function getPageBBox() {
return new SvgShape().M(0, 0).L(doc.page.width, 0).L(doc.page.width, doc.page.height).L(0, doc.page.height)
.transform(inverseMatrix(getGlobalMatrix())).getBoundingBox();
}
function inverseMatrix(m) {
let dt = m[0] * m[3] - m[1] * m[2];
return [m[3] / dt, -m[1] / dt, -m[2] / dt, m[0] / dt, (m[2]*m[5] - m[3]*m[4]) / dt, (m[1]*m[4] - m[0]*m[5]) / dt];
}
function validateMatrix(m) {
let m0 = validateNumber(m[0]), m1 = validateNumber(m[1]), m2 = validateNumber(m[2]),
m3 = validateNumber(m[3]), m4 = validateNumber(m[4]), m5 = validateNumber(m[5]);
if (isNotEqual(m0 * m3 - m1 * m2, 0)) {
return [m0, m1, m2, m3, m4, m5];
}
}
function solveEquation(curve) {
let a = curve[2] || 0, b = curve[1] || 0, c = curve[0] || 0;
if (isEqual(a, 0) && isEqual(b, 0)) {
return [];
} else if (isEqual(a, 0)) {
return [(-c) / b];
} else {
let d = b * b - 4 * a * c;
if (isNotEqual(d, 0) && d > 0) {
return [(-b + Math.sqrt(d)) / (2 * a), (-b - Math.sqrt(d)) / (2 * a)];
} else if (isEqual(d, 0)) {
return [(-b) / (2 * a)];
} else {
return [];
}
}
}
function getCurveValue(t, curve) {
return (curve[0] || 0) + (curve[1] || 0) * t + (curve[2] || 0) * t * t + (curve[3] || 0) * t * t * t;
}
function isEqual(number, ref) {
return Math.abs(number - ref) < 1e-10;
}
function isNotEqual(number, ref) {
return Math.abs(number - ref) >= 1e-10;
}
function validateNumber(n) {
return n > -1e21 && n < 1e21 ? Math.round(n * 1e6) / 1e6 : 0;
}
function isArrayLike(v) {
return typeof v === 'object' && v !== null && typeof v.length === 'number';
}
function parseTranform(v) {
let parser = new StringParser((v || '').trim()), result = [1, 0, 0, 1, 0, 0], temp;
while (temp = parser.match(/^([A-Za-z]+)\s*[(]([^(]+)[)]/, true)) {
let func = temp[1], nums = [], parser2 = new StringParser(temp[2].trim()), temp2;
while (temp2 = parser2.matchNumber()) {
nums.push(Number(temp2));
parser2.matchSeparator();
}
if (func === 'matrix' && nums.length === 6) {
result = multiplyMatrix(result, [nums[0], nums[1], nums[2], nums[3], nums[4], nums[5]]);
} else if (func === 'translate' && nums.length === 2) {
result = multiplyMatrix(result, [1, 0, 0, 1, nums[0], nums[1]]);
} else if (func === 'translate' && nums.length === 1) {
result = multiplyMatrix(result, [1, 0, 0, 1, nums[0], 0]);
} else if (func === 'scale' && nums.length === 2) {
result = multiplyMatrix(result, [nums[0], 0, 0, nums[1], 0, 0]);
} else if (func === 'scale' && nums.length === 1) {
result = multiplyMatrix(result, [nums[0], 0, 0, nums[0], 0, 0]);
} else if (func === 'rotate' && nums.length === 3) {
let a = nums[0] * Math.PI / 180;
result = multiplyMatrix(result, [1, 0, 0, 1, nums[1], nums[2]], [Math.cos(a), Math.sin(a), -Math.sin(a), Math.cos(a), 0, 0], [1, 0, 0, 1, -nums[1], -nums[2]]);
} else if (func === 'rotate' && nums.length === 1) {
let a = nums[0] * Math.PI / 180;
result = multiplyMatrix(result, [Math.cos(a), Math.sin(a), -Math.sin(a), Math.cos(a), 0, 0]);
} else if (func === 'skewX' && nums.length === 1) {
let a = nums[0] * Math.PI / 180;
result = multiplyMatrix(result, [1, 0, Math.tan(a), 1, 0, 0]);
} else if (func === 'skewY' && nums.length === 1) {
let a = nums[0] * Math.PI / 180;
result = multiplyMatrix(result, [1, Math.tan(a), 0, 1, 0, 0]);
} else {return;}
parser.matchSeparator();
}
if (parser.matchAll()) {return;}
return result;
}
function parseAspectRatio(aspectRatio, availWidth, availHeight, elemWidth, elemHeight, initAlign) {
let temp = (aspectRatio || '').trim().match(/^(none)$|^x(Min|Mid|Max)Y(Min|Mid|Max)(?:\s+(meet|slice))?$/) || [],
ratioType = temp[1] || temp[4] || 'meet',
xAlign = temp[2] || 'Mid',
yAlign = temp[3] || 'Mid',
scaleX = availWidth / elemWidth,
scaleY = availHeight / elemHeight,
dx = {'Min':0, 'Mid':0.5, 'Max':1}[xAlign] - (initAlign || 0),
dy = {'Min':0, 'Mid':0.5, 'Max':1}[yAlign] - (initAlign || 0);
if (ratioType === 'slice') {
scaleY = scaleX = Math.max(scaleX, scaleY);
} else if (ratioType === 'meet') {
scaleY = scaleX = Math.min(scaleX, scaleY);
}
return [scaleX, 0, 0, scaleY, dx * (availWidth - elemWidth * scaleX), dy * (availHeight - elemHeight * scaleY)];
}
function parseStyleAttr(v) {
let result = Object.create(null);
v = (v || '').trim().split(/;/);
for (let i = 0; i < v.length; i++) {
let key = (v[i].split(':')[0] || '').trim(),
value = (v[i].split(':')[1] || '').trim();
if (key) {
result[key] = value;
}
}
if (result['marker']) {
if (!result['marker-start']) {result['marker-start'] = result['marker'];}
if (!result['marker-mid']) {result['marker-mid'] = result['marker'];}
if (!result['marker-end']) {result['marker-end'] = result['marker'];}
}
if (result['font']) {
let fontFamily = null, fontSize = null, fontStyle = "normal", fontWeight = "normal", fontVariant = "normal";
let parts = result['font'].split(/\s+/);
for (let i = 0; i < parts.length; i++) {
switch (parts[i]) {
case "normal":
break;
case "italic": case "oblique":
fontStyle = parts[i];
break;
case "small-caps":
fontVariant = parts[i];
break;
case "bold": case "bolder": case "lighter": case "100": case "200": case "300":
case "400": case "500": case "600": case "700": case "800": case "900":
fontWeight = parts[i];
break;
default:
if (!fontSize) {
fontSize = parts[i].split('/')[0];
} else {
if (!fontFamily) {
fontFamily = parts[i];
} else {
fontFamily += ' ' + parts[i];
}
}
break;
}
}
if (!result['font-style']) {result['font-style'] = fontStyle;}
if (!result['font-variant']) {result['font-variant'] = fontVariant;}
if (!result['font-weight']) {result['font-weight'] = fontWeight;}
if (!result['font-size']) {result['font-size'] = fontSize;}
if (!result['font-family']) {result['font-family'] = fontFamily;}
}
return result;
}
function parseSelector(v) {
let parts = v.split(/(?=[.#])/g), ids = [], classes = [], tags = [], temp;
for (let i = 0; i < parts.length; i++) {
if (temp = parts[i].match(/^[#]([_A-Za-z0-9-]+)$/)) {
ids.push(temp[1]);
} else if (temp = parts[i].match(/^[.]([_A-Za-z0-9-]+)$/)) {
classes.push(temp[1]);
} else if (temp = parts[i].match(/^([_A-Za-z0-9-]+)$/)) {
tags.push(temp[1]);
} else if (parts[i] !== '*') {
return;
}
}
return {
tags: tags, ids: ids, classes: classes,
specificity: ids.length * 10000 + classes.length * 100 + tags.length
};
}
function parseStyleSheet(v) {
let parser = new StringParser(v.trim()), rules = [], rule;
while (rule = parser.match(/^\s*([^\{\}]*?)\s*\{([^\{\}]*?)\}/, true)) {
let selectors = rule[1].split(/\s*,\s*/g),
css = parseStyleAttr(rule[2]);
for (let i = 0; i < selectors.length; i++) {
let selector = parseSelector(selectors[i]);
if (selector) {
rules.push({selector: selector, css:css});
}
}
}
return rules;
}
function matchesSelector(elem, selector) {
if (elem.nodeType !== 1) {return false;}
for (let i = 0; i < selector.tags.length; i++) {
if (selector.tags[i] !== elem.nodeName) {return false;}
}
for (let i = 0; i < selector.ids.length; i++) {
if (selector.ids[i] !== elem.id) {return false;}
}
for (let i = 0; i < selector.classes.length; i++) {
if (elem.classList.indexOf(selector.classes[i]) === -1) {return false;}
}
return true;
}
function getStyle(elem) {
let result = Object.create(null);
let specificities = Object.create(null);
for (let i = 0; i < styleRules.length; i++) {
let rule = styleRules[i];
if (matchesSelector(elem, rule.selector)) {
for (let key in rule.css) {
if (!(specificities[key] > rule.selector.specificity)) {
result[key] = rule.css[key];
specificities[key] = rule.selector.specificity;
}
}
}
}
return result;
}
function combineArrays(array1, array2) {
return array1.concat(array2.slice(array1.length));
}
function getAscent(font, size) {
return Math.max(font.ascender, (font.bbox[3] || font.bbox.maxY) * (font.scale || 1)) * size / 1000;
}
function getDescent(font, size) {
return Math.min(font.descender, (font.bbox[1] || font.bbox.minY) * (font.scale || 1)) * size / 1000;
}
function getXHeight(font, size) {
return (font.xHeight || 0.5 * (font.ascender - font.descender)) * size / 1000;
}
function getBaseline(font, size, baseline, shift) {
let dy1, dy2;
switch (baseline) {
case 'middle': dy1 = 0.5 * getXHeight(font, size); break;
case 'central': dy1 = 0.5 * (getDescent(font, size) + getAscent(font, size)); break;
case 'after-edge': case 'text-after-edge': dy1 = getDescent(font, size); break;
case 'alphabetic': case 'auto': case 'baseline': dy1 = 0; break;
case 'mathematical': dy1 = 0.5 * getAscent(font, size); break;
case 'hanging': dy1 = 0.8 * getAscent(font, size); break;
case 'before-edge': case 'text-before-edge': dy1 = getAscent(font, size); break;
default: dy1 = 0; break;
}
switch (shift) {
case 'baseline': dy2 = 0; break;
case 'super': dy2 = 0.6 * size; break;
case 'sub': dy2 = -0.6 * size; break;
default: dy2 = shift; break;
}
return dy1 - dy2;
}
function getTextPos(font, size, text) {
let encoded = font.encode('' + text), hex = encoded[0], pos = encoded[1], data = [];
for (let i = 0; i < hex.length; i++) {
let unicode = font.unicode ? font.unicode[parseInt(hex[i], 16)] : [text.charCodeAt(i)];
data.push({
glyph: hex[i],
unicode: unicode,
width: pos[i].advanceWidth * size / 1000,
xOffset: pos[i].xOffset * size / 1000,
yOffset: pos[i].yOffset * size / 1000,
xAdvance: pos[i].xAdvance * size / 1000,
yAdvance: pos[i].yAdvance * size / 1000
});
}
return data;
}
function createSVGElement(obj, inherits) {
switch (obj.nodeName) {
case 'use': return new SvgElemUse(obj, inherits);
case 'symbol': return new SvgElemSymbol(obj, inherits);
case 'g': return new SvgElemGroup(obj, inherits);
case 'a': return new SvgElemLink(obj, inherits);
case 'svg': return new SvgElemSvg(obj, inherits);
case 'image': return new SVGElemImage(obj, inherits);
case 'rect': return new SvgElemRect(obj, inherits);
case 'circle': return new SvgElemCircle(obj, inherits);
case 'ellipse': return new SvgElemEllipse(obj, inherits);
case 'line': return new SvgElemLine(obj, inherits);
case 'polyline': return new SvgElemPolyline(obj, inherits);
case 'polygon': return new SvgElemPolygon(obj, inherits);
case 'path': return new SvgElemPath(obj, inherits);
case 'text': return new SvgElemText(obj, inherits);
case 'tspan': return new SvgElemTspan(obj, inherits);
case 'textPath': return new SvgElemTextPath(obj, inherits);
case '#text': case '#cdata-section': return new SvgElemTextNode(obj, inherits);
default: return new SvgElem(obj, inherits);
}
}
var StringParser = function(str) {
this.match = function(exp, all) {
let temp = str.match(exp);
if (!temp || temp.index !== 0) {return;}
str = str.substring(temp[0].length);
return (all ? temp : temp[0]);
};
this.matchSeparator = function() {
return this.match(/^(?:\s*,\s*|\s*|)/);
};
this.matchSpace = function() {
return this.match(/^(?:\s*)/);
};
this.matchLengthUnit = function() {
return this.match(/^(?:px|pt|cm|mm|in|pc|em|ex|%|)/);
};
this.matchNumber = function() {
return this.match(/^(?:[-+]?(?:[0-9]+[.][0-9]+|[0-9]+[.]|[.][0-9]+|[0-9]+)(?:[eE][-+]?[0-9]+)?)/);
};
this.matchAll = function() {
return this.match(/^[\s\S]+/);
};
};
var BezierSegment = function(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
let divisions = 6 * precision;
let equationX = [p1x, -3 * p1x + 3 * c1x, 3 * p1x - 6 * c1x + 3 * c2x, -p1x + 3 * c1x - 3 * c2x + p2x];
let equationY = [p1y, -3 * p1y + 3 * c1y, 3 * p1y - 6 * c1y + 3 * c2y, -p1y + 3 * c1y - 3 * c2y + p2y];
let derivativeX = [-3 * p1x + 3 * c1x, 6 * p1x - 12 * c1x + 6 * c2x, -3 * p1x + 9 * c1x - 9 * c2x + 3 * p2x];
let derivativeY = [-3 * p1y + 3 * c1y, 6 * p1y - 12 * c1y + 6 * c2y, -3 * p1y + 9 * c1y - 9 * c2y + 3 * p2y];
let lengthMap = [0];
for (let i = 1; i <= divisions; i++) {
let t = (i - 0.5) / divisions;
let dx = getCurveValue(t, derivativeX) / divisions,
dy = getCurveValue(t, derivativeY) / divisions,
l = Math.sqrt(dx * dx + dy * dy);
lengthMap[i] = lengthMap[i - 1] + l;
}
this.totalLength = lengthMap[divisions];
this.startPoint = [p1x, p1y, isEqual(p1x, c1x) && isEqual(p1y, c1y) ? Math.atan2(c2y - c1y, c2x - c1x) : Math.atan2(c1y - p1y, c1x - p1x)];
this.endPoint = [p2x, p2y, isEqual(c2x, p2x) && isEqual(c2y, p2y) ? Math.atan2(c2y - c1y, c2x - c1x) : Math.atan2(p2y - c2y, p2x - c2x)];
this.getBoundingBox = function() {
let temp;
let minX = getCurveValue(0, equationX), minY = getCurveValue(0, equationY),
maxX = getCurveValue(1, equationX), maxY = getCurveValue(1, equationY);
if (minX > maxX) {temp = maxX; maxX = minX; minX = temp;}
if (minY > maxY) {temp = maxY; maxY = minY; minY = temp;}
let rootsX = solveEquation(derivativeX);
for (let i = 0; i < rootsX.length; i++) {
if (rootsX[i] >= 0 && rootsX[i] <= 1) {
let x = getCurveValue(rootsX[i], equationX);
if (x < minX) {minX = x;}
if (x > maxX) {maxX = x;}
}
}
let rootsY = solveEquation(derivativeY);
for (let i = 0; i < rootsY.length; i++) {
if (rootsY[i] >= 0 && rootsY[i] <= 1) {
let y = getCurveValue(rootsY[i], equationY);
if (y < minY) {minY = y;}
if (y > maxY) {maxY = y;}
}
}
return [minX, minY, maxX, maxY];
};
this.getPointAtLength = function(l) {
if (isEqual(l, 0)) {return this.startPoint;}
if (isEqual(l, this.totalLength)) {return this.endPoint;}
if (l < 0 || l > this.totalLength) {return;}
for (let i = 1; i <= divisions; i++) {
let l1 = lengthMap[i-1], l2 = lengthMap[i];
if (l1 <= l && l <= l2) {
let t = (i - (l2 - l) / (l2 - l1)) / divisions,
x = getCurveValue(t, equationX), y = getCurveValue(t, equationY),
dx = getCurveValue(t, derivativeX), dy = getCurveValue(t, derivativeY);
return [x, y, Math.atan2(dy, dx)];
}
}
};
};
var LineSegment = function(p1x, p1y, p2x, p2y) {
this.totalLength = Math.sqrt((p2x - p1x) * (p2x - p1x) + (p2y - p1y) * (p2y - p1y));
this.startPoint = [p1x, p1y, Math.atan2(p2y - p1y, p2x - p1x)];
this.endPoint = [p2x, p2y, Math.atan2(p2y - p1y, p2x - p1x)];
this.getBoundingBox = function() {
return [Math.min(this.startPoint[0], this.endPoint[0]), Math.min(this.startPoint[1], this.endPoint[1]),
Math.max(this.startPoint[0], this.endPoint[0]), Math.max(this.startPoint[1], this.endPoint[1])];
};
this.getPointAtLength = function(l) {
if (l >= 0 && l <= this.totalLength) {
let r = l / this.totalLength || 0,
x = this.startPoint[0] + r * (this.endPoint[0] - this.startPoint[0]),
y = this.startPoint[1] + r * (this.endPoint[1] - this.startPoint[1]);
return [x, y, this.startPoint[2]];
}
};
};
var SvgShape = function() {
this.pathCommands = [];
this.pathSegments = [];
this.startPoint = null;
this.endPoint = null;
this.totalLength = 0;
let startX = 0, startY = 0, currX = 0, currY = 0, lastCom, lastCtrlX, lastCtrlY;
this.move = function(x, y) {
startX = currX = x; startY = currY = y;
return null;
};
this.line = function(x, y) {
let segment = new LineSegment(currX, currY, x, y);
currX = x; currY = y;
return segment;
};
this.curve = function(c1x, c1y, c2x, c2y, x, y) {
let segment = new BezierSegment(currX, currY, c1x, c1y, c2x, c2y, x, y);
currX = x; currY = y;
return segment;
};
this.close = function() {
let segment = new LineSegment(currX, currY, startX, startY);
currX = startX; currY = startY;
return segment;
};
this.addCommand = function(data) {
this.pathCommands.push(data);
let segment = this[data[0]].apply(this, data.slice(3));
if (segment) {
segment.hasStart = data[1];
segment.hasEnd = data[2];
this.startPoint = this.startPoint || segment.startPoint;
this.endPoint = segment.endPoint;
this.pathSegments.push(segment);
this.totalLength += segment.totalLength;
}
};
this.M = function(x, y) {
this.addCommand(['move', true, true, x, y]);
lastCom = 'M';
return this;
};
this.m = function(x, y) {
return this.M(currX + x, currY + y);
};
this.Z = this.z = function() {
this.addCommand(['close', true, true]);
lastCom = 'Z';
return this;
};
this.L = function(x, y) {
this.addCommand(['line', true, true, x, y]);
lastCom = 'L';
return this;
};
this.l = function(x, y) {
return this.L(currX + x, currY + y);
};
this.H = function(x) {
return this.L(x, currY);
};
this.h = function(x) {
return this.L(currX + x, currY);
};
this.V = function(y) {
return this.L(currX, y);
};
this.v = function(y) {
return this.L(currX, currY + y);
};
this.C = function(c1x, c1y, c2x, c2y, x, y) {
this.addCommand(['curve', true, true, c1x, c1y, c2x, c2y, x, y]);
lastCom = 'C'; lastCtrlX = c2x; lastCtrlY = c2y;
return this;
};
this.c = function(c1x, c1y, c2x, c2y, x, y) {
return this.C(currX + c1x, currY + c1y, currX + c2x, currY + c2y, currX + x, currY + y);
};
this.S = function(c1x, c1y, x, y) {
return this.C(currX + (lastCom === 'C' ? currX - lastCtrlX : 0), currY + (lastCom === 'C' ? currY - lastCtrlY : 0), c1x, c1y, x, y);
};
this.s = function(c1x, c1y, x, y) {
return this.C(currX + (lastCom === 'C' ? currX - lastCtrlX : 0), currY + (lastCom === 'C' ? currY - lastCtrlY : 0), currX + c1x, currY + c1y, currX + x, currY + y);
};
this.Q = function(cx, cy, x, y) {
let c1x = currX + 2 / 3 * (cx - currX), c1y = currY + 2 / 3 * (cy - currY),
c2x = x + 2 / 3 * (cx - x), c2y = y + 2 / 3 * (cy - y);
this.addCommand(['curve', true, true, c1x, c1y, c2x, c2y, x, y]);
lastCom = 'Q'; lastCtrlX = cx; lastCtrlY = cy;
return this;
};
this.q = function(c1x, c1y, x, y) {
return this.Q(currX + c1x, currY + c1y, currX + x, currY + y);
};
this.T = function(x, y) {
return this.Q(currX + (lastCom === 'Q' ? currX - lastCtrlX : 0), currY + (lastCom === 'Q' ? currY - lastCtrlY : 0), x, y);
};
this.t = function(x, y) {
return this.Q(currX + (lastCom === 'Q' ? currX - lastCtrlX : 0), currY + (lastCom === 'Q' ? currY - lastCtrlY : 0), currX + x, currY + y);
};
this.A = function(rx, ry, fi, fa, fs, x, y) {
if (isEqual(rx, 0) || isEqual(ry, 0)) {
this.addCommand(['line', true, true, x, y]);
} else {
fi = fi * (Math.PI / 180);
rx = Math.abs(rx);
ry = Math.abs(ry);
fa = 1 * !!fa;
fs = 1 * !!fs;
let x1 = Math.cos(fi) * (currX - x) / 2 + Math.sin(fi) * (currY - y) / 2,
y1 = Math.cos(fi) * (currY - y) / 2 - Math.sin(fi) * (currX - x) / 2,
lambda = (x1 * x1) / (rx * rx) + (y1 * y1) / (ry * ry);
if (lambda > 1) {
rx *= Math.sqrt(lambda);
ry *= Math.sqrt(lambda);
}
let r = Math.sqrt(Math.max(0, rx * rx * ry * ry - rx * rx * y1 * y1 - ry * ry * x1 * x1) / (rx * rx * y1 * y1 + ry * ry * x1 * x1)),
x2 = (fa === fs ? -1 : 1) * r * rx * y1 / ry,
y2 = (fa === fs ? 1 : -1) * r * ry * x1 / rx;
let cx = Math.cos(fi) * x2 - Math.sin(fi) * y2 + (currX + x) / 2,
cy = Math.sin(fi) * x2 + Math.cos(fi) * y2 + (currY + y) / 2,
th1 = Math.atan2((y1 - y2) / ry, (x1 - x2) / rx),
th2 = Math.atan2((-y1 - y2) / ry, (-x1 - x2) / rx);
if (fs === 0 && th2 - th1 > 0) {
th2 -= 2 * Math.PI;
} else if (fs === 1 && th2 - th1 < 0) {
th2 += 2 * Math.PI;
}
let segms = Math.ceil(Math.abs(th2 - th1) / (Math.PI / precision));
for (let i = 0; i < segms; i++) {
let th3 = th1 + i * (th2 - th1) / segms,
th4 = th1 + (i + 1) * (th2 - th1) / segms,
t = 4/3 * Math.tan((th4 - th3) / 4);
let c1x = cx + Math.cos(fi) * rx * (Math.cos(th3) - t * Math.sin(th3)) - Math.sin(fi) * ry * (Math.sin(th3) + t * Math.cos(th3)),
c1y = cy + Math.sin(fi) * rx * (Math.cos(th3) - t * Math.sin(th3)) + Math.cos(fi) * ry * (Math.sin(th3) + t * Math.cos(th3)),
c2x = cx + Math.cos(fi) * rx * (Math.cos(th4) + t * Math.sin(th4)) - Math.sin(fi) * ry * (Math.sin(th4) - t * Math.cos(th4)),
c2y = cy + Math.sin(fi) * rx * (Math.cos(th4) + t * Math.sin(th4)) + Math.cos(fi) * ry * (Math.sin(th4) - t * Math.cos(th4)),
endX = cx + Math.cos(fi) * rx * Math.cos(th4) - Math.sin(fi) * ry * Math.sin(th4),
endY = cy + Math.sin(fi) * rx * Math.cos(th4) + Math.cos(fi) * ry * Math.sin(th4);
this.addCommand(['curve', (i === 0), (i === segms - 1), c1x, c1y, c2x, c2y, endX, endY]);
}
}
lastCom = 'A';
return this;
};
this.a = function(rx, ry, fi, fa, fs, x, y) {
return this.A(rx, ry, fi, fa, fs, currX + x, currY + y);
};
this.path = function(d) {
let command, value, temp,
parser = new StringParser((d || '').trim());
while (command = parser.match(/^[astvzqmhlcASTVZQMHLC]/)) {
parser.matchSeparator();
let values = [];
while (value = (PathFlags[command + values.length] ? parser.match(/^[01]/) : parser.matchNumber())) {
parser.matchSeparator();
if (values.length === PathArguments[command]) {
this[command].apply(this, values);
values = [];
if (command === 'M') {command = 'L';}
else if (command === 'm') {command = 'l';}
}
values.push(Number(value));
}
if (values.length === PathArguments[command]) {
this[command].apply(this, values);
} else {
warningCallback('SvgPath: command ' + command + ' with ' + values.length + ' numbers'); return;
}
}
if (temp = parser.matchAll()) {
warningCallback('SvgPath: unexpected string ' + temp);
}
return this;
};
this.getBoundingBox = function() {
let bbox = [Infinity, Infinity, -Infinity, -Infinity];
function addBounds(bbox1) {
if (bbox1[0] < bbox[0]) {bbox[0] = bbox1[0];}
if (bbox1[2] > bbox[2]) {bbox[2] = bbox1[2];}
if (bbox1[1] < bbox[1]) {bbox[1] = bbox1[1];}