Skip to content

Commit

Permalink
support @font-face style attrs in registerFont
Browse files Browse the repository at this point in the history
* registerFont is now implemented as a static member function of the
  Canvas class itself. there is a static list of fonts registered
  on the class, which are picked up by setting the font when necessary.
* register_font, if compiled with FT, will fill in its second arg with a
  PangoFontDescription that should resolve to the file specified
  • Loading branch information
chearon committed Apr 24, 2016
1 parent 297fb40 commit 5f26917
Show file tree
Hide file tree
Showing 7 changed files with 358 additions and 56 deletions.
13 changes: 11 additions & 2 deletions lib/canvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ var canvas = require('./bindings')
, Canvas = canvas.Canvas
, Image = canvas.Image
, cairoVersion = canvas.cairoVersion
, registerFont = canvas.registerFont
, Context2d = require('./context2d')
, PNGStream = require('./pngstream')
, JPEGStream = require('./jpegstream')
Expand All @@ -38,7 +37,17 @@ exports.version = packageJson.version;
* Register custom font
*/

exports.registerFont = registerFont;
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.
Expand Down
129 changes: 129 additions & 0 deletions src/Canvas.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
#include <string.h>
#include <node_buffer.h>
#include <node_version.h>
#include <glib.h>
#include <cairo-pdf.h>
#include <cairo-svg.h>

#include "Canvas.h"
#include "PNG.h"
#include "CanvasRenderingContext2d.h"
#include "closure.h"
#include "register_font.h"

#ifdef HAVE_JPEG
#include "JPEGStream.h"
Expand Down Expand Up @@ -56,6 +58,9 @@ Canvas::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) {
Nan::SetTemplate(proto, "PNG_FILTER_PAETH", Nan::New<Uint32>(PNG_FILTER_PAETH));
Nan::SetTemplate(proto, "PNG_ALL_FILTERS", Nan::New<Uint32>(PNG_ALL_FILTERS));

// Class methods
Nan::SetMethod(ctor, "_registerFont", RegisterFont);

Nan::Set(target, Nan::New("Canvas").ToLocalChecked(), ctor->GetFunction());
}

Expand Down Expand Up @@ -467,6 +472,41 @@ NAN_METHOD(Canvas::StreamJPEGSync) {

#endif

NAN_METHOD(Canvas::RegisterFont) {
FontFace face;

if (!info[0]->IsString()) {
return Nan::ThrowError("Wrong argument type");
}

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

if (!register_font((unsigned char*) *filePath, &face.target_desc)) {
Nan::ThrowError("Could not load font to the system's font host");
} else if (face.target_desc) { // NULL if not compiled with FT
PangoFontDescription* d = pango_font_description_copy(face.target_desc);

if (!info[1]->Equals(Nan::Null())) {
String::Utf8Value weight(info[1]->ToString());
pango_font_description_set_weight(d, Canvas::GetWeightFromCSSString(*weight));
}

if (!info[2]->Equals(Nan::Null())) {
String::Utf8Value style(info[2]->ToString());
pango_font_description_set_style(d, Canvas::GetStyleFromCSSString(*style));
}

if (!info[3]->Equals(Nan::Null())) {
String::Utf8Value family(info[3]->ToString());
pango_font_description_set_family(d, *family);
}

face.user_desc = d;

_font_face_list.push_back(face);
}
}

/*
* Initialize cairo surface.
*/
Expand Down Expand Up @@ -517,6 +557,95 @@ Canvas::~Canvas() {
}
}

std::vector<FontFace>
_init_font_face_list() {
std::vector<FontFace> x;
return x;
}

std::vector<FontFace> Canvas::_font_face_list = _init_font_face_list();

/*
* Get a PangoStyle from a CSS string (like "italic")
*/

PangoStyle
Canvas::GetStyleFromCSSString(char *style) {
PangoStyle s = PANGO_STYLE_NORMAL;

if (strlen(style) > 0) {
if (0 == strcmp("italic", style)) {
s = PANGO_STYLE_ITALIC;
} else if (0 == strcmp("oblique", style)) {
s = PANGO_STYLE_OBLIQUE;
}
}

return s;
}

/*
* Get a PangoWeight from a CSS string ("bold", "100", etc)
*/

PangoWeight
Canvas::GetWeightFromCSSString(char *weight) {
PangoWeight w = PANGO_WEIGHT_NORMAL;

if (strlen(weight) > 0) {
if (0 == strcmp("bold", weight)) {
w = PANGO_WEIGHT_BOLD;
} else if (0 == strcmp("100", weight)) {
w = PANGO_WEIGHT_THIN;
} else if (0 == strcmp("200", weight)) {
w = PANGO_WEIGHT_ULTRALIGHT;
} else if (0 == strcmp("300", weight)) {
w = PANGO_WEIGHT_LIGHT;
} else if (0 == strcmp("400", weight)) {
w = PANGO_WEIGHT_NORMAL;
} else if (0 == strcmp("500", weight)) {
w = PANGO_WEIGHT_MEDIUM;
} else if (0 == strcmp("600", weight)) {
w = PANGO_WEIGHT_SEMIBOLD;
} else if (0 == strcmp("700", weight)) {
w = PANGO_WEIGHT_BOLD;
} else if (0 == strcmp("800", weight)) {
w = PANGO_WEIGHT_ULTRABOLD;
} else if (0 == strcmp("900", weight)) {
w = PANGO_WEIGHT_HEAVY;
}
}

return w;
}

/*
* Tries to find a matching font given to registerFont
*/

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
&& pango_font_description_better_match(desc, best_match, f.user_desc)) {

best_match = f.user_desc;
best_match_target = f.target_desc;
}

