Browse Source

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
master
Caleb Hearon 9 years ago
committed by Caleb Hearon
parent
commit
377ada5fd8
  1. 4
      lib/context2d.js
  2. 67
      src/Canvas.cc
  3. 8
      src/Canvas.h
  4. 13
      src/CanvasRenderingContext2d.cc
  5. 10
      test/canvas.test.js

4
lib/context2d.js

@ -77,7 +77,9 @@ var parseFont = exports.parseFont = function(str){
font.style = captures[2] || 'normal';
font.size = parseFloat(captures[3]);
font.unit = captures[4];
font.family = captures[5].replace(/["']/g, '').split(',')[0].trim();
font.family = captures[5].replace(/["']/g, '').split(',').map(function (family) {
return family.trim();
}).join(',');
// TODO: dpi
// TODO: remaining unit conversion

67
src/Canvas.cc

@ -584,10 +584,10 @@ NAN_METHOD(Canvas::RegisterFont) {
String::Utf8Value filePath(info[0]);
if (!register_font((unsigned char*) *filePath, &face.target_desc)) {
if (!register_font((unsigned char *) *filePath, &face.sys_desc)) {
Nan::ThrowError("Could not load font to the system's font host");
} else {
PangoFontDescription* d = pango_font_description_new();
PangoFontDescription *d = pango_font_description_new();
if (!info[1]->IsObject()) {
Nan::ThrowError(GENERIC_FACE_ERROR);
@ -597,9 +597,9 @@ NAN_METHOD(Canvas::RegisterFont) {
Local<String> weight_prop = Nan::New<String>("weight").ToLocalChecked();
Local<String> style_prop = Nan::New<String>("style").ToLocalChecked();
const char* family;
const char* weight = "normal";
const char* style = "normal";
const char *family;
const char *weight = "normal";
const char *style = "normal";
Local<Value> family_val = desc->Get(family_prop);
if (family_val->IsString()) {
@ -633,9 +633,9 @@ NAN_METHOD(Canvas::RegisterFont) {
pango_font_description_set_style(d, Canvas::GetStyleFromCSSString(style));
pango_font_description_set_family(d, family);
free((char*)family);
if (desc->HasOwnProperty(weight_prop)) free((char*)weight);
if (desc->HasOwnProperty(style_prop)) free((char*)style);
free((char *)family);
if (desc->HasOwnProperty(weight_prop)) free((char *)weight);
if (desc->HasOwnProperty(style_prop)) free((char *)style);
face.user_desc = d;
_font_face_list.push_back(face);
@ -757,31 +757,48 @@ Canvas::GetWeightFromCSSString(const char *weight) {
}
/*
* Tries to find a matching font given to registerFont
* Given a user description, return a description that will select the
* font either from the system or @font-face
*/
PangoFontDescription *
Canvas::FindCustomFace(PangoFontDescription *desc) {
PangoFontDescription* best_match = NULL;
PangoFontDescription* best_match_target = NULL;
std::vector<FontFace>::iterator it = _font_face_list.begin();
while (it != _font_face_list.end()) {
FontFace f = *it;
if (g_ascii_strcasecmp(pango_font_description_get_family(desc),
pango_font_description_get_family(f.user_desc)) == 0) {
if (best_match == NULL || pango_font_description_better_match(desc, best_match, f.user_desc)) {
best_match = f.user_desc;
best_match_target = f.target_desc;
Canvas::ResolveFontDescription(const PangoFontDescription *desc) {
FontFace best;
PangoFontDescription *ret = NULL;
// One of the user-specified families could map to multiple SFNT family names
// if someone registered two different fonts under the same family name.
// https://drafts.csswg.org/css-fonts-3/#font-style-matching
char **families = g_strsplit(pango_font_description_get_family(desc), ",", -1);
GString *resolved_families = g_string_new("");
for (int i = 0; families[i]; ++i) {
GString *renamed_families = g_string_new("");
std::vector<FontFace>::iterator it = _font_face_list.begin();
for (; it != _font_face_list.end(); ++it) {
if (g_ascii_strcasecmp(families[i], pango_font_description_get_family(it->user_desc)) == 0) {
if (renamed_families->len) g_string_append(renamed_families, ",");
g_string_append(renamed_families, pango_font_description_get_family(it->sys_desc));
if (i == 0 && (best.user_desc == NULL || pango_font_description_better_match(desc, best.user_desc, it->user_desc))) {
best = *it;
}
}
}
++it;
if (resolved_families->len) g_string_append(resolved_families, ",");
g_string_append(resolved_families, renamed_families->len ? renamed_families->str : families[i]);
g_string_free(renamed_families, true);
}
return best_match_target;
ret = pango_font_description_copy(best.sys_desc ? best.sys_desc : desc);
pango_font_description_set_family_static(ret, resolved_families->str);
g_strfreev(families);
g_string_free(resolved_families, false);
return ret;
}
/*

8
src/Canvas.h

@ -40,14 +40,14 @@ typedef enum {
CANVAS_TYPE_SVG
} canvas_type_t;
/**
/*
* FontFace describes a font file in terms of one PangoFontDescription that
* will resolve to it and one that the user describes it as (like @font-face)
*/
class FontFace {
public:
PangoFontDescription *target_desc;
PangoFontDescription *user_desc;
PangoFontDescription *sys_desc = NULL;
PangoFontDescription *user_desc = NULL;
};
/*
@ -89,7 +89,7 @@ class Canvas: public Nan::ObjectWrap {
#endif
static PangoWeight GetWeightFromCSSString(const char *weight);
static PangoStyle GetStyleFromCSSString(const char *style);
static PangoFontDescription* FindCustomFace(PangoFontDescription *desc);
static PangoFontDescription *ResolveFontDescription(const PangoFontDescription *desc);
inline bool isPDF(){ return CANVAS_TYPE_PDF == type; }
inline bool isSVG(){ return CANVAS_TYPE_SVG == type; }

13
src/CanvasRenderingContext2d.cc

@ -1840,17 +1840,14 @@ NAN_METHOD(Context2d::SetFont) {
if (strlen(*family) > 0) pango_font_description_set_family(desc, *family);
PangoFontDescription *target_desc;
if ((target_desc = Canvas::FindCustomFace(desc))) {
pango_font_description_free(desc);
desc = pango_font_description_copy(target_desc);
}
PangoFontDescription *sys_desc = Canvas::ResolveFontDescription(desc);
pango_font_description_free(desc);
if (size > 0) pango_font_description_set_absolute_size(desc, size * PANGO_SCALE);
if (size > 0) pango_font_description_set_absolute_size(sys_desc, size * PANGO_SCALE);
context->state->fontDescription = desc;
context->state->fontDescription = sys_desc;
pango_layout_set_font_description(context->_layout, desc);
pango_layout_set_font_description(context->_layout, sys_desc);
}
/*

10
test/canvas.test.js

@ -44,15 +44,15 @@ describe('Canvas', function () {
, '20px monospace'
, { size: 20, unit: 'px', family: 'monospace' }
, '50px Arial, sans-serif'
, { size: 50, unit: 'px', family: 'Arial' }
, { size: 50, unit: 'px', family: 'Arial,sans-serif' }
, 'bold italic 50px Arial, sans-serif'
, { style: 'italic', weight: 'bold', size: 50, unit: 'px', family: 'Arial' }
, { style: 'italic', weight: 'bold', size: 50, unit: 'px', family: 'Arial,sans-serif' }
, '50px Helvetica , Arial, sans-serif'
, { size: 50, unit: 'px', family: 'Helvetica' }
, { size: 50, unit: 'px', family: 'Helvetica,Arial,sans-serif' }
, '50px "Helvetica Neue", sans-serif'
, { size: 50, unit: 'px', family: 'Helvetica Neue' }
, { size: 50, unit: 'px', family: 'Helvetica Neue,sans-serif' }
, '50px "Helvetica Neue", "foo bar baz" , sans-serif'
, { size: 50, unit: 'px', family: 'Helvetica Neue' }
, { size: 50, unit: 'px', family: 'Helvetica Neue,foo bar baz,sans-serif' }
, "50px 'Helvetica Neue'"
, { size: 50, unit: 'px', family: 'Helvetica Neue' }
, 'italic 20px Arial'

Loading…
Cancel
Save