[libgxps] Improve text rendering
- From: Carlos Garcia Campos <carlosgc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgxps] Improve text rendering
- Date: Sun, 9 Oct 2011 15:47:39 +0000 (UTC)
commit 9857869e2b0b6c6358cc9f90540791bd315883e2
Author: Jason Crain <jason aquaticape us>
Date: Sun Oct 9 13:01:40 2011 +0200
Improve text rendering
- Clusters handling
- Vertical and horizontal offsets
- UnicodeString beginning with the escape sequence {}
libgxps/gxps-page.c | 353 +++++++++++++++++++++++++++++++++++---------------
1 files changed, 247 insertions(+), 106 deletions(-)
---
diff --git a/libgxps/gxps-page.c b/libgxps/gxps-page.c
index aef57e6..fa13720 100644
--- a/libgxps/gxps-page.c
+++ b/libgxps/gxps-page.c
@@ -2095,30 +2095,73 @@ glyphs_indices_parse_error (GlyphsIndicesToken *token,
token->iter);
}
-static gboolean
-glyphs_indices_parse (const gchar *data,
- gdouble font_size,
- gdouble *advance_widths,
- cairo_glyph_t *glyph_list,
- guint num_glyphs,
- cairo_text_cluster_t *cluster_list,
- guint num_clusters,
- GError **error)
-{
- GlyphsIndicesToken token;
- gint i = 0;
-
- token.iter = (gchar *)data;
- token.end = token.iter + strlen (data);
-
- glyphs_indices_iter_next (&token);
- if (G_UNLIKELY (token.type == GI_TOKEN_EOF))
- return TRUE;
+static gulong
+glyphs_lookup_index (cairo_scaled_font_t *scaled_font,
+ const gchar *utf8)
+{
+ cairo_status_t status;
+ cairo_glyph_t stack_glyphs[1];
+ cairo_glyph_t *glyphs = stack_glyphs;
+ int num_glyphs = 1;
+ int utf8_len = g_utf8_next_char (utf8) - utf8;
+ gulong index = 0;
+
+ if (utf8 == NULL || *utf8 == '\0')
+ return index;
+
+ status = cairo_scaled_font_text_to_glyphs (scaled_font,
+ 0, 0,
+ utf8, utf8_len,
+ &glyphs, &num_glyphs,
+ NULL, NULL, NULL);
+
+ if (status == CAIRO_STATUS_SUCCESS) {
+ index = glyphs[0].index;
+ if (glyphs != stack_glyphs)
+ cairo_glyph_free (glyphs);
+ }
- do {
+ return index;
+}
+static gboolean
+glyphs_indices_parse (const char *indices,
+ cairo_scaled_font_t *scaled_font,
+ gdouble x,
+ gdouble y,
+ const char *utf8,
+ GArray *glyph_array,
+ GArray *cluster_array,
+ GError **error)
+{
+ GlyphsIndicesToken token;
+ cairo_text_cluster_t cluster;
+ cairo_glyph_t glyph;
+ gint cluster_pos = 1;
+ gboolean have_index = FALSE;
+ gdouble advance_width;
+ gdouble advance_height;
+ gboolean have_advance_width = FALSE;
+ gdouble h_offset = 0;
+ gdouble v_offset = 0;
+ cairo_matrix_t font_matrix;
+ gboolean eof = FALSE;
+
+ cairo_scaled_font_get_font_matrix (scaled_font, &font_matrix);
+
+ cluster.num_glyphs = 1;
+ cluster.num_bytes = 0;
+
+ token.iter = (gchar *)indices;
+ token.end = token.iter + strlen (indices);
+ glyphs_indices_iter_next (&token);
+
+ while (1) {
switch (token.type) {
- case GI_TOKEN_START_CLUSTER:
+ case GI_TOKEN_START_CLUSTER: {
+ gint num_code_units;
+ const gchar *utf8_unit_end;
+
glyphs_indices_iter_next (&token);
if (token.type != GI_TOKEN_NUMBER) {
glyphs_indices_parse_error (&token,
@@ -2126,10 +2169,27 @@ glyphs_indices_parse (const gchar *data,
error);
return FALSE;
}
- /* FIXME:
- cluster_list[i].num_bytes = (gint)token.number;*/
+
+ /* Spec defines ClusterCodeUnitCount in terms of UTF-16 code units */
+ num_code_units = (gint)token.number;
+ utf8_unit_end = utf8;
+
+ while (utf8 && num_code_units > 0) {
+ gunichar utf8_char = g_utf8_get_char (utf8_unit_end);
+
+ if (*utf8_unit_end != '\0')
+ utf8_unit_end = g_utf8_next_char (utf8_unit_end);
+
+ num_code_units--;
+ if (utf8_char > 0xFFFF) /* 2 code units */
+ num_code_units--;
+ }
+ cluster.num_bytes = utf8_unit_end - utf8;
glyphs_indices_iter_next (&token);
+ if (token.type == GI_TOKEN_END_CLUSTER)
+ break;
+
if (token.type != GI_TOKEN_COLON) {
glyphs_indices_parse_error (&token,
GI_TOKEN_COLON,
@@ -2145,8 +2205,9 @@ glyphs_indices_parse (const gchar *data,
return FALSE;
}
- /* FIXME:
- cluster_list[i].num_glyphs = (gint)token.number;*/
+
+ cluster.num_glyphs = (gint)token.number;
+ cluster_pos = (gint)token.number;
glyphs_indices_iter_next (&token);
if (token.type != GI_TOKEN_END_CLUSTER) {
@@ -2155,14 +2216,17 @@ glyphs_indices_parse (const gchar *data,
error);
return FALSE;
}
+ }
break;
case GI_TOKEN_NUMBER:
- glyph_list[i].index = (gint)token.number;
+ glyph.index = (gint)token.number;
+ have_index = TRUE;
break;
case GI_TOKEN_COMMA:
glyphs_indices_iter_next (&token);
if (token.type == GI_TOKEN_NUMBER) {
- advance_widths[i] = token.number * font_size / 100.0;
+ advance_width = token.number / 100.0;
+ have_advance_width = TRUE;
glyphs_indices_iter_next (&token);
}
@@ -2171,7 +2235,7 @@ glyphs_indices_parse (const gchar *data,
glyphs_indices_iter_next (&token);
if (token.type == GI_TOKEN_NUMBER) {
- advance_widths[i] += token.number / 100.0;
+ h_offset = token.number / 100.0;
glyphs_indices_iter_next (&token);
}
@@ -2186,11 +2250,54 @@ glyphs_indices_parse (const gchar *data,
return FALSE;
}
- /* TODO: vOffset */
+ v_offset = token.number / 100.0;
break;
- case GI_TOKEN_SEMICOLON:
- i++;
+ case GI_TOKEN_EOF:
+ eof = TRUE;
+ case GI_TOKEN_SEMICOLON: {
+ cairo_text_extents_t extents;
+
+ if (!have_index)
+ glyph.index = glyphs_lookup_index (scaled_font, utf8);
+
+ cairo_matrix_transform_distance (&font_matrix, &h_offset, &v_offset);
+ glyph.x = x + h_offset;
+ glyph.y = y - v_offset;
+
+ cairo_scaled_font_glyph_extents (scaled_font, &glyph, 1, &extents);
+
+ if (!have_advance_width) {
+ advance_height = 0;
+ advance_width = extents.x_advance;
+ } else {
+ advance_height = 0;
+ cairo_matrix_transform_distance (&font_matrix, &advance_width, &advance_height);
+ }
+
+ if (utf8 != NULL && *utf8 != '\0' && cluster.num_bytes == 0)
+ cluster.num_bytes = g_utf8_next_char (utf8) - utf8;
+
+ if (cluster_pos == 1) {
+ utf8 += cluster.num_bytes;
+ g_array_append_val (cluster_array, cluster);
+ cluster.num_bytes = 0;
+ cluster.num_glyphs = 1;
+ } else {
+ cluster_pos--;
+ }
+
+ x += advance_width;
+ y += advance_height;
+ have_index = FALSE;
+ have_advance_width = FALSE;
+ h_offset = 0;
+ v_offset = 0;
+ g_array_append_val (glyph_array, glyph);
+
+ if (eof && (utf8 == NULL || *utf8 == '\0'))
+ return TRUE;
+ }
break;
case GI_TOKEN_INVALID:
g_set_error (error,
@@ -2205,7 +2312,82 @@ glyphs_indices_parse (const gchar *data,
}
glyphs_indices_iter_next (&token);
- } while (token.type != GI_TOKEN_EOF);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gxps_glyphs_to_cairo_glyphs (GXPSGlyphs *gxps_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const gchar *utf8,
+ cairo_glyph_t **glyphs,
+ int *num_glyphs,
+ cairo_text_cluster_t **clusters,
+ int *num_clusters,
+ GError **error)
+{
+ GArray *glyph_array = g_array_new (FALSE, FALSE, sizeof (cairo_glyph_t));
+ GArray *cluster_array = g_array_new (FALSE, FALSE, sizeof (cairo_text_cluster_t));
+ gboolean success;
+
+ if (!gxps_glyphs->indices) {
+ cairo_glyph_t glyph;
+ cairo_text_cluster_t cluster;
+ double x = gxps_glyphs->origin_x;
+ double y = gxps_glyphs->origin_y;
+
+ if (utf8 == NULL || *utf8 == '\0') {
+ g_set_error (error,
+ GXPS_PAGE_ERROR,
+ GXPS_PAGE_ERROR_RENDER,
+ "Error parsing glyphs: Both UnicodeString and Indices are empty");
+ return FALSE;
+ }
+
+ cluster.num_glyphs = 1;
+
+ do {
+ cairo_text_extents_t extents;
+
+ glyph.index = glyphs_lookup_index (scaled_font, utf8);
+ glyph.x = x;
+ glyph.y = y;
+ cluster.num_bytes = g_utf8_next_char (utf8) - utf8;
+
+ cairo_scaled_font_glyph_extents (scaled_font, &glyph, 1, &extents);
+ x += extents.x_advance;
+
+ g_array_append_val (glyph_array, glyph);
+ g_array_append_val (cluster_array, cluster);
+
+ utf8 += cluster.num_bytes;
+ } while (utf8 != NULL && *utf8 != '\0');
+ } else {
+ success = glyphs_indices_parse (gxps_glyphs->indices,
+ scaled_font,
+ gxps_glyphs->origin_x,
+ gxps_glyphs->origin_y,
+ utf8,
+ glyph_array,
+ cluster_array,
+ error);
+ if (!success) {
+ *num_glyphs = 0;
+ *glyphs = NULL;
+ *num_clusters = 0;
+ *clusters = NULL;
+ g_array_free (glyph_array, TRUE);
+ g_array_free (cluster_array, TRUE);
+
+ return FALSE;
+ }
+ }
+
+ *num_glyphs = glyph_array->len;
+ *glyphs = (cairo_glyph_t *)g_array_free (glyph_array, FALSE);
+ *num_clusters = cluster_array->len;
+ *clusters = (cairo_text_cluster_t *)g_array_free (cluster_array, FALSE);
return TRUE;
}
@@ -2553,31 +2735,20 @@ render_end_element (GMarkupParseContext *context,
LOG (g_print ("restore\n"));
cairo_restore (ctx->cr);
} else if (strcmp (element_name, "Glyphs") == 0) {
- GXPSGlyphs *glyphs;
- gint utf8_len;
- gdouble *advance_widths;
- cairo_text_cluster_t *cluster_list = NULL;
- gint num_clusters;
- cairo_text_cluster_flags_t cluster_flags;
- cairo_glyph_t *glyph_list = NULL;
- gint num_glyphs;
- cairo_matrix_t ctm, font_matrix;
- cairo_font_face_t *font_face;
- cairo_font_options_t *font_options;
- cairo_scaled_font_t *scaled_font;
- gint i;
- gdouble acc;
+ GXPSGlyphs *glyphs;
+ gchar *utf8;
+ cairo_text_cluster_t *cluster_list = NULL;
+ gint num_clusters;
+ cairo_glyph_t *glyph_list = NULL;
+ gint num_glyphs;
+ cairo_matrix_t ctm, font_matrix;
+ cairo_font_face_t *font_face;
+ cairo_font_options_t *font_options;
+ cairo_scaled_font_t *scaled_font;
+ gboolean success;
glyphs = g_markup_parse_context_pop (context);
- if (!glyphs->text) {
- gxps_glyphs_free (glyphs);
- LOG (g_print ("restore\n"));
- cairo_restore (ctx->cr);
-
- return;
- }
-
font_face = gxps_fonts_get_font (ctx->page->priv->zip, glyphs->font_uri, error);
if (!font_face) {
gxps_glyphs_free (glyphs);
@@ -2586,7 +2757,6 @@ render_end_element (GMarkupParseContext *context,
cairo_restore (ctx->cr);
return;
}
- cairo_set_font_face (ctx->cr, font_face);
if (glyphs->clip_data) {
if (!path_data_parse (glyphs->clip_data, ctx->cr, error)) {
@@ -2599,10 +2769,6 @@ render_end_element (GMarkupParseContext *context,
cairo_clip (ctx->cr);
}
- utf8_len = g_utf8_strlen (glyphs->text, -1);
-
- advance_widths = g_new0 (gdouble, utf8_len);
-
font_options = cairo_font_options_create ();
cairo_get_font_options (ctx->cr, font_options);
@@ -2617,66 +2783,41 @@ render_end_element (GMarkupParseContext *context,
cairo_font_options_destroy (font_options);
- cairo_scaled_font_text_to_glyphs (scaled_font,
- glyphs->origin_x,
- glyphs->origin_y,
- glyphs->text,
- strlen (glyphs->text),
- &glyph_list, &num_glyphs,
- &cluster_list, &num_clusters,
- &cluster_flags);
-
- if (glyphs->indices) {
- gboolean success;
-
- success = glyphs_indices_parse (glyphs->indices,
- glyphs->em_size,
- advance_widths,
- glyph_list,
- num_glyphs,
- cluster_list,
- num_clusters,
- error);
- if (!success) {
- g_free (advance_widths);
- gxps_glyphs_free (glyphs);
- LOG (g_print ("restore\n"));
- cairo_restore (ctx->cr);
- return;
- }
- }
-
- acc = glyph_list[0].x;
- for (i = 0; i < num_glyphs; i++) {
- glyph_list[i].x = acc;
- if (advance_widths[i] > 0) {
- acc = glyph_list[i].x + advance_widths[i];
- } else {
- cairo_text_extents_t extents;
-
- cairo_scaled_font_glyph_extents (scaled_font, &glyph_list[i], 1, &extents);
- acc = glyph_list[i].x + extents.x_advance;
- }
+ /* UnicodeString may begin with escape sequence "{}" */
+ utf8 = glyphs->text;
+ if (utf8 && g_str_has_prefix (utf8, "{}"))
+ utf8 += 2;
+
+ success = gxps_glyphs_to_cairo_glyphs (glyphs,
+ scaled_font,
+ utf8,
+ &glyph_list, &num_glyphs,
+ &cluster_list, &num_clusters,
+ error);
+ if (!success) {
+ gxps_glyphs_free (glyphs);
+ cairo_scaled_font_destroy (scaled_font);
+ LOG (g_print ("restore\n"));
+ cairo_restore (ctx->cr);
+ return;
}
- g_free (advance_widths);
-
if (glyphs->fill_pattern)
cairo_set_source (ctx->cr, glyphs->fill_pattern);
LOG (g_print ("show_text (%s)\n", glyphs->text));
- cairo_set_font_size (ctx->cr, glyphs->em_size);
+ cairo_set_scaled_font (ctx->cr, scaled_font);
cairo_show_text_glyphs (ctx->cr,
- glyphs->text, -1,
+ utf8, -1,
glyph_list, num_glyphs,
cluster_list, num_clusters,
- cluster_flags);
-
- cairo_glyph_free (glyph_list);
- cairo_text_cluster_free (cluster_list);
+ 0);
+ g_free (glyph_list);
+ g_free (cluster_list);
gxps_glyphs_free (glyphs);
+ cairo_scaled_font_destroy (scaled_font);
LOG (g_print ("restore\n"));
cairo_restore (ctx->cr);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]