UNPKG

@leafer-in/export

Version:
292 lines (269 loc) 11.7 kB
import { TwoPointBoundsHelper, Bounds, Export, FileHelper, Platform, isUndefined, Matrix, MathHelper, Creator, TaskProcessor, Resource, LeaferCanvasBase, Debug, Plugin, UI } from "@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} = 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 Bounds; if (pointBounds) { toBounds(pointBounds, bounds); bounds.scale(1 / canvas.pixelRatio).ceil(); } return bounds; } const ExportModule = { syncExport(leaf, filename, options) { Export.running = true; let result; try { const fileType = FileHelper.fileType(filename); const isDownload = filename.includes("."); options = FileHelper.getExportOptions(options); const {toURL: toURL} = Platform; const {download: download} = 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 = isUndefined(options.smooth) ? leafer ? leafer.config.smooth : true : options.smooth; const contextSettings = options.contextSettings || (leafer ? leafer.config.contextSettings : undefined); const fill = isLeafer && screenshot ? isUndefined(options.fill) ? leaf.fill : options.fill : options.fill; const needFill = FileHelper.isOpaqueImage(filename) || fill, matrix = new 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 }; MathHelper.getScaleData(options.scale, options.size, renderBounds, scaleData); let pixelRatio = options.pixelRatio || 1; let {x: x, y: y, width: width, height: height} = new 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 = 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 && !isUndefined(fill), oldFill = leaf.get("fill"); if (igroneFill) leaf.fill = ""; 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 = Creator.canvas(config); canvas.copyWorld(old, trimBounds, config); old.destroy(); } if (padding) { const [top, right, bottom, left] = MathHelper.fourNumber(padding); const old = canvas, {width: width, height: height} = old; canvas = 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 }; } Export.running = false; return result; }, export(leaf, filename, options) { Export.running = true; return addTask(success => new Promise(resolve => { const getResult = () => __awaiter(this, void 0, void 0, function*() { if (!Resource.isComplete) return Platform.requestRender(getResult); const result = 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 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 = LeaferCanvasBase.prototype; const debug = Debug.get("@leafer-in/export"); canvas.export = function(filename, options) { const {quality: quality, blob: blob} = 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 => { Platform.origin.canvasToBolb(this.view, type, quality).then(blob => { resolve(blob); }).catch(e => { debug.error(e); resolve(null); }); }); }; canvas.toDataURL = function(type, quality) { return Platform.origin.canvasToDataURL(this.view, type, quality); }; canvas.saveAs = function(filename, quality) { return new Promise(resolve => { Platform.origin.canvasSaveAs(this.view, filename, quality).then(() => { resolve(true); }).catch(e => { debug.error(e); resolve(false); }); }); }; Plugin.add("export"); Object.assign(Export, ExportModule); UI.prototype.export = function(filename, options) { return Export.export(this, filename, options); }; UI.prototype.syncExport = function(filename, options) { return Export.syncExport(this, filename, options); };