--- /tmp/cairo-pdf-surface.c 2005-10-09 16:40:15.590738853 +0200 +++ cairo-pdf-surface.c 2005-10-09 17:08:01.000000000 +0200 @@ -805,7 +805,7 @@ emit_surface_pattern (cairo_pdf_surface_ status = _cairo_surface_acquire_source_image (pattern->surface, &image, &image_extra); if (status) - return; + return status; _cairo_pdf_document_close_stream (document); @@ -848,9 +848,9 @@ emit_surface_pattern (cairo_pdf_surface_ } typedef struct _cairo_pdf_color_stop { - cairo_fixed_t offset; - int id; - unsigned char color_char[4]; + double offset; + int id; + unsigned char color_char[4]; } cairo_pdf_color_stop_t; static int @@ -867,64 +867,173 @@ _cairo_pdf_color_stop_compare (const voi return (s1->offset < s2->offset) ? -1 : 1; } -static int -emit_pattern_stops (cairo_pdf_surface_t *surface, cairo_gradient_pattern_t *pattern) +static unsigned int +emit_linear_colorgradient (cairo_pdf_document_t *document, + cairo_pdf_color_stop_t *stop1, + cairo_pdf_color_stop_t *stop2) { - cairo_pdf_document_t *document = surface->document; cairo_output_stream_t *output = document->output_stream; - unsigned int function_id; - cairo_pdf_color_stop_t *stops; - int i; - - function_id = _cairo_pdf_document_new_object (document); - + unsigned int function_id = _cairo_pdf_document_new_object (document); + _cairo_output_stream_printf (output, "%d 0 obj\r\n" "<< /FunctionType 0\r\n" - " /Domain [ 0.0 1.0 ]\r\n" - " /Size [ %d ]\r\n" + " /Domain [ 0 1 ]\r\n" + " /Size [ 2 ]\r\n" " /BitsPerSample 8\r\n" - " /Range [ 0.0 1.0 0.0 1.0 0.0 1.0 ]\r\n" - " /Length %d\r\n" + " /Range [ 0 1 0 1 0 1 ]\r\n" + " /Length 6\r\n" ">>\r\n" "stream\r\n", - function_id, - pattern->n_stops, - pattern->n_stops * 3); + function_id); - stops = malloc (pattern->n_stops * sizeof(cairo_pdf_color_stop_t)); - if (!stops) { + _cairo_output_stream_write (output, stop1->color_char, 3); + _cairo_output_stream_write (output, stop2->color_char, 3); + _cairo_output_stream_printf (output, + "\r\n" + "endstream\r\n" + "endobj\r\n"); + + return function_id; +} + + +static unsigned int +emit_stiched_colorgradient (cairo_pdf_document_t *document, + unsigned int n_stops, + cairo_pdf_color_stop_t stops[]) +{ + cairo_output_stream_t *output = document->output_stream; + unsigned int *linear_ids = malloc(n_stops * sizeof(int)); + unsigned int function_id; + unsigned int i; + + if (!linear_ids) { _cairo_error (CAIRO_STATUS_NO_MEMORY); return 0; } + + /* emit linear gradients between pairs of subsequent stops... */ + for (i = 0; i < n_stops-1; i++) { + linear_ids[i] = emit_linear_colorgradient (document, + &stops[i], + &stops[i+1]); + } + + /* ... and stich them together */ + function_id = _cairo_pdf_document_new_object (document); + _cairo_output_stream_printf (output, + "%d 0 obj\r\n" + "<< /FunctionType 3\r\n" + " /Domain [ 0 1 ]\r\n" + " /Functions [ ", + function_id); + for (i = 0; i < n_stops-1; i++) { + _cairo_output_stream_printf (output, "%d 0 R ", linear_ids[i]); + } + _cairo_output_stream_printf (output, + "]\r\n" + " /Bounds [ "); + for (i = 1; i < n_stops-1; i++) { + _cairo_output_stream_printf (output, + "%f ", + stops[i].offset); + } + _cairo_output_stream_printf (output, + "]\r\n" + " /Encode [ "); + for (i = 1; i < n_stops; i++) { + _cairo_output_stream_printf (output, + "0 1 "); + } + _cairo_output_stream_printf (output, + "]\r\n" + ">>\r\n" + "endobj\r\n"); + free(linear_ids); + return function_id; +} + + +static int +emit_pattern_stops (cairo_pdf_surface_t *surface, cairo_gradient_pattern_t *pattern) +{ + cairo_pdf_document_t *document = surface->document; + unsigned int function_id; + cairo_pdf_color_stop_t *stops, *oldstops; + unsigned int n_stops; + unsigned int i; + + function_id = _cairo_pdf_document_new_object (document); + + stops = malloc(pattern->n_stops * sizeof(cairo_pdf_color_stop_t)); + if (!stops) { + _cairo_error (CAIRO_STATUS_NO_MEMORY); + return -CAIRO_STATUS_NO_MEMORY; + } for (i = 0; i < pattern->n_stops; i++) { stops[i].color_char[0] = pattern->stops[i].color.red * 0xff + 0.5; stops[i].color_char[1] = pattern->stops[i].color.green * 0xff + 0.5; stops[i].color_char[2] = pattern->stops[i].color.blue * 0xff + 0.5; stops[i].color_char[3] = pattern->stops[i].color.alpha * 0xff + 0.5; - stops[i].offset = pattern->stops[i].offset; + stops[i].offset = _cairo_fixed_to_double(pattern->stops[i].offset); stops[i].id = i; } - + /* sort stops in ascending order */ qsort (stops, pattern->n_stops, sizeof (cairo_pdf_color_stop_t), _cairo_pdf_color_stop_compare); - for (i = 0; i < pattern->n_stops; i++) { - _cairo_output_stream_write (output, stops[i].color_char, 3); + /* make sure first offset is 0.0 and last offset is 1.0. (Otherwise Acrobat + * Reader chokes.) */ + n_stops = pattern->n_stops; + if (stops[0].offset != 0.0) n_stops++; + if (stops[pattern->n_stops-1].offset != 1.0) n_stops++; + if (pattern->n_stops != n_stops) { + oldstops = stops; + stops = malloc(n_stops * sizeof(cairo_pdf_color_stop_t)); + if (!stops) { + free(oldstops); + _cairo_error (CAIRO_STATUS_NO_MEMORY); + return -CAIRO_STATUS_NO_MEMORY; + } + + if (oldstops[0].offset != 0.0) { + memcpy(&stops[1], + oldstops, + (pattern->n_stops) * sizeof(cairo_pdf_color_stop_t)); + } else { + memcpy(stops, + oldstops, + (pattern->n_stops) * sizeof(cairo_pdf_color_stop_t)); + } + + if (oldstops[pattern->n_stops-1].offset != 1.0) { + memcpy(&stops[n_stops-1], + &oldstops[pattern->n_stops-1], + sizeof(cairo_pdf_color_stop_t)); + } + + free(oldstops); + } + + if (n_stops == 2) { + /* no need for stiched function */ + function_id = emit_linear_colorgradient(document, &stops[0], &stops[1]); + } else { + /* multiple stops: stich. XXX possible optimization: regulary spaced + * stops do not require stiching. XXX */ + function_id = emit_stiched_colorgradient(document, + n_stops, + stops); } - free (stops); - - _cairo_output_stream_printf (output, - "\r\n" - "endstream\r\n" - "endobj\r\n"); - + free(stops); return function_id; } + static cairo_status_t emit_linear_pattern (cairo_pdf_surface_t *surface, cairo_linear_pattern_t *pattern) {