Recover Android note image rendering failures

This commit is contained in:
tianma-if committed 2026-10-08 17:05:43 +08:00
1 parent 6cb09f4007
commit cd07bc0e68
2 files changed
+43 -4

No files matched your search

@@ -1253,6 +1253,7 @@ function LocalTiptapEditorImpl(props: LocalTiptapEditorProps) {
const reportProgress = (stage: "prepare" | "render" | "transfer") => {
void Promise.resolve(notify({ type: "progress", stage })).catch(() => {});
};
const missingImagePlaceholder = "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=";
void (async () => {
reportProgress("prepare");
@@ -1319,7 +1320,9 @@ function LocalTiptapEditorImpl(props: LocalTiptapEditorProps) {
const failedImages = exportedImages.filter((image) => !image.complete || image.naturalWidth === 0).length;
const totalHeight = Math.max(1, Math.ceil(documentRoot.getBoundingClientRect().height));
const renderPlan = planMobileNoteImageRender(targetWidth, totalHeight);
let embedFailedImages = 0;
let captureRoot = documentRoot;
let captureWrapper: HTMLDivElement | null = null;
if (renderPlan.sourceScale < 1) {
const wrapper = document.createElement("div");
wrapper.style.cssText = `position:relative;width:${renderPlan.sourceWidth}px;height:${renderPlan.sourceHeight}px;overflow:hidden;`;
@@ -1328,18 +1331,50 @@ function LocalTiptapEditorImpl(props: LocalTiptapEditorProps) {
documentRoot.style.transform = `scale(${renderPlan.sourceScale})`;
documentRoot.style.transformOrigin = "top left";
captureRoot = wrapper;
captureWrapper = wrapper;
}
const backgroundColor = NOTE_IMAGE_BACKGROUND_COLORS[resolvedTheme] || themeCfg.canvasBg;
reportProgress("render");
const canvas = await toCanvas(captureRoot, {
const renderCanvas = (root: HTMLElement, width: number, height: number) => toCanvas(root, {
backgroundColor,
cacheBust: false,
height: renderPlan.sourceHeight,
height,
imagePlaceholder: missingImagePlaceholder,
onImageErrorHandler: (event) => {
embedFailedImages += 1;
if (event instanceof Event && event.target instanceof HTMLImageElement) {
event.target.src = missingImagePlaceholder;
}
},
pixelRatio: renderPlan.pixelRatio,
skipFonts: true,
width: renderPlan.sourceWidth,
width,
});
let canvas: HTMLCanvasElement;
try {
canvas = await renderCanvas(captureRoot, renderPlan.sourceWidth, renderPlan.sourceHeight);
} catch {
// Android WebView can reject a large SVG image without an Error message.
// Try a smaller source once while keeping the entire card visible.
const retryScale = renderPlan.sourceScale * 0.75;
const retryWidth = Math.max(1, Math.floor(targetWidth * retryScale));
const retryHeight = Math.max(1, Math.floor(totalHeight * retryScale));
if (retryScale < 0.5) throw new Error("NOTE_IMAGE_RENDER_FAILED");
if (!captureWrapper) {
captureWrapper = document.createElement("div");
documentRoot.replaceWith(captureWrapper);
captureWrapper.appendChild(documentRoot);
}
captureWrapper.style.cssText = `position:relative;width:${retryWidth}px;height:${retryHeight}px;overflow:hidden;`;
documentRoot.style.transform = `scale(${retryScale})`;
documentRoot.style.transformOrigin = "top left";
try {
canvas = await renderCanvas(captureWrapper, retryWidth, retryHeight);
} catch {
throw new Error("NOTE_IMAGE_RENDER_FAILED");
}
}
const blob = await new Promise<Blob>((resolve, reject) => {
canvas.toBlob(
(result) => result ? resolve(result) : reject(new Error("Image renderer returned an empty file")),
@@ -1365,7 +1400,7 @@ function LocalTiptapEditorImpl(props: LocalTiptapEditorProps) {
width: canvas.width,
height: canvas.height,
totalImages: exportedImages.length,
failedImages,
failedImages: Math.min(exportedImages.length, failedImages + embedFailedImages),
});
} catch (error) {
await notify({ type: "error", message: error instanceof Error ? error.message : "Image export failed" });
@@ -826,6 +826,10 @@ export const MemoDetailModal = ({
? (resolvedLocale !== "zh-CN"
? "This note is too long for one readable image. Choose a smaller font size or split the note."
: "这篇笔记太长,无法生成清晰的单张图片。请调小导出字号,或拆分笔记后重试。")
: message === "NOTE_IMAGE_RENDER_FAILED"
? (resolvedLocale !== "zh-CN"
? "This image could not be rendered on this device. Try a smaller font size or a shorter note."
: "这台设备无法渲染这张图片。请调小导出字号,或缩短笔记后重试。")
: message;
Alert.alert(
resolvedLocale !== "zh-CN" ? "Image export failed" : "导出笔记图片失败",