[cairo-commit] 2 commits - doc/public src/cairo-analysis-surface.c src/cairo-analysis-surface-private.h src/cairo-paginated-private.h src/cairo-paginated-surface.c src/cairo-ps.h src/cairo-ps-surface.c src/cairo-ps-surface-private.h

Adrian Johnson ajohnson at kemper.freedesktop.org
Sun Sep 23 01:17:22 PDT 2007


 doc/public/cairo-sections.txt        |    2 
 src/cairo-analysis-surface-private.h |   10 +
 src/cairo-analysis-surface.c         |   32 +++++
 src/cairo-paginated-private.h        |   19 ++-
 src/cairo-paginated-surface.c        |   15 ++
 src/cairo-ps-surface-private.h       |    5 
 src/cairo-ps-surface.c               |  208 ++++++++++++++++++++++++++---------
 src/cairo-ps.h                       |    7 +
 8 files changed, 239 insertions(+), 59 deletions(-)

New commits:
diff-tree 77f1aa78872aa108199afd41247cf21827f7e33a (from beefbdd63864d10924360f41b244de7edf7b5f5b)
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sun Sep 23 17:43:44 2007 +0930

    Add Encapsulated PostScript support
    
    The analysis surface will calculated the tight bounding box for each
    page. A new paginated-surface backend function set_bounding_box() has
    been added for passing the page bounding box to the target surface at
    the end of the analysis phase.
    
    The changes to the PS file when EPS is enabled are:
        - Add EPS header
        - Use tight bounding box instead of page size
        - Use save/restore to ensure PS interpreter is left in the same state

diff --git a/src/cairo-analysis-surface-private.h b/src/cairo-analysis-surface-private.h
index c17b0ef..9fcfed0 100644
--- a/src/cairo-analysis-surface-private.h
+++ b/src/cairo-analysis-surface-private.h
@@ -46,12 +46,16 @@ cairo_private cairo_region_t *
 _cairo_analysis_surface_get_supported (cairo_surface_t *surface);
 
 cairo_private cairo_region_t *
-_cairo_analysis_surface_get_unsupported (cairo_surface_t *unsupported);
+_cairo_analysis_surface_get_unsupported (cairo_surface_t *surface);
 
 cairo_private cairo_bool_t
-_cairo_analysis_surface_has_supported (cairo_surface_t *unsupported);
+_cairo_analysis_surface_has_supported (cairo_surface_t *surface);
 
 cairo_private cairo_bool_t
-_cairo_analysis_surface_has_unsupported (cairo_surface_t *unsupported);
+_cairo_analysis_surface_has_unsupported (cairo_surface_t *surface);
+
+cairo_private void
+_cairo_analysis_surface_get_bounding_box (cairo_surface_t *surface,
+					  cairo_box_t     *bbox);
 
 #endif /* CAIRO_ANALYSIS_SURFACE_H */
diff --git a/src/cairo-analysis-surface.c b/src/cairo-analysis-surface.c
index 967785f..067543f 100644
--- a/src/cairo-analysis-surface.c
+++ b/src/cairo-analysis-surface.c
@@ -48,12 +48,14 @@ typedef struct {
 
     cairo_surface_t	*target;
 
+    cairo_bool_t first_op;
     cairo_bool_t has_supported;
     cairo_bool_t has_unsupported;
 
     cairo_region_t supported_region;
     cairo_region_t fallback_region;
     cairo_rectangle_int_t current_clip;
+    cairo_box_t page_bbox;
 
 } cairo_analysis_surface_t;
 
