javascriptnode.jsfabricjsnode-canvas

FabricJS convert json to png


I have created the following image as fabricjs-json:

enter image description here

The json looks like the following:

{
    "version": "5.2.1",
    "objects": [
        {
            "type": "circle",
            "version": "5.2.1",
            "originX": "center",
            "originY": "center",
            "left": "100",
            "top": "100",
            "width": "60",
            "height": "60",
            "fill": "#e53e3e",
            "name": "a",
            "radius": "30",
            "stroke": null,
            "padding": "0"
        },
        {
            "type": "triangle",
            "version": "5.2.1",
            "originX": "center",
            "originY": "center",
            "left": "80",
            "top": "229.13",
            "fill": "#e0da2e",
            "name": "b",
            "height": "100",
            "width": "100",
            "stroke": null,
            "padding": "0"
        },
        {
            "type": "rect",
            "version": "5.2.1",
            "originX": "center",
            "originY": "center",
            "left": "303.02",
            "top": "69.96",
            "width": "50",
            "height": "50",
            "fill": "#4976d0",
            "name": "c",
            "rx": "0",
            "ry": "0",
            "stroke": null,
            "padding": "0"
        },
        {
            "type": "image",
            "version": "5.2.1",
            "originX": "center",
            "originY": "center",
            "left": "272.23",
            "top": "287.63",
            "width": "400",
            "height": "400",
            "scaleX": "0.58",
            "scaleY": "0.58",
            "name": "d",
            "rx": "0",
            "ry": "0",
            "stroke": null,
            "padding": "0",
            "src": "https:\/\/picsum.photos\/id\/1014\/400",
            "crossOrigin": null
        },
        {
            "type": "textbox",
            "version": "5.2.1",
            "originX": "center",
            "originY": "center",
            "left": "210.97",
            "top": "100",
            "width": "265.14180221557615",
            "height": "25.99",
            "fill": "#000000",
            "angle": "33.4",
            "name": "e",
            "text": "This awesome image",
            "textAlign": "left",
            "fontSize": "23",
            "charSpacing": "7",
            "lineHeight": "1.16",
            "fontWeight": "400",
            "fontFamily": "Inter",
            "fontStyle": "normal",
            "textBackgroundColor": null,
            "maxHeight": "487",
            "stroke": null,
            "padding": "20"
        }
    ],
    "background": "#24bd0f"
}

I created the following script to convert the json to an image:

var fs = require('fs'),
    fabric = require('fabric').fabric,
    out = fs.createWriteStream(__dirname + '/output.png');

let jsonstr = fs.readFileSync(__dirname + '/json/test_json.json', 'utf-8');
let json = JSON.parse(jsonstr);

var canvas = new fabric.StaticCanvas(null, { width: 500, height: 500 });

canvas.loadFromJSON(json, function() {

    canvas.renderAll.bind(canvas);

    var dataUrlOutput = canvas.toDataURL(); // png is default format
    console.log(dataUrlOutput)
    fs.writeFile(__dirname + '/output/output.png', dataUrlOutput, function(err) {
        if (err) throw err;
    });
});

This is my package.json:

{
  "name": "jsonToImage",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "canvas": "^2.9.3",
    "fabric": "^5.2.4"
  }
}

However, when opening the image I get:

enter image description here

I created the following sandbox to parse the json:

Codesandbox Example - JSONtoIMAGE

Any suggestions what I am doing wrong?

UPDATE

I checked the dataUrlOutput from the script and I get the following:

enter image description here

As you can see half of the elements are there.


Solution

  • There seem to be a few things going on, namely the formatting of your JSON, rendering the canvas, and saving the file in Node.

    Formatting your JSON

    The numbers in your json file are enclosed in quotes (e.g. "left": "100"), while for those numerical values, fabric.js expects them not to be in quotes (e.g. "left": 100). When there are quotes around the numbers, fabric.js seems to quasi-load some objects, but not others.

    When you're saving your canvas as JSON, you can use JSON.stringify to ensure that the formatting is preserved. For example, JSON.stringify(canvas.toJSON());

    Rendering the canvas

    You're calling canvas.renderAll.bind(canvas), which creates a new function but doesn't call it. It should just be canvas.renderAll().

    Saving an image

    Saving a data url directly as a text file does not create an image in an encoded binary file. While you could use the data url to create a binary file (like from this answer), fabric.js adds some convenience functions to save a file, namely canvas.createPNGStream().

    It looks something like this (thanks to this answer):

    var out = fs.createWriteStream(__dirname + '/output.png');
    var stream = canvas.createPNGStream();
    stream.pipe(out);
    

    All together

    With the modified JSON and code, the results look something like this:

    Your data file, 'json/test_json.json'

    {
        "version": "5.2.1",
        "objects": [
            {
                "type": "circle",
                "version": "5.2.1",
                "originX": "center",
                "originY": "center",
                "left": 100,
                "top": 100,
                "width": 60,
                "height": 60,
                "fill": "#e53e3e",
                "name": "a",
                "radius": 30,
                "stroke": null,
                "padding": 0
            },
            {
                "type": "triangle",
                "version": "5.2.1",
                "originX": "center",
                "originY": "center",
                "left": 80,
                "top": 229.13,
                "fill": "#e0da2e",
                "name": "b",
                "height": 100,
                "width": 100,
                "stroke": null,
                "padding": 0
            },
            {
                "type": "rect",
                "version": "5.2.1",
                "originX": "center",
                "originY": "center",
                "left": 303.02,
                "top": 69.96,
                "width": 50,
                "height": 50,
                "fill": "#4976d0",
                "name": "c",
                "rx": 0,
                "ry": 0,
                "stroke": null,
                "padding": 0
            },
            {
                "type": "image",
                "version": "5.2.1",
                "originX": "center",
                "originY": "center",
                "left": 272.23,
                "top": 287.63,
                "width": 400,
                "height": 400,
                "scaleX": 0.58,
                "scaleY": 0.58,
                "name": "d",
                "rx": 0,
                "ry": 0,
                "stroke": null,
                "padding": 0,
                "src": "https:\/\/picsum.photos\/id\/1014\/400",
                "crossOrigin": null
            },
            {
                "type": "textbox",
                "version": "5.2.1",
                "originX": "center",
                "originY": "center",
                "left": 210.97,
                "top": 100,
                "width": 265.14180221557615,
                "height": 25.99,
                "fill": "#000000",
                "angle": 33.4,
                "name": "e",
                "text": "This awesome image",
                "textAlign": "left",
                "fontSize": 23,
                "charSpacing": 7,
                "lineHeight": 1.16,
                "fontWeight": 400,
                "fontFamily": "Inter",
                "fontStyle": "normal",
                "textBackgroundColor": null,
                "maxHeight": 487,
                "stroke": null,
                "padding": 20
            }
        ],
        "background": "#24bd0f"
    }
    

    And your node.js program:

    var fs = require('fs'),
        fabric = require('fabric').fabric,
        out = fs.createWriteStream(__dirname + '/output.png');
    
    let jsonstr = fs.readFileSync(__dirname + '/json/test_json.json', 'utf-8');
    let json = JSON.parse(jsonstr);
    
    var canvas = new fabric.StaticCanvas(null, { width: 500, height: 500 });
    
    canvas.loadFromJSON(json, function() {
        canvas.renderAll();
        var stream = canvas.createPNGStream();
        stream.pipe(out);
        out.on('finish', function () {
            // do something here
        });
    });
    

    More reading:

    http://fabricjs.com/fabric-intro-part-4 (the 'Fabric on Node.js' section)