++it;
}

return best_match_target;
}

/*
* Re-alloc the surface, destroying the previous.
*/
Expand Down
17 changes: 17 additions & 0 deletions src/Canvas.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@
#include <node_object_wrap.h>
#include <node_version.h>
#include <pango/pangocairo.h>
#include <vector>
#include <cairo.h>
#include <nan.h>


using namespace node;
using namespace v8;

Expand All @@ -38,6 +40,16 @@ 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;
};

/*
* Canvas.
*/
Expand All @@ -58,6 +70,7 @@ class Canvas: public Nan::ObjectWrap {
static NAN_SETTER(SetHeight);
static NAN_METHOD(StreamPNGSync);
static NAN_METHOD(StreamJPEGSync);
static NAN_METHOD(RegisterFont);
static Local<Value> Error(cairo_status_t status);
#if NODE_VERSION_AT_LEAST(0, 6, 0)
static void ToBufferAsync(uv_work_t *req);
Expand All @@ -72,6 +85,9 @@ 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 PangoFontDescription* FindCustomFace(PangoFontDescription *desc);

inline bool isPDF(){ return CANVAS_TYPE_PDF == type; }
inline bool isSVG(){ return CANVAS_TYPE_SVG == type; }
Expand All @@ -86,6 +102,7 @@ class Canvas: public Nan::ObjectWrap {
~Canvas();
cairo_surface_t *_surface;
void *_closure;
static std::vector<FontFace> _font_face_list;
};

#endif
44 changes: 9 additions & 35 deletions src/CanvasRenderingContext2d.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1817,47 +1817,21 @@ NAN_METHOD(Context2d::SetFont) {

PangoFontDescription *desc = pango_font_description_copy(context->state->fontDescription);
pango_font_description_free(context->state->fontDescription);
context->state->fontDescription = desc;

if (strlen(*family) > 0) pango_font_description_set_family(desc, *family);
pango_font_description_set_style(desc, Canvas::GetStyleFromCSSString(*style));
pango_font_description_set_weight(desc, Canvas::GetWeightFromCSSString(*weight));

if (size > 0) pango_font_description_set_absolute_size(desc, size * PANGO_SCALE);
if (strlen(*family) > 0) pango_font_description_set_family(desc, *family);

PangoStyle s = PANGO_STYLE_NORMAL;
if (strlen(*style) > 0) {
if (0 == strcmp("italic", *style)) {
s = PANGO_STYLE_ITALIC;
} else if (0 == strcmp("oblique", *style)) {
s = PANGO_STYLE_OBLIQUE;
}
PangoFontDescription *target_desc;
if ((target_desc = Canvas::FindCustomFace(desc))) {
pango_font_description_free(desc);
desc = pango_font_description_copy(target_desc);
}

pango_font_description_set_style(desc, s);

PangoWeight w = PANGO_WEIGHT_NORMAL;
if (strlen(*weight) > 0) {
if (0 == strcmp("bold", *weight)) {
w = PANGO_WEIGHT_BOLD;
} else if (0 == strcmp("200", *weight)) {
w = PANGO_WEIGHT_ULTRALIGHT;
} else if (0 == strcmp("300", *weight)) {
w = PANGO_WEIGHT_LIGHT;
} else if (0 == strcmp("400", *weight)) {
w = PANGO_WEIGHT_NORMAL;
} else if (0 == strcmp("500", *weight)) {
w = PANGO_WEIGHT_MEDIUM;
} else if (0 == strcmp("600", *weight)) {
w = PANGO_WEIGHT_SEMIBOLD;
} else if (0 == strcmp("700", *weight)) {
w = PANGO_WEIGHT_BOLD;
} else if (0 == strcmp("800", *weight)) {
w = PANGO_WEIGHT_ULTRABOLD;
} else if (0 == strcmp("900", *weight)) {
w = PANGO_WEIGHT_HEAVY;
}
}
if (size > 0) pango_font_description_set_absolute_size(desc, size * PANGO_SCALE);

pango_font_description_set_weight(desc, w);
context->state->fontDescription = desc;

pango_layout_set_font_description(context->_layout, desc);
}
Expand Down
17 changes: 2 additions & 15 deletions src/init.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,20 @@
//

#include <stdio.h>
#include <pango/pango.h>
#include <glib.h>
#include "Canvas.h"
#include "Image.h"
#include "ImageData.h"
#include "CanvasGradient.h"
#include "CanvasPattern.h"
#include "CanvasRenderingContext2d.h"
#include "register_font.h"

// Compatibility with Visual Studio versions prior to VS2015
#if defined(_MSC_VER) && _MSC_VER < 1900
#define snprintf _snprintf
#endif

NAN_METHOD(register_font_js) {
if (!info[0]->IsString()) {
return Nan::ThrowError("Wrong argument type");
}

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

if (!register_font((unsigned char*) *filePath)) {
Nan::ThrowError("Could not load font to the system's font host");
}
}

NAN_MODULE_INIT(init) {
Canvas::Initialize(target);
Image::Initialize(target);
Expand All @@ -39,8 +28,6 @@ NAN_MODULE_INIT(init) {
Gradient::Initialize(target);
Pattern::Initialize(target);

Nan::SetMethod(target, "registerFont", register_font_js);

target->Set(Nan::New<String>("cairoVersion").ToLocalChecked(), Nan::New<String>(cairo_version_string()).ToLocalChecked());
#ifdef HAVE_JPEG

Expand Down
Loading

0 comments on commit 5f26917

Please sign in to comment.