From 08b6cfcd2e0d6d6e3ffad14b758b75df122e867d Mon Sep 17 00:00:00 2001 From: John Konecny Date: Sat, 1 Apr 2023 16:14:57 -0400 Subject: [PATCH] now can handle null d attribute --- dist/svg2pdf.js | 2729 ++++++++++++------------ dist/svg2pdf.min.js | 7 +- src/svg2pdf.js | 6 +- tests/cbioportal-overlap/reference.pdf | Bin 0 -> 487575 bytes tests/cbioportal-overlap/spec.svg | 2536 ++++++++++++++++++++++ tests/runTests.js | 1 + 6 files changed, 3907 insertions(+), 1372 deletions(-) create mode 100644 tests/cbioportal-overlap/reference.pdf create mode 100644 tests/cbioportal-overlap/spec.svg diff --git a/dist/svg2pdf.js b/dist/svg2pdf.js index d8e212b..5fd3334 100644 --- a/dist/svg2pdf.js +++ b/dist/svg2pdf.js @@ -16,1808 +16,1807 @@ * * font-family: * license: MIT (http://opensource.org/licenses/MIT) - * author: Taro Hanamura + * author: Taro Hanamura (http://hanamurataro.com/) * homepage: https://github.com/hanamura/font-family * version: 0.2.0 * * svgpath: * license: MIT (http://opensource.org/licenses/MIT) - * homepage: https://github.com/fontello/svgpath#readme * version: 2.2.1 * * This header is generated by licensify (https://github.com/twada/licensify) */ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.svg2pdf = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i 0x7E) { + if (codePoint >= 0xD800 && codePoint <= 0xDBFF && counter < length) { + // It’s a high surrogate, and there is a next character. + var extra = string.charCodeAt(counter++); + if ((extra & 0xFC00) == 0xDC00) { + // next character is low surrogate + codePoint = ((codePoint & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000; + } else { + // It’s an unmatched surrogate; only append this code unit, in case + // the next code unit is the high surrogate of a surrogate pair. + counter--; + } + } + value = '\\' + codePoint.toString(16).toUpperCase() + ' '; + } else { + if (options.escapeEverything) { + if (regexAnySingleEscape.test(character)) { + value = '\\' + character; + } else { + value = '\\' + codePoint.toString(16).toUpperCase() + ' '; + } + // Note: `:` could be escaped as `\:`, but that fails in IE < 8. + } else if (/[\t\n\f\r\x0B:]/.test(character)) { + if (!isIdentifier && character == ':') { + value = character; + } else { + value = '\\' + codePoint.toString(16).toUpperCase() + ' '; + } + } else if (character == '\\' || !isIdentifier && (character == '"' && quote == character || character == '\'' && quote == character) || isIdentifier && regexSingleEscape.test(character)) { + value = '\\' + character; + } else { + value = character; + } + } + output += value; + } + if (isIdentifier) { + if (/^_/.test(output)) { + // Prevent IE6 from ignoring the rule altogether (in case this is for an + // identifier used as a selector) + output = '\\_' + output.slice(1); + } else if (/^-[-\d]/.test(output)) { + output = '\\-' + output.slice(1); + } else if (/\d/.test(firstChar)) { + output = '\\3' + firstChar + ' ' + output.slice(1); + } + } -/* eslint-disable space-infix-ops */ + // Remove spaces after `\HEX` escapes that are not followed by a hex digit, + // since they’re redundant. Note that this is only possible if the escape + // sequence isn’t preceded by an odd number of backslashes. + output = output.replace(regexExcessiveSpaces, function ($0, $1, $2) { + if ($1 && $1.length % 2) { + // It’s not safe to remove the space, so don’t. + return $0; + } + // Strip the space. + return ($1 || '') + $2; + }); -// Calculate an angle between two vectors -// -function vector_angle(ux, uy, vx, vy) { - var sign = (ux * vy - uy * vx < 0) ? -1 : 1; - var umag = Math.sqrt(ux * ux + uy * uy); - var vmag = Math.sqrt(ux * ux + uy * uy); - var dot = ux * vx + uy * vy; - var div = dot / (umag * vmag); + if (!isIdentifier && options.wrap) { + return quote + output + quote; + } + return output; +}; - // rounding errors, e.g. -1.0000000000000002 can screw up this - if (div > 1.0) { div = 1.0; } - if (div < -1.0) { div = -1.0; } +// Expose default options (so they can be overridden globally). +cssesc.options = { + 'escapeEverything': false, + 'isIdentifier': false, + 'quotes': 'single', + 'wrap': false +}; - return sign * Math.acos(div); -} +cssesc.version = '1.0.1'; +module.exports = cssesc; -// Convert from endpoint to center parameterization, -// see http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes -// -// Return [cx, cy, theta1, delta_theta] -// -function get_arc_center(x1, y1, x2, y2, fa, fs, rx, ry, sin_phi, cos_phi) { - // Step 1. - // - // Moving an ellipse so origin will be the middlepoint between our two - // points. After that, rotate it to line up ellipse axes with coordinate - // axes. - // - var x1p = cos_phi*(x1-x2)/2 + sin_phi*(y1-y2)/2; - var y1p = -sin_phi*(x1-x2)/2 + cos_phi*(y1-y2)/2; +},{}],2:[function(require,module,exports){ +// parse +// ===== - var rx_sq = rx * rx; - var ry_sq = ry * ry; - var x1p_sq = x1p * x1p; - var y1p_sq = y1p * y1p; +// states +// ------ - // Step 2. - // - // Compute coordinates of the centre of this ellipse (cx', cy') - // in the new coordinate system. - // - var radicant = (rx_sq * ry_sq) - (rx_sq * y1p_sq) - (ry_sq * x1p_sq); +var PLAIN = 0; +var STRINGS = 1; +var ESCAPING = 2; +var IDENTIFIER = 3; +var SEPARATING = 4; - if (radicant < 0) { - // due to rounding errors it might be e.g. -1.3877787807814457e-17 - radicant = 0; - } +// patterns +// -------- - radicant /= (rx_sq * y1p_sq) + (ry_sq * x1p_sq); - radicant = Math.sqrt(radicant) * (fa === fs ? -1 : 1); +var identifierPattern = /[a-z0-9_-]/i; +var spacePattern = /[\s\t]/; - var cxp = radicant * rx/ry * y1p; - var cyp = radicant * -ry/rx * x1p; +// --- - // Step 3. - // - // Transform back to get centre coordinates (cx, cy) in the original - // coordinate system. - // - var cx = cos_phi*cxp - sin_phi*cyp + (x1+x2)/2; - var cy = sin_phi*cxp + cos_phi*cyp + (y1+y2)/2; +var parse = function(str) { - // Step 4. - // - // Compute angles (theta1, delta_theta). - // - var v1x = (x1p - cxp) / rx; - var v1y = (y1p - cyp) / ry; - var v2x = (-x1p - cxp) / rx; - var v2y = (-y1p - cyp) / ry; + // vars + // ---- - var theta1 = vector_angle(1, 0, v1x, v1y); - var delta_theta = vector_angle(v1x, v1y, v2x, v2y); + var starting = true; + var state = PLAIN; + var buffer = ''; + var i = 0; + var quote; + var c; - if (fs === 0 && delta_theta > 0) { - delta_theta -= TAU; - } - if (fs === 1 && delta_theta < 0) { - delta_theta += TAU; - } + // result + // ------ - return [ cx, cy, theta1, delta_theta ]; -} + var names = []; -// -// Approximate one unit arc segment with bézier curves, -// see http://math.stackexchange.com/questions/873224 -// -function approximate_unit_arc(theta1, delta_theta) { - var alpha = 4/3 * Math.tan(delta_theta/4); + // parse + // ----- - var x1 = Math.cos(theta1); - var y1 = Math.sin(theta1); - var x2 = Math.cos(theta1 + delta_theta); - var y2 = Math.sin(theta1 + delta_theta); + while (true) { - return [ x1, y1, x1 - y1*alpha, y1 + x1*alpha, x2 + y2*alpha, y2 - x2*alpha, x2, y2 ]; -} + c = str[i]; -module.exports = function a2c(x1, y1, x2, y2, fa, fs, rx, ry, phi) { - var sin_phi = Math.sin(phi * TAU / 360); - var cos_phi = Math.cos(phi * TAU / 360); + if (state === PLAIN) { - // Make sure radii are valid - // - var x1p = cos_phi*(x1-x2)/2 + sin_phi*(y1-y2)/2; - var y1p = -sin_phi*(x1-x2)/2 + cos_phi*(y1-y2)/2; + if (!c && starting) { - if (x1p === 0 && y1p === 0) { - // we're asked to draw line to itself - return []; - } + break; - if (rx === 0 || ry === 0) { - // one of the radii is zero - return []; - } + } else if (!c && !starting) { + throw new Error('Parse error'); - // Compensate out-of-range radii - // - rx = Math.abs(rx); - ry = Math.abs(ry); + } else if (c === '"' || c === "'") { - var lambda = (x1p * x1p) / (rx * rx) + (y1p * y1p) / (ry * ry); - if (lambda > 1) { - rx *= Math.sqrt(lambda); - ry *= Math.sqrt(lambda); - } + quote = c; + state = STRINGS; + starting = false; + } else if (spacePattern.test(c)) { + } else if (identifierPattern.test(c)) { - // Get center parameters (cx, cy, theta1, delta_theta) - // - var cc = get_arc_center(x1, y1, x2, y2, fa, fs, rx, ry, sin_phi, cos_phi); + state = IDENTIFIER; + starting = false; + i--; - var result = []; - var theta1 = cc[2]; - var delta_theta = cc[3]; + } else { - // Split an arc to multiple segments, so each segment - // will be less than τ/4 (= 90°) - // - var segments = Math.max(Math.ceil(Math.abs(delta_theta) / (TAU / 4)), 1); - delta_theta /= segments; + throw new Error('Parse error'); - for (var i = 0; i < segments; i++) { - result.push(approximate_unit_arc(theta1, delta_theta)); - theta1 += delta_theta; - } + } - // We have a bezier approximation of a unit circle, - // now need to transform back to the original ellipse - // - return result.map(function (curve) { - for (var i = 0; i < curve.length; i += 2) { - var x = curve[i + 0]; - var y = curve[i + 1]; + } else if (state === STRINGS) { - // scale - x *= rx; - y *= ry; + if (!c) { - // rotate - var xp = cos_phi*x - sin_phi*y; - var yp = sin_phi*x + cos_phi*y; + throw new Error('Parse Error'); - // translate - curve[i + 0] = xp + cc[0]; - curve[i + 1] = yp + cc[1]; - } + } else if (c === "\\") { - return curve; - }); -}; + state = ESCAPING; -},{}],3:[function(require,module,exports){ -'use strict'; + } else if (c === quote) { -/* eslint-disable space-infix-ops */ + names.push(buffer); + buffer = ''; + state = SEPARATING; -// The precision used to consider an ellipse as a circle -// -var epsilon = 0.0000000001; + } else { -// To convert degree in radians -// -var torad = Math.PI / 180; + buffer += c; -// Class constructor : -// an ellipse centred at 0 with radii rx,ry and x - axis - angle ax. -// -function Ellipse(rx, ry, ax) { - if (!(this instanceof Ellipse)) { return new Ellipse(rx, ry, ax); } - this.rx = rx; - this.ry = ry; - this.ax = ax; -} + } -// Apply a linear transform m to the ellipse -// m is an array representing a matrix : -// - - -// | m[0] m[2] | -// | m[1] m[3] | -// - - -// -Ellipse.prototype.transform = function (m) { - // We consider the current ellipse as image of the unit circle - // by first scale(rx,ry) and then rotate(ax) ... - // So we apply ma = m x rotate(ax) x scale(rx,ry) to the unit circle. - var c = Math.cos(this.ax * torad), s = Math.sin(this.ax * torad); - var ma = [ - this.rx * (m[0]*c + m[2]*s), - this.rx * (m[1]*c + m[3]*s), - this.ry * (-m[0]*s + m[2]*c), - this.ry * (-m[1]*s + m[3]*c) - ]; + } else if (state === ESCAPING) { - // ma * transpose(ma) = [ J L ] - // [ L K ] - // L is calculated later (if the image is not a circle) - var J = ma[0]*ma[0] + ma[2]*ma[2], - K = ma[1]*ma[1] + ma[3]*ma[3]; + if (c === quote || c === "\\") { - // the discriminant of the characteristic polynomial of ma * transpose(ma) - var D = ((ma[0]-ma[3])*(ma[0]-ma[3]) + (ma[2]+ma[1])*(ma[2]+ma[1])) * - ((ma[0]+ma[3])*(ma[0]+ma[3]) + (ma[2]-ma[1])*(ma[2]-ma[1])); + buffer += c; + state = STRINGS; - // the "mean eigenvalue" - var JK = (J + K) / 2; + } else { - // check if the image is (almost) a circle - if (D < epsilon * JK) { - // if it is - this.rx = this.ry = Math.sqrt(JK); - this.ax = 0; - return this; - } + throw new Error('Parse error'); - // if it is not a circle - var L = ma[0]*ma[1] + ma[2]*ma[3]; + } - D = Math.sqrt(D); + } else if (state === IDENTIFIER) { - // {l1,l2} = the two eigen values of ma * transpose(ma) - var l1 = JK + D/2, - l2 = JK - D/2; - // the x - axis - rotation angle is the argument of the l1 - eigenvector - this.ax = (Math.abs(L) < epsilon && Math.abs(l1 - K) < epsilon) ? - 90 - : - Math.atan(Math.abs(L) > Math.abs(l1 - K) ? - (l1 - J) / L - : - L / (l1 - K) - ) * 180 / Math.PI; + if (!c) { - // if ax > 0 => rx = sqrt(l1), ry = sqrt(l2), else exchange axes and ax += 90 - if (this.ax >= 0) { - // if ax in [0,90] - this.rx = Math.sqrt(l1); - this.ry = Math.sqrt(l2); - } else { - // if ax in ]-90,0[ => exchange axes - this.ax += 90; - this.rx = Math.sqrt(l2); - this.ry = Math.sqrt(l1); - } + names.push(buffer); + break; - return this; -}; + } else if (identifierPattern.test(c)) { -// Check if the ellipse is (almost) degenerate, i.e. rx = 0 or ry = 0 -// -Ellipse.prototype.isDegenerate = function () { - return (this.rx < epsilon * this.ry || this.ry < epsilon * this.rx); -}; + buffer += c; -module.exports = Ellipse; + } else if (c === ',') { -},{}],4:[function(require,module,exports){ -'use strict'; + names.push(buffer); + buffer = ''; + state = PLAIN; -// combine 2 matrixes -// m1, m2 - [a, b, c, d, e, g] -// -function combine(m1, m2) { - return [ - m1[0] * m2[0] + m1[2] * m2[1], - m1[1] * m2[0] + m1[3] * m2[1], - m1[0] * m2[2] + m1[2] * m2[3], - m1[1] * m2[2] + m1[3] * m2[3], - m1[0] * m2[4] + m1[2] * m2[5] + m1[4], - m1[1] * m2[4] + m1[3] * m2[5] + m1[5] - ]; -} + } else if (spacePattern.test(c)) { + names.push(buffer); + buffer = ''; + state = SEPARATING; -function Matrix() { - if (!(this instanceof Matrix)) { return new Matrix(); } - this.queue = []; // list of matrixes to apply - this.cache = null; // combined matrix cache -} + } else { + throw new Error('Parse error'); -Matrix.prototype.matrix = function (m) { - if (m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 1 && m[4] === 0 && m[5] === 0) { - return this; - } - this.cache = null; - this.queue.push(m); - return this; -}; + } + } else if (state === SEPARATING) { -Matrix.prototype.translate = function (tx, ty) { - if (tx !== 0 || ty !== 0) { - this.cache = null; - this.queue.push([ 1, 0, 0, 1, tx, ty ]); - } - return this; -}; + if (!c) { + break; -Matrix.prototype.scale = function (sx, sy) { - if (sx !== 1 || sy !== 1) { - this.cache = null; - this.queue.push([ sx, 0, 0, sy, 0, 0 ]); - } - return this; -}; + } else if (c === ',') { + state = PLAIN; -Matrix.prototype.rotate = function (angle, rx, ry) { - var rad, cos, sin; + } else if (spacePattern.test(c)) { + } else { - if (angle !== 0) { - this.translate(rx, ry); + throw new Error('Parse error'); - rad = angle * Math.PI / 180; - cos = Math.cos(rad); - sin = Math.sin(rad); + } - this.queue.push([ cos, sin, -sin, cos, 0, 0 ]); - this.cache = null; + } + + i++; - this.translate(-rx, -ry); } - return this; -}; + // result + // ------ + + return names; -Matrix.prototype.skewX = function (angle) { - if (angle !== 0) { - this.cache = null; - this.queue.push([ 1, 0, Math.tan(angle * Math.PI / 180), 1, 0, 0 ]); - } - return this; }; +// stringify +// ========= -Matrix.prototype.skewY = function (angle) { - if (angle !== 0) { - this.cache = null; - this.queue.push([ 1, Math.tan(angle * Math.PI / 180), 0, 1, 0, 0 ]); - } - return this; -}; +// pattern +// ------- +var stringsPattern = /[^a-z0-9_-]/i; -// Flatten queue -// -Matrix.prototype.toArray = function () { - if (this.cache) { - return this.cache; - } - - if (!this.queue.length) { - this.cache = [ 1, 0, 0, 1, 0, 0 ]; - return this.cache; - } +// --- - this.cache = this.queue[0]; +var stringify = function(names, options) { - if (this.queue.length === 1) { - return this.cache; - } + // quote + // ----- - for (var i = 1; i < this.queue.length; i++) { - this.cache = combine(this.cache, this.queue[i]); + var quote = options && options.quote || '"'; + if (quote !== '"' && quote !== "'") { + throw new Error('Quote must be `\'` or `"`'); } + var quotePattern = new RegExp(quote, 'g'); - return this.cache; -}; - - -// Apply list of matrixes to (x,y) point. -// If `isRelative` set, `translate` component of matrix will be skipped -// -Matrix.prototype.calc = function (x, y, isRelative) { - var m; + // stringify + // --------- - // Don't change point on empty transforms queue - if (!this.queue.length) { return [ x, y ]; } + var safeNames = []; - // Calculate final matrix, if not exists - // - // NB. if you deside to apply transforms to point one-by-one, - // they should be taken in reverse order + for (var i = 0; i < names.length; ++i) { + var name = names[i]; - if (!this.cache) { - this.cache = this.toArray(); + if (stringsPattern.test(name)) { + name = name + .replace(/\\/g, "\\\\") + .replace(quotePattern, "\\" + quote); + name = quote + name + quote; + } + safeNames.push(name); } - m = this.cache; + // result + // ------ - // Apply matrix to point - return [ - x * m[0] + y * m[2] + (isRelative ? 0 : m[4]), - x * m[1] + y * m[3] + (isRelative ? 0 : m[5]) - ]; + return safeNames.join(', '); }; +// export +// ====== -module.exports = Matrix; +module.exports = { + parse: parse, + stringify: stringify, +}; -},{}],5:[function(require,module,exports){ +},{}],3:[function(require,module,exports){ 'use strict'; +module.exports = require('./lib/svgpath'); -var paramCounts = { a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0 }; +},{"./lib/svgpath":8}],4:[function(require,module,exports){ +// Convert an arc to a sequence of cubic bézier curves +// +'use strict'; -var SPECIAL_SPACES = [ - 0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, - 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF -]; -function isSpace(ch) { - return (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch === 0x2029) || // Line terminators - // White spaces - (ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0) || - (ch >= 0x1680 && SPECIAL_SPACES.indexOf(ch) >= 0); -} +var TAU = Math.PI * 2; -function isCommand(code) { - /*eslint-disable no-bitwise*/ - switch (code | 0x20) { - case 0x6D/* m */: - case 0x7A/* z */: - case 0x6C/* l */: - case 0x68/* h */: - case 0x76/* v */: - case 0x63/* c */: - case 0x73/* s */: - case 0x71/* q */: - case 0x74/* t */: - case 0x61/* a */: - case 0x72/* r */: - return true; - } - return false; -} -function isDigit(code) { - return (code >= 48 && code <= 57); // 0..9 -} +/* eslint-disable space-infix-ops */ -function isDigitStart(code) { - return (code >= 48 && code <= 57) || /* 0..9 */ - code === 0x2B || /* + */ - code === 0x2D || /* - */ - code === 0x2E; /* . */ -} +// Calculate an angle between two vectors +// +function vector_angle(ux, uy, vx, vy) { + var sign = (ux * vy - uy * vx < 0) ? -1 : 1; + var umag = Math.sqrt(ux * ux + uy * uy); + var vmag = Math.sqrt(ux * ux + uy * uy); + var dot = ux * vx + uy * vy; + var div = dot / (umag * vmag); + // rounding errors, e.g. -1.0000000000000002 can screw up this + if (div > 1.0) { div = 1.0; } + if (div < -1.0) { div = -1.0; } -function State(path) { - this.index = 0; - this.path = path; - this.max = path.length; - this.result = []; - this.param = 0.0; - this.err = ''; - this.segmentStart = 0; - this.data = []; + return sign * Math.acos(div); } -function skipSpaces(state) { - while (state.index < state.max && isSpace(state.path.charCodeAt(state.index))) { - state.index++; - } -} +// Convert from endpoint to center parameterization, +// see http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes +// +// Return [cx, cy, theta1, delta_theta] +// +function get_arc_center(x1, y1, x2, y2, fa, fs, rx, ry, sin_phi, cos_phi) { + // Step 1. + // + // Moving an ellipse so origin will be the middlepoint between our two + // points. After that, rotate it to line up ellipse axes with coordinate + // axes. + // + var x1p = cos_phi*(x1-x2)/2 + sin_phi*(y1-y2)/2; + var y1p = -sin_phi*(x1-x2)/2 + cos_phi*(y1-y2)/2; -function scanParam(state) { - var start = state.index, - index = start, - max = state.max, - zeroFirst = false, - hasCeiling = false, - hasDecimal = false, - hasDot = false, - ch; + var rx_sq = rx * rx; + var ry_sq = ry * ry; + var x1p_sq = x1p * x1p; + var y1p_sq = y1p * y1p; - if (index >= max) { - state.err = 'SvgPath: missed param (at pos ' + index + ')'; - return; - } - ch = state.path.charCodeAt(index); + // Step 2. + // + // Compute coordinates of the centre of this ellipse (cx', cy') + // in the new coordinate system. + // + var radicant = (rx_sq * ry_sq) - (rx_sq * y1p_sq) - (ry_sq * x1p_sq); - if (ch === 0x2B/* + */ || ch === 0x2D/* - */) { - index++; - ch = (index < max) ? state.path.charCodeAt(index) : 0; + if (radicant < 0) { + // due to rounding errors it might be e.g. -1.3877787807814457e-17 + radicant = 0; } - // This logic is shamelessly borrowed from Esprima - // https://github.com/ariya/esprimas - // - if (!isDigit(ch) && ch !== 0x2E/* . */) { - state.err = 'SvgPath: param should start with 0..9 or `.` (at pos ' + index + ')'; - return; - } + radicant /= (rx_sq * y1p_sq) + (ry_sq * x1p_sq); + radicant = Math.sqrt(radicant) * (fa === fs ? -1 : 1); - if (ch !== 0x2E/* . */) { - zeroFirst = (ch === 0x30/* 0 */); - index++; + var cxp = radicant * rx/ry * y1p; + var cyp = radicant * -ry/rx * x1p; - ch = (index < max) ? state.path.charCodeAt(index) : 0; + // Step 3. + // + // Transform back to get centre coordinates (cx, cy) in the original + // coordinate system. + // + var cx = cos_phi*cxp - sin_phi*cyp + (x1+x2)/2; + var cy = sin_phi*cxp + cos_phi*cyp + (y1+y2)/2; - if (zeroFirst && index < max) { - // decimal number starts with '0' such as '09' is illegal. - if (ch && isDigit(ch)) { - state.err = 'SvgPath: numbers started with `0` such as `09` are ilegal (at pos ' + start + ')'; - return; - } - } + // Step 4. + // + // Compute angles (theta1, delta_theta). + // + var v1x = (x1p - cxp) / rx; + var v1y = (y1p - cyp) / ry; + var v2x = (-x1p - cxp) / rx; + var v2y = (-y1p - cyp) / ry; - while (index < max && isDigit(state.path.charCodeAt(index))) { - index++; - hasCeiling = true; - } - ch = (index < max) ? state.path.charCodeAt(index) : 0; - } + var theta1 = vector_angle(1, 0, v1x, v1y); + var delta_theta = vector_angle(v1x, v1y, v2x, v2y); - if (ch === 0x2E/* . */) { - hasDot = true; - index++; - while (isDigit(state.path.charCodeAt(index))) { - index++; - hasDecimal = true; - } - ch = (index < max) ? state.path.charCodeAt(index) : 0; + if (fs === 0 && delta_theta > 0) { + delta_theta -= TAU; + } + if (fs === 1 && delta_theta < 0) { + delta_theta += TAU; } - if (ch === 0x65/* e */ || ch === 0x45/* E */) { - if (hasDot && !hasCeiling && !hasDecimal) { - state.err = 'SvgPath: invalid float exponent (at pos ' + index + ')'; - return; - } - - index++; + return [ cx, cy, theta1, delta_theta ]; +} - ch = (index < max) ? state.path.charCodeAt(index) : 0; - if (ch === 0x2B/* + */ || ch === 0x2D/* - */) { - index++; - } - if (index < max && isDigit(state.path.charCodeAt(index))) { - while (index < max && isDigit(state.path.charCodeAt(index))) { - index++; - } - } else { - state.err = 'SvgPath: invalid float exponent (at pos ' + index + ')'; - return; - } - } - - state.index = index; - state.param = parseFloat(state.path.slice(start, index)) + 0.0; -} +// +// Approximate one unit arc segment with bézier curves, +// see http://math.stackexchange.com/questions/873224 +// +function approximate_unit_arc(theta1, delta_theta) { + var alpha = 4/3 * Math.tan(delta_theta/4); + var x1 = Math.cos(theta1); + var y1 = Math.sin(theta1); + var x2 = Math.cos(theta1 + delta_theta); + var y2 = Math.sin(theta1 + delta_theta); -function finalizeSegment(state) { - var cmd, cmdLC; + return [ x1, y1, x1 - y1*alpha, y1 + x1*alpha, x2 + y2*alpha, y2 - x2*alpha, x2, y2 ]; +} - // Process duplicated commands (without comand name) +module.exports = function a2c(x1, y1, x2, y2, fa, fs, rx, ry, phi) { + var sin_phi = Math.sin(phi * TAU / 360); + var cos_phi = Math.cos(phi * TAU / 360); - // This logic is shamelessly borrowed from Raphael - // https://github.com/DmitryBaranovskiy/raphael/ + // Make sure radii are valid // - cmd = state.path[state.segmentStart]; - cmdLC = cmd.toLowerCase(); - - var params = state.data; + var x1p = cos_phi*(x1-x2)/2 + sin_phi*(y1-y2)/2; + var y1p = -sin_phi*(x1-x2)/2 + cos_phi*(y1-y2)/2; - if (cmdLC === 'm' && params.length > 2) { - state.result.push([ cmd, params[0], params[1] ]); - params = params.slice(2); - cmdLC = 'l'; - cmd = (cmd === 'm') ? 'l' : 'L'; + if (x1p === 0 && y1p === 0) { + // we're asked to draw line to itself + return []; } - if (cmdLC === 'r') { - state.result.push([ cmd ].concat(params)); - } else { - - while (params.length >= paramCounts[cmdLC]) { - state.result.push([ cmd ].concat(params.splice(0, paramCounts[cmdLC]))); - if (!paramCounts[cmdLC]) { - break; - } - } + if (rx === 0 || ry === 0) { + // one of the radii is zero + return []; } -} - -function scanSegment(state) { - var max = state.max, - cmdCode, comma_found, need_params, i; - state.segmentStart = state.index; - cmdCode = state.path.charCodeAt(state.index); + // Compensate out-of-range radii + // + rx = Math.abs(rx); + ry = Math.abs(ry); - if (!isCommand(cmdCode)) { - state.err = 'SvgPath: bad command ' + state.path[state.index] + ' (at pos ' + state.index + ')'; - return; + var lambda = (x1p * x1p) / (rx * rx) + (y1p * y1p) / (ry * ry); + if (lambda > 1) { + rx *= Math.sqrt(lambda); + ry *= Math.sqrt(lambda); } - need_params = paramCounts[state.path[state.index].toLowerCase()]; - state.index++; - skipSpaces(state); + // Get center parameters (cx, cy, theta1, delta_theta) + // + var cc = get_arc_center(x1, y1, x2, y2, fa, fs, rx, ry, sin_phi, cos_phi); - state.data = []; + var result = []; + var theta1 = cc[2]; + var delta_theta = cc[3]; - if (!need_params) { - // Z - finalizeSegment(state); - return; + // Split an arc to multiple segments, so each segment + // will be less than τ/4 (= 90°) + // + var segments = Math.max(Math.ceil(Math.abs(delta_theta) / (TAU / 4)), 1); + delta_theta /= segments; + + for (var i = 0; i < segments; i++) { + result.push(approximate_unit_arc(theta1, delta_theta)); + theta1 += delta_theta; } - comma_found = false; + // We have a bezier approximation of a unit circle, + // now need to transform back to the original ellipse + // + return result.map(function (curve) { + for (var i = 0; i < curve.length; i += 2) { + var x = curve[i + 0]; + var y = curve[i + 1]; - for (;;) { - for (i = need_params; i > 0; i--) { - scanParam(state); - if (state.err.length) { - return; - } - state.data.push(state.param); + // scale + x *= rx; + y *= ry; - skipSpaces(state); - comma_found = false; + // rotate + var xp = cos_phi*x - sin_phi*y; + var yp = sin_phi*x + cos_phi*y; - if (state.index < max && state.path.charCodeAt(state.index) === 0x2C/* , */) { - state.index++; - skipSpaces(state); - comma_found = true; - } + // translate + curve[i + 0] = xp + cc[0]; + curve[i + 1] = yp + cc[1]; } - // after ',' param is mandatory - if (comma_found) { - continue; - } + return curve; + }); +}; - if (state.index >= state.max) { - break; - } +},{}],5:[function(require,module,exports){ +'use strict'; - // Stop on next segment - if (!isDigitStart(state.path.charCodeAt(state.index))) { - break; - } - } +/* eslint-disable space-infix-ops */ - finalizeSegment(state); +// The precision used to consider an ellipse as a circle +// +var epsilon = 0.0000000001; + +// To convert degree in radians +// +var torad = Math.PI / 180; + +// Class constructor : +// an ellipse centred at 0 with radii rx,ry and x - axis - angle ax. +// +function Ellipse(rx, ry, ax) { + if (!(this instanceof Ellipse)) { return new Ellipse(rx, ry, ax); } + this.rx = rx; + this.ry = ry; + this.ax = ax; } +// Apply a linear transform m to the ellipse +// m is an array representing a matrix : +// - - +// | m[0] m[2] | +// | m[1] m[3] | +// - - +// +Ellipse.prototype.transform = function (m) { + // We consider the current ellipse as image of the unit circle + // by first scale(rx,ry) and then rotate(ax) ... + // So we apply ma = m x rotate(ax) x scale(rx,ry) to the unit circle. + var c = Math.cos(this.ax * torad), s = Math.sin(this.ax * torad); + var ma = [ + this.rx * (m[0]*c + m[2]*s), + this.rx * (m[1]*c + m[3]*s), + this.ry * (-m[0]*s + m[2]*c), + this.ry * (-m[1]*s + m[3]*c) + ]; -/* Returns array of segments: - * - * [ - * [ command, coord1, coord2, ... ] - * ] - */ -module.exports = function pathParse(svgPath) { - var state = new State(svgPath); - var max = state.max; + // ma * transpose(ma) = [ J L ] + // [ L K ] + // L is calculated later (if the image is not a circle) + var J = ma[0]*ma[0] + ma[2]*ma[2], + K = ma[1]*ma[1] + ma[3]*ma[3]; - skipSpaces(state); + // the discriminant of the characteristic polynomial of ma * transpose(ma) + var D = ((ma[0]-ma[3])*(ma[0]-ma[3]) + (ma[2]+ma[1])*(ma[2]+ma[1])) * + ((ma[0]+ma[3])*(ma[0]+ma[3]) + (ma[2]-ma[1])*(ma[2]-ma[1])); - while (state.index < max && !state.err.length) { - scanSegment(state); + // the "mean eigenvalue" + var JK = (J + K) / 2; + + // check if the image is (almost) a circle + if (D < epsilon * JK) { + // if it is + this.rx = this.ry = Math.sqrt(JK); + this.ax = 0; + return this; } - if (state.err.length) { - state.result = []; + // if it is not a circle + var L = ma[0]*ma[1] + ma[2]*ma[3]; - } else if (state.result.length) { + D = Math.sqrt(D); - if ('mM'.indexOf(state.result[0][0]) < 0) { - state.err = 'SvgPath: string should start with `M` or `m`'; - state.result = []; - } else { - state.result[0][0] = 'M'; - } + // {l1,l2} = the two eigen values of ma * transpose(ma) + var l1 = JK + D/2, + l2 = JK - D/2; + // the x - axis - rotation angle is the argument of the l1 - eigenvector + this.ax = (Math.abs(L) < epsilon && Math.abs(l1 - K) < epsilon) ? + 90 + : + Math.atan(Math.abs(L) > Math.abs(l1 - K) ? + (l1 - J) / L + : + L / (l1 - K) + ) * 180 / Math.PI; + + // if ax > 0 => rx = sqrt(l1), ry = sqrt(l2), else exchange axes and ax += 90 + if (this.ax >= 0) { + // if ax in [0,90] + this.rx = Math.sqrt(l1); + this.ry = Math.sqrt(l2); + } else { + // if ax in ]-90,0[ => exchange axes + this.ax += 90; + this.rx = Math.sqrt(l2); + this.ry = Math.sqrt(l1); } - return { - err: state.err, - segments: state.result - }; + return this; }; -},{}],6:[function(require,module,exports){ -// SVG Path transformations library -// -// Usage: -// -// SvgPath('...') -// .translate(-150, -100) -// .scale(0.5) -// .translate(-150, -100) -// .toFixed(1) -// .toString() +// Check if the ellipse is (almost) degenerate, i.e. rx = 0 or ry = 0 // +Ellipse.prototype.isDegenerate = function () { + return (this.rx < epsilon * this.ry || this.ry < epsilon * this.rx); +}; -'use strict'; - - -var pathParse = require('./path_parse'); -var transformParse = require('./transform_parse'); -var matrix = require('./matrix'); -var a2c = require('./a2c'); -var ellipse = require('./ellipse'); +module.exports = Ellipse; +},{}],6:[function(require,module,exports){ +'use strict'; -// Class constructor +// combine 2 matrixes +// m1, m2 - [a, b, c, d, e, g] // -function SvgPath(path) { - if (!(this instanceof SvgPath)) { return new SvgPath(path); } - - var pstate = pathParse(path); - - // Array of path segments. - // Each segment is array [command, param1, param2, ...] - this.segments = pstate.segments; - - // Error message on parse error. - this.err = pstate.err; - - // Transforms stack for lazy evaluation - this.__stack = []; +function combine(m1, m2) { + return [ + m1[0] * m2[0] + m1[2] * m2[1], + m1[1] * m2[0] + m1[3] * m2[1], + m1[0] * m2[2] + m1[2] * m2[3], + m1[1] * m2[2] + m1[3] * m2[3], + m1[0] * m2[4] + m1[2] * m2[5] + m1[4], + m1[1] * m2[4] + m1[3] * m2[5] + m1[5] + ]; } -SvgPath.prototype.__matrix = function (m) { - var self = this, i; - - // Quick leave for empty matrix - if (!m.queue.length) { return; } - - this.iterate(function (s, index, x, y) { - var p, result, name, isRelative; - - switch (s[0]) { - - // Process 'assymetric' commands separately - case 'v': - p = m.calc(0, s[1], true); - result = (p[0] === 0) ? [ 'v', p[1] ] : [ 'l', p[0], p[1] ]; - break; +function Matrix() { + if (!(this instanceof Matrix)) { return new Matrix(); } + this.queue = []; // list of matrixes to apply + this.cache = null; // combined matrix cache +} - case 'V': - p = m.calc(x, s[1], false); - result = (p[0] === m.calc(x, y, false)[0]) ? [ 'V', p[1] ] : [ 'L', p[0], p[1] ]; - break; - case 'h': - p = m.calc(s[1], 0, true); - result = (p[1] === 0) ? [ 'h', p[0] ] : [ 'l', p[0], p[1] ]; - break; +Matrix.prototype.matrix = function (m) { + if (m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 1 && m[4] === 0 && m[5] === 0) { + return this; + } + this.cache = null; + this.queue.push(m); + return this; +}; - case 'H': - p = m.calc(s[1], y, false); - result = (p[1] === m.calc(x, y, false)[1]) ? [ 'H', p[0] ] : [ 'L', p[0], p[1] ]; - break; - case 'a': - case 'A': - // ARC is: ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y] +Matrix.prototype.translate = function (tx, ty) { + if (tx !== 0 || ty !== 0) { + this.cache = null; + this.queue.push([ 1, 0, 0, 1, tx, ty ]); + } + return this; +}; - // Drop segment if arc is empty (end point === start point) - /*if ((s[0] === 'A' && s[6] === x && s[7] === y) || - (s[0] === 'a' && s[6] === 0 && s[7] === 0)) { - return []; - }*/ - // Transform rx, ry and the x-axis-rotation - var ma = m.toArray(); - var e = ellipse(s[1], s[2], s[3]).transform(ma); +Matrix.prototype.scale = function (sx, sy) { + if (sx !== 1 || sy !== 1) { + this.cache = null; + this.queue.push([ sx, 0, 0, sy, 0, 0 ]); + } + return this; +}; - // flip sweep-flag if matrix is not orientation-preserving - if (ma[0] * ma[3] - ma[1] * ma[2] < 0) { - s[5] = s[5] ? '0' : '1'; - } - // Transform end point as usual (without translation for relative notation) - p = m.calc(s[6], s[7], s[0] === 'a'); +Matrix.prototype.rotate = function (angle, rx, ry) { + var rad, cos, sin; - // Empty arcs can be ignored by renderer, but should not be dropped - // to avoid collisions with `S A S` and so on. Replace with empty line. - if ((s[0] === 'A' && s[6] === x && s[7] === y) || - (s[0] === 'a' && s[6] === 0 && s[7] === 0)) { - result = [ s[0] === 'a' ? 'l' : 'L', p[0], p[1] ]; - break; - } + if (angle !== 0) { + this.translate(rx, ry); - // if the resulting ellipse is (almost) a segment ... - if (e.isDegenerate()) { - // replace the arc by a line - result = [ s[0] === 'a' ? 'l' : 'L', p[0], p[1] ]; - } else { - // if it is a real ellipse - // s[0], s[4] and s[5] are not modified - result = [ s[0], e.rx, e.ry, e.ax, s[4], s[5], p[0], p[1] ]; - } + rad = angle * Math.PI / 180; + cos = Math.cos(rad); + sin = Math.sin(rad); - break; + this.queue.push([ cos, sin, -sin, cos, 0, 0 ]); + this.cache = null; - case 'm': - // Edge case. The very first `m` should be processed as absolute, if happens. - // Make sense for coord shift transforms. - isRelative = index > 0; + this.translate(-rx, -ry); + } + return this; +}; - p = m.calc(s[1], s[2], isRelative); - result = [ 'm', p[0], p[1] ]; - break; - default: - name = s[0]; - result = [ name ]; - isRelative = (name.toLowerCase() === name); +Matrix.prototype.skewX = function (angle) { + if (angle !== 0) { + this.cache = null; + this.queue.push([ 1, 0, Math.tan(angle * Math.PI / 180), 1, 0, 0 ]); + } + return this; +}; - // Apply transformations to the segment - for (i = 1; i < s.length; i += 2) { - p = m.calc(s[i], s[i + 1], isRelative); - result.push(p[0], p[1]); - } - } - self.segments[index] = result; - }, true); +Matrix.prototype.skewY = function (angle) { + if (angle !== 0) { + this.cache = null; + this.queue.push([ 1, Math.tan(angle * Math.PI / 180), 0, 1, 0, 0 ]); + } + return this; }; -// Apply stacked commands +// Flatten queue // -SvgPath.prototype.__evaluateStack = function () { - var m, i; - - if (!this.__stack.length) { return; } +Matrix.prototype.toArray = function () { + if (this.cache) { + return this.cache; + } - if (this.__stack.length === 1) { - this.__matrix(this.__stack[0]); - this.__stack = []; - return; + if (!this.queue.length) { + this.cache = [ 1, 0, 0, 1, 0, 0 ]; + return this.cache; } - m = matrix(); - i = this.__stack.length; + this.cache = this.queue[0]; - while (--i >= 0) { - m.matrix(this.__stack[i].toArray()); + if (this.queue.length === 1) { + return this.cache; } - this.__matrix(m); - this.__stack = []; + for (var i = 1; i < this.queue.length; i++) { + this.cache = combine(this.cache, this.queue[i]); + } + + return this.cache; }; -// Convert processed SVG Path back to string +// Apply list of matrixes to (x,y) point. +// If `isRelative` set, `translate` component of matrix will be skipped // -SvgPath.prototype.toString = function () { - var elements = [], skipCmd, cmd; +Matrix.prototype.calc = function (x, y, isRelative) { + var m; - this.__evaluateStack(); + // Don't change point on empty transforms queue + if (!this.queue.length) { return [ x, y ]; } - for (var i = 0; i < this.segments.length; i++) { - // remove repeating commands names - cmd = this.segments[i][0]; - skipCmd = i > 0 && cmd !== 'm' && cmd !== 'M' && cmd === this.segments[i - 1][0]; - elements = elements.concat(skipCmd ? this.segments[i].slice(1) : this.segments[i]); + // Calculate final matrix, if not exists + // + // NB. if you deside to apply transforms to point one-by-one, + // they should be taken in reverse order + + if (!this.cache) { + this.cache = this.toArray(); } - return elements.join(' ') - // Optimizations: remove spaces around commands & before `-` - // - // We could also remove leading zeros for `0.5`-like values, - // but their count is too small to spend time for. - .replace(/ ?([achlmqrstvz]) ?/gi, '$1') - .replace(/ \-/g, '-') - // workaround for FontForge SVG importing bug - .replace(/zm/g, 'z m'); + m = this.cache; + + // Apply matrix to point + return [ + x * m[0] + y * m[2] + (isRelative ? 0 : m[4]), + x * m[1] + y * m[3] + (isRelative ? 0 : m[5]) + ]; }; -// Translate path to (x [, y]) -// -SvgPath.prototype.translate = function (x, y) { - this.__stack.push(matrix().translate(x, y || 0)); - return this; -}; - +module.exports = Matrix; -// Scale path to (sx [, sy]) -// sy = sx if not defined -// -SvgPath.prototype.scale = function (sx, sy) { - this.__stack.push(matrix().scale(sx, (!sy && (sy !== 0)) ? sx : sy)); - return this; -}; +},{}],7:[function(require,module,exports){ +'use strict'; -// Rotate path around point (sx [, sy]) -// sy = sx if not defined -// -SvgPath.prototype.rotate = function (angle, rx, ry) { - this.__stack.push(matrix().rotate(angle, rx || 0, ry || 0)); - return this; -}; +var paramCounts = { a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0 }; +var SPECIAL_SPACES = [ + 0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, + 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF +]; -// Skew path along the X axis by `degrees` angle -// -SvgPath.prototype.skewX = function (degrees) { - this.__stack.push(matrix().skewX(degrees)); - return this; -}; +function isSpace(ch) { + return (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch === 0x2029) || // Line terminators + // White spaces + (ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0) || + (ch >= 0x1680 && SPECIAL_SPACES.indexOf(ch) >= 0); +} +function isCommand(code) { + /*eslint-disable no-bitwise*/ + switch (code | 0x20) { + case 0x6D/* m */: + case 0x7A/* z */: + case 0x6C/* l */: + case 0x68/* h */: + case 0x76/* v */: + case 0x63/* c */: + case 0x73/* s */: + case 0x71/* q */: + case 0x74/* t */: + case 0x61/* a */: + case 0x72/* r */: + return true; + } + return false; +} -// Skew path along the Y axis by `degrees` angle -// -SvgPath.prototype.skewY = function (degrees) { - this.__stack.push(matrix().skewY(degrees)); - return this; -}; +function isDigit(code) { + return (code >= 48 && code <= 57); // 0..9 +} +function isDigitStart(code) { + return (code >= 48 && code <= 57) || /* 0..9 */ + code === 0x2B || /* + */ + code === 0x2D || /* - */ + code === 0x2E; /* . */ +} -// Apply matrix transform (array of 6 elements) -// -SvgPath.prototype.matrix = function (m) { - this.__stack.push(matrix().matrix(m)); - return this; -}; +function State(path) { + this.index = 0; + this.path = path; + this.max = path.length; + this.result = []; + this.param = 0.0; + this.err = ''; + this.segmentStart = 0; + this.data = []; +} -// Transform path according to "transform" attr of SVG spec -// -SvgPath.prototype.transform = function (transformString) { - if (!transformString.trim()) { - return this; +function skipSpaces(state) { + while (state.index < state.max && isSpace(state.path.charCodeAt(state.index))) { + state.index++; } - this.__stack.push(transformParse(transformString)); - return this; -}; +} -// Round coords with given decimal precition. -// 0 by default (to integers) -// -SvgPath.prototype.round = function (d) { - var contourStartDeltaX = 0, contourStartDeltaY = 0, deltaX = 0, deltaY = 0, l; +function scanParam(state) { + var start = state.index, + index = start, + max = state.max, + zeroFirst = false, + hasCeiling = false, + hasDecimal = false, + hasDot = false, + ch; - d = d || 0; + if (index >= max) { + state.err = 'SvgPath: missed param (at pos ' + index + ')'; + return; + } + ch = state.path.charCodeAt(index); - this.__evaluateStack(); + if (ch === 0x2B/* + */ || ch === 0x2D/* - */) { + index++; + ch = (index < max) ? state.path.charCodeAt(index) : 0; + } - this.segments.forEach(function (s) { - var isRelative = (s[0].toLowerCase() === s[0]); + // This logic is shamelessly borrowed from Esprima + // https://github.com/ariya/esprimas + // + if (!isDigit(ch) && ch !== 0x2E/* . */) { + state.err = 'SvgPath: param should start with 0..9 or `.` (at pos ' + index + ')'; + return; + } - switch (s[0]) { - case 'H': - case 'h': - if (isRelative) { s[1] += deltaX; } - deltaX = s[1] - s[1].toFixed(d); - s[1] = +s[1].toFixed(d); - return; + if (ch !== 0x2E/* . */) { + zeroFirst = (ch === 0x30/* 0 */); + index++; - case 'V': - case 'v': - if (isRelative) { s[1] += deltaY; } - deltaY = s[1] - s[1].toFixed(d); - s[1] = +s[1].toFixed(d); - return; + ch = (index < max) ? state.path.charCodeAt(index) : 0; - case 'Z': - case 'z': - deltaX = contourStartDeltaX; - deltaY = contourStartDeltaY; + if (zeroFirst && index < max) { + // decimal number starts with '0' such as '09' is illegal. + if (ch && isDigit(ch)) { + state.err = 'SvgPath: numbers started with `0` such as `09` are ilegal (at pos ' + start + ')'; return; + } + } - case 'M': - case 'm': - if (isRelative) { - s[1] += deltaX; - s[2] += deltaY; - } - - deltaX = s[1] - s[1].toFixed(d); - deltaY = s[2] - s[2].toFixed(d); - - contourStartDeltaX = deltaX; - contourStartDeltaY = deltaY; + while (index < max && isDigit(state.path.charCodeAt(index))) { + index++; + hasCeiling = true; + } + ch = (index < max) ? state.path.charCodeAt(index) : 0; + } - s[1] = +s[1].toFixed(d); - s[2] = +s[2].toFixed(d); - return; + if (ch === 0x2E/* . */) { + hasDot = true; + index++; + while (isDigit(state.path.charCodeAt(index))) { + index++; + hasDecimal = true; + } + ch = (index < max) ? state.path.charCodeAt(index) : 0; + } - case 'A': - case 'a': - // [cmd, rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y] - if (isRelative) { - s[6] += deltaX; - s[7] += deltaY; - } + if (ch === 0x65/* e */ || ch === 0x45/* E */) { + if (hasDot && !hasCeiling && !hasDecimal) { + state.err = 'SvgPath: invalid float exponent (at pos ' + index + ')'; + return; + } - deltaX = s[6] - s[6].toFixed(d); - deltaY = s[7] - s[7].toFixed(d); + index++; - s[1] = +s[1].toFixed(d); - s[2] = +s[2].toFixed(d); - s[3] = +s[3].toFixed(d + 2); // better precision for rotation - s[6] = +s[6].toFixed(d); - s[7] = +s[7].toFixed(d); - return; + ch = (index < max) ? state.path.charCodeAt(index) : 0; + if (ch === 0x2B/* + */ || ch === 0x2D/* - */) { + index++; + } + if (index < max && isDigit(state.path.charCodeAt(index))) { + while (index < max && isDigit(state.path.charCodeAt(index))) { + index++; + } + } else { + state.err = 'SvgPath: invalid float exponent (at pos ' + index + ')'; + return; + } + } - default: - // a c l q s t - l = s.length; + state.index = index; + state.param = parseFloat(state.path.slice(start, index)) + 0.0; +} - if (isRelative) { - s[l - 2] += deltaX; - s[l - 1] += deltaY; - } - deltaX = s[l - 2] - s[l - 2].toFixed(d); - deltaY = s[l - 1] - s[l - 1].toFixed(d); +function finalizeSegment(state) { + var cmd, cmdLC; - s.forEach(function (val, i) { - if (!i) { return; } - s[i] = +s[i].toFixed(d); - }); - return; - } - }); + // Process duplicated commands (without comand name) - return this; -}; + // This logic is shamelessly borrowed from Raphael + // https://github.com/DmitryBaranovskiy/raphael/ + // + cmd = state.path[state.segmentStart]; + cmdLC = cmd.toLowerCase(); + var params = state.data; -// Apply iterator function to all segments. If function returns result, -// current segment will be replaced to array of returned segments. -// If empty array is returned, current regment will be deleted. -// -SvgPath.prototype.iterate = function (iterator, keepLazyStack) { - var segments = this.segments, - replacements = {}, - needReplace = false, - lastX = 0, - lastY = 0, - countourStartX = 0, - countourStartY = 0; - var i, j, newSegments; - - if (!keepLazyStack) { - this.__evaluateStack(); + if (cmdLC === 'm' && params.length > 2) { + state.result.push([ cmd, params[0], params[1] ]); + params = params.slice(2); + cmdLC = 'l'; + cmd = (cmd === 'm') ? 'l' : 'L'; } - segments.forEach(function (s, index) { - - var res = iterator(s, index, lastX, lastY); + if (cmdLC === 'r') { + state.result.push([ cmd ].concat(params)); + } else { - if (Array.isArray(res)) { - replacements[index] = res; - needReplace = true; + while (params.length >= paramCounts[cmdLC]) { + state.result.push([ cmd ].concat(params.splice(0, paramCounts[cmdLC]))); + if (!paramCounts[cmdLC]) { + break; + } } + } +} - var isRelative = (s[0] === s[0].toLowerCase()); - // calculate absolute X and Y - switch (s[0]) { - case 'm': - case 'M': - lastX = s[1] + (isRelative ? lastX : 0); - lastY = s[2] + (isRelative ? lastY : 0); - countourStartX = lastX; - countourStartY = lastY; - return; +function scanSegment(state) { + var max = state.max, + cmdCode, comma_found, need_params, i; - case 'h': - case 'H': - lastX = s[1] + (isRelative ? lastX : 0); - return; + state.segmentStart = state.index; + cmdCode = state.path.charCodeAt(state.index); - case 'v': - case 'V': - lastY = s[1] + (isRelative ? lastY : 0); - return; + if (!isCommand(cmdCode)) { + state.err = 'SvgPath: bad command ' + state.path[state.index] + ' (at pos ' + state.index + ')'; + return; + } - case 'z': - case 'Z': - // That make sence for multiple contours - lastX = countourStartX; - lastY = countourStartY; - return; + need_params = paramCounts[state.path[state.index].toLowerCase()]; - default: - lastX = s[s.length - 2] + (isRelative ? lastX : 0); - lastY = s[s.length - 1] + (isRelative ? lastY : 0); - } - }); + state.index++; + skipSpaces(state); - // Replace segments if iterator return results + state.data = []; - if (!needReplace) { return this; } + if (!need_params) { + // Z + finalizeSegment(state); + return; + } - newSegments = []; + comma_found = false; - for (i = 0; i < segments.length; i++) { - if (typeof replacements[i] !== 'undefined') { - for (j = 0; j < replacements[i].length; j++) { - newSegments.push(replacements[i][j]); + for (;;) { + for (i = need_params; i > 0; i--) { + scanParam(state); + if (state.err.length) { + return; + } + state.data.push(state.param); + + skipSpaces(state); + comma_found = false; + + if (state.index < max && state.path.charCodeAt(state.index) === 0x2C/* , */) { + state.index++; + skipSpaces(state); + comma_found = true; } - } else { - newSegments.push(segments[i]); } - } - this.segments = newSegments; + // after ',' param is mandatory + if (comma_found) { + continue; + } - return this; -}; + if (state.index >= state.max) { + break; + } + // Stop on next segment + if (!isDigitStart(state.path.charCodeAt(state.index))) { + break; + } + } -// Converts segments from relative to absolute -// -SvgPath.prototype.abs = function () { + finalizeSegment(state); +} - this.iterate(function (s, index, x, y) { - var name = s[0], - nameUC = name.toUpperCase(), - i; - // Skip absolute commands - if (name === nameUC) { return; } +/* Returns array of segments: + * + * [ + * [ command, coord1, coord2, ... ] + * ] + */ +module.exports = function pathParse(svgPath) { + var state = new State(svgPath); + var max = state.max; - s[0] = nameUC; + skipSpaces(state); - switch (name) { - case 'v': - // v has shifted coords parity - s[1] += y; - return; + while (state.index < max && !state.err.length) { + scanSegment(state); + } - case 'a': - // ARC is: ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y] - // touch x, y only - s[6] += x; - s[7] += y; - return; + if (state.err.length) { + state.result = []; - default: - for (i = 1; i < s.length; i++) { - s[i] += i % 2 ? x : y; // odd values are X, even - Y - } + } else if (state.result.length) { + + if ('mM'.indexOf(state.result[0][0]) < 0) { + state.err = 'SvgPath: string should start with `M` or `m`'; + state.result = []; + } else { + state.result[0][0] = 'M'; } - }, true); + } - return this; + return { + err: state.err, + segments: state.result + }; }; - -// Converts segments from absolute to relative +},{}],8:[function(require,module,exports){ +// SVG Path transformations library +// +// Usage: +// +// SvgPath('...') +// .translate(-150, -100) +// .scale(0.5) +// .translate(-150, -100) +// .toFixed(1) +// .toString() // -SvgPath.prototype.rel = function () { - this.iterate(function (s, index, x, y) { - var name = s[0], - nameLC = name.toLowerCase(), - i; +'use strict'; - // Skip relative commands - if (name === nameLC) { return; } - // Don't touch the first M to avoid potential confusions. - if (index === 0 && name === 'M') { return; } +var pathParse = require('./path_parse'); +var transformParse = require('./transform_parse'); +var matrix = require('./matrix'); +var a2c = require('./a2c'); +var ellipse = require('./ellipse'); - s[0] = nameLC; - switch (name) { - case 'V': - // V has shifted coords parity - s[1] -= y; - return; +// Class constructor +// +function SvgPath(path) { + if (!(this instanceof SvgPath)) { return new SvgPath(path); } - case 'A': - // ARC is: ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y] - // touch x, y only - s[6] -= x; - s[7] -= y; - return; + var pstate = pathParse(path); - default: - for (i = 1; i < s.length; i++) { - s[i] -= i % 2 ? x : y; // odd values are X, even - Y - } - } - }, true); + // Array of path segments. + // Each segment is array [command, param1, param2, ...] + this.segments = pstate.segments; - return this; -}; + // Error message on parse error. + this.err = pstate.err; + + // Transforms stack for lazy evaluation + this.__stack = []; +} -// Converts arcs to cubic bézier curves -// -SvgPath.prototype.unarc = function () { +SvgPath.prototype.__matrix = function (m) { + var self = this, i; + + // Quick leave for empty matrix + if (!m.queue.length) { return; } + this.iterate(function (s, index, x, y) { - var new_segments, nextX, nextY, result = [], name = s[0]; + var p, result, name, isRelative; - // Skip anything except arcs - if (name !== 'A' && name !== 'a') { return null; } + switch (s[0]) { - if (name === 'a') { - // convert relative arc coordinates to absolute - nextX = x + s[6]; - nextY = y + s[7]; - } else { - nextX = s[6]; - nextY = s[7]; - } + // Process 'assymetric' commands separately + case 'v': + p = m.calc(0, s[1], true); + result = (p[0] === 0) ? [ 'v', p[1] ] : [ 'l', p[0], p[1] ]; + break; - new_segments = a2c(x, y, nextX, nextY, s[4], s[5], s[1], s[2], s[3]); + case 'V': + p = m.calc(x, s[1], false); + result = (p[0] === m.calc(x, y, false)[0]) ? [ 'V', p[1] ] : [ 'L', p[0], p[1] ]; + break; - // Degenerated arcs can be ignored by renderer, but should not be dropped - // to avoid collisions with `S A S` and so on. Replace with empty line. - if (new_segments.length === 0) { - return [ [ s[0] === 'a' ? 'l' : 'L', s[6], s[7] ] ]; - } + case 'h': + p = m.calc(s[1], 0, true); + result = (p[1] === 0) ? [ 'h', p[0] ] : [ 'l', p[0], p[1] ]; + break; - new_segments.forEach(function (s) { - result.push([ 'C', s[2], s[3], s[4], s[5], s[6], s[7] ]); - }); + case 'H': + p = m.calc(s[1], y, false); + result = (p[1] === m.calc(x, y, false)[1]) ? [ 'H', p[0] ] : [ 'L', p[0], p[1] ]; + break; - return result; - }); + case 'a': + case 'A': + // ARC is: ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y] - return this; -}; + // Drop segment if arc is empty (end point === start point) + /*if ((s[0] === 'A' && s[6] === x && s[7] === y) || + (s[0] === 'a' && s[6] === 0 && s[7] === 0)) { + return []; + }*/ + // Transform rx, ry and the x-axis-rotation + var ma = m.toArray(); + var e = ellipse(s[1], s[2], s[3]).transform(ma); -// Converts smooth curves (with missed control point) to generic curves -// -SvgPath.prototype.unshort = function () { - var segments = this.segments; - var prevControlX, prevControlY, prevSegment; - var curControlX, curControlY; + // flip sweep-flag if matrix is not orientation-preserving + if (ma[0] * ma[3] - ma[1] * ma[2] < 0) { + s[5] = s[5] ? '0' : '1'; + } - // TODO: add lazy evaluation flag when relative commands supported + // Transform end point as usual (without translation for relative notation) + p = m.calc(s[6], s[7], s[0] === 'a'); - this.iterate(function (s, idx, x, y) { - var name = s[0], nameUC = name.toUpperCase(), isRelative; + // Empty arcs can be ignored by renderer, but should not be dropped + // to avoid collisions with `S A S` and so on. Replace with empty line. + if ((s[0] === 'A' && s[6] === x && s[7] === y) || + (s[0] === 'a' && s[6] === 0 && s[7] === 0)) { + result = [ s[0] === 'a' ? 'l' : 'L', p[0], p[1] ]; + break; + } - // First command MUST be M|m, it's safe to skip. - // Protect from access to [-1] for sure. - if (!idx) { return; } + // if the resulting ellipse is (almost) a segment ... + if (e.isDegenerate()) { + // replace the arc by a line + result = [ s[0] === 'a' ? 'l' : 'L', p[0], p[1] ]; + } else { + // if it is a real ellipse + // s[0], s[4] and s[5] are not modified + result = [ s[0], e.rx, e.ry, e.ax, s[4], s[5], p[0], p[1] ]; + } - if (nameUC === 'T') { // quadratic curve - isRelative = (name === 't'); + break; - prevSegment = segments[idx - 1]; + case 'm': + // Edge case. The very first `m` should be processed as absolute, if happens. + // Make sense for coord shift transforms. + isRelative = index > 0; - if (prevSegment[0] === 'Q') { - prevControlX = prevSegment[1] - x; - prevControlY = prevSegment[2] - y; - } else if (prevSegment[0] === 'q') { - prevControlX = prevSegment[1] - prevSegment[3]; - prevControlY = prevSegment[2] - prevSegment[4]; - } else { - prevControlX = 0; - prevControlY = 0; - } + p = m.calc(s[1], s[2], isRelative); + result = [ 'm', p[0], p[1] ]; + break; - curControlX = -prevControlX; - curControlY = -prevControlY; + default: + name = s[0]; + result = [ name ]; + isRelative = (name.toLowerCase() === name); - if (!isRelative) { - curControlX += x; - curControlY += y; - } + // Apply transformations to the segment + for (i = 1; i < s.length; i += 2) { + p = m.calc(s[i], s[i + 1], isRelative); + result.push(p[0], p[1]); + } + } - segments[idx] = [ - isRelative ? 'q' : 'Q', - curControlX, curControlY, - s[1], s[2] - ]; + self.segments[index] = result; + }, true); +}; - } else if (nameUC === 'S') { // cubic curve - isRelative = (name === 's'); - prevSegment = segments[idx - 1]; +// Apply stacked commands +// +SvgPath.prototype.__evaluateStack = function () { + var m, i; - if (prevSegment[0] === 'C') { - prevControlX = prevSegment[3] - x; - prevControlY = prevSegment[4] - y; - } else if (prevSegment[0] === 'c') { - prevControlX = prevSegment[3] - prevSegment[5]; - prevControlY = prevSegment[4] - prevSegment[6]; - } else { - prevControlX = 0; - prevControlY = 0; - } + if (!this.__stack.length) { return; } - curControlX = -prevControlX; - curControlY = -prevControlY; + if (this.__stack.length === 1) { + this.__matrix(this.__stack[0]); + this.__stack = []; + return; + } - if (!isRelative) { - curControlX += x; - curControlY += y; - } + m = matrix(); + i = this.__stack.length; - segments[idx] = [ - isRelative ? 'c' : 'C', - curControlX, curControlY, - s[1], s[2], s[3], s[4] - ]; - } - }); + while (--i >= 0) { + m.matrix(this.__stack[i].toArray()); + } - return this; + this.__matrix(m); + this.__stack = []; }; -module.exports = SvgPath; - -},{"./a2c":2,"./ellipse":3,"./matrix":4,"./path_parse":5,"./transform_parse":7}],7:[function(require,module,exports){ -'use strict'; +// Convert processed SVG Path back to string +// +SvgPath.prototype.toString = function () { + var elements = [], skipCmd, cmd; + this.__evaluateStack(); -var Matrix = require('./matrix'); + for (var i = 0; i < this.segments.length; i++) { + // remove repeating commands names + cmd = this.segments[i][0]; + skipCmd = i > 0 && cmd !== 'm' && cmd !== 'M' && cmd === this.segments[i - 1][0]; + elements = elements.concat(skipCmd ? this.segments[i].slice(1) : this.segments[i]); + } -var operations = { - matrix: true, - scale: true, - rotate: true, - translate: true, - skewX: true, - skewY: true + return elements.join(' ') + // Optimizations: remove spaces around commands & before `-` + // + // We could also remove leading zeros for `0.5`-like values, + // but their count is too small to spend time for. + .replace(/ ?([achlmqrstvz]) ?/gi, '$1') + .replace(/ \-/g, '-') + // workaround for FontForge SVG importing bug + .replace(/zm/g, 'z m'); }; -var CMD_SPLIT_RE = /\s*(matrix|translate|scale|rotate|skewX|skewY)\s*\(\s*(.+?)\s*\)[\s,]*/; -var PARAMS_SPLIT_RE = /[\s,]+/; +// Translate path to (x [, y]) +// +SvgPath.prototype.translate = function (x, y) { + this.__stack.push(matrix().translate(x, y || 0)); + return this; +}; -module.exports = function transformParse(transformString) { - var matrix = new Matrix(); - var cmd, params; - // Split value into ['', 'translate', '10 50', '', 'scale', '2', '', 'rotate', '-45', ''] - transformString.split(CMD_SPLIT_RE).forEach(function (item) { +// Scale path to (sx [, sy]) +// sy = sx if not defined +// +SvgPath.prototype.scale = function (sx, sy) { + this.__stack.push(matrix().scale(sx, (!sy && (sy !== 0)) ? sx : sy)); + return this; +}; - // Skip empty elements - if (!item.length) { return; } - // remember operation - if (typeof operations[item] !== 'undefined') { - cmd = item; - return; - } +// Rotate path around point (sx [, sy]) +// sy = sx if not defined +// +SvgPath.prototype.rotate = function (angle, rx, ry) { + this.__stack.push(matrix().rotate(angle, rx || 0, ry || 0)); + return this; +}; - // extract params & att operation to matrix - params = item.split(PARAMS_SPLIT_RE).map(function (i) { - return +i || 0; - }); - // If params count is not correct - ignore command - switch (cmd) { - case 'matrix': - if (params.length === 6) { - matrix.matrix(params); - } - return; +// Skew path along the X axis by `degrees` angle +// +SvgPath.prototype.skewX = function (degrees) { + this.__stack.push(matrix().skewX(degrees)); + return this; +}; - case 'scale': - if (params.length === 1) { - matrix.scale(params[0], params[0]); - } else if (params.length === 2) { - matrix.scale(params[0], params[1]); - } - return; - case 'rotate': - if (params.length === 1) { - matrix.rotate(params[0], 0, 0); - } else if (params.length === 3) { - matrix.rotate(params[0], params[1], params[2]); - } - return; +// Skew path along the Y axis by `degrees` angle +// +SvgPath.prototype.skewY = function (degrees) { + this.__stack.push(matrix().skewY(degrees)); + return this; +}; - case 'translate': - if (params.length === 1) { - matrix.translate(params[0], 0); - } else if (params.length === 2) { - matrix.translate(params[0], params[1]); - } - return; - case 'skewX': - if (params.length === 1) { - matrix.skewX(params[0]); - } - return; +// Apply matrix transform (array of 6 elements) +// +SvgPath.prototype.matrix = function (m) { + this.__stack.push(matrix().matrix(m)); + return this; +}; - case 'skewY': - if (params.length === 1) { - matrix.skewY(params[0]); - } - return; - } - }); - return matrix; +// Transform path according to "transform" attr of SVG spec +// +SvgPath.prototype.transform = function (transformString) { + if (!transformString.trim()) { + return this; + } + this.__stack.push(transformParse(transformString)); + return this; }; -},{"./matrix":4}],8:[function(require,module,exports){ -/*! https://mths.be/cssesc v1.0.1 by @mathias */ -'use strict'; -var object = {}; -var hasOwnProperty = object.hasOwnProperty; -var merge = function merge(options, defaults) { - if (!options) { - return defaults; - } - var result = {}; - for (var key in defaults) { - // `if (defaults.hasOwnProperty(key) { … }` is not needed here, since - // only recognized option names are used. - result[key] = hasOwnProperty.call(options, key) ? options[key] : defaults[key]; - } - return result; -}; +// Round coords with given decimal precition. +// 0 by default (to integers) +// +SvgPath.prototype.round = function (d) { + var contourStartDeltaX = 0, contourStartDeltaY = 0, deltaX = 0, deltaY = 0, l; -var regexAnySingleEscape = /[ -,\.\/;-@\[-\^`\{-~]/; -var regexSingleEscape = /[ -,\.\/;-@\[\]\^`\{-~]/; -var regexAlwaysEscape = /['"\\]/; -var regexExcessiveSpaces = /(^|\\+)?(\\[A-F0-9]{1,6})\x20(?![a-fA-F0-9\x20])/g; + d = d || 0; -// https://mathiasbynens.be/notes/css-escapes#css -var cssesc = function cssesc(string, options) { - options = merge(options, cssesc.options); - if (options.quotes != 'single' && options.quotes != 'double') { - options.quotes = 'single'; - } - var quote = options.quotes == 'double' ? '"' : '\''; - var isIdentifier = options.isIdentifier; + this.__evaluateStack(); - var firstChar = string.charAt(0); - var output = ''; - var counter = 0; - var length = string.length; - while (counter < length) { - var character = string.charAt(counter++); - var codePoint = character.charCodeAt(); - var value = void 0; - // If it’s not a printable ASCII character… - if (codePoint < 0x20 || codePoint > 0x7E) { - if (codePoint >= 0xD800 && codePoint <= 0xDBFF && counter < length) { - // It’s a high surrogate, and there is a next character. - var extra = string.charCodeAt(counter++); - if ((extra & 0xFC00) == 0xDC00) { - // next character is low surrogate - codePoint = ((codePoint & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000; - } else { - // It’s an unmatched surrogate; only append this code unit, in case - // the next code unit is the high surrogate of a surrogate pair. - counter--; - } - } - value = '\\' + codePoint.toString(16).toUpperCase() + ' '; - } else { - if (options.escapeEverything) { - if (regexAnySingleEscape.test(character)) { - value = '\\' + character; - } else { - value = '\\' + codePoint.toString(16).toUpperCase() + ' '; - } - // Note: `:` could be escaped as `\:`, but that fails in IE < 8. - } else if (/[\t\n\f\r\x0B:]/.test(character)) { - if (!isIdentifier && character == ':') { - value = character; - } else { - value = '\\' + codePoint.toString(16).toUpperCase() + ' '; - } - } else if (character == '\\' || !isIdentifier && (character == '"' && quote == character || character == '\'' && quote == character) || isIdentifier && regexSingleEscape.test(character)) { - value = '\\' + character; - } else { - value = character; - } - } - output += value; - } + this.segments.forEach(function (s) { + var isRelative = (s[0].toLowerCase() === s[0]); + + switch (s[0]) { + case 'H': + case 'h': + if (isRelative) { s[1] += deltaX; } + deltaX = s[1] - s[1].toFixed(d); + s[1] = +s[1].toFixed(d); + return; + + case 'V': + case 'v': + if (isRelative) { s[1] += deltaY; } + deltaY = s[1] - s[1].toFixed(d); + s[1] = +s[1].toFixed(d); + return; - if (isIdentifier) { - if (/^_/.test(output)) { - // Prevent IE6 from ignoring the rule altogether (in case this is for an - // identifier used as a selector) - output = '\\_' + output.slice(1); - } else if (/^-[-\d]/.test(output)) { - output = '\\-' + output.slice(1); - } else if (/\d/.test(firstChar)) { - output = '\\3' + firstChar + ' ' + output.slice(1); - } - } + case 'Z': + case 'z': + deltaX = contourStartDeltaX; + deltaY = contourStartDeltaY; + return; - // Remove spaces after `\HEX` escapes that are not followed by a hex digit, - // since they’re redundant. Note that this is only possible if the escape - // sequence isn’t preceded by an odd number of backslashes. - output = output.replace(regexExcessiveSpaces, function ($0, $1, $2) { - if ($1 && $1.length % 2) { - // It’s not safe to remove the space, so don’t. - return $0; - } - // Strip the space. - return ($1 || '') + $2; - }); + case 'M': + case 'm': + if (isRelative) { + s[1] += deltaX; + s[2] += deltaY; + } - if (!isIdentifier && options.wrap) { - return quote + output + quote; - } - return output; -}; + deltaX = s[1] - s[1].toFixed(d); + deltaY = s[2] - s[2].toFixed(d); -// Expose default options (so they can be overridden globally). -cssesc.options = { - 'escapeEverything': false, - 'isIdentifier': false, - 'quotes': 'single', - 'wrap': false -}; + contourStartDeltaX = deltaX; + contourStartDeltaY = deltaY; -cssesc.version = '1.0.1'; + s[1] = +s[1].toFixed(d); + s[2] = +s[2].toFixed(d); + return; -module.exports = cssesc; + case 'A': + case 'a': + // [cmd, rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y] + if (isRelative) { + s[6] += deltaX; + s[7] += deltaY; + } -},{}],9:[function(require,module,exports){ -// parse -// ===== + deltaX = s[6] - s[6].toFixed(d); + deltaY = s[7] - s[7].toFixed(d); -// states -// ------ + s[1] = +s[1].toFixed(d); + s[2] = +s[2].toFixed(d); + s[3] = +s[3].toFixed(d + 2); // better precision for rotation + s[6] = +s[6].toFixed(d); + s[7] = +s[7].toFixed(d); + return; -var PLAIN = 0; -var STRINGS = 1; -var ESCAPING = 2; -var IDENTIFIER = 3; -var SEPARATING = 4; + default: + // a c l q s t + l = s.length; -// patterns -// -------- + if (isRelative) { + s[l - 2] += deltaX; + s[l - 1] += deltaY; + } -var identifierPattern = /[a-z0-9_-]/i; -var spacePattern = /[\s\t]/; + deltaX = s[l - 2] - s[l - 2].toFixed(d); + deltaY = s[l - 1] - s[l - 1].toFixed(d); -// --- + s.forEach(function (val, i) { + if (!i) { return; } + s[i] = +s[i].toFixed(d); + }); + return; + } + }); -var parse = function(str) { + return this; +}; - // vars - // ---- - var starting = true; - var state = PLAIN; - var buffer = ''; - var i = 0; - var quote; - var c; +// Apply iterator function to all segments. If function returns result, +// current segment will be replaced to array of returned segments. +// If empty array is returned, current regment will be deleted. +// +SvgPath.prototype.iterate = function (iterator, keepLazyStack) { + var segments = this.segments, + replacements = {}, + needReplace = false, + lastX = 0, + lastY = 0, + countourStartX = 0, + countourStartY = 0; + var i, j, newSegments; - // result - // ------ + if (!keepLazyStack) { + this.__evaluateStack(); + } - var names = []; + segments.forEach(function (s, index) { - // parse - // ----- + var res = iterator(s, index, lastX, lastY); - while (true) { + if (Array.isArray(res)) { + replacements[index] = res; + needReplace = true; + } - c = str[i]; + var isRelative = (s[0] === s[0].toLowerCase()); - if (state === PLAIN) { + // calculate absolute X and Y + switch (s[0]) { + case 'm': + case 'M': + lastX = s[1] + (isRelative ? lastX : 0); + lastY = s[2] + (isRelative ? lastY : 0); + countourStartX = lastX; + countourStartY = lastY; + return; - if (!c && starting) { + case 'h': + case 'H': + lastX = s[1] + (isRelative ? lastX : 0); + return; - break; + case 'v': + case 'V': + lastY = s[1] + (isRelative ? lastY : 0); + return; - } else if (!c && !starting) { + case 'z': + case 'Z': + // That make sence for multiple contours + lastX = countourStartX; + lastY = countourStartY; + return; - throw new Error('Parse error'); + default: + lastX = s[s.length - 2] + (isRelative ? lastX : 0); + lastY = s[s.length - 1] + (isRelative ? lastY : 0); + } + }); - } else if (c === '"' || c === "'") { + // Replace segments if iterator return results - quote = c; - state = STRINGS; - starting = false; + if (!needReplace) { return this; } - } else if (spacePattern.test(c)) { - } else if (identifierPattern.test(c)) { + newSegments = []; - state = IDENTIFIER; - starting = false; - i--; + for (i = 0; i < segments.length; i++) { + if (typeof replacements[i] !== 'undefined') { + for (j = 0; j < replacements[i].length; j++) { + newSegments.push(replacements[i][j]); + } + } else { + newSegments.push(segments[i]); + } + } - } else { + this.segments = newSegments; - throw new Error('Parse error'); + return this; +}; - } - } else if (state === STRINGS) { +// Converts segments from relative to absolute +// +SvgPath.prototype.abs = function () { - if (!c) { + this.iterate(function (s, index, x, y) { + var name = s[0], + nameUC = name.toUpperCase(), + i; - throw new Error('Parse Error'); + // Skip absolute commands + if (name === nameUC) { return; } - } else if (c === "\\") { + s[0] = nameUC; - state = ESCAPING; + switch (name) { + case 'v': + // v has shifted coords parity + s[1] += y; + return; - } else if (c === quote) { + case 'a': + // ARC is: ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y] + // touch x, y only + s[6] += x; + s[7] += y; + return; - names.push(buffer); - buffer = ''; - state = SEPARATING; + default: + for (i = 1; i < s.length; i++) { + s[i] += i % 2 ? x : y; // odd values are X, even - Y + } + } + }, true); + + return this; +}; + + +// Converts segments from absolute to relative +// +SvgPath.prototype.rel = function () { + + this.iterate(function (s, index, x, y) { + var name = s[0], + nameLC = name.toLowerCase(), + i; + + // Skip relative commands + if (name === nameLC) { return; } + + // Don't touch the first M to avoid potential confusions. + if (index === 0 && name === 'M') { return; } - } else { + s[0] = nameLC; - buffer += c; + switch (name) { + case 'V': + // V has shifted coords parity + s[1] -= y; + return; - } + case 'A': + // ARC is: ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y] + // touch x, y only + s[6] -= x; + s[7] -= y; + return; - } else if (state === ESCAPING) { + default: + for (i = 1; i < s.length; i++) { + s[i] -= i % 2 ? x : y; // odd values are X, even - Y + } + } + }, true); - if (c === quote || c === "\\") { + return this; +}; - buffer += c; - state = STRINGS; - } else { +// Converts arcs to cubic bézier curves +// +SvgPath.prototype.unarc = function () { + this.iterate(function (s, index, x, y) { + var new_segments, nextX, nextY, result = [], name = s[0]; - throw new Error('Parse error'); + // Skip anything except arcs + if (name !== 'A' && name !== 'a') { return null; } - } + if (name === 'a') { + // convert relative arc coordinates to absolute + nextX = x + s[6]; + nextY = y + s[7]; + } else { + nextX = s[6]; + nextY = s[7]; + } - } else if (state === IDENTIFIER) { + new_segments = a2c(x, y, nextX, nextY, s[4], s[5], s[1], s[2], s[3]); - if (!c) { + // Degenerated arcs can be ignored by renderer, but should not be dropped + // to avoid collisions with `S A S` and so on. Replace with empty line. + if (new_segments.length === 0) { + return [ [ s[0] === 'a' ? 'l' : 'L', s[6], s[7] ] ]; + } - names.push(buffer); - break; + new_segments.forEach(function (s) { + result.push([ 'C', s[2], s[3], s[4], s[5], s[6], s[7] ]); + }); - } else if (identifierPattern.test(c)) { + return result; + }); - buffer += c; + return this; +}; - } else if (c === ',') { - names.push(buffer); - buffer = ''; - state = PLAIN; +// Converts smooth curves (with missed control point) to generic curves +// +SvgPath.prototype.unshort = function () { + var segments = this.segments; + var prevControlX, prevControlY, prevSegment; + var curControlX, curControlY; - } else if (spacePattern.test(c)) { + // TODO: add lazy evaluation flag when relative commands supported - names.push(buffer); - buffer = ''; - state = SEPARATING; + this.iterate(function (s, idx, x, y) { + var name = s[0], nameUC = name.toUpperCase(), isRelative; - } else { + // First command MUST be M|m, it's safe to skip. + // Protect from access to [-1] for sure. + if (!idx) { return; } - throw new Error('Parse error'); + if (nameUC === 'T') { // quadratic curve + isRelative = (name === 't'); + prevSegment = segments[idx - 1]; + + if (prevSegment[0] === 'Q') { + prevControlX = prevSegment[1] - x; + prevControlY = prevSegment[2] - y; + } else if (prevSegment[0] === 'q') { + prevControlX = prevSegment[1] - prevSegment[3]; + prevControlY = prevSegment[2] - prevSegment[4]; + } else { + prevControlX = 0; + prevControlY = 0; } - } else if (state === SEPARATING) { + curControlX = -prevControlX; + curControlY = -prevControlY; - if (!c) { + if (!isRelative) { + curControlX += x; + curControlY += y; + } - break; + segments[idx] = [ + isRelative ? 'q' : 'Q', + curControlX, curControlY, + s[1], s[2] + ]; - } else if (c === ',') { + } else if (nameUC === 'S') { // cubic curve + isRelative = (name === 's'); - state = PLAIN; + prevSegment = segments[idx - 1]; - } else if (spacePattern.test(c)) { + if (prevSegment[0] === 'C') { + prevControlX = prevSegment[3] - x; + prevControlY = prevSegment[4] - y; + } else if (prevSegment[0] === 'c') { + prevControlX = prevSegment[3] - prevSegment[5]; + prevControlY = prevSegment[4] - prevSegment[6]; } else { + prevControlX = 0; + prevControlY = 0; + } - throw new Error('Parse error'); + curControlX = -prevControlX; + curControlY = -prevControlY; + if (!isRelative) { + curControlX += x; + curControlY += y; } + segments[idx] = [ + isRelative ? 'c' : 'C', + curControlX, curControlY, + s[1], s[2], s[3], s[4] + ]; } + }); - i++; + return this; +}; - } - // result - // ------ +module.exports = SvgPath; - return names; +},{"./a2c":4,"./ellipse":5,"./matrix":6,"./path_parse":7,"./transform_parse":9}],9:[function(require,module,exports){ +'use strict'; -}; -// stringify -// ========= +var Matrix = require('./matrix'); -// pattern -// ------- +var operations = { + matrix: true, + scale: true, + rotate: true, + translate: true, + skewX: true, + skewY: true +}; -var stringsPattern = /[^a-z0-9_-]/i; +var CMD_SPLIT_RE = /\s*(matrix|translate|scale|rotate|skewX|skewY)\s*\(\s*(.+?)\s*\)[\s,]*/; +var PARAMS_SPLIT_RE = /[\s,]+/; -// --- -var stringify = function(names, options) { +module.exports = function transformParse(transformString) { + var matrix = new Matrix(); + var cmd, params; - // quote - // ----- + // Split value into ['', 'translate', '10 50', '', 'scale', '2', '', 'rotate', '-45', ''] + transformString.split(CMD_SPLIT_RE).forEach(function (item) { - var quote = options && options.quote || '"'; - if (quote !== '"' && quote !== "'") { - throw new Error('Quote must be `\'` or `"`'); - } - var quotePattern = new RegExp(quote, 'g'); + // Skip empty elements + if (!item.length) { return; } - // stringify - // --------- + // remember operation + if (typeof operations[item] !== 'undefined') { + cmd = item; + return; + } - var safeNames = []; + // extract params & att operation to matrix + params = item.split(PARAMS_SPLIT_RE).map(function (i) { + return +i || 0; + }); - for (var i = 0; i < names.length; ++i) { - var name = names[i]; + // If params count is not correct - ignore command + switch (cmd) { + case 'matrix': + if (params.length === 6) { + matrix.matrix(params); + } + return; - if (stringsPattern.test(name)) { - name = name - .replace(/\\/g, "\\\\") - .replace(quotePattern, "\\" + quote); - name = quote + name + quote; - } - safeNames.push(name); - } + case 'scale': + if (params.length === 1) { + matrix.scale(params[0], params[0]); + } else if (params.length === 2) { + matrix.scale(params[0], params[1]); + } + return; - // result - // ------ + case 'rotate': + if (params.length === 1) { + matrix.rotate(params[0], 0, 0); + } else if (params.length === 3) { + matrix.rotate(params[0], params[1], params[2]); + } + return; - return safeNames.join(', '); -}; + case 'translate': + if (params.length === 1) { + matrix.translate(params[0], 0); + } else if (params.length === 2) { + matrix.translate(params[0], params[1]); + } + return; -// export -// ====== + case 'skewX': + if (params.length === 1) { + matrix.skewX(params[0]); + } + return; -module.exports = { - parse: parse, - stringify: stringify, + case 'skewY': + if (params.length === 1) { + matrix.skewY(params[0]); + } + return; + } + }); + + return matrix; }; -},{}],10:[function(require,module,exports){ +},{"./matrix":6}],10:[function(require,module,exports){ /** * A class to parse color values * @author Stoyan Stefanov @@ -2173,7 +2172,7 @@ SOFTWARE. // pathSegList is marked deprecated in chrome, so parse the d attribute manually if necessary var getPathSegList = function (node) { - var d = node.getAttribute("d"); + var d = node.getAttribute("d") || ""; // Replace arcs before path segment list is handled if (SvgPath) { @@ -2182,7 +2181,7 @@ SOFTWARE. } var pathSegList = node.pathSegList; - + if (pathSegList) { return pathSegList; } @@ -4381,7 +4380,7 @@ SOFTWARE. }); } else if (typeof module !== "undefined" && module.exports) { RGBColor = require("./rgbcolor.js"); - SvgPath = require("SvgPath"); + SvgPath = require("svgpath"); FontFamily = require("font-family"); cssEsc = require("cssesc"); module.exports = svg2pdf; @@ -4397,6 +4396,6 @@ SOFTWARE. return svg2pdf; }(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this)); -},{"./rgbcolor.js":10,"SvgPath":1,"cssesc":8,"font-family":9}]},{},[11])(11) +},{"./rgbcolor.js":10,"cssesc":1,"font-family":2,"svgpath":3}]},{},[11])(11) }); //# sourceMappingURL=svg2pdf.js.map diff --git a/dist/svg2pdf.min.js b/dist/svg2pdf.min.js index 45d9f78..cc88f7d 100644 --- a/dist/svg2pdf.min.js +++ b/dist/svg2pdf.min.js @@ -16,23 +16,22 @@ * * font-family: * license: MIT (http://opensource.org/licenses/MIT) - * author: Taro Hanamura + * author: Taro Hanamura (http://hanamurataro.com/) * homepage: https://github.com/hanamura/font-family * version: 0.2.0 * * svgpath: * license: MIT (http://opensource.org/licenses/MIT) - * homepage: https://github.com/fontello/svgpath#readme * version: 2.2.1 * * This header is generated by licensify (https://github.com/twada/licensify) */ -!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).svg2pdf=t()}}(function(){return function n(s,o,h){function c(e,t){if(!o[e]){if(!s[e]){var r="function"==typeof require&&require;if(!t&&r)return r(e,!0);if(u)return u(e,!0);var a=new Error("Cannot find module '"+e+"'");throw a.code="MODULE_NOT_FOUND",a}var i=o[e]={exports:{}};s[e][0].call(i.exports,function(t){return c(s[e][1][t]||t)},i,i.exports,n,s,o,h)}return o[e].exports}for(var u="function"==typeof require&&require,t=0;tMath.abs(c-n)?(c-i)/h:h/(c-n))/Math.PI,0<=this.ax?(this.rx=Math.sqrt(c),this.ry=Math.sqrt(u)):(this.ax+=90,this.rx=Math.sqrt(u),this.ry=Math.sqrt(c)),this},a.prototype.isDegenerate=function(){return this.rx=s[r]&&(t.result.push([e].concat(a.splice(0,s[r]))),s[r]););}function n(t){var e,r,a,i=t.max;if(t.segmentStart=t.index,function(t){switch(32|t){case 109:case 122:case 108:case 104:case 118:case 99:case 115:case 113:case 116:case 97:case 114:return!0}return!1}(t.path.charCodeAt(t.index)))if(r=s[t.path[t.index].toLowerCase()],t.index++,o(t),t.data=[],r){for(e=!1;;){for(a=r;0=t.max)break;if(!(48<=(n=t.path.charCodeAt(t.index))&&n<=57||43===n||45===n||46===n))break}}var n;u(t)}else u(t);else t.err="SvgPath: bad command "+t.path[t.index]+" (at pos "+t.index+")"}e.exports=function(t){var e=new i(t),r=e.max;for(o(e);e.indexMath.abs(c-n)?(c-i)/h:h/(c-n))/Math.PI,0<=this.ax?(this.rx=Math.sqrt(c),this.ry=Math.sqrt(u)):(this.ax+=90,this.rx=Math.sqrt(u),this.ry=Math.sqrt(c)),this},a.prototype.isDegenerate=function(){return this.rx=s[r]&&(t.result.push([e].concat(a.splice(0,s[r]))),s[r]););}function n(t){var e,r,a,i=t.max;if(t.segmentStart=t.index,function(t){switch(32|t){case 109:case 122:case 108:case 104:case 118:case 99:case 115:case 113:case 116:case 97:case 114:return!0}return!1}(t.path.charCodeAt(t.index)))if(r=s[t.path[t.index].toLowerCase()],t.index++,o(t),t.data=[],r){for(e=!1;;){for(a=r;0=t.max)break;if(!(48<=(n=t.path.charCodeAt(t.index))&&n<=57||43===n||45===n||46===n))break}}var n;u(t)}else u(t);else t.err="SvgPath: bad command "+t.path[t.index]+" (at pos "+t.index+")"}e.exports=function(t){var e=new i(t),r=e.max;for(o(e);e.index * @link http://www.phpied.com/rgb-color-parser-in-javascript/ * @license Use it if you like it */ -!function(t){function f(t){this.ok=!1,"#"==t.charAt(0)&&(t=t.substr(1,6)),t=(t=t.replace(/ /g,"")).toLowerCase();var u={aliceblue:"f0f8ff",antiquewhite:"faebd7",aqua:"00ffff",aquamarine:"7fffd4",azure:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"000000",blanchedalmond:"ffebcd",blue:"0000ff",blueviolet:"8a2be2",brown:"a52a2a",burlywood:"deb887",cadetblue:"5f9ea0",chartreuse:"7fff00",chocolate:"d2691e",coral:"ff7f50",cornflowerblue:"6495ed",cornsilk:"fff8dc",crimson:"dc143c",cyan:"00ffff",darkblue:"00008b",darkcyan:"008b8b",darkgoldenrod:"b8860b",darkgray:"a9a9a9",darkgreen:"006400",darkkhaki:"bdb76b",darkmagenta:"8b008b",darkolivegreen:"556b2f",darkorange:"ff8c00",darkorchid:"9932cc",darkred:"8b0000",darksalmon:"e9967a",darkseagreen:"8fbc8f",darkslateblue:"483d8b",darkslategray:"2f4f4f",darkturquoise:"00ced1",darkviolet:"9400d3",deeppink:"ff1493",deepskyblue:"00bfff",dimgray:"696969",dodgerblue:"1e90ff",feldspar:"d19275",firebrick:"b22222",floralwhite:"fffaf0",forestgreen:"228b22",fuchsia:"ff00ff",gainsboro:"dcdcdc",ghostwhite:"f8f8ff",gold:"ffd700",goldenrod:"daa520",gray:"808080",green:"008000",greenyellow:"adff2f",honeydew:"f0fff0",hotpink:"ff69b4",indianred:"cd5c5c",indigo:"4b0082",ivory:"fffff0",khaki:"f0e68c",lavender:"e6e6fa",lavenderblush:"fff0f5",lawngreen:"7cfc00",lemonchiffon:"fffacd",lightblue:"add8e6",lightcoral:"f08080",lightcyan:"e0ffff",lightgoldenrodyellow:"fafad2",lightgrey:"d3d3d3",lightgreen:"90ee90",lightpink:"ffb6c1",lightsalmon:"ffa07a",lightseagreen:"20b2aa",lightskyblue:"87cefa",lightslateblue:"8470ff",lightslategray:"778899",lightsteelblue:"b0c4de",lightyellow:"ffffe0",lime:"00ff00",limegreen:"32cd32",linen:"faf0e6",magenta:"ff00ff",maroon:"800000",mediumaquamarine:"66cdaa",mediumblue:"0000cd",mediumorchid:"ba55d3",mediumpurple:"9370d8",mediumseagreen:"3cb371",mediumslateblue:"7b68ee",mediumspringgreen:"00fa9a",mediumturquoise:"48d1cc",mediumvioletred:"c71585",midnightblue:"191970",mintcream:"f5fffa",mistyrose:"ffe4e1",moccasin:"ffe4b5",navajowhite:"ffdead",navy:"000080",oldlace:"fdf5e6",olive:"808000",olivedrab:"6b8e23",orange:"ffa500",orangered:"ff4500",orchid:"da70d6",palegoldenrod:"eee8aa",palegreen:"98fb98",paleturquoise:"afeeee",palevioletred:"d87093",papayawhip:"ffefd5",peachpuff:"ffdab9",peru:"cd853f",pink:"ffc0cb",plum:"dda0dd",powderblue:"b0e0e6",purple:"800080",red:"ff0000",rosybrown:"bc8f8f",royalblue:"4169e1",saddlebrown:"8b4513",salmon:"fa8072",sandybrown:"f4a460",seagreen:"2e8b57",seashell:"fff5ee",sienna:"a0522d",silver:"c0c0c0",skyblue:"87ceeb",slateblue:"6a5acd",slategray:"708090",snow:"fffafa",springgreen:"00ff7f",steelblue:"4682b4",tan:"d2b48c",teal:"008080",thistle:"d8bfd8",tomato:"ff6347",turquoise:"40e0d0",violet:"ee82ee",violetred:"d02090",wheat:"f5deb3",white:"ffffff",whitesmoke:"f5f5f5",yellow:"ffff00",yellowgreen:"9acd32"};for(var e in u)t==e&&(t=u[e]);for(var l=[{re:/^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/,example:["rgb(123, 234, 45)","rgb(255,234,245)"],process:function(t){return[parseInt(t[1]),parseInt(t[2]),parseInt(t[3])]}},{re:/^(\w{2})(\w{2})(\w{2})$/,example:["#00ff00","336699"],process:function(t){return[parseInt(t[1],16),parseInt(t[2],16),parseInt(t[3],16)]}},{re:/^(\w{1})(\w{1})(\w{1})$/,example:["#fb0","f0f"],process:function(t){return[parseInt(t[1]+t[1],16),parseInt(t[2]+t[2],16),parseInt(t[3]+t[3],16)]}}],r=0;r "+o.toRGB()+" -> "+o.toHex());s.appendChild(h),s.appendChild(c),n.appendChild(s)}catch(t){}return n}}void 0!==e&&e.exports?e.exports=f:t.RGBColor=f}("undefined"!=typeof self&&self||"undefined"!=typeof window&&window||this)},{}],11:[function(r,d,t){!function(t){var rt,u,c,i,at,it=/url\(["']?#([^"']+)["']?\)/,nt=/^\s*data:(([^/,;]+\/[^/,;]+)(?:;([^,;=]+=[^,;=]+))?)?(?:;(base64))?,(.*\s*)$/i,h="http://www.w3.org/2000/svg",st=function(t){var e=t.getAttribute("d");u&&(e=u(e).unshort().unarc().abs().toString(),t.setAttribute("d",e));var r=t.pathSegList;if(r)return r;r=[];for(var a,i=/([a-df-zA-DF-Z])([^a-df-zA-DF-Z]*)/g;a=i.exec(e);){var n=wt(a[2]),s=a[1],o=0<="zZ".indexOf(s)?0:0<="hHvV".indexOf(s)?1:0<="mMlLtT".indexOf(s)?2:0<="sSqQ".indexOf(s)?4:0<="aA".indexOf(s)?7:0<="cC".indexOf(s)?6:-1,h=0;do{var c={pathSegTypeAsLetter:s};switch(s){case"h":case"H":c.x=n[h];break;case"v":case"V":c.y=n[h];break;case"c":case"C":c.x1=n[h+o-6],c.y1=n[h+o-5];case"s":case"S":c.x2=n[h+o-4],c.y2=n[h+o-3];case"t":case"T":case"l":case"L":case"m":case"M":c.x=n[h+o-2],c.y=n[h+o-1];break;case"q":case"Q":c.x1=n[h],c.y1=n[h+1],c.x=n[h+2],c.y=n[h+3];break;case"a":case"A":throw new Error("Cannot convert Arcs without SvgPath package")}r.push(c),"m"===s?s="l":"M"===s&&(s="L"),h+=o}while(h",o={};return function(t){var e=o[t];if(!e){var r=i(s,t,"16px","normal","normal"),a=n(s,t,"16px","normal","normal");e=Math.abs(r-a)<.1?i:n,o[t]=e}return e}}();function _(t,e){if(0===t.length)return 0;var r=e.fontFamily;return a(r)(t,e.fontFamily,e.fontSize+"px",e.fontStyle,e.fontWeight)}function q(t,e,r){this.texts=[],this.textNodes=[],this.textAnchor=t,this.originX=e,this.originY=r}function P(t,e){var r;return(r=t&&t.toString().match(/^([\-0-9.]+)em$/))?parseFloat(r[1])*e:(r=t&&t.toString().match(/^([\-0-9.]+)(px|)$/))?parseFloat(r[1]):0}q.prototype.add=function(t,e){this.texts.push(e),this.textNodes.push(t)},q.prototype.put=function(t,e){var r,a,i,n=[],s=[],o=[],h=this.originX,c=this.originY,u=h,l=h;for(r=0;r "+o.toRGB()+" -> "+o.toHex());s.appendChild(h),s.appendChild(c),n.appendChild(s)}catch(t){}return n}}void 0!==e&&e.exports?e.exports=f:t.RGBColor=f}("undefined"!=typeof self&&self||"undefined"!=typeof window&&window||this)},{}],11:[function(r,d,t){!function(t){var rt,u,c,i,at,it=/url\(["']?#([^"']+)["']?\)/,nt=/^\s*data:(([^/,;]+\/[^/,;]+)(?:;([^,;=]+=[^,;=]+))?)?(?:;(base64))?,(.*\s*)$/i,h="http://www.w3.org/2000/svg",st=function(t){var e=t.getAttribute("d")||"";u&&(e=u(e).unshort().unarc().abs().toString(),t.setAttribute("d",e));var r=t.pathSegList;if(r)return r;r=[];for(var a,i=/([a-df-zA-DF-Z])([^a-df-zA-DF-Z]*)/g;a=i.exec(e);){var n=wt(a[2]),s=a[1],o=0<="zZ".indexOf(s)?0:0<="hHvV".indexOf(s)?1:0<="mMlLtT".indexOf(s)?2:0<="sSqQ".indexOf(s)?4:0<="aA".indexOf(s)?7:0<="cC".indexOf(s)?6:-1,h=0;do{var c={pathSegTypeAsLetter:s};switch(s){case"h":case"H":c.x=n[h];break;case"v":case"V":c.y=n[h];break;case"c":case"C":c.x1=n[h+o-6],c.y1=n[h+o-5];case"s":case"S":c.x2=n[h+o-4],c.y2=n[h+o-3];case"t":case"T":case"l":case"L":case"m":case"M":c.x=n[h+o-2],c.y=n[h+o-1];break;case"q":case"Q":c.x1=n[h],c.y1=n[h+1],c.x=n[h+2],c.y=n[h+3];break;case"a":case"A":throw new Error("Cannot convert Arcs without SvgPath package")}r.push(c),"m"===s?s="l":"M"===s&&(s="L"),h+=o}while(h",o={};return function(t){var e=o[t];if(!e){var r=i(s,t,"16px","normal","normal"),a=n(s,t,"16px","normal","normal");e=Math.abs(r-a)<.1?i:n,o[t]=e}return e}}();function _(t,e){if(0===t.length)return 0;var r=e.fontFamily;return a(r)(t,e.fontFamily,e.fontSize+"px",e.fontStyle,e.fontWeight)}function q(t,e,r){this.texts=[],this.textNodes=[],this.textAnchor=t,this.originX=e,this.originY=r}function E(t,e){var r;return(r=t&&t.toString().match(/^([\-0-9.]+)em$/))?parseFloat(r[1])*e:(r=t&&t.toString().match(/^([\-0-9.]+)(px|)$/))?parseFloat(r[1]):0}q.prototype.add=function(t,e){this.texts.push(e),this.textNodes.push(t)},q.prototype.put=function(t,e){var r,a,i,n=[],s=[],o=[],h=this.originX,c=this.originY,u=h,l=h;for(r=0;r|&JAaQGvc^J$Crr2l@Qq{OMiJ9GS z0keYH%*;~ew{wXTndj4N_Y9B99AOsFSy5?!n5(M&uI_I3o7%tnvw!zT-*r#NcmL}D z{(t_j|M%VT{o(!J|L=eI?)QH0XTSJA|KXSKfA(j8{i|QT%b)-B%U}QIeV5k1c=xlv z_~k$U{eSzX?|=Ev@B7PN{HI_3m;d_LfAIJJ$NT^FkiS?Cr>FPB(m(xg?|$}g|Nhs% z$#4Gi`{9iI{ojB0%U}POc7gWa*9-m0FMs{3-~8?SVIGeC5X3+K=AVA~*FU~HJoSge zp+CLfU$b`(OOcOUyGkfbQvVXuY(5 z{VMR~3cvsKkNMTx|J}R$*&p@K$ow?4OZ>qv(!uRNy78$yy#K|Yr0Z@trSIpfHWRyP ziVI8M&!_G*#*uz_>c-{8KXd>77eBuHoxl9+AOGQpMDy?e`!E0Lhrj-Z-+lj!zkBzG zzj*iO*Z+Gq?B{>kz5mre-~4##)z9B`@BcXe`@6dZFPy!*>stX=45KmW_VUF6x5s-FfAcn6Ed<>?d~heLOo`_s}5b9b|_ zY`Pm9E1t){{gm;zj?(q#^RS=CD0F%lqv7y$?2e})12i2Dw~Hq(ZD0->ik~ntbN6%_ zr+z$i$9b5>DFbEGkc(d<)Ae6CG|Ta6?3crQ=(>4a zhU3^hlcsFiEi~isG#saHKAq-i7;YE8hNd6BaAAGc{o@rCJ?G~JA zd>WQq{1obrW4FxReCN$^{L;bcrl;xDPf2^z()G(}o{zVCsw>-e3(h>}jy@#KorYt6zgNe(7Y~ z4c*f`jmPnDI4-BLjo&kIs;1pS)Adi|JPhOMbeNCb_1f3SOkX+|ccviuh{c-%@9q&1p zEMGj)PV>`Y?B;G+=0o32r=`E=lCo_#QVjD`-V2sK@0D{NfY+;ENzom?c&1(Q$&kT2 zF3Wfrj;Fr62d8S_J0DFtX{q( zkC{dsax-dA`f18==(lhGCkMwU_CIA^m)&)8o_ZlCQ5zH0OU^7i_Y$aa;Gv4_wm7 zbZohRP&NJVPQP_dSzVhl&Ae&*GX&d$OrCkL#C^UFd0n(oC)cLGefKx#6`jwEXy$OM zg@>}~hj(f+;npK(SmHikhrEVOo$M0PO#D{s6=l;8@6Q!7BRE3kT3F~Z=u})&9~0Swp6{o4tfhKdf6wcnNBZPc*?c}OKn}|pEK#5x#!H% zjcq2Og>^mh1Y6wa>yWpgs+WDDI_8b`+*344&p#&??Pq&bw@l^le7fc4Wkc=b)pY#C z!cWx}ul0M~7@bE}qVx4WjnM|3hRmdF#O5R9VIivO_qEttG}X^O(ai*WwQy85C0=MM zCSkTfmq%NwUS9{jg%!Q*6V*0xUo1K03%Y1aT-DZQoBE-PWGWLBSzue1=x?@R`vb07 z^!sY;HG=GCm+0mhS}$3ZZ9lx@??vwQly%cXB78bdnW^fB1a}KGulf%QXhpBDgx(^n zZuW_677gm{=B0J0y$T_rIut!|8>i4zSTXfaWKGAK94&`!L z)t2b$_qr_WXWimHdzlx?M%*pf0oSbheJ%DDUG=k1bldxNwcJ*=C1PqTqkwBv*wasl zy+v2o+$XxHsa~!t+Y(*1WhV5gO@`hUP>xx?ZQR|09br$^?`yHQ=&GL&qWkeGVLwGB zpUH{*vAqAMUtOg3Z6f-EhQIhWCyjaTYhYK*N#C~(d=S=G#alDVXN!L1wDir`)w9d$ z|1IXB*Y|mKVa1H~Tf|%Q&DVIBO4eV&O~1WUbp3|L$l=7 ztn?PFW}d5lUyHqle$8z6o5Wrd)wkG}ZHc|widpLoP|Zviy}lB9i@UnHw%yhl-z4su z#lFS8Y)jnLR?TQ{!D`mJ>i4zSTkO@(PZ4`ebKgQ=H6`+Bs%E@5KryXd_4+#KE$Haw zr--@cz;8h>+Y)uPdH#4kQnTLf^L5Bu$koeF5%cGH_pfsf{!Q|#X4-E&E8D^|wN*3e zTd4cg@=0 z;$F5T?rN)M@V8(!OJDW-TI?gT73z2@|9u`k;ad$m=w)myNd!>{^%E%p|B_48B2 zUaJGQ*q3dIz1qqc{e&w8l?|U*IY@PZVrAurcW?Q<&h>y>?6p>Kpf3)zHu%4XH2kAx^6 z$=&;G2+9V{>^^+{CtImLr_#?`1dAnoF2O3B_6k;uOLo&_TivD7P%HLR25WQipSOq> ztNdJ|RW|Jvt>mk`PiLF&i{e9Nv^LiRdW&ee?$0G$b=zL?%Dvu4s-F$Emx4sCBv2Wz z%_V}~Bwj29bd6WpwpYAzyJr({)MJ+#MPsXiSH^2|)u1hO27YDOyQe(Y2>6%IRLms0`NT+Cpys)5&8lUH%_ligE=F>E1!Ld$Bn~M*HOI~KC zziitpN~xw1iRzO5IKydfHCj^rsASFddPHv$FcluUvZ!j>D_r@Ci8gYYQrKq9KK)h> zsgBm>UHfklEmtDCgsX1bD_$wMkq?^FlHGJET$+DUq^J(q=AuP!5-=7gx(2Ll+bdvA zlb^~VT}t7O?Y3-Hlj?wNu4D8j0b{+QYrx93y#f|pS*m140zQu8lwv`xkWwA6&83ar zBw#FIbPZV9wpYL+3_Bi&R&065PU1XyqLHlwwz=BTn*=Or_fdj<4OrQ>Q^2yND{t+s zf^ITcl39}Gr*hb1$%r<6mQ8D!|7z7{9%@6pNx)d>=o+xHZLfeuhBcg0a3ovQv-|&a zu7OlDY-LTPSIs!K$;WF|q!xs`qhDygvDtHd#e4qoW>l!2>hyUf%pdtFYU35;`L1i# z5>i|=RdG@jFapO`5jTb1Q(JOQiMeBPW2>$H>|CboMP)LS z(aKWBa|x}~JAZ2XwV(f)=N!3^8u@?qJNHUsZNffhBit*X-Tvd#*U8r7?C{ODv&&ay zb)`Rld7%w%w8``A?Mw->^Q~HY^1J`zbFYw3;;b=Tb3u85-%l2~U*WzEv?34fxhQka zjAKefWe4RUAE&9%w4~hNc+2E!x+l?7+Z~gVGv_~6Ceh>R>6CZwEG}gK_I$`YcuK0a zBKmnB&-cqYeWpanr!l#yek*1VL~e6lCtncH_ExsM#QgSNemf$zFm^Am6I*^OW)DPV z%gb@BYXE!z;grn%e??c|Zexm4iLr36rkDc<|DGW*-k;ebLGL%>PvwM!% zmadPh{P^rFmo%Nax~WrmI9Hr3?VnDE@4li;*TfKWzF)gUJ)axnv`n{5r>1+RTiWYl zN@Jhjkk@9qDY>!FvU4sL8Ah9-d7rvGDIn12D2}Y?q*_;-I?elHzY^MV%}VJ0z?KYk z@u;RhcB!%x(A9me2Daq1-p;kq)qSo8R(66SEtkqAK=%jknqaf(U`kc&e6OC5!5Z3j zu$Oma>htfGVpHoS1q^bRU*7eqOa19CIkxKkuXf9^)fIA9)pyCWcQ3x|+vLzc^-<8} zl!s%NI#Gw?@j1t;>7HZHpX7Z@wDamu@(6o!1WI1!b812a7~R;;(8;Z`^f^PUrTA2+ zZY7ahj$H|DxoahKe_+e8y7=(uQe`KgtNUCHY`JT_ook`1`&;y$xj+IM*?ho8I z$1eFgNn!A5Ilo-yo9-#~7VmdtX7YE;u~YI-m*TkXRi?buf4*m@&wr0Cd*3a`mRE?J zw08mtN79~uKW`AEs*;r-TCQ#^s+hC^~HTTq}cji69zegGi5S!Flw75E3fpQ z?kVaOiMI4(ub0`<{+y`yU6SqHi!YmKyA&%vr1o(up5M+6OR~EqS~cAhZ6?02iFRE7 zGMnPNZK*h)M_rCrY4f4)^IDq~s+_Tse1CnOw=}yB*%H?}9TB=%Y<4WY(9@pY38y{KGlBv9TMDC5-HI3#SX3jgs++Jz&o{^G!sg!rW0dq{$ zZc9y>@yl6+&r=cQrT*lej9!saOGkD~snr#7PUv?@t9LKHY)Z{BR#|^L~5<(?UH zKFPUCZmz>#Ugp+e z+iTk1$d)qI>jJ+jTZLWU>w08MR~u|yja}dCdSqp*_>#ENR$<=)xof)30$UDLd0yu} z=1jcf%NsN+4!h;sQ&uhJKBv}RzVNF{{qa3ny&~aOz3-QCt1DzD<9A8BcQ3wd%AFGS zV-7FQx#0Qg$QGR(nsb>-&6MY3(>>`INav)`w zV=cO6NPUCsi#ya&yZLoDUc73nwk(I6D zOG~=)OW3zS?wfQ!`Y=!Fo}9IQzeU?3?_MvtQER@?2pp?R{fB$HdPUN$dfzYUR#zDE zo}0vb_u|VY-E5D@OwQ?)omZ#Kb!JxkIq9nDo^;O|fy?^U`e{{stVZOQ;~erhKaTl! zy3DCp^8kRhG`6zW{edmt>f+Jre(X|ZC!nkQTn%jbYrUOop{x5`4Xo_+xYJHRefie_ zcg?XM&m&}Kd*rCkcjO_5itd(UKblCWF7@y4+36KIw(5Pq99vx>Gn(He&)&WGvN^Vm z@C#9YKI(5etSl})Hr;dV+~fi0(-?*=8i5C`Yb(p#wS}zAEkpx+pKOJak=T~K)?(iR z*%Ga;pM`@5zbadWUEk|^WJ_WjY+a3A-|Kp0Wvlp-xYAZ(-vYUB(*02U8ukCv`0^Ib zOx%u1_oeu?ywuO{>FO0pw{&E;#jWZJIesXcNWPk+o413!OlCT@pO4ejXW{aB23Jk@ zq??L=?IhIvF>RT`eb5V}LgG_)9AsPYY3j1i;CV|G)`p2~iEJ(QEs!ng>iVq`(#L*P zwhFtx*Y(Jj$Trxz8oR#N^~lOr@uekQ`6cXIAa_l=A1Ml=p~|UVFK*EtClfxLx2Uf4 zM|-5)s`K4mbE^xaa9<{nzM6!aItbDDXC^h}VUum6&zf)3w`#g4+;gtIW!<&Yx7e4O zT?bhr$X0`C%$Y>TUTS( z_qrZg*($!Yge$*WTVCl8cgeS=894WoBQGNNTXfiO7s7{gxz&~a-7YD&a$~oQTU;PhGFegnS`u!~ zTRzTtv&`<+e9~tc^;Vn(4fkZ5>HDk)H;F>(v#fo+#f`r7DaLtNQn_@R=d&hkIltYm zgcj*by)q}g3RvaY$c!I+Dh%y}rKPV#mbuHeI(GqECcBT_NWYf0tZ)_u`8#nB@?_uJ2QWGTT~nKtXn2 z-82bmxo6ts<1Ck*Gwtf-veeB!ET>G*_Qyl3s@m$%zgC_mH83yLrCO%VnPID- zxzpA{_Xj2;=;ArJ`@yBcPC-}qxf2#BXH-W~`mq5A_1J4KPgPC;qstAM*^*i#Pa znIb&6RP5+mk=~d0V_tstO0eg1UaS89;GT~*t%zm&)$zs7wc7sAe|>EcdENqCULbF; z$*gxTzGzm>aW5&doho5DVPZ_V+H7mR&K9WUo>il(ZhFT2F|VF1$ZT15E@Yc-Kjba> znCiHv_hY8jGF_3+p=|ZL)Rk;mbtN)4*GlBxxMToaIf~>5Z)~HGYkORaYgvE2k!zu= z`&=ZQ;OxY>u{=i+cX$rQs9hSG+9Lc09cl_c;oTKpBX&yErfhXR z(x%KBad=AB+-ky>-GBZIYuk1;4%r8G(I}Ny_`QGq__o@rvHs(`>{1?b>fE_J;J>UX zCS4wR_v5?FN~9KRD)_Wn^ptmLht*vRS-WX?%Gy&(FlPTi6ePAkO;hVS1^DcqIxnQWPQ1JMR#uHo`KsW1wIa8(0S3_P8yk11z?<(#=G z6)keYRU`|^`m`cNkZpNu`2HQeJ^#+yt2(e}%m3zG8{&NiSmkt!0p9@4{dYQ~M8%Rl z^WE{7uLqBRj$wbO^GiMmSnxwokM*OG#L4f1KFP{iSvsU#YdGJZn&8It8P_^4U_mcI z$s$>4Aj&X*|87tEsm5tRzb(+b14QGQC%|#)y0(2SuU*PNV@jyLto3;6df(Lo7W@;G zyo&Pr!Ab*BBeo#msehXKWa-q*o>R(tYNu8(e?^1xf#>aepLd>s1px&muVkfx^n6bO zssLm`z%9@rc_7twqjyU^;{MQ1TOk2c8u6htC5J)4f`EdOSJ@BwV5Na5pji;`7$J9# zqf4ojyw1%zaAsUgz-VaKc`jfUaeKH$bC{i`H|<*3rDkq(;`cHM)c#Pnj|3F3AfTY+m8>LCo)-knI(9bZWs7tUMw!OrF^^#ru$5AOSsATWETrcG z76cTOypoj$qORh;1XR_qf`D6~bMnBP8Ra%}ozK4PBQ^oEQTL&qnTG+uf`ICiyvmpL zgOvuN>RLg-uFscOE1H(4Zz@)_vye={R8L;#xqt-$1tqU!rGfN(&quIoEENRY0&UXQ zB!X5B^bmRIkdqSl2tK41?aO*Kt@;xQC}2TALCLE~HXf`r5H+eyz?_4V0_IKo87WlG z$V=rGf5UdI3hx@v2{Zu-6|ZQefbF!(2P=2YfU0C;{%t@;)4iN>9@jADP=&k>jmu2> zrOLuXfgQ0=IFnCEQL2-P1fm8|LA@NyIAlE_1tjtbk_F3d^!INo>+^A0A$kp5K|CNu zr%oms_*nU9U)rgdN5Q)-(46$vjv3EGH!9bh^Ko2Eyg3s{FKZaIG9w-X01N7=Pf{vt zd=FL{h>9l#`7-g-wF769?E5hS{CxcK!5h#@&jl>#Cn#wpD-ERQJ3e@GbE@V6joAXt zH~Vo|^6s6qeIAE`fLRZCD7ioa3Rn}lj0o#dJ zFE_ik{pcPB01E=DPx4Au5-86L0uIqDoU$yHT25JL%QpALlP@_pai!-176cTOypoj$ zqA0+=1dI|8n`3Fr7HGcNbJRs9U?YwQsq#i-0%lj}!{tdp0Sf{ON?t{6@?fQb2vR}7 z%=|><9iiGO9L%u{Qx^F&{oMBXuJc^Lf`Ed0C}E|6^nA}puyzC%8E^~qlsph+ORHv_ zOwh#ACSZz5JnU5@0R=1wC@6WA_5TMe4aAPhf`FO%$*ITr>dO4{X_=$2Dc%8F$&{Cy zz}hZNdM;o=KtahXS!p0W-;;paAXN}>12nTssXLR?c&8M7&gr(PIiR_h)GT@2x#dv+ zu;8EiB&}d1aq_#M-(gJ6qU?%D&CJyDj`|^`6PW*6@ralEVcM28Ixb*AKS9YVS!p0P z*6sQL#)c7-Z#^(y?L*c)PN~cgnctK@Oua)k-M>`JH(W~ZeJpdyyZHmIARdt7Q>N!0 zEF|u6myKDs98DMW}A>=(F_ZT!(Ic^Ept3sP3)4svc{30 z@{&ioWr}B&&%&8-LW)VUkU08$qudVixePI+#xi9+GIw^wX*vC@&u)#}>ZkV63T;aU z`Qh54oN%U`kfM?-6iz;ya+%f1trf9$j(kgL%$)3J$|bU^d=}1>6O!Wx3yA|GKKqFp z6>3Jm&rDMeubNUWddTTab0mU4zMIC4P0~(gugu{zXFg%hL~%bY{Y8A~%bsPAnS@U< z=^US%iRY+5+PS*d-=RIYXr|}w)A#S#B*7-FXyQv+ei3XYw>&u8S{O)Ol)yF^`wp+f zh{bSkfI_dyfIzkAm;#sq6gwo>^hiI-HHnkk1$iWdq>++V(s2Qk(?BjE$srkO;GSwt zdS?kHjoAW~4pI^ONV3?(nlw`i%z}M_l2sCG8c4~ap%815eiByFa{*01Aw7^V(m*=? znQv3{PGY?>L z?f9PblL)b(-v%f(kf5JLl^z7>8VmZdPti|Cl74c$pr0g=q@P5U^jtvGPe`&#MjE)M zPLtkQd`V-rK&5~L{UoWRnF1E{6O^oyOw&M$6%B<%K|euB6^S$rq*PH*${$5P3M%C}futXiVik=Pa9@oky@LkP z?6XEwR$Bo1NInTFX(ObZrXG95bC(v9P6&@5yVO6Dc=^kZC7SdaIMWV54oe(-6p<=A zbb7~}$K%!t)>)xR|1ul!sC@+OB&PHTK+jkZk9{h(GLp=b;|293eI)fHrljWrntVcf zbfh9>g!a^D(mM(83i@qHzl1H#l5=xH&WQ+y-*r%Sou#t?C+r^_-f=7~15=uHQpvfmBIVB?v+*6e) z?QX~+fOc@9fr91|VbOVk92kVHFyE~>i zD=lTo2BZ{^k6e?0Qjr(VDNaaHLy}D5C{H93l1z~;lR6KQO!+Jv=_VjY4+auPmv4mI zC<2ye(j5A+x*kcr<0IiDpOlHhnQ%gi9x_lkx$GyZ1eqe7=8Hu1erp*SM%j+DKZ_)RnaSBFLn-t%;%3RSA5RB9rDyZ}{UoUVH-- zicCiYN=uq4o#prMJTmH&WBn+}Bq!u|uB|SmcBGNgPttJ#b8+2Ff_flP=hMJFHJNnJ zLQ5L61qw~3Lk3YM?Ud58;Gdu*i$s|QQk!Tf^o}H;6qNK_z=D8+dMII~f%N?LMzwK| z3*D3OuE>8|pipI!fRa$!PRcSlwCq#aO0rC{N}d-PP>M%dDGen(7qB3ppyZXTB+#Dk zNkC~13j%I{O7choN<`^lpfJ-z3n+O-E5%fKUJy{4M_MTrB|R6gAfTY+m8>*yPh}?E zv)q!#Y=J_VNdihnNjnA1Fz6FOP!dqmOarMP*9RZ;!Fc6&J>ioM-h-pN_kG82}r1hl2!`1uR4?NL5b%l zu+^Een%YOvPhv_MNqMG+^-=n0o}kDlfrw8=AJ1vZGihU{+dh_O(re%f;sMEl$<*P4 zKGVP*D`?U;X%7qDZGmQ7KCYli;z>+NI|X#!>jWiHB+xXFIz>aFeI)s$rljWr7W5O8 zw4#hu_mal^vv*_DJ&EuN0&amqp-BQtPH8(S(d5vwPeoS}O|nX!7uiqhM_MU8B|R6g zAfTWmpsXa&p6~eP*GYd^5O4!j@<$R-f=UkqMVcO3K*=jwDW=Nvf`HOL(n={R>A8Rf z0R<(mWTk<7sx;}IrI$2j3lu6%5>S##+9_a$0pGrwD)z!9_;}9~QY?~OlQ?P@ ziG&(b7D`ht_A>@J(2VIUP5MIc=&Wnr$$Y zw}drqq~|P+Uq@hI0{fdmzP@0UAW!irs3o;67GTKm)(eCd`Mr$~2_$Dj_pCqHinZ;n> zi1CVy?3EOg=F^!`?pI{YWb(tg=_X^0dN?@U{tH=k$oyD+DanfdaiCLq?V>3#GCdVz=DK87Gp^(!9fvZX9`O5R}pIy zHj}C!i$Y042{?<9VA;-Mc!4!#qzf&O6owX9C_`}$e0PGhx6A>MoB&GzmNqP1ue#uhKh`K ze^)YE7iz;dVId+V8727iU%^VvgbC9B8u7A&-yIn2ng7Q01^m6NhoPe4W#hW zP-r=6rNp81TtJggNDm~91p74N`_fOM%HjdM0V+wS=qHJ&2Z5?n4=kXh3XRkQRgM># zPr6Q8DWNDm7tr(*lJv8Ypr0N8+^>JNu$>H6g~&&Ox=Y2Jf2wowEcwN2}K zJoZcEr(=J**8l8$?Th?0OTnjXgB*@?j+38{r_*7%%1`qG46AfyBQNq(wo!gcOU_DC z+JBXw=56!Q>&tJ+eqOnZ|L12Lnk-#m(hV=a1uOlj<3g#Zm{|x=Jx1!);|>8Tf$~20 zy|L1D(okweJyLL~B|VYNy^4G)c)EV{cP68Bg%ue$VWmKI;3PryZ~)oppxi|XDv3i0 zs*#qU8k>>tyVE=^$9X;s)1jZ=+fu4U>A#Q_9fdW?GD-euDU*@Le`h*MSy<6=6IL2j z($R8JB}XAEItuGNMiDA0CjZ;@m2;@==(q)I zai<@W?()b*#s)E!geK6y+Olm8VRsqQ2lsTd_4g{oMI`k4hJ%GOUu~CM*h;IxAW_s!3g*6>%Dd}kccYQ=lXIRm33)VtUWk-ugMMohk zItpt#(o)h<{#SIQ(vx(gdX#h&vZAA~rXwvS9qs?Fbd=JtqT?p4G^u=Nw0u-_6tbeD zu%;tLsidR)ujoioD(Oi5DCsC`n%N|j1FT0kl~3R%%nSksZRRMJuYS9A>Xy5pEHxZ^aRmbshy-fJ=KHThu#{O&Ad zMMq&xM_Nic+W#9Jw~DezN69TKI&Q(npCMn;Q780=c|HyOvG|}4MaPKSr_(ZZ)3F=I z!w{s{C}hP(Va-MvN;1m(ij35IGB;8~O814V$SADINJ~jZ`@bg{AwevtxB)3;D$}B* zsUC<&y=g^5^(k^uno1z0sYFWnDGEwSD!+j%=m#XpNA;|@hvD9LmRs_}f_ht^(x!@g z7Lp$ zd!{}K5DNlsfLi#8FJ{V9ISkO876fFUA`7h)P@WeAr1DeQk6KcBE?_}GK}kSbDFWK_ z&m~}=TC3fVv*r&eL7GMXalGqO`wZ~5HmXFYr`-1~0c0b`a_kSc^Xa2mRsR=#>al-n z?}6iSKFp^>W*U#(RiApKRl2f~7kw(*s1l_mXQe6azv@%-3H0do<+o%%uUy9e^B;IL zS*pt<9A174R{B)O1+q)CqtK^%jMS^g9r{!P<$Z2^W2H}}p;VH3q~KD^dLo;975S)7 zT|N3clTn((ij14E(x*CbP@S5M(wsIrD0c~K;!vMTipl?qj#PcpQYuMFMXUS&l9Y55vZAA~rXwvS9qs?Fbd=_>qT?1U^{J$zm87DhkQE(;H63Xw=_vmzI#TsX zI#NkWItp3QQCQQFmXeP4e^)w6b6C-F6IS|E(lOt0kNQjx2apvV*(*s#eX5bxry47L zD(OfiDg76+qNA{;W4=(Kq}5xd#((E`M`;c#I&Q*BpGrDfNviB9WJO0|ogJx9CB@`_ zyFP}_s!!3;N>b60$bycw!sjS%or9M?;w3ZZH6tJM8pao~7PbIso zPc>BfR1#5IQt}YMiiUuih19369rt~6Vx>=|HPUAm`P2GT($Gp$@leQ$h{BqQ)TfeO z^1oeQIg$EQQjtnha#6^NjKZ3Yv~>0O?@UK&4l9an!b+b?I$B98Itp3QQCJg)`czU( z{#SIQ>XUS&l9Y55vZAA~rXwvS9qs?Fk7#KQD>`n$TJ?$Poz6;9(NV~Xj>4Lbw3Kv| z{}mmn`Xn8xBqbe%tmr7L=}1dSNBe)H<5oc+U6kgqqT?p4^r_5_R+5U2LRNGX)^wyk zm2{N<6&B zuEn&u^9N)_M`2Az>QhNa`Crj7(@T%~RMIi(g9oqdzmT~;qbjVkBP}Hz?fEt=K56*(gIvMtNV6k*ZIUkxEj!FJwhVVNFI_N;2C2J;?|e zVnM|XNa<5aL~2R(kOr`#A)sU-^{HYZ94|OX@uw&#HK_;)TtPn|Nj{n=?$PT#nI{2a zLA@Qdn5+D`? z+yJ%m6JN~Ml7fH&76cTO1f-QBpgb=KNad#pNG+)dC}2TAK}kSbDFWK_8v(a|Cg;m> zp2p*NIAmkcI81XZZW;+^2$v6_O-+=D=8@YCDe}=ThaojX=E0Dnq3$Z8T7x5|%lKGE|XJz8550*zl7!*LqfAyLlghpwCPi9kkU~3E?`AMVM#*TDH7WEJxM45VL`$zP)SXh2`LKY zKv-$&(FK+yq|j6(lB>$A(z*Xb}l-#3{E601+X_-n+ zDqEzcbgHzNl;orAlcR8WYMaf6aXNK#cD844d-W#h3aX+NrKAM1(x-B2UQ(aB=`i-k z!=WEj3@j&y_;VzvN0&4wMPNlicFN(DodnYNJ@cQ0fd#X+KqVn12`Tmz2?cC<2oK>- zSQ3T;Qjt);7g>;^Oqxldr+gQ%BB8KMgtSv6wC{V8P;$V6gd3oiW8$0G!ch*y@HBL% z!)YFlOHL;m`f(~VA$6nT7VQ*Jz856SuaBt^bR5Rzka9u8uoM$B?FM>qiJ4HqiiEuNDHat8 z<$FOw3NUFVMV-0<1*}LYEJ;W^MMC?&CkZ9}D@eEnDv>Bj7@5kWNKykKU`0Y<`8cLT zR3wz|1qms;Bnc_ylNYCZ(SVw*e^`s2;kMY$E!?6^s*9Ph5&X!8mzs%28Y?%2BQfuZ1f}C#YCQ8^Jnz zy(8_U?=$VzBQ2}MbCyC)4M6r*jhT5qo(?GlzYHm~Rekfi%g01V8$mmHZQ4;%DcVt_ z39p4KXeX#>M;k#qd;PhzJ7sA;&$IcEilI?y44D;umVFNSINjDJ0p=;+qf_RF$G*+I zW@_ZHTs54&_C@)5$_&M_j45F{&Jm3s=izkwEn3AZ6M3mh8qL+CZxmtDlCzGJ_FvVX z^!of;j4H28dGG+Fw-!VPLzlx5Vu##Jnj#O}RIE1X|D6Hw2*Q7^bDCsEw zD>~*&=TRC;I#R_+|AnmRD6Hv7OG!ujzbhT3{j2D>1xw*2=}3X7hXbn5ijMA8&uUvL zr2MbwNbx1~sI@9x=;EqWJN|{%|=?fdi?LxJ6j71&_$^QtE9LI zD@iHoNcE>}ou0;QY&-R*<&@*Ovh;gi+Tb9&SG+NH6qS;W^1sTAlxxybDnRMKkQE(; zH63Xw>1h9V&5qIyR&?BgrD&6Mqz=@>0V!sc9o?(wXiJ5Z{}mmxTK6b0B^@)p^e8Z; z|3X%D6xMX4rKF?%-<6J8?t2hQ4@(NS2_k(QE<_J3D8N=sPLaT8X8Q)WkMLpdBmR&*5B zZ+VoQl8*Afq9eteq$BmA^k2w|j>4Lbw3KwT|GUyry26T%Td>k}l8sb}dNiy(^%w(d zHquaKQF&jHk+M!2N~I{>7qTLwuqGodB^mAio@9jNu%O}wq(rDBq7kUCEpDcz|22CkqVkR%^X6!+-$p3IZ%u%O-+sN|^P9<`$y0|DD0U@}Kg za*u*kaZjEX#Cn)(xE5$#1z9#`CKr9Hj0ZP%Q2uKwvhr!BJ4lST0 zpsmCsSe_RIr0`Q#Qb{V$1uO_CC<#a_ML>K0xda@?r)NFt*nRx7RG-nk^~d=%joHpI z53NGgtY^I7?MzW!Ao zN-Hm@c76AJ^Z!04yS4b@*#xN=a|55l`>8hRIqip(mlyBR5@tGPz;>6oL$7&mkoD>Q z(k7K7FHGg+C2}ySnJ>z@mo|hfyje#7m4hvf+-=b!_78C2@!&ciMHZ^KQM8Lv#`+Sz`!RKqRoWw>og8*bbGs^OMK={DS- zY?EG%!%bz`Ib3RK;nI7vu$SSc9_>8bvi~~VR&nXdR)#hE*-{KQvFG6~ENR1S`(HEM z5(8aF`%_KSgK4m-2fM`TQPfooHT!7eX}#CE#iveRnsmxE2U^;zv+Zc8h8fVyC{wT1 z-p~~H`wQAuLCo3SLSuEl+9%tjx8g8UZ*_^4^;Hfyu;+W)B?QmIDf@5tG*w8vGL=o2 z*ukc1u-!`=Q(JOwq5kVt_p~%Dx10KtZPGJwxT!okhf6KheJ$)|xT#k<54Y^U4L4Oj z9BwLy&i1RN8g5}PH@7Wm!)^OtHQdsO+=ly8ZB*fKxT);9gbPZVYPi|Uc~EJ|xkdKh zhMOuH4mXuKXZzJs4Y#nD;kG4hxNZNdhFcnp+i-ufO?n&-HQA&tufc(q zUgHv|N6|$!%<7{JCiNKShVh|N*>#L5X3!8z9ntK@-xtFR9mT-$G!tDzWRLC*thD@w~hcYs5% z!UBM@Yju59E*U%)o10%*xuNkIFr`h!f>JbHckE!od2m+`+(k!{r{ z(B#JLmwQC|)7VOH^YUA;xzA3A?1xx#c3BplvI>>SA;tyE{%soUk$ti0!PMMY9wT7Q z9vMo4$oqDA4JlO!G&EW9!6~~hWXoC#s<6eFO1ZIm^mits*6b@XZo*RKz`#LOQ49x= z6&tC)AaSV2Fw*)AW2M(19jUb7-6&*5M`2Az>N!Y9jsMPcl%}Af<0h>1AiR&QOeDh|?-N)Xaf$cm1_nvS%TbhQ7wzL`n$P|ijJGG($kQR)YEu4 zpqZ)Y$X-b@>TQfXJxzxkYqRv-GB0DKSVJr1Y;6Cu3}crg2lFdKKOLJSlXRq#hW-m# z(NS2_k$N4{QRBb!!&;h=ijJGG()*B(R_f?sEo4PUVND$Bfk-j=U(nGi9nsNBAJLJ> zf{w(Jj<%F^wEuf%M~PbsHg17Zk0df$O9UrGmGagP4tys7TE4>tHnDKeAfZx-sIaLvny^>!U_Qy(EMj}=f6*HWV zhnOS4Lbw3KwT|F`V8RUJwfrCF*dwgqdI7iJrM_$h6brmUi)u%;s| zB^~8|MMtW{NJlEo=)aH^9fdU=X({Pw|8I2MV_q%NdDUUKJgwVj~@?gromLR&*5Bbfl%EqsD*dhqW|c6~#7TrPm`JrONZWqmUIH zg>`nMo{w~t{}mmnvLhX-)FT~*tmr7L=}1dSNBh5Pc9bfuqT?1UWqf*8TM4LFMWHyZcwsy(<8tVRLszMFwm_xF z6!)y91os3ixF;yNN4=)FC(jG=g=Z0^rN+pT^_KiJO~qPEi1rY8zKaN0&`(hEk5-C* z_IytQN;+BOz70?-HSrX-))NF2uppqIBp|I60p)o?Kq@_z{iqdH`V+7qpr9lmtrP+6 z`R5XFIpn6x`K0Zv(v+y_ySqF!$zW3xC8ASq{=Uz&WGdgxt-D(>D_;1bOq{)<>xdk4?o4MCKIKXxsZzyMlljrT?pdsa`w35P6xh`PKaf+v~9!!Z* z*N*z<&wt22$qNe#Zh=aO>X<=Z+R0F9ODhfvOTtiwDrU*|f`pWIl7y6t%69=P5(-Na z(oT`kzVG?4lc=yD;TEXmrz9bTq8bPRD-sGz5>k9B63X|2gcNj=gcOO&cL6IB3QH2w zPLa^Q?@2;Q2@4W#fKqfy5>g(@fw1P(qYEraNIOMB`CgEaGESOFX{dY`up*(bBq8k- z3GMryB$R-#AmJ9Mq^2YxMWGxBD@{GRz>(-y&xgQn>3SxQ28!kMM7aoLfR=3 z+V?$4DA`~^!Yxn!XxZ{FCnm z0W*R0C?utslzeiKpt!9l$WBEq+DRZNvg^c5B_fqE(hwGWwJ=j8q}WpfAz(#9VM#*T zDH6)}f`k-hl7tj`%69=P5(>*iNITb#`@STU9I#;42B_tjA|WN790=Df^ymUh64FkQ zP`(!=q!g28Qr;=w1*}LYEJ;W^MMC?&=i^u+z=DKZppuL7U68^~4usXB9$jEbLW)I2 zLit{hkOEAaNl~YK7qB9sup}Yv6bbG7o+Om?uOQ(Ts6?V9A?2JJ2mu=n^iC)&Nl1yP zNGRV65>j?aGb!bi?*djN6qY2Uog$%q-;;!r{1qhJ0HxwmPh*NWIS#Ho>A?k-9F&=& zpgb=qNUB2;2;Jqh6Bh(2jwoQK}{UVK?@|Sk``E0CAFo}ZrXn#D>@2mI#LqKtf}$eIX6o2 zSJ813Rr zQCQPamKv$?-4K`va~>EZD|}2eQv|klAUI$4WHX_*N3%KfE67#VI>u1c9c}K7!K$^D>}MY(NSVi z(^39cbd(I!bd<8w{tH>rQCO2qmKI1>WxdgHqgc-_N)K4kaT8XeQPNTR&cfE|X&9En zvF&cm4)c8Cwj#A2WcMmMN;Yab%Kw6n6lBuUHu4W%(SITfIuc8g(NZFtQ4f~xn;j() zEZDdOO8KbBDEVkH7LTIPf{N}FOr(I6OqA~x5v9&F5ux|A?*LXb1eDzBWM+ZPy3>;4 z(}(3S&8O3G_eXR)u|zUbeNSz}QlN=9MruBbv7nl=7+_${FG)!YBw|%jQ6f!SD&433 z7qTLwu;!R7Es*s8_K4mnYPO3~N>)j66INA8nH8n}ENq>g#=JJ4`csMzbXorDy6O?# zxmP7diAqgJ`Crjda!u1w3Q+qmWJO0|O)^<(r2XHOj?xWQblifqXj9oy;?iO`pct(l z)9zJtl%=Ml{IBRJ;il;*EvWq$vZAA~CYdZPkQu)2bU4k!ahaCm(2s3;Ygal-L0Hjo z6IL=)W=F|Pi{XHFw4$SX6&)osH67)DMMp_EO-HFh?Z1!}9fdW?WNCq9Rn}eUC@o<{ z$4yuXPDw{;Lp>ZqR&*5BB$MQ{Kt^YimFnq`Gs|=KOwOE{&o2uixx}2Nqx7NnU&xA% z!kT2Vv_R7TUFj%YVMWI+SZX@;td{h&7!Bx2D>k}U@dk#PjPkz9jgobmj8cl)eIY9{ z3TukV(gL|WrI6Vv^WIY?zPlk6&31lNLvmP9aRX8!RHj9UP>X?hl$KUBWS?MR4C;nH z??I<=n8#ttACbZcsPoT_JU&GoG@c^#8Pjc!BaTE!tLV^;(r@4j`T-b?emtiu}vz`u6r8h(|k%j;{4y3 zMWLJ0)Yrb~Q}YUaXa*g}>2T(*BD+HAPtA z_4&6LRbH9MSJfx&X|hy@KW3>^;oE|h?9*|Pl9Z+v`c#h*dzDi(R6u#3tV1$M^=UFm zNm`5)Tx!=B+1RVcluFw5{_mQ5q&cj}xCsk=Y7sc7PK)6HvJHoF7xk$m4)v)ckTWJO0|onoXFI#Nk$TPNy6Imqr+_dNBf zq?r7#vZGX=zHy`^H64Yl=qRj7MoXDT?f;FATbFk^#L`?=blifa>XVNZDoJUpkX3dR z){j+ND&orjijGo!nvPPEnvOzNbQIQfq@|>z{oj?2(p*+_+=PWb)!C6sQqob#ijKmX zj?||zJIeoxj#7P^j#84EjzU&+6xMX4rKF?%-<6Ki99DGPgoQrUbfl7$bQH3pqp+qU z^{J$z{4eN8)hFrLCM+IQpQIy^1s#bc9c?M;X#e-5qlA|Q8@E8EPbC?tB^4J1ET||b znP`2gm?+;XB1-saB0@`Q9s*d=5Kyy_W{QROeOD4%pK7}|VWCeo4Kua=ppY%b0?3Mp z?iKHW?O?NqQm5vu{I94e)u*W_C8_-vvLd6f#i*hzYk}NVpUPAt&0&=kH({YqH65uW zwXN2t>U-MyRHi%XQ;qDYk0n;BPt#FKQqxh$ijKmXWVDpYQRBbwN=FK2JZcUrI&Q&I z^{Maaj!II}QOGJg3hSI_OO;3Ef0Z4j`ZOJ-BsCp{tmr7L=}1dSNBh6)!&;ilijJGG z(5E^(Qb|fW3R%%nSksaERAxN+U(r#jPt#FKQqxh$ijKmXj6#;X);PlYWIb# z$SABSMoURX`@bg{Aww*vxB)5UCy6L6DYGJg6%7GpPNY6nDG`nr9F+Kz6oi_T1O%?2 zACM#;O%(U&^`6YL=#)lpfkL0kM>4gf;+}v7_XH*Ps81F5Y+U3wCk?RBM}_(5#X_avaCmqqT|0HyL%$&Xr65m3N_fP#{Mwi1tEd0r4u z%1;tdT2c~Fz=D8+l7O^Q3DBN@E&;RLeN6F&WmyhgiXfQNxnGfyf=?Mr4XH=!qc}~=xl|@&>Ry%IY$%bRRHkl~0@6jP z4XeDk2`gnP14j+1ZM8C0W6VxTF$z{QF)0b`1x zjK|~L=QUG?CL|BFNc~(8vLd6fCZi1%8SVb<^I)rHp93tVVMWGGSgBG;M(RgttB`Ft z@R%0VWTYsSWR&|A87cLYp;V93dm$?_3TrajP?6E@-^jSjV_G`Hij14E(xj4%RF85v zge=G?sL4o4D#7SQsiY+WkGrD7j(5#Vt@OQbk3oM?Ds- zn9A{eM3RRVq>6`fy`mwNo-$L~QF;zwMM6N$L0bt9+Vx#2D2-*s!A)4HJV`<-M?DtS zqjG!!H4iCAB@g9(MMTO`Whix{^j^q{io%+UHdI`+`@51+3d4$wo3K)&l8jWNayW!+ z!}0j0XfY}&Mlq_97NZhNiKh&uYLwm!S&>m#lhKBXj2iuYzo9EFs%95AVX5#W8L1le zaJW_~#~4s&MjA?jS&HdiDe#n`)Qr-5AuBQpYckqUkKQ z4p&O$7z1kBP>4!0%Kfmx*yB^`=H02LnRrh80ByX*@gp;X+cdJ%1}u#xnGfyGE^B# zy(qmGvLd6f&Wtuxw6*)Yl2O{iii}&Z5_gh|)QfsFtU~1w18OeHPEt|MS5&05Q+84< zO6P^Fs3@$dXhTIsyT2zDAvG+BxB)2*DrqRas0Sj^nIqom=I%6gIfrFzg_}9Jk4SQm z5>#;zeisa+z7sY|FDkErE64{Vsb>qpJUYE6?<6rSh_?kQ1*(`wwW!8GKr>HBGLHgO zF;9*c)T00u)T35ZehXNTPf)VYMuL5Id{6pGU|7&^1C+{6(T`eD4uk8XYGi>V{b;1< zC&vr=QP>G1sT7sp0v7ZWl=QQapr0N8T>9lB@HicoZkgxqcsS)G!FzSo_Jy>qjUv!| zf*exBb?lCP1gPWK->Ref+86a{`!+tNiuZ9mHhDm#tJ76}I?yUz*~p9hlx2x`7PPcE0^*A{AoFwELmmJ4KKe1EB&eCLaC^jS?EtaM(Wk$4go5G@;*1d zvC?(YP-;azQgEpyJ(115ihPvHt{(lJ$tYQ6MaE57DNr4_kCc_CE`ody2at^p%3YM8 zk~oy08c7YN=}2WKEu~tN{tH>rQCO!KT1q-<{CB3Kl!X-?H({kgB^@mnRdN)vqNA`T z4n?S>qx^5zSI(iflXRqBlynrbqNA{;BP}Hz?f>n~Y^(5(E=pTi(QyklZ`hA2RMIh? zKk6`PtB@5Pg)N99OGz>LU(qpd`HwPG(lH-QEBzO;qNA{;BP}Hz?f;FATcv*-VyO!& zI&Q*Bhe|qHGOFw-WJO0|O-Bk*Nk{o#(UJO2(vg}`kJaI6I%Jh?$+pjV86!169~aN4 zx>x0KT1q|GsQytzAFi(F|1g)2`e=!X-GAy#{v@5iiqr${GuGyNLB|mzp3&h6{#Gh|3X$| z6xM8{rDUVVf8X!vPAbESj+?O3qmqtNnR0rEBGZbF?3E;=Ak|07TkO=dkwOJTEzm^>B<{ zk4X97u8+@xoR|AFrif1@fXg8jeNr($%Sxmp)uZ%Z$cm1_nvS%TbhQ7wKCGoQtmwE2 zD@`i1V;iFfRi=jn$cm2amBh)VmTt;wnl4_?MXUPLCV`v$cm1_nvS%TbhQ7wW=H7_D>`n$Qtv4?QkUw{fY`KRBYP!p zWGI31z9J*_o+KkRq;y}%ij2aVjI@+wwEugO5fa3LiW`tprt&E*WvU0_QEyt&P<@JA zl%^7xy&R9KSR#*4W4DZ_`7n;B(>O=>O*ic`fXdEPFLr!jS(%70XnihTkW^b?f)qm|;HJ>N6+Nq|@o za0ArBPdtUGPUSE_b6OCPeTpo!Qb2iL5Rl4G5s+F^c`jf<(kLFCONrJhf?(9*xq4jl9TH*+!A53_0seY5i558uibk&*$G_OnG4Qs;}r`3*~$~q)7QZEvI>GH8(3NZo*2N>cBy9YA#A|+Sq`+C8%jbX(|aO_bW0| z@F_#vpnM#EdoN@~Mqy1x8cH(S{Tms#YOhE}sSPVKZo*2LN;2jz`vj_+PRG<0&y4Lc zdo*k-kOdjtsS+cFsU)M^ugFNbr^rYJDZLl6BBQV-BMl`P?f$N0l-95!;}$G+sU%}w zMIUvU9u6Q2GP+Zdk%p3ta=#)Y#hxM~^`rD&$cl`@nv67*WVHLcl2J;-ij14EQl*lN z)Q@^NtjgpV1M0^#MX4mC+^@(;sizF3dX(M^S&>m#laYp!jCOxlGD>Gyk#Q4NnpBdJ z>QN4dkc|y^XB5K+Qo~DGu88T`4GyVa35sSm{wo zLMlf+7S^M3d;w)X>?lVi59NMEM2b9RD0QRsUdW1y!kUXTlw7p?yOL1~!-|ZXuq^=% zOAa)M@@(jkx^Kak%p3t zc7IngN?llyaTAvMPG!cn0{Wmt^>6^$*nrIFPW8H&b=5~9D#AkP(+7E{45ea} z-V0fgQCO3ahLVhSe^)X}TUe2C6IMD@W=1MTIUGV3WE9kykup@0QSMh{q_|ULq+XQX z3t5p-Sd)>4l8knLS29XlSdnoHmf}t&M(Ra98djllhygVhWhbd9=PN2w+9^A!7Nzq- zR#X($RHUJ#qTSzMBIRs29+sMdQlGqHK-iiMp9&_d+4bJPJ_7JUL!akJ3&UNv){- z7O)_npkyD76#MM>p7fKzu%O=tsFj^~{!%N-VX*#GBMT(yMdW)cRn0)cE;tmXqB#81vN`dOYeWbxW zS4X9-l9e_(aFE%n*hmQ~=_vmzI#St5OWPCg;}LMK7P6wFu%;s|B^~Ymu8%?~3oANq z!b*cmI_3l6QG-f43R%%nSU(ggLM0vLe?>=XJ4r|CMd`ng6&-~&9cd})X#aPmqqKz; z9k*aD?$l$2f>95LRj3|hU`A#Q_9fdU=X({Pw|97RM)P)rt zH({kiWp<8kdE^SBFDpF81gDlI#M*s;SjQ-qp;47l%kT3^1q;?6`rD_RipBs$byc< zl8&~NbhQ6_(os^wf{k0C)S`-v6pnf{2jh%Fc6&<%= zE%e0qw5y{sJ4m})(NS1Gr)eqaDE})uQt8R;NcAZF7qX(Gu%;s|B^~YmuGvva!-|fZ zu#|d=j+BpjIIKzaAOmYUQj|(M%KwUv)OylV>PP9nkQE(;H63Xw>1h9VrK7Zl6&*KW zrAp<)ngUV|hmeg9dUq7obfhembd>)U9jW%DrBsm8e<3S63TryjQqs}>?@CAM4J$fs z!BX$3Apa1UphxpcT=RdB%`(J$Y)A#4zuh7=>`Nzi}F1X93+n@U1_wWAp-T(6L*B`j_ z-A6uscXR3=-~H_8f7!kN)jz*GJe_5mef!tHT>k6l@4EMY{6Rk-b#v?G56*G?7RkRn zzKCR&tFNK#r;sb>`{a-1{a<{G>NkFVsXP4}KW`cHhv$5L`VK z#?KoIzwz@*y)*yre|*_DrEmQF#?Lz!_0uTPgnb{`u|wQ*WL3zx($7`R)Dl+xzFY_s?(dpZAsX|Nkh#zCGW+ zJ>T!oa{ct}`Tp(s{_XkxlRn>@M){(xZA#^=qyAC%z8l~F;&1X(|MI7y{oNn@;_}z$ z|LDf3xZnTcPon4ThSU3B-Nn4$Md&;po6`As9J^ybXLJ6tM5)@Rmdd!i_-F3l|Ki7Y zzw>8*{hR;#%U}QIpWpxefB)q_{qWcS@VoDS@pte3@E2F*@7=zS^u*mw1RE{vRr&h< z@9t*Aic`vrjoo-9Rg7p>WQOW0x$Y17`FhWtfBxa!UtYtJOZ81~`%=99iQbxxds-Jw z`6(jt;n1B@N_FXmxqE-Lwrsi^|KT`x?=wu-!Mpx^mJgqY?r>ISKYQ7R?v$-RsjLuf z{@724LoR-pbMffS+gKemIM2V|e!Y(Q-cg#nr_(s~ss3=BhiS}qg7FrmvT3gut(^ck}5qPs1=A z`_pg_Q$Kt~VM>;GO117Kx5P3pUAK(WGo9+T-2&AwPsc;*f%9y6+ z&uPf0F2`Z&#_=AgvTe6OrQ-i^Jd7!J-_M6-%ATuK@w)|T{F)MZ>LNYOIa+f})%)c% zwsCzXRMoUwn6mS1So&c+oeuLcPuf&4zlUl1ni6@J5nYaRN`W0xB5>}9oI?5>r?PFg zK&6_0?zw(G4fA|D=IVzRseJybQu#P$3GSMao`2pg5cBf*R{4BmEniit&VJj&*v+XE zIUo9NIxYP@1C?!i1!~F``!NUQ=9xdtd7Pf+Z1lV(pziQhrRpW0F?o_4mt{N*$5Y?k z<5V^67N#x-HyoEK4~l-tn^KE&f zkve@%>AX+1u;Y|Z^5tBa`7~y5pE*^w?G~xA%Ly;jA!oas&fh8d@P3h}#;+-#^T736 zp2|dgK17pp=dsIlMStwBIc&E`rM|);Z%M=HcubbcNALNud&}taSC!FKP~jG-vTe6W zrF3xD9mX+LAPz$&DTebL&Mi{QLq@;M2JSi6c3YOYlwmk~UVE3hUS>z%fB*OY z?bpBg@H6)xGynL7CgEE6GOL@ZlgxzW^)jQ_=K!c76RRmT>88((Z({TDM03iAv_r-7 zL<@u3Jae1k&B<<0;nUro(=~)&)w|R=ayf0~*b3REjBTc!PI*`_x5%mK%_EmWqKCuy zY|oOJi!=F#uPJApBbSkyF!~N3Olaw^x^Edk&Y?jjw8^rA02^ zeQoj}uRDFF5i*-F-6*G)w~kxtH??n{yEXlMCfwsg@{;<`YZt$|RFSzClX+2N=MxJM zDgIL~t5i)tywh);6JFOQi8E8&{tWrHAd{dTEODQ&LtYg3w=x9@JXvd+o%9Nw*Q zoyKw@rfmA*otn(R^~k(VJS+*h&(|TZ!BZzY#x-@~s+F6v>4$e}%9Pb{o|h@NS!V3> zjLh+(TaXJZai6b4UW2Djc8qJ635$BEr)vA*9e*#ke>%%bU|wCb$Tl96RTJAy*c5bn zu&3(xwb*MM^TPRf%{^nB;;r>6QPq~X>G!%!{A78zANsuMO_}yi$*XP9lr^GzpusKVyu4ljB#p? zSL;$$Q=*2ZtO7m_nPghBw3~^6ObO&jt!>bJO?_-j)$8k^x8S0eJ!3nhMt`-KRkkH$ zYU^?`ZRYJWeVz}%u^sE(g1a7>BWNFNai6b4-a@Nh_Ka<2>TdIbkw0C3PDI+z_N#8m zVbR6!e7fc4YeVhnTaNdsm$a(3_^#jUa%l9JmHA8-Ok;Fbry)~08?pI-d9bJI_qEtt zOx4exF;3m#YBj8CO4QKAnW1ga<tUk+s8RiDqdVw$EXU57#XEeKqzPNA|O0jPsnW*WSvuRMJ&jL~{`wWgdTs{$k3^ zSU)7hTcG(scvuN6dVMAI7GHI-V~77_zu>pDoP`Xe03!?0{=l{k|4^i?RCI zGsf+$oGXD*g_muKo7&1);2IV7^b=xlG1fKrjInB_pZiaY%eKT=ZJB9(YLl(Em7SbT z+}_tVVO71p>i4zSTa4Au2QmJ5S@VOs-v9Jsy8DBMR`C1V%y(vzF`Ir3?xr& z=+Voy=_la6X4-F|FWVA(wRwJeJyLV)?(=oXTj15pPZ#;;+5I0cv->xz>iPV-<Jt(vdjg4LXS)$ePuw^3Fb}|`=>8g4sIi$RfT7(H>@J4 zpK9h@D+OtzEof7SamEZ3N1;a1a*J5w?fcp919{8}t)O|FhHg8A5t|{SkI~=y{W;j=pyA^S2r6KPd7ILw5 zMVxN^wN;pXEo8N(X629X%BH&m9~W}^D z`TWn)y`rwu+XapVoGyVYoAwG^-jcJQBYUT>b){Nqr!sJ#S?cNSBFCCem&lb(J4G&) z)}FFGKYOXKrKVc7r!sP%S^eqlBIhbkm(bO1d&MrZ*Acd7clEXWR4V{g#_lru*?53Pp zpFOCD)2)R}yPOijWe<&@^s+{sRmGIXC?g6a)pr&?22npI6ZMJ{h1PxCTN zQ)(8CDV=bxLRE+EGb<^)UFcky>Jq!UZLi>+i-2SYNj4{@YVLF{OjQT(GYeI{VenXx z>KeSVZLi?4RT2*!MKC^b!8wQUxs;J5X(vQ^jMm2GE?_3JYrL1!@UX&c?B3Jco`&iFoRnek13?7SHU4vJ)?G?P~ly8Sq%5kTBc#4Rh z>t5BtTUiI|qv9Q(Go9L|RIin=S}5-Beg%`MFQ$8+f4mu(8{v@B{)aJtCVFLHAd`bFVws7AJB>$h{!m?LU6Y z1*2ijks#UacR3g2@^xEX@XudfZ;M-*5p0_ZQ;aV6?zJpj?bh%9kI%i%kZ<*4s#moX z(Js@<&z0Bt{iK-th3+e3t5enf3smPsI;J96cDEk#7)mX!CG{%D+e1K2_ndn=wVZpr z{IMzL9#2oFe5GfJC427YLtgz;bv2c?hPF;M_UCMyqA#Du7d_is z+42(e+k5%#h}go|y}V9r`K_2e5S1-2$FZ{ICC0XFL+qOsGZ~UnEqT4YrNwqzr%>ne z@;1q*!){qIyYMq>mFk(}i#$?Y@Xzi^WMi^FZE@@Kvtl#+Fr}>JA=OK7*|+j$Ivu|I zN)uz-8#^C!a85=g=bVhwGTjoRn(m2lX)mKW7VG>*zAZ6kG9(k2^C_3i87Ha#no}0q zck0~dRGKVtrF2`i(X_YGHgB>9+EUX>=>EW#J9Y8M&_8ymvJ=qNeXa(!T(;iMwb0dl zt_D_if+8*T$|XSe2kxAYbJSNR7$V8Ysbsf=(su2=ygPHm#D4iWCk>p2EW_Mh?AimM zy5OJgnvbii{MF9+xVlabq53X4`R>!#^P-x!?Y))bCkOhL zl;@3;1KIPHQ-SgokuyP}YU#!yMelQ4<;b9%zmtc;l9%So>_D6AT?1`-ZY6YoV9Uq4 z`0(gbWhbDk`&%{58OE)=SIw#K3&dVd;Yx6r{hyf zbl&gIKBpe+HEVE4Vc#i7gXA6g@=Bh3Twd@$-*eaN-;c|y?3|Cw>qMFJU2^i>r>`d; z_oGkHNVWOKffTa!?sMfw%@lvwxq1@2^8eGBBy3A(-MEe_Fnr+X2ikZr34||94B!5tbiw$mHz>y-kFc7yM86WVSq*dE>SiBO__`hS(2n3sdb}lQHQf_*=Jl@$dffWLoO%}R z$n7Z;{6pr;GM)QAug+POn{sMfL<;Sd;WB@*4%rgkI^^ECmX>wpYz_R#o65#V9DjOeJ(UP#dc|`7w+d2KFuJDpG_u3oLZ2~ma9NVnu^Uatu!}nWN zJ4AH(@dQbE!9TgDsvXjA>DJEax4KRa?)@$)`0mr!lYX;|kR`}N&dbYDeR*LWr|0ym zrhEEL(mg!o3EIkOe%U|p4S_50v*D6q9*T=)%k}Ayq7`7j1=Ul3}hU~A6R`h;nWyN~8H(5+mR@!J1%6ew z3cJ47^~jdmHrTovyS~@;$jVmnrR8GzCG1-ucTUX_zm%n^-6yOP<~ITNt$^r(x7 zI1{yxP4@&n^~udS?)Lo0Hkm|ndL)F|U>=ojrZ^&s$g!_oPIKv&A!Rg{wk6`WF1Hrj zlHOYETOeD4*7Z~I;K8rTR$(<-37P`96)xgS5k2~!I)R%t^aOZrixr~%Y$%&B9cjqDJ z^zN3Ashg-S`0wwzYlnPXU1jflTwN!#uHPjm-+lUe^6{`dy%dsXO5vts%aYk+(>)*0 zZA$n&FFb75O+1K&TSfA&Ez)IvBMR*M+mXvkR1KN?V0}3*^oT8bagj%6J-IY>#RO0q&QeAv7*8`15-*+aW=h zZtXmyQC%mecV*|#SDB#mzLD3{O#Jrqahm!p+CI-XtLdJgQ`YjlW_Nf>9foZ)8d&#E zG0@1+Q=A}jiZ1&+mY{Y0R+{T$zbadWUEk|^WJ`J*Y+a3A-|Kp0 zWvlqo614mh_AQV*Cuk@ua_)C#9bVk7J5FdwSW#W?kM>N@)kSuGeXcH(I+U5-`YMxi zDrIKZMdo}{FFBbxnzs2i-K?g2az3jnZW(%8a>iEV>;}pH{}pPg4XpT z-G1<^vQ^miy{<>Lq_@G=)!6mDu18k3iZ3le%P(Qy0=avFe%QCy%C6+?<$k*kJMIdB zmae+qzuPrES5EDmor}w4wkS*KUt@C43F^l=udCV0oH>Qe`QA$Xpy8gFvm%j2@ut>D zwV!Qo&bWD&l)9e}OG@-k^L&=lE$0{KmCz#9saNK&R{^V>96k4gPlchKu(b4*$TH8_ zR_8fji#+!#WMQkusz}L#($ZG}e}a6R`W#uMzu%(KQ|8n`}%rTx_pIYVFxx9?2I5`|{@(i`yvvP8GRs+vjdE;tePL(*M>|eHo z91pE@a4T$-51KhUsZ&uU1zj(7&01DYfxuPJ+?8vg`va3bbnzK36?O``y3f_X_VhqJ zb@BG_SP9)9SlB6w6m|+qJ6{FdIU8r~J#QrEiY~Wej(saa{qp9_ThngYxH~@X(k?If z2lvdiZSgOK*HY8=!_A20RbG*e&%(Hmwt$z{$qRB4^4+DcCK=~kwv?ey;kq2_GNwRt zwlH5OFVu2R#!@PILsf|*0fcUd>}$8)vRmW)>- zb5E{B?u|?0z?Gu{fAGdO3c0q&wYc`MSa0N7=;}UK0}DGvjRaG63c5dV-y|Fbe5MSq zWn8YM-jaV)+WW;#IY$Gv*Bo*dsK;FIgHple1^>-GV{J>qS%S?Tg%8Ufmsfd363%M* z(h5hO)m*?ul$$wwbaW)Epy)GkmD#%DVo-uvTb9@ ze&iHVyOi5^)9{pqwG>^=W{wDMmSG;==cuK;nI2Mv?XaZ8M2fPd_)qGGzxQ?1@cp|f zlFMY*B-0e2h@c{K9+%%q#atMKs~VW<_~~4IOv_Uqds!CD^!(hk8+kO)iqrtN<*niS zcl7rBJ8Q4%z@9Dtn|E!X_Z()GiY|tI12p?CPKVSlSyD-%J0A08Virut#~RjTg@6T1 z1oenN8cCe|E=ZKrokg?5oD<{Q*&0oSWD?~Vj)&sibX>rKWP*}QveH1582|p={v=dc z-hzZ%pm|-0NVe^4nYylRK0KL)W2*kXEJ=Fmdf(v!7AzE$?263&!Ab*B*0`YIsehXK zWb9OhpHrqir-aR>VG7BA;CY)-APog9XecPzB`Xc2=lj!8)kq5(Zh;QT2`LC2DPSsV z_lGy!p+;At&dU zhLIva^hwVJENCbw*(ECp)R@0THdGPDf`%KQ`Oc3dcE~Q@yx^pySPpD64UZ`U|FYg( zD-Y?x0I;B;`Xsw#C4ut1pkY?yv(5RmFZ6`Br@lo-dI(NTC7H?Ly8;BXhy2SUxI#)1Y zs3_@Dl@7A=>mQVL$*q84PqgmjHq@ffu06XcV|G_}1>rlla@7_W10#kC1`HJ?U8Som zs&o*SEHj36hp4*#ev@D8e9d93H!7uXX*C?$g`q#aSW~+UnmT9D7&0olSfzsPCb5eu zS6B^o$s$wOkt_jz-q%U~-BkAnsh3I9d?bW*ag4>4lst)L&IoFY0&7r-4B}!>0l?!_ z7aX65y6#84L8*^Thsp1e+m)L>zkU?EMlRroq@Ws9qJzszT&)40uE!GS>xtHl5$kSi zhdKRUIisf%uD4IGYj!s*jXPqZt=coGN(0JTkmRi892s&o(+tqBO$DWaj>^SKuO zajNem)Ei{4f$i#*59(aO0HLCUOI13^&aZwbu3uDLkb))kMC;xCb~o26^IFJH(+C(& zb@jkczHxuL9tJT~Fkq-C>8f*Piz*$&P6`n@IUyRGq9=bP(&N8KV}YYf}9{~px2 zf&oKCIZ9Q@pk}_tYN#*Uht;qnT4$q%!&GN&hjIz(YFKrx6R(E*x_sKF-z#pH0U(A7 z1`HJ?U8<5nbsjL>O?Ok>zrBp2dM#KV+0^Gx#<0#2J*;yD1BQx{E>-CuuEnqhh9zF& z+r})ZCtC0B>jo`#+O}>IS7_BMC1Y4087`il7%CVrRFrg;UbCpuLF}Y}VVxr?pS?7? zeUVsq2szZpgnVPtjFnHnH`>1D&CV4J7%J*fqDlwZ`SlOV`toF04SS;dS`$j6X&1n* zRiSC$#;~sBvG`gihoNA=P*KuVg2AFn2l3_8fMJ~@s+)(`d$Kxfy`PUI@`Trh?Fu%Z zenYN(p_QF07%)_nbg4=Q+4=P`)K|m;h8@v58&wwrs@tI)>N??dYrpoJ4x6AZ8?*fG znrDGvpitW+Tqu$`^&KFr3qT#~!an=DW@=sjy z!#ti9Pe1KRuB;RIb^Y+Ve{`L$F0-UARB-t_tOrk8r}ka=s~bKfEtFL>#gmnvwa{8; z7R|OtxFhwX47OpcF=Q!^-`uc-jwq~9?iRR_-m?hS7{!RBP|tfQg)*nM147a*S)>F` zHm+c;Z$L~*k5T5TeplT1lMZm~(V{17?}^H`)PDO+jBH9M%arFC7*v#ONeOk3Vx*%` zLJ6VtO?IxJAym?XM3D}%@#{k<9&Q=KzN2$>XHEK%_S9X5US^^f=E3+&kgWtVl{U};zaJ}H}?1FXq_AGfJJtRl&u8V>+Uuq1#|Hrcs? zMo>vlN3JUO(Df})_9)FSK-d$NUkL~$Y`RQ1lL10*Q@dCaC?Qni0Yd4OgiyjJJ6F&U zDoF@cB!h1J`YWOI#sFbQly)T`l&tsniQMr|XP_ib=R4_oODA|$#>L4{pN1;{{Ldlx!TtP#qqz8#29c1Iz zUkNRBvZS7<%t}BgRg4RL{y0It4jB6v!M~zF_w#;aL62Z1jQl z;YnJROY9_Rszl|Cosxn?1uCbOeT$4g5<6O7JsN3Jw zm6V&8eX%}ko#=ZA0_I$oTCU45)s=;(d0*eMt*efmwNF3qN&9Ta>~^)FZT-z-U9he$ zFM9uXSdgBT!IM2X>meDKtfD!bto*EfvfK6$ccq?{!7F&5ELw8kH#aPyBMR@+`vpEH z%aqOe>c;cYHaYR7_(?L76^s**u>etisOHUpHA@*(=K({Rm8?>(COcO!V5lhRQk4!~-~VKjmO5EdPZa(q zF_f&yIu)#8=naITq)P&*gY+OBg*pOTp5#;DV(LV*eBacvVOVlr`4rxz8m~c8y%X4@nNDPBktpPzSGmHBfda!!gj; z6Rqidel<|yCuNg$D(E$kR~RKO(m)-g4e2NhOCl&|lbtIVAXJoaQC8J&lxF_Kucfg` zseS>&o+vy}Vkl|Tby5W7fVoZWV~L<-OPz<6P>v<5l(osu6$}_EN(@ycgYNw5uLuWO zjse4ts3c2bC~ecjpbF{%BTBkhrFd260Yh1qtWw@4J6AAZs3_@Dl@4Ct1!a>KJXumt z6fP(+l(@+{6|7<4{c&BHeR-WtVkjBZK{}C+!n0(RGB??|f&oKCJxWyRAUnVQYG}oi zCG|vMgAzljn;eFM0YgQJjC4>3X+=5;(~=m<-DKwq1`HMTC{d+@?ELx|%61F~<&G#^ zOHwF#)3cxu>iHr{VpOCK$?Cf}U=&vaWs$Ns*|>rMLPg1zj8F%^XoNmWl~R*!4}Erl zN+bC2AdFA~2qjb%S_$=JmrAHmkrK+>a^IM}R`%yt166@=IMp#?#tuQTr1F?kzgJ)O zYu-7l`)EvoL{HJ8PC5krT~ruQ*Onsp`CySit1U23%552&s{U3bbo-6%dh^~nt# zHuV{D-2$R+-oC4AR@dE1>t;^pzf~V|qF_C-lP5j2Zat5!$kcQtP4;yCXG4@tpa0Us zpIFH&IHImw2IMz4MxZAPP1HLFN~bHeCmQCYt$OxLQ`C^C`Byg~3k%bQO6+v|O4jr1 zw@lG6Q5Gry-Tspfe(uv2sLSulLK*emgEHz;C4#n*Xbn@=@W>hsNy?KU3nSB&O7e97 zN(Lkq4Krn-LQ>Oz;%8&-Q+8llBfGNDM~S3V&rmA;Q4gA1lM0 z-8+H-MI^&4StUpmAgh2VvoPV+m90~;pR1=tQATN)2QNVyhK#Jik$oDH)K5be{;4aK zEb9K13_vPsBBh{)B-_9K;X2%s-bKksO!!LhB$84_JrX6;`lzhoVWC|0)P7naWqBG& ziKFgc$$+Gyo~aAfBI)*5KvJ${!mKAN*^@|0Nev@`Xqu4ZRtXvlWm4@YBxQUWNlB#c zU&(-^qDIn%3Q4!W0+KdPU4B;1Dl8T0zvQQ!E_E$jC_NmM7%0g8olF~{&5+xIo${IA;sv${fGh|_c8cE5e?qA7( zq@rP_EL2Ep`XArkJW3aIpE5Gjfx9OQBa}o+TXm&MCL)zJJF1hbshY2+cNXrdfs|rO00xN7OR8HJrp=> zcGl+}D~p79%D#~^f(UwstPl$#hW&9lydHg4R9SjY6gsQ;E2nLtvvLj!wn0FsP?T87 zXmyZ!r=u`NS)~+IcCMfiRMLZFkzi00Ujss^o8ge$5tTS92qmI=6m(lXUPK8Di_{@j zjfYiG_9&~Aj>^szG=xeLLKg`L-T2eqtJDkF!+t!Bo6TV!#(94{Za+A#_4=suF{-9z z@U%@&+i@<*bsG2g*D@Xa+-J$PK2qM-7czI-<5>UQ?)RJdUUIFM$EZrr?BrQ;b*lDReT1`)sxazJ$-1d z466HD1DusT%0g+RdQxbqEr3GKTNQ%3c*dVKW|YT`8SZ_OE1uQdy%`r`DI1a!JzsSAkOgVuG?O zE7O%wT1wSIsbqpuS+8kST?v}{Z_lswhXyHseQ2c;N+lDN${I>mN+{j`3a{8?FeWH_ zvQ|iSps|)JD3wf5DjV>@rBj_${|QPuq=b@QDxp*|L8+{vWTk}C{jUI}9L5A?S5~$w zSIU~yQup*wkW5f=t6bBlyE@71i5lRV>QZ+ll=b%cf&D9)pj6gSvQk2+`LA;Lmc^K$ z?8?e_C6rcEwNfgXpj6iQPc2fd4&U}j1*P3o{ZcX+pk$U%x>7>v{;$7MN@xsB z_C)JU{JgIZ0d~{=P~Ty$`_I(Nd?9H)6+|i+fK)V)qcX{ul~+e4lM+YysiYCX1S6tm zQI}c`$*W&$mFM4v;IZoWqc1Dxl`v9z^+X`DCLFm{qC@G`Nm`$#oGvMWq@T+El}tz~ zYtmThgXh1-K|5KmEK1&ET1+jI;_b~~K~+nnk_k#>jSnj&lK#S$x0tQ|5c!r&6q&z$yz5BlvY#)rIHCsWdlCr-Ef$8$8k6w>nbL7bGv<=M-*u4 zKS4>Klu*)9C6r1gD3vvotdvl?{}oOVnA z^_5W4QYDm1CMcCPl&q9cy8jE5j}r~qXzl&ueB2y1!+5CAZinsec$=W?%F2Bul-5)Q zrIHCsWep|eS3;@&6O_f@u$}7t-S%+Y&&S~~j{Y2{z0_Y!z_0(6Oi(IoC|N0?bpID9 zAE$c}N=cmw%ARa>wX1jA>yZO)zSkLW}|| zgA`zylwK+j<*TZ1Vp`xUaRf;%I*kQM?*KKt<)!kv3 zrjHivSYz&cj4tZouGWCI7}OU?=IyxIUMyHY_t}Eoj(6?#;j}$&j{8l0cC#JsE!Zuq z(la}GwqW@fT~tyB`y#-rukd|Ga|CZ|KGspe^)I%T(m)lv34~yu@%7XRYf!ta4 zG#CsPtS6)lJ-Tw*)N12TY%C2m^BCB&* zyJ4(vg3jZnUOCrmX8lgvSJgtPWP(yzLrDvkP^$j~CEZd&Nne#vDw&{E)=;uiLh1fj zfKp~-g0d$|3zksYR~3{>CMcCPl&q9cs{aHf-BLnHUzJcQnV?kGP_j}&>Hb%MQf6a< zvMVbKmQdDvy`=@}p&*%{^H+}BtiliA9e zf-&o?ELg&5UsW_JnQ&CrJko+CWa_^?zt%^(r38|`Dv4AwA*rmHWTl=dE#MW_N12`B zpq-3aHek0D@40Qkawsxe6O_stA681xw67ABbV~^(eO30aWP(yzL&-`#)A4RP)MYcq zeO=R|E~HT(9^9_7Qp#*hQ1)bL!E&Xvud0<&$podchLV*MO7)+hq+3cT>8lb-B@>j& z8cJ45DBb^MrF@)l$VO#0CMdhIvS10NeN{oJWP(yzLrDvkP^$j~CEZd&Nnh2o@&$0Y zSG`re&|sy6(*0kce4KE|M(bnzg@c)(?8?f5zC<&wmD+1Md zK#+c^5Tvmx1{DktDoP4jr6_dg*T+zrW5BQ@YQGexdTgu$Lj?ndiV{OsDGb$lz>t2a zFr=|63>6F*DoPAlr7(2o7YrZwR=VC@9uL!Y+HC4;Nz?9dY>H}$qJ>?(N$PrtB5ECk zhh1HUWSlp3LaO|p@m^--6Q2#(It@|ZZaW<78*azL&}ukd{*p@4^VazZ)?TyHLzGZi z#z|yV?f3R;88XX;Pk%|flh3PQ8_82o<*8~3oniDHQHihKF;Y}jXYgJXtNDpUq*gF$PSvRr>Cy9!119#Et}$}%aXs&@qwipmm2)+rR-`}I+jx)@OG ziAr)Miqugx4h0j6$`ZvQWGUkcMfDy~tTyZW>UA};!*M(ucbok*hgFd(s(M#2p{OiT zWSv6MyRrKvqOwGhbqYoIeti_BDFzgKq7quUDpEb=IP9}}#>jG2q|Pc7)q6mZ ziYUvZcB$UNqJQuQZ(f(c6Y1;#Vf>taw%uw>$ZBn z$Z9QgouW{^2Mj5JvP_Dl>RrJEqOv5Bbv|_NYoI9oGVs+Ct$W;TcXOQt-tFe8E+}`X z_l{f*sg!CQ3N{#UShoEt!l(bKRL~H%^E2zIpIQacpLtLQ%bkRk8m1RM#`w?xuND7dNWk z#c&Fz{TeM=Dj9k4c?^RIMQ&7tu}%i-oYZp9U;hGm>Z7%~qhWm-P?y5oh7Z@; zuhxU3l2W-U)}CK%)Z;)ft%}|#P^6M76xDk`k(wu0MXIAhQNe_wvRoBer%-h7*GEx; zVOSM=qEb7C{peuiWElGyMhTtWr-r|6pHTs`Y1{) z3@CO)>2>P(OjVTAXs-!ByIyM|(Hq60s4@kjIu9UH=_H8MMAf>22}ETHBI^{0?)~Z@ z!Y?$24-Xb^yRlx<)gk{_LQVZ6ykC(b%0ckjs~+w$soAJ9TaFsBo%8N}=dlxqzOc zVvjX~J$HR|_+$|pzK16*Z{o;H9aLke?{`ggzIwag)Wz=SU0ob3y)X7Amxo=}2>8^s z;iI@I_^5)SYvlrbiV8l~2>9If)8N~e6tF$kDdM{NX_+6pI(PR$F5T4|*uKZ4dG6~y z>tQlwK&?SF%07e$kW-GgYMzp8iwM5VKL)gbB&6tdvvlJ}IZHRUT;@iq4|bEN+u|kHI()8 zX{nzQO7)+htoK1n0hLhF6=nZQCMcCPl&q9cy8jiRlwp{l?8#E^B$PBpJrsMW9yPLt z(v|8sul^I1R6JQJtx@)`WP(yzL&-`BrTbq2O1Xv!%C4-0R6UTx3?

s);j$ncjQM1T0#iDy(0YzDuiN&t0uCPiNsjGS-a8TPwaL~2{OUfwLI>{@H znsTb3tdw>s`&Tj{sjQh~rDRg`|9B$kaThyaqjC}xlwDbgtAtWM>fl-ry27WS^j7go za-h0ODAj*jB`JjxN_wX3U&#cevWAkC5=!^K!b&MKGeOytr5Z{oX`6Z|5I)mN>8%P% zSE{7?Pf*t9eM^m%P}Yg4g&*o)N+lDN${I>mN+{j`3Q*R^hPBizR8|(%m941`hxvHi zY{$6_=y4uy`H4gGRu>h7p&*%{^j3u$bylvF>OVnAQIt^9J>@T@k_k#>4J9jm@ccg> zz8`np5H>1fF|CwcS!u0YDQTd(RtT^ON^ezAQfwuZ>OVnAWt33TLS_F-CMcCPl&q9c zy8jhcO4*AE%ATyuQ8Gyv)ibf->PaK(cO)!S@T&WSB;`>SN*|T&E18f~)=08aBI*9G zk0iv#0Axo}sw;7n>gsVU1=cnVz3JvQff!|1EstnCkVu+>hMF#y;TmGmkCX{c%v3I+ldS*}uaBWL z$ADo+lxnFkq^rtd*nj1K5haGM5-X-U4;WG}Rh9Hr)wzNJLq&-ps}zRr{An0Y)7=N_ zbs9eZS#D{0b>sH9KTP#ClH;yjRqJxm)bqB^7HcyFr~0bN_OP4lJ#c+P{&JPnvLfpl zwdM08XG`_;Gu=>b`Q(?Lp8wuLWtHbtdwy^E(*IN63Z4p)D_OtHr=9$z$7GGV8~L5* zeSk-bQtvrrhx6Wt6?%F=)z{|FkIALv6VuN;?>Usz$`^RvN*g3EA{zJpRYooo?=tc) zJ|-*E?aInL?=(D;W=;$9G`>3SkGtJ&yWedO!%+Khs+*mkAE^%vj@!Cv*K|D84ZNnq ze7Y9E`LXftFxIMG*9fT7WV^#=Jl>i%`j4mj^++1JvgcncT9c=tYyV$m=rY|dL;vDq zatS$f`iZBVrbp7yl|7G^R^w@~s=u~>dWbz1xMi0Z!#ovs~+PJi!I zzaB|LSN1$~SJH;=`mZo_nOc{jfAKL{dmK9by7Nx6rRQCb?0K}b@=iln{g@=Obb0%$|lGDrrM^{ZAjdl%xBZf9WAwXBs)Jv-2KXs$*f`+(w(Kt+LZ2 zzEAi0A>EgoenM_=*pgl?n>IwUH=t^F4N^Q z^e;YEXPy_<4~I@a=e*Nw`FPhOd;U$Q)pHuU>c0$~E)1WUe$A&kOS@q{)Ic+5IL^C^7Kr!xGJ5*`ruETgjl%CIO=Ut)DhH$0 zjXw`VM)EXbmyoJ{`h4}V`~85w^pLCzO_+v6+f#a=?J6wcik>D+tD;R8rdYZDUh?PHm%=%dSe#~EZh`xZvOsnCvv!Et~F>@PjnyjKdpvv#Z%+LEZ zW*P*oay<~ntmtXXt|C_cv@sWKn`t=Ksl4N6th++Yb(!hQja=*Pf9GR#2ljW{c9+;` z+zxdK@M+jzuA1fNK3fXK`my|l`ml4X+rO2`S8o4!tjC7>HAPi=W+%@U10SP%z)DWO z0_(rG9O?wmvg`BTQvH19xc@(%UVEGn&%zt$pMG;?+)wR-<1C^lTfcMrO?^LOu6w)H zhgtQ>R-IVl-ho`uPN6NQT{SIAt=}?F3RyFxLWzmGZ%?nm(sf{=wL&hMa{EfQ6-+x- z*)UW2ArGGZDoDxeyx;rMF=H*6GEwEf>K#S z$w~>O`(NP|o6L#?Wlxq?4575oA}EziP%0bnku^im)PI7KE(@Wg4?`%GOi(IoC|N0? zbpIxiJK{{W?3AVF!LNKi5vpk$U%x>7>v{;$7MN}&l%_C#qt2}v7EAX32qq@v`}R+M;D z?+HihO2kpd6KO;+!HB3?WSL^oy{~{`@iA}5&91JFU*oF#&>V(z8YtaFmi5;zo$5w~ zhs}PosY`Yo#%q8_d$nn_mbnX>;Cppy-3n3Bb4etK}pw(t0a9f zE|W?oD3vvotdvl?{|l6l^HJHT%%}uqPu4CO&QAKyuB=t&T7ptpL&-`BrTR}$(nTYb z^x4?Ik_k#>4J9ijlmn=a^ zSB_B9w`2cGCMcCPl&q9cYW{1y=cZYfK$C?Qv$6sSrQAQirc^RPsjOE@T7!gA{U<2t z@)1hD!D|;s`NOP>STgZ+Z1B7E*YHe>h6D=x;fym zKMb2g-6S`VNF`Dr%BfV}$OQL_!4R#QJ64&if}!fmnR`RYW$0HLC!kX4F8 zcYgh4P=aPy2RownL2+ERQ3?zd3>Yd(3|XZxRObOh`lMP3X{2f?R4`zuC^2M}!qA;R z4a0d;zkqc|*LHW;y2Q>ne3W2oVd#2@BI=1FkJuic2|5XoBLS-2zkyW+d+pi_+E*n1mCGAc=uY%XO&#bDJ z&{->vO6Z8vC*_Wj`KdbV*I}WE*6*E1gzUhn!_QStowp|kjFetkCG}Lbu3%g5v_F;g zFr~acbm~ui1*R?~F$NHOqS9Tx=OD2TDpUq*B2iglL)le)srP^)1yZ6&DOJ5Im{3%f zD6&qW=-#h?_>;OAQ0$3HawUq?Q8f+)6N<_bMJldBQN0Hgsf`jvs;KH+!Gxl+M3Hq0 zMfZMv6eTPM6g#3+TZtkiRF1>ffAfryC5o(5D602>B4tsQNdZ;8E0|DJmMF4Lq3GVP zkD@fifMQQnLMu_EddhLwXZ4JcC5qHpg`#>7C{ht+nbc0zyMhTtWr-r|6pHTs`Y1|H z3@G+QrLhu43a1)}f(-_}zg3nfQeqW~>fI<>3{{zyP0>4p0YzqoqU#ij?)`$|<5U=R ziL2L{L|Kd>NqJQuQZ!YSGq2$1`7WcP(CR8ERNnzY8lfywlBxPeE+L3031pEV(0yM3 ze4O^87VG1Lg)$nr>WNBOC4zM}b@BXq-<*t9&zBoro^ENM6fD(RQK;SnhIOiGsjIR~ zilrP9`bi*iqXLX|G6+OgIEBNDd5-EkEYvlrZii$nf2=?6d)!~yxX!stUw7iKUFLh9j!Pi0MWDylqtP$|3Yr{uz zQzcRbMc2v&_!JdnFk2ZiQ4O?e_D$+f4iMxY;-Pvnqj(X6a@+9LM`Btd8{|94f{$hvdmH^! zT}iV=UH`pfT8Hmt*C)r6dsWYz_x}^m47+d{lnH{KtVC1q9XO#b)uw5f6t^mdP^d9c z_pfe3780iml^5#vm29h@kWl}u17YbYt6as}1=S6L_J6(%UVvXV~;r94p&MacxEvc^aH zX-JND8xV?(1G<+q&|7 z{Zg)Q_?By!pzO*@Naac?AvFvImT7|0TNRX2QVpg0Pf$wsG?em3-M^9vN@b0jDh)|q zVfF&$qhvrgD(f&o*_D-=N+@NKhFTAIyWPCmw(nWi_ulJmaJ%fTNA0Z&O3A5)QvC-g zseQ82HvdIe?4QX1C9_10l``3iT2#8mN+}UBFxeBOq$(sOsfLLx)zbi^w+SApsgg(a zo^X@{Y8+vex_1N!7fPFGFUd#=7LHBh{gq=gzL zLT59K8Cg>%VKpRaZH6qARaYvz)cq@&kW|*BQCfZQ{8w2Yjxe59_1*rp@yNrOHELEt2%P4QbVc!)1g~>sG*c`>i(5XP%3NGRB1@o_=f#vf81^7 zLtSWmoZ5-7%iEhrebvN`%2P~Gc4Z~Aa;22a>ROddP%3NGq_c)3udo`j+q><&FDYWb zt(%)1>tb1TpGckDALO38el8j|dPg_TnFVuG?KOLJ65 zZwanpCa_i$lisS1-YC>as{3^CmON@C<)gZNB@>d$8Z=cJlJi|%d~IK6%MW$BeAv}h z*j71sLu?E{b|j^`a*2fM8pg2{SQCugCRnV3hTT}NkM`5dL8P5se2DWs?#_ z*s1Irxd0)O1WP44sO!pJe@T?w7y#^vQYgiOC_|N#P%sdvD8Z8U>L4yd8xWLKN(5!8 zvU3Fkgo+X_Rp}r*zdnZ290P_OQLClEQ2MKf0jV`$=xt&lRF%X~od*o1ml8wys_b0B zfT5zKOI12J-_;>vGuNkL+i9A2^RAuWdK!lHqG{Y6#=|_1o8w_qhobpn!H)Inr}Htn zsKao#JMNGBy3TU_cdCz-EgbJX^5b zid@u@m7FYC)_=BO>w>cA`t-NBSNY6LUg27+BMXU~vMUPLC`k_k#>jT$TEn(F>9P(JR%4-Q&pXM(aP zOShB<8v3fNRmrqcD(eHSD;2)#KS3$C)KJP-HIzywD3vvotdvl?{}rH=*_oj1%EE&6 zN=aXpP%4?ARMt?^g5^r7{u7jPOAV!bRYR#{f>K#S$w~>O`(FV{nT-j`t}HBALrGti zP%4?ARMt?^f+dvdKR`*hlu)))8w06@EG$^VSf}L|7i|p_ zK{Da!t>X0|FWYKz)qes~ZmEHkuj>AlOh_tgCRr(&bpI)lJ-Tw+u%4|$fc4c9~dZnbV%9T>dHWZvP zFPr~v*iR+NjK}SvKCj%5bt_1&f3#qov<2&|ELc`5U)4}5nV?kGP|AW0N%p@2lr~#k zR96-jtf8c@N+^{~P%3LEX~7aS_1~UftE${m|N0cE3%Ax#Dw&{EHcYi{Q{F6Gg?@LLoRW`bm5(pwchEL1^tpOBPWY9!^W zx_u=RlFAx1R!St@|MiiC;u(PKNXjoIjxtucLL!)8M3gHTEm$piXgrW8#grhzStW+Z z1qhKOf-F%4vg_;fXSJ23_e5dA@*quPRRk&+2vn2=(t;I%>O3GQzmy2dSS5oB1_%}P zC^vOa@L{M!(NJIUDogLszTbu8fIGiFh7vr(I@l4VU#f+W#;Pz>Fkq-CF?5v}hB^-z z$}c5`GFFM9f&oKCNf)aWhVJ}n7}kg9+q%5OJkOhANN(EMR(p6>iO4ieJmns`3Dg*Yxvwy0I&qjmQ%A&fm zGGM*?;I*1aS*>j-Si=-GJQQGwnc7cCQY%%VG*vyR&-T+{K3(vW`_x<2O6NkE{G$K* zIMtAi%4tk%WLH-HD|a7FRo80&Rg>mM2^#fRCw&oEW+|1bPY^>vc6%J_<);d*U9z~Ys*e#$CM1x zgjav%l$S};Xzf)rs`UgTeNvSvLzSH)m{3I2B)UqF=+;*NQ6^&|u`4U|IB#`?Hms`HX{vT?JiTU8y#ZCwX|G^+iCBUMrrN;{R^E17^))=aop(PUTRPY(rVTf*cezXjEQ_q}oqNQY2NObW_>Ak_ky=jid_|l5T$mBxNxs zBzv;-NQoq`(CVSsc;%!KHIgipNUHsWBsEeMN;8$+E18f~)=0WgA?fy4Kr%S2Ize6n z+R`CS_|hUJk~C926gaGHC|D)AQG&)ookaTyNlK(Dw4Q0{<+6Jv6OzgrNf#<4HT^Zt zMh-F<6J}jm*{(#AUMh#8WI$3;uacBqiKN<3NK$qclC)CUy^;w@WsRf@6_Rd$g;i1p zV?wegD}|Iq(n|GA?7DK$h?+>%Ng&mH0+Ir$>ZFs(=9Nr9Dr+EJsDO0)*9Q_pW5BT^ zDbtlO%1ZS(Y9Ad-2pW#V{xH5Zv_K{iV{K>2?*Wz(;%!j zird3x9_sAwu-)wIzKS2O^xED<^*u(_wBDd>>aw)cupLWtou=_}rB^@q*>P=eb+_Yo zx1F|4HYh>%a4)&;Se2gH$+P6j$LN}}l9R8>`tL2*t-3z{E!EFwj{EK#8G^~_R zYW}N0DSt6R*_D;)N+>O*YN1pzL8+|qq3TK~)qi__tv@tK2_>ymLaAheQdvXEN(rU= zzr135oLI<4WiTcvd$RTVd+E9o%Ibe4JCC~LaF`}l(a|*CCyaN zYIAqk)b(@b`U2{4o=R(P_w@2>N^ezbIx8iV?tcX+WiciwyR!0K38mFkL8)YdQdvVu z;gwLT{{W>uQbB1qRZub+pk$USr7IOx^ty>TOs@>AJ6f(b@M%_5yw zv8cJP@%Cnr$(UH|%2Fl8DT{&5t0w}HHQ~su5*%6O`O4QKRN%tC~1=tN*bz!Qpp6RvWAkC5=zZ~6)0shCMbKd)=9Ne(tGt#AhRYYxmBXZ zN|{vu?fLPhq3$PqH`OJF>b~;xrmjC!cLk`AqzEM)RrarBf>K#S$w~>O`(NReqbDkYLMRoT9h2}xy*Br7G7?*IBoLUas3b|mG$^0+Pk)#F%N ztqDeLQ;1Q3Ww0*Ew{&lr+}=&YJnfI0Y1;3nW4S~=V9Qrk-^c|BktBjNU_~Ity~aBl zNsa-)o~R615olc%$7lrufr|17O$An5sq^;ac!5c~RBIu9RWYbwfKX9V$SOslJHP%i zD9tfo*b%i}isLdJSPlbbYrv4(6f&$*L3JK5q+cowX{@Sq1p|hP5<^xg4Bh$DFl<6n zUDt9uA4@bH%R6nFSG%5Qakp#d2)iDmi+ZTDa>I70Ybw`uTjz1VyIkqj&waLF_uKlw zqdvQ+ON?!%y1~ijcrU>=+i6*pp4iE=1k1;$qNT7WXNin8|Bg z>6L}c>`VysWaYqm??COWtIT28!GYzVxKR5XcUr2a}I)qX;fQmK%nr^@b?Oh_tgBv~ktbo(nHDWfqV*^{OH zN+j!r>eBx7P>>8rdZR*;g%U}%pOBHlFAxM7D^=D{t8ISXG}mw8N@l z^jndlijpJ;6wyRAtIgW#Y3Pe<)9HYk*bqGs`&&Y1ya>XCzZ`BnSfN*K(bH*>GrP=B!tF*V@Fb^ zE0;%EsU8PSSB`j@B#9JVMI!nR9MU31jj~eJHF5z#BncoZ6n|{``t(U;4EXg#<++MK zI;on3f(?S@>qRBWA2nC;r^W+-6i87dja2omU_ek&GRPvupc}tFgwhxTgdI`)qc|ed zNaZkWxoW;h5<(U!2-SFikoqW!q>rk;6$}t6N(fn`AavtTgRssL?dIKnKOc9)VYfN% zhmTi!t+P^{k5M(9?+)9#LwtP^Z!A}B-fZhTlE*SxM?d%3ajnyQMag{J?&r;M-W@la z?d5N=Dm`=USl3?@<70G9S;@&)W&QV-Yn`WCc76I=+^c+MCVyzTK29oR;j$NN>0uE) zS=yuAI}%b^YOq{ADQ;C{s8C{}?h{f~(_HW#$GX^Y-4l6Rx^{stxM`%aeI?rhsGX{8 zn5im#@bsVh%_BD2R{zgE%YnLaF`}l=Meg zX*&jfJ^`=WN+u|kHI%HBP`dvW4vz8{6O>(9nXZJg-aaf%S3;>|f>K!@CaJm-O7)+h zq(Mq3X{EA%B@>j&8cJ45DBb@GP|9FTQ1)c4km^7~E!9J@>*`S>YbaSMp;Z3~N;;&h zlwKrT z@u=Ptj+9A>qx@9%j$ncjQM1T0#iDy(0Y#aNiN&t0oL9m~=hYK|$eM8UR>dQwSMsR- z6OeRC2_*eg_OE0@Qdu*}O39@AUjfSMesT5E1Z7uN)+?c``7bKqi4dx+2}*BOP!=gm zKbKId{{&^VIuGM89*?_enl?jSu*PR6>qcu2?O(|RrLu;Sl@dz#zXFu9855K}S?i>D z&ppt4t^OCEdN7%w^i~BWDKlq)42RrarBf>K#S$w~>O`(FV{`HTt5t}KO8 zK}ku~L$UelQ6tN@H&$N>rTR}$(kNx6v{c!@k_k#>4J9ijlEnV?kGP_j}&>Hb%MQdVPvvL{QcR0}2T zSI-1WYhuz{)zMpp5=nKRkfc>gBx$O$eI*l;${I;lN+jL?^^t_=7=Y|Z%75h&DgV{u zSX!+KMs5= zs1951^pE@C$3OV&r&p(~-$&b)Pd_eyIOon!-TtZne|7UOH~)C^_6Lpqh@JiD(%Qe@ zy!gde!|flwyV=~G6w$H$>wlmB*Dr2{+yDB5t#M#>TOWtt z@7#iGb60wE{bRoU&GUZ#$n!J9AC`{OO7Q=}`tZ!~X}SGm9?$FWpW89+nc-)K?_axp zc!l-M@XhmUw$m&9=hwrZ{%)-KUgLQkeqM(^Wv#uEdR~X0*WvRSm*;i()8CEfb@;i=Iq1ATri@%&oi`L)DCeRzH?@%&oi`L)Ee^OMx5=k)>W z!}Dv2=hqU?uO*&eOFX-mr?=YY6Tctcb^q{(=kpRj+#i3A=P;i?UeYi~siKci-Lq z)3zS{&Fw$my!h$6@87<^9d7>muP=YGq~`m-cys?ZfBx?6`1Y(Lhw z-&(2d$K&m38EHSxx2NCa_G2Z7_FHxOd8qHoU49;Khs(27c(^=Yg-@gTP1{*7zWSG! zr{jDT+WzOSU%r3;`rEg+_y6;6|M>nFU%h|%{&o9<(-+k0W)r8U`o&juvCOuM>uG=S zRb7MXTx8qE|M=B!|Mm5&_xIcQ_=05obPcS1`Md95z5e$0FaPzswl`~fcbmVe*D>|l z=lz>^Z-3UF^e=z*UxxX3sOw#A&j0z(o6Uc!|NE;?=e%6ee)_V0d*A(`byF{uZtr*B zum0`z+rN3$wtacoR4--T|K07|@4x=K#?`Wa|MEZHeP1*E)!WxUefRbE-@N_gdH(qA z>)+pO>JGa%_qEi&Zm0Tg=Kl8gx2Heb9ovm~Z%+^3zPWwB6bM zbIH~AYV(Ufn-0axXSw5M`DgR48vkK#zghm>{kU1?w%bK5^?l5m-)Gya+kGtl>@Y6& z!Tz}HqyFk*d(-gwe%T$Tv61`3=bh^2B+EbB)o;P)xx;7szP5MRE$W+&+fQnF|Lw~+ zU)L+4hWuA={?F?+^B2E)_wN1e*jOvzfBE+J?`}^xxBv08+kd+K@#&er@Q=TKNaUXz aZ{NLt`R)4?ZB^{Noo@c(FaGvdKmWfxc*4B^ literal 0 HcmV?d00001 diff --git a/tests/cbioportal-overlap/spec.svg b/tests/cbioportal-overlap/spec.svg new file mode 100644 index 0000000..e85bbe5 --- /dev/null +++ b/tests/cbioportal-overlap/spec.svg @@ -0,0 +1,2536 @@ +]>Samples overlap01224364860728496108120132144156168180192Overlap count2040608001224364860728496108120132144156168180192(Q>800(P750-800(O700-750(N650-700(M600-650(L550-600(K500-550(J450-500(I400-450(H350-400(G300-350(F250-300(E200-250(D150-200(C100-150(B50-100(A<=50Patients overlap01224364860728496108120132144156168180192Overlap count2040608001224364860728496108120132144156168180192(Q>800(P750-800(O700-750(N650-700(M600-650(L550-600(K500-550(J450-500(I400-450(H350-400(G300-350(F250-300(E200-250(D150-200(C100-150(B50-100(A<=50 \ No newline at end of file diff --git a/tests/runTests.js b/tests/runTests.js index d2f6b13..4ea6663 100644 --- a/tests/runTests.js +++ b/tests/runTests.js @@ -1,6 +1,7 @@ const debug = false; const tests = [ + "cbioportal-overlap", "clippath", "complete-bpmn", "complete-computer-network",