diff options
author | Jonathon Jongsma <jonathon.jongsma@collabora.co.uk> | 2008-08-15 00:37:45 -0500 |
---|---|---|
committer | Jonathon Jongsma <jonathon.jongsma@collabora.co.uk> | 2008-08-18 20:58:27 -0500 |
commit | c6efb90e89953cf25b835a7bb844404a65adcbcb (patch) | |
tree | a91f45d05871e3b49773940d42d6ff059374e032 | |
parent | a98b16b1785685cff8541bc0c3ced5390f652ab1 (diff) |
Add UserFontFace API
-rw-r--r-- | ChangeLog | 14 | ||||
-rw-r--r-- | cairomm/context.h | 2 | ||||
-rw-r--r-- | cairomm/enums.h | 3 | ||||
-rw-r--r-- | cairomm/fontface.cc | 194 | ||||
-rw-r--r-- | cairomm/fontface.h | 67 | ||||
-rw-r--r-- | configure.in | 4 | ||||
-rw-r--r-- | tests/test-font-face.cc | 202 |
7 files changed, 481 insertions, 5 deletions
@@ -1,3 +1,17 @@ +2008-08-14 Jonathon Jongsma <jjongsma@gnome.org> + + * cairomm/context.h: move some text-related typedefs into the fontface + header since they're needed for new user font API and that's really where they + should be owned. context.h includes fontface.h, so there should not be any + breakage for people who were just including context.h + * cairomm/enums.h: add user font type to font type enum + * cairomm/fontface.cc: + * cairomm/fontface.h: add UserFontFace class. This class has a set of + callbacks that can be used to do certain things. In order to do this in a + flexible way, I have added libsigc++ as a dependency of cairo to achieve this + * configure.in: add check for libsigc++ + * tests/test-font-face.cc: add tests for UserFontFace + 2008-08-15 Jonathon Jongsma <jjongsma@gnome.org> * cairomm/scaledfont.h: diff --git a/cairomm/context.h b/cairomm/context.h index 711309e..2371152 100644 --- a/cairomm/context.h +++ b/cairomm/context.h @@ -34,8 +34,6 @@ namespace Cairo { typedef cairo_glyph_t Glyph; //A simple struct. -typedef cairo_font_extents_t FontExtents; //A simple struct. -typedef cairo_text_extents_t TextExtents; //A simple struct. typedef cairo_matrix_t Matrix; //A simple struct. //TODO: Derive and add operator[] and operator. matrix multiplication? typedef cairo_rectangle_t Rectangle; diff --git a/cairomm/enums.h b/cairomm/enums.h index 8f4cb39..062f932 100644 --- a/cairomm/enums.h +++ b/cairomm/enums.h @@ -193,7 +193,8 @@ typedef enum FONT_TYPE_FT = CAIRO_FONT_TYPE_FT, FONT_TYPE_WIN32 = CAIRO_FONT_TYPE_WIN32, FONT_TYPE_ATSUI = CAIRO_FONT_TYPE_QUARTZ, /**< @deprecated Use FONT_TYPE_QUARTZ instead. */ - FONT_TYPE_QUARTZ = CAIRO_FONT_TYPE_QUARTZ + FONT_TYPE_QUARTZ = CAIRO_FONT_TYPE_QUARTZ, + FONT_TYPE_USER = CAIRO_FONT_TYPE_USER } FontType; } // namespace Cairo diff --git a/cairomm/fontface.cc b/cairomm/fontface.cc index f7cf8cd..338f299 100644 --- a/cairomm/fontface.cc +++ b/cairomm/fontface.cc @@ -16,7 +16,9 @@ * 02110-1301, USA. */ +#include <iostream> #include <cairomm/fontface.h> +#include <cairomm/scaledfont.h> #include <cairomm/private.h> namespace Cairo @@ -101,6 +103,198 @@ FontWeight ToyFontFace::get_weight() const } +//*************************// +// UserFont Implementation // +//*************************// +struct UserFontFace::PrivateData +{ + SlotInit* m_init_slot; + SlotUnicodeToGlyph* m_unicode_to_glyph_slot; + SlotRenderGlyph* m_render_glyph_slot; + + PrivateData() + : m_init_slot (0) + , m_unicode_to_glyph_slot (0) + , m_render_glyph_slot (0) + { + } + + ~PrivateData() + { + if (m_init_slot) + delete m_init_slot; + if (m_unicode_to_glyph_slot) + delete m_unicode_to_glyph_slot; + if (m_render_glyph_slot) + delete m_render_glyph_slot; + } +}; + +static const cairo_user_data_key_t user_font_key = {0}; + +static void +log_uncaught_exception(const char* message = 0) +{ + std::cerr << "*** cairomm: Uncaught exception in slot callback"; + if (message) + std::cerr << ": " << message; + std::cerr << std::endl; +} + +cairo_status_t +UserFontFace::init_cb (cairo_scaled_font_t* scaled_font, + cairo_t *cr, + cairo_font_extents_t* metrics) +{ + cairo_font_face_t* face = cairo_scaled_font_get_font_face(scaled_font); + // we've stored a pointer to the wrapper object in the C object's user_data + UserFontFace* instance = + static_cast<UserFontFace*>(cairo_font_face_get_user_data(face, + &user_font_key)); + if (instance && instance->m_priv && instance->m_priv->m_init_slot) { + try { + return (*instance->m_priv->m_init_slot)(RefPtr<ScaledFont>(new ScaledFont(scaled_font)), + RefPtr<Context>(new Context(cr)), + static_cast<FontExtents&>(*metrics)); + } catch (const std::exception& ex) { + log_uncaught_exception(ex.what()); + } catch ( ... ) { + log_uncaught_exception(); + } + } + // this should never happen + return CAIRO_STATUS_USER_FONT_ERROR; +} + +cairo_status_t +UserFontFace::unicode_to_glyph_cb (cairo_scaled_font_t *scaled_font, + unsigned long unicode, + unsigned long *glyph) +{ + cairo_font_face_t* face = cairo_scaled_font_get_font_face(scaled_font); + // we've stored a pointer to the wrapper object in the C object's user_data + UserFontFace* instance = + static_cast<UserFontFace*>(cairo_font_face_get_user_data(face, + &user_font_key)); + if (instance && instance->m_priv && instance->m_priv->m_unicode_to_glyph_slot) { + try { + return (*instance->m_priv->m_unicode_to_glyph_slot)(RefPtr<ScaledFont>(new ScaledFont(scaled_font)), + unicode, *glyph); + } catch (const std::exception& ex) { + log_uncaught_exception(ex.what()); + } catch ( ... ) { + log_uncaught_exception(); + } + } + // this should never happen + return CAIRO_STATUS_USER_FONT_ERROR; +} + +cairo_status_t +UserFontFace::render_glyph_cb (cairo_scaled_font_t *scaled_font, + unsigned long glyph, + cairo_t *cr, + cairo_text_extents_t *metrics) +{ + cairo_font_face_t* face = cairo_scaled_font_get_font_face(scaled_font); + // we've stored a pointer to the wrapper object in the C object's user_data + UserFontFace* instance = + static_cast<UserFontFace*>(cairo_font_face_get_user_data(face, + &user_font_key)); + if (instance && instance->m_priv && instance->m_priv->m_render_glyph_slot) { + try { + return (*instance->m_priv->m_render_glyph_slot)(RefPtr<ScaledFont>(new ScaledFont(scaled_font)), + glyph, RefPtr<Context>(new Context(cr)), + static_cast<TextExtents&>(*metrics)); + } catch (const std::exception& ex) { + log_uncaught_exception(ex.what()); + } catch ( ... ) { + log_uncaught_exception(); + } + } + // this should never happen + return CAIRO_STATUS_USER_FONT_ERROR; +} + +RefPtr<UserFontFace> UserFontFace::create() +{ + return RefPtr<UserFontFace>(new UserFontFace()); +} + +UserFontFace::UserFontFace() + : FontFace(cairo_user_font_face_create(), true /* has reference */) + , m_priv(new PrivateData()) +{ + check_status_and_throw_exception(cairo_font_face_status(m_cobject)); + + // store a pointer to the wrapper class in the user-data, so that when one of + // the callback functions gets called (which has to be a plain-C function so + // can't be a class member), we can get a reference to the wrapper class + cairo_font_face_set_user_data (m_cobject, &user_font_key, + (void*)this, (cairo_destroy_func_t) NULL); +} + +UserFontFace::~UserFontFace() +{ + delete m_priv; +} + +void UserFontFace::set_init_func(const SlotInit& init_func) +{ + if (!m_priv) + return; + if (m_priv->m_init_slot) + delete m_priv->m_init_slot; + // copy the new slot + m_priv->m_init_slot = new SlotInit(init_func); + + // register it with cairo + cairo_user_font_face_set_init_func (cobj(), init_cb); +} + +void UserFontFace::set_render_glyph_func(const SlotRenderGlyph& render_glyph_func) +{ + if (!m_priv) + return; + if (m_priv->m_render_glyph_slot) + delete m_priv->m_render_glyph_slot; + // copy the slot + m_priv->m_render_glyph_slot = new SlotRenderGlyph(render_glyph_func); + cairo_user_font_face_set_render_glyph_func (cobj(), render_glyph_cb); +} + +void UserFontFace::set_unicode_to_glyph_func(const SlotUnicodeToGlyph& unicode_to_glyph_func) +{ + if (!m_priv) + return; + if (m_priv->m_unicode_to_glyph_slot) + delete m_priv->m_unicode_to_glyph_slot; + // copy the slot + m_priv->m_unicode_to_glyph_slot = new SlotUnicodeToGlyph(unicode_to_glyph_func); + cairo_user_font_face_set_unicode_to_glyph_func (cobj(), unicode_to_glyph_cb); +} + +const UserFontFace::SlotInit* UserFontFace::get_init_func() const +{ + if (!m_priv) + return 0; + return m_priv->m_init_slot; +} + +const UserFontFace::SlotRenderGlyph* UserFontFace::get_render_glyph_func() const +{ + if (!m_priv) + return 0; + return m_priv->m_render_glyph_slot; +} + +const UserFontFace::SlotUnicodeToGlyph* UserFontFace::get_unicode_to_glyph_func() const +{ + if (!m_priv) + return 0; + return m_priv->m_unicode_to_glyph_slot; +} + } //namespace Cairo // vim: ts=2 sw=2 et diff --git a/cairomm/fontface.h b/cairomm/fontface.h index ed6da61..c107eb5 100644 --- a/cairomm/fontface.h +++ b/cairomm/fontface.h @@ -22,12 +22,18 @@ #include <string> #include <cairomm/enums.h> #include <cairomm/refptr.h> +#include <sigc++/slot.h> #include <cairo.h> namespace Cairo { +class ScaledFont; +class Context; +typedef cairo_font_extents_t FontExtents; //A simple struct. +typedef cairo_text_extents_t TextExtents; //A simple struct. + /** * This is a reference-counted object that should be used via Cairo::RefPtr. */ @@ -38,7 +44,6 @@ protected: //TODO?: FontFace(cairo_font_face_t *target); public: - /** Create a C++ wrapper for the C instance. This C++ instance should then be given to a RefPtr. * @param cobject The C instance. * @param has_reference Whether we already have a reference. Otherwise, the constructor will take an extra reference. @@ -87,6 +92,66 @@ protected: ToyFontFace(const std::string& family, FontSlant slant, FontWeight weight); }; + +class UserFontFace : public FontFace +{ +public: + static RefPtr<UserFontFace> create(); + + typedef sigc::slot<ErrorStatus, + const RefPtr<ScaledFont>&, + const RefPtr<Context>&, + FontExtents&> SlotInit; + typedef sigc::slot<ErrorStatus, + const RefPtr<ScaledFont>&, + unsigned long /*unicode*/, + unsigned long& /*glyph*/> SlotUnicodeToGlyph; + typedef sigc::slot<ErrorStatus, + const RefPtr<ScaledFont>&, + unsigned long /*glyph*/, + const RefPtr<Context>&, + TextExtents& /*metrics*/> SlotRenderGlyph; + // FIXME: add SlotTextToGlyphs + + void set_init_func(const SlotInit& init_func); + void set_render_glyph_func(const SlotRenderGlyph& render_glyph_func); + void set_unicode_to_glyph_func(const SlotUnicodeToGlyph& unicode_to_glyph_func); + // FIXME: add set_text_to_glyphs_func + + // FIXME: are these really useful? What would you do with a sigc::slot when + // you got it? + const SlotInit* get_init_func() const; + const SlotRenderGlyph* get_render_glyph_func() const; + const SlotUnicodeToGlyph* get_unicode_to_glyph_func() const; + // FIXME: add get_text_to_glyphs_func + + + virtual ~UserFontFace(); + +protected: + UserFontFace(); + +private: + struct PrivateData; + PrivateData* m_priv; + +static cairo_status_t +init_cb(cairo_scaled_font_t* scaled_font, + cairo_t *cr, + cairo_font_extents_t* metrics); + +static cairo_status_t +unicode_to_glyph_cb(cairo_scaled_font_t *scaled_font, + unsigned long unicode, + unsigned long *glyph); + +static cairo_status_t +render_glyph_cb(cairo_scaled_font_t *scaled_font, + unsigned long glyph, + cairo_t *cr, + cairo_text_extents_t *metrics); +}; + } // namespace Cairo #endif //__CAIROMM_FONTFACE_H diff --git a/configure.in b/configure.in index 6392656..b2daf17 100644 --- a/configure.in +++ b/configure.in @@ -105,7 +105,9 @@ dnl some platforms (e.g. Solaris) need additional C headers included so dnl that there are always prototypes and defines available. AC_CHECK_HEADERS(math.h) -PKG_CHECK_MODULES(CAIROMM, cairo >= 1.5.10) +PKG_CHECK_MODULES(CAIROMM, + cairo >= 1.6.0 + sigc++-2.0) AC_ARG_ENABLE(tests, AC_HELP_STRING([--enable-tests=yes|no], diff --git a/tests/test-font-face.cc b/tests/test-font-face.cc index 973f956..fe7f5f0 100644 --- a/tests/test-font-face.cc +++ b/tests/test-font-face.cc @@ -7,11 +7,15 @@ */ #include <cfloat> +#include <stdexcept> #include <boost/test/unit_test.hpp> #include <boost/test/test_tools.hpp> #include <boost/test/floating_point_comparison.hpp> using namespace boost::unit_test; #include <cairomm/fontface.h> +#include <cairomm/scaledfont.h> + +static Cairo::Matrix identity_matrix; void test_create_toy () @@ -36,6 +40,196 @@ void test_toy_getters () BOOST_CHECK_EQUAL (Cairo::FONT_TYPE_TOY, toy->get_type()); } +void test_user_font_create() +{ + Cairo::RefPtr<Cairo::UserFontFace> font = Cairo::UserFontFace::create(); + BOOST_CHECK_EQUAL (Cairo::FONT_TYPE_USER, font->get_type()); +} + +// create some dummy callbacks +static unsigned int init_call_count = 0; +Cairo::ErrorStatus my_init(const Cairo::RefPtr<Cairo::ScaledFont>&, const Cairo::RefPtr<Cairo::Context>&, Cairo::FontExtents&) +{ + init_call_count++; + return CAIRO_STATUS_SUCCESS; +} + +static unsigned int unicode_to_glyph_call_count = 0; +Cairo::ErrorStatus my_unicode_to_glyph(const Cairo::RefPtr<Cairo::ScaledFont>&, unsigned long, unsigned long&) +{ + unicode_to_glyph_call_count++; + return CAIRO_STATUS_SUCCESS; +} + +static unsigned int render_glyph_call_count = 0; +Cairo::ErrorStatus my_render_glyph(const Cairo::RefPtr<Cairo::ScaledFont>&, unsigned long, const Cairo::RefPtr<Cairo::Context>&, Cairo::TextExtents&) +{ + render_glyph_call_count++; + return CAIRO_STATUS_SUCCESS; +} + +void test_user_font_callbacks_ptr() +{ + Cairo::RefPtr<Cairo::UserFontFace> font = Cairo::UserFontFace::create(); + BOOST_CHECK(font); + font->set_init_func(sigc::ptr_fun(my_init)); + font->set_unicode_to_glyph_func(sigc::ptr_fun(my_unicode_to_glyph)); + font->set_render_glyph_func(sigc::ptr_fun(my_render_glyph)); + Cairo::RefPtr<Cairo::ScaledFont> scaled_font = + Cairo::ScaledFont::create(font, identity_matrix, identity_matrix, + Cairo::FontOptions()); + BOOST_CHECK (init_call_count > 0); + Cairo::RefPtr<Cairo::ImageSurface> surface = + Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, 100, 100); + Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create(surface); + cr->set_font_face(font); + cr->show_text("Hello, world"); + BOOST_CHECK (unicode_to_glyph_call_count > 0); + BOOST_CHECK (render_glyph_call_count > 0); +} + +struct UserFontCallbacks +{ +Cairo::ErrorStatus init(const Cairo::RefPtr<Cairo::ScaledFont>&, const Cairo::RefPtr<Cairo::Context>&, Cairo::FontExtents&) +{ + init_call_count++; + return CAIRO_STATUS_SUCCESS; +} + +Cairo::ErrorStatus unicode_to_glyph(const Cairo::RefPtr<Cairo::ScaledFont>&, unsigned long, unsigned long&) +{ + unicode_to_glyph_call_count++; + return CAIRO_STATUS_SUCCESS; +} + +Cairo::ErrorStatus render_glyph(const Cairo::RefPtr<Cairo::ScaledFont>&, unsigned long, const Cairo::RefPtr<Cairo::Context>&, Cairo::TextExtents&) +{ + render_glyph_call_count++; + return CAIRO_STATUS_SUCCESS; +} + +static unsigned int init_call_count; +static unsigned int unicode_to_glyph_call_count; +static unsigned int render_glyph_call_count; +}; + +unsigned int UserFontCallbacks::init_call_count = 0; +unsigned int UserFontCallbacks::unicode_to_glyph_call_count = 0; +unsigned int UserFontCallbacks::render_glyph_call_count = 0; + +void test_user_font_callbacks_mem() +{ + Cairo::RefPtr<Cairo::UserFontFace> font = Cairo::UserFontFace::create(); + BOOST_CHECK(font); + UserFontCallbacks callbacks; + font->set_init_func(sigc::mem_fun(&callbacks, &UserFontCallbacks::init)); + font->set_unicode_to_glyph_func(sigc::mem_fun(&callbacks, + &UserFontCallbacks::unicode_to_glyph)); + font->set_render_glyph_func(sigc::mem_fun(&callbacks, + &UserFontCallbacks::render_glyph)); + Cairo::RefPtr<Cairo::ScaledFont> scaled_font = + Cairo::ScaledFont::create(font, identity_matrix, identity_matrix, + Cairo::FontOptions()); + BOOST_CHECK (UserFontCallbacks::init_call_count > 0); + Cairo::RefPtr<Cairo::ImageSurface> surface = + Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, 100, 100); + Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create(surface); + cr->set_font_face(font); + cr->show_text("Hello, world"); + BOOST_CHECK (UserFontCallbacks::unicode_to_glyph_call_count > 0); + BOOST_CHECK (UserFontCallbacks::render_glyph_call_count > 0); +} + +static unsigned int init_exception_call_count = 0; +Cairo::ErrorStatus my_init_exception(const Cairo::RefPtr<Cairo::ScaledFont>&, const Cairo::RefPtr<Cairo::Context>&, Cairo::FontExtents&) +{ + init_exception_call_count++; + throw std::logic_error("init exception"); +} + +static unsigned int unicode_to_glyph_exception_call_count = 0; +Cairo::ErrorStatus my_unicode_to_glyph_exception(const Cairo::RefPtr<Cairo::ScaledFont>&, unsigned long, unsigned long&) +{ + unicode_to_glyph_exception_call_count++; + throw std::logic_error("unicode-to-glyph exception"); +} + +static unsigned int render_glyph_exception_call_count = 0; +Cairo::ErrorStatus my_render_glyph_exception(const Cairo::RefPtr<Cairo::ScaledFont>&, unsigned long, const Cairo::RefPtr<Cairo::Context>&, Cairo::TextExtents&) +{ + render_glyph_exception_call_count++; + throw std::logic_error("render-glyph exception"); +} + + +void test_user_font_callbacks_exception() +{ + Cairo::RefPtr<Cairo::UserFontFace> font = Cairo::UserFontFace::create(); + BOOST_CHECK(font); + font->set_init_func(sigc::ptr_fun(my_init_exception)); + + // the init() callback will throw an exception, if this isn't handled in the + // callback wrapper, the program will abort since an exception can't unwind + // through C code. However, due to the exception being thrown, the create() + // function will fail and throw a new exception. So if the executable doesn't + // abort, we should get an exception here. + Cairo::RefPtr<Cairo::ScaledFont> scaled_font; + BOOST_CHECK_THROW (scaled_font = Cairo::ScaledFont::create(font, + identity_matrix, + identity_matrix, + Cairo::FontOptions()), + Cairo::logic_error); + BOOST_CHECK (init_exception_call_count > 0); + + // now initialize a scaled font properly so we can test the other callbacks + font = Cairo::UserFontFace::create(); + font->set_init_func(sigc::ptr_fun(my_init)); + font->set_render_glyph_func(sigc::ptr_fun(my_render_glyph_exception)); + font->set_unicode_to_glyph_func(sigc::ptr_fun(my_unicode_to_glyph_exception)); + BOOST_CHECK_NO_THROW (scaled_font = Cairo::ScaledFont::create(font, + identity_matrix, + identity_matrix, + Cairo::FontOptions())) + Cairo::RefPtr<Cairo::ImageSurface> surface = + Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, 100, 100); + Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create(surface); + cr->set_font_face(font); + // this call should throw an exception since the callback wrapper will return + // an error status (that will be translated into an exception) but the test + // shouldn't abort sindce the callback exceptions are handled by the callback + // wrapper + BOOST_REQUIRE_EQUAL (CAIRO_STATUS_SUCCESS, font->get_status()); + BOOST_CHECK_THROW(cr->show_text("Hello, world"), Cairo::logic_error); + BOOST_CHECK (UserFontCallbacks::unicode_to_glyph_call_count > 0); + BOOST_CHECK (UserFontCallbacks::render_glyph_call_count > 0); +} + +// create some dummy callbacks +static unsigned int init2_call_count = 0; +Cairo::ErrorStatus my_init2(const Cairo::RefPtr<Cairo::ScaledFont>&, const Cairo::RefPtr<Cairo::Context>&, Cairo::FontExtents&) +{ + init2_call_count++; + return CAIRO_STATUS_SUCCESS; +} + +void test_user_font_replace_callback() +{ + // reset + init_call_count = 0; + Cairo::RefPtr<Cairo::UserFontFace> font = Cairo::UserFontFace::create(); + font->set_init_func(sigc::ptr_fun(my_init)); + // now replace the init function with my_init2 and make sure that the 2nd + // function is called, not the first + font->set_init_func(sigc::ptr_fun(my_init2)); + Cairo::RefPtr<Cairo::ScaledFont> scaled_font; + BOOST_CHECK_NO_THROW (scaled_font = Cairo::ScaledFont::create(font, + identity_matrix, + identity_matrix, + Cairo::FontOptions())) + BOOST_CHECK (init2_call_count > 0); + BOOST_CHECK_EQUAL (init_call_count, 0); +} + test_suite* init_unit_test_suite(int argc, char* argv[]) @@ -43,10 +237,18 @@ init_unit_test_suite(int argc, char* argv[]) // compile even with -Werror if (argc && argv) {} + // setup + cairo_matrix_init_identity(&identity_matrix); + test_suite* test= BOOST_TEST_SUITE( "Cairo::FontFace Tests" ); test->add (BOOST_TEST_CASE (&test_create_toy)); test->add (BOOST_TEST_CASE (&test_toy_getters)); + test->add (BOOST_TEST_CASE (&test_user_font_create)); + test->add (BOOST_TEST_CASE (&test_user_font_callbacks_ptr)); + test->add (BOOST_TEST_CASE (&test_user_font_callbacks_mem)); + test->add (BOOST_TEST_CASE (&test_user_font_callbacks_exception)); + test->add (BOOST_TEST_CASE (&test_user_font_replace_callback)); return test; } |