Browse Source

require 2nd arg to registerFont, match CSS spec better

* Canvas.registerFont is now implemented entirely in C++
* second arg is required and must have at least `family`.
  the others default to 'normal' per w3 fonts specification
* when there is a family match for registered fonts, it
  should be chosen even if weight/style are wrong. this is
  also in the spec
* update example and documentation
master
Caleb Hearon 9 years ago
parent
commit
e3ac8bdfc1
  1. 10
      Readme.md
  2. 6
      examples/font.js
  3. 16
      lib/canvas.js
  4. 83
      src/Canvas.cc
  5. 4
      src/Canvas.h

10
Readme.md

@ -182,16 +182,16 @@ canvas.toDataURL('image/jpeg', {opts...}, function(err, jpeg){ }); // see Canvas
canvas.toDataURL('image/jpeg', quality, function(err, jpeg){ }); // spec-following; quality from 0 to 1
```
### Canvas#registerFont for bundled fonts
### Canvas.registerFont for bundled fonts
It can be useful to use a custom font file if you are distributing code that uses node-canvas and a specific font. Or perhaps you are using it to do automated tests and you want the renderings to be the same across operating systems regardless of what fonts they have installed.
It can be useful to use a custom font file if you are distributing code that uses node-canvas and a specific font. Or perhaps you are using it to do automated tests and you want the renderings to be the same across operating systems regardless of what fonts are installed.
To do that, you should use `Canvas#registerFont`.
To do that, you should use `Canvas.registerFont`.
**You need to call it before the Canvas is created**
```javascript
Canvas.registerFont('comicsans.ttf');
Canvas.registerFont('comicsans.ttf', {family: 'Comic Sans'});
var canvas = new Canvas(500, 500),
ctx = canvas.getContext('2d');
@ -200,6 +200,8 @@ ctx.font = '12px "Comic Sans"';
ctx.fillText(250, 10, 'Everyone hates this font :(');
```
The second argument is an object with properties that resemble the CSS properties that are specified in `@font-face` rules. You must specify at least `family`. `weight`, and `style` are optional (and default to "normal").
### CanvasRenderingContext2D#patternQuality
Given one of the values below will alter pattern (gradients, images, etc) render quality, defaults to _good_.

6
examples/font.js

