Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

createPNGStream and Font() support in version 2.0.0 rc1 #4541

Closed
IntiDC opened this issue Dec 7, 2017 · 22 comments · Fixed by #4550
Closed

createPNGStream and Font() support in version 2.0.0 rc1 #4541

IntiDC opened this issue Dec 7, 2017 · 22 comments · Fixed by #4550

Comments

@IntiDC
Copy link

IntiDC commented Dec 7, 2017

Hi,

It seems like createCanvasForNode is deprecated. I'm using new fabric.Canvas instead. However, I can't figure out how to add a custom font and create a PNGStream in this setup. I searched the docs but wasn't able to find a solution.

TypeError: canvas.Font is not a constructor
TypeError: canvas.createPNGStream is undefined

Would you mind helping me out?

(Note that my code worked with a previous version of fabricjs, but I need to use the new version for this project)

@asturur
Copy link
Member

asturur commented Dec 7, 2017

jsdom/jsdom#2067

i opened this issue in JSDOM to get access to the underlying canvas.
They closed it for now.

I just added this thing of accessing the Font that i completely forgot about.

I'll help you with createPNGStream asap, i m overloaded those days

@dennisrjohn
Copy link
Contributor

Hi, is there any chance we can get a new release candidate with Font added back in? It would be much appreciated.

Thanks for all of your hard work.

@asturur
Copy link
Member

asturur commented Dec 7, 2017

Can you point me at were Font was before? I admit my node+fabric proficency suck to be the mantainer.
I strongly belive using JSDOM completely is better, unless they get hard on not exposing the uderlying implementation.

I'll re release the RC asap. as i said, i am a bit busy.

@dennisrjohn
Copy link
Contributor

dennisrjohn commented Dec 7, 2017

In the fabric 1.6 code, in createCanvasForNode It simply passed through the Canvas Font:

fabric.createCanvasForNode = function(width, height, options, nodeCanvasOptions) {
    nodeCanvasOptions = nodeCanvasOptions || options;

    var canvasEl = fabric.document.createElement('canvas'),
        nodeCanvas = new Canvas(width || 600, height || 600, nodeCanvasOptions),
        nodeCacheCanvas = new Canvas(width || 600, height || 600, nodeCanvasOptions);

    // jsdom doesn't create style on canvas element, so here be temp. workaround
    canvasEl.style = { };

    canvasEl.width = nodeCanvas.width;
    canvasEl.height = nodeCanvas.height;
    options = options || { };
    options.nodeCanvas = nodeCanvas;
    options.nodeCacheCanvas = nodeCacheCanvas;
    var FabricCanvas = fabric.Canvas || fabric.StaticCanvas,
        fabricCanvas = new FabricCanvas(canvasEl, options);
    fabricCanvas.nodeCanvas = nodeCanvas;
    fabricCanvas.nodeCacheCanvas = nodeCacheCanvas;
    fabricCanvas.contextContainer = nodeCanvas.getContext('2d');
    fabricCanvas.contextCache = nodeCacheCanvas.getContext('2d');
    fabricCanvas.Font = Canvas.Font; // Here is the passthrough
    return fabricCanvas;
  };

@asturur
Copy link
Member

asturur commented Dec 9, 2017

So the old code was binding on a per instance basis, and was passing trough a class from the library, not from the instance.

maybe we should just expose the nodeCanvas import under fabric namespace?

@dennisrjohn
Copy link
Contributor

Yeah, that sounds like it would work.

@asturur
Copy link
Member

asturur commented Dec 10, 2017

It looks like i can't make it to work on my setup, whatever i do the font is not used.

const { fabric } = require('fabric');
const { writeFileSync } = require('fs');
const path = require('path')

function fontFile (name) {
  return path.join(__dirname, name)
}

const canvas = new fabric.StaticCanvas(null, { width: 1000, height: 1000 });
var font = new fabric.nodeCanvas.Font('Yataghan', fontFile('WESTTEST.TTF'));
const text = new fabric.Text('Test custom font', { fontFamily: 'Yataghan', fontSize: 500, objectCaching: false });
canvas.contextContainer.addFont(font);
canvas.backgroundColor = 'white';
canvas.add(text);
console.log(text._getFontDeclaration())
writeFileSync('./testFont.png', canvas.toDataURL().replace('data:image/png;base64,', ""), 'base64');

@asturur
Copy link
Member

asturur commented Dec 10, 2017

anyway i'm merging those changes since they expose back the font method. if you have it working please share your setup, i would like this font registering to be as simple as possible.

@dennisrjohn
Copy link
Contributor

I'll see what I can do tomorrow.

@dennisrjohn
Copy link
Contributor

dennisrjohn commented Dec 11, 2017

I tracked this down to an issue in node-canvas, which it appears has never worked.

This code will only render as Arial/12pt.

const { writeFileSync } = require('fs');
const path = require('path');
let Canvas = require('canvas');
let canvas = new Canvas(600,600);
let Font = require('canvas').Font;

function fontFile (name) {
  return path.join(__dirname, name)
}

let font = new Font('Davis', fontFile('davis.ttf'));
let context = canvas.getContext('2d');
context.addFont(font);

context.font = "90px \"Davis\"";
context.fillText("Hello World",50,100);

writeFileSync('./testFont.png', canvas.toDataURL().replace('data:image/png;base64,', ""), 'base64');

Output:
testfont-1 0

I tried the similar code in the node-canvas 2.0 alpha, and it renders the font properly:

const { writeFileSync } = require('fs');
const path = require('path');

let {createCanvas, registerFont} = require('canvas');
let canvas = createCanvas(600,200);

function fontFile (name) {
  return path.join(__dirname, name)
}

