@leafer-in/export
Version:
294 lines (270 loc) • 11.8 kB
JavaScript
;
var draw = require("@leafer-ui/draw");
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) {
return value instanceof P ? value : new P(function(resolve) {
resolve(value);
});
}
return new (P || (P = Promise))(function(resolve, reject) {
function fulfilled(value) {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
}
function rejected(value) {
try {
step(generator["throw"](value));
} catch (e) {
reject(e);
}
}
function step(result) {
result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
}
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
typeof SuppressedError === "function" ? SuppressedError : function(error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
const {setPoint: setPoint, addPoint: addPoint, toBounds: toBounds} = draw.TwoPointBoundsHelper;
function getTrimBounds(canvas) {
const {width: width, height: height} = canvas.view;
const {data: data} = canvas.context.getImageData(0, 0, width, height);
let x, y, pointBounds, index = 0;
for (let i = 0; i < data.length; i += 4) {
if (data[i + 3] !== 0) {
x = index % width;
y = (index - x) / width;
pointBounds ? addPoint(pointBounds, x, y) : setPoint(pointBounds = {}, x, y);
}
index++;
}
const bounds = new draw.Bounds;
if (pointBounds) {
toBounds(pointBounds, bounds);
bounds.scale(1 / canvas.pixelRatio).ceil();
}
return bounds;
}
const ExportModule = {
syncExport(leaf, filename, options) {
draw.Export.running = true;
let result;
try {
const fileType = draw.FileHelper.fileType(filename);
const isDownload = filename.includes(".");
options = draw.FileHelper.getExportOptions(options);
const {toURL: toURL} = draw.Platform;
const {download: download} = draw.Platform.origin;
if (fileType === "json") {
isDownload && download(toURL(JSON.stringify(leaf.toJSON(options.json)), "text"), filename);
result = {
data: isDownload ? true : leaf.toJSON(options.json)
};
} else if (fileType === "svg") {
isDownload && download(toURL(leaf.toSVG(), "svg"), filename);
result = {
data: isDownload ? true : leaf.toSVG()
};
} else {
let renderBounds, trimBounds, scaleX = 1, scaleY = 1;
const {worldTransform: worldTransform, isLeafer: isLeafer, leafer: leafer, isFrame: isFrame} = leaf;
const {slice: slice, clip: clip, trim: trim, screenshot: screenshot, padding: padding, onCanvas: onCanvas} = options;
const smooth = draw.isUndefined(options.smooth) ? leafer ? leafer.config.smooth : true : options.smooth;
const contextSettings = options.contextSettings || (leafer ? leafer.config.contextSettings : undefined);
const fill = isLeafer && screenshot ? draw.isUndefined(options.fill) ? leaf.fill : options.fill : options.fill;
const needFill = draw.FileHelper.isOpaqueImage(filename) || fill, matrix = new draw.Matrix;
if (screenshot) {
renderBounds = screenshot === true ? isLeafer ? leafer.canvas.bounds : leaf.worldRenderBounds : screenshot;
} else {
let relative = options.relative || (isLeafer ? "inner" : "local");
scaleX = worldTransform.scaleX;
scaleY = worldTransform.scaleY;
switch (relative) {
case "inner":
matrix.set(worldTransform);
break;
case "local":
matrix.set(worldTransform).divide(leaf.localTransform);
scaleX /= leaf.scaleX;
scaleY /= leaf.scaleY;
break;
case "world":
scaleX = 1;
scaleY = 1;
break;
case "page":
relative = leafer || leaf;
default:
matrix.set(worldTransform).divide(leaf.getTransform(relative));
const l = relative.worldTransform;
scaleX /= scaleX / l.scaleX;
scaleY /= scaleY / l.scaleY;
}
renderBounds = leaf.getBounds("render", relative);
}
const scaleData = {
scaleX: 1,
scaleY: 1
};
draw.MathHelper.getScaleData(options.scale, options.size, renderBounds, scaleData);
let pixelRatio = options.pixelRatio || 1;
let {x: x, y: y, width: width, height: height} = new draw.Bounds(renderBounds).scale(scaleData.scaleX, scaleData.scaleY);
if (clip) {
x += clip.x, y += clip.y, width = clip.width, height = clip.height;
if (clip.rotation) matrix.rotateOfInner({
x: x,
y: y
}, -clip.rotation);
}
const renderOptions = {
exporting: true,
matrix: matrix.scale(1 / scaleData.scaleX, 1 / scaleData.scaleY).invert().translate(-x, -y).withScale(1 / scaleX * scaleData.scaleX, 1 / scaleY * scaleData.scaleY)
};
let canvas = draw.Creator.canvas({
width: Math.floor(width),
height: Math.floor(height),
pixelRatio: pixelRatio,
smooth: smooth,
contextSettings: contextSettings
});
let sliceLeaf;
if (slice) {
sliceLeaf = leaf;
sliceLeaf.__worldOpacity = 0;
leaf = leafer || leaf;
renderOptions.bounds = canvas.bounds;
}
canvas.save();
const igroneFill = isFrame && !draw.isUndefined(fill), oldFill = leaf.get("fill");
if (igroneFill) leaf.fill = "";
draw.Platform.render(leaf, canvas, renderOptions);
if (igroneFill) leaf.fill = oldFill;
canvas.restore();
if (sliceLeaf) sliceLeaf.__updateWorldOpacity();
if (trim) {
trimBounds = getTrimBounds(canvas);
const old = canvas, {width: width, height: height} = trimBounds;
const config = {
x: 0,
y: 0,
width: width,
height: height,
pixelRatio: pixelRatio
};
canvas = draw.Creator.canvas(config);
canvas.copyWorld(old, trimBounds, config);
old.destroy();
}
if (padding) {
const [top, right, bottom, left] = draw.MathHelper.fourNumber(padding);
const old = canvas, {width: width, height: height} = old;
canvas = draw.Creator.canvas({
width: width + left + right,
height: height + top + bottom,
pixelRatio: pixelRatio
});
canvas.copyWorld(old, old.bounds, {
x: left,
y: top,
width: width,
height: height
});
old.destroy();
}
if (needFill) canvas.fillWorld(canvas.bounds, fill || "#FFFFFF", "destination-over");
if (onCanvas) onCanvas(canvas);
const data = filename === "canvas" ? canvas : canvas.export(filename, options);
result = {
data: data,
width: canvas.pixelWidth,
height: canvas.pixelHeight,
renderBounds: renderBounds,
trimBounds: trimBounds
};
const app = leafer && leafer.app;
if (app && app.canvasManager) app.canvasManager.clearRecycled();
}
} catch (error) {
result = {
data: "",
error: error
};
}
draw.Export.running = false;
return result;
},
export(leaf, filename, options) {
draw.Export.running = true;
return addTask(success => new Promise(resolve => {
const getResult = () => __awaiter(this, void 0, void 0, function*() {
if (!draw.Resource.isComplete) return draw.Platform.requestRender(getResult);
const result = draw.Export.syncExport(leaf, filename, options);
if (result.data instanceof Promise) result.data = yield result.data;
success(result);
resolve();
});
leaf.updateLayout();
checkLazy(leaf);
const {leafer: leafer} = leaf;
if (leafer) leafer.waitViewCompleted(getResult); else getResult();
}));
}
};
let tasker;
function addTask(task) {
if (!tasker) tasker = new draw.TaskProcessor;
return new Promise(resolve => {
tasker.add(() => __awaiter(this, void 0, void 0, function*() {
return yield task(resolve);
}), {
parallel: false
});
});
}
function checkLazy(leaf) {
if (leaf.__.__needComputePaint) leaf.__.__computePaint();
if (leaf.isBranch) leaf.children.forEach(child => checkLazy(child));
}
const canvas = draw.LeaferCanvasBase.prototype;
const debug = draw.Debug.get("@leafer-in/export");
canvas.export = function(filename, options) {
const {quality: quality, blob: blob} = draw.FileHelper.getExportOptions(options);
if (filename.includes(".")) return this.saveAs(filename, quality); else if (blob) return this.toBlob(filename, quality); else return this.toDataURL(filename, quality);
};
canvas.toBlob = function(type, quality) {
return new Promise(resolve => {
draw.Platform.origin.canvasToBolb(this.view, type, quality).then(blob => {
resolve(blob);
}).catch(e => {
debug.error(e);
resolve(null);
});
});
};
canvas.toDataURL = function(type, quality) {
return draw.Platform.origin.canvasToDataURL(this.view, type, quality);
};
canvas.saveAs = function(filename, quality) {
return new Promise(resolve => {
draw.Platform.origin.canvasSaveAs(this.view, filename, quality).then(() => {
resolve(true);
}).catch(e => {
debug.error(e);
resolve(false);
});
});
};
draw.Plugin.add("export");
Object.assign(draw.Export, ExportModule);
draw.UI.prototype.export = function(filename, options) {
return draw.Export.export(this, filename, options);
};
draw.UI.prototype.syncExport = function(filename, options) {
return draw.Export.syncExport(this, filename, options);
};