UNPKG

svg2pdf.js

Version:

A javascript-only SVG to PDF conversion utility that runs in the browser leveraging jsPDF

1,297 lines (1,269 loc) 179 kB
/** * The MIT License (MIT) * * Copyright (c) 2015-2023 yWorks GmbH * Copyright (c) 2013-2015 by Vitaly Puzrin * * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ import cssEsc from 'cssesc'; import FontFamily from 'font-family-papandreou'; import { jsPDF, GState, ShadingPattern, TilingPattern } from 'jspdf'; import SvgPath from 'svgpath'; import { compare } from 'specificity'; /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function __generator(thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } /* eslint-disable @typescript-eslint/no-explicit-any,@typescript-eslint/explicit-module-boundary-types */ var RGBColor = /** @class */ (function () { function RGBColor(colorString) { this.a = undefined; this.r = 0; this.g = 0; this.b = 0; this.simpleColors = {}; // eslint-disable-next-line @typescript-eslint/ban-types this.colorDefs = []; this.ok = false; if (!colorString) { return; } // strip any leading # if (colorString.charAt(0) == '#') { // remove # if any colorString = colorString.substr(1, 6); } colorString = colorString.replace(/ /g, ''); colorString = colorString.toLowerCase(); // before getting into regexps, try simple matches // and overwrite the input this.simpleColors = { 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', darkgrey: 'a9a9a9', darkgreen: '006400', darkkhaki: 'bdb76b', darkmagenta: '8b008b', darkolivegreen: '556b2f', darkorange: 'ff8c00', darkorchid: '9932cc', darkred: '8b0000', darksalmon: 'e9967a', darkseagreen: '8fbc8f', darkslateblue: '483d8b', darkslategray: '2f4f4f', darkslategrey: '2f4f4f', darkturquoise: '00ced1', darkviolet: '9400d3', deeppink: 'ff1493', deepskyblue: '00bfff', dimgray: '696969', dimgrey: '696969', dodgerblue: '1e90ff', feldspar: 'd19275', firebrick: 'b22222', floralwhite: 'fffaf0', forestgreen: '228b22', fuchsia: 'ff00ff', gainsboro: 'dcdcdc', ghostwhite: 'f8f8ff', gold: 'ffd700', goldenrod: 'daa520', gray: '808080', grey: '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', lightgray: 'd3d3d3', lightgrey: 'd3d3d3', lightgreen: '90ee90', lightpink: 'ffb6c1', lightsalmon: 'ffa07a', lightseagreen: '20b2aa', lightskyblue: '87cefa', lightslateblue: '8470ff', lightslategray: '778899', lightslategrey: '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', slategrey: '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 key in this.simpleColors) { if (colorString == key) { colorString = this.simpleColors[key]; } } // emd of simple type-in colors // array of color definition objects this.colorDefs = [ { re: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/, example: ['rgb(123, 234, 45)', 'rgb(255,234,245)'], process: function (bits) { return [parseInt(bits[1]), parseInt(bits[2]), parseInt(bits[3])]; } }, { re: /^rgb\(([0-9.]+)%,\s*([0-9.]+)%,\s*([0-9.]+)%\)$/, example: ['rgb(50.5%, 25.75%, 75.5%)', 'rgb(100%,0%,0%)'], process: function (bits) { return [ Math.round(parseFloat(bits[1]) * 2.55), Math.round(parseFloat(bits[2]) * 2.55), Math.round(parseFloat(bits[3]) * 2.55) ]; } }, { re: /^(\w{2})(\w{2})(\w{2})$/, example: ['#00ff00', '336699'], process: function (bits) { return [parseInt(bits[1], 16), parseInt(bits[2], 16), parseInt(bits[3], 16)]; } }, { re: /^(\w{1})(\w{1})(\w{1})$/, example: ['#fb0', 'f0f'], process: function (bits) { return [ parseInt(bits[1] + bits[1], 16), parseInt(bits[2] + bits[2], 16), parseInt(bits[3] + bits[3], 16) ]; } } ]; // search through the definitions to find a match for (var i = 0; i < this.colorDefs.length; i++) { var re = this.colorDefs[i].re; var processor = this.colorDefs[i].process; var bits = re.exec(colorString); if (bits) { var channels = processor(bits); this.r = channels[0]; this.g = channels[1]; this.b = channels[2]; this.ok = true; } } // validate/cleanup values this.r = this.r < 0 || isNaN(this.r) ? 0 : this.r > 255 ? 255 : this.r; this.g = this.g < 0 || isNaN(this.g) ? 0 : this.g > 255 ? 255 : this.g; this.b = this.b < 0 || isNaN(this.b) ? 0 : this.b > 255 ? 255 : this.b; } RGBColor.prototype.toRGB = function () { return 'rgb(' + this.r + ', ' + this.g + ', ' + this.b + ')'; }; RGBColor.prototype.toRGBA = function () { return 'rgba(' + this.r + ', ' + this.g + ', ' + this.b + ', ' + (this.a || '1') + ')'; }; RGBColor.prototype.toHex = function () { var r = this.r.toString(16); var g = this.g.toString(16); var b = this.b.toString(16); if (r.length == 1) r = '0' + r; if (g.length == 1) g = '0' + g; if (b.length == 1) b = '0' + b; return '#' + r + g + b; }; // help RGBColor.prototype.getHelpXML = function () { var examples = []; // add regexps for (var i = 0; i < this.colorDefs.length; i++) { var example = this.colorDefs[i].example; for (var j = 0; j < example.length; j++) { examples[examples.length] = example[j]; } } // add type-in colors for (var sc in this.simpleColors) { examples[examples.length] = sc; } var xml = document.createElement('ul'); xml.setAttribute('id', 'rgbcolor-examples'); for (var i = 0; i < examples.length; i++) { try { var listItem = document.createElement('li'); var listColor = new RGBColor(examples[i]); var exampleDiv = document.createElement('div'); exampleDiv.style.cssText = 'margin: 3px; ' + 'border: 1px solid black; ' + 'background:' + listColor.toHex() + '; ' + 'color:' + listColor.toHex(); exampleDiv.appendChild(document.createTextNode('test')); var listItemValue = document.createTextNode(' ' + examples[i] + ' -> ' + listColor.toRGB() + ' -> ' + listColor.toHex()); listItem.appendChild(exampleDiv); listItem.appendChild(listItemValue); xml.appendChild(listItem); } catch (e) { } } return xml; }; return RGBColor; }()); var ColorFill = /** @class */ (function () { function ColorFill(color) { this.color = color; } // eslint-disable-next-line @typescript-eslint/no-unused-vars ColorFill.prototype.getFillData = function (forNode, context) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, undefined]; }); }); }; return ColorFill; }()); var AttributeState = /** @class */ (function () { function AttributeState() { this.xmlSpace = ''; this.fill = null; this.fillOpacity = 1.0; // public fillRule: string = null this.fontFamily = ''; this.fontSize = 16; this.fontStyle = ''; // public fontVariant: string this.fontWeight = ''; this.opacity = 1.0; this.stroke = null; this.strokeDasharray = null; this.strokeDashoffset = 0; this.strokeLinecap = ''; this.strokeLinejoin = ''; this.strokeMiterlimit = 4.0; this.strokeOpacity = 1.0; this.strokeWidth = 1.0; // public textAlign: string this.alignmentBaseline = ''; this.textAnchor = ''; this.visibility = ''; this.color = null; this.contextFill = null; this.contextStroke = null; this.fillRule = null; } AttributeState.prototype.clone = function () { var clone = new AttributeState(); clone.xmlSpace = this.xmlSpace; clone.fill = this.fill; clone.fillOpacity = this.fillOpacity; // clone.fillRule = this.fillRule; clone.fontFamily = this.fontFamily; clone.fontSize = this.fontSize; clone.fontStyle = this.fontStyle; // clone.fontVariant = this.fontVariant; clone.fontWeight = this.fontWeight; clone.opacity = this.opacity; clone.stroke = this.stroke; clone.strokeDasharray = this.strokeDasharray; clone.strokeDashoffset = this.strokeDashoffset; clone.strokeLinecap = this.strokeLinecap; clone.strokeLinejoin = this.strokeLinejoin; clone.strokeMiterlimit = this.strokeMiterlimit; clone.strokeOpacity = this.strokeOpacity; clone.strokeWidth = this.strokeWidth; // clone.textAlign = this.textAlign; clone.textAnchor = this.textAnchor; clone.alignmentBaseline = this.alignmentBaseline; clone.visibility = this.visibility; clone.color = this.color; clone.fillRule = this.fillRule; clone.contextFill = this.contextFill; clone.contextStroke = this.contextStroke; return clone; }; AttributeState.default = function () { var attributeState = new AttributeState(); attributeState.xmlSpace = 'default'; attributeState.fill = new ColorFill(new RGBColor('rgb(0, 0, 0)')); attributeState.fillOpacity = 1.0; // attributeState.fillRule = "nonzero"; attributeState.fontFamily = 'times'; attributeState.fontSize = 16; attributeState.fontStyle = 'normal'; // attributeState.fontVariant = "normal"; attributeState.fontWeight = 'normal'; attributeState.opacity = 1.0; attributeState.stroke = null; attributeState.strokeDasharray = null; attributeState.strokeDashoffset = 0; attributeState.strokeLinecap = 'butt'; attributeState.strokeLinejoin = 'miter'; attributeState.strokeMiterlimit = 4.0; attributeState.strokeOpacity = 1.0; attributeState.strokeWidth = 1.0; // attributeState.textAlign = "start"; attributeState.alignmentBaseline = 'baseline'; attributeState.textAnchor = 'start'; attributeState.visibility = 'visible'; attributeState.color = new RGBColor('rgb(0, 0, 0)'); attributeState.fillRule = 'nonzero'; attributeState.contextFill = null; attributeState.contextStroke = null; return attributeState; }; AttributeState.getContextColors = function (context, includeCurrentColor) { if (includeCurrentColor === void 0) { includeCurrentColor = false; } var colors = {}; if (context.attributeState.contextFill) { colors['contextFill'] = context.attributeState.contextFill; } if (context.attributeState.contextStroke) { colors['contextStroke'] = context.attributeState.contextStroke; } if (includeCurrentColor && context.attributeState.color) { colors['color'] = context.attributeState.color; } return colors; }; return AttributeState; }()); /** * * @package * @param values * @constructor * @property pdf * @property attributeState Keeps track of parent attributes that are inherited automatically * @property refsHandler The handler that will render references on demand * @property styleSheets * @property textMeasure * @property transform The current transformation matrix * @property withinClipPath */ var Context = /** @class */ (function () { function Context(pdf, values) { var _a, _b, _c; this.pdf = pdf; this.svg2pdfParameters = values.svg2pdfParameters; this.attributeState = values.attributeState ? values.attributeState.clone() : AttributeState.default(); this.viewport = values.viewport; this.refsHandler = values.refsHandler; this.styleSheets = values.styleSheets; this.textMeasure = values.textMeasure; this.transform = (_a = values.transform) !== null && _a !== void 0 ? _a : this.pdf.unitMatrix; this.withinClipPath = (_b = values.withinClipPath) !== null && _b !== void 0 ? _b : false; this.withinUse = (_c = values.withinUse) !== null && _c !== void 0 ? _c : false; } Context.prototype.clone = function (values) { var _a, _b, _c, _d; if (values === void 0) { values = {}; } return new Context(this.pdf, { svg2pdfParameters: this.svg2pdfParameters, attributeState: values.attributeState ? values.attributeState.clone() : this.attributeState.clone(), viewport: (_a = values.viewport) !== null && _a !== void 0 ? _a : this.viewport, refsHandler: this.refsHandler, styleSheets: this.styleSheets, textMeasure: this.textMeasure, transform: (_b = values.transform) !== null && _b !== void 0 ? _b : this.transform, withinClipPath: (_c = values.withinClipPath) !== null && _c !== void 0 ? _c : this.withinClipPath, withinUse: (_d = values.withinUse) !== null && _d !== void 0 ? _d : this.withinUse }); }; return Context; }()); var ReferencesHandler = /** @class */ (function () { function ReferencesHandler(idMap) { this.renderedElements = {}; this.idMap = idMap; this.idPrefix = String(ReferencesHandler.instanceCounter++); } ReferencesHandler.prototype.getRendered = function (id, contextColors, renderCallback) { return __awaiter(this, void 0, void 0, function () { var key, svgNode; return __generator(this, function (_a) { switch (_a.label) { case 0: key = this.generateKey(id, contextColors); if (this.renderedElements.hasOwnProperty(key)) { return [2 /*return*/, this.renderedElements[id]]; } svgNode = this.get(id); this.renderedElements[key] = svgNode; return [4 /*yield*/, renderCallback(svgNode)]; case 1: _a.sent(); return [2 /*return*/, svgNode]; } }); }); }; ReferencesHandler.prototype.get = function (id) { return this.idMap[cssEsc(id, { isIdentifier: true })]; }; ReferencesHandler.prototype.generateKey = function (id, contextColors) { var colorHash = ''; var keys = ['color', 'contextFill', 'contextStroke']; if (contextColors) { colorHash = keys.map(function (key) { var _a, _b; return (_b = (_a = contextColors[key]) === null || _a === void 0 ? void 0 : _a.toRGBA()) !== null && _b !== void 0 ? _b : ''; }).join('|'); } return this.idPrefix + '|' + id + '|' + colorHash; }; ReferencesHandler.instanceCounter = 0; return ReferencesHandler; }()); function getAngle(from, to) { return Math.atan2(to[1] - from[1], to[0] - from[0]); } var cToQ = 2 / 3; // ratio to convert quadratic bezier curves to cubic ones // transforms a cubic bezier control point to a quadratic one: returns from + (2/3) * (to - from) function toCubic(from, to) { return [cToQ * (to[0] - from[0]) + from[0], cToQ * (to[1] - from[1]) + from[1]]; } function normalize(v) { var length = Math.sqrt(v[0] * v[0] + v[1] * v[1]); return [v[0] / length, v[1] / length]; } function getDirectionVector(from, to) { var v = [to[0] - from[0], to[1] - from[1]]; return normalize(v); } function addVectors(v1, v2) { return [v1[0] + v2[0], v1[1] + v2[1]]; } // multiplies a vector with a matrix: vec' = vec * matrix function multVecMatrix(vec, matrix) { var x = vec[0]; var y = vec[1]; return [matrix.a * x + matrix.c * y + matrix.e, matrix.b * x + matrix.d * y + matrix.f]; } var Path = /** @class */ (function () { function Path() { this.segments = []; } Path.prototype.moveTo = function (x, y) { this.segments.push(new MoveTo(x, y)); return this; }; Path.prototype.lineTo = function (x, y) { this.segments.push(new LineTo(x, y)); return this; }; Path.prototype.curveTo = function (x1, y1, x2, y2, x, y) { this.segments.push(new CurveTo(x1, y1, x2, y2, x, y)); return this; }; Path.prototype.close = function () { this.segments.push(new Close()); return this; }; /** * Transforms the path in place */ Path.prototype.transform = function (matrix) { this.segments.forEach(function (seg) { if (seg instanceof MoveTo || seg instanceof LineTo || seg instanceof CurveTo) { var p = multVecMatrix([seg.x, seg.y], matrix); seg.x = p[0]; seg.y = p[1]; } if (seg instanceof CurveTo) { var p1 = multVecMatrix([seg.x1, seg.y1], matrix); var p2 = multVecMatrix([seg.x2, seg.y2], matrix); seg.x1 = p1[0]; seg.y1 = p1[1]; seg.x2 = p2[0]; seg.y2 = p2[1]; } }); }; Path.prototype.draw = function (context) { var p = context.pdf; this.segments.forEach(function (s) { if (s instanceof MoveTo) { p.moveTo(s.x, s.y); } else if (s instanceof LineTo) { p.lineTo(s.x, s.y); } else if (s instanceof CurveTo) { p.curveTo(s.x1, s.y1, s.x2, s.y2, s.x, s.y); } else { p.close(); } }); }; return Path; }()); var MoveTo = /** @class */ (function () { function MoveTo(x, y) { this.x = x; this.y = y; } return MoveTo; }()); var LineTo = /** @class */ (function () { function LineTo(x, y) { this.x = x; this.y = y; } return LineTo; }()); var CurveTo = /** @class */ (function () { function CurveTo(x1, y1, x2, y2, x, y) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; this.x = x; this.y = y; } return CurveTo; }()); var Close = /** @class */ (function () { function Close() { } return Close; }()); function nodeIs(node, tagsString) { return tagsString.split(',').indexOf((node.nodeName || node.tagName).toLowerCase()) >= 0; } function forEachChild(node, fn) { // copy list of children, as the original might be modified var children = []; for (var i = 0; i < node.childNodes.length; i++) { var childNode = node.childNodes[i]; if (childNode.nodeName.charAt(0) !== '#') children.push(childNode); } for (var i = 0; i < children.length; i++) { fn(i, children[i]); } } // returns an attribute of a node, either from the node directly or from css function getAttribute(node, styleSheets, propertyNode, propertyCss) { var _a; if (propertyCss === void 0) { propertyCss = propertyNode; } var attribute = (_a = node.style) === null || _a === void 0 ? void 0 : _a.getPropertyValue(propertyCss); if (attribute) { return attribute; } else { var propertyValue = styleSheets.getPropertyValue(node, propertyCss); if (propertyValue) { return propertyValue; } else if (node.hasAttribute(propertyNode)) { return node.getAttribute(propertyNode) || undefined; } else { return undefined; } } } function svgNodeIsVisible(svgNode, parentVisible, context) { if (getAttribute(svgNode.element, context.styleSheets, 'display') === 'none') { return false; } var visible = parentVisible; var visibility = getAttribute(svgNode.element, context.styleSheets, 'visibility'); if (visibility) { visible = visibility !== 'hidden'; } return visible; } function svgNodeAndChildrenVisible(svgNode, parentVisible, context) { var visible = svgNodeIsVisible(svgNode, parentVisible, context); if (svgNode.element.childNodes.length === 0) { return false; } svgNode.children.forEach(function (child) { if (child.isVisible(visible, context)) { visible = true; } }); return visible; } /** * @constructor * @property {Marker[]} markers */ var MarkerList = /** @class */ (function () { function MarkerList() { this.markers = []; } MarkerList.prototype.addMarker = function (markers) { this.markers.push(markers); }; MarkerList.prototype.draw = function (context) { return __awaiter(this, void 0, void 0, function () { var i, marker, tf, angle, anchor, cos, sin, contextColors; return __generator(this, function (_a) { switch (_a.label) { case 0: i = 0; _a.label = 1; case 1: if (!(i < this.markers.length)) return [3 /*break*/, 4]; marker = this.markers[i]; tf = void 0; angle = marker.angle, anchor = marker.anchor; cos = Math.cos(angle); sin = Math.sin(angle); // position at and rotate around anchor tf = context.pdf.Matrix(cos, sin, -sin, cos, anchor[0], anchor[1]); // scale with stroke-width tf = context.pdf.matrixMult(context.pdf.Matrix(context.attributeState.strokeWidth, 0, 0, context.attributeState.strokeWidth, 0, 0), tf); tf = context.pdf.matrixMult(tf, context.transform); // as the marker is already scaled by the current line width we must not apply the line width twice! context.pdf.saveGraphicsState(); contextColors = AttributeState.getContextColors(context); return [4 /*yield*/, context.refsHandler.getRendered(marker.id, contextColors, function (node) { return node.apply(context); })]; case 2: _a.sent(); context.pdf.doFormObject(context.refsHandler.generateKey(marker.id, contextColors), tf); context.pdf.restoreGraphicsState(); _a.label = 3; case 3: i++; return [3 /*break*/, 1]; case 4: return [2 /*return*/]; } }); }); }; return MarkerList; }()); /** * @param {string} id * @param {[number,number]} anchor * @param {number} angle */ var Marker = /** @class */ (function () { function Marker(id, anchor, angle, isStartMarker) { if (isStartMarker === void 0) { isStartMarker = false; } this.id = id; this.anchor = anchor; this.angle = angle; this.isStartMarker = isStartMarker; } return Marker; }()); var iriReference = /url\(["']?#([^"']+)["']?\)/; var alignmentBaselineMap = { bottom: 'bottom', 'text-bottom': 'bottom', top: 'top', 'text-top': 'top', hanging: 'hanging', middle: 'middle', central: 'middle', center: 'middle', mathematical: 'middle', ideographic: 'ideographic', alphabetic: 'alphabetic', baseline: 'alphabetic' }; var svgNamespaceURI = 'http://www.w3.org/2000/svg'; /** * Convert em, px and bare number attributes to pixel values * @param {string} value * @param {number} pdfFontSize */ function toPixels(value, pdfFontSize) { var match; // em match = value && value.toString().match(/^([\-0-9.]+)em$/); if (match) { return parseFloat(match[1]) * pdfFontSize; } // pixels match = value && value.toString().match(/^([\-0-9.]+)(px|)$/); if (match) { return parseFloat(match[1]); } return 0; } function mapAlignmentBaseline(value) { return alignmentBaselineMap[value] || 'alphabetic'; } function parseFloats(str) { var floats = []; var regex = /[+-]?(?:(?:\d+\.?\d*)|(?:\d*\.?\d+))(?:[eE][+-]?\d+)?/g; var match; while ((match = regex.exec(str))) { floats.push(parseFloat(match[0])); } return floats; } /** * extends RGBColor by rgba colors as RGBColor is not capable of it * currentcolor: the color to return if colorString === 'currentcolor' */ function parseColor(colorString, contextColors) { if (colorString === 'transparent') { var transparent = new RGBColor('rgb(0,0,0)'); transparent.a = 0; return transparent; } if (contextColors && colorString.toLowerCase() === 'currentcolor') { return contextColors.color || new RGBColor('rgb(0,0,0)'); } if (contextColors && colorString.toLowerCase() === 'context-stroke') { return contextColors.contextStroke || new RGBColor('rgb(0,0,0)'); } if (contextColors && colorString.toLowerCase() === 'context-fill') { return contextColors.contextFill || new RGBColor('rgb(0,0,0)'); } var match = /\s*rgba\(((?:[^,\)]*,){3}[^,\)]*)\)\s*/.exec(colorString); if (match) { var floats = parseFloats(match[1]); var color = new RGBColor('rgb(' + floats.slice(0, 3).join(',') + ')'); color.a = floats[3]; return color; } else { return new RGBColor(colorString); } } var fontAliases = { 'sans-serif': 'helvetica', verdana: 'helvetica', arial: 'helvetica', fixed: 'courier', monospace: 'courier', terminal: 'courier', serif: 'times', cursive: 'times', fantasy: 'times' }; function findFirstAvailableFontFamily(attributeState, fontFamilies, context) { var fontType = combineFontStyleAndFontWeight(attributeState.fontStyle, attributeState.fontWeight); var availableFonts = context.pdf.getFontList(); var firstAvailable = ''; var fontIsAvailable = fontFamilies.some(function (font) { var availableStyles = availableFonts[font]; if (availableStyles && availableStyles.indexOf(fontType) >= 0) { firstAvailable = font; return true; } font = font.toLowerCase(); if (fontAliases.hasOwnProperty(font)) { firstAvailable = font; return true; } return false; }); if (!fontIsAvailable) { firstAvailable = 'times'; } return firstAvailable; } var isJsPDF23 = (function () { var parts = jsPDF.version.split('.'); return parseFloat(parts[0]) === 2 && parseFloat(parts[1]) === 3; })(); function combineFontStyleAndFontWeight(fontStyle, fontWeight) { if (isJsPDF23) { return fontWeight == 400 ? fontStyle == 'italic' ? 'italic' : 'normal' : fontWeight == 700 && fontStyle !== 'italic' ? 'bold' : fontStyle + '' + fontWeight; } else { return fontWeight == 400 || fontWeight === 'normal' ? fontStyle === 'italic' ? 'italic' : 'normal' : (fontWeight == 700 || fontWeight === 'bold') && fontStyle === 'normal' ? 'bold' : (fontWeight == 700 ? 'bold' : fontWeight) + '' + fontStyle; } } function getBoundingBoxByChildren(context, svgnode) { if (getAttribute(svgnode.element, context.styleSheets, 'display') === 'none') { return [0, 0, 0, 0]; } var boundingBox = []; svgnode.children.forEach(function (child) { var nodeBox = child.getBoundingBox(context); if ((nodeBox[0] === 0) && (nodeBox[1] === 0) && (nodeBox[2] === 0) && (nodeBox[3] === 0)) return; var transform = child.computeNodeTransform(context); // TODO: take into account rotation matrix nodeBox[0] = nodeBox[0] * transform.sx + transform.tx; nodeBox[1] = nodeBox[1] * transform.sy + transform.ty; nodeBox[2] = nodeBox[2] * transform.sx; nodeBox[3] = nodeBox[3] * transform.sy; if (boundingBox.length === 0) boundingBox = nodeBox; else boundingBox = [ Math.min(boundingBox[0], nodeBox[0]), Math.min(boundingBox[1], nodeBox[1]), Math.max(boundingBox[0] + boundingBox[2], nodeBox[0] + nodeBox[2]) - Math.min(boundingBox[0], nodeBox[0]), Math.max(boundingBox[1] + boundingBox[3], nodeBox[1] + nodeBox[3]) - Math.min(boundingBox[1], nodeBox[1]) ]; }); return boundingBox.length === 0 ? [0, 0, 0, 0] : boundingBox; } function defaultBoundingBox(element, context) { // eslint-disable-next-line @typescript-eslint/no-explicit-any var pf = parseFloat; // TODO: check if there are other possible coordinate attributes var x1 = pf(element.getAttribute('x1')) || pf(getAttribute(element, context.styleSheets, 'x')) || pf(getAttribute(element, context.styleSheets, 'cx')) - pf(getAttribute(element, context.styleSheets, 'r')) || 0; var x2 = pf(element.getAttribute('x2')) || x1 + pf(getAttribute(element, context.styleSheets, 'width')) || pf(getAttribute(element, context.styleSheets, 'cx')) + pf(getAttribute(element, context.styleSheets, 'r')) || 0; var y1 = pf(element.getAttribute('y1')) || pf(getAttribute(element, context.styleSheets, 'y')) || pf(getAttribute(element, context.styleSheets, 'cy')) - pf(getAttribute(element, context.styleSheets, 'r')) || 0; var y2 = pf(element.getAttribute('y2')) || y1 + pf(getAttribute(element, context.styleSheets, 'height')) || pf(getAttribute(element, context.styleSheets, 'cy')) + pf(getAttribute(element, context.styleSheets, 'r')) || 0; return [ Math.min(x1, x2), Math.min(y1, y2), Math.max(x1, x2) - Math.min(x1, x2), Math.max(y1, y2) - Math.min(y1, y2) ]; } function computeViewBoxTransform(node, viewBox, eX, eY, eWidth, eHeight, context, noTranslate) { if (noTranslate === void 0) { noTranslate = false; } var vbX = viewBox[0]; var vbY = viewBox[1]; var vbWidth = viewBox[2]; var vbHeight = viewBox[3]; var scaleX = eWidth / vbWidth; var scaleY = eHeight / vbHeight; var align, meetOrSlice; var preserveAspectRatio = node.getAttribute('preserveAspectRatio'); if (preserveAspectRatio) { var alignAndMeetOrSlice = preserveAspectRatio.split(' '); if (alignAndMeetOrSlice[0] === 'defer') { alignAndMeetOrSlice = alignAndMeetOrSlice.slice(1); } align = alignAndMeetOrSlice[0]; meetOrSlice = alignAndMeetOrSlice[1] || 'meet'; } else { align = 'xMidYMid'; meetOrSlice = 'meet'; } if (align !== 'none') { if (meetOrSlice === 'meet') { // uniform scaling with min scale scaleX = scaleY = Math.min(scaleX, scaleY); } else if (meetOrSlice === 'slice') { // uniform scaling with max scale scaleX = scaleY = Math.max(scaleX, scaleY); } } if (noTranslate) { return context.pdf.Matrix(scaleX, 0, 0, scaleY, 0, 0); } var translateX = eX - vbX * scaleX; var translateY = eY - vbY * scaleY; if (align.indexOf('xMid') >= 0) { translateX += (eWidth - vbWidth * scaleX) / 2; } else if (align.indexOf('xMax') >= 0) { translateX += eWidth - vbWidth * scaleX; } if (align.indexOf('YMid') >= 0) { translateY += (eHeight - vbHeight * scaleY) / 2; } else if (align.indexOf('YMax') >= 0) { translateY += eHeight - vbHeight * scaleY; } var translate = context.pdf.Matrix(1, 0, 0, 1, translateX, translateY); var scale = context.pdf.Matrix(scaleX, 0, 0, scaleY, 0, 0); return context.pdf.matrixMult(scale, translate); } // parses the "transform" string function parseTransform(transformString, context) { if (!transformString || transformString === 'none') return context.pdf.unitMatrix; var mRegex = /^[\s,]*matrix\(([^)]+)\)\s*/, tRegex = /^[\s,]*translate\(([^)]+)\)\s*/, rRegex = /^[\s,]*rotate\(([^)]+)\)\s*/, sRegex = /^[\s,]*scale\(([^)]+)\)\s*/, sXRegex = /^[\s,]*skewX\(([^)]+)\)\s*/, sYRegex = /^[\s,]*skewY\(([^)]+)\)\s*/; var resultMatrix = context.pdf.unitMatrix; var m; var tSLength; while (transformString.length > 0 && transformString.length !== tSLength) { tSLength = transformString.length; var match = mRegex.exec(transformString); if (match) { m = parseFloats(match[1]); resultMatrix = context.pdf.matrixMult(context.pdf.Matrix(m[0], m[1], m[2], m[3], m[4], m[5]), resultMatrix); transformString = transformString.substr(match[0].length); } match = rRegex.exec(transformString); if (match) { m = parseFloats(match[1]); var a = (Math.PI * m[0]) / 180; resultMatrix = context.pdf.matrixMult(context.pdf.Matrix(Math.cos(a), Math.sin(a), -Math.sin(a), Math.cos(a), 0, 0), resultMatrix); if (m[1] || m[2]) { var t1 = context.pdf.Matrix(1, 0, 0, 1, m[1], m[2]); var t2 = context.pdf.Matrix(1, 0, 0, 1, -m[1], -m[2]); resultMatrix = context.pdf.matrixMult(t2, context.pdf.matrixMult(resultMatrix, t1)); } transformString = transformString.substr(match[0].length); } match = tRegex.exec(transformString); if (match) { m = parseFloats(match[1]); resultMatrix = context.pdf.matrixMult(context.pdf.Matrix(1, 0, 0, 1, m[0], m[1] || 0), resultMatrix); transformString = transformString.substr(match[0].length); } match = sRegex.exec(transformString); if (match) { m = parseFloats(match[1]); if (!m[1]) m[1] = m[0]; resultMatrix = context.pdf.matrixMult(context.pdf.Matrix(m[0], 0, 0, m[1], 0, 0), resultMatrix); transformString = transformString.substr(match[0].length); } match = sXRegex.exec(transformString); if (match) { m = parseFloat(match[1]); m *= Math.PI / 180; resultMatrix = context.pdf.matrixMult(context.pdf.Matrix(1, 0, Math.tan(m), 1, 0, 0), resultMatrix); transformString = transformString.substr(match[0].length); } match = sYRegex.exec(transformString); if (match) { m = parseFloat(match[1]); m *= Math.PI / 180; resultMatrix = context.pdf.matrixMult(context.pdf.Matrix(1, Math.tan(m), 0, 1, 0, 0), resultMatrix); transformString = transformString.substr(match[0].length); } } return resultMatrix; } var SvgNode = /** @class */ (function () { function SvgNode(element, children) { this.element = element; this.children = children; this.parent = null; } SvgNode.prototype.setParent = function (parent) { this.parent = parent; }; SvgNode.prototype.getParent = function () { return this.parent; }; SvgNode.prototype.getBoundingBox = function (context) { if (getAttribute(this.element, context.styleSheets, 'display') === 'none') { return [0, 0, 0, 0]; } return this.getBoundingBoxCore(context); }; SvgNode.prototype.computeNodeTransform = function (context) { var nodeTransform = this.computeNodeTransformCore(context); var transformString = getAttribute(this.element, context.styleSheets, 'transform'); if (!transformString) return nodeTransform; else return context.pdf.matrixMult(nodeTransform, parseTransform(transformString, context)); }; return SvgNode; }()); var NonRenderedNode = /** @class */ (function (_super) { __extends(NonRenderedNode, _super); function NonRenderedNode() { return _super !== null && _super.apply(this, arguments) || this; } // eslint-disable-next-line @typescript-eslint/no-unused-vars NonRenderedNode.prototype.render = function (parentContext) { return Promise.resolve(); }; // eslint-disable-next-line @typescript-eslint/no-unused-vars NonRenderedNode.prototype.getBoundingBoxCore = function (context) { return []; }; NonRenderedNode.prototype.computeNodeTransformCore = function (context) { return context.pdf.unitMatrix; }; return NonRenderedNode; }(SvgNode)); var Gradient = /** @class */ (function (_super) { __extends(Gradient, _super); function Gradient(pdfGradientType, element, children) { var _this = _super.call(this, element, children) || this; _this.pdfGradientType = pdfGradientType; _this.contextColor = undefined; return _this; } Gradient.prototype.apply = function (context) { return __awaiter(this, void 0, void 0, function () { var id, colors, opacitySum, hasOpacity, gState, pattern; return __generator(this, function (_a) { id = this.element.getAttribute('id'); if (!id) { return [2 /*return*/]; } colors = this.getStops(context.styleSheets); opacitySum = 0; hasOpacity = false; colors.forEach(function (_a) { var opacity = _a.opacity; if (opacity && opacity !== 1) { opacitySum += opacity; hasOpacity = true; } }); if (hasOpacity) { gState = new GState({ opacity: opacitySum / colors.length }); } pattern = new ShadingPattern(this.pdfGradientType, this.getCoordinates(), colors, gState); context.pdf.addShadingPattern(id, pattern); return [2 /*return*/]; }); }); }; Gradient.prototype.getStops = function (styleSheets) { var _this = this; if (this.stops) { return this.stops; } // Only need to calculate contextColor once if (this.contextColor === undefined) { this.contextColor = null; var ancestor = this; while (ancestor) { var colorAttr = getAttribute(ancestor.element, styleSheets, 'color'); if (colorAttr) { this.contextColor = parseColor(colorAttr, null); break; } ancestor = ancestor.getParent(); } } var stops = []; this.children.forEach(function (stop) { if (stop.element.tagName.toLowerCase() === 'stop') { var colorAttr = getAttribute(stop.element, styleSheets, 'color'); var color = parseColor(getAttribute(stop.element, styleSheets, 'stop-color') || '', colorAttr ? { color: parseColor(colorAttr, null) } : { color: _this.contextColor }); var opacity = parseFloat(getAttribute(stop.element, styleSheets, 'stop-opacity') || '1'); stops.push({ offset: Gradient.parseGradientOffset(stop.element.getAttribute('offset') || '0'), color: [color.r, color.g, color.b], opacity: opacity }); } }); return (this.stops = stops); }; Gradient.prototype.getBoundingBoxCore = function (context) {