Skip to content

Commit 50b7c12

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 377ada5 commit 50b7c12

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
@@ -75,6 +75,14 @@ exports.JPEGStream = JPEGStream;
7575
exports.Image = Image;
7676
exports.ImageData = canvas.ImageData;
7777

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

src/Canvas.cc

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

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

7171
Nan::Set(target, Nan::New("Canvas").ToLocalChecked(), ctor->GetFunction());
7272
}
@@ -575,72 +575,75 @@ NAN_METHOD(Canvas::StreamJPEGSync) {
575575

576576
#endif
577577

578-
NAN_METHOD(Canvas::RegisterFont) {
579-
FontFace face;
578+
char *
579+
str_value(Local<Value> val, const char *fallback, bool can_be_number) {
580+
if (val->IsString() || (can_be_number && val->IsNumber())) {
581+
return g_strdup(*String::Utf8Value(val));
582+
} else if (fallback) {
583+
return g_strdup(fallback);
584+
} else {
585+
return NULL;
586+
}
587+
}
580588

589+
NAN_METHOD(Canvas::RegisterFont) {
581590
if (!info[0]->IsString()) {
582591
return Nan::ThrowError("Wrong argument type");
592+
} else if (!info[1]->IsObject()) {
593+
return Nan::ThrowError(GENERIC_FACE_ERROR);
583594
}
584595

585596
String::Utf8Value filePath(info[0]);
597+
PangoFontDescription *sys_desc = get_pango_font_description((unsigned char *) *filePath);
586598

587-
if (!register_font((unsigned char *) *filePath, &face.sys_desc)) {
588-
Nan::ThrowError("Could not load font to the system's font host");
589-
} else {
590-
PangoFontDescription *d = pango_font_description_new();
591-
592-
if (!info[1]->IsObject()) {
593-
Nan::ThrowError(GENERIC_FACE_ERROR);
594-
} else { // now check the attrs, there are many ways to be wrong
595-
Local<Object> desc = info[1]->ToObject();
596-
Local<String> family_prop = Nan::New<String>("family").ToLocalChecked();
597-
Local<String> weight_prop = Nan::New<String>("weight").ToLocalChecked();
598-
Local<String> style_prop = Nan::New<String>("style").ToLocalChecked();
599-
600-
const char *family;
601-
const char *weight = "normal";
602-
const char *style = "normal";
603-
604-
Local<Value> family_val = desc->Get(family_prop);
605-
if (family_val->IsString()) {
606-
family = strdup(*String::Utf8Value(family_val));
607-
} else {
608-
Nan::ThrowError(GENERIC_FACE_ERROR);
609-
return;
610-
}
599+
if (!sys_desc) return Nan::ThrowError("Could not parse font file");
611600

612-
if (desc->HasOwnProperty(weight_prop)) {
613-
Local<Value> weight_val = desc->Get(weight_prop);
614-
if (weight_val->IsString() || weight_val->IsNumber()) {
615-
weight = strdup(*String::Utf8Value(weight_val));
616-
} else {
617-
Nan::ThrowError(GENERIC_FACE_ERROR);
618-
return;
619-
}
620-
}
601+
PangoFontDescription *user_desc = pango_font_description_new();
621602

622-
if (desc->HasOwnProperty(style_prop)) {
623-
Local<Value> style_val = desc->Get(style_prop);
624-
if (style_val->IsString()) {
625-
style = strdup(*String::Utf8Value(style_val));
626-
} else {
627-
Nan::ThrowError(GENERIC_FACE_ERROR);
628-
return;
629-
}
630-
}
603+
// now check the attrs, there are many ways to be wrong
604+
Local<Object> js_user_desc = info[1]->ToObject();
605+
Local<String> family_prop = Nan::New<String>("family").ToLocalChecked();
606+
Local<String> weight_prop = Nan::New<String>("weight").ToLocalChecked();
607+
Local<String> style_prop = Nan::New<String>("style").ToLocalChecked();
608+
609+
char *family = str_value(js_user_desc->Get(family_prop), NULL, false);
610+
char *weight = str_value(js_user_desc->Get(weight_prop), "normal", true);
611+
char *style = str_value(js_user_desc->Get(style_prop), "normal", false);
631612

632-
pango_font_description_set_weight(d, Canvas::GetWeightFromCSSString(weight));
633-
pango_font_description_set_style(d, Canvas::GetStyleFromCSSString(style));
634-
pango_font_description_set_family(d, family);
613+
if (family && weight && style) {
614+
pango_font_description_set_weight(user_desc, Canvas::GetWeightFromCSSString(weight));
615+
pango_font_description_set_style(user_desc, Canvas::GetStyleFromCSSString(style));
616+
pango_font_description_set_family(user_desc, family);
617+
618+
std::vector<FontFace>::iterator it = _font_face_list.begin();
619+
FontFace *already_registered = NULL;
635620

636-
free((char *)family);
637-
if (desc->HasOwnProperty(weight_prop)) free((char *)weight);
638-
if (desc->HasOwnProperty(style_prop)) free((char *)style);
621+
for (; it != _font_face_list.end() && !already_registered; ++it) {
622+
if (pango_font_description_equal(it->sys_desc, sys_desc)) {
623+
already_registered = &(*it);
624+
}
625+
}
639626

640-
face.user_desc = d;
627+
if (already_registered) {
628+
pango_font_description_free(already_registered->user_desc);
629+
already_registered->user_desc = user_desc;
630+
} else if (register_font((unsigned char *) *filePath)) {
631+
FontFace face;
632+
face.user_desc = user_desc;
633+
face.sys_desc = sys_desc;
641634
_font_face_list.push_back(face);
635+
} else {
636+
pango_font_description_free(user_desc);
637+
Nan::ThrowError("Could not load font to the system's font host");
642638
}
639+
} else {
640+
pango_font_description_free(user_desc);
641+
Nan::ThrowError(GENERIC_FACE_ERROR);
643642
}
643+
644+
g_free(family);
645+
g_free(weight);
646+
g_free(style);
644647
}
645648

646649
/*

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)