node-canvas: Using custom font - node.js

I would like to know how to use custom font with node-canvas.
Here is my attempt but it does not work so far:
var Canvas = require('canvas')
, Image = Canvas.Image
, Font = Canvas.Font
, path = require('path');
var gubblebum = new Font('gubblebum', fontFile('GUBBLO___.ttf'));
function fontFile(name) {
return path.join(__dirname, '../fonts', name);
}
function draw() {
var canvas = new Canvas(100, 100)
, ctx = canvas.getContext('2d');
ctx.addFont(gubblebum);
ctx.font = 'bold 40 gubblebum';
ctx.fillText('test', 25, 45);
return canvas;
}
module.exports = draw;
When rendered in a browser, the font is left unchanged from the default, as well as the size.
The font file is correctly loaded. Otherwise, an exception would be thrown.
Interestingly, when I do ctx.font = 'bold 40 someGibberish'; the font size is applied correctly.

This should now "Just Work" in node-canvas 2.0+. See the new Canvas.registerFont method: https://github.com/Automattic/node-canvas/#registerfont.

So I ended up getting node canvas to work with custom fonts.
First I used an older version of Cairo. I used Brew to install all of the dependencies, since it told me which ones I had and which ones were linked correctly, and then once all the dependencies were working correctly, I uninstalled the Cairo version from Brew and installed version 1.12.X (patch version 18 I think it was) of Cairo. This solved a Mac OSX Issue with the font size, but I couldn't load in fonts.
So after some investigation I found there was also an issue with the latest Node Canvas module, so I hard set the version to 1.1.0 in my package.json and FINALLY I got custom fonts to load and size correctly.
So TL;DR: Use Cairo version 1.12.X and Node Canvas 1.1.0 and it should work I think? It took me a while to get it working.

Related

"Error: Unknown Version 0" when using NodeJS PDFKit

I'm trying to get the very excellent NodeJS PDFKit to use custom fonts OpenSans and Roboto from Google Fonts. My code looks like the following:
this.doc = new PDFDocument({bufferPages: true});
this.doc.registerFont("Roboto-Black", path.join(__dirname, "fonts", "Roboto-Black.ttf"));
I've printed the path - it's finding the right file. I'm getting the following error:
C:\projects\qbdvision\node_modules\restructure\src\VersionedStruct.js:37
throw new Error("Unknown version " + res.version);
^
Error: Unknown version 0
at VersionedStruct.decode (C:\projects\qbdvision\node_modules\restructure\src\VersionedStruct.js:37:15)
at C:\projects\qbdvision\node_modules\restructure\src\Pointer.js:69:30
at Pointer.decode (C:\projects\qbdvision\node_modules\restructure\src\Pointer.js:79:16)
at ArrayT.decode (C:\projects\qbdvision\node_modules\restructure\src\Array.js:49:30)
at VersionedStruct.Struct._parseFields (C:\projects\qbdvision\node_modules\restructure\src\Struct.js:53:22)
at VersionedStruct.decode (C:\projects\qbdvision\node_modules\restructure\src\VersionedStruct.js:42:12)
at VersionedStruct.decode (C:\projects\qbdvision\node_modules\restructure\src\VersionedStruct.js:40:23)
at C:\projects\qbdvision\node_modules\restructure\src\Pointer.js:69:30
at Pointer.decode (C:\projects\qbdvision\node_modules\restructure\src\Pointer.js:79:16)
at ArrayT.decode (C:\projects\qbdvision\node_modules\restructure\src\Array.js:49:30)
FAILED
When I removed the Roboto font, and tried the OpenSans one, it worked at least, but everything looked terrible. Letters were bleeding together and looked almost smudged.
I've downloaded the fonts from fonts.google.com by clicking "Select this font", clicking on the "1 Family Selected" popup that comes up and then clicking on the download icon in the upper right hand corner of that popup.
Why won't these fonts work?
The solution is to convert the fonts into base64 encoding and then import them. So at the command line, using Linux / Cygwin, type:
base64 --wrap=0 Roboto-Black.ttf > Roboto-Black-Base64.ttf
That'll produce a new TTF file that should be all text inside. If you use an external service, make sure there isn't any wrapping. It should be one continuous block of text.
Then, in your NodeJS code, do:
let fs = require("fs");
let doc = new PDFDocument({bufferPages: true});
let filePath = path.join(__dirname, "fonts", "Roboto-Black-Base64.ttf");
let fileContents = fs.readFileSync(filePath, "utf8");
this.doc.registerFont(fontName, new Buffer(fileContents, "base64"));
Then your fonts will show up crystal clear. Props to this answer for giving me the clues I needed.

Present canvas interface in shared code on both server and client

