Fix SVG images

This commit is contained in:
Yuri Papouski 2022-09-08 11:33:44 +00:00
parent 2d03b6e8a2
commit 357a9a029f
2 changed files with 58 additions and 38 deletions

View File

@ -4,39 +4,47 @@ import {serializeSvg, deserializeSvg} from '../../core/features';
export class ImageElementContainer extends ElementContainer { export class ImageElementContainer extends ElementContainer {
src: string; src: string;
intrinsicWidth: number; intrinsicWidth: number = 0;
intrinsicHeight: number; intrinsicHeight: number = 0;
isSVG: boolean;
private static INLINE_SVG = /^data:image\/svg\+xml/i; private static SVG = /\.svg$/i;
private static INLINED_SVG = /^data:image\/svg\+xml/i;
private static IS_FIRE_FOX = /firefox/i.test(navigator?.userAgent); private static IS_FIRE_FOX = /firefox/i.test(navigator?.userAgent);
constructor(context: Context, img: HTMLImageElement) { constructor(context: Context, img: HTMLImageElement) {
super(context, img); super(context, img);
this.src = img.currentSrc || img.src; this.src = img.currentSrc || img.src;
this.intrinsicWidth = img.naturalWidth; this.isSVG = this.isSvg() || this.isInlinedSvg();
this.intrinsicHeight = img.naturalHeight;
this.update();
this.context.cache.addImage(this.src); this.context.cache.addImage(this.src);
} }
private update() { private isInlinedSvg = () => ImageElementContainer.INLINED_SVG.test(this.src);
if (!this.intrinsicWidth || !this.intrinsicHeight || ImageElementContainer.IS_FIRE_FOX) { private isSvg = () => ImageElementContainer.SVG.test(this.src);
if (ImageElementContainer.INLINE_SVG.test(this.src)) {
public setup(img: HTMLImageElement) {
if (this.isSvg()) return;
if (this.isInlinedSvg()) {
const [, inlinedSvg] = this.src.split(','); const [, inlinedSvg] = this.src.split(',');
const svgElement = deserializeSvg(inlinedSvg); const svgElement = deserializeSvg(inlinedSvg);
const { const {
width: {baseVal: widthBaseVal}, width: {baseVal: widthBaseVal},
height: {baseVal: heightBaseVal} height: {baseVal: heightBaseVal}
} = svgElement; } = svgElement;
if (ImageElementContainer.IS_FIRE_FOX) {
widthBaseVal.valueAsString = widthBaseVal.value.toString(); widthBaseVal.valueAsString = widthBaseVal.value.toString();
heightBaseVal.valueAsString = heightBaseVal.value.toString(); heightBaseVal.valueAsString = heightBaseVal.value.toString();
this.src = serializeSvg(svgElement, 'base64'); img.src = serializeSvg(svgElement, 'base64');
}
this.intrinsicWidth = widthBaseVal.value; this.intrinsicWidth = widthBaseVal.value;
this.intrinsicHeight = heightBaseVal.value; this.intrinsicHeight = heightBaseVal.value;
return; return;
} }
}
this.intrinsicWidth = img.naturalWidth;
this.intrinsicHeight = img.naturalHeight;
} }
} }

View File

@ -270,12 +270,18 @@ export class CanvasRenderer extends Renderer {
curves: BoundCurves, curves: BoundCurves,
image: HTMLImageElement | HTMLCanvasElement image: HTMLImageElement | HTMLCanvasElement
): void { ): void {
if (image && container.intrinsicWidth > 0 && container.intrinsicHeight > 0) { if (image) {
const isContainerWSizes = container.intrinsicWidth > 0 && container.intrinsicHeight > 0;
const isSVGContainer =
container instanceof SVGElementContainer ||
(container instanceof ImageElementContainer && container.isSVG);
if (isContainerWSizes || isSVGContainer) {
const box = contentBox(container); const box = contentBox(container);
const path = calculatePaddingBoxPath(curves); const path = calculatePaddingBoxPath(curves);
this.path(path); this.path(path);
this.ctx.save(); this.ctx.save();
this.ctx.clip(); this.ctx.clip();
if (isContainerWSizes) {
this.ctx.drawImage( this.ctx.drawImage(
image, image,
0, 0,
@ -287,9 +293,14 @@ export class CanvasRenderer extends Renderer {
box.width, box.width,
box.height box.height
); );
} else {
// As usual it won't work in FF. https://bugzilla.mozilla.org/show_bug.cgi?id=700533
this.ctx.drawImage(image, box.left, box.top, box.width, box.height);
}
this.ctx.restore(); this.ctx.restore();
} }
} }
}
async renderNodeContent(paint: ElementPaint): Promise<void> { async renderNodeContent(paint: ElementPaint): Promise<void> {
this.applyEffects(paint.getEffects(EffectTarget.CONTENT)); this.applyEffects(paint.getEffects(EffectTarget.CONTENT));
@ -303,6 +314,7 @@ export class CanvasRenderer extends Renderer {
if (container instanceof ImageElementContainer) { if (container instanceof ImageElementContainer) {
try { try {
const image = await this.context.cache.match(container.src); const image = await this.context.cache.match(container.src);
container.setup(image);
this.renderReplacedElement(container, curves, image); this.renderReplacedElement(container, curves, image);
} catch (e) { } catch (e) {
this.context.logger.error(`Error loading image ${container.src}`); this.context.logger.error(`Error loading image ${container.src}`);