@leafer-in/export 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +1 -0
- package/dist/export.cjs +224 -0
- package/dist/export.esm.js +222 -0
- package/dist/export.esm.min.js +1 -0
- package/dist/export.js +225 -0
- package/dist/export.min.cjs +1 -0
- package/dist/export.min.js +1 -0
- package/package.json +41 -0
- package/src/canvas.ts +39 -0
- package/src/export.ts +183 -0
- package/src/index.ts +12 -0
- package/src/trim.ts +24 -0
- package/types/index.d.ts +2 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023-present, Chao (Leafer) Wan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# @leafer-in/export
|
package/dist/export.cjs
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var draw = require('@leafer-ui/draw');
|
|
4
|
+
|
|
5
|
+
/******************************************************************************
|
|
6
|
+
Copyright (c) Microsoft Corporation.
|
|
7
|
+
|
|
8
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
9
|
+
purpose with or without fee is hereby granted.
|
|
10
|
+
|
|
11
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
12
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
13
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
14
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
15
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
16
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
17
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
18
|
+
***************************************************************************** */
|
|
19
|
+
/* global Reflect, Promise, SuppressedError, Symbol */
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
23
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
24
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
25
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
26
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
27
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
28
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
33
|
+
var e = new Error(message);
|
|
34
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const { setPoint, addPoint, toBounds } = draw.TwoPointBoundsHelper;
|
|
38
|
+
function getTrimBounds(canvas) {
|
|
39
|
+
const { width, height } = canvas.view;
|
|
40
|
+
const { data } = canvas.context.getImageData(0, 0, width, height);
|
|
41
|
+
let x, y, pointBounds, index = 0;
|
|
42
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
43
|
+
if (data[i + 3] !== 0) {
|
|
44
|
+
x = index % width;
|
|
45
|
+
y = (index - x) / width;
|
|
46
|
+
pointBounds ? addPoint(pointBounds, x, y) : setPoint(pointBounds = {}, x, y);
|
|
47
|
+
}
|
|
48
|
+
index++;
|
|
49
|
+
}
|
|
50
|
+
const bounds = new draw.Bounds();
|
|
51
|
+
toBounds(pointBounds, bounds);
|
|
52
|
+
return bounds.scale(1 / canvas.pixelRatio).ceil();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const ExportModule = {
|
|
56
|
+
export(leaf, filename, options) {
|
|
57
|
+
this.running = true;
|
|
58
|
+
const fileType = draw.FileHelper.fileType(filename);
|
|
59
|
+
const isDownload = filename.includes('.');
|
|
60
|
+
options = draw.FileHelper.getExportOptions(options);
|
|
61
|
+
return addTask((success) => new Promise((resolve) => {
|
|
62
|
+
const over = (result) => {
|
|
63
|
+
success(result);
|
|
64
|
+
resolve();
|
|
65
|
+
this.running = false;
|
|
66
|
+
};
|
|
67
|
+
const { toURL } = draw.Platform;
|
|
68
|
+
const { download } = draw.Platform.origin;
|
|
69
|
+
if (fileType === 'json') {
|
|
70
|
+
isDownload && download(toURL(JSON.stringify(leaf.toJSON(options.json)), 'text'), filename);
|
|
71
|
+
return over({ data: isDownload ? true : leaf.toJSON(options.json) });
|
|
72
|
+
}
|
|
73
|
+
if (fileType === 'svg') {
|
|
74
|
+
isDownload && download(toURL(leaf.toSVG(), 'svg'), filename);
|
|
75
|
+
return over({ data: isDownload ? true : leaf.toSVG() });
|
|
76
|
+
}
|
|
77
|
+
const { leafer } = leaf;
|
|
78
|
+
if (leafer) {
|
|
79
|
+
checkLazy(leaf);
|
|
80
|
+
leafer.waitViewCompleted(() => __awaiter(this, void 0, void 0, function* () {
|
|
81
|
+
let renderBounds, trimBounds, scaleX = 1, scaleY = 1;
|
|
82
|
+
const { worldTransform, isLeafer, isFrame } = leaf;
|
|
83
|
+
const { slice, trim, onCanvas } = options;
|
|
84
|
+
const smooth = options.smooth === undefined ? leafer.config.smooth : options.smooth;
|
|
85
|
+
const contextSettings = options.contextSettings || leafer.config.contextSettings;
|
|
86
|
+
const screenshot = options.screenshot || leaf.isApp;
|
|
87
|
+
const fill = (isLeafer && screenshot) ? (options.fill === undefined ? leaf.fill : options.fill) : options.fill;
|
|
88
|
+
const needFill = draw.FileHelper.isOpaqueImage(filename) || fill, matrix = new draw.Matrix();
|
|
89
|
+
if (screenshot) {
|
|
90
|
+
renderBounds = screenshot === true ? (isLeafer ? leafer.canvas.bounds : leaf.worldRenderBounds) : screenshot;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
let relative = options.relative || (isLeafer ? 'inner' : 'local');
|
|
94
|
+
scaleX = worldTransform.scaleX;
|
|
95
|
+
scaleY = worldTransform.scaleY;
|
|
96
|
+
switch (relative) {
|
|
97
|
+
case 'inner':
|
|
98
|
+
matrix.set(worldTransform);
|
|
99
|
+
break;
|
|
100
|
+
case 'local':
|
|
101
|
+
matrix.set(worldTransform).divide(leaf.localTransform);
|
|
102
|
+
scaleX /= leaf.scaleX;
|
|
103
|
+
scaleY /= leaf.scaleY;
|
|
104
|
+
break;
|
|
105
|
+
case 'world':
|
|
106
|
+
scaleX = 1;
|
|
107
|
+
scaleY = 1;
|
|
108
|
+
break;
|
|
109
|
+
case 'page':
|
|
110
|
+
relative = leaf.leafer;
|
|
111
|
+
default:
|
|
112
|
+
matrix.set(worldTransform).divide(leaf.getTransform(relative));
|
|
113
|
+
const l = relative.worldTransform;
|
|
114
|
+
scaleX /= scaleX / l.scaleX;
|
|
115
|
+
scaleY /= scaleY / l.scaleY;
|
|
116
|
+
}
|
|
117
|
+
renderBounds = leaf.getBounds('render', relative);
|
|
118
|
+
}
|
|
119
|
+
const scaleData = { scaleX: 1, scaleY: 1 };
|
|
120
|
+
draw.MathHelper.getScaleData(options.scale, options.size, renderBounds, scaleData);
|
|
121
|
+
let pixelRatio = options.pixelRatio || 1;
|
|
122
|
+
if (leaf.isApp) {
|
|
123
|
+
scaleData.scaleX *= pixelRatio;
|
|
124
|
+
scaleData.scaleY *= pixelRatio;
|
|
125
|
+
pixelRatio = leaf.app.pixelRatio;
|
|
126
|
+
}
|
|
127
|
+
const { x, y, width, height } = new draw.Bounds(renderBounds).scale(scaleData.scaleX, scaleData.scaleY);
|
|
128
|
+
const renderOptions = { matrix: matrix.scale(1 / scaleData.scaleX, 1 / scaleData.scaleY).invert().translate(-x, -y).withScale(1 / scaleX * scaleData.scaleX, 1 / scaleY * scaleData.scaleY) };
|
|
129
|
+
let canvas = draw.Creator.canvas({ width: Math.round(width), height: Math.round(height), pixelRatio, smooth, contextSettings });
|
|
130
|
+
let sliceLeaf;
|
|
131
|
+
if (slice) {
|
|
132
|
+
sliceLeaf = leaf;
|
|
133
|
+
sliceLeaf.__worldOpacity = 0;
|
|
134
|
+
leaf = leafer;
|
|
135
|
+
renderOptions.bounds = canvas.bounds;
|
|
136
|
+
}
|
|
137
|
+
canvas.save();
|
|
138
|
+
if (isFrame && fill !== undefined) {
|
|
139
|
+
const oldFill = leaf.get('fill');
|
|
140
|
+
leaf.fill = '';
|
|
141
|
+
leaf.__render(canvas, renderOptions);
|
|
142
|
+
leaf.fill = oldFill;
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
leaf.__render(canvas, renderOptions);
|
|
146
|
+
}
|
|
147
|
+
canvas.restore();
|
|
148
|
+
if (sliceLeaf)
|
|
149
|
+
sliceLeaf.__updateWorldOpacity();
|
|
150
|
+
if (trim) {
|
|
151
|
+
trimBounds = getTrimBounds(canvas);
|
|
152
|
+
const old = canvas, { width, height } = trimBounds;
|
|
153
|
+
const config = { x: 0, y: 0, width, height, pixelRatio };
|
|
154
|
+
canvas = draw.Creator.canvas(config);
|
|
155
|
+
canvas.copyWorld(old, trimBounds, config);
|
|
156
|
+
}
|
|
157
|
+
if (needFill)
|
|
158
|
+
canvas.fillWorld(canvas.bounds, fill || '#FFFFFF', 'destination-over');
|
|
159
|
+
if (onCanvas)
|
|
160
|
+
onCanvas(canvas);
|
|
161
|
+
const data = filename === 'canvas' ? canvas : yield canvas.export(filename, options);
|
|
162
|
+
over({ data, width: canvas.pixelWidth, height: canvas.pixelHeight, renderBounds, trimBounds });
|
|
163
|
+
}));
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
over({ data: false });
|
|
167
|
+
}
|
|
168
|
+
}));
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
let tasker;
|
|
172
|
+
function addTask(task) {
|
|
173
|
+
if (!tasker)
|
|
174
|
+
tasker = new draw.TaskProcessor();
|
|
175
|
+
return new Promise((resolve) => {
|
|
176
|
+
tasker.add(() => __awaiter(this, void 0, void 0, function* () { return yield task(resolve); }), { parallel: false });
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
function checkLazy(leaf) {
|
|
180
|
+
if (leaf.__.__needComputePaint)
|
|
181
|
+
leaf.__.__computePaint();
|
|
182
|
+
if (leaf.isBranch)
|
|
183
|
+
leaf.children.forEach(child => checkLazy(child));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const canvas = draw.LeaferCanvasBase.prototype;
|
|
187
|
+
const debug = draw.Debug.get('@leafer-in/export');
|
|
188
|
+
canvas.export = function (filename, options) {
|
|
189
|
+
const { quality, blob } = draw.FileHelper.getExportOptions(options);
|
|
190
|
+
if (filename.includes('.'))
|
|
191
|
+
return this.saveAs(filename, quality);
|
|
192
|
+
else if (blob)
|
|
193
|
+
return this.toBlob(filename, quality);
|
|
194
|
+
else
|
|
195
|
+
return this.toDataURL(filename, quality);
|
|
196
|
+
};
|
|
197
|
+
canvas.toBlob = function (type, quality) {
|
|
198
|
+
return new Promise((resolve) => {
|
|
199
|
+
draw.Platform.origin.canvasToBolb(this.view, type, quality).then((blob) => {
|
|
200
|
+
resolve(blob);
|
|
201
|
+
}).catch((e) => {
|
|
202
|
+
debug.error(e);
|
|
203
|
+
resolve(null);
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
};
|
|
207
|
+
canvas.toDataURL = function (type, quality) {
|
|
208
|
+
return draw.Platform.origin.canvasToDataURL(this.view, type, quality);
|
|
209
|
+
};
|
|
210
|
+
canvas.saveAs = function (filename, quality) {
|
|
211
|
+
return new Promise((resolve) => {
|
|
212
|
+
draw.Platform.origin.canvasSaveAs(this.view, filename, quality).then(() => {
|
|
213
|
+
resolve(true);
|
|
214
|
+
}).catch((e) => {
|
|
215
|
+
debug.error(e);
|
|
216
|
+
resolve(false);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
Object.assign(draw.Export, ExportModule);
|
|
222
|
+
draw.UI.prototype.export = function (filename, options) {
|
|
223
|
+
return draw.Export.export(this, filename, options);
|
|
224
|
+
};
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { Bounds, TwoPointBoundsHelper, FileHelper, TaskProcessor, Platform, Matrix, MathHelper, Creator, LeaferCanvasBase, Debug, Export, UI } from '@leafer-ui/draw';
|
|
2
|
+
|
|
3
|
+
/******************************************************************************
|
|
4
|
+
Copyright (c) Microsoft Corporation.
|
|
5
|
+
|
|
6
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
7
|
+
purpose with or without fee is hereby granted.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
10
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
11
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
12
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
13
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
14
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
15
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
16
|
+
***************************************************************************** */
|
|
17
|
+
/* global Reflect, Promise, SuppressedError, Symbol */
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
21
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
22
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
23
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
24
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
25
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
26
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
31
|
+
var e = new Error(message);
|
|
32
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const { setPoint, addPoint, toBounds } = TwoPointBoundsHelper;
|
|
36
|
+
function getTrimBounds(canvas) {
|
|
37
|
+
const { width, height } = canvas.view;
|
|
38
|
+
const { data } = canvas.context.getImageData(0, 0, width, height);
|
|
39
|
+
let x, y, pointBounds, index = 0;
|
|
40
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
41
|
+
if (data[i + 3] !== 0) {
|
|
42
|
+
x = index % width;
|
|
43
|
+
y = (index - x) / width;
|
|
44
|
+
pointBounds ? addPoint(pointBounds, x, y) : setPoint(pointBounds = {}, x, y);
|
|
45
|
+
}
|
|
46
|
+
index++;
|
|
47
|
+
}
|
|
48
|
+
const bounds = new Bounds();
|
|
49
|
+
toBounds(pointBounds, bounds);
|
|
50
|
+
return bounds.scale(1 / canvas.pixelRatio).ceil();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const ExportModule = {
|
|
54
|
+
export(leaf, filename, options) {
|
|
55
|
+
this.running = true;
|
|
56
|
+
const fileType = FileHelper.fileType(filename);
|
|
57
|
+
const isDownload = filename.includes('.');
|
|
58
|
+
options = FileHelper.getExportOptions(options);
|
|
59
|
+
return addTask((success) => new Promise((resolve) => {
|
|
60
|
+
const over = (result) => {
|
|
61
|
+
success(result);
|
|
62
|
+
resolve();
|
|
63
|
+
this.running = false;
|
|
64
|
+
};
|
|
65
|
+
const { toURL } = Platform;
|
|
66
|
+
const { download } = Platform.origin;
|
|
67
|
+
if (fileType === 'json') {
|
|
68
|
+
isDownload && download(toURL(JSON.stringify(leaf.toJSON(options.json)), 'text'), filename);
|
|
69
|
+
return over({ data: isDownload ? true : leaf.toJSON(options.json) });
|
|
70
|
+
}
|
|
71
|
+
if (fileType === 'svg') {
|
|
72
|
+
isDownload && download(toURL(leaf.toSVG(), 'svg'), filename);
|
|
73
|
+
return over({ data: isDownload ? true : leaf.toSVG() });
|
|
74
|
+
}
|
|
75
|
+
const { leafer } = leaf;
|
|
76
|
+
if (leafer) {
|
|
77
|
+
checkLazy(leaf);
|
|
78
|
+
leafer.waitViewCompleted(() => __awaiter(this, void 0, void 0, function* () {
|
|
79
|
+
let renderBounds, trimBounds, scaleX = 1, scaleY = 1;
|
|
80
|
+
const { worldTransform, isLeafer, isFrame } = leaf;
|
|
81
|
+
const { slice, trim, onCanvas } = options;
|
|
82
|
+
const smooth = options.smooth === undefined ? leafer.config.smooth : options.smooth;
|
|
83
|
+
const contextSettings = options.contextSettings || leafer.config.contextSettings;
|
|
84
|
+
const screenshot = options.screenshot || leaf.isApp;
|
|
85
|
+
const fill = (isLeafer && screenshot) ? (options.fill === undefined ? leaf.fill : options.fill) : options.fill;
|
|
86
|
+
const needFill = FileHelper.isOpaqueImage(filename) || fill, matrix = new Matrix();
|
|
87
|
+
if (screenshot) {
|
|
88
|
+
renderBounds = screenshot === true ? (isLeafer ? leafer.canvas.bounds : leaf.worldRenderBounds) : screenshot;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
let relative = options.relative || (isLeafer ? 'inner' : 'local');
|
|
92
|
+
scaleX = worldTransform.scaleX;
|
|
93
|
+
scaleY = worldTransform.scaleY;
|
|
94
|
+
switch (relative) {
|
|
95
|
+
case 'inner':
|
|
96
|
+
matrix.set(worldTransform);
|
|
97
|
+
break;
|
|
98
|
+
case 'local':
|
|
99
|
+
matrix.set(worldTransform).divide(leaf.localTransform);
|
|
100
|
+
scaleX /= leaf.scaleX;
|
|
101
|
+
scaleY /= leaf.scaleY;
|
|
102
|
+
break;
|
|
103
|
+
case 'world':
|
|
104
|
+
scaleX = 1;
|
|
105
|
+
scaleY = 1;
|
|
106
|
+
break;
|
|
107
|
+
case 'page':
|
|
108
|
+
relative = leaf.leafer;
|
|
109
|
+
default:
|
|
110
|
+
matrix.set(worldTransform).divide(leaf.getTransform(relative));
|
|
111
|
+
const l = relative.worldTransform;
|
|
112
|
+
scaleX /= scaleX / l.scaleX;
|
|
113
|
+
scaleY /= scaleY / l.scaleY;
|
|
114
|
+
}
|
|
115
|
+
renderBounds = leaf.getBounds('render', relative);
|
|
116
|
+
}
|
|
117
|
+
const scaleData = { scaleX: 1, scaleY: 1 };
|
|
118
|
+
MathHelper.getScaleData(options.scale, options.size, renderBounds, scaleData);
|
|
119
|
+
let pixelRatio = options.pixelRatio || 1;
|
|
120
|
+
if (leaf.isApp) {
|
|
121
|
+
scaleData.scaleX *= pixelRatio;
|
|
122
|
+
scaleData.scaleY *= pixelRatio;
|
|
123
|
+
pixelRatio = leaf.app.pixelRatio;
|
|
124
|
+
}
|
|
125
|
+
const { x, y, width, height } = new Bounds(renderBounds).scale(scaleData.scaleX, scaleData.scaleY);
|
|
126
|
+
const renderOptions = { matrix: matrix.scale(1 / scaleData.scaleX, 1 / scaleData.scaleY).invert().translate(-x, -y).withScale(1 / scaleX * scaleData.scaleX, 1 / scaleY * scaleData.scaleY) };
|
|
127
|
+
let canvas = Creator.canvas({ width: Math.round(width), height: Math.round(height), pixelRatio, smooth, contextSettings });
|
|
128
|
+
let sliceLeaf;
|
|
129
|
+
if (slice) {
|
|
130
|
+
sliceLeaf = leaf;
|
|
131
|
+
sliceLeaf.__worldOpacity = 0;
|
|
132
|
+
leaf = leafer;
|
|
133
|
+
renderOptions.bounds = canvas.bounds;
|
|
134
|
+
}
|
|
135
|
+
canvas.save();
|
|
136
|
+
if (isFrame && fill !== undefined) {
|
|
137
|
+
const oldFill = leaf.get('fill');
|
|
138
|
+
leaf.fill = '';
|
|
139
|
+
leaf.__render(canvas, renderOptions);
|
|
140
|
+
leaf.fill = oldFill;
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
leaf.__render(canvas, renderOptions);
|
|
144
|
+
}
|
|
145
|
+
canvas.restore();
|
|
146
|
+
if (sliceLeaf)
|
|
147
|
+
sliceLeaf.__updateWorldOpacity();
|
|
148
|
+
if (trim) {
|
|
149
|
+
trimBounds = getTrimBounds(canvas);
|
|
150
|
+
const old = canvas, { width, height } = trimBounds;
|
|
151
|
+
const config = { x: 0, y: 0, width, height, pixelRatio };
|
|
152
|
+
canvas = Creator.canvas(config);
|
|
153
|
+
canvas.copyWorld(old, trimBounds, config);
|
|
154
|
+
}
|
|
155
|
+
if (needFill)
|
|
156
|
+
canvas.fillWorld(canvas.bounds, fill || '#FFFFFF', 'destination-over');
|
|
157
|
+
if (onCanvas)
|
|
158
|
+
onCanvas(canvas);
|
|
159
|
+
const data = filename === 'canvas' ? canvas : yield canvas.export(filename, options);
|
|
160
|
+
over({ data, width: canvas.pixelWidth, height: canvas.pixelHeight, renderBounds, trimBounds });
|
|
161
|
+
}));
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
over({ data: false });
|
|
165
|
+
}
|
|
166
|
+
}));
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
let tasker;
|
|
170
|
+
function addTask(task) {
|
|
171
|
+
if (!tasker)
|
|
172
|
+
tasker = new TaskProcessor();
|
|
173
|
+
return new Promise((resolve) => {
|
|
174
|
+
tasker.add(() => __awaiter(this, void 0, void 0, function* () { return yield task(resolve); }), { parallel: false });
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
function checkLazy(leaf) {
|
|
178
|
+
if (leaf.__.__needComputePaint)
|
|
179
|
+
leaf.__.__computePaint();
|
|
180
|
+
if (leaf.isBranch)
|
|
181
|
+
leaf.children.forEach(child => checkLazy(child));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const canvas = LeaferCanvasBase.prototype;
|
|
185
|
+
const debug = Debug.get('@leafer-in/export');
|
|
186
|
+
canvas.export = function (filename, options) {
|
|
187
|
+
const { quality, blob } = FileHelper.getExportOptions(options);
|
|
188
|
+
if (filename.includes('.'))
|
|
189
|
+
return this.saveAs(filename, quality);
|
|
190
|
+
else if (blob)
|
|
191
|
+
return this.toBlob(filename, quality);
|
|
192
|
+
else
|
|
193
|
+
return this.toDataURL(filename, quality);
|
|
194
|
+
};
|
|
195
|
+
canvas.toBlob = function (type, quality) {
|
|
196
|
+
return new Promise((resolve) => {
|
|
197
|
+
Platform.origin.canvasToBolb(this.view, type, quality).then((blob) => {
|
|
198
|
+
resolve(blob);
|
|
199
|
+
}).catch((e) => {
|
|
200
|
+
debug.error(e);
|
|
201
|
+
resolve(null);
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
};
|
|
205
|
+
canvas.toDataURL = function (type, quality) {
|
|
206
|
+
return Platform.origin.canvasToDataURL(this.view, type, quality);
|
|
207
|
+
};
|
|
208
|
+
canvas.saveAs = function (filename, quality) {
|
|
209
|
+
return new Promise((resolve) => {
|
|
210
|
+
Platform.origin.canvasSaveAs(this.view, filename, quality).then(() => {
|
|
211
|
+
resolve(true);
|
|
212
|
+
}).catch((e) => {
|
|
213
|
+
debug.error(e);
|
|
214
|
+
resolve(false);
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
Object.assign(Export, ExportModule);
|
|
220
|
+
UI.prototype.export = function (filename, options) {
|
|
221
|
+
return Export.export(this, filename, options);
|
|
222
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Bounds as e,TwoPointBoundsHelper as t,FileHelper as n,TaskProcessor as o,Platform as i,Matrix as s,MathHelper as a,Creator as r,LeaferCanvasBase as c,Debug as l,Export as d,UI as u}from"@leafer-ui/draw";function h(e,t,n,o){return new(n||(n=Promise))((function(i,s){function a(e){try{c(o.next(e))}catch(e){s(e)}}function r(e){try{c(o.throw(e))}catch(e){s(e)}}function c(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,r)}c((o=o.apply(e,t||[])).next())}))}"function"==typeof SuppressedError&&SuppressedError;const{setPoint:f,addPoint:p,toBounds:g}=t;const v={export(t,c,l){this.running=!0;const d=n.fileType(c),u=c.includes(".");return l=n.getExportOptions(l),function(e){w||(w=new o);return new Promise((t=>{w.add((()=>h(this,void 0,void 0,(function*(){return yield e(t)}))),{parallel:!1})}))}((o=>new Promise((v=>{const w=e=>{o(e),v(),this.running=!1},{toURL:m}=i,{download:y}=i.origin;if("json"===d)return u&&y(m(JSON.stringify(t.toJSON(l.json)),"text"),c),w({data:!!u||t.toJSON(l.json)});if("svg"===d)return u&&y(m(t.toSVG(),"svg"),c),w({data:!!u||t.toSVG()});const{leafer:_}=t;_?(x(t),_.waitViewCompleted((()=>h(this,void 0,void 0,(function*(){let o,i,d=1,u=1;const{worldTransform:h,isLeafer:v,isFrame:x}=t,{slice:m,trim:y,onCanvas:b}=l,S=void 0===l.smooth?_.config.smooth:l.smooth,R=l.contextSettings||_.config.contextSettings,B=l.screenshot||t.isApp,O=v&&B&&void 0===l.fill?t.fill:l.fill,P=n.isOpaqueImage(c)||O,X=new s;if(B)o=!0===B?v?_.canvas.bounds:t.worldRenderBounds:B;else{let e=l.relative||(v?"inner":"local");switch(d=h.scaleX,u=h.scaleY,e){case"inner":X.set(h);break;case"local":X.set(h).divide(t.localTransform),d/=t.scaleX,u/=t.scaleY;break;case"world":d=1,u=1;break;case"page":e=t.leafer;default:X.set(h).divide(t.getTransform(e));const n=e.worldTransform;d/=d/n.scaleX,u/=u/n.scaleY}o=t.getBounds("render",e)}const Y={scaleX:1,scaleY:1};a.getScaleData(l.scale,l.size,o,Y);let F=l.pixelRatio||1;t.isApp&&(Y.scaleX*=F,Y.scaleY*=F,F=t.app.pixelRatio);const{x:T,y:A,width:D,height:E}=new e(o).scale(Y.scaleX,Y.scaleY),L={matrix:X.scale(1/Y.scaleX,1/Y.scaleY).invert().translate(-T,-A).withScale(1/d*Y.scaleX,1/u*Y.scaleY)};let j,U=r.canvas({width:Math.round(D),height:Math.round(E),pixelRatio:F,smooth:S,contextSettings:R});if(m&&(j=t,j.__worldOpacity=0,t=_,L.bounds=U.bounds),U.save(),x&&void 0!==O){const e=t.get("fill");t.fill="",t.__render(U,L),t.fill=e}else t.__render(U,L);if(U.restore(),j&&j.__updateWorldOpacity(),y){i=function(t){const{width:n,height:o}=t.view,{data:i}=t.context.getImageData(0,0,n,o);let s,a,r,c=0;for(let e=0;e<i.length;e+=4)0!==i[e+3]&&(s=c%n,a=(c-s)/n,r?p(r,s,a):f(r={},s,a)),c++;const l=new e;return g(r,l),l.scale(1/t.pixelRatio).ceil()}(U);const t=U,{width:n,height:o}=i,s={x:0,y:0,width:n,height:o,pixelRatio:F};U=r.canvas(s),U.copyWorld(t,i,s)}P&&U.fillWorld(U.bounds,O||"#FFFFFF","destination-over"),b&&b(U);const W="canvas"===c?U:yield U.export(c,l);w({data:W,width:U.pixelWidth,height:U.pixelHeight,renderBounds:o,trimBounds:i})}))))):w({data:!1})}))))}};let w;function x(e){e.__.__needComputePaint&&e.__.__computePaint(),e.isBranch&&e.children.forEach((e=>x(e)))}const m=c.prototype,y=l.get("@leafer-in/export");m.export=function(e,t){const{quality:o,blob:i}=n.getExportOptions(t);return e.includes(".")?this.saveAs(e,o):i?this.toBlob(e,o):this.toDataURL(e,o)},m.toBlob=function(e,t){return new Promise((n=>{i.origin.canvasToBolb(this.view,e,t).then((e=>{n(e)})).catch((e=>{y.error(e),n(null)}))}))},m.toDataURL=function(e,t){return i.origin.canvasToDataURL(this.view,e,t)},m.saveAs=function(e,t){return new Promise((n=>{i.origin.canvasSaveAs(this.view,e,t).then((()=>{n(!0)})).catch((e=>{y.error(e),n(!1)}))}))},Object.assign(d,v),u.prototype.export=function(e,t){return d.export(this,e,t)};
|
package/dist/export.js
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
(function (draw) {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/******************************************************************************
|
|
5
|
+
Copyright (c) Microsoft Corporation.
|
|
6
|
+
|
|
7
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
8
|
+
purpose with or without fee is hereby granted.
|
|
9
|
+
|
|
10
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
11
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
12
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
13
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
14
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
15
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
16
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
17
|
+
***************************************************************************** */
|
|
18
|
+
/* global Reflect, Promise, SuppressedError, Symbol */
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
22
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
23
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
24
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
25
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
26
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
27
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
32
|
+
var e = new Error(message);
|
|
33
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const { setPoint, addPoint, toBounds } = draw.TwoPointBoundsHelper;
|
|
37
|
+
function getTrimBounds(canvas) {
|
|
38
|
+
const { width, height } = canvas.view;
|
|
39
|
+
const { data } = canvas.context.getImageData(0, 0, width, height);
|
|
40
|
+
let x, y, pointBounds, index = 0;
|
|
41
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
42
|
+
if (data[i + 3] !== 0) {
|
|
43
|
+
x = index % width;
|
|
44
|
+
y = (index - x) / width;
|
|
45
|
+
pointBounds ? addPoint(pointBounds, x, y) : setPoint(pointBounds = {}, x, y);
|
|
46
|
+
}
|
|
47
|
+
index++;
|
|
48
|
+
}
|
|
49
|
+
const bounds = new draw.Bounds();
|
|
50
|
+
toBounds(pointBounds, bounds);
|
|
51
|
+
return bounds.scale(1 / canvas.pixelRatio).ceil();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const ExportModule = {
|
|
55
|
+
export(leaf, filename, options) {
|
|
56
|
+
this.running = true;
|
|
57
|
+
const fileType = draw.FileHelper.fileType(filename);
|
|
58
|
+
const isDownload = filename.includes('.');
|
|
59
|
+
options = draw.FileHelper.getExportOptions(options);
|
|
60
|
+
return addTask((success) => new Promise((resolve) => {
|
|
61
|
+
const over = (result) => {
|
|
62
|
+
success(result);
|
|
63
|
+
resolve();
|
|
64
|
+
this.running = false;
|
|
65
|
+
};
|
|
66
|
+
const { toURL } = draw.Platform;
|
|
67
|
+
const { download } = draw.Platform.origin;
|
|
68
|
+
if (fileType === 'json') {
|
|
69
|
+
isDownload && download(toURL(JSON.stringify(leaf.toJSON(options.json)), 'text'), filename);
|
|
70
|
+
return over({ data: isDownload ? true : leaf.toJSON(options.json) });
|
|
71
|
+
}
|
|
72
|
+
if (fileType === 'svg') {
|
|
73
|
+
isDownload && download(toURL(leaf.toSVG(), 'svg'), filename);
|
|
74
|
+
return over({ data: isDownload ? true : leaf.toSVG() });
|
|
75
|
+
}
|
|
76
|
+
const { leafer } = leaf;
|
|
77
|
+
if (leafer) {
|
|
78
|
+
checkLazy(leaf);
|
|
79
|
+
leafer.waitViewCompleted(() => __awaiter(this, void 0, void 0, function* () {
|
|
80
|
+
let renderBounds, trimBounds, scaleX = 1, scaleY = 1;
|
|
81
|
+
const { worldTransform, isLeafer, isFrame } = leaf;
|
|
82
|
+
const { slice, trim, onCanvas } = options;
|
|
83
|
+
const smooth = options.smooth === undefined ? leafer.config.smooth : options.smooth;
|
|
84
|
+
const contextSettings = options.contextSettings || leafer.config.contextSettings;
|
|
85
|
+
const screenshot = options.screenshot || leaf.isApp;
|
|
86
|
+
const fill = (isLeafer && screenshot) ? (options.fill === undefined ? leaf.fill : options.fill) : options.fill;
|
|
87
|
+
const needFill = draw.FileHelper.isOpaqueImage(filename) || fill, matrix = new draw.Matrix();
|
|
88
|
+
if (screenshot) {
|
|
89
|
+
renderBounds = screenshot === true ? (isLeafer ? leafer.canvas.bounds : leaf.worldRenderBounds) : screenshot;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
let relative = options.relative || (isLeafer ? 'inner' : 'local');
|
|
93
|
+
scaleX = worldTransform.scaleX;
|
|
94
|
+
scaleY = worldTransform.scaleY;
|
|
95
|
+
switch (relative) {
|
|
96
|
+
case 'inner':
|
|
97
|
+
matrix.set(worldTransform);
|
|
98
|
+
break;
|
|
99
|
+
case 'local':
|
|
100
|
+
matrix.set(worldTransform).divide(leaf.localTransform);
|
|
101
|
+
scaleX /= leaf.scaleX;
|
|
102
|
+
scaleY /= leaf.scaleY;
|
|
103
|
+
break;
|
|
104
|
+
case 'world':
|
|
105
|
+
scaleX = 1;
|
|
106
|
+
scaleY = 1;
|
|
107
|
+
break;
|
|
108
|
+
case 'page':
|
|
109
|
+
relative = leaf.leafer;
|
|
110
|
+
default:
|
|
111
|
+
matrix.set(worldTransform).divide(leaf.getTransform(relative));
|
|
112
|
+
const l = relative.worldTransform;
|
|
113
|
+
scaleX /= scaleX / l.scaleX;
|
|
114
|
+
scaleY /= scaleY / l.scaleY;
|
|
115
|
+
}
|
|
116
|
+
renderBounds = leaf.getBounds('render', relative);
|
|
117
|
+
}
|
|
118
|
+
const scaleData = { scaleX: 1, scaleY: 1 };
|
|
119
|
+
draw.MathHelper.getScaleData(options.scale, options.size, renderBounds, scaleData);
|
|
120
|
+
let pixelRatio = options.pixelRatio || 1;
|
|
121
|
+
if (leaf.isApp) {
|
|
122
|
+
scaleData.scaleX *= pixelRatio;
|
|
123
|
+
scaleData.scaleY *= pixelRatio;
|
|
124
|
+
pixelRatio = leaf.app.pixelRatio;
|
|
125
|
+
}
|
|
126
|
+
const { x, y, width, height } = new draw.Bounds(renderBounds).scale(scaleData.scaleX, scaleData.scaleY);
|
|
127
|
+
const renderOptions = { matrix: matrix.scale(1 / scaleData.scaleX, 1 / scaleData.scaleY).invert().translate(-x, -y).withScale(1 / scaleX * scaleData.scaleX, 1 / scaleY * scaleData.scaleY) };
|
|
128
|
+
let canvas = draw.Creator.canvas({ width: Math.round(width), height: Math.round(height), pixelRatio, smooth, contextSettings });
|
|
129
|
+
let sliceLeaf;
|
|
130
|
+
if (slice) {
|
|
131
|
+
sliceLeaf = leaf;
|
|
132
|
+
sliceLeaf.__worldOpacity = 0;
|
|
133
|
+
leaf = leafer;
|
|
134
|
+
renderOptions.bounds = canvas.bounds;
|
|
135
|
+
}
|
|
136
|
+
canvas.save();
|
|
137
|
+
if (isFrame && fill !== undefined) {
|
|
138
|
+
const oldFill = leaf.get('fill');
|
|
139
|
+
leaf.fill = '';
|
|
140
|
+
leaf.__render(canvas, renderOptions);
|
|
141
|
+
leaf.fill = oldFill;
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
leaf.__render(canvas, renderOptions);
|
|
145
|
+
}
|
|
146
|
+
canvas.restore();
|
|
147
|
+
if (sliceLeaf)
|
|
148
|
+
sliceLeaf.__updateWorldOpacity();
|
|
149
|
+
if (trim) {
|
|
150
|
+
trimBounds = getTrimBounds(canvas);
|
|
151
|
+
const old = canvas, { width, height } = trimBounds;
|
|
152
|
+
const config = { x: 0, y: 0, width, height, pixelRatio };
|
|
153
|
+
canvas = draw.Creator.canvas(config);
|
|
154
|
+
canvas.copyWorld(old, trimBounds, config);
|
|
155
|
+
}
|
|
156
|
+
if (needFill)
|
|
157
|
+
canvas.fillWorld(canvas.bounds, fill || '#FFFFFF', 'destination-over');
|
|
158
|
+
if (onCanvas)
|
|
159
|
+
onCanvas(canvas);
|
|
160
|
+
const data = filename === 'canvas' ? canvas : yield canvas.export(filename, options);
|
|
161
|
+
over({ data, width: canvas.pixelWidth, height: canvas.pixelHeight, renderBounds, trimBounds });
|
|
162
|
+
}));
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
over({ data: false });
|
|
166
|
+
}
|
|
167
|
+
}));
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
let tasker;
|
|
171
|
+
function addTask(task) {
|
|
172
|
+
if (!tasker)
|
|
173
|
+
tasker = new draw.TaskProcessor();
|
|
174
|
+
return new Promise((resolve) => {
|
|
175
|
+
tasker.add(() => __awaiter(this, void 0, void 0, function* () { return yield task(resolve); }), { parallel: false });
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
function checkLazy(leaf) {
|
|
179
|
+
if (leaf.__.__needComputePaint)
|
|
180
|
+
leaf.__.__computePaint();
|
|
181
|
+
if (leaf.isBranch)
|
|
182
|
+
leaf.children.forEach(child => checkLazy(child));
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const canvas = draw.LeaferCanvasBase.prototype;
|
|
186
|
+
const debug = draw.Debug.get('@leafer-in/export');
|
|
187
|
+
canvas.export = function (filename, options) {
|
|
188
|
+
const { quality, blob } = draw.FileHelper.getExportOptions(options);
|
|
189
|
+
if (filename.includes('.'))
|
|
190
|
+
return this.saveAs(filename, quality);
|
|
191
|
+
else if (blob)
|
|
192
|
+
return this.toBlob(filename, quality);
|
|
193
|
+
else
|
|
194
|
+
return this.toDataURL(filename, quality);
|
|
195
|
+
};
|
|
196
|
+
canvas.toBlob = function (type, quality) {
|
|
197
|
+
return new Promise((resolve) => {
|
|
198
|
+
draw.Platform.origin.canvasToBolb(this.view, type, quality).then((blob) => {
|
|
199
|
+
resolve(blob);
|
|
200
|
+
}).catch((e) => {
|
|
201
|
+
debug.error(e);
|
|
202
|
+
resolve(null);
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
};
|
|
206
|
+
canvas.toDataURL = function (type, quality) {
|
|
207
|
+
return draw.Platform.origin.canvasToDataURL(this.view, type, quality);
|
|
208
|
+
};
|
|
209
|
+
canvas.saveAs = function (filename, quality) {
|
|
210
|
+
return new Promise((resolve) => {
|
|
211
|
+
draw.Platform.origin.canvasSaveAs(this.view, filename, quality).then(() => {
|
|
212
|
+
resolve(true);
|
|
213
|
+
}).catch((e) => {
|
|
214
|
+
debug.error(e);
|
|
215
|
+
resolve(false);
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
Object.assign(draw.Export, ExportModule);
|
|
221
|
+
draw.UI.prototype.export = function (filename, options) {
|
|
222
|
+
return draw.Export.export(this, filename, options);
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
})(LeaferUI);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var e=require("@leafer-ui/draw");function t(e,t,o,n){return new(o||(o=Promise))((function(i,r){function a(e){try{l(n.next(e))}catch(e){r(e)}}function s(e){try{l(n.throw(e))}catch(e){r(e)}}function l(e){var t;e.done?i(e.value):(t=e.value,t instanceof o?t:new o((function(e){e(t)}))).then(a,s)}l((n=n.apply(e,t||[])).next())}))}"function"==typeof SuppressedError&&SuppressedError;const{setPoint:o,addPoint:n,toBounds:i}=e.TwoPointBoundsHelper;const r={export(r,l,c){this.running=!0;const d=e.FileHelper.fileType(l),u=l.includes(".");return c=e.FileHelper.getExportOptions(c),function(o){a||(a=new e.TaskProcessor);return new Promise((e=>{a.add((()=>t(this,void 0,void 0,(function*(){return yield o(e)}))),{parallel:!1})}))}((a=>new Promise((f=>{const h=e=>{a(e),f(),this.running=!1},{toURL:p}=e.Platform,{download:g}=e.Platform.origin;if("json"===d)return u&&g(p(JSON.stringify(r.toJSON(c.json)),"text"),l),h({data:!!u||r.toJSON(c.json)});if("svg"===d)return u&&g(p(r.toSVG(),"svg"),l),h({data:!!u||r.toSVG()});const{leafer:v}=r;v?(s(r),v.waitViewCompleted((()=>t(this,void 0,void 0,(function*(){let t,a,s=1,d=1;const{worldTransform:u,isLeafer:f,isFrame:p}=r,{slice:g,trim:w,onCanvas:x}=c,m=void 0===c.smooth?v.config.smooth:c.smooth,y=c.contextSettings||v.config.contextSettings,P=c.screenshot||r.isApp,_=f&&P&&void 0===c.fill?r.fill:c.fill,b=e.FileHelper.isOpaqueImage(l)||_,B=new e.Matrix;if(P)t=!0===P?f?v.canvas.bounds:r.worldRenderBounds:P;else{let e=c.relative||(f?"inner":"local");switch(s=u.scaleX,d=u.scaleY,e){case"inner":B.set(u);break;case"local":B.set(u).divide(r.localTransform),s/=r.scaleX,d/=r.scaleY;break;case"world":s=1,d=1;break;case"page":e=r.leafer;default:B.set(u).divide(r.getTransform(e));const t=e.worldTransform;s/=s/t.scaleX,d/=d/t.scaleY}t=r.getBounds("render",e)}const S={scaleX:1,scaleY:1};e.MathHelper.getScaleData(c.scale,c.size,t,S);let F=c.pixelRatio||1;r.isApp&&(S.scaleX*=F,S.scaleY*=F,F=r.app.pixelRatio);const{x:R,y:O,width:T,height:X}=new e.Bounds(t).scale(S.scaleX,S.scaleY),Y={matrix:B.scale(1/S.scaleX,1/S.scaleY).invert().translate(-R,-O).withScale(1/s*S.scaleX,1/d*S.scaleY)};let E,H=e.Creator.canvas({width:Math.round(T),height:Math.round(X),pixelRatio:F,smooth:m,contextSettings:y});if(g&&(E=r,E.__worldOpacity=0,r=v,Y.bounds=H.bounds),H.save(),p&&void 0!==_){const e=r.get("fill");r.fill="",r.__render(H,Y),r.fill=e}else r.__render(H,Y);if(H.restore(),E&&E.__updateWorldOpacity(),w){a=function(t){const{width:r,height:a}=t.view,{data:s}=t.context.getImageData(0,0,r,a);let l,c,d,u=0;for(let e=0;e<s.length;e+=4)0!==s[e+3]&&(l=u%r,c=(u-l)/r,d?n(d,l,c):o(d={},l,c)),u++;const f=new e.Bounds;return i(d,f),f.scale(1/t.pixelRatio).ceil()}(H);const t=H,{width:r,height:s}=a,l={x:0,y:0,width:r,height:s,pixelRatio:F};H=e.Creator.canvas(l),H.copyWorld(t,a,l)}b&&H.fillWorld(H.bounds,_||"#FFFFFF","destination-over"),x&&x(H);const C="canvas"===l?H:yield H.export(l,c);h({data:C,width:H.pixelWidth,height:H.pixelHeight,renderBounds:t,trimBounds:a})}))))):h({data:!1})}))))}};let a;function s(e){e.__.__needComputePaint&&e.__.__computePaint(),e.isBranch&&e.children.forEach((e=>s(e)))}const l=e.LeaferCanvasBase.prototype,c=e.Debug.get("@leafer-in/export");l.export=function(t,o){const{quality:n,blob:i}=e.FileHelper.getExportOptions(o);return t.includes(".")?this.saveAs(t,n):i?this.toBlob(t,n):this.toDataURL(t,n)},l.toBlob=function(t,o){return new Promise((n=>{e.Platform.origin.canvasToBolb(this.view,t,o).then((e=>{n(e)})).catch((e=>{c.error(e),n(null)}))}))},l.toDataURL=function(t,o){return e.Platform.origin.canvasToDataURL(this.view,t,o)},l.saveAs=function(t,o){return new Promise((n=>{e.Platform.origin.canvasSaveAs(this.view,t,o).then((()=>{n(!0)})).catch((e=>{c.error(e),n(!1)}))}))},Object.assign(e.Export,r),e.UI.prototype.export=function(t,o){return e.Export.export(this,t,o)};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(e){"use strict";function t(e,t,o,n){return new(o||(o=Promise))((function(i,r){function a(e){try{l(n.next(e))}catch(e){r(e)}}function s(e){try{l(n.throw(e))}catch(e){r(e)}}function l(e){var t;e.done?i(e.value):(t=e.value,t instanceof o?t:new o((function(e){e(t)}))).then(a,s)}l((n=n.apply(e,t||[])).next())}))}"function"==typeof SuppressedError&&SuppressedError;const{setPoint:o,addPoint:n,toBounds:i}=e.TwoPointBoundsHelper;const r={export(r,l,c){this.running=!0;const d=e.FileHelper.fileType(l),u=l.includes(".");return c=e.FileHelper.getExportOptions(c),function(o){a||(a=new e.TaskProcessor);return new Promise((e=>{a.add((()=>t(this,void 0,void 0,(function*(){return yield o(e)}))),{parallel:!1})}))}((a=>new Promise((f=>{const h=e=>{a(e),f(),this.running=!1},{toURL:p}=e.Platform,{download:g}=e.Platform.origin;if("json"===d)return u&&g(p(JSON.stringify(r.toJSON(c.json)),"text"),l),h({data:!!u||r.toJSON(c.json)});if("svg"===d)return u&&g(p(r.toSVG(),"svg"),l),h({data:!!u||r.toSVG()});const{leafer:v}=r;v?(s(r),v.waitViewCompleted((()=>t(this,void 0,void 0,(function*(){let t,a,s=1,d=1;const{worldTransform:u,isLeafer:f,isFrame:p}=r,{slice:g,trim:w,onCanvas:x}=c,m=void 0===c.smooth?v.config.smooth:c.smooth,y=c.contextSettings||v.config.contextSettings,P=c.screenshot||r.isApp,_=f&&P&&void 0===c.fill?r.fill:c.fill,b=e.FileHelper.isOpaqueImage(l)||_,B=new e.Matrix;if(P)t=!0===P?f?v.canvas.bounds:r.worldRenderBounds:P;else{let e=c.relative||(f?"inner":"local");switch(s=u.scaleX,d=u.scaleY,e){case"inner":B.set(u);break;case"local":B.set(u).divide(r.localTransform),s/=r.scaleX,d/=r.scaleY;break;case"world":s=1,d=1;break;case"page":e=r.leafer;default:B.set(u).divide(r.getTransform(e));const t=e.worldTransform;s/=s/t.scaleX,d/=d/t.scaleY}t=r.getBounds("render",e)}const S={scaleX:1,scaleY:1};e.MathHelper.getScaleData(c.scale,c.size,t,S);let F=c.pixelRatio||1;r.isApp&&(S.scaleX*=F,S.scaleY*=F,F=r.app.pixelRatio);const{x:R,y:O,width:T,height:X}=new e.Bounds(t).scale(S.scaleX,S.scaleY),Y={matrix:B.scale(1/S.scaleX,1/S.scaleY).invert().translate(-R,-O).withScale(1/s*S.scaleX,1/d*S.scaleY)};let E,H=e.Creator.canvas({width:Math.round(T),height:Math.round(X),pixelRatio:F,smooth:m,contextSettings:y});if(g&&(E=r,E.__worldOpacity=0,r=v,Y.bounds=H.bounds),H.save(),p&&void 0!==_){const e=r.get("fill");r.fill="",r.__render(H,Y),r.fill=e}else r.__render(H,Y);if(H.restore(),E&&E.__updateWorldOpacity(),w){a=function(t){const{width:r,height:a}=t.view,{data:s}=t.context.getImageData(0,0,r,a);let l,c,d,u=0;for(let e=0;e<s.length;e+=4)0!==s[e+3]&&(l=u%r,c=(u-l)/r,d?n(d,l,c):o(d={},l,c)),u++;const f=new e.Bounds;return i(d,f),f.scale(1/t.pixelRatio).ceil()}(H);const t=H,{width:r,height:s}=a,l={x:0,y:0,width:r,height:s,pixelRatio:F};H=e.Creator.canvas(l),H.copyWorld(t,a,l)}b&&H.fillWorld(H.bounds,_||"#FFFFFF","destination-over"),x&&x(H);const L="canvas"===l?H:yield H.export(l,c);h({data:L,width:H.pixelWidth,height:H.pixelHeight,renderBounds:t,trimBounds:a})}))))):h({data:!1})}))))}};let a;function s(e){e.__.__needComputePaint&&e.__.__computePaint(),e.isBranch&&e.children.forEach((e=>s(e)))}const l=e.LeaferCanvasBase.prototype,c=e.Debug.get("@leafer-in/export");l.export=function(t,o){const{quality:n,blob:i}=e.FileHelper.getExportOptions(o);return t.includes(".")?this.saveAs(t,n):i?this.toBlob(t,n):this.toDataURL(t,n)},l.toBlob=function(t,o){return new Promise((n=>{e.Platform.origin.canvasToBolb(this.view,t,o).then((e=>{n(e)})).catch((e=>{c.error(e),n(null)}))}))},l.toDataURL=function(t,o){return e.Platform.origin.canvasToDataURL(this.view,t,o)},l.saveAs=function(t,o){return new Promise((n=>{e.Platform.origin.canvasSaveAs(this.view,t,o).then((()=>{n(!0)})).catch((e=>{c.error(e),n(!1)}))}))},Object.assign(e.Export,r),e.UI.prototype.export=function(t,o){return e.Export.export(this,t,o)}}(LeaferUI);
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@leafer-in/export",
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"description": "@leafer-in/export",
|
|
5
|
+
"author": "Chao (Leafer) Wan",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "dist/export.esm.js",
|
|
9
|
+
"exports": {
|
|
10
|
+
"import": "./dist/export.esm.js",
|
|
11
|
+
"require": "./dist/export.cjs",
|
|
12
|
+
"types": "./types/index.d.ts"
|
|
13
|
+
},
|
|
14
|
+
"types": "types/index.d.ts",
|
|
15
|
+
"unpkg": "dist/export.js",
|
|
16
|
+
"jsdelivr": "dist/export.js",
|
|
17
|
+
"files": [
|
|
18
|
+
"src",
|
|
19
|
+
"types",
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "https://github.com/leaferjs/leafer-in.git"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://github.com/leaferjs/leafer-in/tree/main/packages/export",
|
|
27
|
+
"bugs": "https://github.com/leaferjs/leafer-in/issues",
|
|
28
|
+
"keywords": [
|
|
29
|
+
"leafer export",
|
|
30
|
+
"leafer-export",
|
|
31
|
+
"leafer-in",
|
|
32
|
+
"export",
|
|
33
|
+
"leafer-ui",
|
|
34
|
+
"leaferjs"
|
|
35
|
+
],
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"@leafer-ui/draw": "^1.3.0",
|
|
38
|
+
"@leafer-ui/interface": "^1.3.0",
|
|
39
|
+
"@leafer-in/interface": "^1.3.0"
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/canvas.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { IExportOptions, IExportImageType, IExportFileType, IBlob } from '@leafer-ui/interface'
|
|
2
|
+
import { LeaferCanvasBase, FileHelper, Platform, Debug } from '@leafer-ui/draw'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
const canvas = LeaferCanvasBase.prototype
|
|
6
|
+
const debug = Debug.get('@leafer-in/export')
|
|
7
|
+
|
|
8
|
+
canvas.export = function (filename: IExportFileType | string, options?: IExportOptions | number | boolean): string | Promise<any> {
|
|
9
|
+
const { quality, blob } = FileHelper.getExportOptions(options)
|
|
10
|
+
if (filename.includes('.')) return this.saveAs(filename, quality)
|
|
11
|
+
else if (blob) return this.toBlob(filename as IExportFileType, quality)
|
|
12
|
+
else return this.toDataURL(filename as IExportImageType, quality)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
canvas.toBlob = function (type?: IExportFileType, quality?: number): Promise<IBlob> {
|
|
16
|
+
return new Promise((resolve) => {
|
|
17
|
+
Platform.origin.canvasToBolb(this.view, type, quality).then((blob) => {
|
|
18
|
+
resolve(blob)
|
|
19
|
+
}).catch((e) => {
|
|
20
|
+
debug.error(e)
|
|
21
|
+
resolve(null)
|
|
22
|
+
})
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
canvas.toDataURL = function (type?: IExportImageType, quality?: number): string | Promise<string> {
|
|
27
|
+
return Platform.origin.canvasToDataURL(this.view, type, quality)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
canvas.saveAs = function (filename: string, quality?: number): Promise<boolean> {
|
|
31
|
+
return new Promise((resolve) => {
|
|
32
|
+
Platform.origin.canvasSaveAs(this.view, filename, quality).then(() => {
|
|
33
|
+
resolve(true)
|
|
34
|
+
}).catch((e) => {
|
|
35
|
+
debug.error(e)
|
|
36
|
+
resolve(false)
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
}
|
package/src/export.ts
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { IExportModule, IExportOptions, IExportResult, IExportResultFunction, IUI, IExportFileType, IFunction, IRenderOptions, IBoundsData, IBounds, ILocationType, ILeaf } from '@leafer-ui/interface'
|
|
2
|
+
import { Creator, Matrix, TaskProcessor, FileHelper, Bounds, Platform, MathHelper } from '@leafer-ui/draw'
|
|
3
|
+
|
|
4
|
+
import { getTrimBounds } from './trim'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export const ExportModule: IExportModule = {
|
|
8
|
+
|
|
9
|
+
export(leaf: IUI, filename: IExportFileType | string, options?: IExportOptions | number | boolean): Promise<IExportResult> {
|
|
10
|
+
|
|
11
|
+
this.running = true
|
|
12
|
+
|
|
13
|
+
const fileType = FileHelper.fileType(filename)
|
|
14
|
+
const isDownload = filename.includes('.')
|
|
15
|
+
options = FileHelper.getExportOptions(options)
|
|
16
|
+
|
|
17
|
+
return addTask((success: IExportResultFunction) =>
|
|
18
|
+
|
|
19
|
+
new Promise((resolve: IFunction) => {
|
|
20
|
+
|
|
21
|
+
const over = (result: IExportResult) => {
|
|
22
|
+
success(result)
|
|
23
|
+
resolve()
|
|
24
|
+
this.running = false
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const { toURL } = Platform
|
|
28
|
+
const { download } = Platform.origin
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
if (fileType === 'json') {
|
|
32
|
+
isDownload && download(toURL(JSON.stringify(leaf.toJSON(options.json)), 'text'), filename)
|
|
33
|
+
return over({ data: isDownload ? true : leaf.toJSON(options.json) })
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (fileType === 'svg') {
|
|
37
|
+
isDownload && download(toURL(leaf.toSVG(), 'svg'), filename)
|
|
38
|
+
return over({ data: isDownload ? true : leaf.toSVG() })
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
const { leafer } = leaf
|
|
43
|
+
if (leafer) {
|
|
44
|
+
|
|
45
|
+
checkLazy(leaf)
|
|
46
|
+
|
|
47
|
+
leafer.waitViewCompleted(async () => {
|
|
48
|
+
|
|
49
|
+
let renderBounds: IBoundsData, trimBounds: IBounds, scaleX = 1, scaleY = 1
|
|
50
|
+
const { worldTransform, isLeafer, isFrame } = leaf
|
|
51
|
+
const { slice, trim, onCanvas } = options
|
|
52
|
+
const smooth = options.smooth === undefined ? leafer.config.smooth : options.smooth
|
|
53
|
+
const contextSettings = options.contextSettings || leafer.config.contextSettings
|
|
54
|
+
|
|
55
|
+
const screenshot = options.screenshot || leaf.isApp
|
|
56
|
+
const fill = (isLeafer && screenshot) ? (options.fill === undefined ? leaf.fill : options.fill) : options.fill // leafer use
|
|
57
|
+
const needFill = FileHelper.isOpaqueImage(filename) || fill, matrix = new Matrix()
|
|
58
|
+
|
|
59
|
+
// 获取元素大小
|
|
60
|
+
if (screenshot) {
|
|
61
|
+
renderBounds = screenshot === true ? (isLeafer ? leafer.canvas.bounds : leaf.worldRenderBounds) : screenshot
|
|
62
|
+
} else {
|
|
63
|
+
let relative: ILocationType | ILeaf = options.relative || (isLeafer ? 'inner' : 'local')
|
|
64
|
+
|
|
65
|
+
scaleX = worldTransform.scaleX
|
|
66
|
+
scaleY = worldTransform.scaleY
|
|
67
|
+
|
|
68
|
+
switch (relative) {
|
|
69
|
+
case 'inner':
|
|
70
|
+
matrix.set(worldTransform)
|
|
71
|
+
break
|
|
72
|
+
case 'local':
|
|
73
|
+
matrix.set(worldTransform).divide(leaf.localTransform)
|
|
74
|
+
scaleX /= leaf.scaleX
|
|
75
|
+
scaleY /= leaf.scaleY
|
|
76
|
+
break
|
|
77
|
+
case 'world':
|
|
78
|
+
scaleX = 1
|
|
79
|
+
scaleY = 1
|
|
80
|
+
break
|
|
81
|
+
case 'page':
|
|
82
|
+
relative = leaf.leafer
|
|
83
|
+
default:
|
|
84
|
+
matrix.set(worldTransform).divide(leaf.getTransform(relative))
|
|
85
|
+
const l = relative.worldTransform
|
|
86
|
+
scaleX /= scaleX / l.scaleX
|
|
87
|
+
scaleY /= scaleY / l.scaleY
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
renderBounds = leaf.getBounds('render', relative)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
// 缩放元素
|
|
95
|
+
const scaleData = { scaleX: 1, scaleY: 1 }
|
|
96
|
+
MathHelper.getScaleData(options.scale, options.size, renderBounds, scaleData)
|
|
97
|
+
|
|
98
|
+
let pixelRatio = options.pixelRatio || 1
|
|
99
|
+
if (leaf.isApp) {
|
|
100
|
+
scaleData.scaleX *= pixelRatio // app 只能以自身的pixelRatio导出,需转移到scale上
|
|
101
|
+
scaleData.scaleY *= pixelRatio
|
|
102
|
+
pixelRatio = leaf.app.pixelRatio
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
// 导出元素
|
|
107
|
+
const { x, y, width, height } = new Bounds(renderBounds).scale(scaleData.scaleX, scaleData.scaleY)
|
|
108
|
+
const renderOptions: IRenderOptions = { matrix: matrix.scale(1 / scaleData.scaleX, 1 / scaleData.scaleY).invert().translate(-x, -y).withScale(1 / scaleX * scaleData.scaleX, 1 / scaleY * scaleData.scaleY) }
|
|
109
|
+
let canvas = Creator.canvas({ width: Math.round(width), height: Math.round(height), pixelRatio, smooth, contextSettings })
|
|
110
|
+
|
|
111
|
+
let sliceLeaf: IUI
|
|
112
|
+
if (slice) {
|
|
113
|
+
sliceLeaf = leaf
|
|
114
|
+
sliceLeaf.__worldOpacity = 0 // hide slice
|
|
115
|
+
|
|
116
|
+
leaf = leafer // render all in bounds
|
|
117
|
+
renderOptions.bounds = canvas.bounds
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
canvas.save()
|
|
122
|
+
|
|
123
|
+
if (isFrame && fill !== undefined) {
|
|
124
|
+
const oldFill = leaf.get('fill')
|
|
125
|
+
leaf.fill = ''
|
|
126
|
+
leaf.__render(canvas, renderOptions)
|
|
127
|
+
leaf.fill = oldFill as string
|
|
128
|
+
} else {
|
|
129
|
+
leaf.__render(canvas, renderOptions)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
canvas.restore()
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
if (sliceLeaf) sliceLeaf.__updateWorldOpacity() // show slice
|
|
136
|
+
|
|
137
|
+
if (trim) {
|
|
138
|
+
trimBounds = getTrimBounds(canvas)
|
|
139
|
+
const old = canvas, { width, height } = trimBounds
|
|
140
|
+
const config = { x: 0, y: 0, width, height, pixelRatio }
|
|
141
|
+
|
|
142
|
+
canvas = Creator.canvas(config)
|
|
143
|
+
canvas.copyWorld(old, trimBounds, config)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (needFill) canvas.fillWorld(canvas.bounds, fill || '#FFFFFF', 'destination-over')
|
|
147
|
+
if (onCanvas) onCanvas(canvas)
|
|
148
|
+
|
|
149
|
+
const data = filename === 'canvas' ? canvas : await canvas.export(filename, options)
|
|
150
|
+
over({ data, width: canvas.pixelWidth, height: canvas.pixelHeight, renderBounds, trimBounds })
|
|
151
|
+
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
} else {
|
|
155
|
+
|
|
156
|
+
over({ data: false })
|
|
157
|
+
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
let tasker: TaskProcessor
|
|
170
|
+
|
|
171
|
+
function addTask(task: IFunction): Promise<IExportResult> {
|
|
172
|
+
if (!tasker) tasker = new TaskProcessor()
|
|
173
|
+
|
|
174
|
+
return new Promise((resolve: IExportResultFunction) => {
|
|
175
|
+
tasker.add(async () => await task(resolve), { parallel: false })
|
|
176
|
+
})
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
function checkLazy(leaf: IUI): void {
|
|
181
|
+
if (leaf.__.__needComputePaint) leaf.__.__computePaint()
|
|
182
|
+
if (leaf.isBranch) leaf.children.forEach(child => checkLazy(child))
|
|
183
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { IExportFileType, IExportOptions, IExportResult } from '@leafer-ui/interface'
|
|
2
|
+
import { Export, UI } from '@leafer-ui/draw'
|
|
3
|
+
|
|
4
|
+
import { ExportModule } from './export'
|
|
5
|
+
import './canvas'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
Object.assign(Export, ExportModule)
|
|
9
|
+
|
|
10
|
+
UI.prototype.export = function (filename: IExportFileType | string, options?: IExportOptions | number | boolean): Promise<IExportResult> {
|
|
11
|
+
return Export.export(this, filename, options)
|
|
12
|
+
}
|
package/src/trim.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ITwoPointBoundsData, ILeaferCanvas, IBounds, ISizeData } from '@leafer-ui/interface'
|
|
2
|
+
import { TwoPointBoundsHelper, Bounds } from '@leafer-ui/draw'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
const { setPoint, addPoint, toBounds } = TwoPointBoundsHelper
|
|
6
|
+
|
|
7
|
+
export function getTrimBounds(canvas: ILeaferCanvas): IBounds {
|
|
8
|
+
const { width, height } = canvas.view as ISizeData
|
|
9
|
+
const { data } = canvas.context.getImageData(0, 0, width, height)
|
|
10
|
+
let x: number, y: number, pointBounds: ITwoPointBoundsData, index: number = 0
|
|
11
|
+
|
|
12
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
13
|
+
if (data[i + 3] !== 0) {
|
|
14
|
+
x = index % width
|
|
15
|
+
y = (index - x) / width
|
|
16
|
+
pointBounds ? addPoint(pointBounds, x, y) : setPoint(pointBounds = {} as ITwoPointBoundsData, x, y)
|
|
17
|
+
}
|
|
18
|
+
index++
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const bounds = new Bounds()
|
|
22
|
+
toBounds(pointBounds, bounds)
|
|
23
|
+
return bounds.scale(1 / canvas.pixelRatio).ceil()
|
|
24
|
+
}
|
package/types/index.d.ts
ADDED