@@ -90,10 +92,30 @@ _cairo_analysis_surface_add_operation  (
 					cairo_int_status_t        backend_status)
 {
     cairo_int_status_t status;
+    cairo_box_t bbox;
 
     if (rect->width == 0 || rect->height == 0)
 	return CAIRO_STATUS_SUCCESS;
 
+    bbox.p1.x = _cairo_fixed_from_int (rect->x);
+    bbox.p1.y = _cairo_fixed_from_int (rect->y);
+    bbox.p2.x = _cairo_fixed_from_int (rect->x + rect->width);
+    bbox.p2.y = _cairo_fixed_from_int (rect->y + rect->height);
+
+    if (surface->first_op) {
+	surface->first_op = FALSE;
+	surface->page_bbox = bbox;
+    } else {
+	if (bbox.p1.x < surface->page_bbox.p1.x)
+	    surface->page_bbox.p1.x = bbox.p1.x;
+	if (bbox.p1.y < surface->page_bbox.p1.y)
+	    surface->page_bbox.p1.y = bbox.p1.y;
+	if (bbox.p2.x > surface->page_bbox.p2.x)
+	    surface->page_bbox.p2.x = bbox.p2.x;
+	if (bbox.p2.y > surface->page_bbox.p2.y)
+	    surface->page_bbox.p2.y = bbox.p2.y;
+    }
+
     /* If the operation is completely enclosed within the fallback
      * region there is no benefit in emitting a native operation as
      * the fallback image will be painted on top.
@@ -557,6 +579,7 @@ _cairo_analysis_surface_create (cairo_su
     surface->height = height;
 
     surface->target = target;
+    surface->first_op  = TRUE;
     surface->has_supported = FALSE;
     surface->has_unsupported = FALSE;
     _cairo_region_init (&surface->supported_region);
@@ -604,3 +627,12 @@ _cairo_analysis_surface_has_unsupported 
 
     return surface->has_unsupported;
 }
+
+void
+_cairo_analysis_surface_get_bounding_box (cairo_surface_t *abstract_surface,
+					  cairo_box_t     *bbox)
+{
+    cairo_analysis_surface_t	*surface = (cairo_analysis_surface_t *) abstract_surface;
+
+    *bbox = surface->page_bbox;
+}
diff --git a/src/cairo-paginated-private.h b/src/cairo-paginated-private.h
index fdcb8fa..1ecb014 100644
--- a/src/cairo-paginated-private.h
+++ b/src/cairo-paginated-private.h
@@ -59,6 +59,14 @@ struct _cairo_paginated_surface_backend 
     void
     (*set_paginated_mode)	(void			*surface,
 				 cairo_paginated_mode_t	 mode);
+
+    /* Optional. Specifies the smallest box that encloses all objects
+     * on the page. Will be called at the end of the ANALYZE phase but
+     * before the mode is changed to RENDER.
+     */
+    cairo_warn cairo_int_status_t
+    (*set_bounding_box)	(void	   	*surface,
+			 cairo_box_t	*bbox);
 };
 
 /* A cairo_paginated_surface provides a very convenient wrapper that
@@ -95,11 +103,14 @@ struct _cairo_paginated_surface_backend 
  *    from each operation). This analysis stage is used to decide which
  *    operations will require fallbacks.
  *
- * 4. Calls set_paginated_mode with an argument of CAIRO_PAGINATED_MODE_RENDER
+ * 4. Calls set_bounding_box to provide the target surface with the
+ *    tight bounding box of the page.
+ *
+ * 5. Calls set_paginated_mode with an argument of CAIRO_PAGINATED_MODE_RENDER
  *
- * 5. Replays a subset of the meta-surface operations to the target surface
+ * 6. Replays a subset of the meta-surface operations to the target surface
  *
- * 6. Replays the remaining operations to an image surface, sets an
+ * 7. Replays the remaining operations to an image surface, sets an
  *    appropriate clip on the target, then paints the resulting image
  *    surface to the target.
  *
@@ -114,7 +125,7 @@ struct _cairo_paginated_surface_backend 
  *
  * NOTE: The paginated surface layer assumes that the target surface
  * is "blank" by default at the beginning of each page, without any
- * need for an explicit erasea operation, (as opposed to an image
+ * need for an explicit erase operation, (as opposed to an image
  * surface, for example, which might have uninitialized content
  * originally). As such, it optimizes away CLEAR operations that
  * happen at the beginning of each page---the target surface will not
diff --git a/src/cairo-paginated-surface.c b/src/cairo-paginated-surface.c
index 78b7e30..007cc89 100644
--- a/src/cairo-paginated-surface.c
+++ b/src/cairo-paginated-surface.c
@@ -291,8 +291,6 @@ _paint_page (cairo_paginated_surface_t *
 
     surface->backend->set_paginated_mode (surface->target, CAIRO_PAGINATED_MODE_ANALYZE);
     status = _cairo_meta_surface_replay_and_create_regions (surface->meta, analysis);
-    surface->backend->set_paginated_mode (surface->target, CAIRO_PAGINATED_MODE_RENDER);
-
     if (status || analysis->status) {
 	if (status == CAIRO_STATUS_SUCCESS)
 	    status = analysis->status;
@@ -300,6 +298,19 @@ _paint_page (cairo_paginated_surface_t *
 	return status;
     }
 
+     if (surface->backend->set_bounding_box) {
+	 cairo_box_t bbox;
+
+	 _cairo_analysis_surface_get_bounding_box (analysis, &bbox);
+	 status = surface->backend->set_bounding_box (surface->target, &bbox);
+	 if (status) {
+	     cairo_surface_destroy (analysis);
+	     return status;
+	 }
+     }
+
+    surface->backend->set_paginated_mode (surface->target, CAIRO_PAGINATED_MODE_RENDER);
+
     /* Finer grained fallbacks are currently only supported for some
      * surface types */
     switch (surface->target->type) {
diff --git a/src/cairo-ps-surface-private.h b/src/cairo-ps-surface-private.h
index 9da058c..eea7e17 100644
--- a/src/cairo-ps-surface-private.h
+++ b/src/cairo-ps-surface-private.h
@@ -61,8 +61,7 @@ typedef struct cairo_ps_surface {
 
     double width;
     double height;
-    double max_width;
-    double max_height;
+    int bbox_x1, bbox_y1, bbox_x2, bbox_y2;
 
     int num_pages;
 
diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index 3e36268..27bf849 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -296,21 +296,27 @@ _cairo_ps_surface_emit_header (cairo_ps_
     time_t now;
     char **comments;
     int i, num_comments;
+    const char *eps_header = "";
 
     now = time (NULL);
 
+    if (surface->eps)
+	eps_header = " EPSF-3.0";
+
     _cairo_output_stream_printf (surface->final_stream,
-				 "%%!PS-Adobe-3.0\n"
+				 "%%!PS-Adobe-3.0%s\n"
 				 "%%%%Creator: cairo %s (http://cairographics.org)\n"
 				 "%%%%CreationDate: %s"
 				 "%%%%Pages: %d\n"
 				 "%%%%BoundingBox: %d %d %d %d\n",
-                                 cairo_version_string (),
+				 eps_header,
+				 cairo_version_string (),
 				 ctime (&now),
 				 surface->num_pages,
-				 0, 0,
-				 (int) ceil (surface->max_width),
-				 (int) ceil (surface->max_height));
+				 surface->bbox_x1,
+				 surface->bbox_y1,
+				 surface->bbox_x2,
+				 surface->bbox_y2);
 
     _cairo_output_stream_printf (surface->final_stream,
 				 "%%%%DocumentData: Clean7Bit\n"
@@ -329,7 +335,17 @@ _cairo_ps_surface_emit_header (cairo_ps_
 				 "%%%%EndComments\n");
 
     _cairo_output_stream_printf (surface->final_stream,
-				 "%%%%BeginProlog\n"
+				 "%%%%BeginProlog\n");
+
+    if (surface->eps) {
+	_cairo_output_stream_printf (surface->final_stream,
+				     "/cairo_eps_state save def\n"
+				     "/dict_count countdictstack def\n"
+				     "/op_count count 1 sub def\n"
+				     "userdict begin\n");
+    }
+
+    _cairo_output_stream_printf (surface->final_stream,
 				 "/C{curveto}bind def\n"
 				 "/F{fill}bind def\n"
 				 "/G{setgray}bind def\n"
@@ -766,7 +782,16 @@ static void
 _cairo_ps_surface_emit_footer (cairo_ps_surface_t *surface)
 {
     _cairo_output_stream_printf (surface->final_stream,
-				 "%%%%Trailer\n"
+				 "%%%%Trailer\n");
+
+    if (surface->eps) {
+	_cairo_output_stream_printf (surface->final_stream,
+				     "count op_count sub {pop} repeat\n"
+				     "countdictstack dict_count sub {end} repeat\n"
+				     "cairo_eps_state restore\n");
+    }
+
+    _cairo_output_stream_printf (surface->final_stream,
 				 "%%%%EOF\n");
 }
 
@@ -802,10 +827,9 @@ _cairo_ps_surface_create_for_stream_inte
     if (! surface->font_subsets)
 	goto CLEANUP_OUTPUT_STREAM;
 
+    surface->eps = FALSE;
     surface->width  = width;
     surface->height = height;
-    surface->max_width = width;
-    surface->max_height = height;
     surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE;
     surface->force_fallbacks = FALSE;
 
@@ -1300,48 +1324,11 @@ static cairo_int_status_t
 _cairo_ps_surface_start_page (void *abstract_surface)
 {
     cairo_ps_surface_t *surface = abstract_surface;
-    int i, num_comments;
-    char **comments;
 
     /* Increment before print so page numbers start at 1. */
     surface->num_pages++;
-    _cairo_output_stream_printf (surface->stream,
-				 "%%%%Page: %d %d\n",
-				 surface->num_pages,
-				 surface->num_pages);
-
-    _cairo_output_stream_printf (surface->stream,
-				 "%%%%BeginPageSetup\n");
 
-    num_comments = _cairo_array_num_elements (&surface->dsc_page_setup_comments);
-    comments = _cairo_array_index (&surface->dsc_page_setup_comments, 0);
-    for (i = 0; i < num_comments; i++) {
-	_cairo_output_stream_printf (surface->stream,
-				     "%s\n", comments[i]);
-	free (comments[i]);
-	comments[i] = NULL;
-    }
-    _cairo_array_truncate (&surface->dsc_page_setup_comments, 0);
-
-    _cairo_output_stream_printf (surface->stream,
-				 "%%%%PageBoundingBox: %d %d %d %d\n",
-				 0, 0,
-				 (int) ceil (surface->width),
-				 (int) ceil (surface->height));
-
-    _cairo_output_stream_printf (surface->stream,
-				 "gsave %f %f translate 1.0 -1.0 scale gsave\n",
-				 0.0, surface->height);
-
-    _cairo_output_stream_printf (surface->stream,
-				 "%%%%EndPageSetup\n");
-
-    if (surface->width > surface->max_width)
-	surface->max_width = surface->width;
-    if (surface->height > surface->max_height)
-	surface->max_height = surface->height;
-
-    return _cairo_output_stream_get_status (surface->stream);
+    return CAIRO_STATUS_SUCCESS;
 }
 
 static void
@@ -2426,6 +2413,73 @@ _cairo_ps_surface_set_paginated_mode (vo
     surface->paginated_mode = paginated_mode;
 }
 
+static cairo_int_status_t
+_cairo_ps_surface_set_bounding_box (void		*abstract_surface,
+				    cairo_box_t		*bbox)
+{
+    cairo_ps_surface_t *surface = abstract_surface;
+    int i, num_comments;
+    char **comments;
+    int x1, y1, x2, y2;
+
+    if (surface->eps) {
+	x1 = (int) floor (_cairo_fixed_to_double (bbox->p1.x));
+	y1 = (int) floor (surface->height - _cairo_fixed_to_double (bbox->p2.y));
+	x2 = (int) ceil (_cairo_fixed_to_double (bbox->p2.x));
+	y2 = (int) ceil (surface->height - _cairo_fixed_to_double (bbox->p1.y));
+    } else {
+	x1 = 0;
+	y1 = 0;
+	x2 = (int) ceil (surface->width);
+	y2 = (int) ceil (surface->height);
+    }
+
+    _cairo_output_stream_printf (surface->stream,
+				 "%%%%Page: %d %d\n",
+				 surface->num_pages,
+				 surface->num_pages);
+
+    _cairo_output_stream_printf (surface->stream,
+				 "%%%%BeginPageSetup\n");
+
+    num_comments = _cairo_array_num_elements (&surface->dsc_page_setup_comments);
+    comments = _cairo_array_index (&surface->dsc_page_setup_comments, 0);
+    for (i = 0; i < num_comments; i++) {
+	_cairo_output_stream_printf (surface->stream,
+				     "%s\n", comments[i]);
+	free (comments[i]);
+	comments[i] = NULL;
+    }
+    _cairo_array_truncate (&surface->dsc_page_setup_comments, 0);
+
+    _cairo_output_stream_printf (surface->stream,
+				 "%%%%PageBoundingBox: %d %d %d %d\n"
+				 "gsave %f %f translate 1.0 -1.0 scale gsave\n",
+				 x1, y1, x2, y2,
+				 0.0, surface->height);
+
+    _cairo_output_stream_printf (surface->stream,
+                                 "%%%%EndPageSetup\n");
+
+    if (surface->num_pages == 1) {
+	surface->bbox_x1 = x1;
+	surface->bbox_y1 = y1;
+	surface->bbox_x2 = x2;
+	surface->bbox_y2 = y2;
+    } else {
+	if (x1 < surface->bbox_x1)
+	    surface->bbox_x1 = x1;
+	if (y1 < surface->bbox_y1)
+	    surface->bbox_y1 = y1;
+	if (x2 > surface->bbox_x2)
+	    surface->bbox_x2 = x2;
+	if (y2 > surface->bbox_y2)
+	    surface->bbox_y2 = y2;
+    }
+
+    return _cairo_output_stream_get_status (surface->stream);
+}
+
 static const cairo_surface_backend_t cairo_ps_surface_backend = {
     CAIRO_SURFACE_TYPE_PS,
     NULL, /* create_similar */
@@ -2462,5 +2516,6 @@ static const cairo_surface_backend_t cai
 
 static const cairo_paginated_surface_backend_t cairo_ps_surface_paginated_backend = {
     _cairo_ps_surface_start_page,
-    _cairo_ps_surface_set_paginated_mode
+    _cairo_ps_surface_set_paginated_mode,
+    _cairo_ps_surface_set_bounding_box,
 };
diff-tree beefbdd63864d10924360f41b244de7edf7b5f5b (from b20e08999e2f6e7a72ee75a7c3fd865bf0368794)
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sun Sep 23 14:23:33 2007 +0930

    Add Encapsulated PostScript API

diff --git a/doc/public/cairo-sections.txt b/doc/public/cairo-sections.txt
index 3bf8956..a06692e 100644
--- a/doc/public/cairo-sections.txt
+++ b/doc/public/cairo-sections.txt
@@ -58,6 +58,8 @@ cairo_surface_write_to_png_stream
 <TITLE>PostScript Surfaces</TITLE>
 cairo_ps_surface_create
 cairo_ps_surface_create_for_stream
+cairo_ps_surface_set_eps
+cairo_ps_surface_get_eps
 cairo_ps_surface_set_size
 cairo_ps_surface_dsc_begin_setup
 cairo_ps_surface_dsc_begin_page_setup
diff --git a/src/cairo-ps-surface-private.h b/src/cairo-ps-surface-private.h
index 3a23c21..9da058c 100644
--- a/src/cairo-ps-surface-private.h
+++ b/src/cairo-ps-surface-private.h
@@ -57,6 +57,8 @@ typedef struct cairo_ps_surface {
     FILE *tmpfile;
     cairo_output_stream_t *stream;
 
+    cairo_bool_t eps;
+
     double width;
     double height;
     double max_width;
diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index 83e5bbb..3e36268 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -957,6 +957,63 @@ _extract_ps_surface (cairo_surface_t	 *s
 }
 
 /**
+ * cairo_ps_surface_set_eps:
+ * @surface: a PostScript cairo_surface_t
+ * @eps: TRUE to output EPS format PostScript
+ *
+ * If @eps is TRUE, the PostScript surface will output Encapsulated
+ * PostScript.
+ *
+ * This function should only be called before any drawing operations
+ * have been performed on the current page. The simplest way to do
+ * this is to call this function immediately after creating the
+ * surface. An Encapsulated Postscript file should never contain more
+ * than one page.
+ *
+ * Since: 1.6
+ **/
+void
+cairo_ps_surface_set_eps (cairo_surface_t	*surface,
+			  cairo_bool_t           eps)
+{
+    cairo_ps_surface_t *ps_surface;
+    cairo_status_t status;
+
+    status = _extract_ps_surface (surface, &ps_surface);
+    if (status) {
+	_cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+	return;
+    }
+
+    ps_surface->eps = eps;
+}
+
+/**
+ * cairo_ps_surface_get_eps:
+ * @surface: a PostScript cairo_surface_t
+ *
+ * Check whether the PostScript surface will output Encapsulated PostScript.
+ *
+ * Return value: TRUE if the surface will output Encapsulated PostScript.
+ *
+ * Since: 1.6
+ **/
+cairo_public cairo_bool_t
+cairo_ps_surface_get_eps (cairo_surface_t	*surface)
+{
+    cairo_ps_surface_t *ps_surface;
+    cairo_status_t status;
+
+    status = _extract_ps_surface (surface, &ps_surface);
+    if (status) {
+	_cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+	return FALSE;
+    }
+
+    return ps_surface->eps;
+}
+
+/**
  * cairo_ps_surface_set_size:
  * @surface: a PostScript cairo_surface_t
  * @width_in_points: new surface width, in points (1 point == 1/72.0 inch)
diff --git a/src/cairo-ps.h b/src/cairo-ps.h
index a61d12d..24dd9f6 100644
--- a/src/cairo-ps.h
+++ b/src/cairo-ps.h
@@ -59,6 +59,13 @@ cairo_ps_surface_create_for_stream (cair
 				    double		height_in_points);
 
 cairo_public void
+cairo_ps_surface_set_eps (cairo_surface_t	*surface,
+			  cairo_bool_t           eps);
+
+cairo_public cairo_bool_t
+cairo_ps_surface_get_eps (cairo_surface_t	*surface);
+
+cairo_public void
 cairo_ps_surface_set_size (cairo_surface_t	*surface,
 			   double		 width_in_points,
 			   double		 height_in_points);


More information about the cairo-commit mailing list