From bf7bb8658bb2364576f0fabe501ecec95d9bbc0e Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Thu, 4 Aug 2022 18:39:57 +0300 Subject: [PATCH 01/22] checkout --- src/parser.ts | 1096 ------------------ src/parser/applyViewboxTransform.ts | 118 ++ src/parser/constants.ts | 70 ++ src/parser/doesSomeParentMatch.ts | 15 + src/parser/elementById.ts | 20 + src/parser/elementMatchesRule.ts | 18 + src/parser/getCSSRules.ts | 53 + src/parser/getGlobalStylesForElement.ts | 19 + src/parser/getGradientDefs.ts | 28 + src/parser/getMultipleNodes.ts | 11 + src/parser/getSvgRegex.ts | 5 + src/parser/hasAncestorWithNodeName.ts | 11 + src/parser/index.ts | 335 ++++++ src/parser/loadSVGFromString.ts | 17 + src/parser/loadSVGFromURL.ts | 34 + src/parser/normalizeAttr.ts | 11 + src/parser/normalizeValue.ts | 74 ++ src/parser/parseAttributes.ts | 73 ++ src/parser/parseElements.ts | 16 + src/parser/parseFontDeclaration.ts | 32 + src/parser/parsePointsAttribute.ts | 28 + src/parser/parseStyleAttribute.ts | 21 + src/parser/parseStyleObject.ts | 15 + src/parser/parseStyleString.ts | 14 + src/parser/parseTransformAttribute.ts | 86 ++ src/parser/parseUseDirectives.ts | 59 + src/parser/recursivelyParseGradientsXlink.ts | 21 + src/parser/rotateMatrix.ts | 18 + src/parser/scaleMatrix.ts | 8 + src/parser/selectorMatches.ts | 22 + src/parser/setStrokeFillOpacity.ts | 34 + src/parser/skewMatrix.ts | 7 + src/parser/translateMatrix.ts | 8 + 33 files changed, 1301 insertions(+), 1096 deletions(-) delete mode 100644 src/parser.ts create mode 100644 src/parser/applyViewboxTransform.ts create mode 100644 src/parser/constants.ts create mode 100644 src/parser/doesSomeParentMatch.ts create mode 100644 src/parser/elementById.ts create mode 100644 src/parser/elementMatchesRule.ts create mode 100644 src/parser/getCSSRules.ts create mode 100644 src/parser/getGlobalStylesForElement.ts create mode 100644 src/parser/getGradientDefs.ts create mode 100644 src/parser/getMultipleNodes.ts create mode 100644 src/parser/getSvgRegex.ts create mode 100644 src/parser/hasAncestorWithNodeName.ts create mode 100644 src/parser/index.ts create mode 100644 src/parser/loadSVGFromString.ts create mode 100644 src/parser/loadSVGFromURL.ts create mode 100644 src/parser/normalizeAttr.ts create mode 100644 src/parser/normalizeValue.ts create mode 100644 src/parser/parseAttributes.ts create mode 100644 src/parser/parseElements.ts create mode 100644 src/parser/parseFontDeclaration.ts create mode 100644 src/parser/parsePointsAttribute.ts create mode 100644 src/parser/parseStyleAttribute.ts create mode 100644 src/parser/parseStyleObject.ts create mode 100644 src/parser/parseStyleString.ts create mode 100644 src/parser/parseTransformAttribute.ts create mode 100644 src/parser/parseUseDirectives.ts create mode 100644 src/parser/recursivelyParseGradientsXlink.ts create mode 100644 src/parser/rotateMatrix.ts create mode 100644 src/parser/scaleMatrix.ts create mode 100644 src/parser/selectorMatches.ts create mode 100644 src/parser/setStrokeFillOpacity.ts create mode 100644 src/parser/skewMatrix.ts create mode 100644 src/parser/translateMatrix.ts diff --git a/src/parser.ts b/src/parser.ts deleted file mode 100644 index 7a9a373cb9c..00000000000 --- a/src/parser.ts +++ /dev/null @@ -1,1096 +0,0 @@ -//@ts-nocheck - -import { Color } from "./color"; - -(function(global) { - /** - * @name fabric - * @namespace - */ - - var fabric = global.fabric || (global.fabric = { }), - toFixed = fabric.util.toFixed, - parseUnit = fabric.util.parseUnit, - multiplyTransformMatrices = fabric.util.multiplyTransformMatrices, - - svgValidTagNames = ['path', 'circle', 'polygon', 'polyline', 'ellipse', 'rect', 'line', - 'image', 'text'], - svgViewBoxElements = ['symbol', 'image', 'marker', 'pattern', 'view', 'svg'], - svgInvalidAncestors = ['pattern', 'defs', 'symbol', 'metadata', 'clipPath', 'mask', 'desc'], - svgValidParents = ['symbol', 'g', 'a', 'svg', 'clipPath', 'defs'], - - attributesMap = { - cx: 'left', - x: 'left', - r: 'radius', - cy: 'top', - y: 'top', - display: 'visible', - visibility: 'visible', - transform: 'transformMatrix', - 'fill-opacity': 'fillOpacity', - 'fill-rule': 'fillRule', - 'font-family': 'fontFamily', - 'font-size': 'fontSize', - 'font-style': 'fontStyle', - 'font-weight': 'fontWeight', - 'letter-spacing': 'charSpacing', - 'paint-order': 'paintFirst', - 'stroke-dasharray': 'strokeDashArray', - 'stroke-dashoffset': 'strokeDashOffset', - 'stroke-linecap': 'strokeLineCap', - 'stroke-linejoin': 'strokeLineJoin', - 'stroke-miterlimit': 'strokeMiterLimit', - 'stroke-opacity': 'strokeOpacity', - 'stroke-width': 'strokeWidth', - 'text-decoration': 'textDecoration', - 'text-anchor': 'textAnchor', - opacity: 'opacity', - 'clip-path': 'clipPath', - 'clip-rule': 'clipRule', - 'vector-effect': 'strokeUniform', - 'image-rendering': 'imageSmoothing', - }, - - colorAttributes = { - stroke: 'strokeOpacity', - fill: 'fillOpacity' - }, - - fSize = 'font-size', cPath = 'clip-path'; - - fabric.svgValidTagNamesRegEx = getSvgRegex(svgValidTagNames); - fabric.svgViewBoxElementsRegEx = getSvgRegex(svgViewBoxElements); - fabric.svgInvalidAncestorsRegEx = getSvgRegex(svgInvalidAncestors); - fabric.svgValidParentsRegEx = getSvgRegex(svgValidParents); - - fabric.cssRules = { }; - fabric.gradientDefs = { }; - fabric.clipPaths = { }; - - function normalizeAttr(attr) { - // transform attribute names - if (attr in attributesMap) { - return attributesMap[attr]; - } - return attr; - } - - function normalizeValue(attr, value, parentAttributes, fontSize) { - var isArray = Array.isArray(value), parsed; - - if ((attr === 'fill' || attr === 'stroke') && value === 'none') { - value = ''; - } - else if (attr === 'strokeUniform') { - return (value === 'non-scaling-stroke'); - } - else if (attr === 'strokeDashArray') { - if (value === 'none') { - value = null; - } - else { - value = value.replace(/,/g, ' ').split(/\s+/).map(parseFloat); - } - } - else if (attr === 'transformMatrix') { - if (parentAttributes && parentAttributes.transformMatrix) { - value = multiplyTransformMatrices( - parentAttributes.transformMatrix, fabric.parseTransformAttribute(value)); - } - else { - value = fabric.parseTransformAttribute(value); - } - } - else if (attr === 'visible') { - value = value !== 'none' && value !== 'hidden'; - // display=none on parent element always takes precedence over child element - if (parentAttributes && parentAttributes.visible === false) { - value = false; - } - } - else if (attr === 'opacity') { - value = parseFloat(value); - if (parentAttributes && typeof parentAttributes.opacity !== 'undefined') { - value *= parentAttributes.opacity; - } - } - else if (attr === 'textAnchor' /* text-anchor */) { - value = value === 'start' ? 'left' : value === 'end' ? 'right' : 'center'; - } - else if (attr === 'charSpacing') { - // parseUnit returns px and we convert it to em - parsed = parseUnit(value, fontSize) / fontSize * 1000; - } - else if (attr === 'paintFirst') { - var fillIndex = value.indexOf('fill'); - var strokeIndex = value.indexOf('stroke'); - var value = 'fill'; - if (fillIndex > -1 && strokeIndex > -1 && strokeIndex < fillIndex) { - value = 'stroke'; - } - else if (fillIndex === -1 && strokeIndex > -1) { - value = 'stroke'; - } - } - else if (attr === 'href' || attr === 'xlink:href' || attr === 'font') { - return value; - } - else if (attr === 'imageSmoothing') { - return (value === 'optimizeQuality'); - } - else { - parsed = isArray ? value.map(parseUnit) : parseUnit(value, fontSize); - } - - return (!isArray && isNaN(parsed) ? value : parsed); - } - - /** - * @private - */ - function getSvgRegex(arr) { - return new RegExp('^(' + arr.join('|') + ')\\b', 'i'); - } - - /** - * @private - * @param {Object} attributes Array of attributes to parse - */ - function _setStrokeFillOpacity(attributes) { - for (var attr in colorAttributes) { - - if (typeof attributes[colorAttributes[attr]] === 'undefined' || attributes[attr] === '') { - continue; - } - - if (typeof attributes[attr] === 'undefined') { - if (!fabric.Object.prototype[attr]) { - continue; - } - attributes[attr] = fabric.Object.prototype[attr]; - } - - if (attributes[attr].indexOf('url(') === 0) { - continue; - } - - var color = new Color(attributes[attr]); - attributes[attr] = color.setAlpha(toFixed(color.getAlpha() * attributes[colorAttributes[attr]], 2)).toRgba(); - } - return attributes; - } - - /** - * @private - */ - function _getMultipleNodes(doc, nodeNames) { - var nodeName, nodeArray = [], nodeList, i, len; - for (i = 0, len = nodeNames.length; i < len; i++) { - nodeName = nodeNames[i]; - nodeList = doc.getElementsByTagName(nodeName); - nodeArray = nodeArray.concat(Array.prototype.slice.call(nodeList)); - } - return nodeArray; - } - - /** - * Parses "transform" attribute, returning an array of values - * @static - * @function - * @memberOf fabric - * @param {String} attributeValue String containing attribute value - * @return {Array} Array of 6 elements representing transformation matrix - */ - fabric.parseTransformAttribute = (function() { - function rotateMatrix(matrix, args) { - var cos = fabric.util.cos(args[0]), sin = fabric.util.sin(args[0]), - x = 0, y = 0; - if (args.length === 3) { - x = args[1]; - y = args[2]; - } - - matrix[0] = cos; - matrix[1] = sin; - matrix[2] = -sin; - matrix[3] = cos; - matrix[4] = x - (cos * x - sin * y); - matrix[5] = y - (sin * x + cos * y); - } - - function scaleMatrix(matrix, args) { - var multiplierX = args[0], - multiplierY = (args.length === 2) ? args[1] : args[0]; - - matrix[0] = multiplierX; - matrix[3] = multiplierY; - } - - function skewMatrix(matrix, args, pos) { - matrix[pos] = Math.tan(fabric.util.degreesToRadians(args[0])); - } - - function translateMatrix(matrix, args) { - matrix[4] = args[0]; - if (args.length === 2) { - matrix[5] = args[1]; - } - } - - // identity matrix - var iMatrix = fabric.iMatrix, - - // == begin transform regexp - number = fabric.reNum, - - commaWsp = fabric.commaWsp, - - skewX = '(?:(skewX)\\s*\\(\\s*(' + number + ')\\s*\\))', - - skewY = '(?:(skewY)\\s*\\(\\s*(' + number + ')\\s*\\))', - - rotate = '(?:(rotate)\\s*\\(\\s*(' + number + ')(?:' + - commaWsp + '(' + number + ')' + - commaWsp + '(' + number + '))?\\s*\\))', - - scale = '(?:(scale)\\s*\\(\\s*(' + number + ')(?:' + - commaWsp + '(' + number + '))?\\s*\\))', - - translate = '(?:(translate)\\s*\\(\\s*(' + number + ')(?:' + - commaWsp + '(' + number + '))?\\s*\\))', - - matrix = '(?:(matrix)\\s*\\(\\s*' + - '(' + number + ')' + commaWsp + - '(' + number + ')' + commaWsp + - '(' + number + ')' + commaWsp + - '(' + number + ')' + commaWsp + - '(' + number + ')' + commaWsp + - '(' + number + ')' + - '\\s*\\))', - - transform = '(?:' + - matrix + '|' + - translate + '|' + - scale + '|' + - rotate + '|' + - skewX + '|' + - skewY + - ')', - - transforms = '(?:' + transform + '(?:' + commaWsp + '*' + transform + ')*' + ')', - - transformList = '^\\s*(?:' + transforms + '?)\\s*$', - - // http://www.w3.org/TR/SVG/coords.html#TransformAttribute - reTransformList = new RegExp(transformList), - // == end transform regexp - - reTransform = new RegExp(transform, 'g'); - - return function(attributeValue) { - - // start with identity matrix - var matrix = iMatrix.concat(), - matrices = []; - - // return if no argument was given or - // an argument does not match transform attribute regexp - if (!attributeValue || (attributeValue && !reTransformList.test(attributeValue))) { - return matrix; - } - - attributeValue.replace(reTransform, function(match) { - - var m = new RegExp(transform).exec(match).filter(function (match) { - // match !== '' && match != null - return (!!match); - }), - operation = m[1], - args = m.slice(2).map(parseFloat); - - switch (operation) { - case 'translate': - translateMatrix(matrix, args); - break; - case 'rotate': - args[0] = fabric.util.degreesToRadians(args[0]); - rotateMatrix(matrix, args); - break; - case 'scale': - scaleMatrix(matrix, args); - break; - case 'skewX': - skewMatrix(matrix, args, 2); - break; - case 'skewY': - skewMatrix(matrix, args, 1); - break; - case 'matrix': - matrix = args; - break; - } - - // snapshot current matrix into matrices array - matrices.push(matrix.concat()); - // reset - matrix = iMatrix.concat(); - }); - - var combinedMatrix = matrices[0]; - while (matrices.length > 1) { - matrices.shift(); - combinedMatrix = fabric.util.multiplyTransformMatrices(combinedMatrix, matrices[0]); - } - return combinedMatrix; - }; - })(typeof exports !== 'undefined' ? exports : window); - - /** - * @private - */ - function parseStyleString(style, oStyle) { - var attr, value; - style.replace(/;\s*$/, '').split(';').forEach(function (chunk) { - var pair = chunk.split(':'); - - attr = pair[0].trim().toLowerCase(); - value = pair[1].trim(); - - oStyle[attr] = value; - }); - } - - /** - * @private - */ - function parseStyleObject(style, oStyle) { - var attr, value; - for (var prop in style) { - if (typeof style[prop] === 'undefined') { - continue; - } - - attr = prop.toLowerCase(); - value = style[prop]; - - oStyle[attr] = value; - } - } - - /** - * @private - */ - function getGlobalStylesForElement(element, svgUid) { - var styles = { }; - for (var rule in fabric.cssRules[svgUid]) { - if (elementMatchesRule(element, rule.split(' '))) { - for (var property in fabric.cssRules[svgUid][rule]) { - styles[property] = fabric.cssRules[svgUid][rule][property]; - } - } - } - return styles; - } - - /** - * @private - */ - function elementMatchesRule(element, selectors) { - var firstMatching, parentMatching = true; - //start from rightmost selector. - firstMatching = selectorMatches(element, selectors.pop()); - if (firstMatching && selectors.length) { - parentMatching = doesSomeParentMatch(element, selectors); - } - return firstMatching && parentMatching && (selectors.length === 0); - } - - function doesSomeParentMatch(element, selectors) { - var selector, parentMatching = true; - while (element.parentNode && element.parentNode.nodeType === 1 && selectors.length) { - if (parentMatching) { - selector = selectors.pop(); - } - element = element.parentNode; - parentMatching = selectorMatches(element, selector); - } - return selectors.length === 0; - } - - /** - * @private - */ - function selectorMatches(element, selector) { - var nodeName = element.nodeName, - classNames = element.getAttribute('class'), - id = element.getAttribute('id'), matcher, i; - // i check if a selector matches slicing away part from it. - // if i get empty string i should match - matcher = new RegExp('^' + nodeName, 'i'); - selector = selector.replace(matcher, ''); - if (id && selector.length) { - matcher = new RegExp('#' + id + '(?![a-zA-Z\\-]+)', 'i'); - selector = selector.replace(matcher, ''); - } - if (classNames && selector.length) { - classNames = classNames.split(' '); - for (i = classNames.length; i--;) { - matcher = new RegExp('\\.' + classNames[i] + '(?![a-zA-Z\\-]+)', 'i'); - selector = selector.replace(matcher, ''); - } - } - return selector.length === 0; - } - - /** - * @private - * to support IE8 missing getElementById on SVGdocument and on node xmlDOM - */ - function elementById(doc, id) { - var el; - doc.getElementById && (el = doc.getElementById(id)); - if (el) { - return el; - } - var node, i, len, nodelist = doc.getElementsByTagName('*'); - for (i = 0, len = nodelist.length; i < len; i++) { - node = nodelist[i]; - if (id === node.getAttribute('id')) { - return node; - } - } - } - - /** - * @private - */ - function parseUseDirectives(doc) { - var nodelist = _getMultipleNodes(doc, ['use', 'svg:use']), i = 0; - while (nodelist.length && i < nodelist.length) { - var el = nodelist[i], - xlinkAttribute = el.getAttribute('xlink:href') || el.getAttribute('href'); - - if (xlinkAttribute === null) { - return; - } - - var xlink = xlinkAttribute.slice(1), - x = el.getAttribute('x') || 0, - y = el.getAttribute('y') || 0, - el2 = elementById(doc, xlink).cloneNode(true), - currentTrans = (el2.getAttribute('transform') || '') + ' translate(' + x + ', ' + y + ')', - parentNode, - oldLength = nodelist.length, attr, - j, - attrs, - len, - namespace = fabric.svgNS; - - applyViewboxTransform(el2); - if (/^svg$/i.test(el2.nodeName)) { - var el3 = el2.ownerDocument.createElementNS(namespace, 'g'); - for (j = 0, attrs = el2.attributes, len = attrs.length; j < len; j++) { - attr = attrs.item(j); - el3.setAttributeNS(namespace, attr.nodeName, attr.nodeValue); - } - // el2.firstChild != null - while (el2.firstChild) { - el3.appendChild(el2.firstChild); - } - el2 = el3; - } - - for (j = 0, attrs = el.attributes, len = attrs.length; j < len; j++) { - attr = attrs.item(j); - if (attr.nodeName === 'x' || attr.nodeName === 'y' || - attr.nodeName === 'xlink:href' || attr.nodeName === 'href') { - continue; - } - - if (attr.nodeName === 'transform') { - currentTrans = attr.nodeValue + ' ' + currentTrans; - } - else { - el2.setAttribute(attr.nodeName, attr.nodeValue); - } - } - - el2.setAttribute('transform', currentTrans); - el2.setAttribute('instantiated_by_use', '1'); - el2.removeAttribute('id'); - parentNode = el.parentNode; - parentNode.replaceChild(el2, el); - // some browsers do not shorten nodelist after replaceChild (IE8) - if (nodelist.length === oldLength) { - i++; - } - } - } - - // http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute - // matches, e.g.: +14.56e-12, etc. - var reViewBoxAttrValue = new RegExp( - '^' + - '\\s*(' + fabric.reNum + '+)\\s*,?' + - '\\s*(' + fabric.reNum + '+)\\s*,?' + - '\\s*(' + fabric.reNum + '+)\\s*,?' + - '\\s*(' + fabric.reNum + '+)\\s*' + - '$' - ); - - /** - * Add a element that envelop all child elements and makes the viewbox transformMatrix descend on all elements - */ - function applyViewboxTransform(element) { - if (!fabric.svgViewBoxElementsRegEx.test(element.nodeName)) { - return {}; - } - var viewBoxAttr = element.getAttribute('viewBox'), - scaleX = 1, - scaleY = 1, - minX = 0, - minY = 0, - viewBoxWidth, viewBoxHeight, matrix, el, - widthAttr = element.getAttribute('width'), - heightAttr = element.getAttribute('height'), - x = element.getAttribute('x') || 0, - y = element.getAttribute('y') || 0, - preserveAspectRatio = element.getAttribute('preserveAspectRatio') || '', - missingViewBox = (!viewBoxAttr || !(viewBoxAttr = viewBoxAttr.match(reViewBoxAttrValue))), - missingDimAttr = (!widthAttr || !heightAttr || widthAttr === '100%' || heightAttr === '100%'), - toBeParsed = missingViewBox && missingDimAttr, - parsedDim = { }, translateMatrix = '', widthDiff = 0, heightDiff = 0; - - parsedDim.width = 0; - parsedDim.height = 0; - parsedDim.toBeParsed = toBeParsed; - - if (missingViewBox) { - if (((x || y) && element.parentNode && element.parentNode.nodeName !== '#document')) { - translateMatrix = ' translate(' + parseUnit(x) + ' ' + parseUnit(y) + ') '; - matrix = (element.getAttribute('transform') || '') + translateMatrix; - element.setAttribute('transform', matrix); - element.removeAttribute('x'); - element.removeAttribute('y'); - } - } - - if (toBeParsed) { - return parsedDim; - } - - if (missingViewBox) { - parsedDim.width = parseUnit(widthAttr); - parsedDim.height = parseUnit(heightAttr); - // set a transform for elements that have x y and are inner(only) SVGs - return parsedDim; - } - minX = -parseFloat(viewBoxAttr[1]); - minY = -parseFloat(viewBoxAttr[2]); - viewBoxWidth = parseFloat(viewBoxAttr[3]); - viewBoxHeight = parseFloat(viewBoxAttr[4]); - parsedDim.minX = minX; - parsedDim.minY = minY; - parsedDim.viewBoxWidth = viewBoxWidth; - parsedDim.viewBoxHeight = viewBoxHeight; - if (!missingDimAttr) { - parsedDim.width = parseUnit(widthAttr); - parsedDim.height = parseUnit(heightAttr); - scaleX = parsedDim.width / viewBoxWidth; - scaleY = parsedDim.height / viewBoxHeight; - } - else { - parsedDim.width = viewBoxWidth; - parsedDim.height = viewBoxHeight; - } - - // default is to preserve aspect ratio - preserveAspectRatio = fabric.util.parsePreserveAspectRatioAttribute(preserveAspectRatio); - if (preserveAspectRatio.alignX !== 'none') { - //translate all container for the effect of Mid, Min, Max - if (preserveAspectRatio.meetOrSlice === 'meet') { - scaleY = scaleX = (scaleX > scaleY ? scaleY : scaleX); - // calculate additional translation to move the viewbox - } - if (preserveAspectRatio.meetOrSlice === 'slice') { - scaleY = scaleX = (scaleX > scaleY ? scaleX : scaleY); - // calculate additional translation to move the viewbox - } - widthDiff = parsedDim.width - viewBoxWidth * scaleX; - heightDiff = parsedDim.height - viewBoxHeight * scaleX; - if (preserveAspectRatio.alignX === 'Mid') { - widthDiff /= 2; - } - if (preserveAspectRatio.alignY === 'Mid') { - heightDiff /= 2; - } - if (preserveAspectRatio.alignX === 'Min') { - widthDiff = 0; - } - if (preserveAspectRatio.alignY === 'Min') { - heightDiff = 0; - } - } - - if (scaleX === 1 && scaleY === 1 && minX === 0 && minY === 0 && x === 0 && y === 0) { - return parsedDim; - } - if ((x || y) && element.parentNode.nodeName !== '#document') { - translateMatrix = ' translate(' + parseUnit(x) + ' ' + parseUnit(y) + ') '; - } - - matrix = translateMatrix + ' matrix(' + scaleX + - ' 0' + - ' 0 ' + - scaleY + ' ' + - (minX * scaleX + widthDiff) + ' ' + - (minY * scaleY + heightDiff) + ') '; - // seems unused. - // parsedDim.viewboxTransform = fabric.parseTransformAttribute(matrix); - if (element.nodeName === 'svg') { - el = element.ownerDocument.createElementNS(fabric.svgNS, 'g'); - // element.firstChild != null - while (element.firstChild) { - el.appendChild(element.firstChild); - } - element.appendChild(el); - } - else { - el = element; - el.removeAttribute('x'); - el.removeAttribute('y'); - matrix = el.getAttribute('transform') + matrix; - } - el.setAttribute('transform', matrix); - return parsedDim; - } - - function hasAncestorWithNodeName(element, nodeName) { - while (element && (element = element.parentNode)) { - if (element.nodeName && nodeName.test(element.nodeName.replace('svg:', '')) - && !element.getAttribute('instantiated_by_use')) { - return true; - } - } - return false; - } - - /** - * Parses an SVG document, converts it to an array of corresponding fabric.* instances and passes them to a callback - * @static - * @function - * @memberOf fabric - * @param {SVGDocument} doc SVG document to parse - * @param {Function} callback Callback to call when parsing is finished; - * It's being passed an array of elements (parsed from a document). - * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created. - * @param {Object} [parsingOptions] options for parsing document - * @param {String} [parsingOptions.crossOrigin] crossOrigin settings - * @param {AbortSignal} [parsingOptions.signal] see https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal - */ - fabric.parseSVGDocument = function(doc, callback, reviver, parsingOptions) { - if (!doc) { - return; - } - if (parsingOptions && parsingOptions.signal && parsingOptions.signal.aborted) { - throw new Error('`options.signal` is in `aborted` state'); - } - parseUseDirectives(doc); - - var svgUid = fabric.Object.__uid++, i, len, - options = applyViewboxTransform(doc), - descendants = fabric.util.toArray(doc.getElementsByTagName('*')); - options.crossOrigin = parsingOptions && parsingOptions.crossOrigin; - options.svgUid = svgUid; - options.signal = parsingOptions && parsingOptions.signal; - - if (descendants.length === 0 && fabric.isLikelyNode) { - // we're likely in node, where "o3-xml" library fails to gEBTN("*") - // https://github.com/ajaxorg/node-o3-xml/issues/21 - descendants = doc.selectNodes('//*[name(.)!="svg"]'); - var arr = []; - for (i = 0, len = descendants.length; i < len; i++) { - arr[i] = descendants[i]; - } - descendants = arr; - } - - var elements = descendants.filter(function(el) { - applyViewboxTransform(el); - return fabric.svgValidTagNamesRegEx.test(el.nodeName.replace('svg:', '')) && - !hasAncestorWithNodeName(el, fabric.svgInvalidAncestorsRegEx); // http://www.w3.org/TR/SVG/struct.html#DefsElement - }); - if (!elements || (elements && !elements.length)) { - callback && callback([], {}); - return; - } - var clipPaths = { }; - descendants.filter(function(el) { - return el.nodeName.replace('svg:', '') === 'clipPath'; - }).forEach(function(el) { - var id = el.getAttribute('id'); - clipPaths[id] = fabric.util.toArray(el.getElementsByTagName('*')).filter(function(el) { - return fabric.svgValidTagNamesRegEx.test(el.nodeName.replace('svg:', '')); - }); - }); - fabric.gradientDefs[svgUid] = fabric.getGradientDefs(doc); - fabric.cssRules[svgUid] = fabric.getCSSRules(doc); - fabric.clipPaths[svgUid] = clipPaths; - // Precedence of rules: style > class > attribute - fabric.parseElements(elements, function(instances, elements) { - if (callback) { - callback(instances, options, elements, descendants); - delete fabric.gradientDefs[svgUid]; - delete fabric.cssRules[svgUid]; - delete fabric.clipPaths[svgUid]; - } - }, Object.assign({}, options), reviver, parsingOptions); - }; - - function recursivelyParseGradientsXlink(doc, gradient) { - var gradientsAttrs = ['gradientTransform', 'x1', 'x2', 'y1', 'y2', 'gradientUnits', 'cx', 'cy', 'r', 'fx', 'fy'], - xlinkAttr = 'xlink:href', - xLink = gradient.getAttribute(xlinkAttr).slice(1), - referencedGradient = elementById(doc, xLink); - if (referencedGradient && referencedGradient.getAttribute(xlinkAttr)) { - recursivelyParseGradientsXlink(doc, referencedGradient); - } - gradientsAttrs.forEach(function(attr) { - if (referencedGradient && !gradient.hasAttribute(attr) && referencedGradient.hasAttribute(attr)) { - gradient.setAttribute(attr, referencedGradient.getAttribute(attr)); - } - }); - if (!gradient.children.length) { - var referenceClone = referencedGradient.cloneNode(true); - while (referenceClone.firstChild) { - gradient.appendChild(referenceClone.firstChild); - } - } - gradient.removeAttribute(xlinkAttr); - } - - var reFontDeclaration = new RegExp( - '(normal|italic)?\\s*(normal|small-caps)?\\s*' + - '(normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900)?\\s*(' + - fabric.reNum + - '(?:px|cm|mm|em|pt|pc|in)*)(?:\\/(normal|' + fabric.reNum + '))?\\s+(.*)'); - - fabric.util.object.extend(fabric, { - /** - * Parses a short font declaration, building adding its properties to a style object - * @static - * @function - * @memberOf fabric - * @param {String} value font declaration - * @param {Object} oStyle definition - */ - parseFontDeclaration: function(value, oStyle) { - var match = value.match(reFontDeclaration); - - if (!match) { - return; - } - var fontStyle = match[1], - // font variant is not used - // fontVariant = match[2], - fontWeight = match[3], - fontSize = match[4], - lineHeight = match[5], - fontFamily = match[6]; - - if (fontStyle) { - oStyle.fontStyle = fontStyle; - } - if (fontWeight) { - oStyle.fontWeight = isNaN(parseFloat(fontWeight)) ? fontWeight : parseFloat(fontWeight); - } - if (fontSize) { - oStyle.fontSize = parseUnit(fontSize); - } - if (fontFamily) { - oStyle.fontFamily = fontFamily; - } - if (lineHeight) { - oStyle.lineHeight = lineHeight === 'normal' ? 1 : lineHeight; - } - }, - - /** - * Parses an SVG document, returning all of the gradient declarations found in it - * @static - * @function - * @memberOf fabric - * @param {SVGDocument} doc SVG document to parse - * @return {Object} Gradient definitions; key corresponds to element id, value -- to gradient definition element - */ - getGradientDefs: function(doc) { - var tagArray = [ - 'linearGradient', - 'radialGradient', - 'svg:linearGradient', - 'svg:radialGradient'], - elList = _getMultipleNodes(doc, tagArray), - el, j = 0, gradientDefs = { }; - j = elList.length; - while (j--) { - el = elList[j]; - if (el.getAttribute('xlink:href')) { - recursivelyParseGradientsXlink(doc, el); - } - gradientDefs[el.getAttribute('id')] = el; - } - return gradientDefs; - }, - - /** - * Returns an object of attributes' name/value, given element and an array of attribute names; - * Parses parent "g" nodes recursively upwards. - * @static - * @memberOf fabric - * @param {DOMElement} element Element to parse - * @param {Array} attributes Array of attributes to parse - * @return {Object} object containing parsed attributes' names/values - */ - parseAttributes: function(element, attributes, svgUid) { - - if (!element) { - return; - } - - var value, - parentAttributes = { }, - fontSize, parentFontSize; - - if (typeof svgUid === 'undefined') { - svgUid = element.getAttribute('svgUid'); - } - // if there's a parent container (`g` or `a` or `symbol` node), parse its attributes recursively upwards - if (element.parentNode && fabric.svgValidParentsRegEx.test(element.parentNode.nodeName)) { - parentAttributes = fabric.parseAttributes(element.parentNode, attributes, svgUid); - } - - var ownAttributes = attributes.reduce(function(memo, attr) { - value = element.getAttribute(attr); - if (value) { // eslint-disable-line - memo[attr] = value; - } - return memo; - }, { }); - // add values parsed from style, which take precedence over attributes - // (see: http://www.w3.org/TR/SVG/styling.html#UsingPresentationAttributes) - var cssAttrs = Object.assign( - getGlobalStylesForElement(element, svgUid), - fabric.parseStyleAttribute(element) - ); - ownAttributes = Object.assign( - ownAttributes, - cssAttrs - ); - if (cssAttrs[cPath]) { - element.setAttribute(cPath, cssAttrs[cPath]); - } - fontSize = parentFontSize = parentAttributes.fontSize || fabric.Text.DEFAULT_SVG_FONT_SIZE; - if (ownAttributes[fSize]) { - // looks like the minimum should be 9px when dealing with ems. this is what looks like in browsers. - ownAttributes[fSize] = fontSize = parseUnit(ownAttributes[fSize], parentFontSize); - } - - var normalizedAttr, normalizedValue, normalizedStyle = {}; - for (var attr in ownAttributes) { - normalizedAttr = normalizeAttr(attr); - normalizedValue = normalizeValue(normalizedAttr, ownAttributes[attr], parentAttributes, fontSize); - normalizedStyle[normalizedAttr] = normalizedValue; - } - if (normalizedStyle && normalizedStyle.font) { - fabric.parseFontDeclaration(normalizedStyle.font, normalizedStyle); - } - var mergedAttrs = Object.assign(parentAttributes, normalizedStyle); - return fabric.svgValidParentsRegEx.test(element.nodeName) ? mergedAttrs : _setStrokeFillOpacity(mergedAttrs); - }, - - /** - * Transforms an array of svg elements to corresponding fabric.* instances - * @static - * @memberOf fabric - * @param {Array} elements Array of elements to parse - * @param {Function} callback Being passed an array of fabric instances (transformed from SVG elements) - * @param {Object} [options] Options object - * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created. - */ - parseElements: function(elements, callback, options, reviver, parsingOptions) { - new fabric.ElementsParser(elements, callback, options, reviver, parsingOptions).parse(); - }, - - /** - * Parses "style" attribute, retuning an object with values - * @static - * @memberOf fabric - * @param {SVGElement} element Element to parse - * @return {Object} Objects with values parsed from style attribute of an element - */ - parseStyleAttribute: function(element) { - var oStyle = { }, - style = element.getAttribute('style'); - - if (!style) { - return oStyle; - } - - if (typeof style === 'string') { - parseStyleString(style, oStyle); - } - else { - parseStyleObject(style, oStyle); - } - - return oStyle; - }, - - /** - * Parses "points" attribute, returning an array of values - * @static - * @memberOf fabric - * @param {String} points points attribute string - * @return {Array} array of points - */ - parsePointsAttribute: function(points) { - - // points attribute is required and must not be empty - if (!points) { - return null; - } - - // replace commas with whitespace and remove bookending whitespace - points = points.replace(/,/g, ' ').trim(); - - points = points.split(/\s+/); - var parsedPoints = [], i, len; - - for (i = 0, len = points.length; i < len; i += 2) { - parsedPoints.push({ - x: parseFloat(points[i]), - y: parseFloat(points[i + 1]) - }); - } - - // odd number of points is an error - // if (parsedPoints.length % 2 !== 0) { - // return null; - // } - - return parsedPoints; - }, - - /** - * Returns CSS rules for a given SVG document - * @static - * @function - * @memberOf fabric - * @param {SVGDocument} doc SVG document to parse - * @return {Object} CSS rules of this document - */ - getCSSRules: function(doc) { - var styles = doc.getElementsByTagName('style'), i, len, - allRules = { }, rules; - - // very crude parsing of style contents - for (i = 0, len = styles.length; i < len; i++) { - var styleContents = styles[i].textContent; - - // remove comments - styleContents = styleContents.replace(/\/\*[\s\S]*?\*\//g, ''); - if (styleContents.trim() === '') { - continue; - } - // recovers all the rule in this form `body { style code... }` - // rules = styleContents.match(/[^{]*\{[\s\S]*?\}/g); - rules = styleContents.split('}'); - // remove empty rules. - rules = rules.filter(function(rule) { return rule.trim(); }); - // at this point we have hopefully an array of rules `body { style code... ` - // eslint-disable-next-line no-loop-func - rules.forEach(function(rule) { - - var match = rule.split('{'), - ruleObj = { }, declaration = match[1].trim(), - propertyValuePairs = declaration.split(';').filter(function(pair) { return pair.trim(); }); - - for (i = 0, len = propertyValuePairs.length; i < len; i++) { - var pair = propertyValuePairs[i].split(':'), - property = pair[0].trim(), - value = pair[1].trim(); - ruleObj[property] = value; - } - rule = match[0].trim(); - rule.split(',').forEach(function(_rule) { - _rule = _rule.replace(/^svg/i, '').trim(); - if (_rule === '') { - return; - } - if (allRules[_rule]) { - Object.assign(allRules[_rule], ruleObj); - } - else { - allRules[_rule] = Object.assign({}, ruleObj); - } - }); - }); - } - return allRules; - }, - - /** - * Takes url corresponding to an SVG document, and parses it into a set of fabric objects. - * Note that SVG is fetched via XMLHttpRequest, so it needs to conform to SOP (Same Origin Policy) - * @memberOf fabric - * @param {String} url - * @param {Function} callback - * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created. - * @param {Object} [options] Object containing options for parsing - * @param {String} [options.crossOrigin] crossOrigin crossOrigin setting to use for external resources - * @param {AbortSignal} [options.signal] handle aborting, see https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal - */ - loadSVGFromURL: function(url, callback, reviver, options) { - - url = url.replace(/^\n\s*/, '').trim(); - new fabric.util.request(url, { - method: 'get', - onComplete: onComplete, - signal: options && options.signal - }); - - function onComplete(r) { - - var xml = r.responseXML; - if (!xml || !xml.documentElement) { - callback && callback(null); - return false; - } - - fabric.parseSVGDocument(xml.documentElement, function (results, _options, elements, allElements) { - callback && callback(results, _options, elements, allElements); - }, reviver, options); - } - }, - - /** - * Takes string corresponding to an SVG document, and parses it into a set of fabric objects - * @memberOf fabric - * @param {String} string - * @param {Function} callback - * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created. - * @param {Object} [options] Object containing options for parsing - * @param {String} [options.crossOrigin] crossOrigin crossOrigin setting to use for external resources - * @param {AbortSignal} [options.signal] handle aborting, see https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal - */ - loadSVGFromString: function(string, callback, reviver, options) { - var parser = new fabric.window.DOMParser(), - doc = parser.parseFromString(string.trim(), 'text/xml'); - fabric.parseSVGDocument(doc.documentElement, function (results, _options, elements, allElements) { - callback(results, _options, elements, allElements); - }, reviver, options); - } - }); - -})(typeof exports !== 'undefined' ? exports : window); diff --git a/src/parser/applyViewboxTransform.ts b/src/parser/applyViewboxTransform.ts new file mode 100644 index 00000000000..5332e38e52a --- /dev/null +++ b/src/parser/applyViewboxTransform.ts @@ -0,0 +1,118 @@ +//@ts-nocheck +import { svgNS } from '../constants'; +import { parseUnit } from '../util'; +import { svgViewBoxElementsRegEx, reViewBoxAttrValue } from './constants'; + +/** + * Add a element that envelop all child elements and makes the viewbox transformMatrix descend on all elements + */ + +export function applyViewboxTransform(element) { + if (!svgViewBoxElementsRegEx.test(element.nodeName)) { + return {}; + } + var viewBoxAttr = element.getAttribute('viewBox'), scaleX = 1, scaleY = 1, minX = 0, minY = 0, viewBoxWidth, viewBoxHeight, matrix, el, widthAttr = element.getAttribute('width'), heightAttr = element.getAttribute('height'), x = element.getAttribute('x') || 0, y = element.getAttribute('y') || 0, preserveAspectRatio = element.getAttribute('preserveAspectRatio') || '', missingViewBox = (!viewBoxAttr || !(viewBoxAttr = viewBoxAttr.match(reViewBoxAttrValue))), missingDimAttr = (!widthAttr || !heightAttr || widthAttr === '100%' || heightAttr === '100%'), toBeParsed = missingViewBox && missingDimAttr, parsedDim = {}, translateMatrix = '', widthDiff = 0, heightDiff = 0; + + parsedDim.width = 0; + parsedDim.height = 0; + parsedDim.toBeParsed = toBeParsed; + + if (missingViewBox) { + if (((x || y) && element.parentNode && element.parentNode.nodeName !== '#document')) { + translateMatrix = ' translate(' + parseUnit(x) + ' ' + parseUnit(y) + ') '; + matrix = (element.getAttribute('transform') || '') + translateMatrix; + element.setAttribute('transform', matrix); + element.removeAttribute('x'); + element.removeAttribute('y'); + } + } + + if (toBeParsed) { + return parsedDim; + } + + if (missingViewBox) { + parsedDim.width = parseUnit(widthAttr); + parsedDim.height = parseUnit(heightAttr); + // set a transform for elements that have x y and are inner(only) SVGs + return parsedDim; + } + minX = -parseFloat(viewBoxAttr[1]); + minY = -parseFloat(viewBoxAttr[2]); + viewBoxWidth = parseFloat(viewBoxAttr[3]); + viewBoxHeight = parseFloat(viewBoxAttr[4]); + parsedDim.minX = minX; + parsedDim.minY = minY; + parsedDim.viewBoxWidth = viewBoxWidth; + parsedDim.viewBoxHeight = viewBoxHeight; + if (!missingDimAttr) { + parsedDim.width = parseUnit(widthAttr); + parsedDim.height = parseUnit(heightAttr); + scaleX = parsedDim.width / viewBoxWidth; + scaleY = parsedDim.height / viewBoxHeight; + } + else { + parsedDim.width = viewBoxWidth; + parsedDim.height = viewBoxHeight; + } + + // default is to preserve aspect ratio + preserveAspectRatio = parsePreserveAspectRatioAttribute(preserveAspectRatio); + if (preserveAspectRatio.alignX !== 'none') { + //translate all container for the effect of Mid, Min, Max + if (preserveAspectRatio.meetOrSlice === 'meet') { + scaleY = scaleX = (scaleX > scaleY ? scaleY : scaleX); + // calculate additional translation to move the viewbox + } + if (preserveAspectRatio.meetOrSlice === 'slice') { + scaleY = scaleX = (scaleX > scaleY ? scaleX : scaleY); + // calculate additional translation to move the viewbox + } + widthDiff = parsedDim.width - viewBoxWidth * scaleX; + heightDiff = parsedDim.height - viewBoxHeight * scaleX; + if (preserveAspectRatio.alignX === 'Mid') { + widthDiff /= 2; + } + if (preserveAspectRatio.alignY === 'Mid') { + heightDiff /= 2; + } + if (preserveAspectRatio.alignX === 'Min') { + widthDiff = 0; + } + if (preserveAspectRatio.alignY === 'Min') { + heightDiff = 0; + } + } + + if (scaleX === 1 && scaleY === 1 && minX === 0 && minY === 0 && x === 0 && y === 0) { + return parsedDim; + } + if ((x || y) && element.parentNode.nodeName !== '#document') { + translateMatrix = ' translate(' + parseUnit(x) + ' ' + parseUnit(y) + ') '; + } + + matrix = translateMatrix + ' matrix(' + scaleX + + ' 0' + + ' 0 ' + + scaleY + ' ' + + (minX * scaleX + widthDiff) + ' ' + + (minY * scaleY + heightDiff) + ') '; + // seems unused. + // parsedDim.viewboxTransform = fabric.parseTransformAttribute(matrix); + if (element.nodeName === 'svg') { + el = element.ownerDocument.createElementNS(svgNS, 'g'); + // element.firstChild != null + while (element.firstChild) { + el.appendChild(element.firstChild); + } + element.appendChild(el); + } + else { + el = element; + el.removeAttribute('x'); + el.removeAttribute('y'); + matrix = el.getAttribute('transform') + matrix; + } + el.setAttribute('transform', matrix); + return parsedDim; +} diff --git a/src/parser/constants.ts b/src/parser/constants.ts new file mode 100644 index 00000000000..1a098e97d5c --- /dev/null +++ b/src/parser/constants.ts @@ -0,0 +1,70 @@ +//@ts-nocheck +import { reNum } from '../constants'; +import { getSvgRegex } from "./getSvgRegex"; + + +export const cssRules = {}; +export const gradientDefs = {}; +export const clipPaths = {}; + +export const reFontDeclaration = new RegExp( + '(normal|italic)?\\s*(normal|small-caps)?\\s*' + + '(normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900)?\\s*(' + + reNum + + '(?:px|cm|mm|em|pt|pc|in)*)(?:\\/(normal|' + reNum + '))?\\s+(.*)'); + +export const svgValidTagNames = ['path', 'circle', 'polygon', 'polyline', 'ellipse', 'rect', 'line', + 'image', 'text'], svgViewBoxElements = ['symbol', 'image', 'marker', 'pattern', 'view', 'svg'], svgInvalidAncestors = ['pattern', 'defs', 'symbol', 'metadata', 'clipPath', 'mask', 'desc'], svgValidParents = ['symbol', 'g', 'a', 'svg', 'clipPath', 'defs'], attributesMap = { + cx: 'left', + x: 'left', + r: 'radius', + cy: 'top', + y: 'top', + display: 'visible', + visibility: 'visible', + transform: 'transformMatrix', + 'fill-opacity': 'fillOpacity', + 'fill-rule': 'fillRule', + 'font-family': 'fontFamily', + 'font-size': 'fontSize', + 'font-style': 'fontStyle', + 'font-weight': 'fontWeight', + 'letter-spacing': 'charSpacing', + 'paint-order': 'paintFirst', + 'stroke-dasharray': 'strokeDashArray', + 'stroke-dashoffset': 'strokeDashOffset', + 'stroke-linecap': 'strokeLineCap', + 'stroke-linejoin': 'strokeLineJoin', + 'stroke-miterlimit': 'strokeMiterLimit', + 'stroke-opacity': 'strokeOpacity', + 'stroke-width': 'strokeWidth', + 'text-decoration': 'textDecoration', + 'text-anchor': 'textAnchor', + opacity: 'opacity', + 'clip-path': 'clipPath', + 'clip-rule': 'clipRule', + 'vector-effect': 'strokeUniform', + 'image-rendering': 'imageSmoothing', + }, colorAttributes = { + stroke: 'strokeOpacity', + fill: 'fillOpacity' + }, fSize = 'font-size', cPath = 'clip-path'; + +export const svgValidTagNamesRegEx = getSvgRegex(svgValidTagNames); + +export const svgViewBoxElementsRegEx = getSvgRegex(svgViewBoxElements); + +export const svgInvalidAncestorsRegEx = getSvgRegex(svgInvalidAncestors); + +export const svgValidParentsRegEx = getSvgRegex(svgValidParents); + +// http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute +// matches, e.g.: +14.56e-12, etc. +export const reViewBoxAttrValue = new RegExp( + '^' + + '\\s*(' + reNum + '+)\\s*,?' + + '\\s*(' + reNum + '+)\\s*,?' + + '\\s*(' + reNum + '+)\\s*,?' + + '\\s*(' + reNum + '+)\\s*' + + '$' +); diff --git a/src/parser/doesSomeParentMatch.ts b/src/parser/doesSomeParentMatch.ts new file mode 100644 index 00000000000..3cff1efcb23 --- /dev/null +++ b/src/parser/doesSomeParentMatch.ts @@ -0,0 +1,15 @@ +//@ts-nocheck +import { selectorMatches } from "./selectorMatches"; + + +export function doesSomeParentMatch(element, selectors) { + var selector, parentMatching = true; + while (element.parentNode && element.parentNode.nodeType === 1 && selectors.length) { + if (parentMatching) { + selector = selectors.pop(); + } + element = element.parentNode; + parentMatching = selectorMatches(element, selector); + } + return selectors.length === 0; +} diff --git a/src/parser/elementById.ts b/src/parser/elementById.ts new file mode 100644 index 00000000000..539618ccb12 --- /dev/null +++ b/src/parser/elementById.ts @@ -0,0 +1,20 @@ +//@ts-nocheck + +/** + * @private + * to support IE8 missing getElementById on SVGdocument and on node xmlDOM + */ +export function elementById(doc, id) { + var el; + doc.getElementById && (el = doc.getElementById(id)); + if (el) { + return el; + } + var node, i, len, nodelist = doc.getElementsByTagName('*'); + for (i = 0, len = nodelist.length; i < len; i++) { + node = nodelist[i]; + if (id === node.getAttribute('id')) { + return node; + } + } +} diff --git a/src/parser/elementMatchesRule.ts b/src/parser/elementMatchesRule.ts new file mode 100644 index 00000000000..8d8f25479ba --- /dev/null +++ b/src/parser/elementMatchesRule.ts @@ -0,0 +1,18 @@ +//@ts-nocheck + +import { selectorMatches } from "./selectorMatches"; +import { doesSomeParentMatch } from "./doesSomeParentMatch"; + +/** + * @private + */ + +export function elementMatchesRule(element, selectors) { + var firstMatching, parentMatching = true; + //start from rightmost selector. + firstMatching = selectorMatches(element, selectors.pop()); + if (firstMatching && selectors.length) { + parentMatching = doesSomeParentMatch(element, selectors); + } + return firstMatching && parentMatching && (selectors.length === 0); +} diff --git a/src/parser/getCSSRules.ts b/src/parser/getCSSRules.ts new file mode 100644 index 00000000000..93022b0fc65 --- /dev/null +++ b/src/parser/getCSSRules.ts @@ -0,0 +1,53 @@ +/** + * Returns CSS rules for a given SVG document + * @static + * @function + * @memberOf fabric + * @param {SVGDocument} doc SVG document to parse + * @return {Object} CSS rules of this document + */ + +export function getCSSRules(doc) { + var styles = doc.getElementsByTagName('style'), i, len, allRules = {}, rules; + + // very crude parsing of style contents + for (i = 0, len = styles.length; i < len; i++) { + var styleContents = styles[i].textContent; + + // remove comments + styleContents = styleContents.replace(/\/\*[\s\S]*?\*\//g, ''); + if (styleContents.trim() === '') { + continue; + } + // recovers all the rule in this form `body { style code... }` + // rules = styleContents.match(/[^{]*\{[\s\S]*?\}/g); + rules = styleContents.split('}'); + // remove empty rules. + rules = rules.filter(function (rule) { return rule.trim(); }); + // at this point we have hopefully an array of rules `body { style code... ` + // eslint-disable-next-line no-loop-func + rules.forEach(function (rule) { + + var match = rule.split('{'), ruleObj = {}, declaration = match[1].trim(), propertyValuePairs = declaration.split(';').filter(function (pair) { return pair.trim(); }); + + for (i = 0, len = propertyValuePairs.length; i < len; i++) { + var pair = propertyValuePairs[i].split(':'), property = pair[0].trim(), value = pair[1].trim(); + ruleObj[property] = value; + } + rule = match[0].trim(); + rule.split(',').forEach(function (_rule) { + _rule = _rule.replace(/^svg/i, '').trim(); + if (_rule === '') { + return; + } + if (allRules[_rule]) { + Object.assign(allRules[_rule], ruleObj); + } + else { + allRules[_rule] = Object.assign({}, ruleObj); + } + }); + }); + } + return allRules; +} diff --git a/src/parser/getGlobalStylesForElement.ts b/src/parser/getGlobalStylesForElement.ts new file mode 100644 index 00000000000..f6c794fd435 --- /dev/null +++ b/src/parser/getGlobalStylesForElement.ts @@ -0,0 +1,19 @@ +//@ts-nocheck +import { cssRules } from './constants'; +import { elementMatchesRule } from "./elementMatchesRule"; + +/** + * @private + */ + +export function getGlobalStylesForElement(element, svgUid) { + var styles = {}; + for (var rule in cssRules[svgUid]) { + if (elementMatchesRule(element, rule.split(' '))) { + for (var property in cssRules[svgUid][rule]) { + styles[property] = cssRules[svgUid][rule][property]; + } + } + } + return styles; +} diff --git a/src/parser/getGradientDefs.ts b/src/parser/getGradientDefs.ts new file mode 100644 index 00000000000..0838679915a --- /dev/null +++ b/src/parser/getGradientDefs.ts @@ -0,0 +1,28 @@ +import { getMultipleNodes } from "./getMultipleNodes"; +import { recursivelyParseGradientsXlink } from './recursivelyParseGradientsXlink'; + +/** + * Parses an SVG document, returning all of the gradient declarations found in it + * @static + * @function + * @memberOf fabric + * @param {SVGDocument} doc SVG document to parse + * @return {Object} Gradient definitions; key corresponds to element id, value -- to gradient definition element + */ +function getGradientDefs(doc) { + var tagArray = [ + 'linearGradient', + 'radialGradient', + 'svg:linearGradient', + 'svg:radialGradient' + ], elList = getMultipleNodes(doc, tagArray), el, j = 0, gradientDefs = {}; + j = elList.length; + while (j--) { + el = elList[j]; + if (el.getAttribute('xlink:href')) { + recursivelyParseGradientsXlink(doc, el); + } + gradientDefs[el.getAttribute('id')] = el; + } + return gradientDefs; +} diff --git a/src/parser/getMultipleNodes.ts b/src/parser/getMultipleNodes.ts new file mode 100644 index 00000000000..d1ae48d9f0a --- /dev/null +++ b/src/parser/getMultipleNodes.ts @@ -0,0 +1,11 @@ +//@ts-nocheck + +export function getMultipleNodes(doc, nodeNames) { + var nodeName, nodeArray = [], nodeList, i, len; + for (i = 0, len = nodeNames.length; i < len; i++) { + nodeName = nodeNames[i]; + nodeList = doc.getElementsByTagName(nodeName); + nodeArray = nodeArray.concat(Array.prototype.slice.call(nodeList)); + } + return nodeArray; +} diff --git a/src/parser/getSvgRegex.ts b/src/parser/getSvgRegex.ts new file mode 100644 index 00000000000..1ea5aa6f5b4 --- /dev/null +++ b/src/parser/getSvgRegex.ts @@ -0,0 +1,5 @@ +//@ts-nocheck + +export function getSvgRegex(arr) { + return new RegExp('^(' + arr.join('|') + ')\\b', 'i'); +} diff --git a/src/parser/hasAncestorWithNodeName.ts b/src/parser/hasAncestorWithNodeName.ts new file mode 100644 index 00000000000..fc0d3626449 --- /dev/null +++ b/src/parser/hasAncestorWithNodeName.ts @@ -0,0 +1,11 @@ +//@ts-nocheck + +export function hasAncestorWithNodeName(element, nodeName) { + while (element && (element = element.parentNode)) { + if (element.nodeName && nodeName.test(element.nodeName.replace('svg:', '')) + && !element.getAttribute('instantiated_by_use')) { + return true; + } + } + return false; +} diff --git a/src/parser/index.ts b/src/parser/index.ts new file mode 100644 index 00000000000..bf5cd5f4763 --- /dev/null +++ b/src/parser/index.ts @@ -0,0 +1,335 @@ +//@ts-nocheck +import { incrementUID } from '../constants'; +import { extend, toArray } from '../util'; +import { hasAncestorWithNodeName } from "./hasAncestorWithNodeName"; +import { applyViewboxTransform } from "./applyViewboxTransform"; +import { parseUseDirectives } from "./parseUseDirectives"; +import { elementById } from "./elementById"; +import { parseAttributes } from "./parseAttributes"; +import { parseTransformAttribute } from "./parseTransformAttribute"; +import { parsePointsAttribute } from "./parsePointsAttribute"; +import { getMultipleNodes } from "./getMultipleNodes"; +import { parseFontDeclaration } from "./parseFontDeclaration"; +import { parseStyleAttribute } from "./parseStyleAttribute"; +import { clipPaths, cssRules, gradientDefs, svgInvalidAncestorsRegEx, svgValidParentsRegEx, svgValidTagNamesRegEx, svgViewBoxElementsRegEx } from "./constants"; + + +(function (global) { + /** + * @name fabric + * @namespace + */ + + var fabric = global.fabric || (global.fabric = {}); + + + + fabric.svgValidTagNamesRegEx = svgValidTagNamesRegEx; + fabric.svgViewBoxElementsRegEx = svgViewBoxElementsRegEx; + fabric.svgInvalidAncestorsRegEx = svgInvalidAncestorsRegEx; + fabric.svgValidParentsRegEx = svgValidParentsRegEx; + + fabric.cssRules = cssRules; + fabric.gradientDefs = gradientDefs; + fabric.clipPaths = clipPaths; + + + /** + * Parses "transform" attribute, returning an array of values + * @static + * @function + * @memberOf fabric + * @param {String} attributeValue String containing attribute value + * @return {Array} Array of 6 elements representing transformation matrix + */ + fabric.parseTransformAttribute = parseTransformAttribute; + + + /** + * Parses an SVG document, converts it to an array of corresponding fabric.* instances and passes them to a callback + * @static + * @function + * @memberOf fabric + * @param {SVGDocument} doc SVG document to parse + * @param {Function} callback Callback to call when parsing is finished; + * It's being passed an array of elements (parsed from a document). + * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created. + * @param {Object} [parsingOptions] options for parsing document + * @param {String} [parsingOptions.crossOrigin] crossOrigin settings + * @param {AbortSignal} [parsingOptions.signal] see https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal + */ + fabric.parseSVGDocument = function (doc, callback, reviver, parsingOptions) { + if (!doc) { + return; + } + if (parsingOptions && parsingOptions.signal && parsingOptions.signal.aborted) { + throw new Error('`options.signal` is in `aborted` state'); + } + parseUseDirectives(doc); + + var svgUid = incrementUID(), i, len, + options = applyViewboxTransform(doc), + descendants = toArray(doc.getElementsByTagName('*')); + options.crossOrigin = parsingOptions && parsingOptions.crossOrigin; + options.svgUid = svgUid; + options.signal = parsingOptions && parsingOptions.signal; + + if (descendants.length === 0 && isLikelyNode) { + // we're likely in node, where "o3-xml" library fails to gEBTN("*") + // https://github.com/ajaxorg/node-o3-xml/issues/21 + descendants = doc.selectNodes('//*[name(.)!="svg"]'); + var arr = []; + for (i = 0, len = descendants.length; i < len; i++) { + arr[i] = descendants[i]; + } + descendants = arr; + } + + var elements = descendants.filter(function (el) { + applyViewboxTransform(el); + return svgValidTagNamesRegEx.test(el.nodeName.replace('svg:', '')) && + !hasAncestorWithNodeName(el, svgInvalidAncestorsRegEx); // http://www.w3.org/TR/SVG/struct.html#DefsElement + }); + if (!elements || (elements && !elements.length)) { + callback && callback([], {}); + return; + } + var clipPaths = {}; + descendants.filter(function (el) { + return el.nodeName.replace('svg:', '') === 'clipPath'; + }).forEach(function (el) { + var id = el.getAttribute('id'); + clipPaths[id] = toArray(el.getElementsByTagName('*')).filter(function (el) { + return svgValidTagNamesRegEx.test(el.nodeName.replace('svg:', '')); + }); + }); + fabric.gradientDefs[svgUid] = fabric.getGradientDefs(doc); + fabric.cssRules[svgUid] = fabric.getCSSRules(doc); + fabric.clipPaths[svgUid] = clipPaths; + // Precedence of rules: style > class > attribute + fabric.parseElements(elements, function (instances, elements) { + if (callback) { + callback(instances, options, elements, descendants); + delete fabric.gradientDefs[svgUid]; + delete fabric.cssRules[svgUid]; + delete fabric.clipPaths[svgUid]; + } + }, Object.assign({}, options), reviver, parsingOptions); + }; + + function recursivelyParseGradientsXlink(doc, gradient) { + var gradientsAttrs = ['gradientTransform', 'x1', 'x2', 'y1', 'y2', 'gradientUnits', 'cx', 'cy', 'r', 'fx', 'fy'], + xlinkAttr = 'xlink:href', + xLink = gradient.getAttribute(xlinkAttr).slice(1), + referencedGradient = elementById(doc, xLink); + if (referencedGradient && referencedGradient.getAttribute(xlinkAttr)) { + recursivelyParseGradientsXlink(doc, referencedGradient); + } + gradientsAttrs.forEach(function (attr) { + if (referencedGradient && !gradient.hasAttribute(attr) && referencedGradient.hasAttribute(attr)) { + gradient.setAttribute(attr, referencedGradient.getAttribute(attr)); + } + }); + if (!gradient.children.length) { + var referenceClone = referencedGradient.cloneNode(true); + while (referenceClone.firstChild) { + gradient.appendChild(referenceClone.firstChild); + } + } + gradient.removeAttribute(xlinkAttr); + } + + + + extend(fabric, { + /** + * Parses a short font declaration, building adding its properties to a style object + * @static + * @function + * @memberOf fabric + * @param {String} value font declaration + * @param {Object} oStyle definition + */ + parseFontDeclaration, + + /** + * Parses an SVG document, returning all of the gradient declarations found in it + * @static + * @function + * @memberOf fabric + * @param {SVGDocument} doc SVG document to parse + * @return {Object} Gradient definitions; key corresponds to element id, value -- to gradient definition element + */ + getGradientDefs: function (doc) { + var tagArray = [ + 'linearGradient', + 'radialGradient', + 'svg:linearGradient', + 'svg:radialGradient'], + elList = getMultipleNodes(doc, tagArray), + el, j = 0, gradientDefs = {}; + j = elList.length; + while (j--) { + el = elList[j]; + if (el.getAttribute('xlink:href')) { + recursivelyParseGradientsXlink(doc, el); + } + gradientDefs[el.getAttribute('id')] = el; + } + return gradientDefs; + }, + + /** + * Returns an object of attributes' name/value, given element and an array of attribute names; + * Parses parent "g" nodes recursively upwards. + * @static + * @memberOf fabric + * @param {DOMElement} element Element to parse + * @param {Array} attributes Array of attributes to parse + * @return {Object} object containing parsed attributes' names/values + */ + parseAttributes, + + /** + * Transforms an array of svg elements to corresponding fabric.* instances + * @static + * @memberOf fabric + * @param {Array} elements Array of elements to parse + * @param {Function} callback Being passed an array of fabric instances (transformed from SVG elements) + * @param {Object} [options] Options object + * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created. + */ + parseElements: function (elements, callback, options, reviver, parsingOptions) { + new fabric.ElementsParser(elements, callback, options, reviver, parsingOptions).parse(); + }, + + /** + * Parses "style" attribute, retuning an object with values + * @static + * @memberOf fabric + * @param {SVGElement} element Element to parse + * @return {Object} Objects with values parsed from style attribute of an element + */ + parseStyleAttribute, + + /** + * Parses "points" attribute, returning an array of values + * @static + * @memberOf fabric + * @param {String} points points attribute string + * @return {Array} array of points + */ + parsePointsAttribute, + + /** + * Returns CSS rules for a given SVG document + * @static + * @function + * @memberOf fabric + * @param {SVGDocument} doc SVG document to parse + * @return {Object} CSS rules of this document + */ + getCSSRules: function (doc) { + var styles = doc.getElementsByTagName('style'), i, len, + allRules = {}, rules; + + // very crude parsing of style contents + for (i = 0, len = styles.length; i < len; i++) { + var styleContents = styles[i].textContent; + + // remove comments + styleContents = styleContents.replace(/\/\*[\s\S]*?\*\//g, ''); + if (styleContents.trim() === '') { + continue; + } + // recovers all the rule in this form `body { style code... }` + // rules = styleContents.match(/[^{]*\{[\s\S]*?\}/g); + rules = styleContents.split('}'); + // remove empty rules. + rules = rules.filter(function (rule) { return rule.trim(); }); + // at this point we have hopefully an array of rules `body { style code... ` + // eslint-disable-next-line no-loop-func + rules.forEach(function (rule) { + + var match = rule.split('{'), + ruleObj = {}, declaration = match[1].trim(), + propertyValuePairs = declaration.split(';').filter(function (pair) { return pair.trim(); }); + + for (i = 0, len = propertyValuePairs.length; i < len; i++) { + var pair = propertyValuePairs[i].split(':'), + property = pair[0].trim(), + value = pair[1].trim(); + ruleObj[property] = value; + } + rule = match[0].trim(); + rule.split(',').forEach(function (_rule) { + _rule = _rule.replace(/^svg/i, '').trim(); + if (_rule === '') { + return; + } + if (allRules[_rule]) { + Object.assign(allRules[_rule], ruleObj); + } + else { + allRules[_rule] = Object.assign({}, ruleObj); + } + }); + }); + } + return allRules; + }, + + /** + * Takes url corresponding to an SVG document, and parses it into a set of fabric objects. + * Note that SVG is fetched via XMLHttpRequest, so it needs to conform to SOP (Same Origin Policy) + * @memberOf fabric + * @param {String} url + * @param {Function} callback + * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created. + * @param {Object} [options] Object containing options for parsing + * @param {String} [options.crossOrigin] crossOrigin crossOrigin setting to use for external resources + * @param {AbortSignal} [options.signal] handle aborting, see https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal + */ + loadSVGFromURL: function (url, callback, reviver, options) { + + url = url.replace(/^\n\s*/, '').trim(); + new request(url, { + method: 'get', + onComplete: onComplete, + signal: options && options.signal + }); + + function onComplete(r) { + + var xml = r.responseXML; + if (!xml || !xml.documentElement) { + callback && callback(null); + return false; + } + + fabric.parseSVGDocument(xml.documentElement, function (results, _options, elements, allElements) { + callback && callback(results, _options, elements, allElements); + }, reviver, options); + } + }, + + /** + * Takes string corresponding to an SVG document, and parses it into a set of fabric objects + * @memberOf fabric + * @param {String} string + * @param {Function} callback + * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created. + * @param {Object} [options] Object containing options for parsing + * @param {String} [options.crossOrigin] crossOrigin crossOrigin setting to use for external resources + * @param {AbortSignal} [options.signal] handle aborting, see https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal + */ + loadSVGFromString: function (string, callback, reviver, options) { + var parser = new fabric.window.DOMParser(), + doc = parser.parseFromString(string.trim(), 'text/xml'); + fabric.parseSVGDocument(doc.documentElement, function (results, _options, elements, allElements) { + callback(results, _options, elements, allElements); + }, reviver, options); + } + }); + +})(typeof exports !== 'undefined' ? exports : window); diff --git a/src/parser/loadSVGFromString.ts b/src/parser/loadSVGFromString.ts new file mode 100644 index 00000000000..72ed0c9c91a --- /dev/null +++ b/src/parser/loadSVGFromString.ts @@ -0,0 +1,17 @@ +/** + * Takes string corresponding to an SVG document, and parses it into a set of fabric objects + * @memberOf fabric + * @param {String} string + * @param {Function} callback + * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created. + * @param {Object} [options] Object containing options for parsing + * @param {String} [options.crossOrigin] crossOrigin crossOrigin setting to use for external resources + * @param {AbortSignal} [options.signal] handle aborting, see https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal + */ + +export function loadSVGFromString(string, callback, reviver, options) { + var parser = new fabric.window.DOMParser(), doc = parser.parseFromString(string.trim(), 'text/xml'); + fabric.parseSVGDocument(doc.documentElement, function (results, _options, elements, allElements) { + callback(results, _options, elements, allElements); + }, reviver, options); +} diff --git a/src/parser/loadSVGFromURL.ts b/src/parser/loadSVGFromURL.ts new file mode 100644 index 00000000000..31029ca97fd --- /dev/null +++ b/src/parser/loadSVGFromURL.ts @@ -0,0 +1,34 @@ +/** + * Takes url corresponding to an SVG document, and parses it into a set of fabric objects. + * Note that SVG is fetched via XMLHttpRequest, so it needs to conform to SOP (Same Origin Policy) + * @memberOf fabric + * @param {String} url + * @param {Function} callback + * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created. + * @param {Object} [options] Object containing options for parsing + * @param {String} [options.crossOrigin] crossOrigin crossOrigin setting to use for external resources + * @param {AbortSignal} [options.signal] handle aborting, see https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal + */ + +export function loadSVGFromURL(url, callback, reviver, options) { + + url = url.replace(/^\n\s*/, '').trim(); + new request(url, { + method: 'get', + onComplete: onComplete, + signal: options && options.signal + }); + + function onComplete(r) { + + var xml = r.responseXML; + if (!xml || !xml.documentElement) { + callback && callback(null); + return false; + } + + fabric.parseSVGDocument(xml.documentElement, function (results, _options, elements, allElements) { + callback && callback(results, _options, elements, allElements); + }, reviver, options); + } +} diff --git a/src/parser/normalizeAttr.ts b/src/parser/normalizeAttr.ts new file mode 100644 index 00000000000..3b7f06e21e1 --- /dev/null +++ b/src/parser/normalizeAttr.ts @@ -0,0 +1,11 @@ +//@ts-nocheck +import { attributesMap } from './constants'; + + +export function normalizeAttr(attr) { + // transform attribute names + if (attr in attributesMap) { + return attributesMap[attr]; + } + return attr; +} diff --git a/src/parser/normalizeValue.ts b/src/parser/normalizeValue.ts new file mode 100644 index 00000000000..b0591edbe41 --- /dev/null +++ b/src/parser/normalizeValue.ts @@ -0,0 +1,74 @@ +//@ts-nocheck +import { multiplyTransformMatrices, parseUnit } from '../util'; +import { parseTransformAttribute } from "./parseTransformAttribute"; + + +export function normalizeValue(attr, value, parentAttributes, fontSize) { + var isArray = Array.isArray(value), parsed; + + if ((attr === 'fill' || attr === 'stroke') && value === 'none') { + value = ''; + } + else if (attr === 'strokeUniform') { + return (value === 'non-scaling-stroke'); + } + else if (attr === 'strokeDashArray') { + if (value === 'none') { + value = null; + } + else { + value = value.replace(/,/g, ' ').split(/\s+/).map(parseFloat); + } + } + else if (attr === 'transformMatrix') { + if (parentAttributes && parentAttributes.transformMatrix) { + value = multiplyTransformMatrices( + parentAttributes.transformMatrix, parseTransformAttribute(value)); + } + else { + value = parseTransformAttribute(value); + } + } + else if (attr === 'visible') { + value = value !== 'none' && value !== 'hidden'; + // display=none on parent element always takes precedence over child element + if (parentAttributes && parentAttributes.visible === false) { + value = false; + } + } + else if (attr === 'opacity') { + value = parseFloat(value); + if (parentAttributes && typeof parentAttributes.opacity !== 'undefined') { + value *= parentAttributes.opacity; + } + } + else if (attr === 'textAnchor' /* text-anchor */) { + value = value === 'start' ? 'left' : value === 'end' ? 'right' : 'center'; + } + else if (attr === 'charSpacing') { + // parseUnit returns px and we convert it to em + parsed = parseUnit(value, fontSize) / fontSize * 1000; + } + else if (attr === 'paintFirst') { + var fillIndex = value.indexOf('fill'); + var strokeIndex = value.indexOf('stroke'); + var value = 'fill'; + if (fillIndex > -1 && strokeIndex > -1 && strokeIndex < fillIndex) { + value = 'stroke'; + } + else if (fillIndex === -1 && strokeIndex > -1) { + value = 'stroke'; + } + } + else if (attr === 'href' || attr === 'xlink:href' || attr === 'font') { + return value; + } + else if (attr === 'imageSmoothing') { + return (value === 'optimizeQuality'); + } + else { + parsed = isArray ? value.map(parseUnit) : parseUnit(value, fontSize); + } + + return (!isArray && isNaN(parsed) ? value : parsed); +} diff --git a/src/parser/parseAttributes.ts b/src/parser/parseAttributes.ts new file mode 100644 index 00000000000..d1ee8a653e5 --- /dev/null +++ b/src/parser/parseAttributes.ts @@ -0,0 +1,73 @@ +//@ts-nocheck +import { DEFAULT_SVG_FONT_SIZE } from '../constants'; +import { parseUnit } from '../util'; +import { svgValidParentsRegEx, cPath, fSize } from './constants'; +import { normalizeAttr } from './normalizeAttr'; +import { normalizeValue } from './normalizeValue'; +import { parseFontDeclaration } from './parseFontDeclaration'; +import { parseStyleAttribute } from './parseStyleAttribute'; +import { setStrokeFillOpacity } from './setStrokeFillOpacity'; +import { getGlobalStylesForElement } from "./getGlobalStylesForElement"; + +/** + * Returns an object of attributes' name/value, given element and an array of attribute names; + * Parses parent "g" nodes recursively upwards. + * @param {DOMElement} element Element to parse + * @param {Array} attributes Array of attributes to parse + * @return {Object} object containing parsed attributes' names/values + */ + +export function parseAttributes(element, attributes, svgUid) { + + if (!element) { + return; + } + + var value, parentAttributes = {}, fontSize, parentFontSize; + + if (typeof svgUid === 'undefined') { + svgUid = element.getAttribute('svgUid'); + } + // if there's a parent container (`g` or `a` or `symbol` node), parse its attributes recursively upwards + if (element.parentNode && svgValidParentsRegEx.test(element.parentNode.nodeName)) { + parentAttributes = parseAttributes(element.parentNode, attributes, svgUid); + } + + var ownAttributes = attributes.reduce(function (memo, attr) { + value = element.getAttribute(attr); + if (value) { // eslint-disable-line + memo[attr] = value; + } + return memo; + }, {}); + // add values parsed from style, which take precedence over attributes + // (see: http://www.w3.org/TR/SVG/styling.html#UsingPresentationAttributes) + var cssAttrs = Object.assign( + getGlobalStylesForElement(element, svgUid), + parseStyleAttribute(element) + ); + ownAttributes = Object.assign( + ownAttributes, + cssAttrs + ); + if (cssAttrs[cPath]) { + element.setAttribute(cPath, cssAttrs[cPath]); + } + fontSize = parentFontSize = parentAttributes.fontSize || DEFAULT_SVG_FONT_SIZE; + if (ownAttributes[fSize]) { + // looks like the minimum should be 9px when dealing with ems. this is what looks like in browsers. + ownAttributes[fSize] = fontSize = parseUnit(ownAttributes[fSize], parentFontSize); + } + + var normalizedAttr, normalizedValue, normalizedStyle = {}; + for (var attr in ownAttributes) { + normalizedAttr = normalizeAttr(attr); + normalizedValue = normalizeValue(normalizedAttr, ownAttributes[attr], parentAttributes, fontSize); + normalizedStyle[normalizedAttr] = normalizedValue; + } + if (normalizedStyle && normalizedStyle.font) { + parseFontDeclaration(normalizedStyle.font, normalizedStyle); + } + var mergedAttrs = Object.assign(parentAttributes, normalizedStyle); + return svgValidParentsRegEx.test(element.nodeName) ? mergedAttrs : setStrokeFillOpacity(mergedAttrs); +} diff --git a/src/parser/parseElements.ts b/src/parser/parseElements.ts new file mode 100644 index 00000000000..323758da93e --- /dev/null +++ b/src/parser/parseElements.ts @@ -0,0 +1,16 @@ +//@ts-nocheck + +import { fabric } from '../../HEADER'; + +/** + * Transforms an array of svg elements to corresponding fabric.* instances + * @static + * @memberOf fabric + * @param {Array} elements Array of elements to parse + * @param {Function} callback Being passed an array of fabric instances (transformed from SVG elements) + * @param {Object} [options] Options object + * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created. + */ +export function parseElements(elements, callback, options, reviver, parsingOptions) { + new fabric.ElementsParser(elements, callback, options, reviver, parsingOptions).parse(); +} diff --git a/src/parser/parseFontDeclaration.ts b/src/parser/parseFontDeclaration.ts new file mode 100644 index 00000000000..b0e6f277c04 --- /dev/null +++ b/src/parser/parseFontDeclaration.ts @@ -0,0 +1,32 @@ +//@ts-nocheck +import { parseUnit } from '../util'; +import { reFontDeclaration } from './constants'; + + +export function parseFontDeclaration(value, oStyle) { + var match = value.match(reFontDeclaration); + + if (!match) { + return; + } + var fontStyle = match[1], + // font variant is not used + // fontVariant = match[2], + fontWeight = match[3], fontSize = match[4], lineHeight = match[5], fontFamily = match[6]; + + if (fontStyle) { + oStyle.fontStyle = fontStyle; + } + if (fontWeight) { + oStyle.fontWeight = isNaN(parseFloat(fontWeight)) ? fontWeight : parseFloat(fontWeight); + } + if (fontSize) { + oStyle.fontSize = parseUnit(fontSize); + } + if (fontFamily) { + oStyle.fontFamily = fontFamily; + } + if (lineHeight) { + oStyle.lineHeight = lineHeight === 'normal' ? 1 : lineHeight; + } +} diff --git a/src/parser/parsePointsAttribute.ts b/src/parser/parsePointsAttribute.ts new file mode 100644 index 00000000000..83e8682bebc --- /dev/null +++ b/src/parser/parsePointsAttribute.ts @@ -0,0 +1,28 @@ +//@ts-nocheck + +export function parsePointsAttribute(points) { + + // points attribute is required and must not be empty + if (!points) { + return null; + } + + // replace commas with whitespace and remove bookending whitespace + points = points.replace(/,/g, ' ').trim(); + + points = points.split(/\s+/); + var parsedPoints = [], i, len; + + for (i = 0, len = points.length; i < len; i += 2) { + parsedPoints.push({ + x: parseFloat(points[i]), + y: parseFloat(points[i + 1]) + }); + } + + // odd number of points is an error + // if (parsedPoints.length % 2 !== 0) { + // return null; + // } + return parsedPoints; +} diff --git a/src/parser/parseStyleAttribute.ts b/src/parser/parseStyleAttribute.ts new file mode 100644 index 00000000000..fd92e8f1b84 --- /dev/null +++ b/src/parser/parseStyleAttribute.ts @@ -0,0 +1,21 @@ +//@ts-nocheck +import { parseStyleObject } from "./parseStyleObject"; +import { parseStyleString } from "./parseStyleString"; + + +export function parseStyleAttribute(element) { + var oStyle = {}, style = element.getAttribute('style'); + + if (!style) { + return oStyle; + } + + if (typeof style === 'string') { + parseStyleString(style, oStyle); + } + else { + parseStyleObject(style, oStyle); + } + + return oStyle; +} diff --git a/src/parser/parseStyleObject.ts b/src/parser/parseStyleObject.ts new file mode 100644 index 00000000000..b504d87deaf --- /dev/null +++ b/src/parser/parseStyleObject.ts @@ -0,0 +1,15 @@ +//@ts-nocheck + +export function parseStyleObject(style, oStyle) { + var attr, value; + for (var prop in style) { + if (typeof style[prop] === 'undefined') { + continue; + } + + attr = prop.toLowerCase(); + value = style[prop]; + + oStyle[attr] = value; + } +} diff --git a/src/parser/parseStyleString.ts b/src/parser/parseStyleString.ts new file mode 100644 index 00000000000..f0a3791a5ae --- /dev/null +++ b/src/parser/parseStyleString.ts @@ -0,0 +1,14 @@ +//@ts-nocheck + + +export function parseStyleString(style, oStyle) { + var attr, value; + style.replace(/;\s*$/, '').split(';').forEach(function (chunk) { + var pair = chunk.split(':'); + + attr = pair[0].trim().toLowerCase(); + value = pair[1].trim(); + + oStyle[attr] = value; + }); +} diff --git a/src/parser/parseTransformAttribute.ts b/src/parser/parseTransformAttribute.ts new file mode 100644 index 00000000000..16be0f15c19 --- /dev/null +++ b/src/parser/parseTransformAttribute.ts @@ -0,0 +1,86 @@ +//@ts-nocheck +import { commaWsp, iMatrix, reNum } from '../constants'; +import { degreesToRadians, multiplyTransformMatrices } from '../util'; +import { rotateMatrix } from './rotateMatrix'; +import { scaleMatrix } from './scaleMatrix'; +import { skewMatrix } from './skewMatrix'; +import { translateMatrix } from './translateMatrix'; + +// == begin transform regexp +let number = reNum, skewX = '(?:(skewX)\\s*\\(\\s*(' + number + ')\\s*\\))', skewY = '(?:(skewY)\\s*\\(\\s*(' + number + ')\\s*\\))', rotate = '(?:(rotate)\\s*\\(\\s*(' + number + ')(?:' + + commaWsp + '(' + number + ')' + + commaWsp + '(' + number + '))?\\s*\\))', scale = '(?:(scale)\\s*\\(\\s*(' + number + ')(?:' + + commaWsp + '(' + number + '))?\\s*\\))', translate = '(?:(translate)\\s*\\(\\s*(' + number + ')(?:' + + commaWsp + '(' + number + '))?\\s*\\))', matrix = '(?:(matrix)\\s*\\(\\s*' + + '(' + number + ')' + commaWsp + + '(' + number + ')' + commaWsp + + '(' + number + ')' + commaWsp + + '(' + number + ')' + commaWsp + + '(' + number + ')' + commaWsp + + '(' + number + ')' + + '\\s*\\))', transform = '(?:' + + matrix + '|' + + translate + '|' + + scale + '|' + + rotate + '|' + + skewX + '|' + + skewY + + ')', transforms = '(?:' + transform + '(?:' + commaWsp + '*' + transform + ')*' + ')', transformList = '^\\s*(?:' + transforms + '?)\\s*$', + // http://www.w3.org/TR/SVG/coords.html#TransformAttribute + reTransformList = new RegExp(transformList), + // == end transform regexp + reTransform = new RegExp(transform, 'g'); + +export function parseTransformAttribute(attributeValue) { + + // start with identity matrix + var matrix = iMatrix.concat(), matrices = []; + + // return if no argument was given or + // an argument does not match transform attribute regexp + if (!attributeValue || (attributeValue && !reTransformList.test(attributeValue))) { + return matrix; + } + + attributeValue.replace(reTransform, function (match) { + + var m = new RegExp(transform).exec(match).filter(function (match) { + // match !== '' && match != null + return (!!match); + }), operation = m[1], args = m.slice(2).map(parseFloat); + + switch (operation) { + case 'translate': + translateMatrix(matrix, args); + break; + case 'rotate': + args[0] = degreesToRadians(args[0]); + rotateMatrix(matrix, args); + break; + case 'scale': + scaleMatrix(matrix, args); + break; + case 'skewX': + skewMatrix(matrix, args, 2); + break; + case 'skewY': + skewMatrix(matrix, args, 1); + break; + case 'matrix': + matrix = args; + break; + } + + // snapshot current matrix into matrices array + matrices.push(matrix.concat()); + // reset + matrix = iMatrix.concat(); + }); + + var combinedMatrix = matrices[0]; + while (matrices.length > 1) { + matrices.shift(); + combinedMatrix = multiplyTransformMatrices(combinedMatrix, matrices[0]); + } + return combinedMatrix; +} diff --git a/src/parser/parseUseDirectives.ts b/src/parser/parseUseDirectives.ts new file mode 100644 index 00000000000..3c529b18b79 --- /dev/null +++ b/src/parser/parseUseDirectives.ts @@ -0,0 +1,59 @@ +//@ts-nocheck +import { svgNS } from '../constants'; +import { elementById } from './elementById'; +import { getMultipleNodes } from './getMultipleNodes'; +import { applyViewboxTransform } from "./applyViewboxTransform"; + + + +export function parseUseDirectives(doc) { + var nodelist = getMultipleNodes(doc, ['use', 'svg:use']), i = 0; + while (nodelist.length && i < nodelist.length) { + var el = nodelist[i], xlinkAttribute = el.getAttribute('xlink:href') || el.getAttribute('href'); + + if (xlinkAttribute === null) { + return; + } + + var xlink = xlinkAttribute.slice(1), x = el.getAttribute('x') || 0, y = el.getAttribute('y') || 0, el2 = elementById(doc, xlink).cloneNode(true), currentTrans = (el2.getAttribute('transform') || '') + ' translate(' + x + ', ' + y + ')', parentNode, oldLength = nodelist.length, attr, j, attrs, len, namespace = svgNS; + + applyViewboxTransform(el2); + if (/^svg$/i.test(el2.nodeName)) { + var el3 = el2.ownerDocument.createElementNS(namespace, 'g'); + for (j = 0, attrs = el2.attributes, len = attrs.length; j < len; j++) { + attr = attrs.item(j); + el3.setAttributeNS(namespace, attr.nodeName, attr.nodeValue); + } + // el2.firstChild != null + while (el2.firstChild) { + el3.appendChild(el2.firstChild); + } + el2 = el3; + } + + for (j = 0, attrs = el.attributes, len = attrs.length; j < len; j++) { + attr = attrs.item(j); + if (attr.nodeName === 'x' || attr.nodeName === 'y' || + attr.nodeName === 'xlink:href' || attr.nodeName === 'href') { + continue; + } + + if (attr.nodeName === 'transform') { + currentTrans = attr.nodeValue + ' ' + currentTrans; + } + else { + el2.setAttribute(attr.nodeName, attr.nodeValue); + } + } + + el2.setAttribute('transform', currentTrans); + el2.setAttribute('instantiated_by_use', '1'); + el2.removeAttribute('id'); + parentNode = el.parentNode; + parentNode.replaceChild(el2, el); + // some browsers do not shorten nodelist after replaceChild (IE8) + if (nodelist.length === oldLength) { + i++; + } + } +} diff --git a/src/parser/recursivelyParseGradientsXlink.ts b/src/parser/recursivelyParseGradientsXlink.ts new file mode 100644 index 00000000000..0e4853e5cd7 --- /dev/null +++ b/src/parser/recursivelyParseGradientsXlink.ts @@ -0,0 +1,21 @@ +import { elementById } from "./elementById"; + + +export function recursivelyParseGradientsXlink(doc, gradient) { + var gradientsAttrs = ['gradientTransform', 'x1', 'x2', 'y1', 'y2', 'gradientUnits', 'cx', 'cy', 'r', 'fx', 'fy'], xlinkAttr = 'xlink:href', xLink = gradient.getAttribute(xlinkAttr).slice(1), referencedGradient = elementById(doc, xLink); + if (referencedGradient && referencedGradient.getAttribute(xlinkAttr)) { + recursivelyParseGradientsXlink(doc, referencedGradient); + } + gradientsAttrs.forEach(function (attr) { + if (referencedGradient && !gradient.hasAttribute(attr) && referencedGradient.hasAttribute(attr)) { + gradient.setAttribute(attr, referencedGradient.getAttribute(attr)); + } + }); + if (!gradient.children.length) { + var referenceClone = referencedGradient.cloneNode(true); + while (referenceClone.firstChild) { + gradient.appendChild(referenceClone.firstChild); + } + } + gradient.removeAttribute(xlinkAttr); +} diff --git a/src/parser/rotateMatrix.ts b/src/parser/rotateMatrix.ts new file mode 100644 index 00000000000..1cdf9a4be29 --- /dev/null +++ b/src/parser/rotateMatrix.ts @@ -0,0 +1,18 @@ +//@ts-nocheck +import { cos, sin } from '../util'; + + +export function rotateMatrix(matrix, args) { + var cosValue = cos(args[0]), sinValue = sin(args[0]), x = 0, y = 0; + if (args.length === 3) { + x = args[1]; + y = args[2]; + } + + matrix[0] = cosValue; + matrix[1] = sinValue; + matrix[2] = -sinValue; + matrix[3] = cosValue; + matrix[4] = x - (cosValue * x - sinValue * y); + matrix[5] = y - (sinValue * x + cosValue * y); +} diff --git a/src/parser/scaleMatrix.ts b/src/parser/scaleMatrix.ts new file mode 100644 index 00000000000..bab03a5e66d --- /dev/null +++ b/src/parser/scaleMatrix.ts @@ -0,0 +1,8 @@ +//@ts-nocheck + +export function scaleMatrix(matrix, args) { + var multiplierX = args[0], multiplierY = (args.length === 2) ? args[1] : args[0]; + + matrix[0] = multiplierX; + matrix[3] = multiplierY; +} diff --git a/src/parser/selectorMatches.ts b/src/parser/selectorMatches.ts new file mode 100644 index 00000000000..f9fe9ab129f --- /dev/null +++ b/src/parser/selectorMatches.ts @@ -0,0 +1,22 @@ +//@ts-nocheck + + +export function selectorMatches(element, selector) { + var nodeName = element.nodeName, classNames = element.getAttribute('class'), id = element.getAttribute('id'), matcher, i; + // i check if a selector matches slicing away part from it. + // if i get empty string i should match + matcher = new RegExp('^' + nodeName, 'i'); + selector = selector.replace(matcher, ''); + if (id && selector.length) { + matcher = new RegExp('#' + id + '(?![a-zA-Z\\-]+)', 'i'); + selector = selector.replace(matcher, ''); + } + if (classNames && selector.length) { + classNames = classNames.split(' '); + for (i = classNames.length; i--;) { + matcher = new RegExp('\\.' + classNames[i] + '(?![a-zA-Z\\-]+)', 'i'); + selector = selector.replace(matcher, ''); + } + } + return selector.length === 0; +} diff --git a/src/parser/setStrokeFillOpacity.ts b/src/parser/setStrokeFillOpacity.ts new file mode 100644 index 00000000000..8b55de806b7 --- /dev/null +++ b/src/parser/setStrokeFillOpacity.ts @@ -0,0 +1,34 @@ +//@ts-nocheck +import { Color } from '../color'; +import { FabricObject } from '../shapes/object.class'; +import { toFixed } from '../util'; +import { colorAttributes } from './constants'; + +/** + * @private + * @param {Object} attributes Array of attributes to parse + */ + +export function setStrokeFillOpacity(attributes) { + for (var attr in colorAttributes) { + + if (typeof attributes[colorAttributes[attr]] === 'undefined' || attributes[attr] === '') { + continue; + } + + if (typeof attributes[attr] === 'undefined') { + if (!FabricObject.prototype[attr]) { + continue; + } + attributes[attr] = FabricObject.prototype[attr]; + } + + if (attributes[attr].indexOf('url(') === 0) { + continue; + } + + var color = new Color(attributes[attr]); + attributes[attr] = color.setAlpha(toFixed(color.getAlpha() * attributes[colorAttributes[attr]], 2)).toRgba(); + } + return attributes; +} diff --git a/src/parser/skewMatrix.ts b/src/parser/skewMatrix.ts new file mode 100644 index 00000000000..7e9da7eec7f --- /dev/null +++ b/src/parser/skewMatrix.ts @@ -0,0 +1,7 @@ +//@ts-nocheck +import { degreesToRadians } from '../util'; + + +export function skewMatrix(matrix, args, pos) { + matrix[pos] = Math.tan(degreesToRadians(args[0])); +} diff --git a/src/parser/translateMatrix.ts b/src/parser/translateMatrix.ts new file mode 100644 index 00000000000..d82ef05bbf9 --- /dev/null +++ b/src/parser/translateMatrix.ts @@ -0,0 +1,8 @@ +//@ts-nocheck + +export function translateMatrix(matrix, args) { + matrix[4] = args[0]; + if (args.length === 2) { + matrix[5] = args[1]; + } +} From c263b36d0612af520361da8928117eaf2e3fa867 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Thu, 4 Aug 2022 18:58:45 +0300 Subject: [PATCH 02/22] build passes --- HEADER.js | 4 +--- src/constants.ts | 4 ++++ src/parser/applyViewboxTransform.ts | 4 ++-- src/parser/constants.ts | 7 ++++++- src/parser/index.ts | 15 +++++++-------- src/parser/parseAttributes.ts | 4 ++-- src/parser/parseTransformAttribute.ts | 3 ++- src/parser/parseUseDirectives.ts | 2 +- src/parser/setStrokeFillOpacity.ts | 6 +++--- src/parser/uid.ts | 14 ++++++++++++++ src/shapes/text.class.ts | 13 +++++-------- src/util/misc.ts | 4 +++- 12 files changed, 50 insertions(+), 30 deletions(-) create mode 100644 src/parser/uid.ts diff --git a/HEADER.js b/HEADER.js index 5430e1b3cb8..a509b75ae3d 100644 --- a/HEADER.js +++ b/HEADER.js @@ -72,13 +72,11 @@ fabric.SHARED_ATTRIBUTES = [ * Pixel per Inch as a default value set to 96. Can be changed for more realistic conversion. */ fabric.DPI = 96; -fabric.reNum = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:[eE][-+]?\\d+)?)'; -fabric.commaWsp = '(?:\\s+,?\\s*|,\\s*)'; fabric.rePathCommand = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:[eE][-+]?\d+)?)/ig; fabric.reNonWord = /[ \n\.,;!\?\-]/; fabric.fontPaths = { }; fabric.iMatrix = [1, 0, 0, 1, 0, 0]; -fabric.svgNS = 'http://www.w3.org/2000/svg'; + /** * Pixel limit for cache canvases. 1Mpx , 4Mpx should be fine. diff --git a/src/constants.ts b/src/constants.ts index c50b8d2c337..c00b86e2f80 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,2 +1,6 @@ //@ts-nocheck export const halfPI = Math.PI / 2; + +export const iMatrix = Object.freeze([1, 0, 0, 1, 0, 0]); + +export const DEFAULT_SVG_FONT_SIZE = 16; \ No newline at end of file diff --git a/src/parser/applyViewboxTransform.ts b/src/parser/applyViewboxTransform.ts index 5332e38e52a..edac81cd94e 100644 --- a/src/parser/applyViewboxTransform.ts +++ b/src/parser/applyViewboxTransform.ts @@ -1,6 +1,6 @@ //@ts-nocheck -import { svgNS } from '../constants'; -import { parseUnit } from '../util'; +import { svgNS } from './constants'; +import { parsePreserveAspectRatioAttribute, parseUnit } from '../util'; import { svgViewBoxElementsRegEx, reViewBoxAttrValue } from './constants'; /** diff --git a/src/parser/constants.ts b/src/parser/constants.ts index 1a098e97d5c..9679d80ae2e 100644 --- a/src/parser/constants.ts +++ b/src/parser/constants.ts @@ -1,5 +1,4 @@ //@ts-nocheck -import { reNum } from '../constants'; import { getSvgRegex } from "./getSvgRegex"; @@ -7,6 +6,12 @@ export const cssRules = {}; export const gradientDefs = {}; export const clipPaths = {}; +export const reNum = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:[eE][-+]?\\d+)?)'; + +export const svgNS = 'http://www.w3.org/2000/svg'; + +export const commaWsp = '(?:\\s+,?\\s*|,\\s*)'; + export const reFontDeclaration = new RegExp( '(normal|italic)?\\s*(normal|small-caps)?\\s*' + '(normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900)?\\s*(' + diff --git a/src/parser/index.ts b/src/parser/index.ts index bf5cd5f4763..9feb14d60b9 100644 --- a/src/parser/index.ts +++ b/src/parser/index.ts @@ -1,17 +1,16 @@ //@ts-nocheck -import { incrementUID } from '../constants'; import { extend, toArray } from '../util'; -import { hasAncestorWithNodeName } from "./hasAncestorWithNodeName"; import { applyViewboxTransform } from "./applyViewboxTransform"; -import { parseUseDirectives } from "./parseUseDirectives"; +import { clipPaths, cssRules, gradientDefs, svgInvalidAncestorsRegEx, svgValidParentsRegEx, svgValidTagNamesRegEx, svgViewBoxElementsRegEx } from "./constants"; import { elementById } from "./elementById"; -import { parseAttributes } from "./parseAttributes"; -import { parseTransformAttribute } from "./parseTransformAttribute"; -import { parsePointsAttribute } from "./parsePointsAttribute"; import { getMultipleNodes } from "./getMultipleNodes"; +import { hasAncestorWithNodeName } from "./hasAncestorWithNodeName"; +import { parseAttributes } from "./parseAttributes"; import { parseFontDeclaration } from "./parseFontDeclaration"; +import { parsePointsAttribute } from "./parsePointsAttribute"; import { parseStyleAttribute } from "./parseStyleAttribute"; -import { clipPaths, cssRules, gradientDefs, svgInvalidAncestorsRegEx, svgValidParentsRegEx, svgValidTagNamesRegEx, svgViewBoxElementsRegEx } from "./constants"; +import { parseTransformAttribute } from "./parseTransformAttribute"; +import { parseUseDirectives } from "./parseUseDirectives"; (function (global) { @@ -67,7 +66,7 @@ import { clipPaths, cssRules, gradientDefs, svgInvalidAncestorsRegEx, svgValidPa } parseUseDirectives(doc); - var svgUid = incrementUID(), i, len, + var svgUid = fabric.Object.__uid++, i, len, options = applyViewboxTransform(doc), descendants = toArray(doc.getElementsByTagName('*')); options.crossOrigin = parsingOptions && parsingOptions.crossOrigin; diff --git a/src/parser/parseAttributes.ts b/src/parser/parseAttributes.ts index d1ee8a653e5..d17452d0932 100644 --- a/src/parser/parseAttributes.ts +++ b/src/parser/parseAttributes.ts @@ -1,13 +1,13 @@ //@ts-nocheck import { DEFAULT_SVG_FONT_SIZE } from '../constants'; import { parseUnit } from '../util'; -import { svgValidParentsRegEx, cPath, fSize } from './constants'; +import { cPath, fSize, svgValidParentsRegEx } from './constants'; +import { getGlobalStylesForElement } from "./getGlobalStylesForElement"; import { normalizeAttr } from './normalizeAttr'; import { normalizeValue } from './normalizeValue'; import { parseFontDeclaration } from './parseFontDeclaration'; import { parseStyleAttribute } from './parseStyleAttribute'; import { setStrokeFillOpacity } from './setStrokeFillOpacity'; -import { getGlobalStylesForElement } from "./getGlobalStylesForElement"; /** * Returns an object of attributes' name/value, given element and an array of attribute names; diff --git a/src/parser/parseTransformAttribute.ts b/src/parser/parseTransformAttribute.ts index 16be0f15c19..ded9dcd855c 100644 --- a/src/parser/parseTransformAttribute.ts +++ b/src/parser/parseTransformAttribute.ts @@ -1,5 +1,6 @@ //@ts-nocheck -import { commaWsp, iMatrix, reNum } from '../constants'; +import { iMatrix } from '../constants'; +import { commaWsp,reNum } from './constants'; import { degreesToRadians, multiplyTransformMatrices } from '../util'; import { rotateMatrix } from './rotateMatrix'; import { scaleMatrix } from './scaleMatrix'; diff --git a/src/parser/parseUseDirectives.ts b/src/parser/parseUseDirectives.ts index 3c529b18b79..9c1dea7d6d5 100644 --- a/src/parser/parseUseDirectives.ts +++ b/src/parser/parseUseDirectives.ts @@ -1,5 +1,5 @@ //@ts-nocheck -import { svgNS } from '../constants'; +import { svgNS } from './constants'; import { elementById } from './elementById'; import { getMultipleNodes } from './getMultipleNodes'; import { applyViewboxTransform } from "./applyViewboxTransform"; diff --git a/src/parser/setStrokeFillOpacity.ts b/src/parser/setStrokeFillOpacity.ts index 8b55de806b7..daee7f77ed3 100644 --- a/src/parser/setStrokeFillOpacity.ts +++ b/src/parser/setStrokeFillOpacity.ts @@ -1,6 +1,6 @@ //@ts-nocheck +import { fabric } from '../../HEADER'; import { Color } from '../color'; -import { FabricObject } from '../shapes/object.class'; import { toFixed } from '../util'; import { colorAttributes } from './constants'; @@ -17,10 +17,10 @@ export function setStrokeFillOpacity(attributes) { } if (typeof attributes[attr] === 'undefined') { - if (!FabricObject.prototype[attr]) { + if (!fabric.Object.prototype[attr]) { continue; } - attributes[attr] = FabricObject.prototype[attr]; + attributes[attr] = fabric.Object.prototype[attr]; } if (attributes[attr].indexOf('url(') === 0) { diff --git a/src/parser/uid.ts b/src/parser/uid.ts new file mode 100644 index 00000000000..a66b4f56a3b --- /dev/null +++ b/src/parser/uid.ts @@ -0,0 +1,14 @@ + +export let uid = 0; + +export function increment() { + uid++; +} + +export function set(value: number) { + uid = value; +} + +export function reset() { + uid = 0; +} \ No newline at end of file diff --git a/src/shapes/text.class.ts b/src/shapes/text.class.ts index 1f148f9f0a6..c8a90f46486 100644 --- a/src/shapes/text.class.ts +++ b/src/shapes/text.class.ts @@ -1,4 +1,8 @@ //@ts-nocheck + +import { DEFAULT_SVG_FONT_SIZE } from "../constants"; + + (function(global) { var fabric = global.fabric || (global.fabric = { }); @@ -1590,13 +1594,6 @@ fabric.Text.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat( 'x y dx dy font-family font-style font-weight font-size letter-spacing text-decoration text-anchor'.split(' ')); - /** - * Default SVG font size - * @static - * @memberOf fabric.Text - */ - fabric.Text.DEFAULT_SVG_FONT_SIZE = 16; - /** * Returns fabric.Text instance from an SVG element (not yet implemented) * @static @@ -1636,7 +1633,7 @@ options.top += parsedAttributes.dy; } if (!('fontSize' in options)) { - options.fontSize = fabric.Text.DEFAULT_SVG_FONT_SIZE; + options.fontSize = DEFAULT_SVG_FONT_SIZE; } var textContent = ''; diff --git a/src/util/misc.ts b/src/util/misc.ts index a0aff79ed67..8a1934db599 100644 --- a/src/util/misc.ts +++ b/src/util/misc.ts @@ -1,5 +1,7 @@ //@ts-nocheck +import { DEFAULT_SVG_FONT_SIZE } from '../constants'; import { cos } from './cos'; + (function(global) { var fabric = global.fabric, sqrt = Math.sqrt, atan2 = Math.atan2, @@ -400,7 +402,7 @@ import { cos } from './cos'; var unit = /\D{0,2}$/.exec(value), number = parseFloat(value); if (!fontSize) { - fontSize = fabric.Text.DEFAULT_SVG_FONT_SIZE; + fontSize = DEFAULT_SVG_FONT_SIZE; } switch (unit[0]) { case 'mm': From bbc8ff5ec10c5425b817eea8ba666383903d6716 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Thu, 4 Aug 2022 19:02:08 +0300 Subject: [PATCH 03/22] fix imports --- HEADER.js | 1 - src/parser/constants.ts | 2 ++ src/util/path.ts | 10 +++++++--- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/HEADER.js b/HEADER.js index a509b75ae3d..967d480f711 100644 --- a/HEADER.js +++ b/HEADER.js @@ -72,7 +72,6 @@ fabric.SHARED_ATTRIBUTES = [ * Pixel per Inch as a default value set to 96. Can be changed for more realistic conversion. */ fabric.DPI = 96; -fabric.rePathCommand = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:[eE][-+]?\d+)?)/ig; fabric.reNonWord = /[ \n\.,;!\?\-]/; fabric.fontPaths = { }; fabric.iMatrix = [1, 0, 0, 1, 0, 0]; diff --git a/src/parser/constants.ts b/src/parser/constants.ts index 9679d80ae2e..a56eb623ab5 100644 --- a/src/parser/constants.ts +++ b/src/parser/constants.ts @@ -12,6 +12,8 @@ export const svgNS = 'http://www.w3.org/2000/svg'; export const commaWsp = '(?:\\s+,?\\s*|,\\s*)'; +export const rePathCommand = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:[eE][-+]?\d+)?)/ig; + export const reFontDeclaration = new RegExp( '(normal|italic)?\\s*(normal|small-caps)?\\s*' + '(normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900)?\\s*(' + diff --git a/src/util/path.ts b/src/util/path.ts index 3006875baff..40fc69a818d 100644 --- a/src/util/path.ts +++ b/src/util/path.ts @@ -1,4 +1,8 @@ //@ts-nocheck + +import { commaWsp, rePathCommand } from "../parser/constants"; + + (function(global) { var fabric = global.fabric, _join = Array.prototype.join, @@ -680,10 +684,10 @@ coords = [], currentPath, parsed, - re = fabric.rePathCommand, + re = rePathCommand, rNumber = '[-+]?(?:\\d*\\.\\d+|\\d+\\.?)(?:[eE][-+]?\\d+)?\\s*', - rNumberCommaWsp = '(' + rNumber + ')' + fabric.commaWsp, - rFlagCommaWsp = '([01])' + fabric.commaWsp + '?', + rNumberCommaWsp = '(' + rNumber + ')' + commaWsp, + rFlagCommaWsp = '([01])' + commaWsp + '?', rArcSeq = rNumberCommaWsp + '?' + rNumberCommaWsp + '?' + rNumberCommaWsp + rFlagCommaWsp + rFlagCommaWsp + rNumberCommaWsp + '?(' + rNumber + ')', regArcArgumentSequence = new RegExp(rArcSeq, 'g'), From 0999697e7543d62bba23186ed19360bada459ca9 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Thu, 4 Aug 2022 19:25:50 +0300 Subject: [PATCH 04/22] Update applyViewboxTransform.ts --- src/parser/applyViewboxTransform.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser/applyViewboxTransform.ts b/src/parser/applyViewboxTransform.ts index edac81cd94e..8929af7ab12 100644 --- a/src/parser/applyViewboxTransform.ts +++ b/src/parser/applyViewboxTransform.ts @@ -98,7 +98,7 @@ export function applyViewboxTransform(element) { (minX * scaleX + widthDiff) + ' ' + (minY * scaleY + heightDiff) + ') '; // seems unused. - // parsedDim.viewboxTransform = fabric.parseTransformAttribute(matrix); + // parsedDim.viewboxTransform = parseTransformAttribute(matrix); if (element.nodeName === 'svg') { el = element.ownerDocument.createElementNS(svgNS, 'g'); // element.firstChild != null From 71d8f4e5d983cb5992a3c569b3c48d82c787e9c0 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Thu, 4 Aug 2022 19:25:57 +0300 Subject: [PATCH 05/22] Update getCSSRules.ts --- src/parser/getCSSRules.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/parser/getCSSRules.ts b/src/parser/getCSSRules.ts index 93022b0fc65..cae4882ea6d 100644 --- a/src/parser/getCSSRules.ts +++ b/src/parser/getCSSRules.ts @@ -1,12 +1,11 @@ +//@ts-nocheck + + /** * Returns CSS rules for a given SVG document - * @static - * @function - * @memberOf fabric * @param {SVGDocument} doc SVG document to parse * @return {Object} CSS rules of this document */ - export function getCSSRules(doc) { var styles = doc.getElementsByTagName('style'), i, len, allRules = {}, rules; From 331970d1c38787768bba115f51da7e257e1b1b37 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Thu, 4 Aug 2022 19:26:10 +0300 Subject: [PATCH 06/22] Update getGradientDefs.ts --- src/parser/getGradientDefs.ts | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/parser/getGradientDefs.ts b/src/parser/getGradientDefs.ts index 0838679915a..ea6faf1d0b9 100644 --- a/src/parser/getGradientDefs.ts +++ b/src/parser/getGradientDefs.ts @@ -1,21 +1,22 @@ +//@ts-nocheck + import { getMultipleNodes } from "./getMultipleNodes"; import { recursivelyParseGradientsXlink } from './recursivelyParseGradientsXlink'; +const tagArray = [ + 'linearGradient', + 'radialGradient', + 'svg:linearGradient', + 'svg:radialGradient' +]; + /** * Parses an SVG document, returning all of the gradient declarations found in it - * @static - * @function - * @memberOf fabric * @param {SVGDocument} doc SVG document to parse * @return {Object} Gradient definitions; key corresponds to element id, value -- to gradient definition element */ -function getGradientDefs(doc) { - var tagArray = [ - 'linearGradient', - 'radialGradient', - 'svg:linearGradient', - 'svg:radialGradient' - ], elList = getMultipleNodes(doc, tagArray), el, j = 0, gradientDefs = {}; +export function getGradientDefs(doc) { + var elList = getMultipleNodes(doc, tagArray), el, j = 0, gradientDefs = {}; j = elList.length; while (j--) { el = elList[j]; From 85cf000568821c6fdf559385eeeb0451d990e6bb Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Thu, 4 Aug 2022 19:26:39 +0300 Subject: [PATCH 07/22] Update loadSVGFromString.ts --- src/parser/loadSVGFromString.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/parser/loadSVGFromString.ts b/src/parser/loadSVGFromString.ts index 72ed0c9c91a..01da72557a4 100644 --- a/src/parser/loadSVGFromString.ts +++ b/src/parser/loadSVGFromString.ts @@ -1,3 +1,7 @@ +//@ts-nocheck +import { fabric } from "../../HEADER"; +import { parseSVGDocument } from "./parseSVGDocument"; + /** * Takes string corresponding to an SVG document, and parses it into a set of fabric objects * @memberOf fabric @@ -8,10 +12,9 @@ * @param {String} [options.crossOrigin] crossOrigin crossOrigin setting to use for external resources * @param {AbortSignal} [options.signal] handle aborting, see https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal */ - export function loadSVGFromString(string, callback, reviver, options) { var parser = new fabric.window.DOMParser(), doc = parser.parseFromString(string.trim(), 'text/xml'); - fabric.parseSVGDocument(doc.documentElement, function (results, _options, elements, allElements) { + parseSVGDocument(doc.documentElement, function (results, _options, elements, allElements) { callback(results, _options, elements, allElements); }, reviver, options); } From 74c06f0bea0294a74f0955e7ffb989a81e8fd60c Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Thu, 4 Aug 2022 19:26:47 +0300 Subject: [PATCH 08/22] Update loadSVGFromURL.ts --- src/parser/loadSVGFromURL.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/parser/loadSVGFromURL.ts b/src/parser/loadSVGFromURL.ts index 31029ca97fd..666dbad6440 100644 --- a/src/parser/loadSVGFromURL.ts +++ b/src/parser/loadSVGFromURL.ts @@ -1,3 +1,7 @@ +//@ts-nocheck + +import { parseSVGDocument } from "./parseSVGDocument"; + /** * Takes url corresponding to an SVG document, and parses it into a set of fabric objects. * Note that SVG is fetched via XMLHttpRequest, so it needs to conform to SOP (Same Origin Policy) @@ -9,7 +13,6 @@ * @param {String} [options.crossOrigin] crossOrigin crossOrigin setting to use for external resources * @param {AbortSignal} [options.signal] handle aborting, see https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal */ - export function loadSVGFromURL(url, callback, reviver, options) { url = url.replace(/^\n\s*/, '').trim(); @@ -27,7 +30,7 @@ export function loadSVGFromURL(url, callback, reviver, options) { return false; } - fabric.parseSVGDocument(xml.documentElement, function (results, _options, elements, allElements) { + parseSVGDocument(xml.documentElement, function (results, _options, elements, allElements) { callback && callback(results, _options, elements, allElements); }, reviver, options); } From f8f1afe03ab13f4dffb1c6ddf66c130e3180ed05 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Thu, 4 Aug 2022 19:26:54 +0300 Subject: [PATCH 09/22] Update parseTransformAttribute.ts --- src/parser/parseTransformAttribute.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/parser/parseTransformAttribute.ts b/src/parser/parseTransformAttribute.ts index ded9dcd855c..7661967bd6d 100644 --- a/src/parser/parseTransformAttribute.ts +++ b/src/parser/parseTransformAttribute.ts @@ -32,6 +32,14 @@ let number = reNum, skewX = '(?:(skewX)\\s*\\(\\s*(' + number + ')\\s*\\))', ske // == end transform regexp reTransform = new RegExp(transform, 'g'); +/** + * Parses "transform" attribute, returning an array of values + * @static + * @function + * @memberOf fabric + * @param {String} attributeValue String containing attribute value + * @return {Array} Array of 6 elements representing transformation matrix + */ export function parseTransformAttribute(attributeValue) { // start with identity matrix From 21e2e5ff0fdcf10832e37a586148b04ffb4585fe Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Thu, 4 Aug 2022 19:27:05 +0300 Subject: [PATCH 10/22] Update recursivelyParseGradientsXlink.ts --- src/parser/recursivelyParseGradientsXlink.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/parser/recursivelyParseGradientsXlink.ts b/src/parser/recursivelyParseGradientsXlink.ts index 0e4853e5cd7..cff0d564802 100644 --- a/src/parser/recursivelyParseGradientsXlink.ts +++ b/src/parser/recursivelyParseGradientsXlink.ts @@ -1,8 +1,13 @@ +//@ts-nocheck + import { elementById } from "./elementById"; +const gradientsAttrs = ['gradientTransform', 'x1', 'x2', 'y1', 'y2', 'gradientUnits', 'cx', 'cy', 'r', 'fx', 'fy']; +const xlinkAttr = 'xlink:href'; + export function recursivelyParseGradientsXlink(doc, gradient) { - var gradientsAttrs = ['gradientTransform', 'x1', 'x2', 'y1', 'y2', 'gradientUnits', 'cx', 'cy', 'r', 'fx', 'fy'], xlinkAttr = 'xlink:href', xLink = gradient.getAttribute(xlinkAttr).slice(1), referencedGradient = elementById(doc, xLink); + const xLink = gradient.getAttribute(xlinkAttr).slice(1), referencedGradient = elementById(doc, xLink); if (referencedGradient && referencedGradient.getAttribute(xlinkAttr)) { recursivelyParseGradientsXlink(doc, referencedGradient); } @@ -12,7 +17,7 @@ export function recursivelyParseGradientsXlink(doc, gradient) { } }); if (!gradient.children.length) { - var referenceClone = referencedGradient.cloneNode(true); + const referenceClone = referencedGradient.cloneNode(true); while (referenceClone.firstChild) { gradient.appendChild(referenceClone.firstChild); } From 6832c0fb54ec51f4c60fa0b55b0997329a4e37c8 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Thu, 4 Aug 2022 19:27:08 +0300 Subject: [PATCH 11/22] Delete uid.ts --- src/parser/uid.ts | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 src/parser/uid.ts diff --git a/src/parser/uid.ts b/src/parser/uid.ts deleted file mode 100644 index a66b4f56a3b..00000000000 --- a/src/parser/uid.ts +++ /dev/null @@ -1,14 +0,0 @@ - -export let uid = 0; - -export function increment() { - uid++; -} - -export function set(value: number) { - uid = value; -} - -export function reset() { - uid = 0; -} \ No newline at end of file From 0bed2e2c77bd7aa898c4416d4833f8877dfe2376 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Thu, 4 Aug 2022 19:36:48 +0300 Subject: [PATCH 12/22] c --- src/parser/parseAttributes.ts | 1 - src/parser/parseFontDeclaration.ts | 9 ++++++++- src/parser/parsePointsAttribute.ts | 8 ++++++++ src/parser/parseStyleAttribute.ts | 8 +++++++- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/parser/parseAttributes.ts b/src/parser/parseAttributes.ts index d17452d0932..5e58a79c41f 100644 --- a/src/parser/parseAttributes.ts +++ b/src/parser/parseAttributes.ts @@ -16,7 +16,6 @@ import { setStrokeFillOpacity } from './setStrokeFillOpacity'; * @param {Array} attributes Array of attributes to parse * @return {Object} object containing parsed attributes' names/values */ - export function parseAttributes(element, attributes, svgUid) { if (!element) { diff --git a/src/parser/parseFontDeclaration.ts b/src/parser/parseFontDeclaration.ts index b0e6f277c04..ccc68e577c1 100644 --- a/src/parser/parseFontDeclaration.ts +++ b/src/parser/parseFontDeclaration.ts @@ -2,7 +2,14 @@ import { parseUnit } from '../util'; import { reFontDeclaration } from './constants'; - +/** + * Parses a short font declaration, building adding its properties to a style object + * @static + * @function + * @memberOf fabric + * @param {String} value font declaration + * @param {Object} oStyle definition + */ export function parseFontDeclaration(value, oStyle) { var match = value.match(reFontDeclaration); diff --git a/src/parser/parsePointsAttribute.ts b/src/parser/parsePointsAttribute.ts index 83e8682bebc..280ce60dc72 100644 --- a/src/parser/parsePointsAttribute.ts +++ b/src/parser/parsePointsAttribute.ts @@ -1,5 +1,13 @@ //@ts-nocheck + +/** + * Parses "points" attribute, returning an array of values + * @static + * @memberOf fabric + * @param {String} points points attribute string + * @return {Array} array of points + */ export function parsePointsAttribute(points) { // points attribute is required and must not be empty diff --git a/src/parser/parseStyleAttribute.ts b/src/parser/parseStyleAttribute.ts index fd92e8f1b84..aae1b537183 100644 --- a/src/parser/parseStyleAttribute.ts +++ b/src/parser/parseStyleAttribute.ts @@ -2,7 +2,13 @@ import { parseStyleObject } from "./parseStyleObject"; import { parseStyleString } from "./parseStyleString"; - +/** + * Parses "style" attribute, retuning an object with values + * @static + * @memberOf fabric + * @param {SVGElement} element Element to parse + * @return {Object} Objects with values parsed from style attribute of an element + */ export function parseStyleAttribute(element) { var oStyle = {}, style = element.getAttribute('style'); From f6663cb0fceb3510448cf82d391679e40c96b66c Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Thu, 4 Aug 2022 19:43:18 +0300 Subject: [PATCH 13/22] Create parseSVGDocument.ts --- src/parser/index.ts | 179 ++++---------------------------------------- 1 file changed, 13 insertions(+), 166 deletions(-) diff --git a/src/parser/index.ts b/src/parser/index.ts index 9feb14d60b9..dc83320d4a7 100644 --- a/src/parser/index.ts +++ b/src/parser/index.ts @@ -3,9 +3,14 @@ import { extend, toArray } from '../util'; import { applyViewboxTransform } from "./applyViewboxTransform"; import { clipPaths, cssRules, gradientDefs, svgInvalidAncestorsRegEx, svgValidParentsRegEx, svgValidTagNamesRegEx, svgViewBoxElementsRegEx } from "./constants"; import { elementById } from "./elementById"; +import { getCSSRules } from './getCSSRules'; +import { getGradientDefs } from './getGradientDefs'; import { getMultipleNodes } from "./getMultipleNodes"; import { hasAncestorWithNodeName } from "./hasAncestorWithNodeName"; +import { loadSVGFromString } from './loadSVGFromString'; +import { loadSVGFromURL } from './loadSVGFromURL'; import { parseAttributes } from "./parseAttributes"; +import { parseElements } from './parseElements'; import { parseFontDeclaration } from "./parseFontDeclaration"; import { parsePointsAttribute } from "./parsePointsAttribute"; import { parseStyleAttribute } from "./parseStyleAttribute"; @@ -141,52 +146,13 @@ import { parseUseDirectives } from "./parseUseDirectives"; extend(fabric, { - /** - * Parses a short font declaration, building adding its properties to a style object - * @static - * @function - * @memberOf fabric - * @param {String} value font declaration - * @param {Object} oStyle definition - */ + parseFontDeclaration, - /** - * Parses an SVG document, returning all of the gradient declarations found in it - * @static - * @function - * @memberOf fabric - * @param {SVGDocument} doc SVG document to parse - * @return {Object} Gradient definitions; key corresponds to element id, value -- to gradient definition element - */ - getGradientDefs: function (doc) { - var tagArray = [ - 'linearGradient', - 'radialGradient', - 'svg:linearGradient', - 'svg:radialGradient'], - elList = getMultipleNodes(doc, tagArray), - el, j = 0, gradientDefs = {}; - j = elList.length; - while (j--) { - el = elList[j]; - if (el.getAttribute('xlink:href')) { - recursivelyParseGradientsXlink(doc, el); - } - gradientDefs[el.getAttribute('id')] = el; - } - return gradientDefs; - }, - /** - * Returns an object of attributes' name/value, given element and an array of attribute names; - * Parses parent "g" nodes recursively upwards. - * @static - * @memberOf fabric - * @param {DOMElement} element Element to parse - * @param {Array} attributes Array of attributes to parse - * @return {Object} object containing parsed attributes' names/values - */ + getGradientDefs, + + parseAttributes, /** @@ -198,137 +164,18 @@ import { parseUseDirectives } from "./parseUseDirectives"; * @param {Object} [options] Options object * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created. */ - parseElements: function (elements, callback, options, reviver, parsingOptions) { - new fabric.ElementsParser(elements, callback, options, reviver, parsingOptions).parse(); - }, + parseElements, - /** - * Parses "style" attribute, retuning an object with values - * @static - * @memberOf fabric - * @param {SVGElement} element Element to parse - * @return {Object} Objects with values parsed from style attribute of an element - */ parseStyleAttribute, - /** - * Parses "points" attribute, returning an array of values - * @static - * @memberOf fabric - * @param {String} points points attribute string - * @return {Array} array of points - */ parsePointsAttribute, - /** - * Returns CSS rules for a given SVG document - * @static - * @function - * @memberOf fabric - * @param {SVGDocument} doc SVG document to parse - * @return {Object} CSS rules of this document - */ - getCSSRules: function (doc) { - var styles = doc.getElementsByTagName('style'), i, len, - allRules = {}, rules; - - // very crude parsing of style contents - for (i = 0, len = styles.length; i < len; i++) { - var styleContents = styles[i].textContent; - - // remove comments - styleContents = styleContents.replace(/\/\*[\s\S]*?\*\//g, ''); - if (styleContents.trim() === '') { - continue; - } - // recovers all the rule in this form `body { style code... }` - // rules = styleContents.match(/[^{]*\{[\s\S]*?\}/g); - rules = styleContents.split('}'); - // remove empty rules. - rules = rules.filter(function (rule) { return rule.trim(); }); - // at this point we have hopefully an array of rules `body { style code... ` - // eslint-disable-next-line no-loop-func - rules.forEach(function (rule) { + getCSSRules, - var match = rule.split('{'), - ruleObj = {}, declaration = match[1].trim(), - propertyValuePairs = declaration.split(';').filter(function (pair) { return pair.trim(); }); - for (i = 0, len = propertyValuePairs.length; i < len; i++) { - var pair = propertyValuePairs[i].split(':'), - property = pair[0].trim(), - value = pair[1].trim(); - ruleObj[property] = value; - } - rule = match[0].trim(); - rule.split(',').forEach(function (_rule) { - _rule = _rule.replace(/^svg/i, '').trim(); - if (_rule === '') { - return; - } - if (allRules[_rule]) { - Object.assign(allRules[_rule], ruleObj); - } - else { - allRules[_rule] = Object.assign({}, ruleObj); - } - }); - }); - } - return allRules; - }, - - /** - * Takes url corresponding to an SVG document, and parses it into a set of fabric objects. - * Note that SVG is fetched via XMLHttpRequest, so it needs to conform to SOP (Same Origin Policy) - * @memberOf fabric - * @param {String} url - * @param {Function} callback - * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created. - * @param {Object} [options] Object containing options for parsing - * @param {String} [options.crossOrigin] crossOrigin crossOrigin setting to use for external resources - * @param {AbortSignal} [options.signal] handle aborting, see https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal - */ - loadSVGFromURL: function (url, callback, reviver, options) { + loadSVGFromURL, - url = url.replace(/^\n\s*/, '').trim(); - new request(url, { - method: 'get', - onComplete: onComplete, - signal: options && options.signal - }); - - function onComplete(r) { - - var xml = r.responseXML; - if (!xml || !xml.documentElement) { - callback && callback(null); - return false; - } - - fabric.parseSVGDocument(xml.documentElement, function (results, _options, elements, allElements) { - callback && callback(results, _options, elements, allElements); - }, reviver, options); - } - }, - - /** - * Takes string corresponding to an SVG document, and parses it into a set of fabric objects - * @memberOf fabric - * @param {String} string - * @param {Function} callback - * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created. - * @param {Object} [options] Object containing options for parsing - * @param {String} [options.crossOrigin] crossOrigin crossOrigin setting to use for external resources - * @param {AbortSignal} [options.signal] handle aborting, see https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal - */ - loadSVGFromString: function (string, callback, reviver, options) { - var parser = new fabric.window.DOMParser(), - doc = parser.parseFromString(string.trim(), 'text/xml'); - fabric.parseSVGDocument(doc.documentElement, function (results, _options, elements, allElements) { - callback(results, _options, elements, allElements); - }, reviver, options); - } + loadSVGFromString }); })(typeof exports !== 'undefined' ? exports : window); From 125708505f615f19846691ef34c1bce7fe7a9ff4 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Thu, 4 Aug 2022 19:44:20 +0300 Subject: [PATCH 14/22] Update index.ts --- src/parser/index.ts | 52 +-------------------------------------------- 1 file changed, 1 insertion(+), 51 deletions(-) diff --git a/src/parser/index.ts b/src/parser/index.ts index dc83320d4a7..1e12fb04f44 100644 --- a/src/parser/index.ts +++ b/src/parser/index.ts @@ -1,11 +1,10 @@ //@ts-nocheck +import { fabric } from '../../HEADER'; import { extend, toArray } from '../util'; import { applyViewboxTransform } from "./applyViewboxTransform"; import { clipPaths, cssRules, gradientDefs, svgInvalidAncestorsRegEx, svgValidParentsRegEx, svgValidTagNamesRegEx, svgViewBoxElementsRegEx } from "./constants"; -import { elementById } from "./elementById"; import { getCSSRules } from './getCSSRules'; import { getGradientDefs } from './getGradientDefs'; -import { getMultipleNodes } from "./getMultipleNodes"; import { hasAncestorWithNodeName } from "./hasAncestorWithNodeName"; import { loadSVGFromString } from './loadSVGFromString'; import { loadSVGFromURL } from './loadSVGFromURL'; @@ -18,14 +17,6 @@ import { parseTransformAttribute } from "./parseTransformAttribute"; import { parseUseDirectives } from "./parseUseDirectives"; -(function (global) { - /** - * @name fabric - * @namespace - */ - - var fabric = global.fabric || (global.fabric = {}); - fabric.svgValidTagNamesRegEx = svgValidTagNamesRegEx; @@ -37,15 +28,6 @@ import { parseUseDirectives } from "./parseUseDirectives"; fabric.gradientDefs = gradientDefs; fabric.clipPaths = clipPaths; - - /** - * Parses "transform" attribute, returning an array of values - * @static - * @function - * @memberOf fabric - * @param {String} attributeValue String containing attribute value - * @return {Array} Array of 6 elements representing transformation matrix - */ fabric.parseTransformAttribute = parseTransformAttribute; @@ -121,28 +103,6 @@ import { parseUseDirectives } from "./parseUseDirectives"; }, Object.assign({}, options), reviver, parsingOptions); }; - function recursivelyParseGradientsXlink(doc, gradient) { - var gradientsAttrs = ['gradientTransform', 'x1', 'x2', 'y1', 'y2', 'gradientUnits', 'cx', 'cy', 'r', 'fx', 'fy'], - xlinkAttr = 'xlink:href', - xLink = gradient.getAttribute(xlinkAttr).slice(1), - referencedGradient = elementById(doc, xLink); - if (referencedGradient && referencedGradient.getAttribute(xlinkAttr)) { - recursivelyParseGradientsXlink(doc, referencedGradient); - } - gradientsAttrs.forEach(function (attr) { - if (referencedGradient && !gradient.hasAttribute(attr) && referencedGradient.hasAttribute(attr)) { - gradient.setAttribute(attr, referencedGradient.getAttribute(attr)); - } - }); - if (!gradient.children.length) { - var referenceClone = referencedGradient.cloneNode(true); - while (referenceClone.firstChild) { - gradient.appendChild(referenceClone.firstChild); - } - } - gradient.removeAttribute(xlinkAttr); - } - extend(fabric, { @@ -155,15 +115,6 @@ import { parseUseDirectives } from "./parseUseDirectives"; parseAttributes, - /** - * Transforms an array of svg elements to corresponding fabric.* instances - * @static - * @memberOf fabric - * @param {Array} elements Array of elements to parse - * @param {Function} callback Being passed an array of fabric instances (transformed from SVG elements) - * @param {Object} [options] Options object - * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created. - */ parseElements, parseStyleAttribute, @@ -178,4 +129,3 @@ import { parseUseDirectives } from "./parseUseDirectives"; loadSVGFromString }); -})(typeof exports !== 'undefined' ? exports : window); From f992a1b79c0214170bc813bae50a6858da78b9ca Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Thu, 4 Aug 2022 19:46:06 +0300 Subject: [PATCH 15/22] m --- src/parser/index.ts | 78 ++------------------------------ src/parser/parseSVGDocument.ts | 81 ++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 75 deletions(-) create mode 100644 src/parser/parseSVGDocument.ts diff --git a/src/parser/index.ts b/src/parser/index.ts index 1e12fb04f44..7ba1f502b0b 100644 --- a/src/parser/index.ts +++ b/src/parser/index.ts @@ -1,11 +1,9 @@ //@ts-nocheck import { fabric } from '../../HEADER'; -import { extend, toArray } from '../util'; -import { applyViewboxTransform } from "./applyViewboxTransform"; +import { extend } from '../util'; import { clipPaths, cssRules, gradientDefs, svgInvalidAncestorsRegEx, svgValidParentsRegEx, svgValidTagNamesRegEx, svgViewBoxElementsRegEx } from "./constants"; import { getCSSRules } from './getCSSRules'; import { getGradientDefs } from './getGradientDefs'; -import { hasAncestorWithNodeName } from "./hasAncestorWithNodeName"; import { loadSVGFromString } from './loadSVGFromString'; import { loadSVGFromURL } from './loadSVGFromURL'; import { parseAttributes } from "./parseAttributes"; @@ -13,8 +11,8 @@ import { parseElements } from './parseElements'; import { parseFontDeclaration } from "./parseFontDeclaration"; import { parsePointsAttribute } from "./parsePointsAttribute"; import { parseStyleAttribute } from "./parseStyleAttribute"; +import { parseSVGDocument } from './parseSVGDocument'; import { parseTransformAttribute } from "./parseTransformAttribute"; -import { parseUseDirectives } from "./parseUseDirectives"; @@ -31,77 +29,7 @@ import { parseUseDirectives } from "./parseUseDirectives"; fabric.parseTransformAttribute = parseTransformAttribute; - /** - * Parses an SVG document, converts it to an array of corresponding fabric.* instances and passes them to a callback - * @static - * @function - * @memberOf fabric - * @param {SVGDocument} doc SVG document to parse - * @param {Function} callback Callback to call when parsing is finished; - * It's being passed an array of elements (parsed from a document). - * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created. - * @param {Object} [parsingOptions] options for parsing document - * @param {String} [parsingOptions.crossOrigin] crossOrigin settings - * @param {AbortSignal} [parsingOptions.signal] see https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal - */ - fabric.parseSVGDocument = function (doc, callback, reviver, parsingOptions) { - if (!doc) { - return; - } - if (parsingOptions && parsingOptions.signal && parsingOptions.signal.aborted) { - throw new Error('`options.signal` is in `aborted` state'); - } - parseUseDirectives(doc); - - var svgUid = fabric.Object.__uid++, i, len, - options = applyViewboxTransform(doc), - descendants = toArray(doc.getElementsByTagName('*')); - options.crossOrigin = parsingOptions && parsingOptions.crossOrigin; - options.svgUid = svgUid; - options.signal = parsingOptions && parsingOptions.signal; - - if (descendants.length === 0 && isLikelyNode) { - // we're likely in node, where "o3-xml" library fails to gEBTN("*") - // https://github.com/ajaxorg/node-o3-xml/issues/21 - descendants = doc.selectNodes('//*[name(.)!="svg"]'); - var arr = []; - for (i = 0, len = descendants.length; i < len; i++) { - arr[i] = descendants[i]; - } - descendants = arr; - } - - var elements = descendants.filter(function (el) { - applyViewboxTransform(el); - return svgValidTagNamesRegEx.test(el.nodeName.replace('svg:', '')) && - !hasAncestorWithNodeName(el, svgInvalidAncestorsRegEx); // http://www.w3.org/TR/SVG/struct.html#DefsElement - }); - if (!elements || (elements && !elements.length)) { - callback && callback([], {}); - return; - } - var clipPaths = {}; - descendants.filter(function (el) { - return el.nodeName.replace('svg:', '') === 'clipPath'; - }).forEach(function (el) { - var id = el.getAttribute('id'); - clipPaths[id] = toArray(el.getElementsByTagName('*')).filter(function (el) { - return svgValidTagNamesRegEx.test(el.nodeName.replace('svg:', '')); - }); - }); - fabric.gradientDefs[svgUid] = fabric.getGradientDefs(doc); - fabric.cssRules[svgUid] = fabric.getCSSRules(doc); - fabric.clipPaths[svgUid] = clipPaths; - // Precedence of rules: style > class > attribute - fabric.parseElements(elements, function (instances, elements) { - if (callback) { - callback(instances, options, elements, descendants); - delete fabric.gradientDefs[svgUid]; - delete fabric.cssRules[svgUid]; - delete fabric.clipPaths[svgUid]; - } - }, Object.assign({}, options), reviver, parsingOptions); - }; + fabric.parseSVGDocument = parseSVGDocument diff --git a/src/parser/parseSVGDocument.ts b/src/parser/parseSVGDocument.ts new file mode 100644 index 00000000000..91993fd7687 --- /dev/null +++ b/src/parser/parseSVGDocument.ts @@ -0,0 +1,81 @@ +//@ts-nocheck + +import { fabric } from '../../HEADER'; +import { toArray } from '../util'; +import { applyViewboxTransform } from "./applyViewboxTransform"; +import { clipPaths, cssRules, gradientDefs, svgInvalidAncestorsRegEx, svgValidTagNamesRegEx } from "./constants"; +import { getCSSRules } from './getCSSRules'; +import { getGradientDefs } from './getGradientDefs'; +import { hasAncestorWithNodeName } from "./hasAncestorWithNodeName"; +import { parseElements } from './parseElements'; +import { parseUseDirectives } from "./parseUseDirectives"; + +/** + * Parses an SVG document, converts it to an array of corresponding fabric.* instances and passes them to a callback + * @static + * @function + * @memberOf fabric + * @param {SVGDocument} doc SVG document to parse + * @param {Function} callback Callback to call when parsing is finished; + * It's being passed an array of elements (parsed from a document). + * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created. + * @param {Object} [parsingOptions] options for parsing document + * @param {String} [parsingOptions.crossOrigin] crossOrigin settings + * @param {AbortSignal} [parsingOptions.signal] see https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal + */ +export function parseSVGDocument(doc, callback, reviver, parsingOptions) { + if (!doc) { + return; + } + if (parsingOptions && parsingOptions.signal && parsingOptions.signal.aborted) { + throw new Error('`options.signal` is in `aborted` state'); + } + parseUseDirectives(doc); + + var svgUid = fabric.Object.__uid++, i, len, options = applyViewboxTransform(doc), descendants = toArray(doc.getElementsByTagName('*')); + options.crossOrigin = parsingOptions && parsingOptions.crossOrigin; + options.svgUid = svgUid; + options.signal = parsingOptions && parsingOptions.signal; + + if (descendants.length === 0 && isLikelyNode) { + // we're likely in node, where "o3-xml" library fails to gEBTN("*") + // https://github.com/ajaxorg/node-o3-xml/issues/21 + descendants = doc.selectNodes('//*[name(.)!="svg"]'); + var arr = []; + for (i = 0, len = descendants.length; i < len; i++) { + arr[i] = descendants[i]; + } + descendants = arr; + } + + var elements = descendants.filter(function (el) { + applyViewboxTransform(el); + return svgValidTagNamesRegEx.test(el.nodeName.replace('svg:', '')) && + !hasAncestorWithNodeName(el, svgInvalidAncestorsRegEx); // http://www.w3.org/TR/SVG/struct.html#DefsElement + }); + if (!elements || (elements && !elements.length)) { + callback && callback([], {}); + return; + } + const localClipPaths = {}; + descendants.filter(function (el) { + return el.nodeName.replace('svg:', '') === 'clipPath'; + }).forEach(function (el) { + var id = el.getAttribute('id'); + localClipPaths[id] = toArray(el.getElementsByTagName('*')).filter(function (el) { + return svgValidTagNamesRegEx.test(el.nodeName.replace('svg:', '')); + }); + }); + gradientDefs[svgUid] = getGradientDefs(doc); + cssRules[svgUid] = getCSSRules(doc); + clipPaths[svgUid] = localClipPaths; + // Precedence of rules: style > class > attribute + parseElements(elements, function (instances, elements) { + if (callback) { + callback(instances, options, elements, descendants); + delete gradientDefs[svgUid]; + delete cssRules[svgUid]; + delete clipPaths[svgUid]; + } + }, Object.assign({}, options), reviver, parsingOptions); +} From 44b9f10fc57f867f614b776e7d4450523bd98163 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Thu, 4 Aug 2022 19:50:13 +0300 Subject: [PATCH 16/22] Update index.ts --- src/parser/index.ts | 58 +++++++++++++-------------------------------- 1 file changed, 16 insertions(+), 42 deletions(-) diff --git a/src/parser/index.ts b/src/parser/index.ts index 7ba1f502b0b..b557c9edd91 100644 --- a/src/parser/index.ts +++ b/src/parser/index.ts @@ -1,6 +1,5 @@ //@ts-nocheck import { fabric } from '../../HEADER'; -import { extend } from '../util'; import { clipPaths, cssRules, gradientDefs, svgInvalidAncestorsRegEx, svgValidParentsRegEx, svgValidTagNamesRegEx, svgViewBoxElementsRegEx } from "./constants"; import { getCSSRules } from './getCSSRules'; import { getGradientDefs } from './getGradientDefs'; @@ -15,45 +14,20 @@ import { parseSVGDocument } from './parseSVGDocument'; import { parseTransformAttribute } from "./parseTransformAttribute"; - - - fabric.svgValidTagNamesRegEx = svgValidTagNamesRegEx; - fabric.svgViewBoxElementsRegEx = svgViewBoxElementsRegEx; - fabric.svgInvalidAncestorsRegEx = svgInvalidAncestorsRegEx; - fabric.svgValidParentsRegEx = svgValidParentsRegEx; - - fabric.cssRules = cssRules; - fabric.gradientDefs = gradientDefs; - fabric.clipPaths = clipPaths; - - fabric.parseTransformAttribute = parseTransformAttribute; - - - fabric.parseSVGDocument = parseSVGDocument - - - - extend(fabric, { - - parseFontDeclaration, - - - getGradientDefs, - - - parseAttributes, - - parseElements, - - parseStyleAttribute, - - parsePointsAttribute, - - getCSSRules, - - - loadSVGFromURL, - - loadSVGFromString - }); +Object.assign(fabric, { + cssRules, + gradientDefs, + clipPaths, + parseTransformAttribute, + parseSVGDocument, + parseFontDeclaration, + getGradientDefs, + parseAttributes, + parseElements, + parseStyleAttribute, + parsePointsAttribute, + getCSSRules, + loadSVGFromURL, + loadSVGFromString +}); From f7f1ce07c4998ebdad71ca5c67e97c7c0c63abd4 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Thu, 4 Aug 2022 19:57:38 +0300 Subject: [PATCH 17/22] Update index.ts --- src/parser/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser/index.ts b/src/parser/index.ts index b557c9edd91..0cdfce61de2 100644 --- a/src/parser/index.ts +++ b/src/parser/index.ts @@ -1,6 +1,6 @@ //@ts-nocheck import { fabric } from '../../HEADER'; -import { clipPaths, cssRules, gradientDefs, svgInvalidAncestorsRegEx, svgValidParentsRegEx, svgValidTagNamesRegEx, svgViewBoxElementsRegEx } from "./constants"; +import { clipPaths, cssRules, gradientDefs } from "./constants"; import { getCSSRules } from './getCSSRules'; import { getGradientDefs } from './getGradientDefs'; import { loadSVGFromString } from './loadSVGFromString'; From 25223576332b61c545f80d35d3a2680ceb89a659 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Thu, 4 Aug 2022 19:59:22 +0300 Subject: [PATCH 18/22] lint --- src/parser/applyViewboxTransform.ts | 2 +- src/parser/doesSomeParentMatch.ts | 2 +- src/parser/elementById.ts | 4 ++-- src/parser/elementMatchesRule.ts | 2 +- src/parser/getCSSRules.ts | 8 ++++---- src/parser/getGlobalStylesForElement.ts | 6 +++--- src/parser/getGradientDefs.ts | 2 +- src/parser/getMultipleNodes.ts | 2 +- src/parser/loadSVGFromString.ts | 2 +- src/parser/loadSVGFromURL.ts | 2 +- src/parser/normalizeValue.ts | 6 +++--- src/parser/parseAttributes.ts | 12 ++++++------ src/parser/parseFontDeclaration.ts | 4 ++-- src/parser/parsePointsAttribute.ts | 2 +- src/parser/parseSVGDocument.ts | 8 ++++---- src/parser/parseStyleAttribute.ts | 2 +- src/parser/parseStyleObject.ts | 4 ++-- src/parser/parseStyleString.ts | 4 ++-- src/parser/parseTransformAttribute.ts | 8 ++++---- src/parser/parseUseDirectives.ts | 6 +++--- src/parser/rotateMatrix.ts | 2 +- src/parser/scaleMatrix.ts | 2 +- src/parser/selectorMatches.ts | 2 +- src/parser/setStrokeFillOpacity.ts | 4 ++-- 24 files changed, 49 insertions(+), 49 deletions(-) diff --git a/src/parser/applyViewboxTransform.ts b/src/parser/applyViewboxTransform.ts index 8929af7ab12..c485c20bd0a 100644 --- a/src/parser/applyViewboxTransform.ts +++ b/src/parser/applyViewboxTransform.ts @@ -11,7 +11,7 @@ export function applyViewboxTransform(element) { if (!svgViewBoxElementsRegEx.test(element.nodeName)) { return {}; } - var viewBoxAttr = element.getAttribute('viewBox'), scaleX = 1, scaleY = 1, minX = 0, minY = 0, viewBoxWidth, viewBoxHeight, matrix, el, widthAttr = element.getAttribute('width'), heightAttr = element.getAttribute('height'), x = element.getAttribute('x') || 0, y = element.getAttribute('y') || 0, preserveAspectRatio = element.getAttribute('preserveAspectRatio') || '', missingViewBox = (!viewBoxAttr || !(viewBoxAttr = viewBoxAttr.match(reViewBoxAttrValue))), missingDimAttr = (!widthAttr || !heightAttr || widthAttr === '100%' || heightAttr === '100%'), toBeParsed = missingViewBox && missingDimAttr, parsedDim = {}, translateMatrix = '', widthDiff = 0, heightDiff = 0; + let viewBoxAttr = element.getAttribute('viewBox'), scaleX = 1, scaleY = 1, minX = 0, minY = 0, viewBoxWidth, viewBoxHeight, matrix, el, widthAttr = element.getAttribute('width'), heightAttr = element.getAttribute('height'), x = element.getAttribute('x') || 0, y = element.getAttribute('y') || 0, preserveAspectRatio = element.getAttribute('preserveAspectRatio') || '', missingViewBox = (!viewBoxAttr || !(viewBoxAttr = viewBoxAttr.match(reViewBoxAttrValue))), missingDimAttr = (!widthAttr || !heightAttr || widthAttr === '100%' || heightAttr === '100%'), toBeParsed = missingViewBox && missingDimAttr, parsedDim = {}, translateMatrix = '', widthDiff = 0, heightDiff = 0; parsedDim.width = 0; parsedDim.height = 0; diff --git a/src/parser/doesSomeParentMatch.ts b/src/parser/doesSomeParentMatch.ts index 3cff1efcb23..d4a22d4f82a 100644 --- a/src/parser/doesSomeParentMatch.ts +++ b/src/parser/doesSomeParentMatch.ts @@ -3,7 +3,7 @@ import { selectorMatches } from "./selectorMatches"; export function doesSomeParentMatch(element, selectors) { - var selector, parentMatching = true; + let selector, parentMatching = true; while (element.parentNode && element.parentNode.nodeType === 1 && selectors.length) { if (parentMatching) { selector = selectors.pop(); diff --git a/src/parser/elementById.ts b/src/parser/elementById.ts index 539618ccb12..cd505234f0b 100644 --- a/src/parser/elementById.ts +++ b/src/parser/elementById.ts @@ -5,12 +5,12 @@ * to support IE8 missing getElementById on SVGdocument and on node xmlDOM */ export function elementById(doc, id) { - var el; + let el; doc.getElementById && (el = doc.getElementById(id)); if (el) { return el; } - var node, i, len, nodelist = doc.getElementsByTagName('*'); + let node, i, len, nodelist = doc.getElementsByTagName('*'); for (i = 0, len = nodelist.length; i < len; i++) { node = nodelist[i]; if (id === node.getAttribute('id')) { diff --git a/src/parser/elementMatchesRule.ts b/src/parser/elementMatchesRule.ts index 8d8f25479ba..a925d50091a 100644 --- a/src/parser/elementMatchesRule.ts +++ b/src/parser/elementMatchesRule.ts @@ -8,7 +8,7 @@ import { doesSomeParentMatch } from "./doesSomeParentMatch"; */ export function elementMatchesRule(element, selectors) { - var firstMatching, parentMatching = true; + let firstMatching, parentMatching = true; //start from rightmost selector. firstMatching = selectorMatches(element, selectors.pop()); if (firstMatching && selectors.length) { diff --git a/src/parser/getCSSRules.ts b/src/parser/getCSSRules.ts index cae4882ea6d..9ccf1355531 100644 --- a/src/parser/getCSSRules.ts +++ b/src/parser/getCSSRules.ts @@ -7,11 +7,11 @@ * @return {Object} CSS rules of this document */ export function getCSSRules(doc) { - var styles = doc.getElementsByTagName('style'), i, len, allRules = {}, rules; + let styles = doc.getElementsByTagName('style'), i, len, allRules = {}, rules; // very crude parsing of style contents for (i = 0, len = styles.length; i < len; i++) { - var styleContents = styles[i].textContent; + let styleContents = styles[i].textContent; // remove comments styleContents = styleContents.replace(/\/\*[\s\S]*?\*\//g, ''); @@ -27,10 +27,10 @@ export function getCSSRules(doc) { // eslint-disable-next-line no-loop-func rules.forEach(function (rule) { - var match = rule.split('{'), ruleObj = {}, declaration = match[1].trim(), propertyValuePairs = declaration.split(';').filter(function (pair) { return pair.trim(); }); + const match = rule.split('{'), ruleObj = {}, declaration = match[1].trim(), propertyValuePairs = declaration.split(';').filter(function (pair) { return pair.trim(); }); for (i = 0, len = propertyValuePairs.length; i < len; i++) { - var pair = propertyValuePairs[i].split(':'), property = pair[0].trim(), value = pair[1].trim(); + const pair = propertyValuePairs[i].split(':'), property = pair[0].trim(), value = pair[1].trim(); ruleObj[property] = value; } rule = match[0].trim(); diff --git a/src/parser/getGlobalStylesForElement.ts b/src/parser/getGlobalStylesForElement.ts index f6c794fd435..0cf1bd1fdc1 100644 --- a/src/parser/getGlobalStylesForElement.ts +++ b/src/parser/getGlobalStylesForElement.ts @@ -7,10 +7,10 @@ import { elementMatchesRule } from "./elementMatchesRule"; */ export function getGlobalStylesForElement(element, svgUid) { - var styles = {}; - for (var rule in cssRules[svgUid]) { + const styles = {}; + for (const rule in cssRules[svgUid]) { if (elementMatchesRule(element, rule.split(' '))) { - for (var property in cssRules[svgUid][rule]) { + for (const property in cssRules[svgUid][rule]) { styles[property] = cssRules[svgUid][rule][property]; } } diff --git a/src/parser/getGradientDefs.ts b/src/parser/getGradientDefs.ts index ea6faf1d0b9..afb7339417e 100644 --- a/src/parser/getGradientDefs.ts +++ b/src/parser/getGradientDefs.ts @@ -16,7 +16,7 @@ const tagArray = [ * @return {Object} Gradient definitions; key corresponds to element id, value -- to gradient definition element */ export function getGradientDefs(doc) { - var elList = getMultipleNodes(doc, tagArray), el, j = 0, gradientDefs = {}; + let elList = getMultipleNodes(doc, tagArray), el, j = 0, gradientDefs = {}; j = elList.length; while (j--) { el = elList[j]; diff --git a/src/parser/getMultipleNodes.ts b/src/parser/getMultipleNodes.ts index d1ae48d9f0a..852e22292c4 100644 --- a/src/parser/getMultipleNodes.ts +++ b/src/parser/getMultipleNodes.ts @@ -1,7 +1,7 @@ //@ts-nocheck export function getMultipleNodes(doc, nodeNames) { - var nodeName, nodeArray = [], nodeList, i, len; + let nodeName, nodeArray = [], nodeList, i, len; for (i = 0, len = nodeNames.length; i < len; i++) { nodeName = nodeNames[i]; nodeList = doc.getElementsByTagName(nodeName); diff --git a/src/parser/loadSVGFromString.ts b/src/parser/loadSVGFromString.ts index 01da72557a4..977da0f0874 100644 --- a/src/parser/loadSVGFromString.ts +++ b/src/parser/loadSVGFromString.ts @@ -13,7 +13,7 @@ import { parseSVGDocument } from "./parseSVGDocument"; * @param {AbortSignal} [options.signal] handle aborting, see https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal */ export function loadSVGFromString(string, callback, reviver, options) { - var parser = new fabric.window.DOMParser(), doc = parser.parseFromString(string.trim(), 'text/xml'); + const parser = new fabric.window.DOMParser(), doc = parser.parseFromString(string.trim(), 'text/xml'); parseSVGDocument(doc.documentElement, function (results, _options, elements, allElements) { callback(results, _options, elements, allElements); }, reviver, options); diff --git a/src/parser/loadSVGFromURL.ts b/src/parser/loadSVGFromURL.ts index 666dbad6440..5dcc45b1bae 100644 --- a/src/parser/loadSVGFromURL.ts +++ b/src/parser/loadSVGFromURL.ts @@ -24,7 +24,7 @@ export function loadSVGFromURL(url, callback, reviver, options) { function onComplete(r) { - var xml = r.responseXML; + const xml = r.responseXML; if (!xml || !xml.documentElement) { callback && callback(null); return false; diff --git a/src/parser/normalizeValue.ts b/src/parser/normalizeValue.ts index b0591edbe41..0291630b9ee 100644 --- a/src/parser/normalizeValue.ts +++ b/src/parser/normalizeValue.ts @@ -4,7 +4,7 @@ import { parseTransformAttribute } from "./parseTransformAttribute"; export function normalizeValue(attr, value, parentAttributes, fontSize) { - var isArray = Array.isArray(value), parsed; + let isArray = Array.isArray(value), parsed; if ((attr === 'fill' || attr === 'stroke') && value === 'none') { value = ''; @@ -50,8 +50,8 @@ export function normalizeValue(attr, value, parentAttributes, fontSize) { parsed = parseUnit(value, fontSize) / fontSize * 1000; } else if (attr === 'paintFirst') { - var fillIndex = value.indexOf('fill'); - var strokeIndex = value.indexOf('stroke'); + const fillIndex = value.indexOf('fill'); + const strokeIndex = value.indexOf('stroke'); var value = 'fill'; if (fillIndex > -1 && strokeIndex > -1 && strokeIndex < fillIndex) { value = 'stroke'; diff --git a/src/parser/parseAttributes.ts b/src/parser/parseAttributes.ts index 5e58a79c41f..08166afdd4a 100644 --- a/src/parser/parseAttributes.ts +++ b/src/parser/parseAttributes.ts @@ -22,7 +22,7 @@ export function parseAttributes(element, attributes, svgUid) { return; } - var value, parentAttributes = {}, fontSize, parentFontSize; + let value, parentAttributes = {}, fontSize, parentFontSize; if (typeof svgUid === 'undefined') { svgUid = element.getAttribute('svgUid'); @@ -32,7 +32,7 @@ export function parseAttributes(element, attributes, svgUid) { parentAttributes = parseAttributes(element.parentNode, attributes, svgUid); } - var ownAttributes = attributes.reduce(function (memo, attr) { + let ownAttributes = attributes.reduce(function (memo, attr) { value = element.getAttribute(attr); if (value) { // eslint-disable-line memo[attr] = value; @@ -41,7 +41,7 @@ export function parseAttributes(element, attributes, svgUid) { }, {}); // add values parsed from style, which take precedence over attributes // (see: http://www.w3.org/TR/SVG/styling.html#UsingPresentationAttributes) - var cssAttrs = Object.assign( + const cssAttrs = Object.assign( getGlobalStylesForElement(element, svgUid), parseStyleAttribute(element) ); @@ -58,8 +58,8 @@ export function parseAttributes(element, attributes, svgUid) { ownAttributes[fSize] = fontSize = parseUnit(ownAttributes[fSize], parentFontSize); } - var normalizedAttr, normalizedValue, normalizedStyle = {}; - for (var attr in ownAttributes) { + let normalizedAttr, normalizedValue, normalizedStyle = {}; + for (const attr in ownAttributes) { normalizedAttr = normalizeAttr(attr); normalizedValue = normalizeValue(normalizedAttr, ownAttributes[attr], parentAttributes, fontSize); normalizedStyle[normalizedAttr] = normalizedValue; @@ -67,6 +67,6 @@ export function parseAttributes(element, attributes, svgUid) { if (normalizedStyle && normalizedStyle.font) { parseFontDeclaration(normalizedStyle.font, normalizedStyle); } - var mergedAttrs = Object.assign(parentAttributes, normalizedStyle); + const mergedAttrs = Object.assign(parentAttributes, normalizedStyle); return svgValidParentsRegEx.test(element.nodeName) ? mergedAttrs : setStrokeFillOpacity(mergedAttrs); } diff --git a/src/parser/parseFontDeclaration.ts b/src/parser/parseFontDeclaration.ts index ccc68e577c1..c86c04cbbca 100644 --- a/src/parser/parseFontDeclaration.ts +++ b/src/parser/parseFontDeclaration.ts @@ -11,12 +11,12 @@ import { reFontDeclaration } from './constants'; * @param {Object} oStyle definition */ export function parseFontDeclaration(value, oStyle) { - var match = value.match(reFontDeclaration); + const match = value.match(reFontDeclaration); if (!match) { return; } - var fontStyle = match[1], + const fontStyle = match[1], // font variant is not used // fontVariant = match[2], fontWeight = match[3], fontSize = match[4], lineHeight = match[5], fontFamily = match[6]; diff --git a/src/parser/parsePointsAttribute.ts b/src/parser/parsePointsAttribute.ts index 280ce60dc72..00024210045 100644 --- a/src/parser/parsePointsAttribute.ts +++ b/src/parser/parsePointsAttribute.ts @@ -19,7 +19,7 @@ export function parsePointsAttribute(points) { points = points.replace(/,/g, ' ').trim(); points = points.split(/\s+/); - var parsedPoints = [], i, len; + let parsedPoints = [], i, len; for (i = 0, len = points.length; i < len; i += 2) { parsedPoints.push({ diff --git a/src/parser/parseSVGDocument.ts b/src/parser/parseSVGDocument.ts index 91993fd7687..25f2cb8459c 100644 --- a/src/parser/parseSVGDocument.ts +++ b/src/parser/parseSVGDocument.ts @@ -32,7 +32,7 @@ export function parseSVGDocument(doc, callback, reviver, parsingOptions) { } parseUseDirectives(doc); - var svgUid = fabric.Object.__uid++, i, len, options = applyViewboxTransform(doc), descendants = toArray(doc.getElementsByTagName('*')); + let svgUid = fabric.Object.__uid++, i, len, options = applyViewboxTransform(doc), descendants = toArray(doc.getElementsByTagName('*')); options.crossOrigin = parsingOptions && parsingOptions.crossOrigin; options.svgUid = svgUid; options.signal = parsingOptions && parsingOptions.signal; @@ -41,14 +41,14 @@ export function parseSVGDocument(doc, callback, reviver, parsingOptions) { // we're likely in node, where "o3-xml" library fails to gEBTN("*") // https://github.com/ajaxorg/node-o3-xml/issues/21 descendants = doc.selectNodes('//*[name(.)!="svg"]'); - var arr = []; + const arr = []; for (i = 0, len = descendants.length; i < len; i++) { arr[i] = descendants[i]; } descendants = arr; } - var elements = descendants.filter(function (el) { + const elements = descendants.filter(function (el) { applyViewboxTransform(el); return svgValidTagNamesRegEx.test(el.nodeName.replace('svg:', '')) && !hasAncestorWithNodeName(el, svgInvalidAncestorsRegEx); // http://www.w3.org/TR/SVG/struct.html#DefsElement @@ -61,7 +61,7 @@ export function parseSVGDocument(doc, callback, reviver, parsingOptions) { descendants.filter(function (el) { return el.nodeName.replace('svg:', '') === 'clipPath'; }).forEach(function (el) { - var id = el.getAttribute('id'); + const id = el.getAttribute('id'); localClipPaths[id] = toArray(el.getElementsByTagName('*')).filter(function (el) { return svgValidTagNamesRegEx.test(el.nodeName.replace('svg:', '')); }); diff --git a/src/parser/parseStyleAttribute.ts b/src/parser/parseStyleAttribute.ts index aae1b537183..3a3941d8cf5 100644 --- a/src/parser/parseStyleAttribute.ts +++ b/src/parser/parseStyleAttribute.ts @@ -10,7 +10,7 @@ import { parseStyleString } from "./parseStyleString"; * @return {Object} Objects with values parsed from style attribute of an element */ export function parseStyleAttribute(element) { - var oStyle = {}, style = element.getAttribute('style'); + const oStyle = {}, style = element.getAttribute('style'); if (!style) { return oStyle; diff --git a/src/parser/parseStyleObject.ts b/src/parser/parseStyleObject.ts index b504d87deaf..9bfd8ecc780 100644 --- a/src/parser/parseStyleObject.ts +++ b/src/parser/parseStyleObject.ts @@ -1,8 +1,8 @@ //@ts-nocheck export function parseStyleObject(style, oStyle) { - var attr, value; - for (var prop in style) { + let attr, value; + for (const prop in style) { if (typeof style[prop] === 'undefined') { continue; } diff --git a/src/parser/parseStyleString.ts b/src/parser/parseStyleString.ts index f0a3791a5ae..1bf259f4773 100644 --- a/src/parser/parseStyleString.ts +++ b/src/parser/parseStyleString.ts @@ -2,9 +2,9 @@ export function parseStyleString(style, oStyle) { - var attr, value; + let attr, value; style.replace(/;\s*$/, '').split(';').forEach(function (chunk) { - var pair = chunk.split(':'); + const pair = chunk.split(':'); attr = pair[0].trim().toLowerCase(); value = pair[1].trim(); diff --git a/src/parser/parseTransformAttribute.ts b/src/parser/parseTransformAttribute.ts index 7661967bd6d..5144e882198 100644 --- a/src/parser/parseTransformAttribute.ts +++ b/src/parser/parseTransformAttribute.ts @@ -8,7 +8,7 @@ import { skewMatrix } from './skewMatrix'; import { translateMatrix } from './translateMatrix'; // == begin transform regexp -let number = reNum, skewX = '(?:(skewX)\\s*\\(\\s*(' + number + ')\\s*\\))', skewY = '(?:(skewY)\\s*\\(\\s*(' + number + ')\\s*\\))', rotate = '(?:(rotate)\\s*\\(\\s*(' + number + ')(?:' + +const number = reNum, skewX = '(?:(skewX)\\s*\\(\\s*(' + number + ')\\s*\\))', skewY = '(?:(skewY)\\s*\\(\\s*(' + number + ')\\s*\\))', rotate = '(?:(rotate)\\s*\\(\\s*(' + number + ')(?:' + commaWsp + '(' + number + ')' + commaWsp + '(' + number + '))?\\s*\\))', scale = '(?:(scale)\\s*\\(\\s*(' + number + ')(?:' + commaWsp + '(' + number + '))?\\s*\\))', translate = '(?:(translate)\\s*\\(\\s*(' + number + ')(?:' + @@ -43,7 +43,7 @@ let number = reNum, skewX = '(?:(skewX)\\s*\\(\\s*(' + number + ')\\s*\\))', ske export function parseTransformAttribute(attributeValue) { // start with identity matrix - var matrix = iMatrix.concat(), matrices = []; + let matrix = iMatrix.concat(), matrices = []; // return if no argument was given or // an argument does not match transform attribute regexp @@ -53,7 +53,7 @@ export function parseTransformAttribute(attributeValue) { attributeValue.replace(reTransform, function (match) { - var m = new RegExp(transform).exec(match).filter(function (match) { + const m = new RegExp(transform).exec(match).filter(function (match) { // match !== '' && match != null return (!!match); }), operation = m[1], args = m.slice(2).map(parseFloat); @@ -86,7 +86,7 @@ export function parseTransformAttribute(attributeValue) { matrix = iMatrix.concat(); }); - var combinedMatrix = matrices[0]; + let combinedMatrix = matrices[0]; while (matrices.length > 1) { matrices.shift(); combinedMatrix = multiplyTransformMatrices(combinedMatrix, matrices[0]); diff --git a/src/parser/parseUseDirectives.ts b/src/parser/parseUseDirectives.ts index 9c1dea7d6d5..865de382354 100644 --- a/src/parser/parseUseDirectives.ts +++ b/src/parser/parseUseDirectives.ts @@ -7,9 +7,9 @@ import { applyViewboxTransform } from "./applyViewboxTransform"; export function parseUseDirectives(doc) { - var nodelist = getMultipleNodes(doc, ['use', 'svg:use']), i = 0; + let nodelist = getMultipleNodes(doc, ['use', 'svg:use']), i = 0; while (nodelist.length && i < nodelist.length) { - var el = nodelist[i], xlinkAttribute = el.getAttribute('xlink:href') || el.getAttribute('href'); + const el = nodelist[i], xlinkAttribute = el.getAttribute('xlink:href') || el.getAttribute('href'); if (xlinkAttribute === null) { return; @@ -19,7 +19,7 @@ export function parseUseDirectives(doc) { applyViewboxTransform(el2); if (/^svg$/i.test(el2.nodeName)) { - var el3 = el2.ownerDocument.createElementNS(namespace, 'g'); + const el3 = el2.ownerDocument.createElementNS(namespace, 'g'); for (j = 0, attrs = el2.attributes, len = attrs.length; j < len; j++) { attr = attrs.item(j); el3.setAttributeNS(namespace, attr.nodeName, attr.nodeValue); diff --git a/src/parser/rotateMatrix.ts b/src/parser/rotateMatrix.ts index 1cdf9a4be29..ef449413bf8 100644 --- a/src/parser/rotateMatrix.ts +++ b/src/parser/rotateMatrix.ts @@ -3,7 +3,7 @@ import { cos, sin } from '../util'; export function rotateMatrix(matrix, args) { - var cosValue = cos(args[0]), sinValue = sin(args[0]), x = 0, y = 0; + let cosValue = cos(args[0]), sinValue = sin(args[0]), x = 0, y = 0; if (args.length === 3) { x = args[1]; y = args[2]; diff --git a/src/parser/scaleMatrix.ts b/src/parser/scaleMatrix.ts index bab03a5e66d..3575592474d 100644 --- a/src/parser/scaleMatrix.ts +++ b/src/parser/scaleMatrix.ts @@ -1,7 +1,7 @@ //@ts-nocheck export function scaleMatrix(matrix, args) { - var multiplierX = args[0], multiplierY = (args.length === 2) ? args[1] : args[0]; + const multiplierX = args[0], multiplierY = (args.length === 2) ? args[1] : args[0]; matrix[0] = multiplierX; matrix[3] = multiplierY; diff --git a/src/parser/selectorMatches.ts b/src/parser/selectorMatches.ts index f9fe9ab129f..0be8bab0317 100644 --- a/src/parser/selectorMatches.ts +++ b/src/parser/selectorMatches.ts @@ -2,7 +2,7 @@ export function selectorMatches(element, selector) { - var nodeName = element.nodeName, classNames = element.getAttribute('class'), id = element.getAttribute('id'), matcher, i; + let nodeName = element.nodeName, classNames = element.getAttribute('class'), id = element.getAttribute('id'), matcher, i; // i check if a selector matches slicing away part from it. // if i get empty string i should match matcher = new RegExp('^' + nodeName, 'i'); diff --git a/src/parser/setStrokeFillOpacity.ts b/src/parser/setStrokeFillOpacity.ts index daee7f77ed3..2a1d3e742da 100644 --- a/src/parser/setStrokeFillOpacity.ts +++ b/src/parser/setStrokeFillOpacity.ts @@ -10,7 +10,7 @@ import { colorAttributes } from './constants'; */ export function setStrokeFillOpacity(attributes) { - for (var attr in colorAttributes) { + for (const attr in colorAttributes) { if (typeof attributes[colorAttributes[attr]] === 'undefined' || attributes[attr] === '') { continue; @@ -27,7 +27,7 @@ export function setStrokeFillOpacity(attributes) { continue; } - var color = new Color(attributes[attr]); + const color = new Color(attributes[attr]); attributes[attr] = color.setAlpha(toFixed(color.getAlpha() * attributes[colorAttributes[attr]], 2)).toRgba(); } return attributes; From 90a1b5f6b1ca917e3ac4ec9ef1034422c18a8630 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Thu, 4 Aug 2022 20:04:20 +0300 Subject: [PATCH 19/22] fix imports --- src/parser/loadSVGFromURL.ts | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/parser/loadSVGFromURL.ts b/src/parser/loadSVGFromURL.ts index 5dcc45b1bae..dcf762ac261 100644 --- a/src/parser/loadSVGFromURL.ts +++ b/src/parser/loadSVGFromURL.ts @@ -14,24 +14,17 @@ import { parseSVGDocument } from "./parseSVGDocument"; * @param {AbortSignal} [options.signal] handle aborting, see https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal */ export function loadSVGFromURL(url, callback, reviver, options) { - - url = url.replace(/^\n\s*/, '').trim(); - new request(url, { + return fetch(url.replace(/^\n\s*/, '').trim(), { method: 'get', onComplete: onComplete, signal: options && options.signal - }); - - function onComplete(r) { - - const xml = r.responseXML; + }).then(({ responseXML: xml }) => { if (!xml || !xml.documentElement) { callback && callback(null); return false; } - - parseSVGDocument(xml.documentElement, function (results, _options, elements, allElements) { - callback && callback(results, _options, elements, allElements); - }, reviver, options); - } + return parseSVGDocument(xml.documentElement, (results, _options, elements, allElements) => { + callback && callback(results, _options, elements, allElements); + }, reviver, options); + }); } From 53078e9fac23070f90a82d5fd22cb730921a45a4 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Thu, 4 Aug 2022 20:06:11 +0300 Subject: [PATCH 20/22] Revert "fix imports" This reverts commit 90a1b5f6b1ca917e3ac4ec9ef1034422c18a8630. --- src/parser/loadSVGFromURL.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/parser/loadSVGFromURL.ts b/src/parser/loadSVGFromURL.ts index dcf762ac261..5dcc45b1bae 100644 --- a/src/parser/loadSVGFromURL.ts +++ b/src/parser/loadSVGFromURL.ts @@ -14,17 +14,24 @@ import { parseSVGDocument } from "./parseSVGDocument"; * @param {AbortSignal} [options.signal] handle aborting, see https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal */ export function loadSVGFromURL(url, callback, reviver, options) { - return fetch(url.replace(/^\n\s*/, '').trim(), { + + url = url.replace(/^\n\s*/, '').trim(); + new request(url, { method: 'get', onComplete: onComplete, signal: options && options.signal - }).then(({ responseXML: xml }) => { + }); + + function onComplete(r) { + + const xml = r.responseXML; if (!xml || !xml.documentElement) { callback && callback(null); return false; } - return parseSVGDocument(xml.documentElement, (results, _options, elements, allElements) => { - callback && callback(results, _options, elements, allElements); - }, reviver, options); - }); + + parseSVGDocument(xml.documentElement, function (results, _options, elements, allElements) { + callback && callback(results, _options, elements, allElements); + }, reviver, options); + } } From 0d45bc78e4f58e7d46150c43c8f49754fa0d13ab Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Thu, 4 Aug 2022 20:06:32 +0300 Subject: [PATCH 21/22] Update loadSVGFromURL.ts --- src/parser/loadSVGFromURL.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parser/loadSVGFromURL.ts b/src/parser/loadSVGFromURL.ts index 5dcc45b1bae..4a7f2478b15 100644 --- a/src/parser/loadSVGFromURL.ts +++ b/src/parser/loadSVGFromURL.ts @@ -1,5 +1,6 @@ //@ts-nocheck +import { request } from "../util"; import { parseSVGDocument } from "./parseSVGDocument"; /** @@ -15,8 +16,7 @@ import { parseSVGDocument } from "./parseSVGDocument"; */ export function loadSVGFromURL(url, callback, reviver, options) { - url = url.replace(/^\n\s*/, '').trim(); - new request(url, { + new request(url.replace(/^\n\s*/, '').trim(), { method: 'get', onComplete: onComplete, signal: options && options.signal From 61811130f3eb4f289556d7674a4c4455d32eaa06 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Thu, 4 Aug 2022 20:13:01 +0300 Subject: [PATCH 22/22] Update HEADER.js --- HEADER.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/HEADER.js b/HEADER.js index 967d480f711..5863064eb3a 100644 --- a/HEADER.js +++ b/HEADER.js @@ -1,5 +1,7 @@ /*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */ +import { iMatrix } from './src/constants'; + var fabric = fabric || { version: '5.1.0' }; if (typeof exports !== 'undefined') { exports.fabric = fabric; @@ -74,7 +76,7 @@ fabric.SHARED_ATTRIBUTES = [ fabric.DPI = 96; fabric.reNonWord = /[ \n\.,;!\?\-]/; fabric.fontPaths = { }; -fabric.iMatrix = [1, 0, 0, 1, 0, 0]; +fabric.iMatrix = iMatrix; /**