I am trying to write shared code (that runs on both server and client) that uses an HTML canvas.
On the client, this should work perfectly fine. On the server, Node doesn't have a canvas (or a DOM), so I'd like to use the node-canvas plugin: https://github.com/Automattic/node-canvas.
However, I can't work out a way to access it that doesn't make webpack try to bundle node-canvas into my client-side code (which crashes webpack). Is there any way of loading node-canvas in such a way that I can reference it with the same code I'll use in the browser and without making webpack crash horribly?
My current effort, which did not work:
canvas.server.js
import Canvas from 'canvas';
const createCanvas = (width, height) => new Canvas(width, height);
export default createCanvas;
canvas.client.js
const createCanvas = (width, height) => {
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
return canvas;
};
export default createCanvas;
canvas.js
let createCanvas;
if (typeof document === 'undefined') {
// SERVER/node
createCanvas = require('./canvas.server.js');
} else {
// BROWSER
createCanvas = require('./canvas.client.js');
}
export default createCanvas;
in use:
import createCanvas from './canvas';
const canvasElement = createCanvas(width, height);
const ctx = canvasElement.getContext('2d');
Unfortunately, webpack still bundles in node-canvas.
Did you try requiring node-canvas only when the code is running in node?
If you do not actually call the require in front-end code, webpack will not bundle it. This means calling the actual require inside aforementioned conditional statement and not at the top of your file. This is important. Also, verify that you did not put node-canvas as an entry point in webpack.
Example:
// let's assume there is `isNode` variable as described in linked answer
let canvas;
if (isNode) {
const Canvas = require('canvas');
canvas = new Canvas(x, y);
else {
canvas = document.getElementById('canvas');
}
// get canvas context and draw on it
Edit after OP provided code example:
I've reproduced the exact structure in this WebpackBin to prove this should be working as explained above. After all, this is a feature of common.js (and other module loaders) and is therefore supported in webpack.
My changes to the code:
Replaced the code in canvas.client.js and canvas.server.js with console.log of what would be called at that line
Fixed wrong use of require with export default (should be require(...).default). Irrelevant, but had to do it to make the code work.
Removed the canvasElement.getContex call. Would not work in the example code and is irrelevant anyway, so there is no point of mocking it.

Add transparent color layer to b&w image using ImageMagick

I'm trying to take a black and white image and add a red transparent layer on top. Something similar to the image. Does anyone know how I can do this using ImageMagick in Node using gm?
I took #Bonzo suggestion and used blend. With Node gm you can call ImageMagick methods directly.
const gm = require('gm');
const im = gm.subClass({ imageMagick: true });
im()
.command('composite')
.in('-blend', '100x100')
.in('-gravity', 'center')
.in('/tmp/top.png')
.in('/tmp/bottom.png').toBuffer('PNG',function (err, buffer) { }

Node JS canvas image data

I am trying to read a image and the result i want to get is the same when you use a HTML canvas "Uint8ClampedArray"? var imageData = ctx.getImageData(0, 0, width, height); I found a NPM lib canvas but i cant get it to install.
So is there a another way to go without using Canvas?
To strictly answer the question:
So is there a another way to go without using Canvas?
The issue is that, even if we can load an image binary data, we need to be able to parse its binary format to represent it as raw pixel data (ImageData/Uint8Array objects).
This is why the canvas module needs to be compiled when installed: it rely on and links to libpng, libjpeg and other native libraries.
To load a Uint8Array representing pixel raw data from a file, without canvas (or native library wrapper), will requires decoders running only in Javascript.
For e.g. there exist decoders for png and jpeg as third-party libraries.
Decoding PNG
Using png.js:
const PNG = require('png-js');
PNG.decode('./image.png', function(pixels) {
// Pixels is a 1d array (in rgba order) of decoded pixel data
});
Decoding JPEG
Using inkjet:
const inkjet = require('inkjet');
inkjet.decode(fs.readFileSync('./image.jpg'), function(err, decoded) {
// decoded: { width: number, height: number, data: Uint8Array }
});
https://github.com/gchudnov/inkjet
Do not realy know what i did to get canvas to work. I used pixel-util to set image data.
var pixelUtil = require('pixel-util'),
Canvas = require('canvas');
var image = new Canvas.Image,
pixelUtil.createBuffer('http://127.0.0.1:8080/image/test.jpg').then(function(buffer){
image.src = buffer;
canvas = new Canvas(image.width, image.height);
var ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0);
runImage(ctx.getImageData(0, 0, canvas.width, canvas.height));
});

Fabricjs+Node.js: fabric's canvas toDataURL calling toBuffer() not a method

I'm using the following code to try to see if I can load a canvas from a json string then generate a dataURL png for it:
var fabric=require('fabric');
var canvas = new fabric.fabric.Canvas();
var jsonStr='{"objects":[],"background":"rgba(0, 0, 0,0)","backgroundImage":"http://entropy.tmok.com/~gauze/canvas/any.gif","backgroundImageOpacity":1,"backgroundImageStretch":true,"overlayImage":"http://entropy.tmok.com/~gauze/canvas/frame.png","overlayImageLeft":0,"overlayImageTop":0}';
canvas.loadFromJSON(jsonStr);
img=canvas.toDataURL('png');
it errors on the toDataURL() line with:
/root/node-v0.8.16-linux-x86/node_modules/canvas/lib/canvas.js:190
return prefix + this.toBuffer().toString('base64');
^
which tells me 'this' (which is a Canvas according to console.log) doesn't have a .toBuffer() method. am I doing something wrong or is this a bug in fabric's node module?
thanks.
never mind I missed : .createCanvasForNode()
cant wait for documentation of this :P

Resources