HTML Canvas images with text differ across operating systems

Viewed 93

I'm building a small JS class that holds a <canvas> element and uses it to draw some images dynamically. Particularly those images have both background (fill) and text.

After doing this, I wanted to write some unit tests around this to ensure that, given some input, the generated output (as a base64 string) is always the same. That means that if I want to generate an image with a pink background and white text, the generated output is always a pink image with white text, and the dimensions are the same. In other words, the base64 encoded strings are equal.

I can run these tests locally and they pass (MacOS) but they fail on my Jenkins integration job (runs on Linux). That's because the image's font is slightly different. After reading a lot on this, I suspect this is because the fonts are implemented differently across different OSs.

See the images:

Expected, obtained locally in MacOS:

introducir la descripción de la imagen aquí

Actual, obtained in Linux:

introducir la descripción de la imagen aquí

You can see that the actual image I'm obtaining is a little bit taller than the expected (bottom padding below the text is smaller). Current font I'm using is "44px Arial"

See code:

Implementation

 generateImage(backgroundColor, fontColor = "#000000") {
        if (backgroundColor) {
            this.drawBackgroundWithColor(backgroundColor);
        }
        this.drawTexOverBackground("Text to draw", X_POSITION, Y_POSITION, fontColor, "44px Arial");
        return this.getCurrentCanvasAsBase64String();
    }

 drawBackgroundWithColor(backgroundColor){
        if (backgroundColor) {
            const context = this.getCanvasContext();
            context.save();
            context.fillStyle = backgroundColor;
            context.fillRect(0, 0, this.width, this.height);
            context.restore();
        }
    }

drawTexOverBackground(text, x, y, hexColor, font = this.font) {
        const context = this.getCanvasContext();
        context.save();
        context.font = font;
        context.textBaseline = "top";
        context.fillStyle = hexColor;
        context.fillText(text, x, y);
        context.restore();
    }

Unit test:

it('should create a basic pink background with black text', () => {
        const backgroundGenerator = new ImageGenerator();
        const createdBackground = backgroundGenerator.generateImage("#FFC0CB");
        assert.equal(createdBackground, expectedTransparentImageWithText);
    });

Being expectedTransparentImageWithText a global variable of the module that holds the string base64 value I expect and obtained in MacOS.

I must clarify that for unit testing this I'm using the canvas npm module which runs on a node and as the docs say it "implements that API as closely as possible" (referring to the Canvas browser API). I suspect that that's the reason why the fonts differ. I also got here and found that fonts DO differ between operating systems, and there's no 100% compatibility among them.

Is there any suggestion you can give me to unit test this better and take into account the fonts a well? Right now I'm only testing images without text, so I can avoid running into the explained scenario, but of course, not the best thing to do.

0 Answers
Related