mnfst 0.5.73 → 0.5.74
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/lib/manifest.export.js +47 -6
- package/package.json +1 -1
package/lib/manifest.export.js
CHANGED
|
@@ -150,11 +150,33 @@ function initializeExportPlugin() {
|
|
|
150
150
|
return `export-${ts}.${ext}`;
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
// Transparent 1×1 PNG, used as a fallback when an inline image fails to fetch.
|
|
154
|
+
// html-to-image would otherwise reject the whole snapshot on a single CORS or 404.
|
|
155
|
+
const TRANSPARENT_PIXEL = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkAAIAAAoAAv/lxKUAAAAASUVORK5CYII=';
|
|
156
|
+
|
|
157
|
+
// html-to-image rejects with the raw `image.onerror` Event when the assembled
|
|
158
|
+
// SVG fails to decode (oversized clones, malformed inline assets, etc.).
|
|
159
|
+
// Translate those into something readable before logging.
|
|
160
|
+
function describeExportError(err) {
|
|
161
|
+
if (err && err.message) return err;
|
|
162
|
+
if (err && typeof Event !== 'undefined' && err instanceof Event) {
|
|
163
|
+
const tag = err.target && err.target.tagName ? err.target.tagName.toLowerCase() : 'image';
|
|
164
|
+
return new Error(
|
|
165
|
+
`failed to render ${tag} during export. ` +
|
|
166
|
+
`Common causes: cross-origin images without CORS headers, ` +
|
|
167
|
+
`an oversized target element, or a no-target snapshot of a complex page. ` +
|
|
168
|
+
`Pass a "target" option to scope the snapshot.`
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
return new Error(String(err));
|
|
172
|
+
}
|
|
173
|
+
|
|
153
174
|
function emitError(format, err) {
|
|
154
|
-
|
|
175
|
+
const e = describeExportError(err);
|
|
176
|
+
console.error('[x-export] export failed:', e.message);
|
|
155
177
|
try {
|
|
156
178
|
window.dispatchEvent(new CustomEvent('manifest:export-error', {
|
|
157
|
-
detail: { format, error:
|
|
179
|
+
detail: { format, error: e.message }
|
|
158
180
|
}));
|
|
159
181
|
} catch { /* ignore */ }
|
|
160
182
|
}
|
|
@@ -194,6 +216,10 @@ function initializeExportPlugin() {
|
|
|
194
216
|
const out = {
|
|
195
217
|
pixelRatio: Number(opts.resolution) > 0 ? Number(opts.resolution) : 2,
|
|
196
218
|
cacheBust: true,
|
|
219
|
+
// A single cross-origin image without CORS headers would otherwise
|
|
220
|
+
// reject the whole snapshot. Substitute a transparent pixel so the
|
|
221
|
+
// export still produces a usable file.
|
|
222
|
+
imagePlaceholder: TRANSPARENT_PIXEL,
|
|
197
223
|
filter
|
|
198
224
|
};
|
|
199
225
|
if (opts.backgroundColor) out.backgroundColor = opts.backgroundColor;
|
|
@@ -202,20 +228,35 @@ function initializeExportPlugin() {
|
|
|
202
228
|
return out;
|
|
203
229
|
}
|
|
204
230
|
|
|
231
|
+
// Run an html-to-image call once. If it rejects with a raw Event (the SVG
|
|
232
|
+
// failed to decode — usually a font or oversized clone issue), retry once
|
|
233
|
+
// with safer options before propagating the error. Most real-world failures
|
|
234
|
+
// recover on the second attempt.
|
|
235
|
+
async function snapshotWithFallback(lib, fn, target, so) {
|
|
236
|
+
try {
|
|
237
|
+
return await lib[fn](target, so);
|
|
238
|
+
} catch (err) {
|
|
239
|
+
if (err && err.message) throw err; // a real Error — don't mask it
|
|
240
|
+
const safer = { ...so, skipFonts: true, pixelRatio: Math.min(so.pixelRatio || 2, 1) };
|
|
241
|
+
return await lib[fn](target, safer);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
205
245
|
async function exportImage(opts, filename, ext) {
|
|
206
246
|
const lib = await loadHtmlToImage();
|
|
207
247
|
const target = resolveTarget(opts);
|
|
208
248
|
const so = snapshotOptions(opts);
|
|
209
249
|
let dataUrl;
|
|
210
|
-
if (ext === 'png') dataUrl = await lib
|
|
250
|
+
if (ext === 'png') dataUrl = await snapshotWithFallback(lib, 'toPng', target, so);
|
|
211
251
|
else if (ext === 'jpeg') {
|
|
212
252
|
if (!so.backgroundColor) so.backgroundColor = effectivePageBackground();
|
|
213
253
|
so.quality = Number(opts.quality) > 0 && Number(opts.quality) <= 1
|
|
214
254
|
? Number(opts.quality)
|
|
215
255
|
: 0.95;
|
|
216
|
-
dataUrl = await lib
|
|
256
|
+
dataUrl = await snapshotWithFallback(lib, 'toJpeg', target, so);
|
|
217
257
|
} else if (ext === 'webp') {
|
|
218
|
-
const
|
|
258
|
+
const webpOpts = { ...so, type: 'image/webp', quality: opts.quality || 0.95 };
|
|
259
|
+
const blob = await snapshotWithFallback(lib, 'toBlob', target, webpOpts);
|
|
219
260
|
dataUrl = URL.createObjectURL(blob);
|
|
220
261
|
}
|
|
221
262
|
triggerDownload(dataUrl, filename);
|
|
@@ -226,7 +267,7 @@ function initializeExportPlugin() {
|
|
|
226
267
|
const target = resolveTarget(opts);
|
|
227
268
|
const so = snapshotOptions(opts);
|
|
228
269
|
if (!so.backgroundColor) so.backgroundColor = effectivePageBackground();
|
|
229
|
-
const dataUrl = await imgLib
|
|
270
|
+
const dataUrl = await snapshotWithFallback(imgLib, 'toPng', target, so);
|
|
230
271
|
const img = await loadImage(dataUrl);
|
|
231
272
|
const orientation = img.width > img.height ? 'landscape' : 'portrait';
|
|
232
273
|
const pdf = new jsPDFCtor({ orientation, unit: 'pt', format: opts.pageSize || 'a4' });
|