pdfmake
Version:
Client/server side PDF printing in pure JavaScript
1,772 lines (1,770 loc) • 124 kB
JavaScript
"use strict";
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