@ -11,9 +11,9 @@ function fontFile (name) {
// name as it is embedded in the TTF. If you aren't sure, open the font in
// FontForge and visit Element -> Font Information and copy the Family Name
Canvas.registerFont(fontFile('Pfennig.ttf'), {family: 'pfennigFont'})
Canvas.registerFont(fontFile('PfennigBold.ttf'), {family: 'pfennigFont'})
Canvas.registerFont(fontFile('PfennigItalic.ttf'), {family: 'pfennigFont'})
Canvas.registerFont(fontFile('PfennigBoldItalic.ttf'), {family: 'pfennigFont'})
Canvas.registerFont(fontFile('PfennigBold.ttf'), {family: 'pfennigFont', weight: 'bold'})
Canvas.registerFont(fontFile('PfennigItalic.ttf'), {family: 'pfennigFont', style: 'italic'})
Canvas.registerFont(fontFile('PfennigBoldItalic.ttf'), {family: 'pfennigFont', weight: 'bold', style: 'italic'})
var canvas = new Canvas(320, 320)
var ctx = canvas.getContext('2d')

16
lib/canvas.js

@ -34,22 +34,6 @@ var Canvas = exports = module.exports = Canvas;
exports.version = packageJson.version;
/**
* Register custom font
*/
exports.registerFont = function(src, fontFace) {
var weight, style, family;
if (typeof fontFace === 'object') {
weight = fontFace.weight;
style = fontFace.style;
family = fontFace.family;
}
this._registerFont(src, weight, style, family);
};
/**
* Cairo version.
*/

83
src/Canvas.cc

@ -23,6 +23,11 @@
#include "JPEGStream.h"
#endif
#define GENERIC_FACE_ERROR \
"The second argument to registerFont is required, and should be an object " \
"with at least a family (string) and optionally weight (string/number) " \
"and style (string)."
Nan::Persistent<FunctionTemplate> Canvas::constructor;
/*
@ -61,7 +66,7 @@ Canvas::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) {
Nan::SetTemplate(proto, "PNG_ALL_FILTERS", Nan::New<Uint32>(PNG_ALL_FILTERS));
// Class methods
Nan::SetMethod(ctor, "_registerFont", RegisterFont);
Nan::SetMethod(ctor, "registerFont", RegisterFont);
Nan::Set(target, Nan::New("Canvas").ToLocalChecked(), ctor->GetFunction());
}
@ -582,26 +587,59 @@ NAN_METHOD(Canvas::RegisterFont) {
if (!register_font((unsigned char*) *filePath, &face.target_desc)) {
Nan::ThrowError("Could not load font to the system's font host");
} else {
PangoFontDescription* d = pango_font_description_copy(face.target_desc);
PangoFontDescription* d = pango_font_description_new();
if (!info[1]->IsObject()) {
Nan::ThrowError(GENERIC_FACE_ERROR);
} else { // now check the attrs, there are many ways to be wrong
Local<Object> desc = info[1]->ToObject();
Local<String> family_prop = Nan::New<String>("family").ToLocalChecked();
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";
Local<Value> family_val = desc->Get(family_prop);
if (family_val->IsString()) {
family = strdup(*String::Utf8Value(family_val));
} else {
Nan::ThrowError(GENERIC_FACE_ERROR);
return;
}
if (!info[1]->Equals(Nan::Null())) {
String::Utf8Value weight(info[1]->ToString());
pango_font_description_set_weight(d, Canvas::GetWeightFromCSSString(*weight));
}
if (desc->HasOwnProperty(weight_prop)) {
Local<Value> weight_val = desc->Get(weight_prop);
if (weight_val->IsString() || weight_val->IsNumber()) {
weight = strdup(*String::Utf8Value(weight_val));
} else {
Nan::ThrowError(GENERIC_FACE_ERROR);
return;
}
}
if (!info[2]->Equals(Nan::Null())) {
String::Utf8Value style(info[2]->ToString());
pango_font_description_set_style(d, Canvas::GetStyleFromCSSString(*style));
}
if (desc->HasOwnProperty(style_prop)) {
Local<Value> style_val = desc->Get(style_prop);
if (style_val->IsString()) {
style = strdup(*String::Utf8Value(style_val));
} else {
Nan::ThrowError(GENERIC_FACE_ERROR);
return;
}
}
if (!info[3]->Equals(Nan::Null())) {
String::Utf8Value family(info[3]->ToString());
pango_font_description_set_family(d, *family);
}
pango_font_description_set_weight(d, Canvas::GetWeightFromCSSString(weight));
pango_font_description_set_style(d, Canvas::GetStyleFromCSSString(style));
pango_font_description_set_family(d, family);
face.user_desc = d;
free((char*)family);
if (desc->HasOwnProperty(weight_prop)) free((char*)weight);
if (desc->HasOwnProperty(style_prop)) free((char*)style);
_font_face_list.push_back(face);
face.user_desc = d;
_font_face_list.push_back(face);
}
}
}
@ -669,7 +707,7 @@ std::vector<FontFace> Canvas::_font_face_list = _init_font_face_list();
*/
PangoStyle
Canvas::GetStyleFromCSSString(char *style) {
Canvas::GetStyleFromCSSString(const char *style) {
PangoStyle s = PANGO_STYLE_NORMAL;
if (strlen(style) > 0) {
@ -688,7 +726,7 @@ Canvas::GetStyleFromCSSString(char *style) {
*/
PangoWeight
Canvas::GetWeightFromCSSString(char *weight) {
Canvas::GetWeightFromCSSString(const char *weight) {
PangoWeight w = PANGO_WEIGHT_NORMAL;
if (strlen(weight) > 0) {
@ -732,11 +770,12 @@ Canvas::FindCustomFace(PangoFontDescription *desc) {
FontFace f = *it;
if (g_ascii_strcasecmp(pango_font_description_get_family(desc),
pango_font_description_get_family(f.user_desc)) == 0
&& pango_font_description_better_match(desc, best_match, f.user_desc)) {
pango_font_description_get_family(f.user_desc)) == 0) {
best_match = f.user_desc;
best_match_target = f.target_desc;
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;
}
}
++it;

4
src/Canvas.h

@ -87,8 +87,8 @@ class Canvas: public Nan::ObjectWrap {
EIO_ToBuffer(eio_req *req);
static int EIO_AfterToBuffer(eio_req *req);
#endif
static PangoWeight GetWeightFromCSSString(char *weight);
static PangoStyle GetStyleFromCSSString(char *style);
static PangoWeight GetWeightFromCSSString(const char *weight);
static PangoStyle GetStyleFromCSSString(const char *style);
static PangoFontDescription* FindCustomFace(PangoFontDescription *desc);
inline bool isPDF(){ return CANVAS_TYPE_PDF == type; }

Loading…
Cancel
Save