devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
785 lines (748 loc) • 29.9 kB
JavaScript
/**
* DevExtreme (cjs/exporter/image_creator.js)
* Version: 24.2.6
* Build date: Mon Mar 17 2025
*
* Copyright (c) 2012 - 2025 Developer Express Inc. ALL RIGHTS RESERVED
* Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
*/
;
exports.calcScaledInfo = calcScaledInfo;
exports.getData = getData;
exports.imageCreator = void 0;
exports.testFormats = testFormats;
var _renderer = _interopRequireDefault(require("../core/renderer"));
var _color = _interopRequireDefault(require("../color"));
var _type = require("../core/utils/type");
var _m_svg = _interopRequireDefault(require("../__internal/core/utils/m_svg"));
var _iterator = require("../core/utils/iterator");
var _extend = require("../core/utils/extend");
var _dom_adapter = _interopRequireDefault(require("../core/dom_adapter"));
var _dom = require("../core/utils/dom");
var _window = require("../core/utils/window");
var _inflector = require("../core/utils/inflector");
var _deferred = require("../core/utils/deferred");
function _interopRequireDefault(e) {
return e && e.__esModule ? e : {
default: e
}
}
const window = (0, _window.getWindow)();
const _math = Math;
const PI = _math.PI;
const _min = _math.min;
const _abs = _math.abs;
const _sqrt = _math.sqrt;
const _pow = _math.pow;
const _atan2 = _math.atan2;
const _cos = _math.cos;
const _sin = _math.sin;
const _number = Number;
const IMAGE_QUALITY = 1;
const TEXT_DECORATION_LINE_WIDTH_COEFF = .05;
const DEFAULT_FONT_SIZE = "10px";
const DEFAULT_FONT_FAMILY = "sans-serif";
const DEFAULT_TEXT_COLOR = "#000";
let parseAttributes;
function getStringFromCanvas(canvas, mimeType) {
const dataURL = canvas.toDataURL(mimeType, 1);
const imageData = window.atob(dataURL.substring(("data:" + mimeType + ";base64,").length));
return imageData
}
function arcTo(x1, y1, x2, y2, radius, largeArcFlag, clockwise, context) {
const cBx = (x1 + x2) / 2;
const cBy = (y1 + y2) / 2;
let aB = _atan2(y1 - y2, x1 - x2);
const k = largeArcFlag ? 1 : -1;
aB += PI / 180 * 90 * (clockwise ? 1 : -1);
const opSide = _sqrt(_pow(x2 - x1, 2) + _pow(y2 - y1, 2)) / 2;
const adjSide = _sqrt(_abs(_pow(radius, 2) - _pow(opSide, 2)));
const centerX = cBx + k * (adjSide * _cos(aB));
const centerY = cBy + k * (adjSide * _sin(aB));
const startAngle = _atan2(y1 - centerY, x1 - centerX);
const endAngle = _atan2(y2 - centerY, x2 - centerX);
context.arc(centerX, centerY, radius, startAngle, endAngle, !clockwise)
}
function getElementOptions(element, rootAppended) {
const attr = parseAttributes(element.attributes || {});
const options = (0, _extend.extend)({}, attr, {
text: element.textContent.replace(/\s+/g, " "),
textAlign: "middle" === attr["text-anchor"] ? "center" : attr["text-anchor"]
});
const transform = attr.transform;
let coords;
if (transform) {
coords = transform.match(/translate\(-*\d+([.]\d+)*(,*\s*-*\d+([.]\d+)*)*/);
if (coords) {
coords = coords[0].match(/-*\d+([.]\d+)*/g);
options.translateX = _number(coords[0]);
options.translateY = coords[1] ? _number(coords[1]) : 0
}
coords = transform.match(/rotate\(-*\d+([.]\d+)*(,*\s*-*\d+([.]\d+)*,*\s*-*\d+([.]\d+)*)*/);
if (coords) {
coords = coords[0].match(/-*\d+([.]\d+)*/g);
options.rotationAngle = _number(coords[0]);
options.rotationX = coords[1] && _number(coords[1]);
options.rotationY = coords[2] && _number(coords[2])
}
coords = transform.match(/scale\(-*\d+([.]\d+)*(,*\s*-*\d+([.]\d+)*)*/);
if (coords) {
coords = coords[0].match(/-*\d+([.]\d+)*/g);
options.scaleX = _number(coords[0]);
if (coords.length > 1) {
options.scaleY = _number(coords[1])
} else {
options.scaleY = options.scaleX
}
}
}
parseStyles(element, options, rootAppended);
return options
}
function drawRect(context, options) {
const x = options.x;
const y = options.y;
const width = options.width;
const height = options.height;
let cornerRadius = options.rx;
if (!cornerRadius) {
context.rect(x, y, width, height)
} else {
cornerRadius = _min(cornerRadius, width / 2, height / 2);
context.save();
context.translate(x, y);
context.moveTo(width / 2, 0);
context.arcTo(width, 0, width, height, cornerRadius);
context.arcTo(width, height, 0, height, cornerRadius);
context.arcTo(0, height, 0, 0, cornerRadius);
context.arcTo(0, 0, cornerRadius, 0, cornerRadius);
context.lineTo(width / 2, 0);
context.restore()
}
}
function drawImage(context, options, shared) {
const d = new _deferred.Deferred;
const image = new window.Image;
image.onload = function() {
context.save();
context.globalAlpha = options.globalAlpha;
transformElement(context, options);
clipElement(context, options, shared);
context.drawImage(image, options.x || 0, options.y || 0, options.width, options.height);
context.restore();
d.resolve()
};
image.onerror = function() {
d.resolve()
};
image.setAttribute("crossOrigin", "anonymous");
image.src = options.href || options["xlink:href"];
return d
}
function drawPath(context, dAttr) {
const dArray = dAttr.replace(/,/g, " ").split(/([A-Z])/i).filter((item => "" !== item.trim()));
let i = 0;
let params;
let prevParams;
let prevParamsLen;
do {
params = (dArray[i + 1] || "").trim().split(" ");
switch (dArray[i]) {
case "M":
context.moveTo(_number(params[0]), _number(params[1]));
i += 2;
break;
case "L":
for (let j = 0; j < params.length / 2; j++) {
context.lineTo(_number(params[2 * j]), _number(params[2 * j + 1]))
}
i += 2;
break;
case "C":
context.bezierCurveTo(_number(params[0]), _number(params[1]), _number(params[2]), _number(params[3]), _number(params[4]), _number(params[5]));
i += 2;
break;
case "a":
prevParams = dArray[i - 1].trim().split(" ");
prevParamsLen = prevParams.length - 1;
arcTo(_number(prevParams[prevParamsLen - 1]), _number(prevParams[prevParamsLen]), _number(prevParams[prevParamsLen - 1]) + _number(params[5]), _number(prevParams[prevParamsLen]) + _number(params[6]), _number(params[0]), _number(params[3]), _number(params[4]), context);
i += 2;
break;
case "A":
prevParams = dArray[i - 1].trim().split(" ");
prevParamsLen = prevParams.length - 1;
arcTo(_number(prevParams[prevParamsLen - 1]), _number(prevParams[prevParamsLen]), _number(params[5]), _number(params[6]), _number(params[0]), _number(params[3]), _number(params[4]), context);
i += 2;
break;
case "Z":
context.closePath();
i += 1;
break;
default:
i++
}
} while (i < dArray.length)
}
function parseStyles(element, options, rootAppended) {
let style = element.style || {};
let field;
for (field in style) {
if ("" !== style[field]) {
options[(0, _inflector.camelize)(field)] = style[field]
}
}
if (rootAppended && _dom_adapter.default.isElementNode(element)) {
style = window.getComputedStyle(element);
["fill", "stroke", "stroke-width", "font-family", "font-size", "font-style", "font-weight"].forEach((function(prop) {
if (prop in style && "" !== style[prop]) {
options[(0, _inflector.camelize)(prop)] = style[prop]
}
}));
["opacity", "fill-opacity", "stroke-opacity"].forEach((function(prop) {
if (prop in style && "" !== style[prop] && "1" !== style[prop]) {
options[prop] = _number(style[prop])
}
}))
}
options.textDecoration = options.textDecoration || options.textDecorationLine;
options.globalAlpha = (0, _type.isDefined)(options.opacity) ? options.opacity : options.globalAlpha
}
function parseUrl(urlString) {
const matches = urlString && urlString.match(/url\(.*#(.*?)["']?\)/i);
return matches && matches[1]
}
function setFontStyle(context, options) {
const fontParams = [];
options.fontSize = options.fontSize || "10px";
options.fontFamily = options.fontFamily || "sans-serif";
options.fill = options.fill || "#000";
options.fontStyle && fontParams.push(options.fontStyle);
options.fontWeight && fontParams.push(options.fontWeight);
fontParams.push(options.fontSize);
fontParams.push(options.fontFamily);
context.font = fontParams.join(" ");
context.textAlign = options.textAlign;
context.fillStyle = options.fill;
context.globalAlpha = options.globalAlpha
}
function drawText(context, options, shared) {
setFontStyle(context, options);
applyFilter(context, options, shared);
options.text && context.fillText(options.text, options.x || 0, options.y || 0);
strokeElement(context, options, true);
drawTextDecoration(context, options, shared)
}
function drawTextDecoration(context, options, shared) {
if (!options.textDecoration || "none" === options.textDecoration) {
return
}
const x = options.x;
const textWidth = context.measureText(options.text).width;
const textHeight = parseInt(options.fontSize, 10);
const lineHeight = .05 * textHeight < 1 ? 1 : .05 * textHeight;
let y = options.y;
switch (options.textDecoration) {
case "line-through":
y -= textHeight / 3 + lineHeight / 2;
break;
case "overline":
y -= textHeight - lineHeight;
break;
case "underline":
y += lineHeight
}
context.rect(x, y, textWidth, lineHeight);
fillElement(context, options, shared);
strokeElement(context, options)
}
function aggregateOpacity(options) {
options.strokeOpacity = void 0 !== options["stroke-opacity"] ? options["stroke-opacity"] : 1;
options.fillOpacity = void 0 !== options["fill-opacity"] ? options["fill-opacity"] : 1;
if (void 0 !== options.opacity) {
options.strokeOpacity *= options.opacity;
options.fillOpacity *= options.opacity
}
}
function hasTspan(element) {
const nodes = element.childNodes;
for (let i = 0; i < nodes.length; i++) {
if ("tspan" === nodes[i].tagName) {
return true
}
}
return false
}
function drawTextElement(childNodes, context, options, shared) {
const lines = [];
let line;
let offset = 0;
for (let i = 0; i < childNodes.length; i++) {
const element = childNodes[i];
if (void 0 === element.tagName) {
drawElement(element, context, options, shared)
} else if ("tspan" === element.tagName || "text" === element.tagName) {
const elementOptions = getElementOptions(element, shared.rootAppended);
const mergedOptions = (0, _extend.extend)({}, options, elementOptions);
if ("tspan" === element.tagName && hasTspan(element)) {
drawTextElement(element.childNodes, context, mergedOptions, shared);
continue
}
mergedOptions.textAlign = "start";
if (!line || void 0 !== elementOptions.x) {
line = {
elements: [],
options: [],
widths: [],
offsets: []
};
lines.push(line)
}
if (void 0 !== elementOptions.y) {
offset = 0
}
if (void 0 !== elementOptions.dy) {
offset += parseFloat(elementOptions.dy)
}
line.elements.push(element);
line.options.push(mergedOptions);
line.offsets.push(offset);
setFontStyle(context, mergedOptions);
line.widths.push(context.measureText(mergedOptions.text).width)
}
}
lines.forEach((function(line) {
const commonWidth = line.widths.reduce((function(commonWidth, width) {
return commonWidth + width
}), 0);
let xDiff = 0;
let currentOffset = 0;
if ("center" === options.textAlign) {
xDiff = commonWidth / 2
}
if ("end" === options.textAlign) {
xDiff = commonWidth
}
line.options.forEach((function(o, index) {
const width = line.widths[index];
o.x = o.x - xDiff + currentOffset;
o.y += line.offsets[index];
currentOffset += width
}));
line.elements.forEach((function(element, index) {
drawTextElement(element.childNodes, context, line.options[index], shared)
}))
}))
}
function drawElement(element, context, parentOptions, shared) {
const tagName = element.tagName;
const isText = "text" === tagName || "tspan" === tagName || void 0 === tagName;
const isImage = "image" === tagName;
const isComment = 8 === element.nodeType;
const options = (0, _extend.extend)({}, parentOptions, getElementOptions(element, shared.rootAppended));
if ("hidden" === options.visibility || options[_m_svg.default.HIDDEN_FOR_EXPORT] || isComment) {
return
}
context.save();
!isImage && transformElement(context, options);
clipElement(context, options, shared);
aggregateOpacity(options);
let promise;
context.beginPath();
switch (element.tagName) {
case void 0:
drawText(context, options, shared);
break;
case "text":
case "tspan":
drawTextElement(element.childNodes, context, options, shared);
break;
case "image":
promise = drawImage(context, options, shared);
break;
case "path":
drawPath(context, options.d);
break;
case "rect":
drawRect(context, options);
context.closePath();
break;
case "circle":
context.arc(options.cx, options.cy, options.r, 0, 2 * PI, 1)
}
if (!isText) {
applyFilter(context, options, shared);
if (!isImage) {
promise = fillElement(context, options, shared)
}
strokeElement(context, options)
}
applyGradient(context, options, shared, element, "linear");
applyGradient(context, options, shared, element, "radial");
context.restore();
return promise
}
function applyGradient(context, options, _ref, element, type) {
let {
linearGradients: linearGradients,
radialGradients: radialGradients
} = _ref;
const gradients = "linear" === type ? linearGradients : radialGradients;
if (0 === Object.keys(gradients).length) {
return
}
const id = parseUrl(options.fill);
if (id && gradients[id]) {
const box = element.getBBox();
const horizontalCenter = box.x + box.width / 2;
const verticalCenter = box.y + box.height / 2;
const maxRadius = Math.max(box.height / 2, box.width / 2);
const gradient = "linear" === type ? context.createLinearGradient(box.x, 0, box.x + box.width, 0) : context.createRadialGradient(horizontalCenter, verticalCenter, 0, horizontalCenter, verticalCenter, maxRadius);
gradients[id].colors.forEach((opt => {
const offset = parseInt(opt.offset.replace(/%/, ""));
gradient.addColorStop(offset / 100, opt.stopColor)
}));
if ("linear" === type) {
var _gradients$id$transfo;
const angle = ((null === (_gradients$id$transfo = gradients[id].transform) || void 0 === _gradients$id$transfo ? void 0 : _gradients$id$transfo.replace(/\D/g, "")) || 0) * Math.PI / 180;
context.translate(horizontalCenter, verticalCenter);
context.rotate(angle);
context.translate(-horizontalCenter, -verticalCenter)
}
context.globalAlpha = options.opacity;
context.fillStyle = gradient;
context.fill()
}
}
function applyFilter(context, options, shared) {
let filterOptions;
const id = parseUrl(options.filter);
if (id) {
filterOptions = shared.filters[id];
if (!filterOptions) {
filterOptions = {
offsetX: 0,
offsetY: 0,
blur: 0,
color: "#000"
}
}
context.shadowOffsetX = filterOptions.offsetX;
context.shadowOffsetY = filterOptions.offsetY;
context.shadowColor = filterOptions.color;
context.shadowBlur = filterOptions.blur
}
}
function transformElement(context, options) {
context.translate(options.translateX || 0, options.translateY || 0);
options.translateX = void 0;
options.translateY = void 0;
if (options.rotationAngle) {
context.translate(options.rotationX || 0, options.rotationY || 0);
context.rotate(options.rotationAngle * PI / 180);
context.translate(-(options.rotationX || 0), -(options.rotationY || 0));
options.rotationAngle = void 0;
options.rotationX = void 0;
options.rotationY = void 0
}
if (isFinite(options.scaleX)) {
context.scale(options.scaleX, options.scaleY);
options.scaleX = void 0;
options.scaleY = void 0
}
}
function clipElement(context, options, shared) {
if (options["clip-path"]) {
drawElement(shared.clipPaths[parseUrl(options["clip-path"])], context, {}, shared);
context.clip();
options["clip-path"] = void 0
}
}
function hex2rgba(hexColor, alpha) {
const color = new _color.default(hexColor);
return "rgba(" + color.r + "," + color.g + "," + color.b + "," + alpha + ")"
}
function createGradient(element) {
var _element$attributes$g;
const options = {
colors: [],
transform: null === (_element$attributes$g = element.attributes.gradientTransform) || void 0 === _element$attributes$g ? void 0 : _element$attributes$g.textContent
};
(0, _iterator.each)(element.childNodes, ((_, _ref2) => {
let {
attributes: attributes
} = _ref2;
options.colors.push({
offset: attributes.offset.value,
stopColor: attributes["stop-color"].value
})
}));
return options
}
function createFilter(element) {
let color;
let opacity;
const filterOptions = {};
(0, _iterator.each)(element.childNodes, (function(_, node) {
const attr = node.attributes;
if (!attr.result) {
return
}
switch (attr.result.value) {
case "gaussianBlurResult":
filterOptions.blur = _number(attr.stdDeviation.value);
break;
case "offsetResult":
filterOptions.offsetX = _number(attr.dx.value);
filterOptions.offsetY = _number(attr.dy.value);
break;
case "floodResult":
color = attr["flood-color"] ? attr["flood-color"].value : "#000";
opacity = attr["flood-opacity"] ? attr["flood-opacity"].value : 1;
filterOptions.color = hex2rgba(color, opacity)
}
}));
return filterOptions
}
function asyncEach(array, callback) {
let d = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : new _deferred.Deferred;
let i = 0;
for (; i < array.length; i++) {
const result = callback(array[i]);
if ((0, _type.isPromise)(result)) {
result.then((() => {
asyncEach(Array.prototype.slice.call(array, i + 1), callback, d)
}));
break
}
}
if (i === array.length) {
d.resolve()
}
return d
}
function drawCanvasElements(elements, context, parentOptions, shared) {
return asyncEach(elements, (function(element) {
switch (element.tagName && element.tagName.toLowerCase()) {
case "g":
case "svg": {
const options = (0, _extend.extend)({}, parentOptions, getElementOptions(element, shared.rootAppended));
context.save();
transformElement(context, options);
clipElement(context, options, shared);
const onDone = () => {
context.restore()
};
const promise = drawCanvasElements(element.childNodes, context, options, shared);
if ((0, _type.isPromise)(promise)) {
promise.then(onDone)
} else {
onDone()
}
return promise
}
case "defs":
return drawCanvasElements(element.childNodes, context, {}, shared);
case "clippath":
shared.clipPaths[element.attributes.id.textContent] = element.childNodes[0];
break;
case "pattern":
shared.patterns[element.attributes.id.textContent] = element;
break;
case "filter":
shared.filters[element.id] = createFilter(element);
break;
case "lineargradient":
shared.linearGradients[element.attributes.id.textContent] = createGradient(element);
break;
case "radialgradient":
shared.radialGradients[element.attributes.id.textContent] = createGradient(element);
break;
default:
return drawElement(element, context, parentOptions, shared)
}
}))
}
function setLineDash(context, options) {
let matches = options["stroke-dasharray"] && options["stroke-dasharray"].match(/(\d+)/g);
if (matches && matches.length) {
matches = (0, _iterator.map)(matches, (function(item) {
return _number(item)
}));
context.setLineDash(matches)
}
}
function strokeElement(context, options, isText) {
const stroke = options.stroke;
if (stroke && "none" !== stroke && 0 !== options["stroke-width"]) {
setLineDash(context, options);
context.lineJoin = options["stroke-linejoin"];
context.lineWidth = options["stroke-width"];
context.globalAlpha = options.strokeOpacity;
context.strokeStyle = stroke;
isText ? context.strokeText(options.text, options.x, options.y) : context.stroke();
context.globalAlpha = 1
}
}
function getPattern(context, pattern, shared, parentOptions) {
const options = getElementOptions(pattern, shared.rootAppended);
const patternCanvas = imageCreator._createCanvas(options.width, options.height, 0);
const patternContext = patternCanvas.getContext("2d");
const promise = drawCanvasElements(pattern.childNodes, patternContext, options, shared);
const onDone = () => {
context.fillStyle = context.createPattern(patternCanvas, "repeat");
context.globalAlpha = parentOptions.fillOpacity;
context.fill();
context.globalAlpha = 1
};
if ((0, _type.isPromise)(promise)) {
promise.then(onDone)
} else {
onDone()
}
return promise
}
function fillElement(context, options, shared) {
const fill = options.fill;
let promise;
if (fill && "none" !== fill) {
if (-1 === fill.search(/url/)) {
context.fillStyle = fill;
context.globalAlpha = options.fillOpacity;
context.fill();
context.globalAlpha = 1
} else {
const pattern = shared.patterns[parseUrl(fill)];
if (!pattern) {
return
}
promise = getPattern(context, pattern, shared, options)
}
}
return promise
}
parseAttributes = function(attributes) {
const newAttributes = {};
let attr;
(0, _iterator.each)(attributes, (function(index, item) {
attr = item.textContent;
if (isFinite(attr)) {
attr = _number(attr)
}
newAttributes[item.name.toLowerCase()] = attr
}));
return newAttributes
};
function drawBackground(context, width, height, backgroundColor, margin) {
context.fillStyle = backgroundColor || "#ffffff";
context.fillRect(-margin, -margin, width + 2 * margin, height + 2 * margin)
}
function createInvisibleDiv() {
const invisibleDiv = _dom_adapter.default.createElement("div");
invisibleDiv.style.left = "-9999px";
invisibleDiv.style.position = "absolute";
return invisibleDiv
}
function convertSvgToCanvas(svg, canvas, rootAppended) {
return drawCanvasElements(svg.childNodes, canvas.getContext("2d"), {}, {
clipPaths: {},
patterns: {},
filters: {},
linearGradients: {},
radialGradients: {},
rootAppended: rootAppended
})
}
function getCanvasFromSvg(markup, _ref3) {
let {
width: width,
height: height,
backgroundColor: backgroundColor,
margin: margin,
svgToCanvas: svgToCanvas = convertSvgToCanvas
} = _ref3;
const scaledScreenInfo = calcScaledInfo(width, height);
const canvas = imageCreator._createCanvas(scaledScreenInfo.width, scaledScreenInfo.height, margin);
const context = canvas.getContext("2d");
context.setTransform(scaledScreenInfo.pixelRatio, 0, 0, scaledScreenInfo.pixelRatio, 0, 0);
const svgElem = _m_svg.default.getSvgElement(markup);
let invisibleDiv;
const markupIsDomElement = _dom_adapter.default.isElementNode(markup) || (0, _type.isRenderer)(markup);
context.translate(margin, margin);
_dom_adapter.default.getBody().appendChild(canvas);
if (!markupIsDomElement) {
invisibleDiv = createInvisibleDiv();
invisibleDiv.appendChild(svgElem);
_dom_adapter.default.getBody().appendChild(invisibleDiv)
}
if (svgElem.attributes.direction) {
canvas.dir = svgElem.attributes.direction.textContent
}
drawBackground(context, width, height, backgroundColor, margin);
return (0, _deferred.fromPromise)(svgToCanvas(svgElem, canvas, markupIsDomElement && (0, _dom.contains)(_dom_adapter.default.getBody(), (0, _renderer.default)(markup).get(0)))).then((() => canvas)).always((() => {
invisibleDiv && _dom_adapter.default.getBody().removeChild(invisibleDiv);
_dom_adapter.default.getBody().removeChild(canvas)
}))
}
const imageCreator = exports.imageCreator = {
getImageData: function(markup, options) {
const mimeType = "image/" + options.format;
if ((0, _type.isFunction)(options.__parseAttributesFn)) {
parseAttributes = options.__parseAttributesFn
}
return getCanvasFromSvg(markup, options).then((canvas => getStringFromCanvas(canvas, mimeType)))
},
getData: function(markup, options) {
const that = this;
return imageCreator.getImageData(markup, options).then((binaryData => {
const mimeType = "image/" + options.format;
const data = (0, _type.isFunction)(window.Blob) && !options.useBase64 ? that._getBlob(binaryData, mimeType) : that._getBase64(binaryData);
return data
}))
},
_getBlob: function(binaryData, mimeType) {
let i;
const dataArray = new Uint8Array(binaryData.length);
for (i = 0; i < binaryData.length; i++) {
dataArray[i] = binaryData.charCodeAt(i)
}
return new window.Blob([dataArray.buffer], {
type: mimeType
})
},
_getBase64: function(binaryData) {
return window.btoa(binaryData)
},
_createCanvas(width, height, margin) {
const canvas = (0, _renderer.default)("<canvas>")[0];
canvas.width = width + 2 * margin;
canvas.height = height + 2 * margin;
canvas.hidden = true;
return canvas
}
};
function getData(data, options) {
return imageCreator.getData(data, options)
}
function testFormats(formats) {
const canvas = imageCreator._createCanvas(100, 100, 0);
return formats.reduce((function(r, f) {
const mimeType = ("image/" + f).toLowerCase();
if (-1 !== canvas.toDataURL(mimeType).indexOf(mimeType)) {
r.supported.push(f)
} else {
r.unsupported.push(f)
}
return r
}), {
supported: [],
unsupported: []
})
}
function calcScaledInfo(width, height) {
const pixelRatio = window.devicePixelRatio || 1;
return {
pixelRatio: pixelRatio,
width: width * pixelRatio,
height: height * pixelRatio
}
}