I am currently working on generating an image with node-canvas
via Firebase Cloud Functions, then sending it as an email attachment with nodemailer
.
The requirement of the image is that I need to input some string onto a template with a specific font.
So on Firebase Cloud Functions, here is what I did:
exports.test = functions.https.onRequest((request, response) => {
cors(request, response, () => {
let tempFilePath = path.join(os.tmpdir(), 'tnr.ttf');
functions.logger.log('tempFilePath is', tempFilePath);
bucket.file('tnr.ttf').download({destination: tempFilePath});
functions.logger.log('Font downloaded locally to', tempFilePath);
registerFont(path.resolve(tempFilePath), {family: 'tnr'});
functions.logger.log('Font registered successfully');
}
}
I have already verified repeatedly that the file 'tnr.ttf'
exists on my project cloud and can successfully be loaded to that temp path.
At the registerFont
function however, Firebase would always throw this error, no matter which font file I use:
Error: Could not parse font file
at registerFont (/workspace/node_modules/canvas/index.js:48:17)
Could there possibly be a problem with how Firebase handles this package or function?
You are trying to use the font before it has finished downloading. By the time you realize the problem, open up a terminal and confirm that it's actually present, the download has finished.
What you need to do is wait for either the Promise returned by download(...)
to resolve or attach a callback to the download()
method. Then after it's finished downloading, you can register the font.
exports.test = functions.https.onRequest((request, response) => {
cors(request, response, (corsErr) => {
if (corsErr) {
// this is rare, but still possible.
functions.logger.error('Request rejected by CORS: ', corsErr);
res.status(412).send('Rejected by CORS'); // 412 FAILED PRECONDITION
return;
}
let tempFilePath = path.join(os.tmpdir(), 'tnr.ttf');
functions.logger.log('tempFilePath is ', tempFilePath);
bucket.file('tnr.ttf').download(
{destination: tempFilePath},
(err) => {
if (err) {
functions.logger.error('Failed to download font to local system: ', err);
res.status(422).send('Failed to register font'); // 422 UNPROCESSABLE ENTITY
return;
}
functions.logger.log('Font downloaded locally to ', tempFilePath);
registerFont(path.resolve(tempFilePath), {family: 'tnr'});
functions.logger.log('Font registered successfully');
res.send('Registered font successfully');
}
);
}
}
Notes:
registerFont
method where you will be using it. You can't have separate /registerFonts
and /drawWithFont
endpoints, as you aren't guaranteed to be talking to the same function instance you were a moment ago.