Skip to content

Commit ab9bd46

Browse files
committed
support family name lists, both system and custom
* so you can now specify ctx.font = 'custom1, arial'; etc and the later fonts will be used for glyphs that aren't in the first ones * i'm now calling them sys_desc and user_desc to distinguish the description that matches the font on the system vs the description the user passes to Canvas.registerFont * cleaned up some style
1 parent bb14516 commit ab9bd46

File tree

5 files changed

+59
-43
lines changed

5 files changed

+59
-43
lines changed

lib/context2d.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ var parseFont = exports.parseFont = function(str){
7777
font.style = captures[2] || 'normal';
7878
font.size = parseFloat(captures[3]);
7979
font.unit = captures[4];
80-
font.family = captures[5].replace(/["']/g, '').split(',')[0].trim();
80+
font.family = captures[5].replace(/["']/g, '').split(',').map(function (family) {
81+
return family.trim();
82+
}).join(',');
8183

8284
// TODO: dpi
8385
// TODO: remaining unit conversion

src/Canvas.cc

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -486,10 +486,10 @@ NAN_METHOD(Canvas::RegisterFont) {
486486

487487
String::Utf8Value filePath(info[0]);
488488

489-
if (!register_font((unsigned char*) *filePath, &face.target_desc)) {
489+
if (!register_font((unsigned char *) *filePath, &face.sys_desc)) {
490490
Nan::ThrowError("Could not load font to the system's font host");
491491
} else {
492-
PangoFontDescription* d = pango_font_description_new();
492+
PangoFontDescription *d = pango_font_description_new();
493493

494494
if (!info[1]->IsObject()) {
495495
Nan::ThrowError(GENERIC_FACE_ERROR);
@@ -499,9 +499,9 @@ NAN_METHOD(Canvas::RegisterFont) {
499499
Local<String> weight_prop = Nan::New<String>("weight").ToLocalChecked();
500500
Local<String> style_prop = Nan::New<String>("style").ToLocalChecked();
501501

502-
const char* family;
503-
const char* weight = "normal";
504-
const char* style = "normal";
502+
const char *family;
503+
const char *weight = "normal";
504+
const char *style = "normal";
505505

506506
Local<Value> family_val = desc->Get(family_prop);
507507
if (family_val->IsString()) {
@@ -535,9 +535,9 @@ NAN_METHOD(Canvas::RegisterFont) {
535535
pango_font_description_set_style(d, Canvas::GetStyleFromCSSString(style));
536536
pango_font_description_set_family(d, family);
537537

538-
free((char*)family);
539-
if (desc->HasOwnProperty(weight_prop)) free((char*)weight);
540-
if (desc->HasOwnProperty(style_prop)) free((char*)style);
538+
free((char *)family);
539+
if (desc->HasOwnProperty(weight_prop)) free((char *)weight);
540+
if (desc->HasOwnProperty(style_prop)) free((char *)style);
541541

542542
face.user_desc = d;
543543
_font_face_list.push_back(face);
@@ -658,31 +658,48 @@ Canvas::GetWeightFromCSSString(const char *weight) {
658658
}
659659

660660
/*
661-
* Tries to find a matching font given to registerFont
661+
* Given a user description, return a description that will select the
662+
* font either from the system or @font-face
662663
*/
663664

664665
PangoFontDescription *
665-
Canvas::FindCustomFace(PangoFontDescription *desc) {
666-
PangoFontDescription* best_match = NULL;
667-
PangoFontDescription* best_match_target = NULL;
668-
std::vector<FontFace>::iterator it = _font_face_list.begin();
669-
670-
while (it != _font_face_list.end()) {
671-
FontFace f = *it;
672-
673-
if (g_ascii_strcasecmp(pango_font_description_get_family(desc),
674-
pango_font_description_get_family(f.user_desc)) == 0) {
675-
676-
if (best_match == NULL || pango_font_description_better_match(desc, best_match, f.user_desc)) {
677-
best_match = f.user_desc;
678-
best_match_target = f.target_desc;
666+
Canvas::ResolveFontDescription(const PangoFontDescription *desc) {
667+
FontFace best;
668+
PangoFontDescription *ret = NULL;
669+
670+
// One of the user-specified families could map to multiple SFNT family names
671+
// if someone registered two different fonts under the same family name.
672+
// https://drafts.csswg.org/css-fonts-3/#font-style-matching
673+
char **families = g_strsplit(pango_font_description_get_family(desc), ",", -1);
674+
GString *resolved_families = g_string_new("");
675+
676+
for (int i = 0; families[i]; ++i) {
677+
GString *renamed_families = g_string_new("");
678+
std::vector<FontFace>::iterator it = _font_face_list.begin();
679+
680+
for (; it != _font_face_list.end(); ++it) {
681+
if (g_ascii_strcasecmp(families[i], pango_font_description_get_family(it->user_desc)) == 0) {
682+
if (renamed_families->len) g_string_append(renamed_families, ",");
683+
g_string_append(renamed_families, pango_font_description_get_family(it->sys_desc));
684+
685+
if (i == 0 && (best.user_desc == NULL || pango_font_description_better_match(desc, best.user_desc, it->user_desc))) {
686+
best = *it;
687+
}
679688
}
680689
}
681690

682-
++it;
691+
if (resolved_families->len) g_string_append(resolved_families, ",");
692+
g_string_append(resolved_families, renamed_families->len ? renamed_families->str : families[i]);
693+
g_string_free(renamed_families, true);
683694
}
684695

685-
return best_match_target;
696+
ret = pango_font_description_copy(best.sys_desc ? best.sys_desc : desc);
697+
pango_font_description_set_family_static(ret, resolved_families->str);
698+
699+
g_strfreev(families);
700+
g_string_free(resolved_families, false);
701+
702+
return ret;
686703
}
687704

688705
/*

src/Canvas.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,14 @@ typedef enum {
4040
CANVAS_TYPE_SVG
4141
} canvas_type_t;
4242

43-
/**
43+
/*
4444
* FontFace describes a font file in terms of one PangoFontDescription that
4545
* will resolve to it and one that the user describes it as (like @font-face)
4646
*/
4747
class FontFace {
4848
public:
49-
PangoFontDescription *target_desc;
50-
PangoFontDescription *user_desc;
49+
PangoFontDescription *sys_desc = NULL;
50+
PangoFontDescription *user_desc = NULL;
5151
};
5252

5353
/*
@@ -87,7 +87,7 @@ class Canvas: public Nan::ObjectWrap {
8787
#endif
8888
static PangoWeight GetWeightFromCSSString(const char *weight);
8989
static PangoStyle GetStyleFromCSSString(const char *style);
90-
static PangoFontDescription* FindCustomFace(PangoFontDescription *desc);
90+
static PangoFontDescription *ResolveFontDescription(const PangoFontDescription *desc);
9191

9292
inline bool isPDF(){ return CANVAS_TYPE_PDF == type; }
9393
inline bool isSVG(){ return CANVAS_TYPE_SVG == type; }

src/CanvasRenderingContext2d.cc

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1840,17 +1840,14 @@ NAN_METHOD(Context2d::SetFont) {
18401840

18411841
if (strlen(*family) > 0) pango_font_description_set_family(desc, *family);
18421842

1843-
PangoFontDescription *target_desc;
1844-
if ((target_desc = Canvas::FindCustomFace(desc))) {
1845-
pango_font_description_free(desc);
1846-
desc = pango_font_description_copy(target_desc);
1847-
}
1843+
PangoFontDescription *sys_desc = Canvas::ResolveFontDescription(desc);
1844+
pango_font_description_free(desc);
18481845

1849-
if (size > 0) pango_font_description_set_absolute_size(desc, size * PANGO_SCALE);
1846+
if (size > 0) pango_font_description_set_absolute_size(sys_desc, size * PANGO_SCALE);
18501847

1851-
context->state->fontDescription = desc;
1848+
context->state->fontDescription = sys_desc;
18521849

1853-
pango_layout_set_font_description(context->_layout, desc);
1850+
pango_layout_set_font_description(context->_layout, sys_desc);
18541851
}
18551852

18561853
/*

test/canvas.test.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,15 @@ describe('Canvas', function () {
4343
, '20px monospace'
4444
, { size: 20, unit: 'px', family: 'monospace' }
4545
, '50px Arial, sans-serif'
46-
, { size: 50, unit: 'px', family: 'Arial' }
46+
, { size: 50, unit: 'px', family: 'Arial,sans-serif' }
4747
, 'bold italic 50px Arial, sans-serif'
48-
, { style: 'italic', weight: 'bold', size: 50, unit: 'px', family: 'Arial' }
48+
, { style: 'italic', weight: 'bold', size: 50, unit: 'px', family: 'Arial,sans-serif' }
4949
, '50px Helvetica , Arial, sans-serif'
50-
, { size: 50, unit: 'px', family: 'Helvetica' }
50+
, { size: 50, unit: 'px', family: 'Helvetica,Arial,sans-serif' }
5151
, '50px "Helvetica Neue", sans-serif'
52-
, { size: 50, unit: 'px', family: 'Helvetica Neue' }
52+
, { size: 50, unit: 'px', family: 'Helvetica Neue,sans-serif' }
5353
, '50px "Helvetica Neue", "foo bar baz" , sans-serif'
54-
, { size: 50, unit: 'px', family: 'Helvetica Neue' }
54+
, { size: 50, unit: 'px', family: 'Helvetica Neue,foo bar baz,sans-serif' }
5555
, "50px 'Helvetica Neue'"
5656
, { size: 50, unit: 'px', family: 'Helvetica Neue' }
5757
, 'italic 20px Arial'

0 commit comments

Comments
 (0)