From 007a73293a1394e1d08102a0ea7fab38e9c90e73 Mon Sep 17 00:00:00 2001 From: Sebastien Ponce Date: Thu, 12 Jan 2023 18:19:30 +0100 Subject: [PATCH 1/3] Added test of external svg with no width/height attribute --- tests/assets/imageViewPortOnly.svg | 46 ++++++++++++++++++++++++++ tests/reftests/images/svg/nowidth.html | 17 ++++++++++ 2 files changed, 63 insertions(+) create mode 100644 tests/assets/imageViewPortOnly.svg create mode 100644 tests/reftests/images/svg/nowidth.html diff --git a/tests/assets/imageViewPortOnly.svg b/tests/assets/imageViewPortOnly.svg new file mode 100644 index 000000000..03d44c20f --- /dev/null +++ b/tests/assets/imageViewPortOnly.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SVG + + + + + + + + + + diff --git a/tests/reftests/images/svg/nowidth.html b/tests/reftests/images/svg/nowidth.html new file mode 100644 index 000000000..a85a47555 --- /dev/null +++ b/tests/reftests/images/svg/nowidth.html @@ -0,0 +1,17 @@ + + + + Image tests + + + + + + SVG taints image:
+ + + From 9bc852f9f5d0322f1c65dc6f6703b12fd92db0ec Mon Sep 17 00:00:00 2001 From: Sebastien Ponce Date: Thu, 12 Jan 2023 11:40:28 +0100 Subject: [PATCH 2/3] Fixed handling of svg images with no width/height attributes Such images were not appearing in resulting canvas when using firefox. The details of the problem are explained in https://bugzilla.mozilla.org/show_bug.cgi?id=700533 and https://webcompat.com/issues/64352 but a rough summary is that firefox won't render svg with no width/height attribute via drawImage. This fix thus recreates the missing attributes from the viewport one when they are missing --- src/render/canvas/canvas-renderer.ts | 82 ++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 24 deletions(-) diff --git a/src/render/canvas/canvas-renderer.ts b/src/render/canvas/canvas-renderer.ts index 6efb648bf..f2ea6da1a 100644 --- a/src/render/canvas/canvas-renderer.ts +++ b/src/render/canvas/canvas-renderer.ts @@ -265,29 +265,64 @@ export class CanvasRenderer extends Renderer { }); } - renderReplacedElement( + async fixSVGImage( + container: ReplacedElementContainer, + image: HTMLImageElement | HTMLCanvasElement + ): Promise { + if (image instanceof HTMLImageElement && container.intrinsicWidth == 0 && container.intrinsicHeight == 0) { + // in such case we will to use the viewport attribute + // and we preprocess the svg image and add the width/height svg attributes to it so that drawImage works on firefox + // See https://bugzilla.mozilla.org/show_bug.cgi?id=700533 and https://webcompat.com/issues/64352 + var img: HTMLImageElement = image; + var response = await fetch(img.src); + var str = await response.text(); + var doc = new window.DOMParser().parseFromString(str, 'text/xml'); + var svgElem = doc.documentElement; + if (svgElem) { + var viewBox = svgElem.getAttribute('viewBox'); + var match = viewBox?.match(/\w+ \w+ (\w+) (\w+)/); + if (match) { + var viewBoxWidth = match[1]; + var viewBoxHeight = match[2]; + container.intrinsicWidth = +viewBoxWidth; + container.intrinsicHeight = +viewBoxHeight; + svgElem.setAttribute('width', viewBoxWidth); + svgElem.setAttribute('height', viewBoxHeight); + var svgData = new XMLSerializer().serializeToString(svgElem); + img.src = 'data:image/svg+xml;base64,' + btoa(svgData); + } + } + } + return image; + } + + async renderReplacedElement( container: ReplacedElementContainer, curves: BoundCurves, image: HTMLImageElement | HTMLCanvasElement - ): void { - if (image && container.intrinsicWidth > 0 && container.intrinsicHeight > 0) { - const box = contentBox(container); - const path = calculatePaddingBoxPath(curves); - this.path(path); - this.ctx.save(); - this.ctx.clip(); - this.ctx.drawImage( - image, - 0, - 0, - container.intrinsicWidth, - container.intrinsicHeight, - box.left, - box.top, - box.width, - box.height - ); - this.ctx.restore(); + ) { + if (image) { + // Special fix for displaying svg images with no width/height attributes + var img = await this.fixSVGImage(container, image); + if (container.intrinsicWidth > 0 && container.intrinsicHeight > 0) { + const box = contentBox(container); + const path = calculatePaddingBoxPath(curves); + this.path(path); + this.ctx.save(); + this.ctx.clip(); + this.ctx.drawImage( + img, + 0, + 0, + container.intrinsicWidth, + container.intrinsicHeight, + box.left, + box.top, + box.width, + box.height + ); + this.ctx.restore(); + } } } @@ -303,20 +338,20 @@ export class CanvasRenderer extends Renderer { if (container instanceof ImageElementContainer) { try { const image = await this.context.cache.match(container.src); - this.renderReplacedElement(container, curves, image); + await this.renderReplacedElement(container, curves, image); } catch (e) { this.context.logger.error(`Error loading image ${container.src}`); } } if (container instanceof CanvasElementContainer) { - this.renderReplacedElement(container, curves, container.canvas); + await this.renderReplacedElement(container, curves, container.canvas); } if (container instanceof SVGElementContainer) { try { const image = await this.context.cache.match(container.svg); - this.renderReplacedElement(container, curves, image); + await this.renderReplacedElement(container, curves, image); } catch (e) { this.context.logger.error(`Error loading svg ${container.svg.substring(0, 255)}`); } @@ -903,7 +938,6 @@ export class CanvasRenderer extends Renderer { } const stack = parseStackingContexts(element); - await this.renderStack(stack); this.applyEffects([]); return this.canvas; From f7a3566f222074630f10e94473f25cb68cb6bbc8 Mon Sep 17 00:00:00 2001 From: Sebastien Ponce Date: Thu, 12 Jan 2023 18:29:29 +0100 Subject: [PATCH 3/3] DO NOT MERGE : bad workaroud to remaning bug This adds a sleep in renderReplacedElement so that drawimage works properly when the fix to svg files is used. Without that, it looks like drawImage fires too fast and the fix is not yet done (code is async here) So far I did not find out where I messed up with async code. Any help would be welcome ! --- src/render/canvas/canvas-renderer.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/render/canvas/canvas-renderer.ts b/src/render/canvas/canvas-renderer.ts index f2ea6da1a..9b3c39dd4 100644 --- a/src/render/canvas/canvas-renderer.ts +++ b/src/render/canvas/canvas-renderer.ts @@ -265,6 +265,9 @@ export class CanvasRenderer extends Renderer { }); } + sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); + } async fixSVGImage( container: ReplacedElementContainer, image: HTMLImageElement | HTMLCanvasElement @@ -310,6 +313,7 @@ export class CanvasRenderer extends Renderer { this.path(path); this.ctx.save(); this.ctx.clip(); + await this.sleep(100); this.ctx.drawImage( img, 0,