summaryrefslogtreecommitdiff
path: root/src/hb-shape.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/hb-shape.cc')
-rw-r--r--src/hb-shape.cc394
1 files changed, 282 insertions, 112 deletions
diff --git a/src/hb-shape.cc b/src/hb-shape.cc
index 163a5bf..4e22c61 100644
--- a/src/hb-shape.cc
+++ b/src/hb-shape.cc
@@ -1,5 +1,6 @@
/*
* Copyright © 2009 Red Hat, Inc.
+ * Copyright © 2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
@@ -22,128 +23,279 @@
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
*/
#include "hb-private.hh"
-#include "hb-shape.h"
-
+#include "hb-shaper-private.hh"
+#include "hb-shape-plan-private.hh"
#include "hb-buffer-private.hh"
+#include "hb-font-private.hh"
-#ifdef HAVE_GRAPHITE
-#include "hb-graphite2-private.hh"
-#endif
-#ifdef HAVE_UNISCRIBE
-# include "hb-uniscribe-private.hh"
-#endif
-#ifdef HAVE_OT
-# include "hb-ot-shape-private.hh"
-#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);
-
-#define HB_SHAPER_IMPLEMENT(name) {#name, _hb_##name##_shape}
-static const struct hb_shaper_pair_t {
- char name[16];
- hb_shape_func_t func;
-} all_shapers[] = {
- /* v--- Add new shapers in the right place here */
-#ifdef HAVE_GRAPHITE
- HB_SHAPER_IMPLEMENT (graphite2),
-#endif
-#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 bool
+parse_space (const char **pp, const char *end)
+{
+ while (*pp < end && ISSPACE (**pp))
+ (*pp)++;
+ return true;
+}
-/* Thread-safe, lock-free, shapers */
+static bool
+parse_char (const char **pp, const char *end, char c)
+{
+ parse_space (pp, end);
-static hb_shaper_pair_t *static_shapers;
+ if (*pp == end || **pp != c)
+ return false;
-static
-void free_static_shapers (void)
+ (*pp)++;
+ return true;
+}
+
+static bool
+parse_uint (const char **pp, const char *end, unsigned int *pv)
{
- if (unlikely (static_shapers != all_shapers))
- free (static_shapers);
+ char buf[32];
+ unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
+ strncpy (buf, *pp, len);
+ buf[len] = '\0';
+
+ char *p = buf;
+ char *pend = p;
+ unsigned int v;
+
+ /* Intentionally use strtol instead of strtoul, such that
+ * -1 turns into "big number"... */
+ errno = 0;
+ v = strtol (p, &pend, 0);
+ if (errno || p == pend)
+ return false;
+
+ *pv = v;
+ *pp += pend - p;
+ return true;
}
-static const hb_shaper_pair_t *
-get_shapers (void)
+static bool
+parse_bool (const char **pp, const char *end, unsigned int *pv)
{
-retry:
- hb_shaper_pair_t *shapers = (hb_shaper_pair_t *) hb_atomic_ptr_get (&static_shapers);
+ parse_space (pp, end);
+
+ const char *p = *pp;
+ while (*pp < end && ISALPHA(**pp))
+ (*pp)++;
+
+ /* CSS allows on/off as aliases 1/0. */
+ if (*pp - p == 2 || 0 == strncmp (p, "on", 2))
+ *pv = 1;
+ else if (*pp - p == 3 || 0 == strncmp (p, "off", 2))
+ *pv = 0;
+ else
+ return false;
+
+ return true;
+}
+
+static bool
+parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
+{
+ if (parse_char (pp, end, '-'))
+ feature->value = 0;
+ else {
+ parse_char (pp, end, '+');
+ feature->value = 1;
+ }
+
+ return true;
+}
- if (unlikely (!shapers))
+static bool
+parse_feature_tag (const char **pp, const char *end, hb_feature_t *feature)
+{
+ parse_space (pp, end);
+
+ char quote = 0;
+
+ if (*pp < end && (**pp == '\'' || **pp == '"'))
{
- char *env = getenv ("HB_SHAPER_LIST");
- if (!env || !*env) {
- hb_atomic_ptr_cmpexch (&static_shapers, NULL, (const hb_shaper_pair_t *) all_shapers);
- return (const hb_shaper_pair_t *) all_shapers;
- }
+ quote = **pp;
+ (*pp)++;
+ }
- /* Not found; allocate one. */
- shapers = (hb_shaper_pair_t *) malloc (sizeof (all_shapers));
- if (unlikely (!shapers))
- return (const hb_shaper_pair_t *) all_shapers;
- memcpy (shapers, all_shapers, sizeof (all_shapers));
-
- /* Reorder shaper list to prefer requested shapers. */
- unsigned int i = 0;
- char *end, *p = env;
- for (;;) {
- end = strchr (p, ',');
- if (!end)
- end = p + strlen (p);
-
- for (unsigned int j = i; j < ARRAY_LENGTH (all_shapers); j++)
- if (end - p == (int) strlen (shapers[j].name) &&
- 0 == strncmp (shapers[j].name, p, end - p))
- {
- /* Reorder this shaper to position i */
- struct hb_shaper_pair_t t = shapers[j];
- memmove (&shapers[i + 1], &shapers[i], sizeof (shapers[i]) * (j - i));
- shapers[i] = t;
- i++;
- }
-
- if (!*end)
- break;
- else
- p = end + 1;
- }
+ const char *p = *pp;
+ while (*pp < end && ISALNUM(**pp))
+ (*pp)++;
- if (!hb_atomic_ptr_cmpexch (&static_shapers, NULL, shapers)) {
- free (shapers);
- goto retry;
- }
+ if (p == *pp || *pp - p > 4)
+ return false;
-#ifdef HAVE_ATEXIT
- atexit (free_static_shapers); /* First person registers atexit() callback. */
-#endif
+ feature->tag = hb_tag_from_string (p, *pp - p);
+
+ if (quote)
+ {
+ /* CSS expects exactly four bytes. And we only allow quotations for
+ * CSS compatibility. So, enforce the length. */
+ if (*pp - p != 4)
+ return false;
+ if (*pp == end || **pp != quote)
+ return false;
+ (*pp)++;
+ }
+
+ return true;
+}
+
+static bool
+parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
+{
+ parse_space (pp, end);
+
+ bool has_start;
+
+ feature->start = 0;
+ feature->end = (unsigned int) -1;
+
+ if (!parse_char (pp, end, '['))
+ return true;
+
+ has_start = parse_uint (pp, end, &feature->start);
+
+ if (parse_char (pp, end, ':')) {
+ parse_uint (pp, end, &feature->end);
+ } else {
+ if (has_start)
+ feature->end = feature->start + 1;
}
- return shapers;
+ return parse_char (pp, end, ']');
+}
+
+static bool
+parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
+{
+ bool had_equal = parse_char (pp, end, '=');
+ bool had_value = parse_uint (pp, end, &feature->value) ||
+ parse_bool (pp, end, &feature->value);
+ /* CSS doesn't use equal-sign between tag and value.
+ * If there was an equal-sign, then there *must* be a value.
+ * A value without an eqaul-sign is ok, but not required. */
+ return !had_equal || had_value;
+}
+
+
+static bool
+parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
+{
+ return parse_feature_value_prefix (pp, end, feature) &&
+ parse_feature_tag (pp, end, feature) &&
+ parse_feature_indices (pp, end, feature) &&
+ parse_feature_value_postfix (pp, end, feature) &&
+ parse_space (pp, end) &&
+ *pp == end;
+}
+
+/**
+ * hb_feature_from_string:
+ * @str: (array length=len) (element-type uint8_t):
+ * @len:
+ * @feature: (out) (optional):
+ *
+ *
+ *
+ * Return value:
+ *
+ * Since: 1.0
+ **/
+hb_bool_t
+hb_feature_from_string (const char *str, int len,
+ hb_feature_t *feature)
+{
+ hb_feature_t feat;
+
+ if (len < 0)
+ len = strlen (str);
+
+ if (likely (parse_one_feature (&str, str + len, &feat)))
+ {
+ if (feature)
+ *feature = feat;
+ return true;
+ }
+
+ if (feature)
+ memset (feature, 0, sizeof (*feature));
+ return false;
+}
+
+/**
+ * hb_feature_to_string:
+ * @feature:
+ * @buf: (array length=size):
+ * @size:
+ *
+ *
+ *
+ * Since: 1.0
+ **/
+void
+hb_feature_to_string (hb_feature_t *feature,
+ char *buf, unsigned int size)
+{
+ if (unlikely (!size)) return;
+
+ char s[128];
+ unsigned int len = 0;
+ if (feature->value == 0)
+ s[len++] = '-';
+ hb_tag_to_string (feature->tag, s + len);
+ len += 4;
+ while (len && s[len - 1] == ' ')
+ len--;
+ if (feature->start != 0 || feature->end != (unsigned int) -1)
+ {
+ s[len++] = '[';
+ if (feature->start)
+ len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
+ if (feature->end != feature->start + 1) {
+ s[len++] = ':';
+ if (feature->end != (unsigned int) -1)
+ len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
+ }
+ s[len++] = ']';
+ }
+ if (feature->value > 1)
+ {
+ s[len++] = '=';
+ len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
+ }
+ assert (len < ARRAY_LENGTH (s));
+ len = MIN (len, size - 1);
+ memcpy (buf, s, len);
+ buf[len] = '\0';
}
static const char **static_shaper_list;
+#ifdef HB_USE_ATEXIT
static
void free_static_shaper_list (void)
{
free (static_shaper_list);
}
+#endif
+/**
+ * hb_shape_list_shapers:
+ *
+ *
+ *
+ * Return value: (transfer none):
+ *
+ * Since: 1.0
+ **/
const char **
hb_shape_list_shapers (void)
{
@@ -153,15 +305,15 @@ retry:
if (unlikely (!shaper_list))
{
/* Not found; allocate one. */
- shaper_list = (const char **) calloc (1 + ARRAY_LENGTH (all_shapers), sizeof (const char *));
+ shaper_list = (const char **) calloc (1 + HB_SHAPERS_COUNT, sizeof (const char *));
if (unlikely (!shaper_list)) {
static const char *nil_shaper_list[] = {NULL};
return nil_shaper_list;
}
- const hb_shaper_pair_t *shapers = get_shapers ();
+ const hb_shaper_pair_t *shapers = _hb_shapers_get ();
unsigned int i;
- for (i = 0; i < ARRAY_LENGTH (all_shapers); i++)
+ for (i = 0; i < HB_SHAPERS_COUNT; i++)
shaper_list[i] = shapers[i].name;
shaper_list[i] = NULL;
@@ -170,7 +322,7 @@ retry:
goto retry;
}
-#ifdef HAVE_ATEXIT
+#ifdef HB_USE_ATEXIT
atexit (free_static_shaper_list); /* First person registers atexit() callback. */
#endif
}
@@ -179,6 +331,20 @@ retry:
}
+/**
+ * hb_shape_full:
+ * @font: a font.
+ * @buffer: a buffer.
+ * @features: (array length=num_features):
+ * @num_features:
+ * @shaper_list: (array zero-terminated=1):
+ *
+ *
+ *
+ * Return value:
+ *
+ * Since: 1.0
+ **/
hb_bool_t
hb_shape_full (hb_font_t *font,
hb_buffer_t *buffer,
@@ -186,27 +352,31 @@ hb_shape_full (hb_font_t *font,
unsigned int num_features,
const char * const *shaper_list)
{
- hb_font_make_immutable (font); /* So we can safely cache stuff on it */
+ if (unlikely (!buffer->len))
+ return true;
- if (likely (!shaper_list)) {
- const hb_shaper_pair_t *shapers = get_shapers ();
- for (unsigned int i = 0; i < ARRAY_LENGTH (all_shapers); i++)
- if (likely (shapers[i].func (font, buffer, features, num_features)))
- return true;
- } else {
- while (*shaper_list) {
- for (unsigned int i = 0; i < ARRAY_LENGTH (all_shapers); i++)
- if (0 == strcmp (*shaper_list, all_shapers[i].name)) {
- if (likely (all_shapers[i].func (font, buffer, features, num_features)))
- return true;
- break;
- }
- shaper_list++;
- }
- }
- return false;
+ assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE);
+
+ hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props, features, num_features, shaper_list);
+ hb_bool_t res = hb_shape_plan_execute (shape_plan, font, buffer, features, num_features);
+ hb_shape_plan_destroy (shape_plan);
+
+ if (res)
+ buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS;
+ return res;
}
+/**
+ * hb_shape:
+ * @font: a font.
+ * @buffer: a buffer.
+ * @features: (array length=num_features):
+ * @num_features:
+ *
+ *
+ *
+ * Since: 1.0
+ **/
void
hb_shape (hb_font_t *font,
hb_buffer_t *buffer,