From 0d04b19e48d10fe5cd6eb39863c4044c24780dac Mon Sep 17 00:00:00 2001 From: kanalin Date: Tue, 18 Nov 2025 17:27:07 +0800 Subject: [PATCH] feat: add onImageError callback for handling image load errors --- src/context/context.ts | 1 + src/nodes/image.ts | 36 ++++++++++++++++++++++++++++++++---- src/svg2pdf.ts | 13 +++++++++++++ types.d.ts | 14 ++++++++++++++ 4 files changed, 60 insertions(+), 4 deletions(-) diff --git a/src/context/context.ts b/src/context/context.ts index a9993854..56533a45 100644 --- a/src/context/context.ts +++ b/src/context/context.ts @@ -90,4 +90,5 @@ export interface Svg2pdfParameters { width?: number height?: number loadExternalStyleSheets?: boolean + onImageError?: (imageUrl: string, error: Error, element: Element) => boolean } diff --git a/src/nodes/image.ts b/src/nodes/image.ts index 9f3da102..3e8a748f 100644 --- a/src/nodes/image.ts +++ b/src/nodes/image.ts @@ -41,7 +41,26 @@ export class ImageNode extends GraphicsNode { return } - const { data, format } = await this.imageLoadingPromise + let data + let format + try { + const res = await this.imageLoadingPromise + data = res.data + format = res.format + } catch (e) { + const error = e instanceof Error ? e : new Error(String(e)) + const onImageError = context.svg2pdfParameters?.onImageError + if (onImageError && this.imageUrl) { + const shouldThrow = onImageError(this.imageUrl, error, this.element) + if (shouldThrow === true) { + throw error + } + } + } + + if (!data || !format) { + return + } if (format.indexOf('svg') === 0) { const parser = new DOMParser() @@ -99,9 +118,18 @@ export class ImageNode extends GraphicsNode { imgHeight ) } catch (e) { - typeof console === 'object' && - console.warn && - console.warn(`Could not load image ${this.imageUrl}. \n${e}`) + const error = e instanceof Error ? e : new Error(String(e)) + const onImageError = context.svg2pdfParameters?.onImageError + if (onImageError && this.imageUrl) { + const shouldThrow = onImageError(this.imageUrl, error, this.element) + if (shouldThrow === true) { + throw error + } + } else { + typeof console === 'object' && + console.warn && + console.warn(`Could not load image ${this.imageUrl}. \n${e}`) + } } } } diff --git a/src/svg2pdf.ts b/src/svg2pdf.ts index f805b937..16e1fc65 100644 --- a/src/svg2pdf.ts +++ b/src/svg2pdf.ts @@ -77,4 +77,17 @@ export interface Svg2PdfOptions { width?: number height?: number loadExternalStyleSheets?: boolean + /** + * Optional callback function that is called when an image fails to load. + * If provided, this callback will be invoked with error details instead of + * silently ignoring the error. The callback can decide how to handle the error + * (e.g., throw an exception, log to a custom logging system, or provide fallback behavior). + * + * @param imageUrl - The URL of the image that failed to load + * @param error - The error object describing what went wrong + * @param element - The SVG image element that failed to load + * @returns If the callback returns `true`, the error will be re-thrown to interrupt the rendering process. + * If it returns `false`, the error will be silently ignored and the image will be skipped. + */ + onImageError?: (imageUrl: string, error: Error, element: Element) => boolean } diff --git a/types.d.ts b/types.d.ts index abef38e4..ce3444cd 100644 --- a/types.d.ts +++ b/types.d.ts @@ -67,4 +67,18 @@ export interface Svg2pdfOptions { * policies are ignored. The default is false. */ loadExternalStyleSheets?: boolean + + /** + * Optional callback function that is called when an image fails to load. + * If provided, this callback will be invoked with error details instead of + * silently ignoring the error. The callback can decide how to handle the error + * (e.g., throw an exception, log to a custom logging system, or provide fallback behavior). + * + * @param imageUrl - The URL of the image that failed to load + * @param error - The error object describing what went wrong + * @param element - The SVG image element that failed to load + * @returns If the callback returns `true`, the error will be re-thrown to interrupt the rendering process. + * If it returns `false`, the error will be silently ignored and the image will be skipped. + */ + onImageError?: (imageUrl: string, error: Error, element: Element) => boolean }