Skip to content

SVG width and height units are invalid set #2503

@BrightShadow

Description

@BrightShadow

The problem

I discovered that depending on environment and OS the SVG export of the canvas (.toBuffer() method) exports the SVG file with the units "pt" set in markup. This is invalid behavior. SVG units should stay "none", so the example below:

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="800pt" height="400pt" viewBox="0 0 800 400" version="1.1">
...
</svg>

is invalid and we should get the format like below (in pixels, no units):

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="800" height="400" viewBox="0 0 800 400" version="1.1">
...
</svg>

The usage code in TypeScript

import {imageSize} from 'image-size';
import {Canvas, createCanvas} from 'canvas';

const canvas: Canvas = createCanvas(400, 400, 'svg');
const buffer: Buffer = canvas.toBuffer();
const dimensions = imageSize(buffer);

// jest test
expect(dimensions.width).toEqual(400); // here it fails
expect(dimensions.height).toEqual(400); // here it fails

Environments

The code is working locally on Mac OS, Windows (not checked on any linux locally)
It fails on Docker image with Debian (probably 12) / Node 20.11.0 (other linux OS not checked).

Why this is a problem?

Invalid units causes invalid scaling of the image when it is used. The "pt" units is not 1:1 a pixel, each pixel is represented by the following unit:
96/72 pt => 1.3333 px, this results in an image scaled to ~133% of initially set dimensions.

Solution

Always use RAW pixels, do not set any unit while exporting to buffer. The cavnas itself seems to have correct size, width and height in pixels, but I was not testing it intensively. The problem seems to be on the export only - in the .toBuffer() method (parameterless).

Temporary fix made as workaround

let svgFileXML = this.canvas.toBuffer().toString('utf8'); // convert buffer to XML string
const regex = /(<svg.*width="[0-9]+)pt(".*height="[0-9]+)pt(".*>)/; // match SVG header tag
svgFileXML = svgFileXML.replace(regex, '$1$2$3'); // replace with tag without units
return Buffer.from(svgFileXML, 'utf8'); // I use buffer in my code so I convert back to buffer

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions