update master to V7

This commit is contained in:
JFH
2021-05-09 19:29:45 +02:00
parent 41fc05672d
commit 593c415664
1000 changed files with 47537 additions and 54304 deletions

View File

@@ -6,29 +6,30 @@
* @copyright 2011 Jeff Schiller
*/
import {jsPDF} from 'jspdf/dist/jspdf.es.min.js';
import { jsPDF } from 'jspdf/dist/jspdf.es.min.js';
import 'svg2pdf.js/dist/svg2pdf.es.js';
import jQueryPluginSVG from '../common/jQuery.attr.js';
import jQueryPluginSVG from './jQuery.attr.js';
import * as hstry from './history.js';
import {
text2xml, cleanupElement, findDefs, getHref, preventClickDefault,
toXml, getStrokedBBoxDefaultVisible, encode64, createObjectURL,
dataURLToObjectURL, walkTree, getBBox as utilsGetBBox
} from '../common/utilities.js';
} from './utilities.js';
import {
transformPoint, transformListToTransform
} from '../common/math.js';
import {resetListMap} from '../common/svgtransformlist.js';
} from './math.js';
import { resetListMap } from './svgtransformlist.js';
import {
convertUnit, shortFloat, convertToNum
} from '../common/units.js';
import {isGecko, isChrome, isWebkit} from '../common/browser.js';
import { isGecko, isChrome, isWebkit } from '../common/browser.js';
import * as pathModule from './path.js';
import {NS} from '../common/namespaces.js';
import { NS } from '../common/namespaces.js';
import * as draw from './draw.js';
import {
recalculateDimensions
} from './recalculate.js';
import { getParents, getClosest } from '../editor/components/jgraduate/Util.js';
const {
InsertElementCommand, RemoveElementCommand,
@@ -38,6 +39,7 @@ const {
const $ = jQueryPluginSVG(jQuery);
let svgContext_ = null;
let $id = null;
/**
* @function module:svg-exec.init
@@ -46,6 +48,8 @@ let svgContext_ = null;
*/
export const init = function (svgContext) {
svgContext_ = svgContext;
const svgCanvas = svgContext_.getCanvas();
$id = svgCanvas.$id;
};
/**
@@ -55,12 +59,13 @@ export const init = function (svgContext) {
*/
export const svgCanvasToString = function () {
// keep calling it until there are none to remove
while (svgContext_.getCanvas().removeUnusedDefElems() > 0) {} // eslint-disable-line no-empty
while (svgContext_.getCanvas().removeUnusedDefElems() > 0) { } // eslint-disable-line no-empty
svgContext_.getCanvas().pathActions.clear(true);
// Keep SVG-Edit comment on top
$.each(svgContext_.getSVGContent().childNodes, function (i, node) {
const childNodesElems = svgContext_.getSVGContent().childNodes;
childNodesElems.forEach(function (node, i) {
if (i && node.nodeType === 8 && node.data.includes('Created with')) {
svgContext_.getSVGContent().firstChild.before(node);
}
@@ -75,8 +80,9 @@ export const svgCanvasToString = function () {
const nakedSvgs = [];
// Unwrap gsvg if it has no special attributes (only id and style)
$(svgContext_.getSVGContent()).find('g:data(gsvg)').each(function () {
const attrs = this.attributes;
const gsvgElems = svgContext_.getSVGContent().querySelectorAll('g[data-gsvg]');
Array.prototype.forEach.call(gsvgElems, function (element) {
const attrs = element.attributes;
let len = attrs.length;
for (let i = 0; i < len; i++) {
if (attrs[i].nodeName === 'id' || attrs[i].nodeName === 'style') {
@@ -85,17 +91,17 @@ export const svgCanvasToString = function () {
}
// No significant attributes, so ungroup
if (len <= 0) {
const svg = this.firstChild;
const svg = element.firstChild;
nakedSvgs.push(svg);
$(this).replaceWith(svg);
element.replaceWith(svg);
}
});
const output = this.svgToString(svgContext_.getSVGContent(), 0);
// Rewrap gsvg
if (nakedSvgs.length) {
$(nakedSvgs).each(function () {
svgContext_.getCanvas().groupSvgElem(this);
Array.prototype.forEach.call(nakedSvgs, function (el) {
svgContext_.getCanvas().groupSvgElem(el);
});
}
@@ -153,22 +159,26 @@ export const svgToString = function (elem, indent) {
const nsuris = {};
// Check elements for namespaces, add if found
$(elem).find('*').andSelf().each(function () {
const csElements = elem.querySelectorAll('*');
const cElements = Array.prototype.slice.call(csElements);
cElements.push(elem);
Array.prototype.forEach.call(cElements, function (el) {
// const el = this;
// for some elements have no attribute
const uri = this.namespaceURI;
const uri = el.namespaceURI;
if (uri && !nsuris[uri] && nsMap[uri] && nsMap[uri] !== 'xmlns' && nsMap[uri] !== 'xml') {
nsuris[uri] = true;
out.push(' xmlns:' + nsMap[uri] + '="' + uri + '"');
}
$.each(this.attributes, function (i, attr) {
const u = attr.namespaceURI;
if (u && !nsuris[u] && nsMap[u] !== 'xmlns' && nsMap[u] !== 'xml') {
nsuris[u] = true;
out.push(' xmlns:' + nsMap[u] + '="' + u + '"');
if (el.attributes.length > 0) {
for (const [, attr] of Object.entries(el.attributes)) {
const u = attr.namespaceURI;
if (u && !nsuris[u] && nsMap[u] !== 'xmlns' && nsMap[u] !== 'xml') {
nsuris[u] = true;
out.push(' xmlns:' + nsMap[u] + '="' + u + '"');
}
}
});
}
});
let i = attrs.length;
@@ -181,12 +191,10 @@ export const svgToString = function (elem, indent) {
if (attr.nodeName.startsWith('xmlns:')) { continue; }
// only serialize attributes we don't use internally
if (attrVal !== '' && !attrNames.includes(attr.localName)) {
if (!attr.namespaceURI || nsMap[attr.namespaceURI]) {
out.push(' ');
out.push(attr.nodeName); out.push('="');
out.push(attrVal); out.push('"');
}
if (attrVal !== '' && !attrNames.includes(attr.localName) && (!attr.namespaceURI || nsMap[attr.namespaceURI])) {
out.push(' ');
out.push(attr.nodeName); out.push('="');
out.push(attrVal); out.push('"');
}
}
} else {
@@ -216,10 +224,10 @@ export const svgToString = function (elem, indent) {
// Embed images when saving
if (svgContext_.getSvgOptionApply() &&
elem.nodeName === 'image' &&
attr.localName === 'href' &&
svgContext_.getSvgOptionImages() &&
svgContext_.getSvgOptionImages() === 'embed'
elem.nodeName === 'image' &&
attr.localName === 'href' &&
svgContext_.getSvgOptionImages() &&
svgContext_.getSvgOptionImages() === 'embed'
) {
const img = svgContext_.getEncodableImages(attrVal);
if (img) { attrVal = img; }
@@ -243,31 +251,31 @@ export const svgToString = function (elem, indent) {
for (let i = 0; i < childs.length; i++) {
const child = childs.item(i);
switch (child.nodeType) {
case 1: // element node
out.push('\n');
out.push(this.svgToString(child, indent));
break;
case 3: { // text node
const str = child.nodeValue.replace(/^\s+|\s+$/g, '');
if (str !== '') {
bOneLine = true;
out.push(String(toXml(str)));
}
break;
} case 4: // cdata node
out.push('\n');
out.push(new Array(indent + 1).join(' '));
out.push('<![CDATA[');
out.push(child.nodeValue);
out.push(']]>');
break;
case 8: // comment
out.push('\n');
out.push(new Array(indent + 1).join(' '));
out.push('<!--');
out.push(child.data);
out.push('-->');
break;
case 1: // element node
out.push('\n');
out.push(this.svgToString(child, indent));
break;
case 3: { // text node
const str = child.nodeValue.replace(/^\s+|\s+$/g, '');
if (str !== '') {
bOneLine = true;
out.push(String(toXml(str)));
}
break;
} case 4: // cdata node
out.push('\n');
out.push(new Array(indent + 1).join(' '));
out.push('<![CDATA[');
out.push(child.nodeValue);
out.push(']]>');
break;
case 8: // comment
out.push('\n');
out.push(new Array(indent + 1).join(' '));
out.push('<!--');
out.push(child.data);
out.push('-->');
break;
} // switch on node type
}
indent--;
@@ -298,11 +306,12 @@ export const svgToString = function (elem, indent) {
*/
export const setSvgString = function (xmlString, preventUndo) {
const curConfig = svgContext_.getCurConfig();
const dataStorage = svgContext_.getDataStorage();
try {
// convert string into XML document
const newDoc = text2xml(xmlString);
if (newDoc.firstElementChild &&
newDoc.firstElementChild.namespaceURI !== NS.SVG) {
newDoc.firstElementChild.namespaceURI !== NS.SVG) {
return false;
}
@@ -311,7 +320,7 @@ export const setSvgString = function (xmlString, preventUndo) {
const batchCmd = new BatchCommand('Change Source');
// remove old svg document
const {nextSibling} = svgContext_.getSVGContent();
const { nextSibling } = svgContext_.getSVGContent();
svgContext_.getSVGContent().remove();
const oldzoom = svgContext_.getSVGContent();
@@ -326,7 +335,7 @@ export const setSvgString = function (xmlString, preventUndo) {
}
svgContext_.getSVGRoot().append(svgContext_.getSVGContent());
const content = $(svgContext_.getSVGContent());
const content = svgContext_.getSVGContent();
svgContext_.getCanvas().current_drawing_ = new draw.Drawing(svgContext_.getSVGContent(), svgContext_.getIdPrefix());
@@ -339,8 +348,8 @@ export const setSvgString = function (xmlString, preventUndo) {
}
// change image href vals if possible
content.find('image').each(function () {
const image = this;
const elements = content.querySelectorAll('image');
Array.prototype.forEach.call(elements, function (image) {
preventClickDefault(image);
const val = svgContext_.getCanvas().getHref(this);
if (val) {
@@ -351,9 +360,11 @@ export const setSvgString = function (xmlString, preventUndo) {
if (m) {
const url = decodeURIComponent(m[1]);
// const url = decodeURIComponent(m.groups.url);
$(new Image()).load(function () {
const iimg = new Image();
iimg.addEventListener("load", () => {
image.setAttributeNS(NS.XLINK, 'xlink:href', url);
}).attr('src', url);
});
iimg.src = url;
}
}
// Add to encodableImages if it loads
@@ -362,25 +373,30 @@ export const setSvgString = function (xmlString, preventUndo) {
});
// Wrap child SVGs in group elements
content.find('svg').each(function () {
const svgElements = content.querySelectorAll('svg');
Array.prototype.forEach.call(svgElements, function (element) {
// Skip if it's in a <defs>
if ($(this).closest('defs').length) { return; }
if (getClosest(element.parentNode, 'defs')) { return; }
svgContext_.getCanvas().uniquifyElems(this);
svgContext_.getCanvas().uniquifyElems(element);
// Check if it already has a gsvg group
const pa = this.parentNode;
const pa = element.parentNode;
if (pa.childNodes.length === 1 && pa.nodeName === 'g') {
$(pa).data('gsvg', this);
dataStorage.put(pa, 'gsvg', element);
pa.id = pa.id || svgContext_.getCanvas().getNextId();
} else {
svgContext_.getCanvas().groupSvgElem(this);
svgContext_.getCanvas().groupSvgElem(element);
}
});
// For Firefox: Put all paint elems in defs
if (isGecko()) {
content.find('linearGradient, radialGradient, pattern').appendTo(findDefs());
const svgDefs = findDefs();
const findElems = content.querySelectorAll('linearGradient, radialGradient, pattern');
Array.prototype.forEach.call(findElems, function (ele) {
svgDefs.appendChild(ele);
});
}
// Set ref element for <use> elements
@@ -388,7 +404,7 @@ export const setSvgString = function (xmlString, preventUndo) {
// TODO: This should also be done if the object is re-added through "redo"
svgContext_.getCanvas().setUseData(content);
svgContext_.getCanvas().convertGradients(content[0]);
svgContext_.getCanvas().convertGradients(content);
const attrs = {
id: 'svgcontent',
@@ -398,16 +414,16 @@ export const setSvgString = function (xmlString, preventUndo) {
let percs = false;
// determine proper size
if (content.attr('viewBox')) {
const vb = content.attr('viewBox').split(' ');
if (content.getAttribute('viewBox')) {
const viBox = content.getAttribute('viewBox');
const vb = viBox.split(' ');
attrs.width = vb[2];
attrs.height = vb[3];
// handle content that doesn't have a viewBox
} else {
$.each(['width', 'height'], function (i, dim) {
['width', 'height'].forEach(function (dim) {
// Set to 100 if not given
const val = content.attr(dim) || '100%';
const val = content.getAttribute(dim) || '100%';
if (String(val).substr(-1) === '%') {
// Use user units if percentage given
percs = true;
@@ -421,8 +437,12 @@ export const setSvgString = function (xmlString, preventUndo) {
draw.identifyLayers();
// Give ID for any visible layer children missing one
content.children().find(svgContext_.getVisElems()).each(function () {
if (!this.id) { this.id = svgContext_.getCanvas().getNextId(); }
const chiElems = content.children;
Array.prototype.forEach.call(chiElems, function (chiElem) {
const visElems = chiElem.querySelectorAll(svgContext_.getVisElems());
Array.prototype.forEach.call(visElems, function (elem) {
if (!elem.id) { elem.id = svgContext_.getCanvas().getNextId(); }
});
});
// Percentage width/height, so let's base it on visible elements
@@ -437,13 +457,17 @@ export const setSvgString = function (xmlString, preventUndo) {
if (attrs.width <= 0) { attrs.width = 100; }
if (attrs.height <= 0) { attrs.height = 100; }
content.attr(attrs);
for (const [key, value] of Object.entries(attrs)) {
content.setAttribute(key, value);
}
this.contentW = attrs.width;
this.contentH = attrs.height;
batchCmd.addSubCommand(new InsertElementCommand(svgContext_.getSVGContent()));
// update root to the correct size
const changes = content.attr(['width', 'height']);
const width = content.getAttribute('width');
const height = content.getAttribute('height');
const changes = { width: width, height: height };
batchCmd.addSubCommand(new ChangeElementCommand(svgContext_.getSVGRoot(), changes));
// reset zoom
@@ -458,7 +482,7 @@ export const setSvgString = function (xmlString, preventUndo) {
if (!preventUndo) svgContext_.addCommandToHistory(batchCmd);
svgContext_.call('changed', [svgContext_.getSVGContent()]);
} catch (e) {
console.log(e); // eslint-disable-line no-console
console.log(e);
return false;
}
@@ -480,6 +504,7 @@ export const setSvgString = function (xmlString, preventUndo) {
* was obtained
*/
export const importSvgString = function (xmlString) {
const dataStorage = svgContext_.getDataStorage();
let j, ts, useEl;
try {
// Get unique ID
@@ -487,8 +512,9 @@ export const importSvgString = function (xmlString) {
let useExisting = false;
// Look for symbol and make sure symbol exists in image
if (svgContext_.getImportIds(uid)) {
if ($(svgContext_.getImportIds(uid).symbol).parents('#svgroot').length) {
if (svgContext_.getImportIds(uid) && svgContext_.getImportIds(uid).symbol) {
const parents = getParents(svgContext_.getImportIds(uid).symbol, '#svgroot');
if (parents.length) {
useExisting = true;
}
}
@@ -496,7 +522,7 @@ export const importSvgString = function (xmlString) {
const batchCmd = new BatchCommand('Import Image');
let symbol;
if (useExisting) {
({symbol} = svgContext_.getImportIds());
({ symbol } = svgContext_.getImportIds());
ts = svgContext_.getImportIds(uid).xform;
} else {
// convert string into XML document
@@ -538,7 +564,10 @@ export const importSvgString = function (xmlString) {
// Move all gradients into root for Firefox, workaround for this bug:
// https://bugzilla.mozilla.org/show_bug.cgi?id=353575
// TODO: Make this properly undo-able.
$(svg).find('linearGradient, radialGradient, pattern').appendTo(defs);
const elements = svg.querySelectorAll('linearGradient, radialGradient, pattern');
Array.prototype.forEach.call(elements, function (el) {
defs.appendChild(el);
});
}
while (svg.firstChild) {
@@ -571,7 +600,8 @@ export const importSvgString = function (xmlString) {
useEl.setAttribute('transform', ts);
recalculateDimensions(useEl);
$(useEl).data('symbol', symbol).data('ref', symbol);
dataStorage.put(useEl, 'symbol', symbol);
dataStorage.put(useEl, 'ref', symbol);
svgContext_.getCanvas().addToSelection([useEl]);
// TODO: Find way to add this in a recalculateDimensions-parsable way
@@ -581,7 +611,7 @@ export const importSvgString = function (xmlString) {
svgContext_.addCommandToHistory(batchCmd);
svgContext_.call('changed', [svgContext_.getSVGContent()]);
} catch (e) {
console.log(e); // eslint-disable-line no-console
console.log(e);
return null;
}
@@ -602,20 +632,16 @@ export const importSvgString = function (xmlString) {
*/
export const embedImage = function (src) {
// Todo: Remove this Promise in favor of making an async/await `Image.load` utility
// eslint-disable-next-line promise/avoid-new
return new Promise(function (resolve, reject) {
// load in the image and once it's loaded, get the dimensions
$(new Image()).load(function (response, status, xhr) {
if (status === 'error') {
reject(new Error('Error loading image: ' + xhr.status + ' ' + xhr.statusText));
return;
}
const imgI = new Image();
imgI.addEventListener("load", (e) => {
// create a canvas the same size as the raster image
const cvs = document.createElement('canvas');
cvs.width = this.width;
cvs.height = this.height;
cvs.width = e.currentTarget.width;
cvs.height = e.currentTarget.height;
// load the raster image into the canvas
cvs.getContext('2d').drawImage(this, 0, 0);
cvs.getContext('2d').drawImage(e.currentTarget, 0, 0);
// retrieve the data: URL
try {
let urldata = ';svgedit_url=' + encodeURIComponent(src);
@@ -626,7 +652,11 @@ export const embedImage = function (src) {
}
svgContext_.getCanvas().setGoodImage(src);
resolve(svgContext_.getEncodableImages(src));
}).attr('src', src);
});
imgI.addEventListener("error", () => {
reject(new Error('Error loading image: '));
});
imgI.setAttribute('src', src);
});
};
@@ -660,7 +690,7 @@ export const save = function (opts) {
* Codes only is useful for locale-independent detection.
* @returns {module:svgcanvas.IssuesAndCodes}
*/
function getIssues () {
function getIssues() {
const uiStrings = svgContext_.getUIStrings();
// remove the selected outline before serializing
svgContext_.getCanvas().clearSelection();
@@ -675,20 +705,20 @@ function getIssues () {
foreignObject: uiStrings.exportNoforeignObject,
'[stroke-dasharray]': uiStrings.exportNoDashArray
};
const content = $(svgContext_.getSVGContent());
const content = svgContext_.getSVGContent();
// Add font/text check if Canvas Text API is not implemented
if (!('font' in $('<canvas>')[0].getContext('2d'))) {
if (!('font' in document.querySelector('CANVAS').getContext('2d'))) {
issueList.text = uiStrings.exportNoText;
}
$.each(issueList, function (sel, descr) {
if (content.find(sel).length) {
for (const [sel, descr] of Object.entries(issueList)) {
if (content.querySelectorAll(sel).length) {
issueCodes.push(sel);
issues.push(descr);
}
});
return {issues, issueCodes};
}
return { issues, issueCodes };
}
/**
* @typedef {PlainObject} module:svgcanvas.ImageExportedResults
@@ -720,20 +750,25 @@ function getIssues () {
export const rasterExport = async function (imgType, quality, exportWindowName, opts = {}) {
const type = imgType === 'ICO' ? 'BMP' : (imgType || 'PNG');
const mimeType = 'image/' + type.toLowerCase();
const {issues, issueCodes} = getIssues();
const { issues, issueCodes } = getIssues();
const svg = this.svgCanvasToString();
if (!$('#export_canvas').length) {
$('<canvas>', {id: 'export_canvas'}).hide().appendTo('body');
if (!$id('export_canvas')) {
const canvasEx = document.createElement('CANVAS');
canvasEx.id = 'export_canvas';
canvasEx.style.display = 'none';
document.body.appendChild(canvasEx);
}
const c = $('#export_canvas')[0];
c.width = svgContext_.getCanvas().contentW;
c.height = svgContext_.getCanvas().contentH;
const c = $id('export_canvas');
c.style.width = svgContext_.getCanvas().contentW + "px";
c.style.height = svgContext_.getCanvas().contentH + "px";
const canvg = svgContext_.getcanvg();
await canvg(c, svg);
const ctx = c.getContext('2d');
const v = canvg.fromString(ctx, svg);
// Render only first frame, ignoring animations.
await v.render();
// Todo: Make async/await utility in place of `toBlob`, so we can remove this constructor
// eslint-disable-next-line promise/avoid-new
return new Promise((resolve, reject) => {
return new Promise((resolve) => {
const dataURLType = type.toLowerCase();
const datauri = quality
? c.toDataURL('image/' + dataURLType, quality)
@@ -743,7 +778,7 @@ export const rasterExport = async function (imgType, quality, exportWindowName,
* Called when `bloburl` is available for export.
* @returns {void}
*/
function done () {
function done() {
const obj = {
datauri, bloburl, svg, issues, issueCodes, type: imgType,
mimeType, quality, exportWindowName
@@ -820,17 +855,17 @@ export const exportPDF = async (
keywords: '',
creator: '' */
});
const {issues, issueCodes} = getIssues();
const { issues, issueCodes } = getIssues();
// const svg = this.svgCanvasToString();
// await doc.addSvgAsImage(svg)
await doc.svg(svgContext_.getSVGContent(), {x: 0, y: 0, width: res.w, height: res.h});
await doc.svg(svgContext_.getSVGContent(), { x: 0, y: 0, width: res.w, height: res.h });
// doc.output('save'); // Works to open in a new
// window; todo: configure this and other export
// options to optionally work in this manner as
// opposed to opening a new tab
outputType = outputType || 'dataurlstring';
const obj = {issues, issueCodes, exportWindowName, outputType};
const obj = { issues, issueCodes, exportWindowName, outputType };
obj.output = doc.output(outputType, outputType === 'save' ? (exportWindowName || 'svg.pdf') : undefined);
svgContext_.call('exportedPDF', obj);
return obj;
@@ -861,14 +896,14 @@ export const uniquifyElemsMethod = function (g) {
// and we haven't tracked this ID yet
if (!(n.id in ids)) {
// add this id to our map
ids[n.id] = {elem: null, attrs: [], hrefs: []};
ids[n.id] = { elem: null, attrs: [], hrefs: [] };
}
ids[n.id].elem = n;
}
// now search for all attributes on this element that might refer
// to other elements
$.each(svgContext_.getrefAttrs(), function (i, attr) {
svgContext_.getrefAttrs().forEach(function(attr){
const attrnode = n.getAttributeNode(attr);
if (attrnode) {
// the incoming file has been sanitized, so we should be able to safely just strip off the leading #
@@ -877,7 +912,7 @@ export const uniquifyElemsMethod = function (g) {
if (refid) {
if (!(refid in ids)) {
// add this id to our map
ids[refid] = {elem: null, attrs: [], hrefs: []};
ids[refid] = { elem: null, attrs: [], hrefs: [] };
}
ids[refid].attrs.push(attrnode);
}
@@ -892,7 +927,7 @@ export const uniquifyElemsMethod = function (g) {
if (refid) {
if (!(refid in ids)) {
// add this id to our map
ids[refid] = {elem: null, attrs: [], hrefs: []};
ids[refid] = { elem: null, attrs: [], hrefs: [] };
}
ids[refid].hrefs.push(n);
}
@@ -903,7 +938,7 @@ export const uniquifyElemsMethod = function (g) {
// in ids, we now have a map of ids, elements and attributes, let's re-identify
for (const oldid in ids) {
if (!oldid) { continue; }
const {elem} = ids[oldid];
const { elem } = ids[oldid];
if (elem) {
const newid = svgContext_.getCanvas().getNextId();
@@ -911,7 +946,7 @@ export const uniquifyElemsMethod = function (g) {
elem.id = newid;
// remap all url() attributes
const {attrs} = ids[oldid];
const { attrs } = ids[oldid];
let j = attrs.length;
while (j--) {
const attr = attrs[j];
@@ -936,19 +971,22 @@ export const uniquifyElemsMethod = function (g) {
* @returns {void}
*/
export const setUseDataMethod = function (parent) {
let elems = $(parent);
let elems = parent;
if (parent.tagName !== 'use') {
elems = elems.find('use');
// elems = elems.find('use');
elems = elems.querySelectorAll('use');
}
elems.each(function () {
const id = svgContext_.getCanvas().getHref(this).substr(1);
Array.prototype.forEach.call(elems, function (el, _) {
const dataStorage = svgContext_.getDataStorage();
const id = svgContext_.getCanvas().getHref(el).substr(1);
const refElem = svgContext_.getCanvas().getElem(id);
if (!refElem) { return; }
$(this).data('ref', refElem);
dataStorage.put(el, 'ref', refElem);
if (refElem.tagName === 'symbol' || refElem.tagName === 'svg') {
$(this).data('symbol', refElem).data('ref', refElem);
dataStorage.put(el, 'symbol', refElem);
dataStorage.put(el, 'ref', refElem);
}
});
};
@@ -990,18 +1028,20 @@ export const removeUnusedDefElemsMethod = function () {
}
}
const defelems = $(defs).find('linearGradient, radialGradient, filter, marker, svg, symbol');
i = defelems.length;
while (i--) {
const defelem = defelems[i];
const {id} = defelem;
if (!defelemUses.includes(id)) {
// Not found, so remove (but remember)
svgContext_.setRemovedElements(id, defelem);
defelem.remove();
numRemoved++;
Array.prototype.forEach.call(defs, function (def, i) {
const defelems = def.querySelectorAll('linearGradient, radialGradient, filter, marker, svg, symbol');
i = defelems.length;
while (i--) {
const defelem = defelems[i];
const { id } = defelem;
if (!defelemUses.includes(id)) {
// Not found, so remove (but remember)
svgContext_.setRemovedElements(id, defelem);
defelem.remove();
numRemoved++;
}
}
}
});
return numRemoved;
};
@@ -1012,20 +1052,19 @@ export const removeUnusedDefElemsMethod = function () {
* @returns {void}
*/
export const convertGradientsMethod = function (elem) {
let elems = $(elem).find('linearGradient, radialGradient');
let elems = elem.querySelectorAll('linearGradient, radialGradient');
if (!elems.length && isWebkit()) {
// Bug in webkit prevents regular *Gradient selector search
elems = $(elem).find('*').filter(function () {
return (this.tagName.includes('Gradient'));
elems = Array.prototype.filter.call(elem.querySelectorAll('*'), function (curThis) {
return (curThis.tagName.includes('Gradient'));
});
}
elems.each(function () {
const grad = this;
if ($(grad).attr('gradientUnits') === 'userSpaceOnUse') {
Array.prototype.forEach.call(elems, function (grad) {
if (grad.getAttribute('gradientUnits') === 'userSpaceOnUse') {
const svgcontent = svgContext_.getSVGContent();
// TODO: Support more than one element with this ref by duplicating parent grad
const fillStrokeElems = $(svgcontent).find('[fill="url(#' + grad.id + ')"],[stroke="url(#' + grad.id + ')"]');
const fillStrokeElems = svgcontent.querySelectorAll('[fill="url(#' + grad.id + ')"],[stroke="url(#' + grad.id + ')"]');
if (!fillStrokeElems.length) { return; }
// get object's bounding box
@@ -1036,7 +1075,12 @@ export const convertGradientsMethod = function (elem) {
if (!bb) { return; }
if (grad.tagName === 'linearGradient') {
const gCoords = $(grad).attr(['x1', 'y1', 'x2', 'y2']);
const gCoords = {
x1: grad.getAttribute('x1'),
y1: grad.getAttribute('y1'),
x2: grad.getAttribute('x2'),
y2: grad.getAttribute('y2'),
};
// If has transform, convert
const tlist = grad.gradientTransform.baseVal;
@@ -1051,35 +1095,12 @@ export const convertGradientsMethod = function (elem) {
gCoords.y2 = pt2.y;
grad.removeAttribute('gradientTransform');
}
$(grad).attr({
x1: (gCoords.x1 - bb.x) / bb.width,
y1: (gCoords.y1 - bb.y) / bb.height,
x2: (gCoords.x2 - bb.x) / bb.width,
y2: (gCoords.y2 - bb.y) / bb.height
});
grad.setAttribute('x1', (gCoords.x1 - bb.x) / bb.width);
grad.setAttribute('y1', (gCoords.y1 - bb.y) / bb.height);
grad.setAttribute('x2', (gCoords.x2 - bb.x) / bb.width);
grad.setAttribute('y2', (gCoords.y2 - bb.y) / bb.height);
grad.removeAttribute('gradientUnits');
}
// else {
// Note: radialGradient elements cannot be easily converted
// because userSpaceOnUse will keep circular gradients, while
// objectBoundingBox will x/y scale the gradient according to
// its bbox.
//
// For now we'll do nothing, though we should probably have
// the gradient be updated as the element is moved, as
// inkscape/illustrator do.
//
// const gCoords = $(grad).attr(['cx', 'cy', 'r']);
//
// $(grad).attr({
// cx: (gCoords.cx - bb.x) / bb.width,
// cy: (gCoords.cy - bb.y) / bb.height,
// r: gCoords.r
// });
//
// grad.removeAttribute('gradientUnits');
// }
}
});
};