summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTatsuhiko Miyabe <tatsuhiko@miya.be>2012-05-23 23:03:38 +0200
committerEmmanuel Pacaud <emmanuel@gnome.org>2012-06-15 14:19:29 +0200
commitd1d4791029d28a8451cd5ef9991d3357b7c74f56 (patch)
treede0ac425b980cb92b79e7d9885c983b25be656c8
parent35e4ffd91cc73fb17c47a12010f515941d6f4d3d (diff)
svg: show_text_glyphs supportsvg-text
-rw-r--r--src/cairo-svg-surface-private.h1
-rw-r--r--src/cairo-svg-surface.c587
-rw-r--r--src/cairo-unicode.c5
-rw-r--r--src/cairoint.h3
4 files changed, 549 insertions, 47 deletions
diff --git a/src/cairo-svg-surface-private.h b/src/cairo-svg-surface-private.h
index ddbf464b..904e485c 100644
--- a/src/cairo-svg-surface-private.h
+++ b/src/cairo-svg-surface-private.h
@@ -47,6 +47,7 @@
#include "cairo-surface-clipper-private.h"
typedef struct cairo_svg_document cairo_svg_document_t;
+typedef struct cairo_svg_font_output cairo_svg_font_output_t;
typedef struct cairo_svg_surface {
cairo_surface_t base;
diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c
index f32d5224..c593c41a 100644
--- a/src/cairo-svg-surface.c
+++ b/src/cairo-svg-surface.c
@@ -126,6 +126,14 @@ struct cairo_svg_page {
cairo_output_stream_t *xml_node;
};
+typedef struct _cairo_svg_font_style {
+ cairo_hash_entry_t base;
+ cairo_operator_t op;
+ cairo_pattern_t* pattern;
+ cairo_scaled_font_t *scaled_font;
+ int id;
+} cairo_svg_font_style;
+
struct cairo_svg_document {
cairo_output_stream_t *output_stream;
unsigned long refcount;
@@ -136,7 +144,6 @@ struct cairo_svg_document {
double height;
cairo_output_stream_t *xml_node_defs;
- cairo_output_stream_t *xml_node_glyphs;
unsigned int linear_pattern_id;
unsigned int radial_pattern_id;
@@ -149,7 +156,22 @@ struct cairo_svg_document {
cairo_svg_version_t svg_version;
- cairo_scaled_font_subsets_t *font_subsets;
+ cairo_scaled_font_subsets_t *font_text_subsets;
+ cairo_scaled_font_subsets_t *font_glyphs_subsets;
+
+ unsigned int style_id;
+ cairo_output_stream_t *css;
+ cairo_hash_table_t *font_to_id;
+
+ cairo_output_stream_t *text_x, *text_y, *text;
+ cairo_svg_font_style prev_style;
+ char prev_char;
+ cairo_bool_t preserve_space;
+};
+
+struct cairo_svg_font_output {
+ cairo_output_stream_t *xml_node_glyphs;
+ int prev_font_id;
};
static cairo_status_t
@@ -185,6 +207,9 @@ _cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream,
static const cairo_surface_backend_t cairo_svg_surface_backend;
static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend;
+static void
+_cairo_svg_end_text (cairo_svg_surface_t *surface);
+
/**
* cairo_svg_surface_create_for_stream:
* @write_func: a #cairo_write_func_t to accept the output data, may be %NULL
@@ -430,6 +455,8 @@ _cairo_svg_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper
cairo_svg_document_t *document = surface->document;
unsigned int i;
+ _cairo_svg_end_text(surface);
+
if (path == NULL) {
for (i = 0; i < surface->clip_level; i++)
_cairo_output_stream_printf (surface->xml_node, "</g>\n");
@@ -581,6 +608,8 @@ _cairo_svg_surface_store_page (cairo_svg_surface_t *surface)
cairo_int_status_t status;
unsigned int i;
+ _cairo_svg_end_text(surface);
+
stream = _cairo_memory_stream_create ();
if (_cairo_output_stream_get_status (stream)) {
status = _cairo_output_stream_destroy (stream);
@@ -627,6 +656,8 @@ _cairo_svg_surface_show_page (void *abstract_surface)
{
cairo_svg_surface_t *surface = abstract_surface;
+ _cairo_svg_end_text(surface);
+
if (unlikely (_cairo_svg_surface_store_page (surface) == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
@@ -751,7 +782,7 @@ _cairo_svg_surface_emit_path (cairo_output_stream_t *output,
}
static cairo_int_status_t
-_cairo_svg_document_emit_outline_glyph_data (cairo_svg_document_t *document,
+_cairo_svg_document_emit_outline_glyph_data (cairo_svg_font_output_t *fontout,
cairo_scaled_font_t *scaled_font,
unsigned long glyph_index)
{
@@ -766,20 +797,20 @@ _cairo_svg_document_emit_outline_glyph_data (cairo_svg_document_t *document,
if (unlikely (status))
return status;
- _cairo_output_stream_printf (document->xml_node_glyphs,
+ _cairo_output_stream_printf (fontout->xml_node_glyphs,
"<path style=\"stroke:none;\" ");
- _cairo_svg_surface_emit_path (document->xml_node_glyphs,
+ _cairo_svg_surface_emit_path (fontout->xml_node_glyphs,
scaled_glyph->path, NULL);
- _cairo_output_stream_printf (document->xml_node_glyphs,
+ _cairo_output_stream_printf (fontout->xml_node_glyphs,
"/>\n");
return status;
}
static cairo_int_status_t
-_cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t *document,
+_cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_font_output_t *fontout,
cairo_scaled_font_t *scaled_font,
unsigned long glyph_index)
{
@@ -804,24 +835,24 @@ _cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t *document,
if (unlikely (status))
return status;
- _cairo_output_stream_printf (document->xml_node_glyphs, "<g");
- _cairo_svg_surface_emit_transform (document->xml_node_glyphs, " transform",
+ _cairo_output_stream_printf (fontout->xml_node_glyphs, "<g");
+ _cairo_svg_surface_emit_transform (fontout->xml_node_glyphs, " transform",
&image->base.device_transform_inverse, NULL);
- _cairo_output_stream_printf (document->xml_node_glyphs, ">/n");
+ _cairo_output_stream_printf (fontout->xml_node_glyphs, ">/n");
for (y = 0, row = image->data, rows = image->height; rows; row += image->stride, rows--, y++) {
for (x = 0, byte = row, cols = (image->width + 7) / 8; cols; byte++, cols--) {
uint8_t output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte);
for (bit = 7; bit >= 0 && x < image->width; bit--, x++) {
if (output_byte & (1 << bit)) {
- _cairo_output_stream_printf (document->xml_node_glyphs,
+ _cairo_output_stream_printf (fontout->xml_node_glyphs,
"<rect x=\"%d\" y=\"%d\" width=\"1\" height=\"1\"/>\n",
x, y);
}
}
}
}
- _cairo_output_stream_printf (document->xml_node_glyphs, "</g>\n");
+ _cairo_output_stream_printf (fontout->xml_node_glyphs, "</g>\n");
cairo_surface_destroy (&image->base);
@@ -829,7 +860,7 @@ _cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t *document,
}
static cairo_int_status_t
-_cairo_svg_document_emit_glyph (cairo_svg_document_t *document,
+_cairo_svg_document_emit_glyph (cairo_svg_font_output_t *fontout,
cairo_scaled_font_t *scaled_font,
unsigned long scaled_font_glyph_index,
unsigned int font_id,
@@ -837,22 +868,22 @@ _cairo_svg_document_emit_glyph (cairo_svg_document_t *document,
{
cairo_int_status_t status;
- _cairo_output_stream_printf (document->xml_node_glyphs,
+ _cairo_output_stream_printf (fontout->xml_node_glyphs,
"<symbol overflow=\"visible\" id=\"glyph%d-%d\">\n",
font_id,
subset_glyph_index);
- status = _cairo_svg_document_emit_outline_glyph_data (document,
+ status = _cairo_svg_document_emit_outline_glyph_data (fontout,
scaled_font,
scaled_font_glyph_index);
if (status == CAIRO_INT_STATUS_UNSUPPORTED)
- status = _cairo_svg_document_emit_bitmap_glyph_data (document,
+ status = _cairo_svg_document_emit_bitmap_glyph_data (fontout,
scaled_font,
scaled_font_glyph_index);
if (unlikely (status))
return status;
- _cairo_output_stream_printf (document->xml_node_glyphs, "</symbol>\n");
+ _cairo_output_stream_printf (fontout->xml_node_glyphs, "</symbol>\n");
return CAIRO_INT_STATUS_SUCCESS;
}
@@ -879,24 +910,183 @@ _cairo_svg_document_emit_font_subset (cairo_scaled_font_subset_t *font_subset,
return status;
}
+
+static cairo_int_status_t
+_cairo_svg_document_emit_outline_text_glyph_data (cairo_svg_font_output_t *fontout,
+ cairo_scaled_font_t *scaled_font,
+ unsigned long glyph_index,
+ const char* utf8)
+{
+ cairo_scaled_glyph_t *scaled_glyph;
+ cairo_matrix_t matrix;
+ cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+ long c;
+ int len, i;
+ double font_size;
+ cairo_bool_t missing = FALSE;
+
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ glyph_index,
+ CAIRO_SCALED_GLYPH_INFO_METRICS|
+ CAIRO_SCALED_GLYPH_INFO_PATH,
+ &scaled_glyph);
+ if (unlikely (status))
+ return status;
+
+ font_size = scaled_font->font_matrix.xx;
+ if (font_size == 0) {
+ font_size = -scaled_font->font_matrix.xy;
+ }
+
+ if (utf8 == NULL || ((unsigned char)utf8[0] == (unsigned char)0xEF && (unsigned char)utf8[1] == (unsigned char)0xBF && (unsigned char)utf8[2] == (unsigned char)0xBF)) {
+ if (scaled_glyph->x_advance) {
+ _cairo_output_stream_printf (fontout->xml_node_glyphs,
+ "<missing-glyph horiz-adv-x=\"%d\"><path ",
+ (int)(scaled_glyph->x_advance * 2048 / font_size));
+ }
+ else {
+ _cairo_output_stream_printf (fontout->xml_node_glyphs,
+ "<missing-glyph><path ");
+ }
+ missing = TRUE;
+ }
+ else {
+ _cairo_output_stream_printf (fontout->xml_node_glyphs,
+ "<glyph unicode=\"");
+ len = strlen(utf8);
+ for (i = 0; i < len; i++) {
+ if (utf8[i] == '<') {
+ _cairo_output_stream_printf (fontout->xml_node_glyphs, "&lt;");
+ continue;
+ }
+ if (utf8[i] == '>') {
+ _cairo_output_stream_printf (fontout->xml_node_glyphs, "&gt;");
+ continue;
+ }
+ if (utf8[i] == '&') {
+ _cairo_output_stream_printf (fontout->xml_node_glyphs, "&amp;");
+ continue;
+ }
+ if (utf8[i] == '\"') {
+ _cairo_output_stream_printf (fontout->xml_node_glyphs, "&quot;");
+ continue;
+ }
+ _cairo_output_stream_printf (fontout->xml_node_glyphs, "%c", utf8[i]);
+ }
+ if (scaled_glyph->x_advance) {
+ _cairo_output_stream_printf (fontout->xml_node_glyphs,
+ "\" horiz-adv-x=\"%d\" ", (int)(scaled_glyph->x_advance * 2048 / font_size));
+ }
+ else {
+ _cairo_output_stream_printf (fontout->xml_node_glyphs,
+ "\" ");
+ }
+ }
+ cairo_matrix_init(&matrix, 2048 / font_size, 0, 0, -2048 / font_size, 0, 0);
+
+ _cairo_svg_surface_emit_path (fontout->xml_node_glyphs,
+ scaled_glyph->path, &matrix);
+
+ if (missing) {
+ _cairo_output_stream_printf (fontout->xml_node_glyphs, "/></missing-glyph>\n");
+ }
+ else {
+ _cairo_output_stream_printf (fontout->xml_node_glyphs, "/>\n");
+ }
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_svg_surface_emit_font_face (cairo_output_stream_t *output,
+ const cairo_scaled_font_subset_t *subset)
+{
+ _cairo_output_stream_printf (output,
+ " font-family=\"font%d\"", subset->font_id);
+ _cairo_output_stream_printf (output,
+ " units-per-em=\"2048\"");
+
+ _cairo_output_stream_printf (output,
+ " ascent=\"2048\"");
+ _cairo_output_stream_printf (output,
+ " descent=\"0\"");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_svg_document_emit_font_subset_text (cairo_scaled_font_subset_t *font_subset,
+ void *closure)
+{
+ cairo_svg_font_output_t *fontout = closure;
+ unsigned int i;
+ double font_size;
+ cairo_status_t status = CAIRO_INT_STATUS_SUCCESS;
+
+ _cairo_scaled_font_freeze_cache (font_subset->scaled_font);
+
+ font_size = font_subset->scaled_font->font_matrix.xx;
+ if (font_size == 0) {
+ font_size = -font_subset->scaled_font->font_matrix.xy;
+ }
+
+ if (fontout->prev_font_id != font_subset->font_id) {
+ if (fontout->prev_font_id != -1) {
+ _cairo_output_stream_printf (fontout->xml_node_glyphs, "</font>\n");
+ }
+ fontout->prev_font_id = font_subset->font_id;
+ _cairo_output_stream_printf (fontout->xml_node_glyphs,
+ "<font id=\"font-%d\" horiz-adv-x=\"2048\"",
+ font_subset->font_id);
+ _cairo_output_stream_printf (fontout->xml_node_glyphs,
+ ">\n");
+ _cairo_output_stream_printf (fontout->xml_node_glyphs,
+ "<font-face");
+ _cairo_svg_surface_emit_font_face(fontout->xml_node_glyphs, font_subset);
+ _cairo_output_stream_printf (fontout->xml_node_glyphs,
+ "/>\n");
+ }
+
+ for (i = 0; i < font_subset->num_glyphs; i++) {
+ status = _cairo_svg_document_emit_outline_text_glyph_data (fontout,
+ font_subset->scaled_font,
+ font_subset->glyphs[i],
+ font_subset->utf8[i]);
+ if (unlikely (status))
+ break;
+ }
+
+ _cairo_scaled_font_thaw_cache (font_subset->scaled_font);
+
+ return status;
+}
+
static cairo_status_t
-_cairo_svg_document_emit_font_subsets (cairo_svg_document_t *document)
+_cairo_svg_document_emit_font_subsets (cairo_svg_font_output_t *fontout,
+ cairo_scaled_font_subsets_t* font_text_subsets,
+ cairo_scaled_font_subsets_t* font_glyphs_subsets)
{
cairo_status_t status;
- status = _cairo_scaled_font_subsets_foreach_scaled (document->font_subsets,
+ fontout->prev_font_id = -1;
+ status = _cairo_scaled_font_subsets_foreach_unscaled (font_text_subsets,
+ _cairo_svg_document_emit_font_subset_text,
+ fontout);
+ if (fontout->prev_font_id != -1) {
+ _cairo_output_stream_printf (fontout->xml_node_glyphs, "</font>\n");
+ }
+
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_scaled_font_subsets_foreach_scaled (font_glyphs_subsets,
_cairo_svg_document_emit_font_subset,
- document);
+ fontout);
if (unlikely (status))
- goto FAIL;
+ return status;
- status = _cairo_scaled_font_subsets_foreach_user (document->font_subsets,
+ status = _cairo_scaled_font_subsets_foreach_user (font_glyphs_subsets,
_cairo_svg_document_emit_font_subset,
- document);
-
- FAIL:
- _cairo_scaled_font_subsets_destroy (document->font_subsets);
- document->font_subsets = NULL;
+ fontout);
return status;
}
@@ -1217,7 +1407,8 @@ _cairo_svg_surface_emit_operator (cairo_output_stream_t *output,
{
if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 &&
op != CAIRO_OPERATOR_OVER) {
- _cairo_output_stream_printf (output, " comp-op=\"%s\"", _cairo_svg_surface_operators[op]);
+ _cairo_output_stream_printf (output, " opacity=\".7\"");
+ // miyabe FIXME_cairo_output_stream_printf (output, " comp-op=\"%s\"", _cairo_svg_surface_operators[op]);
if (!_cairo_operator_bounded_by_source (op))
_cairo_output_stream_printf (output, " clip-to-self=\"true\"");
}
@@ -1501,6 +1692,8 @@ _cairo_svg_surface_emit_composite_recording_pattern (cairo_output_stream_t *outp
cairo_matrix_t p2u;
cairo_status_t status;
+ _cairo_svg_end_text(surface);
+
p2u = pattern->base.matrix;
status = cairo_matrix_invert (&p2u);
/* cairo_pattern_set_matrix ensures the matrix is invertible */
@@ -2165,6 +2358,8 @@ _cairo_svg_surface_fill_stroke (void *abstract_surface,
cairo_svg_surface_t *surface = abstract_surface;
cairo_status_t status;
+ _cairo_svg_end_text(surface);
+
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
return status;
@@ -2206,6 +2401,8 @@ _cairo_svg_surface_fill (void *abstract_surface,
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
return _cairo_svg_surface_analyze_operation (surface, op, source);
+ _cairo_svg_end_text(surface);
+
assert (_cairo_svg_surface_operation_supported (surface, op, source));
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
@@ -2294,6 +2491,8 @@ _cairo_svg_surface_paint (void *abstract_surface,
cairo_status_t status;
cairo_svg_surface_t *surface = abstract_surface;
+ _cairo_svg_end_text(surface);
+
/* Emulation of clear and source operators, when no clipping region
* is defined. We just delete existing content of surface root node,
* and exit early if operator is clear.
@@ -2387,6 +2586,8 @@ _cairo_svg_surface_mask (void *abstract_surface,
assert (_cairo_svg_surface_operation_supported (surface, op, source));
assert (_cairo_svg_surface_operation_supported (surface, CAIRO_OPERATOR_OVER, mask));
+ _cairo_svg_end_text(surface);
+
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
return status;
@@ -2461,6 +2662,7 @@ _cairo_svg_surface_stroke (void *abstract_dst,
assert (_cairo_svg_surface_operation_supported (surface, op, source));
+ _cairo_svg_end_text(surface);
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
return status;
@@ -2505,6 +2707,8 @@ _cairo_svg_surface_show_glyphs (void *abstract_surface,
if (num_glyphs <= 0)
return CAIRO_STATUS_SUCCESS;
+ _cairo_svg_end_text(surface);
+
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
return status;
@@ -2526,7 +2730,7 @@ _cairo_svg_surface_show_glyphs (void *abstract_surface,
_cairo_output_stream_printf (surface->xml_node, "\">\n");
for (i = 0; i < num_glyphs; i++) {
- status = _cairo_scaled_font_subsets_map_glyph (document->font_subsets,
+ status = _cairo_scaled_font_subsets_map_glyph (document->font_glyphs_subsets,
scaled_font, glyphs[i].index,
NULL, 0,
&subset_glyph);
@@ -2594,6 +2798,242 @@ _cairo_svg_surface_get_supported_mime_types (void *abstract_surface)
return _cairo_svg_supported_mime_types;
}
+static cairo_bool_t
+_cairo_svg_surface_has_show_text_glyphs (void *abstract_surface)
+{
+ return TRUE;
+}
+
+static void
+_cairo_svg_end_text (cairo_svg_surface_t *surface)
+{
+ cairo_output_stream_t *output = surface->xml_node;
+ if (surface->document->prev_style.scaled_font != NULL) {
+ if (surface->document->preserve_space) {
+ _cairo_output_stream_printf (output,
+ " xml:space=\"preserve\"");
+ }
+ surface->document->preserve_space = FALSE;
+ surface->document->prev_char = 0x20;
+ _cairo_output_stream_printf (output,
+ " x=\"");
+ _cairo_memory_stream_copy (surface->document->text_x, output);
+ _cairo_output_stream_printf (output,
+ "\" y=\"");
+ _cairo_memory_stream_copy (surface->document->text_y, output);
+ _cairo_output_stream_printf (output,
+ "\">");
+ _cairo_memory_stream_copy (surface->document->text, output);
+ _cairo_output_stream_printf (output,
+ "</text>\n");
+
+ surface->document->prev_style.scaled_font = (cairo_scaled_font_t*)NULL;
+ _cairo_output_stream_destroy(surface->document->text_x);
+ _cairo_output_stream_destroy(surface->document->text_y);
+ _cairo_output_stream_destroy(surface->document->text);
+ }
+}
+
+static cairo_int_status_t
+_cairo_svg_surface_emit_font_style (cairo_output_stream_t *output,
+ cairo_svg_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ const cairo_scaled_font_t *scaled_font,
+ const cairo_scaled_font_subsets_glyph_t *subset_glyph)
+{
+ double font_size;
+ cairo_status_t status;
+
+ font_size = scaled_font->font_matrix.xx;
+ if (font_size == 0) {
+ font_size = -scaled_font->font_matrix.xy;
+ }
+ _cairo_output_stream_printf (output,
+ "font:%fpx font%d;", font_size, subset_glyph->font_id);
+
+ status = _cairo_svg_surface_emit_pattern (surface, pattern,
+ output, FALSE, NULL);
+ if (unlikely (status))
+ return status;
+ _cairo_svg_surface_emit_operator_for_style (output, surface, op);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_svg_surface_equal_font(const void *key_a, const void *key_b) {
+ cairo_svg_font_style *style_a = (cairo_svg_font_style*)key_a;
+ cairo_svg_font_style *style_b = (cairo_svg_font_style*)key_b;
+ return style_a->op == style_b->op &&
+ style_a->scaled_font == style_b->scaled_font &&
+ _cairo_pattern_equal(style_a->pattern, style_b->pattern);
+}
+
+static cairo_int_status_t
+_cairo_svg_surface_show_text_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ const char *utf8,
+ int utf8_len,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_text_cluster_t *clusters,
+ int num_clusters,
+ cairo_text_cluster_flags_t cluster_flags,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip)
+{
+ cairo_svg_surface_t *surface = abstract_surface;
+ cairo_output_stream_t *output = surface->xml_node;
+ cairo_scaled_font_subsets_t *subsets = surface->document->font_text_subsets;
+ cairo_status_t status;
+ int i, j, k, l;
+ double font_size, x, y;
+ cairo_matrix_t matrix;
+ cairo_svg_font_style style;
+ cairo_svg_font_style *entry;
+ cairo_scaled_font_subsets_glyph_t subset_glyph;
+
+ if (utf8_len == 0) {
+ return _cairo_svg_surface_show_glyphs(abstract_surface, op, pattern, glyphs, num_glyphs, scaled_font, clip);
+ }
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+ return _cairo_svg_surface_analyze_operation (surface, op, pattern);
+
+ assert (_cairo_svg_surface_operation_supported (surface, op, pattern));
+
+ if (num_glyphs <= 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ return status;
+
+ j = k = 0;
+ for (i = 0; i < num_clusters; ++i) {
+ if (clusters[i].num_glyphs > 0)
+ status = _cairo_scaled_font_subsets_map_glyph (subsets,
+ scaled_font, glyphs[k].index,
+ &utf8[j], clusters[i].num_bytes, &subset_glyph);
+ if (unlikely (status))
+ return status;
+ j += clusters[i].num_bytes;
+ k += clusters[i].num_glyphs;
+ }
+
+ font_size = scaled_font->font_matrix.xx;
+ if (font_size == 0) {
+ font_size = -scaled_font->font_matrix.xy;
+ }
+
+ i = l = 0;
+ style.id = 0;
+ style.op = op;
+ style.scaled_font = scaled_font;
+ style.pattern = pattern;
+ style.base.hash = _cairo_pattern_hash(style.pattern);
+ style.base.hash = _cairo_hash_bytes(style.base.hash, &style.op, sizeof(style.op));
+ style.base.hash = _cairo_hash_bytes(style.base.hash, &style.scaled_font, sizeof(style.scaled_font));
+ cairo_bool_t newtext = surface->document->prev_style.scaled_font == NULL ||
+ !_cairo_svg_surface_equal_font(&surface->document->prev_style, &style);
+ if (newtext) {
+ _cairo_svg_end_text(surface);
+ entry = _cairo_hash_table_lookup(surface->document->font_to_id, &style.base);
+ if (!entry) {
+ style.id = ++surface->document->style_id;
+ _cairo_output_stream_printf (surface->document->css, ".t%d {", surface->document->style_id);
+ _cairo_svg_surface_emit_font_style(surface->document->css, surface, op, pattern, scaled_font, &subset_glyph);
+ _cairo_output_stream_printf (surface->document->css, "}\n");
+
+ entry = malloc(sizeof(style));
+ *entry = style;
+ _cairo_pattern_create_copy(&entry->pattern, entry->pattern);
+ _cairo_hash_table_insert(surface->document->font_to_id, entry);
+ }
+ else {
+ style.id = entry->id;
+ }
+
+ _cairo_output_stream_printf (output,
+ "<text class=\"t%d\"", style.id);
+ surface->document->text_x = _cairo_memory_stream_create ();
+ surface->document->text_y = _cairo_memory_stream_create ();
+ surface->document->text = _cairo_memory_stream_create ();
+ surface->document->prev_style = style;
+
+ matrix = scaled_font->scale;
+ cairo_matrix_scale(&matrix, 1 / font_size, 1 / font_size);
+ _cairo_svg_surface_emit_transform(output, " transform", &matrix, NULL);
+ cairo_matrix_invert(&matrix);
+ }
+ else {
+ matrix = scaled_font->scale;
+ cairo_matrix_scale(&matrix, 1 / font_size, 1 / font_size);
+ cairo_matrix_invert(&matrix);
+ }
+ j = i;
+ for (i = l; i < utf8_len;) {
+ if (j < num_glyphs) {
+ x = glyphs[j].x;
+ y = glyphs[j].y;
+ cairo_matrix_transform_point(&matrix, &x, &y);
+ if (i > l || !newtext) {
+ _cairo_output_stream_printf (surface->document->text_x, " ");
+ _cairo_output_stream_printf (surface->document->text_y, " ");
+ }
+ _cairo_output_stream_printf (surface->document->text_x, "%f", x);
+ _cairo_output_stream_printf (surface->document->text_y, "%f", y);
+
+ }
+ ++j;
+ if (utf8[i] == 0x20 && surface->document->prev_char == 0x20) {
+ surface->document->preserve_space = TRUE;
+ }
+ surface->document->prev_char = utf8[i];
+ switch (utf8[i]) {
+ case '<':
+ _cairo_output_stream_printf (surface->document->text, "&lt;");
+ break;
+ case '>':
+ _cairo_output_stream_printf (surface->document->text, "&gt;");
+ break;
+ case '&':
+ _cairo_output_stream_printf (surface->document->text, "&amp;");
+ break;
+ case '"':
+ _cairo_output_stream_printf (surface->document->text, "&quot;");
+ break;
+ case '\'':
+ _cairo_output_stream_printf (surface->document->text, "&apos;");
+ break;
+ default: {
+ if ((unsigned char)utf8[i] < 32 && utf8[i] != 0x20 && utf8[i] != 0x0D && utf8[i] != 0x0A && utf8[i] != 0x09) {
+ _cairo_output_stream_printf (surface->document->text, "&#xA0;");
+ break;
+ }
+ int skip;
+ for (skip = _cairo_utf8_get_char_bytes(&utf8[i]); skip > 0; --skip) {
+ _cairo_output_stream_printf (surface->document->text, "%c", utf8[i]);
+ ++i;
+ }
+ continue;
+ }
+ }
+ ++i;
+ }
+ if (j > num_glyphs)
+ _cairo_svg_end_text(surface);
+
+ // for Webkit performance.
+ if (_cairo_memory_stream_length(surface->document->text) >= 300) {
+ _cairo_svg_end_text(surface);
+ }
+
+ return _cairo_output_stream_get_status (output);
+}
+
static const cairo_surface_backend_t cairo_svg_surface_backend = {
CAIRO_SURFACE_TYPE_SVG,
_cairo_svg_surface_finish,
@@ -2625,8 +3065,8 @@ static const cairo_surface_backend_t cairo_svg_surface_backend = {
_cairo_svg_surface_fill,
_cairo_svg_surface_fill_stroke,
_cairo_svg_surface_show_glyphs,
- NULL, /* has_show_text_glyphs */
- NULL, /* show_text_glyphs */
+ _cairo_svg_surface_has_show_text_glyphs,
+ _cairo_svg_surface_show_text_glyphs,
_cairo_svg_surface_get_supported_mime_types,
};
@@ -2647,9 +3087,16 @@ _cairo_svg_document_create (cairo_output_stream_t *output_stream,
if (unlikely (document == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
- /* The use of defs for font glyphs imposes no per-subset limit. */
- document->font_subsets = _cairo_scaled_font_subsets_create_scaled ();
- if (unlikely (document->font_subsets == NULL)) {
+ /* The use of defs for font glyphs imposes no per-subset limit. */
+ document->font_text_subsets = _cairo_scaled_font_subsets_create_simple ();
+ if (unlikely (document->font_text_subsets == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_DOCUMENT;
+ }
+
+ /* The use of defs for font glyphs imposes no per-subset limit. */
+ document->font_glyphs_subsets = _cairo_scaled_font_subsets_create_scaled ();
+ if (unlikely (document->font_glyphs_subsets == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP_DOCUMENT;
}
@@ -2661,6 +3108,18 @@ _cairo_svg_document_create (cairo_output_stream_t *output_stream,
document->width = width;
document->height = height;
+ document->style_id = 0;
+ document->css = _cairo_memory_stream_create ();
+ status = _cairo_output_stream_get_status (document->css);
+ if (unlikely (status))
+ goto CLEANUP_NODE_DEFS;
+
+ document->prev_style.op = (cairo_operator_t)NULL;
+ document->prev_style.scaled_font = (cairo_scaled_font_t*)NULL;
+ document->prev_char = 0x20;
+ document->preserve_space = FALSE;
+ document->font_to_id = _cairo_hash_table_create(_cairo_svg_surface_equal_font);
+
document->linear_pattern_id = 0;
document->radial_pattern_id = 0;
document->pattern_id = 0;
@@ -2673,11 +3132,6 @@ _cairo_svg_document_create (cairo_output_stream_t *output_stream,
if (unlikely (status))
goto CLEANUP_NODE_DEFS;
- document->xml_node_glyphs = _cairo_memory_stream_create ();
- status = _cairo_output_stream_get_status (document->xml_node_glyphs);
- if (unlikely (status))
- goto CLEANUP_NODE_GLYPHS;
-
document->alpha_filter = FALSE;
document->svg_version = version;
@@ -2685,11 +3139,10 @@ _cairo_svg_document_create (cairo_output_stream_t *output_stream,
*document_out = document;
return CAIRO_STATUS_SUCCESS;
- CLEANUP_NODE_GLYPHS:
- status_ignored = _cairo_output_stream_destroy (document->xml_node_glyphs);
CLEANUP_NODE_DEFS:
status_ignored = _cairo_output_stream_destroy (document->xml_node_defs);
- _cairo_scaled_font_subsets_destroy (document->font_subsets);
+ _cairo_scaled_font_subsets_destroy (document->font_text_subsets);
+ _cairo_scaled_font_subsets_destroy (document->font_glyphs_subsets);
CLEANUP_DOCUMENT:
free (document);
return status;
@@ -2725,17 +3178,32 @@ _cairo_svg_document_destroy (cairo_svg_document_t *document)
return status;
}
+static void
+_cairo_svg_surface_font_pluck (void *_entry, void *dict)
+{
+ cairo_svg_font_style *entry = (cairo_svg_font_style*)_entry;
+ _cairo_hash_table_remove (dict, &entry->base);
+ cairo_pattern_destroy(entry->pattern);
+ free(entry);
+}
+
static cairo_status_t
_cairo_svg_document_finish (cairo_svg_document_t *document)
{
cairo_status_t status, status2;
cairo_output_stream_t *output = document->output_stream;
cairo_svg_page_t *page;
+ cairo_svg_font_output_t fontout;
unsigned int i;
if (document->finished)
return CAIRO_STATUS_SUCCESS;
+ _cairo_hash_table_foreach (document->font_to_id,
+ _cairo_svg_surface_font_pluck,
+ document->font_to_id);
+ _cairo_hash_table_destroy(document->font_to_id);
+
/*
* Should we add DOCTYPE?
*
@@ -2768,14 +3236,34 @@ _cairo_svg_document_finish (cairo_svg_document_t *document)
document->width, document->height,
_cairo_svg_internal_version_strings [document->svg_version]);
- status = _cairo_svg_document_emit_font_subsets (document);
+ fontout.prev_font_id = -1;
+ fontout.xml_node_glyphs = _cairo_memory_stream_create ();
+ status = _cairo_output_stream_get_status (fontout.xml_node_glyphs);
+ if (unlikely (status))
+ goto CLEANUP_NODE_GLYPHS;
+
+ status = _cairo_svg_document_emit_font_subsets (&fontout,
+ document->font_text_subsets, document->font_glyphs_subsets);
+
+ if (unlikely (status)) {
+ _cairo_scaled_font_subsets_destroy (document->font_text_subsets);
+ document->font_text_subsets = NULL;
+ _cairo_scaled_font_subsets_destroy (document->font_glyphs_subsets);
+ document->font_glyphs_subsets = NULL;
+ }
- if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0 ||
+ if (_cairo_memory_stream_length (document->css) > 0) {
+ _cairo_output_stream_printf (output, "<style type=\"text/css\">\n");
+ _cairo_memory_stream_copy (document->css, output);
+ _cairo_output_stream_printf (output, "</style>\n");
+ }
+
+ if (_cairo_memory_stream_length (fontout.xml_node_glyphs) > 0 ||
_cairo_memory_stream_length (document->xml_node_defs) > 0) {
_cairo_output_stream_printf (output, "<defs>\n");
- if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0) {
+ if (_cairo_memory_stream_length (fontout.xml_node_glyphs) > 0) {
_cairo_output_stream_printf (output, "<g>\n");
- _cairo_memory_stream_copy (document->xml_node_glyphs, output);
+ _cairo_memory_stream_copy (fontout.xml_node_glyphs, output);
_cairo_output_stream_printf (output, "</g>\n");
}
_cairo_memory_stream_copy (document->xml_node_defs, output);
@@ -2819,7 +3307,7 @@ _cairo_svg_document_finish (cairo_svg_document_t *document)
_cairo_output_stream_printf (output, "</svg>\n");
- status2 = _cairo_output_stream_destroy (document->xml_node_glyphs);
+ status2 = _cairo_output_stream_destroy (document->css);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
@@ -2831,6 +3319,11 @@ _cairo_svg_document_finish (cairo_svg_document_t *document)
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
+ CLEANUP_NODE_GLYPHS:
+ status2 = _cairo_output_stream_destroy (fontout.xml_node_glyphs);
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = status2;
+
document->finished = TRUE;
return status;
diff --git a/src/cairo-unicode.c b/src/cairo-unicode.c
index 88de3951..2908b490 100644
--- a/src/cairo-unicode.c
+++ b/src/cairo-unicode.c
@@ -116,6 +116,11 @@ static const char utf8_skip_data[256] = {
#define UTF8_NEXT_CHAR(p) ((p) + utf8_skip_data[*(unsigned char *)(p)])
+int
+ _cairo_utf8_get_char_bytes (const char *p) {
+ return utf8_skip_data[*(unsigned char *)(p)];
+}
+
/* Converts a sequence of bytes encoded as UTF-8 to a Unicode character.
* If @p does not point to a valid UTF-8 encoded character, results are
* undefined.
diff --git a/src/cairoint.h b/src/cairoint.h
index ff101c15..15c73ef2 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1776,6 +1776,9 @@ _cairo_pattern_reset_static_data (void);
/* cairo-unicode.c */
cairo_private int
+_cairo_utf8_get_char_bytes (const char *p);
+
+cairo_private int
_cairo_utf8_get_char_validated (const char *p,
uint32_t *unicode);