Skip to content

Commit a98acb2

Browse files
committed
handle double-registering fonts. expand font paths
this should make it much less likely registering a font will fail: 1. paths to fonts with .., sym links, etc are resolved before passed to the OS and freetype 2. when you register a font with identical metadata to one you have already registered, its description will be adjusted accordingly (if you call registerFont with name = 'a' then name = 'b', you will then need to set ctx.font = 'b' to select it) this commit also fixes some minor memory leaks
1 parent ab9bd46 commit a98acb2

File tree

4 files changed

+79
-63
lines changed

4 files changed

+79
-63
lines changed

lib/canvas.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,14 @@ exports.JPEGStream = JPEGStream;
7373
exports.Image = Image;
7474
exports.ImageData = canvas.ImageData;
7575

76+
/**
77+
* Resolve paths for registerFont
78+
*/
79+
80+
Canvas.registerFont = function(src, fontFace){
81+
return Canvas._registerFont(fs.realpathSync(src), fontFace);
82+
};
83+
7684
/**
7785
* Context2d implementation.
7886
*/

src/Canvas.cc

Lines changed: 55 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ Canvas::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) {
6464
Nan::SetTemplate(proto, "PNG_ALL_FILTERS", Nan::New<Uint32>(PNG_ALL_FILTERS));
6565

6666
// Class methods
67-
Nan::SetMethod(ctor, "registerFont", RegisterFont);
67+
Nan::SetMethod(ctor, "_registerFont", RegisterFont);
6868

6969
Nan::Set(target, Nan::New("Canvas").ToLocalChecked(), ctor->GetFunction());
7070
}
@@ -477,72 +477,75 @@ NAN_METHOD(Canvas::StreamJPEGSync) {
477477

478478
#endif
479479

480-
NAN_METHOD(Canvas::RegisterFont) {
481-
FontFace face;
480+
char *
481+
str_value(Local<Value> val, const char *fallback, bool can_be_number) {
482+
if (val->IsString() || (can_be_number && val->IsNumber())) {
483+
return g_strdup(*String::Utf8Value(val));
484+
} else if (fallback) {
485+
return g_strdup(fallback);
486+
} else {
487+
return NULL;
488+
}
489+
}
482490

491+
NAN_METHOD(Canvas::RegisterFont) {
483492
if (!info[0]->IsString()) {
484493
return Nan::ThrowError("Wrong argument type");
494+
} else if (!info[1]->IsObject()) {
495+
return Nan::ThrowError(GENERIC_FACE_ERROR);
485496
}
486497

487498
String::Utf8Value filePath(info[0]);
499+
PangoFontDescription *sys_desc = get_pango_font_description((unsigned char *) *filePath);
488500

489-
if (!register_font((unsigned char *) *filePath, &face.sys_desc)) {
490-
Nan::ThrowError("Could not load font to the system's font host");
491-
} else {
492-
PangoFontDescription *d = pango_font_description_new();
493-
494-
if (!info[1]->IsObject()) {
495-
Nan::ThrowError(GENERIC_FACE_ERROR);
496-
} else { // now check the attrs, there are many ways to be wrong
497-
Local<Object> desc = info[1]->ToObject();
498-
Local<String> family_prop = Nan::New<String>("family").ToLocalChecked();
499-
Local<String> weight_prop = Nan::New<String>("weight").ToLocalChecked();
500-
Local<String> style_prop = Nan::New<String>("style").ToLocalChecked();
501-
502-
const char *family;
503-
const char *weight = "normal";
504-
const char *style = "normal";
505-
506-
Local<Value> family_val = desc->Get(family_prop);
507-
if (family_val->IsString()) {
508-
family = strdup(*String::Utf8Value(family_val));
509-
} else {
510-
Nan::ThrowError(GENERIC_FACE_ERROR);
511-
return;
512-
}
501+
if (!sys_desc) return Nan::ThrowError("Could not parse font file");
513502

514-
if (desc->HasOwnProperty(weight_prop)) {
515-
Local<Value> weight_val = desc->Get(weight_prop);
516-
if (weight_val->IsString() || weight_val->IsNumber()) {
517-
weight = strdup(*String::Utf8Value(weight_val));
518-
} else {
519-
Nan::ThrowError(GENERIC_FACE_ERROR);
520-
return;
521-
}
522-
}
503+
PangoFontDescription *user_desc = pango_font_description_new();
523504

524-
if (desc->HasOwnProperty(style_prop)) {
525-
Local<Value> style_val = desc->Get(style_prop);
526-
if (style_val->IsString()) {
527-
style = strdup(*String::Utf8Value(style_val));
528-
} else {
529-
Nan::ThrowError(GENERIC_FACE_ERROR);
530-
return;
531-
}
532-
}
505+
// now check the attrs, there are many ways to be wrong
506+
Local<Object> js_user_desc = info[1]->ToObject();
507+
Local<String> family_prop = Nan::New<String>("family").ToLocalChecked();
508+
Local<String> weight_prop = Nan::New<String>("weight").ToLocalChecked();
509+
Local<String> style_prop = Nan::New<String>("style").ToLocalChecked();
510+
511+
char *family = str_value(js_user_desc->Get(family_prop), NULL, false);
512+
char *weight = str_value(js_user_desc->Get(weight_prop), "normal", true);
513+
char *style = str_value(js_user_desc->Get(style_prop), "normal", false);
533514

534-
pango_font_description_set_weight(d, Canvas::GetWeightFromCSSString(weight));
535-
pango_font_description_set_style(d, Canvas::GetStyleFromCSSString(style));
536-
pango_font_description_set_family(d, family);
515+
if (family && weight && style) {
516+
pango_font_description_set_weight(user_desc, Canvas::GetWeightFromCSSString(weight));
517+
pango_font_description_set_style(user_desc, Canvas::GetStyleFromCSSString(style));
518+
pango_font_description_set_family(user_desc, family);
519+
520+
std::vector<FontFace>::iterator it = _font_face_list.begin();
521+
FontFace *already_registered = NULL;
537522

538-
free((char *)family);
539-
if (desc->HasOwnProperty(weight_prop)) free((char *)weight);
540-
if (desc->HasOwnProperty(style_prop)) free((char *)style);
523+
for (; it != _font_face_list.end() && !already_registered; ++it) {
524+
if (pango_font_description_equal(it->sys_desc, sys_desc)) {
525+
already_registered = &(*it);
526+
}
527+
}
541528

542-
face.user_desc = d;
529+
if (already_registered) {
530+
pango_font_description_free(already_registered->user_desc);
531+
already_registered->user_desc = user_desc;
532+
} else if (register_font((unsigned char *) *filePath)) {
533+
FontFace face;
534+
face.user_desc = user_desc;
535+
face.sys_desc = sys_desc;
543536
_font_face_list.push_back(face);
537+
} else {
538+
pango_font_description_free(user_desc);
539+
Nan::ThrowError("Could not load font to the system's font host");
544540
}
541+
} else {
542+
pango_font_description_free(user_desc);
543+
Nan::ThrowError(GENERIC_FACE_ERROR);
545544
}
545+
546+
g_free(family);
547+
g_free(weight);
548+
g_free(style);
546549
}
547550

548551
/*

src/register_font.cc

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,10 @@ get_pango_style(FT_Long flags) {
191191
}
192192
}
193193

194+
/*
195+
* Return a PangoFontDescription that will resolve to the font file
196+
*/
197+
194198
PangoFontDescription *
195199
get_pango_font_description(unsigned char* filepath) {
196200
FT_Library library;
@@ -199,16 +203,18 @@ get_pango_font_description(unsigned char* filepath) {
199203

200204
if (!FT_Init_FreeType(&library) && !FT_New_Face(library, (const char*)filepath, 0, &face)) {
201205
TT_OS2 *table = (TT_OS2*)FT_Get_Sfnt_Table(face, FT_SFNT_OS2);
202-
char *family = get_family_name(face);
206+
if (table) {
207+
char *family = get_family_name(face);
203208

204-
if (family) pango_font_description_set_family_static(desc, family);
205-
pango_font_description_set_weight(desc, get_pango_weight(table->usWeightClass));
206-
pango_font_description_set_stretch(desc, get_pango_stretch(table->usWidthClass));
207-
pango_font_description_set_style(desc, get_pango_style(face->style_flags));
209+
if (family) pango_font_description_set_family_static(desc, family);
210+
pango_font_description_set_weight(desc, get_pango_weight(table->usWeightClass));
211+
pango_font_description_set_stretch(desc, get_pango_stretch(table->usWidthClass));
212+
pango_font_description_set_style(desc, get_pango_style(face->style_flags));
208213

209-
FT_Done_Face(face);
214+
FT_Done_Face(face);
210215

211-
return desc;
216+
return desc;
217+
}
212218
}
213219

214220
pango_font_description_free(desc);
@@ -221,7 +227,7 @@ get_pango_font_description(unsigned char* filepath) {
221227
*/
222228

223229
bool
224-
register_font(unsigned char *filepath, PangoFontDescription **desc) {
230+
register_font(unsigned char *filepath) {
225231
bool success;
226232

227233
#ifdef __APPLE__
@@ -235,8 +241,6 @@ register_font(unsigned char *filepath, PangoFontDescription **desc) {
235241

236242
if (!success) return false;
237243

238-
*desc = get_pango_font_description(filepath);
239-
240244
// Tell Pango to throw away the current FontMap and create a new one. This
241245
// has the effect of registering the new font in Pango by re-looking up all
242246
// font families.

src/register_font.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include <pango/pango.h>
22

3-
bool register_font(unsigned char *filepath, PangoFontDescription** desc);
3+
PangoFontDescription *get_pango_font_description(unsigned char *filepath);
4+
bool register_font(unsigned char *filepath);
45

0 commit comments

Comments
 (0)