diff --git a/TODO b/TODO index ef8f41329..577843f4d 100644 --- a/TODO +++ b/TODO @@ -23,8 +23,6 @@ API issues to fix before 1.0: - Add sanitize API (and a cached version, that saves result on blob user-data) -- hb_shape() currently does a bit more than hb_ot_shape(). Shouldn't. - - Add glib GBoxedType stuff and introspection diff --git a/configure.ac b/configure.ac index 3a6e5282f..3f13c4365 100644 --- a/configure.ac +++ b/configure.ac @@ -138,12 +138,21 @@ AM_CONDITIONAL(HAVE_FREETYPE, $have_freetype) dnl =========================================================================== +have_ot=true; +if $have_ot; then + AC_DEFINE(HAVE_OT, 1, [Have native OpenType Layout backend]) +fi +AM_CONDITIONAL(HAVE_OT, $have_ot) + +dnl =========================================================================== + AC_CHECK_HEADERS(usp10.h windows.h, have_uniscribe=true, have_uniscribe=false) if $have_uniscribe; then UNISCRIBE_CFLAGS= UNISCRIBE_LIBS="-lusp10 -lgdi32" AC_SUBST(UNISCRIBE_CFLAGS) AC_SUBST(UNISCRIBE_LIBS) + AC_DEFINE(HAVE_UNISCRIBE, 1, [Have Uniscribe backend]) fi AM_CONDITIONAL(HAVE_UNISCRIBE, $have_uniscribe) diff --git a/src/Makefile.am b/src/Makefile.am index c4f35e9aa..ff5a6d283 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,6 +18,8 @@ HBSOURCES = \ hb-buffer-private.hh \ hb-buffer.cc \ hb-common.cc \ + hb-fallback-shape-private.hh \ + hb-fallback-shape.cc \ hb-font-private.hh \ hb-font.cc \ hb-mutex-private.hh \ @@ -30,6 +32,7 @@ HBSOURCES = \ hb-shape.cc \ hb-unicode-private.hh \ hb-unicode.cc \ + hb-ot-tag.cc \ $(NULL) HBHEADERS = \ hb.h \ @@ -42,6 +45,11 @@ HBHEADERS = \ hb-version.h \ $(NULL) +MAINTAINERCLEANFILES += \ + $(srcdir)/hb-version.h \ + $(NULL) + +if HAVE_OT HBSOURCES += \ hb-ot-layout.cc \ hb-ot-layout-common-private.hh \ @@ -62,7 +70,6 @@ HBSOURCES += \ hb-ot-shape-complex-private.hh \ hb-ot-shape-normalize.cc \ hb-ot-shape-private.hh \ - hb-ot-tag.cc \ $(NULL) HBHEADERS += \ hb-ot.h \ @@ -70,10 +77,7 @@ HBHEADERS += \ hb-ot-shape.h \ hb-ot-tag.h \ $(NULL) - -MAINTAINERCLEANFILES += \ - $(srcdir)/hb-version.h \ - $(NULL) +endif if HAVE_GLIB HBCFLAGS += $(GLIB_CFLAGS) diff --git a/src/hb-buffer-private.hh b/src/hb-buffer-private.hh index 1a4a42379..f3f3ab0c3 100644 --- a/src/hb-buffer-private.hh +++ b/src/hb-buffer-private.hh @@ -94,6 +94,7 @@ struct _hb_buffer_t { HB_INTERNAL void reverse_range (unsigned int start, unsigned int end); HB_INTERNAL void reverse (void); HB_INTERNAL void reverse_clusters (void); + HB_INTERNAL void guess_properties (void); HB_INTERNAL void swap_buffers (void); HB_INTERNAL void clear_output (void); diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc index 749330b82..e19e7dd1a 100644 --- a/src/hb-buffer.cc +++ b/src/hb-buffer.cc @@ -402,6 +402,35 @@ hb_buffer_t::reverse_clusters (void) reverse_range (start, i); } +void +hb_buffer_t::guess_properties (void) +{ + /* If script is set to INVALID, guess from buffer contents */ + if (props.script == HB_SCRIPT_INVALID) { + for (unsigned int i = 0; i < len; i++) { + hb_script_t script = hb_unicode_script (unicode, info[i].codepoint); + if (likely (script != HB_SCRIPT_COMMON && + script != HB_SCRIPT_INHERITED && + script != HB_SCRIPT_UNKNOWN)) { + props.script = script; + break; + } + } + } + + /* If direction is set to INVALID, guess from script */ + if (props.direction == HB_DIRECTION_INVALID) { + props.direction = hb_script_get_horizontal_direction (props.script); + } + + /* If language is not set, use default language from locale */ + if (props.language == HB_LANGUAGE_INVALID) { + /* TODO get_default_for_script? using $LANGUAGE */ + props.language = hb_language_get_default (); + } +} + + static inline void dump_var_allocation (const hb_buffer_t *buffer) { @@ -675,6 +704,12 @@ hb_buffer_reverse_clusters (hb_buffer_t *buffer) buffer->reverse_clusters (); } +void +hb_buffer_guess_properties (hb_buffer_t *buffer) +{ + buffer->guess_properties (); +} + #define ADD_UTF(T) \ HB_STMT_START { \ const T *next = (const T *) text + item_offset; \ diff --git a/src/hb-buffer.h b/src/hb-buffer.h index 020a12079..1c4a9a7f5 100644 --- a/src/hb-buffer.h +++ b/src/hb-buffer.h @@ -132,6 +132,9 @@ hb_buffer_reverse (hb_buffer_t *buffer); void hb_buffer_reverse_clusters (hb_buffer_t *buffer); +void +hb_buffer_guess_properties (hb_buffer_t *buffer); + /* Filling the buffer in */ diff --git a/src/hb-fallback-shape-private.hh b/src/hb-fallback-shape-private.hh new file mode 100644 index 000000000..40e9541fd --- /dev/null +++ b/src/hb-fallback-shape-private.hh @@ -0,0 +1,48 @@ +/* + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_FALLBACK_SHAPE_PRIVATE_HH +#define HB_FALLBACK_SHAPE_PRIVATE_HH + +#include "hb-private.hh" + +#include "hb-shape.h" + + +HB_BEGIN_DECLS + + +HB_INTERNAL hb_bool_t +hb_fallback_shape (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features, + const char *shaper_options); + + +HB_END_DECLS + +#endif /* HB_FALLBACK_SHAPE_PRIVATE_HH */ diff --git a/src/hb-fallback-shape.cc b/src/hb-fallback-shape.cc new file mode 100644 index 000000000..6c3200d07 --- /dev/null +++ b/src/hb-fallback-shape.cc @@ -0,0 +1,43 @@ +/* + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-fallback-shape-private.hh" + +#include "hb-buffer-private.hh" + +hb_bool_t +hb_fallback_shape (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features, + const char *shaper_options) +{ + buffer->guess_properties (); + + return TRUE; +} + + diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc index 5f8b37220..fc2fa4188 100644 --- a/src/hb-ot-shape.cc +++ b/src/hb-ot-shape.cc @@ -428,16 +428,21 @@ hb_ot_shape_execute (hb_ot_shape_plan_t *plan, hb_ot_shape_execute_internal (&c); } -void +hb_bool_t hb_ot_shape (hb_font_t *font, hb_buffer_t *buffer, const hb_feature_t *features, - unsigned int num_features) + unsigned int num_features, + const char *shaper_options) { hb_ot_shape_plan_t plan; + buffer->guess_properties (); + hb_ot_shape_plan_internal (&plan, font->face, &buffer->props, features, num_features); hb_ot_shape_execute (&plan, font, buffer, features, num_features); + + return TRUE; } diff --git a/src/hb-ot-shape.h b/src/hb-ot-shape.h index f9560e57e..0fa3f3629 100644 --- a/src/hb-ot-shape.h +++ b/src/hb-ot-shape.h @@ -34,11 +34,12 @@ HB_BEGIN_DECLS -void +hb_bool_t hb_ot_shape (hb_font_t *font, hb_buffer_t *buffer, const hb_feature_t *features, - unsigned int num_features); + unsigned int num_features, + const char *shaper_options); HB_END_DECLS diff --git a/src/hb-shape.cc b/src/hb-shape.cc index 1b802d602..9ebbd81bd 100644 --- a/src/hb-shape.cc +++ b/src/hb-shape.cc @@ -30,62 +30,89 @@ #include "hb-buffer-private.hh" -#include "hb-ot-shape.h" - -#ifdef HAVE_GRAPHITE -#include "hb-graphite.h" +#ifdef HAVE_UNISCRIBE +# include "hb-uniscribe.h" #endif +#ifdef HAVE_OT +# include "hb-ot-shape.h" +#endif +#include "hb-fallback-shape-private.hh" +typedef hb_bool_t (*hb_shape_func_t) (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features, + const char *shaper_options); +#define HB_SHAPER_IMPLEMENT(name) {#name, hb_##name##_shape} +static const struct hb_shaper_pair_t { + const char name[16]; + hb_shape_func_t func; +} shapers[] = { + /* v--- Add new shapers in the right place here */ +#ifdef HAVE_UNISCRIBE + HB_SHAPER_IMPLEMENT (uniscribe), +#endif +#ifdef HAVE_OT + HB_SHAPER_IMPLEMENT (ot), +#endif + HB_SHAPER_IMPLEMENT (fallback) /* should be last */ +}; +#undef HB_SHAPER_IMPLEMENT -static void -hb_shape_internal (hb_font_t *font, - hb_buffer_t *buffer, - const hb_feature_t *features, - unsigned int num_features) +static class static_shaper_list_t { + public: + static_shaper_list_t (void) { + char *env = getenv ("HB_SHAPER_LIST"); + shaper_list = NULL; + if (!env || !*env) + return; + unsigned int count = 3; /* initial, fallback, null */ + for (const char *p = env; (p == strchr (p, ':')) && p++; ) + count++; + if (count <= ARRAY_LENGTH (static_shaper_list)) + shaper_list = static_shaper_list; + else + shaper_list = (const char **) malloc (count * sizeof (shaper_list[0])); + + count = 0; + shaper_list[count++] = env; + for (char *p = env; (p == strchr (p, ':')) && (*p = '\0', TRUE) && p++; ) + shaper_list[count++] = p; + shaper_list[count++] = "fallback"; + shaper_list[count] = NULL; + } + const char **shaper_list; + const char *static_shaper_list[10]; +} env_shaper_list; + +hb_bool_t +hb_shape (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features, + const char *shaper_options, + const char **shaper_list) { - hb_ot_shape (font, buffer, features, num_features); -} + if (likely (!shaper_list)) + shaper_list = env_shaper_list.shaper_list; -void -hb_shape (hb_font_t *font, - hb_buffer_t *buffer, - const hb_feature_t *features, - unsigned int num_features) -{ - hb_segment_properties_t orig_props; - - orig_props = buffer->props; - - /* If script is set to INVALID, guess from buffer contents */ - if (buffer->props.script == HB_SCRIPT_INVALID) { - hb_unicode_funcs_t *unicode = buffer->unicode; - unsigned int count = buffer->len; - for (unsigned int i = 0; i < count; i++) { - hb_script_t script = hb_unicode_script (unicode, buffer->info[i].codepoint); - if (likely (script != HB_SCRIPT_COMMON && - script != HB_SCRIPT_INHERITED && - script != HB_SCRIPT_UNKNOWN)) { - buffer->props.script = script; - break; - } + if (likely (!shaper_list)) { + for (unsigned int i = 0; i < ARRAY_LENGTH (shapers); i++) + if (likely (shapers[i].func (font, buffer, + features, num_features, + shaper_options))) + return TRUE; + } else { + while (*shaper_list) { + for (unsigned int i = 0; i < ARRAY_LENGTH (shapers); i++) + if (0 == strcmp (*shaper_list, shapers[i].name) && + likely (shapers[i].func (font, buffer, + features, num_features, + shaper_options))) + return TRUE; + shaper_list++; } } - - /* If direction is set to INVALID, guess from script */ - if (buffer->props.direction == HB_DIRECTION_INVALID) { - buffer->props.direction = hb_script_get_horizontal_direction (buffer->props.script); - } - - /* If language is not set, use default language from locale */ - if (buffer->props.language == HB_LANGUAGE_INVALID) { - /* TODO get_default_for_script? using $LANGUAGE */ - buffer->props.language = hb_language_get_default (); - } - - hb_shape_internal (font, buffer, features, num_features); - - buffer->props = orig_props; + return FALSE; } - - diff --git a/src/hb-shape.h b/src/hb-shape.h index 39383af5e..b2193f749 100644 --- a/src/hb-shape.h +++ b/src/hb-shape.h @@ -41,11 +41,14 @@ typedef struct _hb_feature_t { unsigned int end; } hb_feature_t; -void -hb_shape (hb_font_t *font, - hb_buffer_t *buffer, - const hb_feature_t *features, - unsigned int num_features); + +hb_bool_t +hb_shape (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features, + const char *shaper_options, + const char **shaper_list); HB_END_DECLS diff --git a/src/hb-uniscribe-shape.cc b/src/hb-uniscribe-shape.cc index 3d5b1d57c..6c93f3b72 100644 --- a/src/hb-uniscribe-shape.cc +++ b/src/hb-uniscribe-shape.cc @@ -56,13 +56,6 @@ DWORD GetFontData( ); */ -static void -fallback_shape (hb_font_t *font, - hb_buffer_t *buffer) -{ - DEBUG_MSG (UNISCRIBE, NULL, "Fallback shaper invoked"); -} - static void populate_log_font (LOGFONTW *lf, HDC hdc, @@ -78,18 +71,19 @@ populate_log_font (LOGFONTW *lf, lf->lfFaceName[i] = family_name[i]; } -void +hb_bool_t hb_uniscribe_shape (hb_font_t *font, hb_buffer_t *buffer, const hb_feature_t *features, - unsigned int num_features) + unsigned int num_features, + const char *shaper_options) { + buffer->guess_properties (); + HRESULT hr; - if (unlikely (!buffer->len)) { - fallback: - fallback_shape (font, buffer); - } + if (unlikely (!buffer->len)) + return TRUE; retry: @@ -138,10 +132,10 @@ retry: ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size); -#define FALLBACK(...) \ +#define FAIL(...) \ HB_STMT_START { \ DEBUG_MSG (UNISCRIBE, NULL, __VA_ARGS__); \ - goto fallback; \ + return FALSE; \ } HB_STMT_END; @@ -164,7 +158,7 @@ retry: script_tags, &item_count); if (unlikely (FAILED (hr))) - FALLBACK ("ScriptItemizeOpenType() failed: %d", hr); + FAIL ("ScriptItemizeOpenType() failed: %d", hr); #undef MAX_ITEMS @@ -178,17 +172,13 @@ retry: hb_blob_t *blob = hb_face_get_blob (font->face); unsigned int blob_length; const char *blob_data = hb_blob_get_data (blob, &blob_length); - if (unlikely (!blob_length)) { - hb_blob_destroy (blob); - FALLBACK ("Empty font blob"); - } + if (unlikely (!blob_length)) + FAIL ("Empty font blob"); DWORD num_fonts_installed; HANDLE fh = AddFontMemResourceEx ((void *) blob_data, blob_length, 0, &num_fonts_installed); - if (unlikely (!fh)) { - hb_blob_destroy (blob); - FALLBACK ("AddFontMemResourceEx() failed"); - } + if (unlikely (!fh)) + FAIL ("AddFontMemResourceEx() failed"); /* FREE stuff, specially when taking fallback... */ @@ -230,16 +220,16 @@ retry: (int *) &glyphs_len); if (unlikely (items[i].a.fNoGlyphIndex)) - FALLBACK ("ScriptShapeOpenType() set fNoGlyphIndex"); + FAIL ("ScriptShapeOpenType() set fNoGlyphIndex"); if (unlikely (hr == E_OUTOFMEMORY)) { buffer->ensure (buffer->allocated * 2); if (buffer->in_error) - FALLBACK ("Buffer resize failed"); + FAIL ("Buffer resize failed"); goto retry; } if (unlikely (FAILED (hr))) - FALLBACK ("ScriptShapeOpenType() failed: %d", hr); + FAIL ("ScriptShapeOpenType() failed: %d", hr); hr = ScriptPlaceOpenType (hdc, &script_cache, @@ -261,7 +251,7 @@ retry: offsets + glyphs_offset, NULL); if (unlikely (FAILED (hr))) - FALLBACK ("ScriptPlaceOpenType() failed: %d", hr); + FAIL ("ScriptPlaceOpenType() failed: %d", hr); glyphs_offset += glyphs_len; } @@ -285,9 +275,9 @@ retry: buffer->ensure (glyphs_len); if (buffer->in_error) - FALLBACK ("Buffer in error"); + FAIL ("Buffer in error"); -#undef FALLBACK +#undef FAIL /* Set glyph infos */ for (unsigned int i = 0; i < glyphs_len; i++) @@ -317,7 +307,7 @@ retry: } /* Wow, done! */ - return; + return TRUE; } diff --git a/src/hb-uniscribe.h b/src/hb-uniscribe.h index d498f975a..aa893bd3d 100644 --- a/src/hb-uniscribe.h +++ b/src/hb-uniscribe.h @@ -34,11 +34,12 @@ HB_BEGIN_DECLS -void +hb_bool_t hb_uniscribe_shape (hb_font_t *font, hb_buffer_t *buffer, const hb_feature_t *features, - unsigned int num_features); + unsigned int num_features, + const char *shaper_options); HB_END_DECLS diff --git a/src/hb-view.cc b/src/hb-view.cc index dc3fc47f7..67070ef32 100644 --- a/src/hb-view.cc +++ b/src/hb-view.cc @@ -370,7 +370,7 @@ _hb_cr_text_glyphs (cairo_t *cr, len = strlen (utf8); hb_buffer_add_utf8 (hb_buffer, utf8, len, 0, len); - hb_shape (hb_font, hb_buffer, features, num_features); + hb_shape (hb_font, hb_buffer, features, num_features, NULL, NULL); num_glyphs = hb_buffer_get_length (hb_buffer); hb_glyph = hb_buffer_get_glyph_infos (hb_buffer, NULL); diff --git a/src/test.cc b/src/test.cc index 54141bc61..8679b333a 100644 --- a/src/test.cc +++ b/src/test.cc @@ -30,7 +30,6 @@ #endif #include "hb.h" -#include "hb-uniscribe.h" #ifdef HAVE_GLIB #include @@ -97,7 +96,7 @@ main (int argc, char **argv) hb_buffer_add_utf8 (buffer, "test", 4, 0, 4); - hb_uniscribe_shape (font, buffer, NULL, 0); + hb_shape (font, buffer, NULL, 0, NULL, NULL); unsigned int count = hb_buffer_get_length (buffer); hb_glyph_info_t *infos = hb_buffer_get_glyph_infos (buffer, NULL); diff --git a/test/test-shape.c b/test/test-shape.c index 5a41f0c4b..a8b7f1de9 100644 --- a/test/test-shape.c +++ b/test/test-shape.c @@ -110,7 +110,7 @@ test_shape (void) hb_buffer_set_direction (buffer, HB_DIRECTION_LTR); hb_buffer_add_utf8 (buffer, TesT, 4, 0, 4); - hb_shape (font, buffer, NULL, 0); + hb_shape (font, buffer, NULL, 0, NULL, NULL); len = hb_buffer_get_length (buffer); glyphs = hb_buffer_get_glyph_infos (buffer, NULL);