diff --git src/cairo-surface.c src/cairo-surface.c old mode 100644 new mode 100755 diff --git src/cairo-svg-surface-private.h src/cairo-svg-surface-private.h index ddbf464..904e485 100644 --- src/cairo-svg-surface-private.h +++ 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 src/cairo-svg-surface.c src/cairo-svg-surface.c old mode 100644 new mode 100755 index 6f607d3..ffb3e2e --- src/cairo-svg-surface.c +++ src/cairo-svg-surface.c @@ -90,7 +90,8 @@ static const cairo_svg_version_t _cairo_svg_versions[] = static void _cairo_svg_surface_emit_path (cairo_output_stream_t *output, const cairo_path_fixed_t *path, - const cairo_matrix_t *ctm_inverse); + const cairo_matrix_t *ctm_inverse, + double precision); static cairo_bool_t _cairo_svg_version_has_page_set_support (cairo_svg_version_t version) @@ -110,12 +111,30 @@ static const char * _cairo_svg_internal_version_strings[CAIRO_SVG_VERSION_LAST] "1.2" }; +static double _round(double a, int precision) { + if (precision == 0) { + return a; + } + if (a < 0) { + return (double)(long)(a * precision - 0.5) / precision; + } + return (double)(long)(a * precision + 0.5) / precision; +} + struct cairo_svg_page { unsigned int surface_id; unsigned int clip_level; 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; @@ -126,7 +145,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; @@ -139,7 +157,20 @@ 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; +}; + +struct cairo_svg_font_output { + cairo_output_stream_t *xml_node_glyphs; + int prev_font_id; }; static cairo_status_t @@ -175,6 +206,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 @@ -420,6 +454,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, "\n"); @@ -436,7 +472,7 @@ _cairo_svg_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper "\n" " clip_id); - _cairo_svg_surface_emit_path (document->xml_node_defs, path, NULL); + _cairo_svg_surface_emit_path (document->xml_node_defs, path, NULL, 0); _cairo_output_stream_printf (document->xml_node_defs, "/>\n" @@ -571,6 +607,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); @@ -617,6 +655,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); @@ -646,6 +686,7 @@ _cairo_svg_surface_emit_transform (cairo_output_stream_t *output, typedef struct { cairo_output_stream_t *output; const cairo_matrix_t *ctm_inverse; + double precision; } svg_path_info_t; static cairo_status_t @@ -659,7 +700,8 @@ _cairo_svg_path_move_to (void *closure, if (info->ctm_inverse) cairo_matrix_transform_point (info->ctm_inverse, &x, &y); - _cairo_output_stream_printf (info->output, "M %f %f ", x, y); + _cairo_output_stream_printf (info->output, "M %f %f ", + _round(x, info->precision), _round(y, info->precision)); return CAIRO_STATUS_SUCCESS; } @@ -675,7 +717,8 @@ _cairo_svg_path_line_to (void *closure, if (info->ctm_inverse) cairo_matrix_transform_point (info->ctm_inverse, &x, &y); - _cairo_output_stream_printf (info->output, "L %f %f ", x, y); + _cairo_output_stream_printf (info->output, "L %f %f ", + _round(x, info->precision), _round(y, info->precision)); return CAIRO_STATUS_SUCCESS; } @@ -702,7 +745,7 @@ _cairo_svg_path_curve_to (void *closure, _cairo_output_stream_printf (info->output, "C %f %f %f %f %f %f ", - bx, by, cx, cy, dx, dy); + _round(bx, info->precision), _round(by, info->precision), _round(cx, info->precision), _round(cy, info->precision), _round(dx, info->precision), _round(dy, info->precision)); return CAIRO_STATUS_SUCCESS; } @@ -720,7 +763,8 @@ _cairo_svg_path_close_path (void *closure) static void _cairo_svg_surface_emit_path (cairo_output_stream_t *output, const cairo_path_fixed_t *path, - const cairo_matrix_t *ctm_inverse) + const cairo_matrix_t *ctm_inverse, + const double precision) { cairo_status_t status; svg_path_info_t info; @@ -729,6 +773,7 @@ _cairo_svg_surface_emit_path (cairo_output_stream_t *output, info.output = output; info.ctm_inverse = ctm_inverse; + info.precision = precision; status = _cairo_path_fixed_interpret (path, _cairo_svg_path_move_to, _cairo_svg_path_line_to, @@ -741,7 +786,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) { @@ -756,20 +801,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, "xml_node_glyphs, - scaled_glyph->path, NULL); + _cairo_svg_surface_emit_path (fontout->xml_node_glyphs, + scaled_glyph->path, NULL, 0); - _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) { @@ -794,24 +839,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, "xml_node_glyphs, " transform", + _cairo_output_stream_printf (fontout->xml_node_glyphs, "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, "\n", x, y); } } } } - _cairo_output_stream_printf (document->xml_node_glyphs, "\n"); + _cairo_output_stream_printf (fontout->xml_node_glyphs, "\n"); cairo_surface_destroy (&image->base); @@ -819,7 +864,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, @@ -827,22 +872,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, "\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, "\n"); + _cairo_output_stream_printf (fontout->xml_node_glyphs, "\n"); return CAIRO_INT_STATUS_SUCCESS; } @@ -869,24 +914,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, + "x_advance * 2048 / font_size)); + } + else { + _cairo_output_stream_printf (fontout->xml_node_glyphs, + "xml_node_glyphs, + "xml_node_glyphs, "<"); + continue; + } + if (utf8[i] == '>') { + _cairo_output_stream_printf (fontout->xml_node_glyphs, ">"); + continue; + } + if (utf8[i] == '&') { + _cairo_output_stream_printf (fontout->xml_node_glyphs, "&"); + continue; + } + if (utf8[i] == '\"') { + _cairo_output_stream_printf (fontout->xml_node_glyphs, """); + 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, 1); + + if (missing) { + _cairo_output_stream_printf (fontout->xml_node_glyphs, "/>\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, "\n"); + } + fontout->prev_font_id = font_subset->font_id; + _cairo_output_stream_printf (fontout->xml_node_glyphs, + "font_id); + _cairo_output_stream_printf (fontout->xml_node_glyphs, + ">\n"); + _cairo_output_stream_printf (fontout->xml_node_glyphs, + "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, "\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; } @@ -1207,7 +1411,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\""); } @@ -1491,6 +1696,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 */ @@ -2154,6 +2361,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; @@ -2171,7 +2380,7 @@ _cairo_svg_surface_fill_stroke (void *abstract_surface, _cairo_output_stream_printf (surface->xml_node, "\" "); - _cairo_svg_surface_emit_path (surface->xml_node, path, stroke_ctm_inverse); + _cairo_svg_surface_emit_path (surface->xml_node, path, stroke_ctm_inverse, 0); _cairo_svg_surface_emit_transform (surface->xml_node, " transform", stroke_ctm, NULL); _cairo_output_stream_printf (surface->xml_node, "/>\n"); @@ -2195,6 +2404,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); @@ -2208,7 +2419,7 @@ _cairo_svg_surface_fill (void *abstract_surface, _cairo_output_stream_printf (surface->xml_node, "\" "); - _cairo_svg_surface_emit_path (surface->xml_node, path, NULL); + _cairo_svg_surface_emit_path (surface->xml_node, path, NULL, 0); _cairo_output_stream_printf (surface->xml_node, "/>\n"); @@ -2283,6 +2494,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. @@ -2376,6 +2589,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; @@ -2450,6 +2665,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; @@ -2462,7 +2678,7 @@ _cairo_svg_surface_stroke (void *abstract_dst, _cairo_output_stream_printf (surface->xml_node, "\" "); - _cairo_svg_surface_emit_path (surface->xml_node, path, ctm_inverse); + _cairo_svg_surface_emit_path (surface->xml_node, path, ctm_inverse, 0); _cairo_svg_surface_emit_transform (surface->xml_node, " transform", ctm, NULL); _cairo_output_stream_printf (surface->xml_node, "/>\n"); @@ -2494,6 +2710,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; @@ -2515,7 +2733,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); @@ -2576,6 +2794,244 @@ _cairo_svg_surface_get_font_options (void *abstract_surface, _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF); } +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) { + _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, + "\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 const char utf8_skip_data[256] = { + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 +}; + +#define UTF8_NEXT_CHAR(p) ((p) + utf8_skip_data[*(unsigned char *)(p)]) + +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)); + if (surface->document->prev_style.scaled_font == NULL || + !_cairo_svg_surface_equal_font(&surface->document->prev_style, &style)) { + _cairo_svg_end_text(surface); + while(i < utf8_len && utf8[i] == 0x20) { + ++i; + ++l; + } + if (i == utf8_len) { + return status; + } + + 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, + "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); + + x = glyphs[i].x; + y = glyphs[i].y; + cairo_matrix_transform_point(&matrix, &x, &y); + + _cairo_output_stream_printf (surface->document->text_x, "%f", x); + _cairo_output_stream_printf (surface->document->text_y, "%f", y); + ++i; + } + else { + matrix = scaled_font->scale; + cairo_matrix_scale(&matrix, 1 / font_size, 1 / font_size); + cairo_matrix_invert(&matrix); + } + for (;i < num_glyphs; i++) { + x = glyphs[i].x; + y = glyphs[i].y; + cairo_matrix_transform_point(&matrix, &x, &y); + _cairo_output_stream_printf (surface->document->text_x, " %f", x); + _cairo_output_stream_printf (surface->document->text_y, " %f", y); + } + for (i = l; i < utf8_len; i++) { + if (utf8[i] == '<') { + _cairo_output_stream_printf (surface->document->text, "<"); + continue; + } + if (utf8[i] == '>') { + _cairo_output_stream_printf (surface->document->text, ">"); + continue; + } + if (utf8[i] == '&') { + _cairo_output_stream_printf (surface->document->text, "&"); + continue; + } + _cairo_output_stream_printf (surface->document->text, "%c", utf8[i]); + } + + const unsigned char * const ustr = (const unsigned char *) utf8; + const unsigned char *in = ustr; + int n_chars = 0; + while (ustr + utf8_len - in > 0) { + n_chars++; + in = UTF8_NEXT_CHAR (in); + } + if (n_chars != num_clusters) + _cairo_svg_end_text(surface); + + // for XML space and 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, @@ -2606,6 +3062,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, + _cairo_svg_surface_has_show_text_glyphs, + _cairo_svg_surface_show_text_glyphs, }; static cairo_status_t @@ -2625,9 +3083,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; } @@ -2639,6 +3104,16 @@ _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->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; @@ -2651,11 +3126,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; @@ -2663,11 +3133,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; @@ -2703,17 +3172,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? * @@ -2746,14 +3230,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, "\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, "\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, "\n"); - _cairo_memory_stream_copy (document->xml_node_glyphs, output); + _cairo_memory_stream_copy (fontout.xml_node_glyphs, output); _cairo_output_stream_printf (output, "\n"); } _cairo_memory_stream_copy (document->xml_node_defs, output); @@ -2797,7 +3301,7 @@ _cairo_svg_document_finish (cairo_svg_document_t *document) _cairo_output_stream_printf (output, "\n"); - status2 = _cairo_output_stream_destroy (document->xml_node_glyphs); + status2 = _cairo_output_stream_destroy (document->css); if (status == CAIRO_STATUS_SUCCESS) status = status2; @@ -2809,6 +3313,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 test/reference/hatchings.ref.png test/reference/hatchings.ref.png index 3b34e5c..449d040 100644 Binary files test/reference/hatchings.ref.png and test/reference/hatchings.ref.png differ diff --git test/reference/twin-antialias-none.ref.png test/reference/twin-antialias-none.ref.png index 42f386d..d00e501 100644 Binary files test/reference/twin-antialias-none.ref.png and test/reference/twin-antialias-none.ref.png differ