registerFont(fontFile('davis.woff'), {
    family:"Davis"
})

let context = canvas.getContext('2d');
context.font = "90px \"Davis\"";
context.fillText("Hello World",50,100);

writeFileSync('./testFont.png', canvas.toDataURL().replace('data:image/png;base64,', ""), 'base64');

Output:
testfont-2 0

There is another thing that may be causing a misunderstanding on the node-canvas side. If you have the font installed on your system, the first example works fine. Unfortunately, this is not an option for us as we use Lambda as our node.js container.

I'm going to raise this issue with node-canvas as well and see if they are willing to fix it. I'm also going to try to fork fabric to use the node-canvas 2.0 alpha and see if I can get the issue fixed that way.

@dennisrjohn
Copy link
Contributor

It appears that this has been a known issue for a while:

Automattic/node-canvas#715

They didn't want to fix it in the 1.0 code, so they implemented the fixes in the 2.0 branch.

@asturur
Copy link
Member

asturur commented Dec 12, 2017

you can build node 1.x to use pango instead of cairo. i do not remember how.

@asturur
Copy link
Member

asturur commented Dec 12, 2017

Problem of switching to node 2 is that jsdom does not support it yet, so you have to fork jsdom too and pull in the proposed patch.

@asturur
Copy link
Member

asturur commented Dec 12, 2017

I built node-canvas with pango support but nothing changed.

@dennisrjohn
Copy link
Contributor

I forked jsdom today to support node-canvas 2, and I have fonts drawing on the canvas. The change to jsdom was trivial.

Tomorrow I'll fork fabric using my jsdom fork and verify that everything works as expected. Then I'll submit my pull request to jsdom and see how they respond.

@asturur
Copy link
Member

asturur commented Dec 12, 2017

They have a pull request for it already fron node-canvas themselves. They prefer to wait for node-canvas to be out of alpha/beta.

Also they do not seem really happy to expose the canvas proprietary methods like registerFont addFont or simply createPngStream. I asked them to expose at least the utility to get the node-canvas implementation of a HTMLCanvas instance but they prefer not

@asturur
Copy link
Member

asturur commented Dec 12, 2017

jsdom/jsdom#2067 try to leave a comment here about using the font registering as i did.

@dennisrjohn
Copy link
Contributor

I spent quite a bit of time on this yesterday, and uncovered a very strange behavior.

I forked jsdom to use node-canvas 2.0 alpha and forked fabric to use my jsdom fork.

If I create a canvas (using fabric.util.createCanvas) and simply import registerFont from node canvas, the font renders correctly:

const { fabric } = require('./dist/fabric');
const { writeFileSync } = require('fs');
const path = require('path');

let {registerFont} = require('canvas');
let canvasElement = fabric.util.createCanvasElement();
canvasElement.width = 600;
canvasElement.height = 200;

function fontFile (name) {
    return path.join(__dirname, name)
}

registerFont(fontFile('daniel.woff'), {
    family:"Daniel"
})

let context = canvasElement.getContext('2d');

context.font = "90px \"Daniel\"";
context.fillText("Hello World",50,100);

writeFileSync('./testFont.png', canvasElement.toDataURL().replace('data:image/png;base64,', ""), 'base64');

Output:
testfont

However, if I simply create a fabric StaticCanvas, passing the canvas I created in the constructor, then run the exact same code, the font doesn't render properly:

const { fabric } = require('./dist/fabric');
const { writeFileSync } = require('fs');
const path = require('path');

let {registerFont} = require('canvas');
let canvasElement = fabric.util.createCanvasElement();
canvasElement.width = 600;
canvasElement.height = 200;
const canvas = new fabric.StaticCanvas(canvasElement, { width: 600, height: 200 });

function fontFile (name) {
    return path.join(__dirname, name)
}

registerFont(fontFile('daniel.woff'), {
    family:"Daniel"
})

let context = canvasElement.getContext('2d');

context.font = "90px \"Daniel\"";
context.fillText("Hello World",50,100);

writeFileSync('./testFont.png', canvasElement.toDataURL().replace('data:image/png;base64,', ""), 'base64');

Output:
testfont

I'm not sure what it means...

@asturur
Copy link
Member

asturur commented Dec 13, 2017 via email

@dennisrjohn
Copy link
Contributor

In my jsdom/node-canvas2/fabric branches, the registered font doesn't work if I used an iText. I also tried passing registerFont from jsdom to fabric and it also didn't work. I think the above issue could be the cause of registerFont not working at all with node-canvas2 in fabric2.0.

@asturur
Copy link
Member

asturur commented Dec 13, 2017 via email

@dennisrjohn
Copy link
Contributor

dennisrjohn commented Dec 13, 2017

I set objectCaching to false on both the Canvas and the Text object:

const { fabric } = require('./dist/fabric');
const { writeFileSync } = require('fs');
const path = require('path');

let canvasElement = fabric.util.createCanvasElement();
canvasElement.width = 600;
canvasElement.height = 200;
const canvas = new fabric.StaticCanvas(canvasElement, { width: 600, height: 200, objectCaching: false, backgroundColor: 'white' });

function fontFile (name) {
    return path.join(__dirname, name)
}

//registerFont is a passthrough to canvas.registerFont that I exposed in my jsdom fork
fabric.registerFont(fontFile('daniel.woff'), {
    family:"Daniel"
})

const text = new fabric.Text('Hello World', { fontFamily: 'Daniel', fontSize: 90, objectCaching: false });
canvas.add(text);

writeFileSync('./testFont.png', canvas.toDataURL().replace('data:image/png;base64,', ""), 'base64');

Produces the same results:
testfont

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants