Skip to content

Commit

Permalink
fix: Some svg image sizes are drawn incorrectly
Browse files Browse the repository at this point in the history
  • Loading branch information
ppchart committed Oct 17, 2022
1 parent 6020386 commit 99390d2
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 59 deletions.
103 changes: 48 additions & 55 deletions src/render/canvas/canvas-renderer.ts
@@ -1,49 +1,49 @@
import {ElementPaint, parseStackingContexts, StackingContext} from '../stacking-context';
import {asString, Color, isTransparent} from '../../css/types/color';
import {ElementContainer, FLAGS} from '../../dom/element-container';
import {BORDER_STYLE} from '../../css/property-descriptors/border-style';
import {CSSParsedDeclaration} from '../../css';
import {TextContainer} from '../../dom/text-container';
import {Path, transformPath} from '../path';
import {BACKGROUND_CLIP} from '../../css/property-descriptors/background-clip';
import {BoundCurves, calculateBorderBoxPath, calculateContentBoxPath, calculatePaddingBoxPath} from '../bound-curves';
import {BezierCurve, isBezierCurve} from '../bezier-curve';
import {Vector} from '../vector';
import {CSSImageType, CSSURLImage, isLinearGradient, isRadialGradient} from '../../css/types/image';
import { ElementPaint, parseStackingContexts, StackingContext } from '../stacking-context';
import { asString, Color, isTransparent } from '../../css/types/color';
import { ElementContainer, FLAGS } from '../../dom/element-container';
import { BORDER_STYLE } from '../../css/property-descriptors/border-style';
import { CSSParsedDeclaration } from '../../css';
import { TextContainer } from '../../dom/text-container';
import { Path, transformPath } from '../path';
import { BACKGROUND_CLIP } from '../../css/property-descriptors/background-clip';
import { BoundCurves, calculateBorderBoxPath, calculateContentBoxPath, calculatePaddingBoxPath } from '../bound-curves';
import { BezierCurve, isBezierCurve } from '../bezier-curve';
import { Vector } from '../vector';
import { CSSImageType, CSSURLImage, isLinearGradient, isRadialGradient } from '../../css/types/image';
import {
parsePathForBorder,
parsePathForBorderDoubleInner,
parsePathForBorderDoubleOuter,
parsePathForBorderStroke
} from '../border';
import {calculateBackgroundRendering, getBackgroundValueForIndex} from '../background';
import {isDimensionToken} from '../../css/syntax/parser';
import {segmentGraphemes, TextBounds} from '../../css/layout/text';
import {ImageElementContainer} from '../../dom/replaced-elements/image-element-container';
import {contentBox} from '../box-sizing';
import {CanvasElementContainer} from '../../dom/replaced-elements/canvas-element-container';
import {SVGElementContainer} from '../../dom/replaced-elements/svg-element-container';
import {ReplacedElementContainer} from '../../dom/replaced-elements';
import {EffectTarget, IElementEffect, isClipEffect, isOpacityEffect, isTransformEffect} from '../effects';
import {contains} from '../../core/bitwise';
import {calculateGradientDirection, calculateRadius, processColorStops} from '../../css/types/functions/gradient';
import {FIFTY_PERCENT, getAbsoluteValue} from '../../css/types/length-percentage';
import {TEXT_DECORATION_LINE} from '../../css/property-descriptors/text-decoration-line';
import {FontMetrics} from '../font-metrics';
import {DISPLAY} from '../../css/property-descriptors/display';
import {Bounds} from '../../css/layout/bounds';
import {LIST_STYLE_TYPE} from '../../css/property-descriptors/list-style-type';
import {computeLineHeight} from '../../css/property-descriptors/line-height';
import {CHECKBOX, INPUT_COLOR, InputElementContainer, RADIO} from '../../dom/replaced-elements/input-element-container';
import {TEXT_ALIGN} from '../../css/property-descriptors/text-align';
import {TextareaElementContainer} from '../../dom/elements/textarea-element-container';
import {SelectElementContainer} from '../../dom/elements/select-element-container';
import {IFrameElementContainer} from '../../dom/replaced-elements/iframe-element-container';
import {TextShadow} from '../../css/property-descriptors/text-shadow';
import {PAINT_ORDER_LAYER} from '../../css/property-descriptors/paint-order';
import {Renderer} from '../renderer';
import {Context} from '../../core/context';
import {DIRECTION} from '../../css/property-descriptors/direction';
import { calculateBackgroundRendering, getBackgroundValueForIndex } from '../background';
import { isDimensionToken } from '../../css/syntax/parser';
import { segmentGraphemes, TextBounds } from '../../css/layout/text';
import { ImageElementContainer } from '../../dom/replaced-elements/image-element-container';
import { contentBox } from '../box-sizing';
import { CanvasElementContainer } from '../../dom/replaced-elements/canvas-element-container';
import { SVGElementContainer } from '../../dom/replaced-elements/svg-element-container';
import { ReplacedElementContainer } from '../../dom/replaced-elements';
import { EffectTarget, IElementEffect, isClipEffect, isOpacityEffect, isTransformEffect } from '../effects';
import { contains } from '../../core/bitwise';
import { calculateGradientDirection, calculateRadius, processColorStops } from '../../css/types/functions/gradient';
import { FIFTY_PERCENT, getAbsoluteValue } from '../../css/types/length-percentage';
import { TEXT_DECORATION_LINE } from '../../css/property-descriptors/text-decoration-line';
import { FontMetrics } from '../font-metrics';
import { DISPLAY } from '../../css/property-descriptors/display';
import { Bounds } from '../../css/layout/bounds';
import { LIST_STYLE_TYPE } from '../../css/property-descriptors/list-style-type';
import { computeLineHeight } from '../../css/property-descriptors/line-height';
import { CHECKBOX, INPUT_COLOR, InputElementContainer, RADIO } from '../../dom/replaced-elements/input-element-container';
import { TEXT_ALIGN } from '../../css/property-descriptors/text-align';
import { TextareaElementContainer } from '../../dom/elements/textarea-element-container';
import { SelectElementContainer } from '../../dom/elements/select-element-container';
import { IFrameElementContainer } from '../../dom/replaced-elements/iframe-element-container';
import { TextShadow } from '../../css/property-descriptors/text-shadow';
import { PAINT_ORDER_LAYER } from '../../css/property-descriptors/paint-order';
import { Renderer } from '../renderer';
import { Context } from '../../core/context';
import { DIRECTION } from '../../css/property-descriptors/direction';

export type RenderConfigurations = RenderOptions & {
backgroundColor: Color | null;
Expand Down Expand Up @@ -181,7 +181,7 @@ export class CanvasRenderer extends Renderer {
this.ctx.direction = styles.direction === DIRECTION.RTL ? 'rtl' : 'ltr';
this.ctx.textAlign = 'left';
this.ctx.textBaseline = 'alphabetic';
const {baseline, middle} = this.fontMetrics.getMetrics(fontFamily, fontSize);
const { baseline, middle } = this.fontMetrics.getMetrics(fontFamily, fontSize);
const paintOrder = styles.paintOrder;

text.textBounds.forEach((text) => {
Expand Down Expand Up @@ -278,10 +278,6 @@ export class CanvasRenderer extends Renderer {
this.ctx.clip();
this.ctx.drawImage(
image,
0,
0,
container.intrinsicWidth,
container.intrinsicHeight,
box.left,
box.top,
box.width,
Expand Down Expand Up @@ -389,7 +385,7 @@ export class CanvasRenderer extends Renderer {

if (isTextInputElement(container) && container.value.length) {
const [fontFamily, fontSize] = this.createFontStyle(styles);
const {baseline} = this.fontMetrics.getMetrics(fontFamily, fontSize);
const { baseline } = this.fontMetrics.getMetrics(fontFamily, fontSize);

this.ctx.font = fontFamily;
this.ctx.fillStyle = asString(styles.color);
Expand Down Expand Up @@ -569,16 +565,13 @@ export class CanvasRenderer extends Renderer {
}

resizeImage(image: HTMLImageElement, width: number, height: number): HTMLCanvasElement | HTMLImageElement {
if (image.width === width && image.height === height) {
return image;
}

const ownerDocument = this.canvas.ownerDocument ?? document;
const canvas = ownerDocument.createElement('canvas');
canvas.width = Math.max(1, width);
canvas.height = Math.max(1, height);
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, width, height);
ctx.scale(width / image.width, height / image.height)
ctx.drawImage(image, 0, 0, image.width, image.height);
return canvas;
}

Expand Down Expand Up @@ -696,10 +689,10 @@ export class CanvasRenderer extends Renderer {
const hasBackground = !isTransparent(styles.backgroundColor) || styles.backgroundImage.length;

const borders = [
{style: styles.borderTopStyle, color: styles.borderTopColor, width: styles.borderTopWidth},
{style: styles.borderRightStyle, color: styles.borderRightColor, width: styles.borderRightWidth},
{style: styles.borderBottomStyle, color: styles.borderBottomColor, width: styles.borderBottomWidth},
{style: styles.borderLeftStyle, color: styles.borderLeftColor, width: styles.borderLeftWidth}
{ style: styles.borderTopStyle, color: styles.borderTopColor, width: styles.borderTopWidth },
{ style: styles.borderRightStyle, color: styles.borderRightColor, width: styles.borderRightWidth },
{ style: styles.borderBottomStyle, color: styles.borderBottomColor, width: styles.borderBottomWidth },
{ style: styles.borderLeftStyle, color: styles.borderLeftColor, width: styles.borderLeftWidth }
];

const backgroundPaintingArea = calculateBackgroundCurvedPaintingArea(
Expand Down
42 changes: 42 additions & 0 deletions tests/reftests/images/svg/background.html
@@ -0,0 +1,42 @@
<!DOCTYPE html>
<html>

<head>
<title>Base64 svg</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../../../test.js"></script>
<style>
body {
font-family: Arial;
}

#svg1:before {
content: '';
width: 125px;
height: 150px;
display: inline-block;
background-size: 125px 150px;
background-image: url('data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA3OC4zMSA5My44MiIgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6cmVkIj4NCgk8ZGVmcz4NCgkJPHN0eWxlPg0KCQkJLmNscy0xe2ZpbGw6IzEzYTNkNzt9DQoJCTwvc3R5bGU+DQoJPC9kZWZzPg0KCTx0aXRsZT4NCgkJbmV3cy1pY29uLWRqLXVwZGF0ZWQNCgk8L3RpdGxlPg0KCTxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTAsNDYuOTFWMEw1MS42NC4xM2wyLjIxLjU1YzEwLjcxLDIuNjgsMTguNTYsOS41MiwyMi40LDE5LjUxYTM0Ljc2LDM0Ljc2LDAsMCwxLDEuOTMsOC44NmwuMTMsMi4wOUg2Mi43MWwtLjEzLTFhMTYuMzksMTYuMzksMCwwLDAtMy4zMi04LjQsMTYuODIsMTYuODIsMCwwLDAtNy44My01LjQ4bC0xLjY5LS41OC0zNC0uMTJ2NjIuN0gzMS4zNFY5My44MkgwVjQ2LjkxWiIgLz4NCgk8cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik00My4xMiw4Ni4xVjc4LjMybDIuNzktLjE0QTMyLjA2LDMyLjA2LDAsMCwwLDUwLDc3LjczLDE3LjEsMTcuMSwwLDAsMCw2MC45LDY5LjE0YzEuNy0zLjQzLDEuNjQtMi44OSwxLjczLTE1LjIzbC4wOC0xMUg3OC4yN2wtLjE2LDIzLjg3TDc3LjU3LDY5QTMxLjA5LDMxLjA5LDAsMCwxLDY5LDg0LjM2LDMxLjM1LDMxLjM1LDAsMCwxLDU2LjgzLDkyYTI5LjQyLDI5LjQyLDAsMCwxLTEwLDEuNzRsLTMuNzMuMTJWODYuMVoiIC8+DQo8L3N2Zz4NCg==');
}
#svg2:before {
content: '';
width: 64px;
height: 64px;
display: inline-block;
background-size: 64px 64px;
background-image: url('data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA3OC4zMSA5My44MiIgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6cmVkIj4NCgk8ZGVmcz4NCgkJPHN0eWxlPg0KCQkJLmNscy0xe2ZpbGw6IzEzYTNkNzt9DQoJCTwvc3R5bGU+DQoJPC9kZWZzPg0KCTx0aXRsZT4NCgkJbmV3cy1pY29uLWRqLXVwZGF0ZWQNCgk8L3RpdGxlPg0KCTxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTAsNDYuOTFWMEw1MS42NC4xM2wyLjIxLjU1YzEwLjcxLDIuNjgsMTguNTYsOS41MiwyMi40LDE5LjUxYTM0Ljc2LDM0Ljc2LDAsMCwxLDEuOTMsOC44NmwuMTMsMi4wOUg2Mi43MWwtLjEzLTFhMTYuMzksMTYuMzksMCwwLDAtMy4zMi04LjQsMTYuODIsMTYuODIsMCwwLDAtNy44My01LjQ4bC0xLjY5LS41OC0zNC0uMTJ2NjIuN0gzMS4zNFY5My44MkgwVjQ2LjkxWiIgLz4NCgk8cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik00My4xMiw4Ni4xVjc4LjMybDIuNzktLjE0QTMyLjA2LDMyLjA2LDAsMCwwLDUwLDc3LjczLDE3LjEsMTcuMSwwLDAsMCw2MC45LDY5LjE0YzEuNy0zLjQzLDEuNjQtMi44OSwxLjczLTE1LjIzbC4wOC0xMUg3OC4yN2wtLjE2LDIzLjg3TDc3LjU3LDY5QTMxLjA5LDMxLjA5LDAsMCwxLDY5LDg0LjM2LDMxLjM1LDMxLjM1LDAsMCwxLDU2LjgzLDkyYTI5LjQyLDI5LjQyLDAsMCwxLTEwLDEuNzRsLTMuNzMuMTJWODYuMVoiIC8+DQo8L3N2Zz4NCg==');
}
</style>
</head>

<body>
<div>
Inline svg1 image(same size as fixed size): <br />
<div id="svg1"></div>

Inline svg2 image(custom size): <br />
<div id="svg2"></div>
</div>
</body>

</html>
15 changes: 11 additions & 4 deletions tests/reftests/images/svg/base64.html
@@ -1,5 +1,6 @@
<!DOCTYPE html>
<html>

<head>
<title>Base64 svg</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
Expand All @@ -10,9 +11,15 @@
}
</style>
</head>

<body>
<div>
Inline svg image: <br />
<img width="200" height="200" src="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgd2lkdGg9IjMwNiIgaGVpZ2h0PSIyOTYiPjxkZWZzIGlkPSJkZWZzNCIgLz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTYyLjQ2OTk1LC00NzcuMjg2MykiIGlkPSJsYXllcjEiPjxwYXRoIGQ9Im0gMzE0LjE1NzQ1LDQ4MS42OTU1OCBjIC01OS4yMDA4OSwwLjUzNzc0IC0xMTQuODA5NzksMzYuNzIyMTkgLTEzNy4zMTI1LDk1LjM0Mzc1IC0yOS4zOTEyOSw3Ni41NjY5MyA4LjgzOTMyLDE2Mi40NTI0NiA4NS40MDYyNSwxOTEuODQzNzUgbCAzNC4wMzEyNSwtODguNjg3NSBjIC0yMC4wNjc4LC03LjcxMzU4IC0zNC4zMTI1LC0yNy4xNTMyNCAtMzQuMzEyNSwtNDkuOTM3NSAwLC0yOS41NDcyMyAyMy45NTI3NywtNTMuNSA1My41LC01My41IDI5LjU0NzIzLDAgNTMuNSwyMy45NTI3NyA1My41LDUzLjUgMCwyMi43ODQyNiAtMTQuMjQ0Nyw0Mi4yMjM5MiAtMzQuMzEyNSw0OS45Mzc1IGwgMzQuMDMxMjUsODguNjg3NSBjIDM5LjI5MDg1LC0xNS4wODIzNCA3MC4zMjM5LC00Ni4xMTU0IDg1LjQwNjI1LC04NS40MDYyNSAyOS4zOTEyOSwtNzYuNTY2OTMgLTguODM5MzIsLTE2Mi40ODM3MSAtODUuNDA2MjUsLTE5MS44NzUgLTE3Ljk0NTM3LC02Ljg4ODU5IC0zNi40MDg1MywtMTAuMDcwODcgLTU0LjUzMTI1LC05LjkwNjI1IHoiIGlkPSJwYXRoMjgzMCIgc3R5bGU9ImZpbGw6IzQwYWE1NDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzIwNTUyYTtzdHJva2Utd2lkdGg6Ny45OTk5OTk1MjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lIiAvPjwvZz48L3N2Zz4=" /></div>
<div>
Inline svg image: <br />
<img width="200" height="200"
src="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgd2lkdGg9IjMwNiIgaGVpZ2h0PSIyOTYiPjxkZWZzIGlkPSJkZWZzNCIgLz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTYyLjQ2OTk1LC00NzcuMjg2MykiIGlkPSJsYXllcjEiPjxwYXRoIGQ9Im0gMzE0LjE1NzQ1LDQ4MS42OTU1OCBjIC01OS4yMDA4OSwwLjUzNzc0IC0xMTQuODA5NzksMzYuNzIyMTkgLTEzNy4zMTI1LDk1LjM0Mzc1IC0yOS4zOTEyOSw3Ni41NjY5MyA4LjgzOTMyLDE2Mi40NTI0NiA4NS40MDYyNSwxOTEuODQzNzUgbCAzNC4wMzEyNSwtODguNjg3NSBjIC0yMC4wNjc4LC03LjcxMzU4IC0zNC4zMTI1LC0yNy4xNTMyNCAtMzQuMzEyNSwtNDkuOTM3NSAwLC0yOS41NDcyMyAyMy45NTI3NywtNTMuNSA1My41LC01My41IDI5LjU0NzIzLDAgNTMuNSwyMy45NTI3NyA1My41LDUzLjUgMCwyMi43ODQyNiAtMTQuMjQ0Nyw0Mi4yMjM5MiAtMzQuMzEyNSw0OS45Mzc1IGwgMzQuMDMxMjUsODguNjg3NSBjIDM5LjI5MDg1LC0xNS4wODIzNCA3MC4zMjM5LC00Ni4xMTU0IDg1LjQwNjI1LC04NS40MDYyNSAyOS4zOTEyOSwtNzYuNTY2OTMgLTguODM5MzIsLTE2Mi40ODM3MSAtODUuNDA2MjUsLTE5MS44NzUgLTE3Ljk0NTM3LC02Ljg4ODU5IC0zNi40MDg1MywtMTAuMDcwODcgLTU0LjUzMTI1LC05LjkwNjI1IHoiIGlkPSJwYXRoMjgzMCIgc3R5bGU9ImZpbGw6IzQwYWE1NDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzIwNTUyYTtzdHJva2Utd2lkdGg6Ny45OTk5OTk1MjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lIiAvPjwvZz48L3N2Zz4=" />
<img width="200" height="200"
src="data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA3OC4zMSA5My44MiIgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6cmVkIj4NCgk8ZGVmcz4NCgkJPHN0eWxlPg0KCQkJLmNscy0xe2ZpbGw6IzEzYTNkNzt9DQoJCTwvc3R5bGU+DQoJPC9kZWZzPg0KCTx0aXRsZT4NCgkJbmV3cy1pY29uLWRqLXVwZGF0ZWQNCgk8L3RpdGxlPg0KCTxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTAsNDYuOTFWMEw1MS42NC4xM2wyLjIxLjU1YzEwLjcxLDIuNjgsMTguNTYsOS41MiwyMi40LDE5LjUxYTM0Ljc2LDM0Ljc2LDAsMCwxLDEuOTMsOC44NmwuMTMsMi4wOUg2Mi43MWwtLjEzLTFhMTYuMzksMTYuMzksMCwwLDAtMy4zMi04LjQsMTYuODIsMTYuODIsMCwwLDAtNy44My01LjQ4bC0xLjY5LS41OC0zNC0uMTJ2NjIuN0gzMS4zNFY5My44MkgwVjQ2LjkxWiIgLz4NCgk8cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik00My4xMiw4Ni4xVjc4LjMybDIuNzktLjE0QTMyLjA2LDMyLjA2LDAsMCwwLDUwLDc3LjczLDE3LjEsMTcuMSwwLDAsMCw2MC45LDY5LjE0YzEuNy0zLjQzLDEuNjQtMi44OSwxLjczLTE1LjIzbC4wOC0xMUg3OC4yN2wtLjE2LDIzLjg3TDc3LjU3LDY5QTMxLjA5LDMxLjA5LDAsMCwxLDY5LDg0LjM2LDMxLjM1LDMxLjM1LDAsMCwxLDU2LjgzLDkyYTI5LjQyLDI5LjQyLDAsMCwxLTEwLDEuNzRsLTMuNzMuMTJWODYuMVoiIC8+DQo8L3N2Zz4NCg==" />
</div>
</body>
</html>

</html>

0 comments on commit 99390d2

Please sign in to comment.