[cairo] Meta surface update and new ps surface

Kristian Høgsberg krh at bitplanet.net
Fri Jun 17 12:17:58 PDT 2005


Hi,

Using the meta surface I posted a few weeks back, I've been working on a 
better postscript backend.  I'm attaching a patch with the meta surface 
and the new postscript backend.  The meta surface hasn't changed much 
from what I posted earlier; I've updated it to support the new path 
clipping and fixed a couple of bugs.

The postscript surface replaces the existing one-big-image 
implementation with a hybrid approach that use postscript primitives 
wherever possible and falls back to overlaying images in areas where 
postscript is not sufficient.

The postscript surface collects all pages using a meta surface per page, 
and when the surface is finished, these pages are written to disk.  It 
is necessary to process the document in two passes, since we need to 
first figure out which subsets of the fonts used we need, and then write 
out the postscript document with the font subsets embedded before any 
pages that use them.  If the current approach of storing the entire 
document in memory turns out to be infeasible, it's easy to change it to 
use a temporary file instead.  I would rather avoid temporary files and 
all the problems they come with, though.

The meta surface is replayed a few times during generation of the 
postscript file:

	- Determining the font subsets for the document
	- Outputting the postscript primitives
	- Determining the fallback areas for a page
	- Rendering the fallback areas for a page

The last two aren't implemented yet but the infrastructure is in place 
for them.

The postscript surface currently supports:

	- Basic filling and stroking using solid colors.
	- Clipping.
	- Compositing non-repeating images
	- Text, font subsetting and embedding (truetype only).

Still to be done:

	- Gradients.
	- Tiling surface patterns.
	- Image fallbacks

The obligatory cairo snippets output:

	http://freedesktop.org/~krh/cairo/snippets.ps.gz

The patch is up to 4000 lines at this point, so I'm hoping to land some 
of this in CVS soon.

cheers,
Kristian
-------------- next part --------------
? make-patch.sh
? doc/public/tmpl/cairo-image.sgml
? doc/public/tmpl/cairo-win3.sgml
? src/cairo-font-subset-private.h
? src/cairo-font-subset.c
? src/cairo-meta-surface-private.h
? src/cairo-meta-surface.c
? test/icecream-0.6-1.i386.rpm
? test/meta-ps-surface.c
? test/meta-surface.c
? test/meta-surface.png
? test/pdf-clip.ps
Index: src/Makefile.am
===================================================================
RCS file: /cvs/cairo/cairo/src/Makefile.am,v
retrieving revision 1.51
diff -u -p -r1.51 Makefile.am
--- src/Makefile.am	17 May 2005 16:08:23 -0000	1.51
+++ src/Makefile.am	17 Jun 2005 17:49:39 -0000
@@ -2,11 +2,13 @@
 if CAIRO_HAS_PS_SURFACE
 libcairo_ps_headers = cairo-ps.h
 libcairo_ps_sources = cairo-ps-surface.c
+libcairo_font_subset_sources = cairo-font-subset.c cairo-font-subset-private.h
 endif
 
 if CAIRO_HAS_PDF_SURFACE
 libcairo_pdf_headers = cairo-pdf.h
 libcairo_pdf_sources = cairo-pdf-surface.c
+libcairo_font_subset_sources = cairo-font-subset.c cairo-font-subset-private.h
 endif
 
 if CAIRO_HAS_PNG_FUNCTIONS
@@ -75,50 +77,53 @@ cairoinclude_HEADERS =			\
 
 lib_LTLIBRARIES = libcairo.la
 
-libcairo_la_SOURCES = 		\
-	cairo.c			\
-	cairo.h			\
-	cairo-private.h		\
-	cairo-arc.c		\
-	cairo-arc-private.h	\
-	cairo-array.c		\
-	cairo-cache.c		\
-	cairo-color.c		\
-	cairo-fixed.c		\
-	cairo-font.c		\
-	cairo-gstate.c		\
-	cairo-gstate-private.h	\
-	cairo-hull.c		\
-	cairo-image-surface.c	\
-	cairo-matrix.c		\
-	cairo-path.c		\
-	cairo-path-bounds.c	\
-	cairo-path-data.c	\
-	cairo-path-data-private.h \
-	cairo-path-fill.c	\
-	cairo-path-fixed-private.h \
-	cairo-path-stroke.c	\
-	cairo-pen.c		\
-	cairo-polygon.c		\
-	cairo-slope.c		\
-	cairo-spline.c		\
-	cairo-surface.c		\
-	cairo-traps.c		\
-	cairo-pattern.c		\
-	cairo-unicode.c		\
-	cairo-output-stream.c	\
-	cairo-wideint.c		\
-	cairo-wideint.h		\
-	$(libcairo_atsui_sources)\
-	$(libcairo_ft_sources)\
-	$(libcairo_ps_sources)  \
-	$(libcairo_pdf_sources) \
-	$(libcairo_png_sources) \
-	$(libcairo_xlib_sources)\
-	$(libcairo_quartz_sources)\
-	$(libcairo_xcb_sources) \
-	$(libcairo_glitz_sources)\
-	$(libcairo_win32_sources)\
+libcairo_la_SOURCES =				\
+	cairo.c					\
+	cairo.h					\
+	cairo-private.h				\
+	cairo-arc.c				\
+	cairo-arc-private.h			\
+	cairo-array.c				\
+	cairo-cache.c				\
+	cairo-color.c				\
+	cairo-fixed.c				\
+	cairo-font.c				\
+	cairo-gstate.c				\
+	cairo-gstate-private.h			\
+	cairo-hull.c				\
+	cairo-image-surface.c			\
+	cairo-matrix.c				\
+	cairo-path.c				\
+	cairo-path-bounds.c			\
+	cairo-path-data.c			\
+	cairo-path-data-private.h		\
+	cairo-path-fill.c			\
+	cairo-path-fixed-private.h		\
+	cairo-path-stroke.c			\
+	cairo-pen.c				\
+	cairo-polygon.c				\
+	cairo-slope.c				\
+	cairo-spline.c				\
+	cairo-surface.c				\
+	cairo-traps.c				\
+	cairo-pattern.c				\
+	cairo-unicode.c				\
+	cairo-output-stream.c			\
+	cairo-wideint.c				\
+	cairo-wideint.h				\
+	cairo-meta-surface.c			\
+	cairo-meta-surface.h			\
+	$(libcairo_atsui_sources)		\
+	$(libcairo_ft_sources)			\
+	$(libcairo_ps_sources)			\
+	$(libcairo_pdf_sources)			\
+	$(libcairo_font_subset_sources)		\
+	$(libcairo_png_sources)			\
+	$(libcairo_xlib_sources)		\
+	$(libcairo_quartz_sources)		\
+	$(libcairo_xcb_sources)			\
+	$(libcairo_glitz_sources)		\
+	$(libcairo_win32_sources)		\
 	cairoint.h
 
 libcairo_la_LDFLAGS = -version-info @VERSION_INFO@ -no-undefined
Index: src/cairo-output-stream.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo-output-stream.c,v
retrieving revision 1.6
diff -u -p -r1.6 cairo-output-stream.c
--- src/cairo-output-stream.c	17 May 2005 19:40:55 -0000	1.6
+++ src/cairo-output-stream.c	17 Jun 2005 17:49:39 -0000
@@ -90,6 +90,26 @@ _cairo_output_stream_write (cairo_output
     return stream->status;
 }
 
+void
+_cairo_output_stream_write_hex_string (cairo_output_stream_t *stream,
+				       const char *data,
+				       size_t length)
+{
+    const char hex_chars[] = "0123456789abcdef";
+    char buffer[2];
+    int i, column;
+
+    for (i = 0, column = 0; i < length; i++, column++) {
+	if (column == 38) {
+	    _cairo_output_stream_write (stream, "\n", 1);
+	    column = 0;
+	}
+	buffer[0] = hex_chars[(data[i] >> 4) & 0x0f];
+	buffer[1] = hex_chars[data[i] & 0x0f];
+	_cairo_output_stream_write (stream, buffer, 2);
+    }
+}
+
 /* Format a double in a locale independent way and trim trailing
  * zeros.  Based on code from Alex Larson <alexl at redhat.com>.
  * http://mail.gnome.org/archives/gtk-devel-list/2001-October/msg00087.html
@@ -187,8 +207,8 @@ _cairo_output_stream_vprintf (cairo_outp
 
 	switch (*f | length_modifier) {
 	case '%':
-	    p[0] = *f;
-	    p[1] = 0;
+	    buffer[0] = *f;
+	    buffer[1] = 0;
 	    break;
 	case 'd':
 	    snprintf (buffer, sizeof buffer, "%d", va_arg (ap, int));
@@ -211,6 +231,10 @@ _cairo_output_stream_vprintf (cairo_outp
 	case 'f':
 	    dtostr (buffer, sizeof buffer, va_arg (ap, double));
 	    break;
+	case 'c':
+	    buffer[0] = va_arg (ap, int);
+	    buffer[1] = 0;
+	    break;
 	default:
 	    ASSERT_NOT_REACHED;
 	}
Index: src/cairo-pdf-surface.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo-pdf-surface.c,v
retrieving revision 1.46
diff -u -p -r1.46 cairo-pdf-surface.c
--- src/cairo-pdf-surface.c	15 Jun 2005 02:45:23 -0000	1.46
+++ src/cairo-pdf-surface.c	17 Jun 2005 17:49:40 -0000
@@ -36,15 +36,9 @@
 
 #include "cairoint.h"
 #include "cairo-pdf.h"
-/* XXX: Eventually, we need to handle other font backends */
+#include "cairo-font-subset-private.h"
 #include "cairo-ft-private.h"
 
-#include <ft2build.h>
-#include FT_FREETYPE_H
-#include FT_OUTLINE_H
-#include FT_TRUETYPE_TAGS_H
-#include FT_TRUETYPE_TABLES_H
-
 #include <time.h>
 #include <zlib.h>
 
@@ -93,45 +87,6 @@
  *   instead of outputting the cm operator in every page.
  */
 
-typedef struct ft_subset_glyph ft_subset_glyph_t;
-struct ft_subset_glyph {
-    int parent_index;
-    unsigned long location;
-};
-
-typedef struct cairo_pdf_font_backend cairo_pdf_font_backend_t;
-struct cairo_pdf_font_backend {
-    int			(*use_glyph)	(void *abstract_font,
-					 int glyph);
-    cairo_status_t	(*generate)	(void *abstract_font,
-					 const char **data,
-					 unsigned long *length);
-    void		(*destroy)	(void *abstract_font);
-};
-
-typedef struct cairo_pdf_font cairo_pdf_font_t;
-struct cairo_pdf_font {
-    cairo_pdf_font_backend_t *backend;
-    cairo_unscaled_font_t *unscaled_font;
-    unsigned int font_id;
-    char *base_font;
-    int num_glyphs;
-    int *widths;
-    long x_min, y_min, x_max, y_max;
-    long ascent, descent;
-};
-
-typedef struct cairo_pdf_ft_font cairo_pdf_ft_font_t;
-struct cairo_pdf_ft_font {
-    cairo_pdf_font_t base;
-    ft_subset_glyph_t *glyphs;
-    FT_Face face;
-    int checksum_index;
-    cairo_array_t output;
-    int *parent_to_subset;
-    cairo_status_t status;
-};
-
 typedef struct cairo_pdf_object cairo_pdf_object_t;
 typedef struct cairo_pdf_resource cairo_pdf_resource_t;
 typedef struct cairo_pdf_stream cairo_pdf_stream_t;
@@ -221,6 +176,9 @@ static cairo_pdf_stream_t *
 _cairo_pdf_document_open_stream (cairo_pdf_document_t	*document,
 				 const char		*fmt,
 				 ...);
+static void
+_cairo_pdf_document_close_stream (cairo_pdf_document_t	*document);
+
 static cairo_surface_t *
 _cairo_pdf_surface_create_for_document (cairo_pdf_document_t	*document,
 					double			width,
@@ -233,590 +191,6 @@ _cairo_pdf_surface_ensure_stream (cairo_
 
 static const cairo_surface_backend_t cairo_pdf_surface_backend;
 
-/* Truetype font subsetting code */
-
-#define ARRAY_LENGTH(a) ( (sizeof (a)) / (sizeof ((a)[0])) )
-
-#define SFNT_VERSION			0x00010000
-
-#ifdef WORDS_BIGENDIAN
-
-#define cpu_to_be16(v) (v)
-#define be16_to_cpu(v) (v)
-#define cpu_to_be32(v) (v)
-#define be32_to_cpu(v) (v)
-
-#else
-
-static inline unsigned short
-cpu_to_be16(unsigned short v)
-{
-    return (v << 8) | (v >> 8);
-}
-
-static inline unsigned short
-be16_to_cpu(unsigned short v)
-{
-    return cpu_to_be16 (v);
-}
-
-static inline unsigned long
-cpu_to_be32(unsigned long v)
-{
-    return (cpu_to_be16 (v) << 16) | cpu_to_be16 (v >> 16);
-}
-
-static inline unsigned long
-be32_to_cpu(unsigned long v)
-{
-    return cpu_to_be32 (v);
-}
-
-#endif
-
-static cairo_pdf_font_backend_t cairo_pdf_ft_font_backend;
-
-static int
-cairo_pdf_font_use_glyph (cairo_pdf_font_t *font, int glyph)
-{
-    return font->backend->use_glyph (font, glyph);
-}
-
-static cairo_status_t
-cairo_pdf_font_generate (cairo_pdf_font_t *font,
-			 const char **data, unsigned long *length)
-{
-    return font->backend->generate (font, data, length);
-}
-
-static void
-cairo_pdf_font_destroy (cairo_pdf_font_t *font)
-{
-    if (font == NULL)
-	return;
-
-    font->backend->destroy (font);
-}
-
-static cairo_pdf_font_t *
-cairo_pdf_ft_font_create (cairo_pdf_document_t  *document,
-			  cairo_unscaled_font_t *unscaled_font)
-{
-    FT_Face face;
-    cairo_pdf_ft_font_t *font;
-    unsigned long size;
-    int i, j;
-
-    face = _cairo_ft_unscaled_font_lock_face (unscaled_font);
-
-    /* We currently only support freetype truetype fonts. */
-    size = 0;
-    if (!FT_IS_SFNT (face) ||
-	FT_Load_Sfnt_Table (face, TTAG_glyf, 0, NULL, &size) != 0)
-	return NULL;
-
-    font = malloc (sizeof (cairo_pdf_ft_font_t));
-    if (font == NULL)
-	return NULL;
-
-    font->base.unscaled_font = unscaled_font;
-    _cairo_unscaled_font_reference (unscaled_font);
-    font->base.backend = &cairo_pdf_ft_font_backend;
-    font->base.font_id = _cairo_pdf_document_new_object (document);
-
-    _cairo_array_init (&font->output, sizeof (char));
-    if (_cairo_array_grow_by (&font->output, 4096) != CAIRO_STATUS_SUCCESS)
-	goto fail1;
-
-    font->glyphs = calloc (face->num_glyphs + 1, sizeof (ft_subset_glyph_t));
-    if (font->glyphs == NULL)
-	goto fail2;
-
-    font->parent_to_subset = calloc (face->num_glyphs, sizeof (int));
-    if (font->parent_to_subset == NULL)
-	goto fail3;
-
-    font->base.num_glyphs = 1;
-    font->base.x_min = face->bbox.xMin;
-    font->base.y_min = face->bbox.yMin;
-    font->base.x_max = face->bbox.xMax;
-    font->base.y_max = face->bbox.yMax;
-    font->base.ascent = face->ascender;
-    font->base.descent = face->descender;
-    font->base.base_font = strdup (face->family_name);
-    if (font->base.base_font == NULL)
-	goto fail4;
-
-    for (i = 0, j = 0; font->base.base_font[j]; j++) {
-	if (font->base.base_font[j] == ' ')
-	    continue;
-	font->base.base_font[i++] = font->base.base_font[j];
-    }
-    font->base.base_font[i] = '\0';
-
-    font->base.widths = calloc (face->num_glyphs, sizeof (int));
-    if (font->base.widths == NULL)
-	goto fail5;
-
-    _cairo_ft_unscaled_font_unlock_face (unscaled_font);
-
-    font->status = CAIRO_STATUS_SUCCESS;
-
-    return &font->base;
-
- fail5:
-    free (font->base.base_font);
- fail4:
-    free (font->parent_to_subset);
- fail3:
-    free (font->glyphs);
- fail2:
-    _cairo_array_fini (&font->output);
- fail1:
-    free (font);
-    return NULL;
-}
-
-static void
-cairo_pdf_ft_font_destroy (void *abstract_font)
-{
-    cairo_pdf_ft_font_t *font = abstract_font;
-
-    if (font == NULL)
-	return;
-
-    _cairo_unscaled_font_destroy (font->base.unscaled_font);
-    free (font->base.base_font);
-    free (font->parent_to_subset);
-    free (font->glyphs);
-    _cairo_array_fini (&font->output);
-    free (font);
-}
-
-static void *
-cairo_pdf_ft_font_write (cairo_pdf_ft_font_t *font,
-			 const void *data, size_t length)
-{
-    void *p;
-
-    p = _cairo_array_append (&font->output, data, length);
-    if (p == NULL)
-	font->status = CAIRO_STATUS_NO_MEMORY;
-
-    return p;
-}
-
-static void
-cairo_pdf_ft_font_write_be16 (cairo_pdf_ft_font_t *font,
-			      unsigned short value)
-{
-    unsigned short be16_value;
-
-    be16_value = cpu_to_be16 (value);
-    cairo_pdf_ft_font_write (font, &be16_value, sizeof be16_value);
-}
-
-static void
-cairo_pdf_ft_font_write_be32 (cairo_pdf_ft_font_t *font, unsigned long value)
-{
-    unsigned long be32_value;
-
-    be32_value = cpu_to_be32 (value);
-    cairo_pdf_ft_font_write (font, &be32_value, sizeof be32_value);
-}
-
-static unsigned long
-cairo_pdf_ft_font_align_output (cairo_pdf_ft_font_t *font)
-{
-    int length, aligned;
-    static const char pad[4];
-
-    length = _cairo_array_num_elements (&font->output);
-    aligned = (length + 3) & ~3;
-    cairo_pdf_ft_font_write (font, pad, aligned - length);
-
-    return aligned;
-}
-
-static int
-cairo_pdf_ft_font_write_cmap_table (cairo_pdf_ft_font_t *font, unsigned long tag)
-{
-    int i;
-
-    cairo_pdf_ft_font_write_be16 (font, 0);
-    cairo_pdf_ft_font_write_be16 (font, 1);
-
-    cairo_pdf_ft_font_write_be16 (font, 1);
-    cairo_pdf_ft_font_write_be16 (font, 0);
-    cairo_pdf_ft_font_write_be32 (font, 12);
-
-    /* Output a format 6 encoding table. */
-
-    cairo_pdf_ft_font_write_be16 (font, 6);
-    cairo_pdf_ft_font_write_be16 (font, 10 + 2 * (font->base.num_glyphs - 1));
-    cairo_pdf_ft_font_write_be16 (font, 0);
-    cairo_pdf_ft_font_write_be16 (font, 1); /* First glyph */
-    cairo_pdf_ft_font_write_be16 (font, font->base.num_glyphs - 1);
-    for (i = 1; i < font->base.num_glyphs; i++)
-	cairo_pdf_ft_font_write_be16 (font, i);
-
-    return font->status;
-}
-
-static int
-cairo_pdf_ft_font_write_generic_table (cairo_pdf_ft_font_t *font,
-				       unsigned long tag)
-{
-    unsigned char *buffer;
-    unsigned long size;
-
-    size = 0;
-    FT_Load_Sfnt_Table (font->face, tag, 0, NULL, &size);
-    buffer = cairo_pdf_ft_font_write (font, NULL, size);
-    FT_Load_Sfnt_Table (font->face, tag, 0, buffer, &size);
-    
-    return 0;
-}
-
-static int
-cairo_pdf_ft_font_write_glyf_table (cairo_pdf_ft_font_t *font,
-				    unsigned long tag)
-{
-    unsigned long start_offset, index, size;
-    TT_Header *header;
-    unsigned long begin, end;
-    unsigned char *buffer;
-    int i;
-    union {
-	unsigned char *bytes;
-	unsigned short *short_offsets;
-	unsigned long *long_offsets;
-    } u;
-
-    header = FT_Get_Sfnt_Table (font->face, ft_sfnt_head);
-    if (header->Index_To_Loc_Format == 0)
-	size = sizeof (short) * (font->face->num_glyphs + 1);
-    else
-	size = sizeof (long) * (font->face->num_glyphs + 1);
-
-    u.bytes = malloc (size);
-    if (u.bytes == NULL) {
-	font->status = CAIRO_STATUS_NO_MEMORY;
-	return font->status;
-    }
-    FT_Load_Sfnt_Table (font->face, TTAG_loca, 0, u.bytes, &size);
-
-    start_offset = _cairo_array_num_elements (&font->output);
-    for (i = 0; i < font->base.num_glyphs; i++) {
-	index = font->glyphs[i].parent_index;
-	if (header->Index_To_Loc_Format == 0) {
-	    begin = be16_to_cpu (u.short_offsets[index]) * 2;
-	    end = be16_to_cpu (u.short_offsets[index + 1]) * 2;
-	} else {
-	    begin = be32_to_cpu (u.long_offsets[index]);
-	    end = be32_to_cpu (u.long_offsets[index + 1]);
-	}
-
-	size = end - begin;
-
-	font->glyphs[i].location =
-	    cairo_pdf_ft_font_align_output (font) - start_offset;
-	buffer = cairo_pdf_ft_font_write (font, NULL, size);
-	if (buffer == NULL)
-	    break;
-	FT_Load_Sfnt_Table (font->face, TTAG_glyf, begin, buffer, &size);
-	/* FIXME: remap composite glyphs */
-    }
-
-    font->glyphs[i].location =
-	cairo_pdf_ft_font_align_output (font) - start_offset;
-
-    free (u.bytes);
-
-    return font->status;
-}
-
-static int
-cairo_pdf_ft_font_write_head_table (cairo_pdf_ft_font_t *font,
-				    unsigned long tag)
-{
-    TT_Header *head;
-
-    head = FT_Get_Sfnt_Table (font->face, ft_sfnt_head);
-
-    cairo_pdf_ft_font_write_be32 (font, head->Table_Version);
-    cairo_pdf_ft_font_write_be32 (font, head->Font_Revision);
-
-    font->checksum_index = _cairo_array_num_elements (&font->output);
-    cairo_pdf_ft_font_write_be32 (font, 0);
-    cairo_pdf_ft_font_write_be32 (font, head->Magic_Number);
-
-    cairo_pdf_ft_font_write_be16 (font, head->Flags);
-    cairo_pdf_ft_font_write_be16 (font, head->Units_Per_EM);
-
-    cairo_pdf_ft_font_write_be32 (font, head->Created[0]);
-    cairo_pdf_ft_font_write_be32 (font, head->Created[1]);
-    cairo_pdf_ft_font_write_be32 (font, head->Modified[0]);
-    cairo_pdf_ft_font_write_be32 (font, head->Modified[1]);
-
-    cairo_pdf_ft_font_write_be16 (font, head->xMin);
-    cairo_pdf_ft_font_write_be16 (font, head->yMin);
-    cairo_pdf_ft_font_write_be16 (font, head->xMax);
-    cairo_pdf_ft_font_write_be16 (font, head->yMax);
-
-    cairo_pdf_ft_font_write_be16 (font, head->Mac_Style);
-    cairo_pdf_ft_font_write_be16 (font, head->Lowest_Rec_PPEM);
-
-    cairo_pdf_ft_font_write_be16 (font, head->Font_Direction);
-    cairo_pdf_ft_font_write_be16 (font, head->Index_To_Loc_Format);
-    cairo_pdf_ft_font_write_be16 (font, head->Glyph_Data_Format);
-
-    return font->status;
-}
-
-static int cairo_pdf_ft_font_write_hhea_table (cairo_pdf_ft_font_t *font, unsigned long tag)
-{
-    TT_HoriHeader *hhea;
-
-    hhea = FT_Get_Sfnt_Table (font->face, ft_sfnt_hhea);
-
-    cairo_pdf_ft_font_write_be32 (font, hhea->Version);
-    cairo_pdf_ft_font_write_be16 (font, hhea->Ascender);
-    cairo_pdf_ft_font_write_be16 (font, hhea->Descender);
-    cairo_pdf_ft_font_write_be16 (font, hhea->Line_Gap);
-
-    cairo_pdf_ft_font_write_be16 (font, hhea->advance_Width_Max);
-
-    cairo_pdf_ft_font_write_be16 (font, hhea->min_Left_Side_Bearing);
-    cairo_pdf_ft_font_write_be16 (font, hhea->min_Right_Side_Bearing);
-    cairo_pdf_ft_font_write_be16 (font, hhea->xMax_Extent);
-    cairo_pdf_ft_font_write_be16 (font, hhea->caret_Slope_Rise);
-    cairo_pdf_ft_font_write_be16 (font, hhea->caret_Slope_Run);
-    cairo_pdf_ft_font_write_be16 (font, hhea->caret_Offset);
-
-    cairo_pdf_ft_font_write_be16 (font, 0);
-    cairo_pdf_ft_font_write_be16 (font, 0);
-    cairo_pdf_ft_font_write_be16 (font, 0);
-    cairo_pdf_ft_font_write_be16 (font, 0);
-
-    cairo_pdf_ft_font_write_be16 (font, hhea->metric_Data_Format);
-    cairo_pdf_ft_font_write_be16 (font, font->base.num_glyphs);
-
-    return font->status;
-}
-
-static int
-cairo_pdf_ft_font_write_hmtx_table (cairo_pdf_ft_font_t *font,
-				    unsigned long tag)
-{
-    unsigned long entry_size;
-    short *p;
-    int i;
-
-    for (i = 0; i < font->base.num_glyphs; i++) {
-	entry_size = 2 * sizeof (short);
-	p = cairo_pdf_ft_font_write (font, NULL, entry_size);
-	FT_Load_Sfnt_Table (font->face, TTAG_hmtx, 
-			    font->glyphs[i].parent_index * entry_size,
-			    (FT_Byte *) p, &entry_size);
-	font->base.widths[i] = be16_to_cpu (p[0]);
-    }
-
-    return font->status;
-}
-
-static int
-cairo_pdf_ft_font_write_loca_table (cairo_pdf_ft_font_t *font,
-				    unsigned long tag)
-{
-    int i;
-    TT_Header *header;
-
-    header = FT_Get_Sfnt_Table (font->face, ft_sfnt_head);
-
-    if (header->Index_To_Loc_Format == 0) {
-	for (i = 0; i < font->base.num_glyphs + 1; i++)
-	    cairo_pdf_ft_font_write_be16 (font, font->glyphs[i].location / 2);
-    } else {
-	for (i = 0; i < font->base.num_glyphs + 1; i++)
-	    cairo_pdf_ft_font_write_be32 (font, font->glyphs[i].location);
-    }
-
-    return font->status;
-}
-
-static int
-cairo_pdf_ft_font_write_maxp_table (cairo_pdf_ft_font_t *font,
-				    unsigned long tag)
-{
-    TT_MaxProfile *maxp;
-
-    maxp = FT_Get_Sfnt_Table (font->face, ft_sfnt_maxp);
-    
-    cairo_pdf_ft_font_write_be32 (font, maxp->version);
-    cairo_pdf_ft_font_write_be16 (font, font->base.num_glyphs);
-    cairo_pdf_ft_font_write_be16 (font, maxp->maxPoints);
-    cairo_pdf_ft_font_write_be16 (font, maxp->maxContours);
-    cairo_pdf_ft_font_write_be16 (font, maxp->maxCompositePoints);
-    cairo_pdf_ft_font_write_be16 (font, maxp->maxCompositeContours);
-    cairo_pdf_ft_font_write_be16 (font, maxp->maxZones);
-    cairo_pdf_ft_font_write_be16 (font, maxp->maxTwilightPoints);
-    cairo_pdf_ft_font_write_be16 (font, maxp->maxStorage);
-    cairo_pdf_ft_font_write_be16 (font, maxp->maxFunctionDefs);
-    cairo_pdf_ft_font_write_be16 (font, maxp->maxInstructionDefs);
-    cairo_pdf_ft_font_write_be16 (font, maxp->maxStackElements);
-    cairo_pdf_ft_font_write_be16 (font, maxp->maxSizeOfInstructions);
-    cairo_pdf_ft_font_write_be16 (font, maxp->maxComponentElements);
-    cairo_pdf_ft_font_write_be16 (font, maxp->maxComponentDepth);
-
-    return font->status;
-}
-
-typedef struct table table_t;
-struct table {
-    unsigned long tag;
-    int (*write) (cairo_pdf_ft_font_t *font, unsigned long tag);
-};
-
-static const table_t truetype_tables[] = {
-    { TTAG_cmap, cairo_pdf_ft_font_write_cmap_table },
-    { TTAG_cvt,  cairo_pdf_ft_font_write_generic_table },
-    { TTAG_fpgm, cairo_pdf_ft_font_write_generic_table },
-    { TTAG_glyf, cairo_pdf_ft_font_write_glyf_table },
-    { TTAG_head, cairo_pdf_ft_font_write_head_table },
-    { TTAG_hhea, cairo_pdf_ft_font_write_hhea_table },
-    { TTAG_hmtx, cairo_pdf_ft_font_write_hmtx_table },
-    { TTAG_loca, cairo_pdf_ft_font_write_loca_table },
-    { TTAG_maxp, cairo_pdf_ft_font_write_maxp_table },
-    { TTAG_name, cairo_pdf_ft_font_write_generic_table },
-    { TTAG_prep, cairo_pdf_ft_font_write_generic_table },
-};
-
-static cairo_status_t
-cairo_pdf_ft_font_write_offset_table (cairo_pdf_ft_font_t *font)
-{
-    unsigned short search_range, entry_selector, range_shift;
-    int num_tables;
-
-    num_tables = ARRAY_LENGTH (truetype_tables);
-    search_range = 1;
-    entry_selector = 0;
-    while (search_range * 2 <= num_tables) {
-	search_range *= 2;
-	entry_selector++;
-    }
-    search_range *= 16;
-    range_shift = num_tables * 16 - search_range;
-
-    cairo_pdf_ft_font_write_be32 (font, SFNT_VERSION);
-    cairo_pdf_ft_font_write_be16 (font, num_tables);
-    cairo_pdf_ft_font_write_be16 (font, search_range);
-    cairo_pdf_ft_font_write_be16 (font, entry_selector);
-    cairo_pdf_ft_font_write_be16 (font, range_shift);
-
-    cairo_pdf_ft_font_write (font, NULL, ARRAY_LENGTH (truetype_tables) * 16);
-
-    return font->status;
-}    
-
-static unsigned long
-cairo_pdf_ft_font_calculate_checksum (cairo_pdf_ft_font_t *font,
-			   unsigned long start, unsigned long end)
-{
-    unsigned long *padded_end;
-    unsigned long *p;
-    unsigned long checksum;
-    char *data;
-
-    checksum = 0; 
-    data = _cairo_array_index (&font->output, 0);
-    p = (unsigned long *) (data + start);
-    padded_end = (unsigned long *) (data + ((end + 3) & ~3));
-    while (p < padded_end)
-	checksum += *p++;
-
-    return checksum;
-}
-
-static void
-cairo_pdf_ft_font_update_entry (cairo_pdf_ft_font_t *font, int index, unsigned long tag,
-			unsigned long start, unsigned long end)
-{
-    unsigned long *entry;
-
-    entry = _cairo_array_index (&font->output, 12 + 16 * index);
-    entry[0] = cpu_to_be32 (tag);
-    entry[1] = cpu_to_be32 (cairo_pdf_ft_font_calculate_checksum (font, start, end));
-    entry[2] = cpu_to_be32 (start);
-    entry[3] = cpu_to_be32 (end - start);
-}
-
-static cairo_status_t
-cairo_pdf_ft_font_generate (void *abstract_font,
-			    const char **data, unsigned long *length)
-{
-    cairo_pdf_ft_font_t *font = abstract_font;
-    unsigned long start, end, next, checksum;
-    unsigned long *checksum_location;
-    int i;
-
-    font->face = _cairo_ft_unscaled_font_lock_face (font->base.unscaled_font);
-
-    if (cairo_pdf_ft_font_write_offset_table (font))
-	goto fail;
-
-    start = cairo_pdf_ft_font_align_output (font);
-    end = start;
-
-    end = 0;
-    for (i = 0; i < ARRAY_LENGTH (truetype_tables); i++) {
-	if (truetype_tables[i].write (font, truetype_tables[i].tag))
-	    goto fail;
-
-	end = _cairo_array_num_elements (&font->output);
-	next = cairo_pdf_ft_font_align_output (font);
-	cairo_pdf_ft_font_update_entry (font, i, truetype_tables[i].tag,
-					start, end);
-	start = next;
-    }
-
-    checksum =
-	0xb1b0afba - cairo_pdf_ft_font_calculate_checksum (font, 0, end);
-    checksum_location = _cairo_array_index (&font->output, font->checksum_index);
-    *checksum_location = cpu_to_be32 (checksum);
-
-    *data = _cairo_array_index (&font->output, 0);
-    *length = _cairo_array_num_elements (&font->output);
-
- fail:
-    _cairo_ft_unscaled_font_unlock_face (font->base.unscaled_font);      
-    font->face = NULL;
-
-    return font->status;
-}
-
-static int
-cairo_pdf_ft_font_use_glyph (void *abstract_font, int glyph)
-{
-    cairo_pdf_ft_font_t *font = abstract_font;
-
-    if (font->parent_to_subset[glyph] == 0) {
-	font->parent_to_subset[glyph] = font->base.num_glyphs;
-	font->glyphs[font->base.num_glyphs].parent_index = glyph;
-	font->base.num_glyphs++;
-    }
-
-    return font->parent_to_subset[glyph];
-}
-
-static cairo_pdf_font_backend_t cairo_pdf_ft_font_backend = {
-    cairo_pdf_ft_font_use_glyph,
-    cairo_pdf_ft_font_generate,
-    cairo_pdf_ft_font_destroy
-};
-
-/* PDF Generation */
-
 static unsigned int
 _cairo_pdf_document_new_object (cairo_pdf_document_t *document)
 {
@@ -1851,14 +1225,15 @@ _cairo_pdf_surface_get_extents (void		  
     return CAIRO_STATUS_SUCCESS;
 }
 
-static cairo_pdf_font_t *
+static cairo_font_subset_t *
 _cairo_pdf_document_get_font (cairo_pdf_document_t	*document,
 			      cairo_scaled_font_t	*scaled_font)
 {
     cairo_unscaled_font_t *unscaled_font;
-    cairo_pdf_font_t *pdf_font;
+    cairo_font_subset_t *pdf_font;
     unsigned int num_fonts, i;
 
+    /* XXX Why is this an ft specific function? */
     unscaled_font = _cairo_ft_scaled_font_get_unscaled_font (scaled_font);
 
     num_fonts = _cairo_array_num_elements (&document->fonts);
@@ -1870,12 +1245,14 @@ _cairo_pdf_document_get_font (cairo_pdf_
 
     /* FIXME: Figure out here which font backend is in use and call
      * the appropriate constructor. */
-    pdf_font = cairo_pdf_ft_font_create (document, unscaled_font);
+    pdf_font = cairo_font_subset_create (unscaled_font);
     if (pdf_font == NULL)
 	return NULL;
 
+    pdf_font->font_id = _cairo_pdf_document_new_object (document);
+
     if (_cairo_array_append (&document->fonts, &pdf_font, 1) == NULL) {
-	cairo_pdf_font_destroy (pdf_font);
+	cairo_font_subset_destroy (pdf_font);
 	return NULL;
     }
 
@@ -1899,7 +1276,7 @@ _cairo_pdf_surface_show_glyphs (cairo_sc
     cairo_pdf_surface_t *surface = abstract_surface;
     cairo_pdf_document_t *document = surface->document;
     cairo_output_stream_t *output = document->output_stream;
-    cairo_pdf_font_t *pdf_font;
+    cairo_font_subset_t *pdf_font;
     int i, index;
 
     pdf_font = _cairo_pdf_document_get_font (document, scaled_font);
@@ -1912,7 +1289,7 @@ _cairo_pdf_surface_show_glyphs (cairo_sc
 				 "BT /res%u 1 Tf", pdf_font->font_id);
     for (i = 0; i < num_glyphs; i++) {
 
-	index = cairo_pdf_font_use_glyph (pdf_font, glyphs[i].index);
+	index = cairo_font_subset_use_glyph (pdf_font, glyphs[i].index);
 
 	_cairo_output_stream_printf (output,
 				     " %f %f %f %f %f %f Tm (\\%o) Tj",
@@ -2036,7 +1413,7 @@ _cairo_pdf_document_create (cairo_output
 
     document->pages_id = _cairo_pdf_document_new_object (document);
 
-    _cairo_array_init (&document->fonts, sizeof (cairo_pdf_font_t *));
+    _cairo_array_init (&document->fonts, sizeof (cairo_font_subset_t *));
 
     /* Document header */
     _cairo_output_stream_printf (output_stream,
@@ -2100,7 +1477,7 @@ static cairo_status_t
 _cairo_pdf_document_write_fonts (cairo_pdf_document_t *document)
 {
     cairo_output_stream_t *output = document->output_stream;
-    cairo_pdf_font_t *font;
+    cairo_font_subset_t *font;
     int num_fonts, i, j;
     const char *data;
     char *compressed;
@@ -2112,7 +1489,7 @@ _cairo_pdf_document_write_fonts (cairo_p
     for (i = 0; i < num_fonts; i++) {
 	_cairo_array_copy_element (&document->fonts, i, &font);
 
-	status = cairo_pdf_font_generate (font, &data, &data_size);
+	status = cairo_font_subset_generate (font, &data, &data_size);
 	if (status)
 	    goto fail;
 
@@ -2195,7 +1572,7 @@ _cairo_pdf_document_write_fonts (cairo_p
 				     "endobj\r\n");
 
     fail:
-	cairo_pdf_ft_font_destroy (font);
+	cairo_font_subset_destroy (font);
     }
 
     return status;
Index: src/cairo-ps-surface.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo-ps-surface.c,v
retrieving revision 1.37
diff -u -p -r1.37 cairo-ps-surface.c
--- src/cairo-ps-surface.c	15 Jun 2005 02:45:23 -0000	1.37
+++ src/cairo-ps-surface.c	17 Jun 2005 17:49:40 -0000
@@ -1,6 +1,7 @@
 /* cairo - a vector graphics library with display and print output
  *
  * Copyright © 2003 University of Southern California
+ * Copyright © 2005 Red Hat, Inc
  *
  * This library is free software; you can redistribute it and/or
  * modify it either under the terms of the GNU Lesser General Public
@@ -32,14 +33,27 @@
  *
  * Contributor(s):
  *	Carl D. Worth <cworth at cworth.org>
+ *	Kristian Høgsberg <krh at redhat.com>
  */
 
 #include "cairoint.h"
 #include "cairo-ps.h"
+#include "cairo-font-subset-private.h"
+#include "cairo-meta-surface-private.h"
+#include "cairo-ft-private.h"
 
 #include <time.h>
 #include <zlib.h>
 
+/* TODO:
+ *
+ * - Add document structure convention comments where appropriate.
+ *
+ * - Fix image compression.
+ *
+ * - Create a set of procs to use... specifically a trapezoid proc.
+ */
+
 static const cairo_surface_backend_t cairo_ps_surface_backend;
 
 typedef struct cairo_ps_surface {
@@ -48,29 +62,39 @@ typedef struct cairo_ps_surface {
     /* PS-specific fields */
     cairo_output_stream_t *stream;
 
-    double width_in_points;
-    double height_in_points;
+    double width;
+    double height;
     double x_dpi;
     double y_dpi;
 
-    int pages;
-
-    cairo_image_surface_t *image;
+    cairo_surface_t *current_page;
+    cairo_array_t pages;
+    cairo_array_t fonts;
 } cairo_ps_surface_t;
 
 #define PS_SURFACE_DPI_DEFAULT 300.0
 
-static void
-_cairo_ps_surface_erase (cairo_ps_surface_t *surface);
+static cairo_int_status_t
+_cairo_ps_surface_find_font_subsets (cairo_ps_surface_t *surface);
+
+static cairo_int_status_t
+_cairo_ps_surface_write_font_subsets (cairo_ps_surface_t *surface);
+
+static cairo_int_status_t
+_cairo_ps_surface_render_page (cairo_ps_surface_t *surface,
+			       cairo_surface_t *page, int page_number);
+
+static cairo_int_status_t
+_cairo_ps_surface_render_fallbacks (cairo_ps_surface_t *surface,
+				    cairo_surface_t    *page);
+
 
 static cairo_surface_t *
 _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream,
-					      double	    width_in_points,
-					      double	   height_in_points)
+					      double		     width,
+					      double		     height)
 {
     cairo_ps_surface_t *surface;
-    int width_in_pixels, height_in_pixels;
-    time_t now = time (0);
 
     surface = malloc (sizeof (cairo_ps_surface_t));
     if (surface == NULL)
@@ -80,45 +104,20 @@ _cairo_ps_surface_create_for_stream_inte
 
     surface->stream = stream;
 
-    surface->width_in_points  = width_in_points;
-    surface->height_in_points = height_in_points;
+    surface->width  = width;
+    surface->height = height;
     surface->x_dpi = PS_SURFACE_DPI_DEFAULT;
     surface->y_dpi = PS_SURFACE_DPI_DEFAULT;
 
-    surface->pages = 0;
-
-    width_in_pixels = (int) (surface->x_dpi * width_in_points / 72.0 + 1.0);
-    height_in_pixels = (int) (surface->y_dpi * height_in_points / 72.0 + 1.0);
-
-    surface->image = (cairo_image_surface_t *)
-	cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
-				    width_in_pixels, height_in_pixels);
-    if (surface->image == NULL) {
+    surface->current_page = _cairo_meta_surface_create (width,
+							height);
+    if (surface->current_page == NULL) {
 	free (surface);
 	return NULL;
     }
 
-    _cairo_ps_surface_erase (surface);
-
-    /* Document header */
-    _cairo_output_stream_printf (surface->stream,
-				 "%%!PS-Adobe-3.0\n"
-				 "%%%%Creator: Cairo (http://cairographics.org)\n");
-    _cairo_output_stream_printf (surface->stream,
-				 "%%%%CreationDate: %s",
-				 ctime (&now));
-    _cairo_output_stream_printf (surface->stream,
-				 "%%%%BoundingBox: %f %f %f %f\n",
-				 0.0, 0.0,
-				 surface->width_in_points,
-				 surface->height_in_points);
-    /* The "/FlateDecode filter" currently used is a feature of LanguageLevel 3 */
-    _cairo_output_stream_printf (surface->stream,
-				 "%%%%DocumentData: Clean7Bit\n"
-				 "%%%%LanguageLevel: 3\n");
-    _cairo_output_stream_printf (surface->stream,
-				 "%%%%Orientation: Portrait\n"
-				 "%%%%EndComments\n");
+    _cairo_array_init (&surface->pages, sizeof (cairo_surface_t *));
+    _cairo_array_init (&surface->fonts, sizeof (cairo_font_subset_t *));
 
     return &surface->base;
 }
@@ -169,210 +168,1183 @@ static cairo_status_t
 _cairo_ps_surface_finish (void *abstract_surface)
 {
     cairo_ps_surface_t *surface = abstract_surface;
+    cairo_surface_t *page;
+    cairo_status_t status;
+    int i;
+    time_t now;
+
+    now = time (0);
+
+    /* Document header */
+    _cairo_output_stream_printf (surface->stream,
+				 "%%!PS-Adobe-3.0\n"
+				 "%%%%Creator: Cairo (http://cairographics.org)\n"
+				 "%%%%CreationDate: %s"
+				 "%%%%Title: Some clever title\n"
+				 "%%%%Pages: %d\n"
+				 "%%%%BoundingBox: %f %f %f %f\n",
+				 ctime (&now),
+				 surface->pages.num_elements,
+				 0.0, 0.0, 
+				 surface->width,
+				 surface->height);
+
+    /* The "/FlateDecode filter" currently used is a feature of
+     * LanguageLevel 3 */
+    _cairo_output_stream_printf (surface->stream,
+				 "%%%%DocumentData: Clean7Bit\n"
+				 "%%%%LanguageLevel: 3\n"
+				 "%%%%Orientation: Portrait\n"
+				 "%%%%EndComments\n");
+
+    _cairo_ps_surface_find_font_subsets (surface);
+    _cairo_ps_surface_write_font_subsets (surface);
+
+    status = CAIRO_STATUS_SUCCESS;
+    for (i = 0; i < surface->pages.num_elements; i++) {
+	_cairo_array_copy_element (&surface->pages, i, &page);
+
+	status = _cairo_ps_surface_render_page (surface, page, i + 1);
+	if (status)
+	    break;
+    }
 
     /* Document footer */
     _cairo_output_stream_printf (surface->stream,
+				 "%%%%Trailer\n"
 				 "%%%%EOF\n");
 
-    cairo_surface_destroy (&surface->image->base);
+    for (i = 0; i < surface->pages.num_elements; i++) {
+	_cairo_array_copy_element (&surface->pages, i, &page);
+	cairo_surface_destroy (page);
+    }	
 
-    return CAIRO_STATUS_SUCCESS;
+    _cairo_output_stream_destroy (surface->stream);
+    cairo_surface_destroy (surface->current_page);
+
+    return status;
 }
 
-static void
-_cairo_ps_surface_erase (cairo_ps_surface_t *surface)
+static cairo_int_status_t
+_cairo_ps_surface_composite (cairo_operator_t	operator,
+			     cairo_pattern_t	*src_pattern,
+			     cairo_pattern_t	*mask_pattern,
+			     void		*abstract_dst,
+			     int		src_x,
+			     int		src_y,
+			     int		mask_x,
+			     int		mask_y,
+			     int		dst_x,
+			     int		dst_y,
+			     unsigned int	width,
+			     unsigned int	height)
 {
-    _cairo_surface_fill_rectangle (&surface->image->base,
-				   CAIRO_OPERATOR_SOURCE,
-				   CAIRO_COLOR_TRANSPARENT,
-				   0, 0,
-				   surface->image->width,
-				   surface->image->height);
+    cairo_ps_surface_t *surface = abstract_dst;
+
+    return _cairo_surface_composite (operator,
+				     src_pattern,
+				     mask_pattern,
+				     surface->current_page,
+				     src_x,
+				     src_y,
+				     mask_x,
+				     mask_y,
+				     dst_x,
+				     dst_y,
+				     width,
+				     height);
 }
 
-static cairo_status_t
-_cairo_ps_surface_acquire_source_image (void                    *abstract_surface,
-					cairo_image_surface_t  **image_out,
-					void                   **image_extra)
+static cairo_int_status_t
+_cairo_ps_surface_fill_rectangles (void			*abstract_surface,
+				   cairo_operator_t	operator,
+				   const cairo_color_t	*color,
+				   cairo_rectangle_t	*rects,
+				   int			num_rects)
 {
     cairo_ps_surface_t *surface = abstract_surface;
-    
-    *image_out = surface->image;
+
+    return _cairo_surface_fill_rectangles (surface->current_page,
+					   operator,
+					   color,
+					   rects,
+					   num_rects);
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_composite_trapezoids (cairo_operator_t	operator,
+					cairo_pattern_t		*pattern,
+					void			*abstract_dst,
+					int			x_src,
+					int			y_src,
+					int			x_dst,
+					int			y_dst,
+					unsigned int		width,
+					unsigned int		height,
+					cairo_trapezoid_t	*traps,
+					int			num_traps)
+{
+    cairo_ps_surface_t *surface = abstract_dst;
+
+    return _cairo_surface_composite_trapezoids (operator,
+						pattern,
+						surface->current_page,
+						x_src,
+						y_src,
+						x_dst,
+						y_dst,
+						width,
+						height,
+						traps,
+						num_traps);
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_copy_page (void *abstract_surface)
+{
+    cairo_ps_surface_t *surface = abstract_surface;
+
+    /* FIXME: We need to copy the meta surface contents here */
+
+    return _cairo_surface_show_page (&surface->base);
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_show_page (void *abstract_surface)
+{
+    cairo_ps_surface_t *surface = abstract_surface;
+
+    _cairo_array_append (&surface->pages, &surface->current_page, 1);
+    surface->current_page = _cairo_meta_surface_create (surface->width,
+							surface->height);
+    if (surface->current_page == NULL)
+	return CAIRO_STATUS_NO_MEMORY;
 
     return CAIRO_STATUS_SUCCESS;
 }
 
-static cairo_status_t
-_cairo_ps_surface_acquire_dest_image (void                    *abstract_surface,
-				      cairo_rectangle_t       *interest_rect,
-				      cairo_image_surface_t  **image_out,
-				      cairo_rectangle_t       *image_rect,
-				      void                   **image_extra)
+static cairo_int_status_t
+_cairo_ps_surface_intersect_clip_path (void		  *dst,
+				       cairo_path_fixed_t *path,
+				       cairo_fill_rule_t   fill_rule,
+				       double		   tolerance)
+{
+    cairo_ps_surface_t *surface = dst;
+
+    return _cairo_surface_intersect_clip_path (surface->current_page,
+					       path,
+					       fill_rule,
+					       tolerance);
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_get_extents (void		 *abstract_surface,
+			       cairo_rectangle_t *rectangle)
 {
     cairo_ps_surface_t *surface = abstract_surface;
-    
-    image_rect->x = 0;
-    image_rect->y = 0;
-    image_rect->width = surface->image->width;
-    image_rect->height = surface->image->height;
-    
-    *image_out = surface->image;
+
+    rectangle->x = 0;
+    rectangle->y = 0;
+
+    /* XXX: The conversion to integers here is pretty bogus, (not to
+     * mention the aribitray limitation of width to a short(!). We
+     * may need to come up with a better interface for get_size.
+     */
+    rectangle->width  = surface->width;
+    rectangle->height = surface->height;
 
     return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_int_status_t
-_cairo_ps_surface_copy_page (void *abstract_surface)
+_cairo_ps_surface_show_glyphs (cairo_scaled_font_t	*scaled_font,
+			       cairo_operator_t		operator,
+			       cairo_pattern_t		*pattern,
+			       void			*abstract_surface,
+			       int			source_x,
+			       int			source_y,
+			       int			dest_x,
+			       int			dest_y,
+			       unsigned int		width,
+			       unsigned int		height,
+			       const cairo_glyph_t	*glyphs,
+			       int			num_glyphs)
 {
-    cairo_status_t status = CAIRO_STATUS_SUCCESS;
     cairo_ps_surface_t *surface = abstract_surface;
-    int width = surface->image->width;
-    int height = surface->image->height;
-    cairo_output_stream_t *stream = surface->stream;
 
-    int i, x, y;
+    return _cairo_surface_show_glyphs (scaled_font,
+				       operator,
+				       pattern,
+				       surface->current_page,
+				       source_x,
+				       source_y,
+				       dest_x,
+				       dest_y,
+				       width,
+				       height,
+				       glyphs,
+				       num_glyphs);
+}
 
-    cairo_solid_pattern_t white_pattern;
-    unsigned char *rgb, *compressed;
-    unsigned long rgb_size, compressed_size;
+static cairo_int_status_t
+_cairo_ps_surface_fill_path (cairo_operator_t	operator,
+			     cairo_pattern_t	*pattern,
+			     void		*abstract_dst,
+			     cairo_path_fixed_t	*path,
+			     cairo_fill_rule_t   fill_rule,
+			     double		 tolerance)
+{
+    cairo_ps_surface_t *surface = abstract_dst;
 
-    rgb_size = 3 * width * height;
-    rgb = malloc (rgb_size);
-    if (rgb == NULL) {
-	status = CAIRO_STATUS_NO_MEMORY;
-	goto BAIL0;
+    return _cairo_surface_fill_path (operator,
+				     pattern,
+				     surface->current_page,
+				     path,
+				     fill_rule,
+				     tolerance);
+}
+
+static const cairo_surface_backend_t cairo_ps_surface_backend = {
+    _cairo_ps_surface_create_similar,
+    _cairo_ps_surface_finish,
+    NULL, /* acquire_source_image */
+    NULL, /* release_source_image */
+    NULL, /* acquire_dest_image */
+    NULL, /* release_dest_image */
+    NULL, /* clone_similar */
+    _cairo_ps_surface_composite,
+    _cairo_ps_surface_fill_rectangles,
+    _cairo_ps_surface_composite_trapezoids,
+    _cairo_ps_surface_copy_page,
+    _cairo_ps_surface_show_page,
+    NULL, /* set_clip_region */
+    _cairo_ps_surface_intersect_clip_path,
+    _cairo_ps_surface_get_extents,
+    _cairo_ps_surface_show_glyphs,
+    _cairo_ps_surface_fill_path
+};
+
+static cairo_font_subset_t *
+_cairo_ps_surface_get_font (cairo_ps_surface_t  *surface,
+			    cairo_scaled_font_t *scaled_font)
+{
+    cairo_unscaled_font_t *unscaled_font;
+    cairo_font_subset_t *subset;
+    unsigned int num_fonts, i;
+
+    /* XXX Why is this an ft specific function? */
+    unscaled_font = _cairo_ft_scaled_font_get_unscaled_font (scaled_font);
+
+    num_fonts = _cairo_array_num_elements (&surface->fonts);
+    for (i = 0; i < num_fonts; i++) {
+	_cairo_array_copy_element (&surface->fonts, i, &subset);
+	if (subset->unscaled_font == unscaled_font)
+	    return subset;
+    }
+
+    subset = cairo_font_subset_create (unscaled_font);
+    if (subset == NULL)
+	return NULL;
+
+    subset->font_id = surface->fonts.num_elements;
+    if (_cairo_array_append (&surface->fonts, &subset, 1) == NULL) {
+	cairo_font_subset_destroy (subset);
+	return NULL;
     }
 
-    compressed_size = (int) (1.0 + 1.1 * rgb_size);
-    compressed = malloc (compressed_size);
-    if (compressed == NULL) {
-	status = CAIRO_STATUS_NO_MEMORY;
-	goto BAIL1;
+    return subset;
+}
+
+static cairo_int_status_t
+_ps_find_subsets_show_glyphs (cairo_scaled_font_t *scaled_font,
+			      cairo_operator_t     operator,
+			      cairo_pattern_t     *pattern,
+			      void	          *abstract_surface,
+			      int	           source_x,
+			      int	           source_y,
+			      int	           dest_x,
+			      int	           dest_y,
+			      unsigned int         width,
+			      unsigned int         height,
+			      const cairo_glyph_t *glyphs,
+			      int		   num_glyphs)
+{
+    cairo_font_subset_t *subset;
+    int i;
+
+    subset = _cairo_ps_surface_get_font (abstract_surface, scaled_font);
+    if (subset == NULL)
+	return CAIRO_STATUS_NO_MEMORY;
+
+    for (i = 0; i < num_glyphs; i++)
+	cairo_font_subset_use_glyph (subset, glyphs[i].index);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t _ps_find_subsets_backend = {
+    NULL, /* create_similar */
+    NULL, /* finish */
+    NULL, /* acquire_source_image */
+    NULL, /* release_source_image */
+    NULL, /* acquire_dest_image */
+    NULL, /* release_dest_image */
+    NULL, /* clone_similar */
+    NULL, /* composite */
+    NULL, /* fill_rectangles */
+    NULL, /* composite_trapezoids */
+    NULL, /* copy_page */
+    NULL, /* show_page */
+    NULL, /* set_clip_region */
+    NULL, /* intersect_clip_path */
+    NULL, /* get_extents */
+    _ps_find_subsets_show_glyphs,
+    NULL, /* fill_path */
+};
+
+static cairo_int_status_t
+_cairo_ps_surface_find_font_subsets (cairo_ps_surface_t *surface)
+{
+    cairo_surface_t *page;
+    cairo_int_status_t status;
+    int i;
+
+    status = CAIRO_STATUS_SUCCESS;
+    for (i = 0; i < surface->pages.num_elements; i++) {
+	_cairo_array_copy_element (&surface->pages, i, &page);
+
+	status = _cairo_meta_surface_replay (page,
+					     &_ps_find_subsets_backend,
+					     surface);
+	if (status)
+	    break;
     }
 
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_write_type42_dict (cairo_ps_surface_t  *surface,
+				     cairo_font_subset_t *subset)
+{
+    const char *data;
+    unsigned long data_size;
+    cairo_status_t status;
+    int i;
+
+    status = CAIRO_STATUS_SUCCESS;
+
+    /* FIXME: Figure out document structure convention for fonts */
+
+    _cairo_output_stream_printf (surface->stream,
+				 "11 dict begin\n"
+				 "/FontType 42 def\n"
+				 "/FontName /f%d def\n"
+				 "/PaintType 0 def\n"
+				 "/FontMatrix [ 1 0 0 1 0 0 ] def\n"
+				 "/FontBBox [ 0 0 0 0 ] def\n"
+				 "/Encoding 256 array def\n"
+				 "0 1 255 { Encoding exch /.notdef put } for\n",
+				 subset->font_id);
+
+    /* FIXME: Figure out how subset->x_max etc maps to the /FontBBox */
+
+    for (i = 1; i < subset->num_glyphs; i++)
+	_cairo_output_stream_printf (surface->stream,
+				     "Encoding %d /g%d put\n", i, i);
+
+    _cairo_output_stream_printf (surface->stream,
+				 "/CharStrings %d dict dup begin\n"
+				 "/.notdef 0 def\n",
+				 subset->num_glyphs);
+
+    for (i = 1; i < subset->num_glyphs; i++)
+	_cairo_output_stream_printf (surface->stream,
+				     "/g%d %d def\n", i, i);
+
+    _cairo_output_stream_printf (surface->stream,
+				 "end readonly def\n");
+
+    status = cairo_font_subset_generate (subset, &data, &data_size);
+
+    /* FIXME: We need to break up fonts bigger than 64k so we don't
+     * exceed string size limitation.  At glyph boundaries.  Stupid
+     * postscript. */
+    _cairo_output_stream_printf (surface->stream,
+				 "/sfnts [<");
+
+    _cairo_output_stream_write_hex_string (surface->stream, data, data_size);
+
+    _cairo_output_stream_printf (surface->stream,
+				 ">] def\n"
+				 "FontName currentdict end definefont pop\n");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_write_font_subsets (cairo_ps_surface_t *surface)
+{
+    cairo_font_subset_t *subset;
+    int i;
+
+    for (i = 0; i < surface->fonts.num_elements; i++) {
+	_cairo_array_copy_element (&surface->fonts, i, &subset);
+	_cairo_ps_surface_write_type42_dict (surface, subset);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+color_is_gray (cairo_color_t *color)
+{
+    const double epsilon = 0.00001;
+
+    return (fabs (color->red - color->green) < epsilon &&
+	    fabs (color->red - color->blue) < epsilon);
+}
+
+/* PS Output - this section handles output of the parts of the meta
+ * surface we can render natively in PS. */
+
+static void *
+compress_dup (const void *data, unsigned long data_size,
+	      unsigned long *compressed_size)
+{
+    void *compressed;
+
+    /* Bound calculation taken from zlib. */
+    *compressed_size = data_size + (data_size >> 12) + (data_size >> 14) + 11;
+    compressed = malloc (*compressed_size);
+    if (compressed == NULL)
+	return NULL;
+
+    compress (compressed, compressed_size, data, data_size);
+
+    return compressed;
+}
+
+static cairo_status_t
+emit_image (cairo_ps_surface_t    *surface,
+	    cairo_image_surface_t *image,
+	    cairo_matrix_t	  *matrix)
+{
+    cairo_status_t status = CAIRO_STATUS_NO_MEMORY;
+    unsigned char *rgb, *compressed;
+    unsigned long rgb_size, compressed_size;
+    cairo_surface_t *opaque;
+    cairo_pattern_union_t pattern;
+    cairo_matrix_t d2i;
+    int x, y, i;
+
     /* PostScript can not represent the alpha channel, so we blend the
        current image over a white RGB surface to eliminate it. */
 
-    _cairo_pattern_init_solid (&white_pattern, CAIRO_COLOR_WHITE);
+    opaque = _cairo_surface_create_similar_solid (&image->base,
+						  CAIRO_FORMAT_RGB24,
+						  image->width,
+						  image->height, 
+						  CAIRO_COLOR_WHITE);
+    if (opaque == NULL)
+	goto bail0;
+
+    _cairo_pattern_init_for_surface (&pattern.surface, &image->base);
 
     _cairo_surface_composite (CAIRO_OPERATOR_DEST_OVER,
-			      &white_pattern.base,
+			      &pattern.base,
 			      NULL,
-			      &surface->image->base,
+			      opaque,
 			      0, 0,
 			      0, 0,
 			      0, 0,
-			      width, height);
-    
-    _cairo_pattern_fini (&white_pattern.base);
+			      image->width,
+			      image->height);
+
+    _cairo_pattern_fini (&pattern.base);
+
+    rgb_size = 3 * image->width * image->height;
+    rgb = malloc (rgb_size);
+    if (rgb == NULL)
+	goto bail1;
 
     i = 0;
-    for (y = 0; y < height; y++) {
-	pixman_bits_t *pixel = (pixman_bits_t *) (surface->image->data + y * surface->image->stride);
-	for (x = 0; x < width; x++, pixel++) {
+    for (y = 0; y < image->height; y++) {
+	pixman_bits_t *pixel = (pixman_bits_t *) (image->data + y * image->stride);
+	for (x = 0; x < image->width; x++, pixel++) {
 	    rgb[i++] = (*pixel & 0x00ff0000) >> 16;
 	    rgb[i++] = (*pixel & 0x0000ff00) >>  8;
 	    rgb[i++] = (*pixel & 0x000000ff) >>  0;
 	}
     }
 
-    compress (compressed, &compressed_size, rgb, rgb_size);
+    compressed = compress_dup (rgb, rgb_size, &compressed_size);
+    if (compressed == NULL)
+	goto bail2;
+
+    /* matrix transforms from user space to image space.  We need to
+     * transform from device space to image space to compensate for
+     * postscripts coordinate system. */
+    cairo_matrix_init (&d2i, 1, 0, 0, -1, 0, surface->height);
+    cairo_matrix_multiply (&d2i, &d2i, matrix);
 
-    /* Page header */
-    _cairo_output_stream_printf (stream, "%%%%Page: %d\n", ++surface->pages);
+    _cairo_output_stream_printf (surface->stream,
+				 "/DeviceRGB setcolorspace\n"
+				 "<<\n"
+				 "	/ImageType 1\n"
+				 "	/Width %d\n"
+				 "	/Height %d\n"
+				 "	/BitsPerComponent 8\n"
+				 "	/Decode [ 0 1 0 1 0 1 ]\n"
+				 "	/DataSource currentfile\n"
+				 "	/ImageMatrix [ %f %f %f %f %f %f ]\n"
+				 ">>\n"
+				 "image\n",
+				 image->width,
+				 image->height,
+				 d2i.xx, d2i.yx,
+				 d2i.xy, d2i.yy,
+				 d2i.x0, d2i.y0);
 
-    _cairo_output_stream_printf (stream, "gsave\n");
+    /* Compressed image data */
+    _cairo_output_stream_write (surface->stream, rgb, rgb_size);
+    status = CAIRO_STATUS_SUCCESS;
 
-    /* Image header goop */
-    _cairo_output_stream_printf (stream, "%f %f translate\n",
-				 0.0, surface->height_in_points);
-    _cairo_output_stream_printf (stream, "/DeviceRGB setcolorspace\n");
-    _cairo_output_stream_printf (stream, "<<\n");
-    _cairo_output_stream_printf (stream, "	/ImageType 1\n");
-    _cairo_output_stream_printf (stream, "	/Width %d\n", width);
-    _cairo_output_stream_printf (stream, "	/Height %d\n", height);
-    _cairo_output_stream_printf (stream, "	/BitsPerComponent 8\n");
-    _cairo_output_stream_printf (stream, "	/Decode [ 0 1 0 1 0 1 ]\n");
-    _cairo_output_stream_printf (stream, "	/DataSource currentfile /FlateDecode filter\n");
-    _cairo_output_stream_printf (stream, "	/ImageMatrix [ 1 0 0 -1 0 1 ]\n");
-    _cairo_output_stream_printf (stream, ">>\n");
-    _cairo_output_stream_printf (stream, "image\n");
+    _cairo_output_stream_printf (surface->stream,
+				 "\n");
 
-    /* Compressed image data */
-    _cairo_output_stream_write (stream, compressed, compressed_size);
+    free (compressed);
+ bail2:
+    free (rgb);
+ bail1:
+    cairo_surface_destroy (opaque);
+ bail0:
+    return status;
+}
 
-    _cairo_output_stream_printf (stream, "showpage\n");
+static void
+emit_solid_pattern (cairo_ps_surface_t *surface,
+		    cairo_solid_pattern_t *pattern)
+{
+    if (color_is_gray (&pattern->color))
+	_cairo_output_stream_printf (surface->stream,
+				     "%f setgray\n",
+				     pattern->color.red);
+    else
+	_cairo_output_stream_printf (surface->stream,
+				     "%f %f %f setrgbcolor\n",
+				     pattern->color.red,
+				     pattern->color.green,
+				     pattern->color.blue);
+}
 
-    _cairo_output_stream_printf (stream, "grestore\n");
+static void
+emit_surface_pattern (cairo_ps_surface_t *surface,
+		      cairo_surface_pattern_t *pattern)
+{
+}
 
-    /* Page footer */
-    _cairo_output_stream_printf (stream, "%%%%EndPage\n");
+static void
+emit_linear_pattern (cairo_ps_surface_t *surface,
+		     cairo_linear_pattern_t *pattern)
+{
+}
+
+static void
+emit_radial_pattern (cairo_ps_surface_t *surface,
+		     cairo_radial_pattern_t *pattern)
+{
+}
+
+static void
+emit_pattern (cairo_ps_surface_t *surface, cairo_pattern_t *pattern)
+{
+    /* FIXME: We should keep track of what pattern is currently set in
+     * the postscript file and only emit code if we're setting a
+     * different pattern. */
+
+    switch (pattern->type) {
+    case CAIRO_PATTERN_SOLID:	
+	emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern);
+	break;
+
+    case CAIRO_PATTERN_SURFACE:
+	emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern);
+	break;
+
+    case CAIRO_PATTERN_LINEAR:
+	emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern);
+	break;
+
+    case CAIRO_PATTERN_RADIAL:
+	emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern);
+	break;	    
+    }
+}
+
+
+static cairo_int_status_t
+_ps_output_composite (cairo_operator_t	operator,
+		      cairo_pattern_t  *src_pattern,
+		      cairo_pattern_t  *mask_pattern,
+		      void	       *abstract_dst,
+		      int		src_x,
+		      int		src_y,
+		      int		mask_x,
+		      int		mask_y,
+		      int		dst_x,
+		      int		dst_y,
+		      unsigned int	width,
+		      unsigned int	height)
+{
+    cairo_ps_surface_t *surface = abstract_dst;
+    cairo_surface_pattern_t *surface_pattern;
+    cairo_status_t status;
+    cairo_image_surface_t *image;
+    void *image_extra;
+
+    if (mask_pattern) {
+	/* FIXME: Investigate how this can be done... we'll probably
+	 * need pixmap fallbacks for this, though. */
+	_cairo_output_stream_printf (surface->stream,
+				     "%% _ps_output_composite: with mask\n");
+	return CAIRO_STATUS_SUCCESS;
+    }
+
+    status = CAIRO_STATUS_SUCCESS;
+    switch (src_pattern->type) {
+    case CAIRO_PATTERN_SOLID:
+	_cairo_output_stream_printf (surface->stream,
+				     "%% _ps_output_composite: solid\n");
+	break;
+
+    case CAIRO_PATTERN_SURFACE:
+	surface_pattern = (cairo_surface_pattern_t *) src_pattern;
+
+	if (src_pattern->extend != CAIRO_EXTEND_NONE) {
+	    _cairo_output_stream_printf (surface->stream,
+					 "%% _ps_output_composite: repeating image\n");
+	    break;
+	}
+	    
+
+	status = _cairo_surface_acquire_source_image (surface_pattern->surface,
+						      &image,
+						      &image_extra);
+	if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+	    _cairo_output_stream_printf (surface->stream,
+					 "%% _ps_output_composite: src_pattern not available as image\n");
+	    break;
+	} else if (status) {
+	    break;
+	}
+	status = emit_image (surface, image, &src_pattern->matrix);
+	_cairo_surface_release_source_image (surface_pattern->surface,
+					     image, image_extra);
+	break;
+
+    case CAIRO_PATTERN_LINEAR:
+    case CAIRO_PATTERN_RADIAL:
+	_cairo_output_stream_printf (surface->stream,
+				     "%% _ps_output_composite: gradient\n");
+	break;
+    }
 
-    free (compressed);
-    BAIL1:
-    free (rgb);
-    BAIL0:
     return status;
 }
 
 static cairo_int_status_t
-_cairo_ps_surface_show_page (void *abstract_surface)
+_ps_output_fill_rectangles (void		*abstract_surface,
+			    cairo_operator_t	 operator,
+			    const cairo_color_t	*color,
+			    cairo_rectangle_t	*rects,
+			    int			 num_rects)
 {
-    cairo_int_status_t status;
     cairo_ps_surface_t *surface = abstract_surface;
+    cairo_solid_pattern_t solid;
+    int i;
+
+    _cairo_output_stream_printf (surface->stream,
+				 "%% _ps_output_fill_rectangles\n");
 
-    status = _cairo_ps_surface_copy_page (surface);
-    if (status)
-	return status;
+    _cairo_pattern_init_solid (&solid, color);
+    emit_pattern (surface, &solid.base);
+    _cairo_pattern_fini (&solid.base);
+
+    _cairo_output_stream_printf (surface->stream, "[");
+    for (i = 0; i < num_rects; i++) {
+      _cairo_output_stream_printf (surface->stream,
+				   " %d %d %d %d",
+				   rects[i].x,
+				   surface->height - rects[i].y - rects[i].height,
+				   rects[i].width, rects[i].height);
+    }
 
-    _cairo_ps_surface_erase (surface);
+    _cairo_output_stream_printf (surface->stream, " ] rectfill\n");
 
     return CAIRO_STATUS_SUCCESS;
 }
 
+static double
+intersect (cairo_line_t *line, cairo_fixed_t y)
+{
+    return _cairo_fixed_to_double (line->p1.x) +
+	_cairo_fixed_to_double (line->p2.x - line->p1.x) *
+	_cairo_fixed_to_double (y - line->p1.y) /
+	_cairo_fixed_to_double (line->p2.y - line->p1.y);
+}
+
 static cairo_int_status_t
-_cairo_ps_surface_set_clip_region (void *abstract_surface,
-				   pixman_region16_t *region)
+_ps_output_composite_trapezoids (cairo_operator_t	operator,
+				 cairo_pattern_t	*pattern,
+				 void			*abstract_dst,
+				 int			x_src,
+				 int			y_src,
+				 int			x_dst,
+				 int			y_dst,
+				 unsigned int		width,
+				 unsigned int		height,
+				 cairo_trapezoid_t	*traps,
+				 int			num_traps)
 {
-    cairo_ps_surface_t *surface = abstract_surface;
+    cairo_ps_surface_t *surface = abstract_dst;
+    int i;
+
+    _cairo_output_stream_printf (surface->stream,
+				 "%% _ps_output_composite_trapezoids\n");
+
+    emit_pattern (surface, pattern);
+
+    for (i = 0; i < num_traps; i++) {
+	double left_x1, left_x2, right_x1, right_x2, top, bottom;
 
-    return _cairo_image_surface_set_clip_region (surface->image, region);
+	left_x1  = intersect (&traps[i].left, traps[i].top);
+	left_x2  = intersect (&traps[i].left, traps[i].bottom);
+	right_x1 = intersect (&traps[i].right, traps[i].top);
+	right_x2 = intersect (&traps[i].right, traps[i].bottom);
+	top      = surface->height - _cairo_fixed_to_double (traps[i].top);
+	bottom   = surface->height - _cairo_fixed_to_double (traps[i].bottom);
+
+	_cairo_output_stream_printf
+	    (surface->stream,
+	     "%f %f moveto %f %f lineto %f %f lineto %f %f lineto "
+	     "closepath\n",
+	     left_x1, top,
+	     left_x2, bottom,
+	     right_x2, bottom,
+	     right_x1, top);
+    }
+
+    _cairo_output_stream_printf (surface->stream,
+				 "fill\n");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+typedef struct
+{
+    double height;
+    cairo_output_stream_t *output_stream;
+    cairo_bool_t has_current_point;
+} ps_output_path_info_t;
+
+static cairo_status_t
+_ps_output_path_move_to (void *closure, cairo_point_t *point)
+{
+    ps_output_path_info_t *info = closure;
+
+    _cairo_output_stream_printf (info->output_stream,
+				 "%f %f moveto ",
+				 _cairo_fixed_to_double (point->x),
+				 info->height - _cairo_fixed_to_double (point->y));
+    info->has_current_point = TRUE;
+    
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_ps_output_path_line_to (void *closure, cairo_point_t *point)
+{
+    ps_output_path_info_t *info = closure;
+    const char *ps_operator;
+
+    if (info->has_current_point)
+	ps_operator = "lineto";
+    else
+	ps_operator = "moveto";
+    
+    _cairo_output_stream_printf (info->output_stream,
+				 "%f %f %s ",
+				 _cairo_fixed_to_double (point->x),
+				 info->height - _cairo_fixed_to_double (point->y),
+				 ps_operator);
+    info->has_current_point = TRUE;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_ps_output_path_curve_to (void          *closure,
+			  cairo_point_t *b,
+			  cairo_point_t *c,
+			  cairo_point_t *d)
+{
+    ps_output_path_info_t *info = closure;
+
+    _cairo_output_stream_printf (info->output_stream,
+				 "%f %f %f %f %f %f curveto ",
+				 _cairo_fixed_to_double (b->x),
+				 info->height - _cairo_fixed_to_double (b->y),
+				 _cairo_fixed_to_double (c->x),
+				 info->height - _cairo_fixed_to_double (c->y),
+				 _cairo_fixed_to_double (d->x),
+				 info->height - _cairo_fixed_to_double (d->y));
+    
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_ps_output_path_close_path (void *closure)
+{
+    ps_output_path_info_t *info = closure;
+    
+    _cairo_output_stream_printf (info->output_stream,
+				 "closepath\n");
+    info->has_current_point = FALSE;
+
+    return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_int_status_t
-_cairo_ps_surface_get_extents (void		 *abstract_surface,
-			       cairo_rectangle_t *rectangle)
+_ps_output_intersect_clip_path (void		   *dst,
+				cairo_path_fixed_t *path,
+				cairo_fill_rule_t   fill_rule,
+				double		    tolerance)
+{
+    cairo_ps_surface_t *surface = dst;
+    cairo_status_t status;
+    ps_output_path_info_t info;
+    const char *ps_operator;
+
+    _cairo_output_stream_printf (surface->stream,
+				 "%% _ps_output_intersect_clip_path\n");
+
+    if (path == NULL) {
+	_cairo_output_stream_printf (surface->stream, "initclip\n");
+	return CAIRO_STATUS_SUCCESS;
+    }
+
+    info.output_stream = surface->stream;
+    info.has_current_point = FALSE;
+    info.height = surface->height;
+
+    status = _cairo_path_fixed_interpret (path,
+					  CAIRO_DIRECTION_FORWARD,
+					  _ps_output_path_move_to,
+					  _ps_output_path_line_to,
+					  _ps_output_path_curve_to,
+					  _ps_output_path_close_path,
+					  &info);
+
+    switch (fill_rule) {
+    case CAIRO_FILL_RULE_WINDING:
+	ps_operator = "clip";
+	break;
+    case CAIRO_FILL_RULE_EVEN_ODD:
+	ps_operator = "eoclip";
+	break;
+    default:
+	ASSERT_NOT_REACHED;
+    }
+
+    _cairo_output_stream_printf (surface->stream,
+				 "%s newpath\n",
+				 ps_operator);
+
+    return status;
+}
+
+
+static cairo_int_status_t
+_ps_output_show_glyphs (cairo_scaled_font_t	*scaled_font,
+			cairo_operator_t	operator,
+			cairo_pattern_t		*pattern,
+			void			*abstract_surface,
+			int			source_x,
+			int			source_y,
+			int			dest_x,
+			int			dest_y,
+			unsigned int		width,
+			unsigned int		height,
+			const cairo_glyph_t	*glyphs,
+			int			num_glyphs)
 {
     cairo_ps_surface_t *surface = abstract_surface;
+    cairo_font_subset_t *subset;
+    int i, subset_index;
 
-    rectangle->x = 0;
-    rectangle->y = 0;
+    _cairo_output_stream_printf (surface->stream,
+				 "%% _ps_output_show_glyphs\n");
 
-    /* XXX: The conversion to integers here is pretty bogus, (not to
-     * mention the aribitray limitation of width to a short(!). We
-     * may need to come up with a better interface for get_size.
-     */
-    rectangle->width  = (surface->width_in_points + 0.5);
-    rectangle->height = (surface->height_in_points + 0.5);
+    emit_pattern (surface, pattern);
+
+    /* FIXME: Need to optimize this so we only do this sequence if the
+     * font isn't already set. */
+
+    subset = _cairo_ps_surface_get_font (abstract_surface, scaled_font);
+    _cairo_output_stream_printf (surface->stream,
+				 "/f%d findfont\n"
+				 "[ %f %f %f %f 0 0 ] makefont\n"
+				 "setfont\n",
+				 subset->font_id,
+				 scaled_font->scale.xx,
+				 scaled_font->scale.yx,
+				 scaled_font->scale.xy,
+				 scaled_font->scale.yy);
+
+    /* FIXME: Need to optimize per glyph code.  Should detect when
+     * glyphs share the same baseline and when the spacing corresponds
+     * to the glyph widths. */
+
+    for (i = 0; i < num_glyphs; i++) {
+	subset_index = cairo_font_subset_use_glyph (subset, glyphs[i].index);
+	_cairo_output_stream_printf (surface->stream,
+				     "%f %f moveto (\\%o) show\n",
+				     glyphs[i].x,
+				     surface->height - glyphs[i].y,
+				     subset_index);
+	
+    }
 
     return CAIRO_STATUS_SUCCESS;
 }
 
-static const cairo_surface_backend_t cairo_ps_surface_backend = {
-    _cairo_ps_surface_create_similar,
-    _cairo_ps_surface_finish,
-    _cairo_ps_surface_acquire_source_image,
+static cairo_int_status_t
+_ps_output_fill_path (cairo_operator_t	  operator,
+		      cairo_pattern_t	 *pattern,
+		      void		 *abstract_dst,
+		      cairo_path_fixed_t *path,
+		      cairo_fill_rule_t   fill_rule,
+		      double		  tolerance)
+{
+    cairo_ps_surface_t *surface = abstract_dst;
+    cairo_int_status_t status;
+    ps_output_path_info_t info;
+    const char *ps_operator;
+
+    _cairo_output_stream_printf (surface->stream,
+				 "%% _ps_output_fill_path\n");
+
+    emit_pattern (surface, pattern);
+
+    info.output_stream = surface->stream;
+    info.has_current_point = FALSE;
+    info.height = surface->height;
+
+    status = _cairo_path_fixed_interpret (path,
+					  CAIRO_DIRECTION_FORWARD,
+					  _ps_output_path_move_to,
+					  _ps_output_path_line_to,
+					  _ps_output_path_curve_to,
+					  _ps_output_path_close_path,
+					  &info);
+
+    switch (fill_rule) {
+    case CAIRO_FILL_RULE_WINDING:
+	ps_operator = "fill";
+	break;
+    case CAIRO_FILL_RULE_EVEN_ODD:
+	ps_operator = "eofill";
+	break;
+    default:
+	ASSERT_NOT_REACHED;
+    }
+
+    _cairo_output_stream_printf (surface->stream,
+				 "%s\n", ps_operator);
+
+    return status;
+}
+
+static const cairo_surface_backend_t ps_output_backend = {
+    NULL, /* create_similar */
+    NULL, /* finish */
+    NULL, /* acquire_source_image */
     NULL, /* release_source_image */
-    _cairo_ps_surface_acquire_dest_image,
+    NULL, /* acquire_dest_image */
     NULL, /* release_dest_image */
     NULL, /* clone_similar */
-    NULL, /* composite */
-    NULL, /* fill_rectangles */
-    NULL, /* composite_trapezoids */
-    _cairo_ps_surface_copy_page,
-    _cairo_ps_surface_show_page,
-    _cairo_ps_surface_set_clip_region,
+    _ps_output_composite,
+    _ps_output_fill_rectangles,
+    _ps_output_composite_trapezoids,
+    NULL, /* copy_page */
+    NULL, /* show_page */
+    NULL, /* set_clip_region */
+    _ps_output_intersect_clip_path,
+    NULL, /* get_extents */
+    _ps_output_show_glyphs,
+    _ps_output_fill_path
+};
+
+static cairo_int_status_t
+_cairo_ps_surface_render_page (cairo_ps_surface_t *surface,
+			       cairo_surface_t *page, int page_number)
+{
+    cairo_int_status_t status;
+
+    _cairo_output_stream_printf (surface->stream,
+				 "%%%%Page: %d\n"
+				 "gsave\n",
+				 page_number);
+
+    status = _cairo_meta_surface_replay (page,
+					 &ps_output_backend,
+					 surface);
+
+    _cairo_ps_surface_render_fallbacks (surface, page);
+
+    _cairo_output_stream_printf (surface->stream,
+				 "showpage\n"
+				 "grestore\n"
+				 "%%%%EndPage\n");
+
+    return status;
+}
+
+typedef struct _cairo_ps_fallback_area cairo_ps_fallback_area_t;
+struct _cairo_ps_fallback_area {
+    /* area */
+    cairo_ps_fallback_area_t *next;
+};
+
+typedef struct _cairo_ps_fallback_info cairo_ps_fallback_info_t;
+struct _cairo_ps_fallback_info {
+    cairo_ps_fallback_area_t *fallback_areas;
+};
+
+static cairo_int_status_t
+_cairo_ps_fallback_info_add_area (cairo_ps_fallback_info_t *info,
+				  int x, int y,
+				  unsigned int width,
+				  unsigned int height)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_pattern_is_translucent (cairo_pattern_t *abstract_pattern)
+{
+    cairo_pattern_union_t *pattern;
+
+    pattern = (cairo_pattern_union_t *) abstract_pattern;
+    switch (pattern->base.type) {
+    case CAIRO_PATTERN_SOLID:
+	return pattern->solid.color.alpha < 0.999;
+    case CAIRO_PATTERN_SURFACE:
+    case CAIRO_PATTERN_LINEAR:
+    case CAIRO_PATTERN_RADIAL:
+	return FALSE;
+    }	
+}
+
+static cairo_int_status_t
+_ps_locate_fallbacks_composite (cairo_operator_t  operator,
+				cairo_pattern_t  *src_pattern,
+				cairo_pattern_t  *mask_pattern,
+				void	         *abstract_dst,
+				int		  src_x,
+				int		  src_y,
+				int		  mask_x,
+				int		  mask_y,
+				int		  dst_x,
+				int		  dst_y,
+				unsigned int	  width,
+				unsigned int	  height)
+{
+}
+
+static cairo_int_status_t
+_ps_locate_fallbacks_fill_rectangles (void		  *abstract_surface,
+				      cairo_operator_t	   operator,
+				      const cairo_color_t *color,
+				      cairo_rectangle_t	  *rects,
+				      int		   num_rects)
+{
+}
+
+static cairo_int_status_t
+_ps_locate_fallbacks_composite_trapezoids (cairo_operator_t   operator,
+					   cairo_pattern_t   *pattern,
+					   void		     *abstract_dst,
+					   int		      x_src,
+					   int		      y_src,
+					   int		      x_dst,
+					   int		      y_dst,
+					   unsigned int	      width,
+					   unsigned int	      height,
+					   cairo_trapezoid_t *traps,
+					   int		      num_traps)
+{
+    cairo_ps_fallback_info_t *info;
+
+    info = abstract_dst;
+
+    if (_pattern_is_translucent (pattern))
+	_cairo_ps_fallback_info_add_area (info, x_dst, y_dst, width, height);
+}
+
+static cairo_int_status_t
+_ps_locate_fallbacks_show_glyphs (cairo_scaled_font_t *scaled_font,
+				  cairo_operator_t     operator,
+				  cairo_pattern_t     *pattern,
+				  void		      *abstract_surface,
+				  int		       source_x,
+				  int		       source_y,
+				  int		       dest_x,
+				  int		       dest_y,
+				  unsigned int	       width,
+				  unsigned int	       height,
+				  const cairo_glyph_t *glyphs,
+				  int		       num_glyphs)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_ps_locate_fallbacks_fill_path (cairo_operator_t    operator,
+				cairo_pattern_t	   *pattern,
+				void		   *abstract_dst,
+				cairo_path_fixed_t *path,
+				cairo_fill_rule_t   fill_rule,
+				double		    tolerance)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t ps_locate_fallbacks_backend = {
+    NULL, /* create_similar */
+    NULL, /* finish */
+    NULL, /* acquire_source_image */
+    NULL, /* release_source_image */
+    NULL, /* acquire_dest_image */
+    NULL, /* release_dest_image */
+    NULL, /* clone_similar */
+    _ps_locate_fallbacks_composite,
+    _ps_locate_fallbacks_fill_rectangles,
+    _ps_locate_fallbacks_composite_trapezoids,
+    NULL, /* copy_page */
+    NULL, /* show_page */
+    NULL, /* set_clip_region */
     NULL, /* intersect_clip_path */
-    _cairo_ps_surface_get_extents,
-    NULL /* show_glyphs */
+    NULL, /* get_extents */
+    _ps_locate_fallbacks_show_glyphs,
+    _ps_locate_fallbacks_fill_path
 };
+
+
+static cairo_int_status_t
+_cairo_ps_surface_render_fallbacks (cairo_ps_surface_t *surface,
+				    cairo_surface_t    *page)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
Index: src/cairo-surface.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo-surface.c,v
retrieving revision 1.75
diff -u -p -r1.75 cairo-surface.c
--- src/cairo-surface.c	15 Jun 2005 23:52:11 -0000	1.75
+++ src/cairo-surface.c	17 Jun 2005 17:49:40 -0000
@@ -893,6 +893,23 @@ _cairo_surface_set_clip_region (cairo_su
     return surface->backend->set_clip_region (surface, region);
 }
 
+cairo_private cairo_int_status_t
+_cairo_surface_intersect_clip_path (cairo_surface_t    *surface,
+				    cairo_path_fixed_t *path,
+				    cairo_fill_rule_t   fill_rule,
+				    double		tolerance)
+{
+    if (surface->finished)
+	return CAIRO_STATUS_SURFACE_FINISHED;
+    
+    assert (surface->backend->intersect_clip_path != NULL);
+
+    return surface->backend->intersect_clip_path (surface,
+						  path,
+						  fill_rule,
+						  tolerance);
+}
+
 static cairo_status_t
 _cairo_surface_set_clip_path_recursive (cairo_surface_t *surface,
 					cairo_clip_path_t *clip_path)
Index: src/cairoint.h
===================================================================
RCS file: /cvs/cairo/cairo/src/cairoint.h,v
retrieving revision 1.155
diff -u -p -r1.155 cairoint.h
--- src/cairoint.h	15 Jun 2005 02:45:23 -0000	1.155
+++ src/cairoint.h	17 Jun 2005 17:49:40 -0000
@@ -1525,6 +1525,12 @@ _cairo_surface_set_clip_region (cairo_su
 				pixman_region16_t   *region,
 				unsigned int	    serial);
 
+cairo_private cairo_int_status_t
+_cairo_surface_intersect_clip_path (cairo_surface_t    *surface,
+				    cairo_path_fixed_t *path,
+				    cairo_fill_rule_t   fill_rule,
+				    double		tolerance);
+
 typedef struct _cairo_clip_path cairo_clip_path_t;
 
 cairo_private cairo_status_t
@@ -1824,6 +1830,11 @@ cairo_private cairo_status_t
 _cairo_output_stream_write (cairo_output_stream_t *stream,
 			    const void *data, size_t length);
 
+cairo_private void
+_cairo_output_stream_write_hex_string (cairo_output_stream_t *stream,
+				       const char *data,
+				       size_t length);
+
 cairo_private cairo_status_t
 _cairo_output_stream_vprintf (cairo_output_stream_t *stream,
 			      const char *fmt, va_list ap);
Index: test/Makefile.am
===================================================================
RCS file: /cvs/cairo/cairo/test/Makefile.am,v
retrieving revision 1.58
diff -u -p -r1.58 Makefile.am
--- test/Makefile.am	16 Jun 2005 19:20:46 -0000	1.58
+++ test/Makefile.am	17 Jun 2005 17:49:40 -0000
@@ -33,7 +33,8 @@ transforms			\
 translate-show-surface		\
 trap-clip			\
 user-data			\
-rel-path
+rel-path			\
+ps-surface
 
 if CAIRO_HAS_PDF_SURFACE
 TESTS += pdf-surface pdf-clip
--- /dev/null	2005-06-17 06:38:10.873695992 -0400
+++ src/cairo-meta-surface.c	2005-06-17 12:12:23.000000000 -0400
@@ -0,0 +1,634 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *	Kristian Høgsberg <krh at redhat.com>
+ */
+
+#include "cairoint.h"
+#include "cairo-meta-surface-private.h"
+
+static const cairo_surface_backend_t cairo_meta_surface_backend;
+
+static void
+_meta_pattern_init_copy (cairo_pattern_t *dest, cairo_pattern_t *src)
+{
+    if (src)
+	_cairo_pattern_init_copy (dest, src);
+    else
+	dest->ref_count = 0;
+}
+
+static void
+_meta_pattern_fini (cairo_pattern_t *pattern)
+{
+    if (pattern->ref_count > 0)
+	_cairo_pattern_fini (pattern);
+}
+
+static cairo_pattern_t *
+_meta_pattern_get (cairo_pattern_t *pattern)
+{
+    if (pattern->ref_count > 0)
+	return pattern;
+    else
+	return NULL;
+}
+
+cairo_surface_t *
+_cairo_meta_surface_create (double width, double height)
+{
+    cairo_meta_surface_t *meta;
+
+    meta = malloc (sizeof (cairo_meta_surface_t));
+    if (meta == NULL)
+	return NULL;
+
+    meta->width = width;
+    meta->height = height;
+    _cairo_surface_init (&meta->base, &cairo_meta_surface_backend);
+    _cairo_array_init (&meta->commands, sizeof (cairo_command_t *));
+
+    return &meta->base;
+}
+
+static cairo_surface_t *
+_cairo_meta_surface_create_similar (void		*abstract_surface,
+				    cairo_format_t	format,
+				    int			width,
+				    int			height)
+{
+    return _cairo_meta_surface_create (width, height);
+}
+
+static cairo_status_t
+_cairo_meta_surface_finish (void *abstract_surface)
+{
+    cairo_meta_surface_t *meta = abstract_surface;
+
+    _cairo_meta_surface_clear (&meta->base);
+    _cairo_array_fini (&meta->commands);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_meta_surface_composite (cairo_operator_t	operator,
+			       cairo_pattern_t	*src_pattern,
+			       cairo_pattern_t	*mask_pattern,
+			       void		*abstract_surface,
+			       int		src_x,
+			       int		src_y,
+			       int		mask_x,
+			       int		mask_y,
+			       int		dst_x,
+			       int		dst_y,
+			       unsigned int	width,
+			       unsigned int	height)
+{
+    cairo_meta_surface_t *meta = abstract_surface;
+    cairo_command_composite_t *command;
+
+    command = malloc (sizeof (cairo_command_composite_t));
+    if (command == NULL)
+	return CAIRO_STATUS_NO_MEMORY;
+
+    command->type = CAIRO_COMMAND_COMPOSITE;
+    command->operator = operator;
+    _meta_pattern_init_copy (&command->src_pattern.base, src_pattern);
+    _meta_pattern_init_copy (&command->mask_pattern.base, mask_pattern);
+    command->src_x = src_x;
+    command->src_y = src_y;
+    command->mask_x = mask_x;
+    command->mask_y = mask_y;
+    command->dst_x = dst_x;
+    command->dst_y = dst_y;
+    command->width = width;
+    command->height = height;
+
+    if (_cairo_array_append (&meta->commands, &command, 1) == NULL) {
+	_meta_pattern_fini (&command->src_pattern.base);
+	_meta_pattern_fini (&command->mask_pattern.base);
+	free (command);
+	return CAIRO_STATUS_NO_MEMORY;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_meta_surface_fill_rectangles (void			*abstract_surface,
+				     cairo_operator_t		operator,
+				     const cairo_color_t	*color,
+				     cairo_rectangle_t		*rects,
+				     int			num_rects)
+{
+    cairo_meta_surface_t *meta = abstract_surface;
+    cairo_command_fill_rectangles_t *command;
+    cairo_rectangle_t *rects_copy;
+
+    rects_copy = malloc (sizeof (cairo_rectangle_t) * num_rects);
+    if (rects_copy == NULL)
+        return CAIRO_STATUS_NO_MEMORY;
+    memcpy (rects_copy, rects, sizeof (cairo_rectangle_t) * num_rects);
+
+    command = malloc (sizeof (cairo_command_fill_rectangles_t));
+    if (command == NULL) {
+	free (rects_copy);
+	return CAIRO_STATUS_NO_MEMORY;
+    }
+
+    command->type = CAIRO_COMMAND_FILL_RECTANGLES;
+    command->operator = operator;
+    command->color = *color;
+    command->rects = rects_copy;
+    command->num_rects = num_rects;
+
+    if (_cairo_array_append (&meta->commands, &command, 1) == NULL) {
+	free (command);
+	free (rects_copy);
+	return CAIRO_STATUS_NO_MEMORY;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_meta_surface_composite_trapezoids (cairo_operator_t	operator,
+					  cairo_pattern_t	*pattern,
+					  void			*abstract_surface,
+					  int			x_src,
+					  int			y_src,
+					  int			x_dst,
+					  int			y_dst,
+					  unsigned int		width,
+					  unsigned int		height,
+					  cairo_trapezoid_t	*traps,
+					  int			num_traps)
+{
+    cairo_meta_surface_t *meta = abstract_surface;
+    cairo_command_composite_trapezoids_t *command;
+    cairo_trapezoid_t *traps_copy;
+
+    traps_copy = malloc (sizeof (cairo_trapezoid_t) * num_traps);
+    if (traps_copy == NULL)
+        return CAIRO_STATUS_NO_MEMORY;
+    memcpy (traps_copy, traps, sizeof (cairo_trapezoid_t) * num_traps);
+
+    command = malloc (sizeof (cairo_command_composite_trapezoids_t));
+    if (command == NULL) {
+	free (traps_copy);
+	return CAIRO_STATUS_NO_MEMORY;
+    }
+
+    command->type = CAIRO_COMMAND_COMPOSITE_TRAPEZOIDS;
+    command->operator = operator;
+    _meta_pattern_init_copy (&command->pattern.base, pattern);
+    command->x_src = x_src;
+    command->y_src = y_src;
+    command->x_dst = x_dst;
+    command->y_dst = y_dst;
+    command->width = width;
+    command->height = height;
+    command->traps = traps_copy;
+    command->num_traps = num_traps;
+
+    if (_cairo_array_append (&meta->commands, &command, 1) == NULL) {
+	_meta_pattern_fini (&command->pattern.base);
+	free (command);
+	free (traps_copy);
+	return CAIRO_STATUS_NO_MEMORY;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_meta_surface_copy_page (void *abstract_surface)
+{
+    cairo_meta_surface_t *meta = abstract_surface;
+    static cairo_command_copy_page_t singleton = { CAIRO_COMMAND_COPY_PAGE };
+    cairo_command_copy_page_t *command = &singleton;
+
+    if (_cairo_array_append (&meta->commands, &command, 1) == NULL)
+	return CAIRO_STATUS_NO_MEMORY;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_meta_surface_show_page (void *abstract_surface)
+{
+    cairo_meta_surface_t *meta = abstract_surface;
+    static cairo_command_show_page_t singleton = { CAIRO_COMMAND_SHOW_PAGE };
+    cairo_command_show_page_t *command = &singleton;
+
+    if (_cairo_array_append (&meta->commands, &command, 1) == NULL)
+	return CAIRO_STATUS_NO_MEMORY;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_meta_surface_set_clip_region (void	       *abstract_surface,
+				     pixman_region16_t *region)
+{
+    cairo_meta_surface_t *meta = abstract_surface;
+    cairo_command_set_clip_region_t *command;
+
+    command = malloc (sizeof (cairo_command_set_clip_region_t));
+    if (command == NULL)
+	return CAIRO_STATUS_NO_MEMORY;
+
+    command->type = CAIRO_COMMAND_SET_CLIP_REGION;
+    command->region = region;
+
+    if (_cairo_array_append (&meta->commands, &command, 1) == NULL) {
+	free (command);
+	return CAIRO_STATUS_NO_MEMORY;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_meta_surface_intersect_clip_path (void		    *dst,
+					 cairo_path_fixed_t *path,
+					 cairo_fill_rule_t   fill_rule,
+					 double		     tolerance)
+{
+    cairo_meta_surface_t *meta = dst;
+    cairo_command_intersect_clip_path_t *command;
+    cairo_status_t status;
+
+    command = malloc (sizeof (cairo_command_intersect_clip_path_t));
+    if (command == NULL)
+	return CAIRO_STATUS_NO_MEMORY;
+
+    command->type = CAIRO_COMMAND_INTERSECT_CLIP_PATH;
+    printf ("meta path: %p\n", path);
+    if (path) {
+	status = _cairo_path_fixed_init_copy (&command->path, path);
+	if (status) {
+	    free (command);
+	    return status;
+	}
+	command->path_pointer = &command->path;
+    } else {
+	command->path_pointer = NULL;
+    }
+    command->fill_rule = fill_rule;
+    command->tolerance = tolerance;
+
+    if (_cairo_array_append (&meta->commands, &command, 1) == NULL) {
+	if (path)
+	    _cairo_path_fixed_fini (&command->path);
+	free (command);
+	return CAIRO_STATUS_NO_MEMORY;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_meta_surface_get_extents (void		   *abstract_surface,
+				 cairo_rectangle_t *rectangle)
+{
+    cairo_meta_surface_t *meta = abstract_surface;
+
+    /* Currently this is used for getting the extents of the surface
+     * before calling cairo_paint().  This is the only this that
+     * requires the meta surface to have an explicit size.  If paint
+     * was just a backend function, this would not be necessary. */
+
+    rectangle->x = 0;
+    rectangle->y = 0;
+    rectangle->width = meta->width;
+    rectangle->height = meta->height;
+    
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_meta_surface_show_glyphs (cairo_scaled_font_t	*scaled_font,
+				 cairo_operator_t	operator,
+				 cairo_pattern_t	*pattern,
+				 void			*abstract_surface,
+				 int			source_x,
+				 int			source_y,
+				 int			dest_x,
+				 int			dest_y,
+				 unsigned int		width,
+				 unsigned int		height,
+				 const cairo_glyph_t	*glyphs,
+				 int			num_glyphs)
+{
+    cairo_meta_surface_t *meta = abstract_surface;
+    cairo_command_show_glyphs_t *command;
+    cairo_glyph_t *glyphs_copy;
+
+    glyphs_copy = malloc (sizeof (cairo_glyph_t) * num_glyphs);
+    if (glyphs_copy == NULL)
+        return CAIRO_STATUS_NO_MEMORY;
+    memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
+
+    command = malloc (sizeof (cairo_command_show_glyphs_t));
+    if (command == NULL) {
+	free (glyphs_copy);
+	return CAIRO_STATUS_NO_MEMORY;
+    }
+
+    command->type = CAIRO_COMMAND_SHOW_GLYPHS;
+    command->scaled_font = scaled_font;
+    cairo_scaled_font_reference (scaled_font);
+    command->operator = operator;
+    _meta_pattern_init_copy (&command->pattern.base, pattern);
+    command->source_x = source_x;
+    command->source_y = source_y;
+    command->dest_x = dest_x;
+    command->dest_y = dest_y;
+    command->width = width;
+    command->height = height;
+    command->glyphs = glyphs_copy;
+    command->num_glyphs = num_glyphs;
+
+    if (_cairo_array_append (&meta->commands, &command, 1) == NULL) {
+	_meta_pattern_fini (&command->pattern.base);
+	free (command);
+	free (glyphs_copy);
+	return CAIRO_STATUS_NO_MEMORY;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_meta_surface_fill_path (cairo_operator_t	   operator,
+			       cairo_pattern_t	  *pattern,
+			       void		  *abstract_surface,
+			       cairo_path_fixed_t *path,
+			       cairo_fill_rule_t   fill_rule,
+			       double		   tolerance)
+{
+    cairo_meta_surface_t *meta = abstract_surface;
+    cairo_command_fill_path_t *command;
+    cairo_status_t status;
+
+    command = malloc (sizeof (cairo_command_fill_path_t));
+    if (command == NULL)
+	return CAIRO_STATUS_NO_MEMORY;
+
+    command->type = CAIRO_COMMAND_FILL_PATH;
+    command->operator = operator;
+    _meta_pattern_init_copy (&command->pattern.base, pattern);
+    status = _cairo_path_fixed_init_copy (&command->path, path);
+    if (status) {
+	_meta_pattern_fini (&command->pattern.base);
+	free (command);
+	return CAIRO_STATUS_NO_MEMORY;
+    }	
+    command->fill_rule = fill_rule;
+    command->tolerance = tolerance;
+
+    if (_cairo_array_append (&meta->commands, &command, 1) == NULL) {
+	_cairo_path_fixed_fini (&command->path);
+	_meta_pattern_fini (&command->pattern.base);
+	free (command);
+	return CAIRO_STATUS_NO_MEMORY;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t cairo_meta_surface_backend = {
+    _cairo_meta_surface_create_similar,
+    _cairo_meta_surface_finish,
+    NULL, /* acquire_source_image */
+    NULL, /* release_source_image */
+    NULL, /* acquire_dest_image */
+    NULL, /* release_dest_image */
+    NULL, /* clone_similar */
+    _cairo_meta_surface_composite,
+    _cairo_meta_surface_fill_rectangles,
+    _cairo_meta_surface_composite_trapezoids,
+    _cairo_meta_surface_copy_page,
+    _cairo_meta_surface_show_page,
+    _cairo_meta_surface_set_clip_region,
+    _cairo_meta_surface_intersect_clip_path,
+    _cairo_meta_surface_get_extents,
+    _cairo_meta_surface_show_glyphs,
+    _cairo_meta_surface_fill_path,
+};
+
+void
+_cairo_meta_surface_clear (cairo_surface_t *surface)
+{
+    cairo_meta_surface_t *meta;
+    cairo_command_t *command;
+    cairo_command_t **elements;
+    int i, num_elements;
+
+    meta = (cairo_meta_surface_t *) surface;
+
+    num_elements = meta->commands.num_elements;
+    elements = (cairo_command_t **) meta->commands.elements;
+    for (i = 0; i < num_elements; i++) {
+	command = elements[i];
+	switch (command->type) {
+	case CAIRO_COMMAND_COMPOSITE:
+	    _meta_pattern_fini (&command->composite.src_pattern.base);
+	    _meta_pattern_fini (&command->composite.mask_pattern.base);
+	    free (command);
+	    break;
+
+	case CAIRO_COMMAND_FILL_RECTANGLES:
+	    free (command->fill_rectangles.rects);
+	    free (command);
+	    break;
+
+	case CAIRO_COMMAND_COMPOSITE_TRAPEZOIDS:
+	    _meta_pattern_fini (&command->composite_trapezoids.pattern.base);
+	    free (command->composite_trapezoids.traps);
+	    free (command);
+	    break;
+
+	case CAIRO_COMMAND_SET_CLIP_REGION:
+	    free (command);
+	    break;
+
+	case CAIRO_COMMAND_INTERSECT_CLIP_PATH:
+	    if (command->intersect_clip_path.path_pointer)
+		_cairo_path_fixed_fini (&command->intersect_clip_path.path);
+	    free (command);
+	    break;
+
+	case CAIRO_COMMAND_SHOW_GLYPHS:
+	    cairo_scaled_font_destroy (command->show_glyphs.scaled_font);
+	    _meta_pattern_fini (&command->show_glyphs.pattern.base);
+	    free (command->show_glyphs.glyphs);
+	    free (command);
+	    break;
+
+	case CAIRO_COMMAND_FILL_PATH:
+	    _meta_pattern_fini (&command->fill_path.pattern.base);
+	    _cairo_path_fixed_fini (&command->fill_path.path);
+	    free (command);
+	    break;
+
+	default:
+	    assert (0);
+	}
+    }
+    _cairo_array_truncate (&meta->commands, 0);
+}
+
+cairo_int_status_t
+_cairo_meta_surface_replay (cairo_surface_t			*surface,
+			    const cairo_surface_backend_t	*backend,
+			    void				*closure)
+{
+    cairo_meta_surface_t *meta;
+    cairo_command_t *command, **elements;
+    int i, num_elements;
+    cairo_int_status_t status;
+
+    meta = (cairo_meta_surface_t *) surface;
+    status = CAIRO_STATUS_SUCCESS;
+
+    num_elements = meta->commands.num_elements;
+    elements = (cairo_command_t **) meta->commands.elements;
+    for (i = 0; i < num_elements; i++) {
+	command = elements[i];
+	switch (command->type) {
+	case CAIRO_COMMAND_COMPOSITE:
+	    if (backend->composite == NULL)
+		break;
+	    status = backend->composite
+		(command->composite.operator,
+		 _meta_pattern_get (&command->composite.src_pattern.base),
+		 _meta_pattern_get (&command->composite.mask_pattern.base),
+		 closure,
+		 command->composite.src_x,
+		 command->composite.src_y,
+		 command->composite.mask_x,
+		 command->composite.mask_y,
+		 command->composite.dst_x,
+		 command->composite.dst_y,
+		 command->composite.width,
+		 command->composite.height);
+	    break;
+
+	case CAIRO_COMMAND_FILL_RECTANGLES:
+	    if (backend->fill_rectangles == NULL)
+		break;
+	    status = backend->fill_rectangles
+		(closure,
+		 command->fill_rectangles.operator,
+		 &command->fill_rectangles.color,
+		 command->fill_rectangles.rects,
+		 command->fill_rectangles.num_rects);
+	    break;
+
+	case CAIRO_COMMAND_COMPOSITE_TRAPEZOIDS:
+	    if (backend->composite_trapezoids == NULL)
+		break;
+	    status = backend->composite_trapezoids
+		(command->composite_trapezoids.operator,
+		 _meta_pattern_get (&command->composite_trapezoids.pattern.base),
+		 closure,
+		 command->composite_trapezoids.x_src,
+		 command->composite_trapezoids.y_src,
+		 command->composite_trapezoids.x_dst,
+		 command->composite_trapezoids.y_dst,
+		 command->composite_trapezoids.width,
+		 command->composite_trapezoids.height,
+		 command->composite_trapezoids.traps,
+		 command->composite_trapezoids.num_traps);
+	    break;
+
+	case CAIRO_COMMAND_SET_CLIP_REGION:
+	    if (backend->set_clip_region == NULL)
+		break;
+	    status = backend->set_clip_region
+		(closure,
+		 command->set_clip_region.region);
+	    break;
+
+	case CAIRO_COMMAND_INTERSECT_CLIP_PATH:
+	    if (backend->intersect_clip_path == NULL)
+		break;
+	    status = backend->intersect_clip_path
+		(closure,
+		 command->intersect_clip_path.path_pointer,
+		 command->intersect_clip_path.fill_rule,
+		 command->intersect_clip_path.tolerance);
+	    break;
+
+	case CAIRO_COMMAND_SHOW_GLYPHS:
+	    if (backend->show_glyphs == NULL)
+		break;
+	    status = backend->show_glyphs
+		(command->show_glyphs.scaled_font,
+		 command->show_glyphs.operator,
+		 _meta_pattern_get (&command->show_glyphs.pattern.base),
+		 closure,
+		 command->show_glyphs.source_x,
+		 command->show_glyphs.source_y,
+		 command->show_glyphs.dest_x,
+		 command->show_glyphs.dest_y,
+		 command->show_glyphs.width,
+		 command->show_glyphs.height,
+		 command->show_glyphs.glyphs,
+		 command->show_glyphs.num_glyphs);
+	    break;
+
+	case CAIRO_COMMAND_FILL_PATH:
+	    if (backend->fill_path == NULL)
+		break;
+	    status = backend->fill_path
+		(command->fill_path.operator,
+		 _meta_pattern_get (&command->fill_path.pattern.base),
+		 closure,
+		 &command->fill_path.path,
+		 command->fill_path.fill_rule,
+		 command->fill_path.tolerance);
+	    break;
+
+	default:
+	    assert (0);
+	}
+
+	if (status)
+	    break;
+    }
+
+    return status;
+}
--- /dev/null	2005-06-17 06:38:10.873695992 -0400
+++ src/cairo-meta-surface-private.h	2005-06-15 15:26:23.000000000 -0400
@@ -0,0 +1,166 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *	Kristian Høgsberg <krh at redhat.com>
+ */
+
+#ifndef CAIRO_META_SURFACE_H
+#define CAIRO_META_SURFACE_H
+
+#include "cairoint.h"
+#include "cairo-path-fixed-private.h"
+
+typedef enum {
+    CAIRO_COMMAND_COMPOSITE,
+    CAIRO_COMMAND_FILL_RECTANGLES,
+    CAIRO_COMMAND_COMPOSITE_TRAPEZOIDS,
+    CAIRO_COMMAND_COPY_PAGE,
+    CAIRO_COMMAND_SHOW_PAGE,
+    CAIRO_COMMAND_SET_CLIP_REGION,
+    CAIRO_COMMAND_INTERSECT_CLIP_PATH,
+    CAIRO_COMMAND_SHOW_GLYPHS,
+    CAIRO_COMMAND_FILL_PATH
+} cairo_command_type_t;
+
+typedef struct _cairo_command_composite {
+    cairo_command_type_t	type;
+    cairo_operator_t		operator;
+    cairo_pattern_union_t	src_pattern;
+    cairo_pattern_union_t	mask_pattern;
+    int				src_x;
+    int				src_y;
+    int				mask_x;
+    int				mask_y;
+    int				dst_x;
+    int				dst_y;
+    unsigned int		width;
+    unsigned int		height;
+} cairo_command_composite_t;
+
+typedef struct _cairo_command_fill_rectangles {
+    cairo_command_type_t	type;
+    cairo_operator_t		operator;
+    cairo_color_t		color;
+    cairo_rectangle_t		*rects;
+    int				num_rects;
+} cairo_command_fill_rectangles_t;
+
+typedef struct _cairo_command_composite_trapezoids {
+    cairo_command_type_t	type;
+    cairo_operator_t		operator;
+    cairo_pattern_union_t	pattern;
+    int				x_src;
+    int				y_src;
+    int				x_dst;
+    int				y_dst;
+    unsigned int		width;
+    unsigned int		height;
+    cairo_trapezoid_t		*traps;
+    int				num_traps;
+} cairo_command_composite_trapezoids_t;
+
+typedef struct _cairo_command_copy_page {
+    cairo_command_type_t	type;
+} cairo_command_copy_page_t;
+
+typedef struct _cairo_command_show_page {
+    cairo_command_type_t	type;
+} cairo_command_show_page_t;
+
+typedef struct _cairo_command_set_clip_region {
+    cairo_command_type_t	type;
+    pixman_region16_t		*region;
+} cairo_command_set_clip_region_t;
+
+typedef struct _cairo_command_intersect_clip_path {
+    cairo_command_type_t	type;
+    cairo_path_fixed_t		*path_pointer;
+    cairo_path_fixed_t		path;
+    cairo_fill_rule_t		fill_rule;
+    double			tolerance;
+} cairo_command_intersect_clip_path_t;
+
+typedef struct _cairo_command_show_glyphs {
+    cairo_command_type_t	type;
+    cairo_scaled_font_t		*scaled_font;
+    cairo_operator_t		operator;
+    cairo_pattern_union_t	pattern;
+    int				source_x;
+    int				source_y;
+    int				dest_x;
+    int				dest_y;
+    unsigned int		width;
+    unsigned int		height;
+    cairo_glyph_t		*glyphs;
+    int				num_glyphs;
+} cairo_command_show_glyphs_t;
+
+typedef struct _cairo_command_fill_path {
+    cairo_command_type_t	type;
+    cairo_operator_t		operator;
+    cairo_pattern_union_t	pattern;
+    cairo_path_fixed_t		path;
+    cairo_fill_rule_t		fill_rule;
+    double			tolerance;
+} cairo_command_fill_path_t;
+
+typedef union _cairo_command {
+    cairo_command_type_t			type;
+    cairo_command_composite_t			composite;	
+    cairo_command_fill_rectangles_t		fill_rectangles;
+    cairo_command_composite_trapezoids_t	composite_trapezoids;
+    cairo_command_copy_page_t			copy_page;
+    cairo_command_show_page_t			show_page;
+    cairo_command_set_clip_region_t		set_clip_region;
+    cairo_command_intersect_clip_path_t		intersect_clip_path;
+    cairo_command_show_glyphs_t			show_glyphs;
+    cairo_command_fill_path_t			fill_path;
+} cairo_command_t;
+
+typedef struct _cairo_meta_surface {
+    cairo_surface_t base;
+    double width, height;
+    cairo_array_t commands;
+} cairo_meta_surface_t;
+
+cairo_surface_t *
+_cairo_meta_surface_create (double width, double height);
+
+void
+_cairo_meta_surface_clear (cairo_surface_t *surface);
+
+cairo_int_status_t
+_cairo_meta_surface_replay (cairo_surface_t			*surface,
+			    const cairo_surface_backend_t	*backend,
+			    void				*closure);
+
+#endif /* CAIRO_META_SURFACE_H */
--- /dev/null	2005-06-17 06:38:10.873695992 -0400
+++ src/cairo-font-subset.c	2005-06-07 15:38:36.000000000 -0400
@@ -0,0 +1,646 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *	Kristian Høgsberg <krh at redhat.com>
+ */
+
+#include "cairoint.h"
+#include "cairo-pdf.h"
+/* XXX: Eventually, we need to handle other font backends */
+#include "cairo-font-subset-private.h"
+#include "cairo-ft-private.h"
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_OUTLINE_H
+#include FT_TRUETYPE_TAGS_H
+#include FT_TRUETYPE_TABLES_H
+
+typedef struct ft_subset_glyph ft_subset_glyph_t;
+struct ft_subset_glyph {
+    int parent_index;
+    unsigned long location;
+};
+
+struct cairo_font_subset_backend {
+    int			(*use_glyph)	(void *abstract_font,
+					 int glyph);
+    cairo_status_t	(*generate)	(void *abstract_font,
+					 const char **data,
+					 unsigned long *length);
+    void		(*destroy)	(void *abstract_font);
+};
+
+typedef struct cairo_pdf_ft_font cairo_pdf_ft_font_t;
+struct cairo_pdf_ft_font {
+    cairo_font_subset_t base;
+    ft_subset_glyph_t *glyphs;
+    FT_Face face;
+    unsigned long *checksum_location;
+    cairo_array_t output;
+    int *parent_to_subset;
+    cairo_status_t status;
+};
+
+#define ARRAY_LENGTH(a) ( (sizeof (a)) / (sizeof ((a)[0])) )
+
+#define SFNT_VERSION			0x00010000
+
+#ifdef WORDS_BIGENDIAN
+
+#define cpu_to_be16(v) (v)
+#define be16_to_cpu(v) (v)
+#define cpu_to_be32(v) (v)
+#define be32_to_cpu(v) (v)
+
+#else
+
+static inline unsigned short
+cpu_to_be16(unsigned short v)
+{
+    return (v << 8) | (v >> 8);
+}
+
+static inline unsigned short
+be16_to_cpu(unsigned short v)
+{
+    return cpu_to_be16 (v);
+}
+
+static inline unsigned long
+cpu_to_be32(unsigned long v)
+{
+    return (cpu_to_be16 (v) << 16) | cpu_to_be16 (v >> 16);
+}
+
+static inline unsigned long
+be32_to_cpu(unsigned long v)
+{
+    return cpu_to_be32 (v);
+}
+
+#endif
+
+static cairo_font_subset_backend_t cairo_pdf_ft_font_backend;
+
+int
+cairo_font_subset_use_glyph (cairo_font_subset_t *font, int glyph)
+{
+    return font->backend->use_glyph (font, glyph);
+}
+
+cairo_status_t
+cairo_font_subset_generate (cairo_font_subset_t *font,
+			 const char **data, unsigned long *length)
+{
+    return font->backend->generate (font, data, length);
+}
+
+void
+cairo_font_subset_destroy (cairo_font_subset_t *font)
+{
+    font->backend->destroy (font);
+}
+
+cairo_font_subset_t *
+cairo_font_subset_create (cairo_unscaled_font_t *unscaled_font)
+{
+    FT_Face face;
+    cairo_pdf_ft_font_t *font;
+    unsigned long size;
+    int i, j;
+
+    face = _cairo_ft_unscaled_font_lock_face (unscaled_font);
+
+    /* We currently only support freetype truetype fonts. */
+    size = 0;
+    if (!FT_IS_SFNT (face) ||
+	FT_Load_Sfnt_Table (face, TTAG_glyf, 0, NULL, &size) != 0)
+	return NULL;
+
+    font = malloc (sizeof (cairo_pdf_ft_font_t));
+    if (font == NULL)
+	return NULL;
+
+    font->base.unscaled_font = unscaled_font;
+    _cairo_unscaled_font_reference (unscaled_font);
+    font->base.backend = &cairo_pdf_ft_font_backend;
+
+    _cairo_array_init (&font->output, sizeof (char));
+    if (_cairo_array_grow_by (&font->output, 4096) != CAIRO_STATUS_SUCCESS)
+	goto fail1;
+
+    font->glyphs = calloc (face->num_glyphs + 1, sizeof (ft_subset_glyph_t));
+    if (font->glyphs == NULL)
+	goto fail2;
+
+    font->parent_to_subset = calloc (face->num_glyphs, sizeof (int));
+    if (font->parent_to_subset == NULL)
+	goto fail3;
+
+    font->base.num_glyphs = 1;
+    font->base.x_min = face->bbox.xMin;
+    font->base.y_min = face->bbox.yMin;
+    font->base.x_max = face->bbox.xMax;
+    font->base.y_max = face->bbox.yMax;
+    font->base.ascent = face->ascender;
+    font->base.descent = face->descender;
+    font->base.base_font = strdup (face->family_name);
+    if (font->base.base_font == NULL)
+	goto fail4;
+
+    for (i = 0, j = 0; font->base.base_font[j]; j++) {
+	if (font->base.base_font[j] == ' ')
+	    continue;
+	font->base.base_font[i++] = font->base.base_font[j];
+    }
+    font->base.base_font[i] = '\0';
+
+    font->base.widths = calloc (face->num_glyphs, sizeof (int));
+    if (font->base.widths == NULL)
+	goto fail5;
+
+    _cairo_ft_unscaled_font_unlock_face (unscaled_font);
+
+    font->status = CAIRO_STATUS_SUCCESS;
+
+    return &font->base;
+
+ fail5:
+    free (font->base.base_font);
+ fail4:
+    free (font->parent_to_subset);
+ fail3:
+    free (font->glyphs);
+ fail2:
+    _cairo_array_fini (&font->output);
+ fail1:
+    free (font);
+    return NULL;
+}
+
+static void
+cairo_pdf_ft_font_destroy (void *abstract_font)
+{
+    cairo_pdf_ft_font_t *font = abstract_font;
+
+    _cairo_unscaled_font_destroy (font->base.unscaled_font);
+    free (font->base.base_font);
+    free (font->parent_to_subset);
+    free (font->glyphs);
+    _cairo_array_fini (&font->output);
+    free (font);
+}
+
+static void *
+cairo_pdf_ft_font_write (cairo_pdf_ft_font_t *font,
+			 const void *data, size_t length)
+{
+    void *p;
+
+    p = _cairo_array_append (&font->output, data, length);
+    if (p == NULL)
+	font->status = CAIRO_STATUS_NO_MEMORY;
+
+    return p;
+}
+
+static void
+cairo_pdf_ft_font_write_be16 (cairo_pdf_ft_font_t *font,
+			      unsigned short value)
+{
+    unsigned short be16_value;
+
+    be16_value = cpu_to_be16 (value);
+    cairo_pdf_ft_font_write (font, &be16_value, sizeof be16_value);
+}
+
+static void
+cairo_pdf_ft_font_write_be32 (cairo_pdf_ft_font_t *font, unsigned long value)
+{
+    unsigned long be32_value;
+
+    be32_value = cpu_to_be32 (value);
+    cairo_pdf_ft_font_write (font, &be32_value, sizeof be32_value);
+}
+
+static unsigned long
+cairo_pdf_ft_font_align_output (cairo_pdf_ft_font_t *font)
+{
+    int length, aligned;
+    static const char pad[4];
+
+    length = _cairo_array_num_elements (&font->output);
+    aligned = (length + 3) & ~3;
+    cairo_pdf_ft_font_write (font, pad, aligned - length);
+
+    return aligned;
+}
+
+static int
+cairo_pdf_ft_font_write_cmap_table (cairo_pdf_ft_font_t *font, unsigned long tag)
+{
+    int i;
+
+    cairo_pdf_ft_font_write_be16 (font, 0);
+    cairo_pdf_ft_font_write_be16 (font, 1);
+
+    cairo_pdf_ft_font_write_be16 (font, 1);
+    cairo_pdf_ft_font_write_be16 (font, 0);
+    cairo_pdf_ft_font_write_be32 (font, 12);
+
+    /* Output a format 6 encoding table. */
+
+    cairo_pdf_ft_font_write_be16 (font, 6);
+    cairo_pdf_ft_font_write_be16 (font, 10 + 2 * (font->base.num_glyphs - 1));
+    cairo_pdf_ft_font_write_be16 (font, 0);
+    cairo_pdf_ft_font_write_be16 (font, 1); /* First glyph */
+    cairo_pdf_ft_font_write_be16 (font, font->base.num_glyphs - 1);
+    for (i = 1; i < font->base.num_glyphs; i++)
+	cairo_pdf_ft_font_write_be16 (font, i);
+
+    return font->status;
+}
+
+static int
+cairo_pdf_ft_font_write_generic_table (cairo_pdf_ft_font_t *font,
+				       unsigned long tag)
+{
+    unsigned char *buffer;
+    unsigned long size;
+
+    size = 0;
+    FT_Load_Sfnt_Table (font->face, tag, 0, NULL, &size);
+    buffer = cairo_pdf_ft_font_write (font, NULL, size);
+    FT_Load_Sfnt_Table (font->face, tag, 0, buffer, &size);
+    
+    return 0;
+}
+
+static int
+cairo_pdf_ft_font_write_glyf_table (cairo_pdf_ft_font_t *font,
+				    unsigned long tag)
+{
+    unsigned long start_offset, index, size;
+    TT_Header *header;
+    unsigned long begin, end;
+    unsigned char *buffer;
+    int i;
+    union {
+	unsigned char *bytes;
+	unsigned short *short_offsets;
+	unsigned long *long_offsets;
+    } u;
+
+    header = FT_Get_Sfnt_Table (font->face, ft_sfnt_head);
+    if (header->Index_To_Loc_Format == 0)
+	size = sizeof (short) * (font->face->num_glyphs + 1);
+    else
+	size = sizeof (long) * (font->face->num_glyphs + 1);
+
+    u.bytes = malloc (size);
+    if (u.bytes == NULL) {
+	font->status = CAIRO_STATUS_NO_MEMORY;
+	return font->status;
+    }
+    FT_Load_Sfnt_Table (font->face, TTAG_loca, 0, u.bytes, &size);
+
+    start_offset = _cairo_array_num_elements (&font->output);
+    for (i = 0; i < font->base.num_glyphs; i++) {
+	index = font->glyphs[i].parent_index;
+	if (header->Index_To_Loc_Format == 0) {
+	    begin = be16_to_cpu (u.short_offsets[index]) * 2;
+	    end = be16_to_cpu (u.short_offsets[index + 1]) * 2;
+	}
+	else {
+	    begin = be32_to_cpu (u.long_offsets[index]);
+	    end = be32_to_cpu (u.long_offsets[index + 1]);
+	}
+
+	size = end - begin;
+
+	font->glyphs[i].location =
+	    cairo_pdf_ft_font_align_output (font) - start_offset;
+	buffer = cairo_pdf_ft_font_write (font, NULL, size);
+	if (buffer == NULL)
+	    break;
+	FT_Load_Sfnt_Table (font->face, TTAG_glyf, begin, buffer, &size);
+	/* FIXME: remap composite glyphs */
+    }
+
+    font->glyphs[i].location =
+	cairo_pdf_ft_font_align_output (font) - start_offset;
+
+    free (u.bytes);
+
+    return font->status;
+}
+
+static int
+cairo_pdf_ft_font_write_head_table (cairo_pdf_ft_font_t *font,
+				    unsigned long tag)
+{
+    TT_Header *head;
+
+    head = FT_Get_Sfnt_Table (font->face, ft_sfnt_head);
+
+    cairo_pdf_ft_font_write_be32 (font, head->Table_Version);
+    cairo_pdf_ft_font_write_be32 (font, head->Font_Revision);
+
+    font->checksum_location =
+	(unsigned long *) _cairo_array_index (&font->output, 0) +
+	_cairo_array_num_elements (&font->output) / sizeof (long);
+    cairo_pdf_ft_font_write_be32 (font, 0);
+    cairo_pdf_ft_font_write_be32 (font, head->Magic_Number);
+
+    cairo_pdf_ft_font_write_be16 (font, head->Flags);
+    cairo_pdf_ft_font_write_be16 (font, head->Units_Per_EM);
+
+    cairo_pdf_ft_font_write_be32 (font, head->Created[0]);
+    cairo_pdf_ft_font_write_be32 (font, head->Created[1]);
+    cairo_pdf_ft_font_write_be32 (font, head->Modified[0]);
+    cairo_pdf_ft_font_write_be32 (font, head->Modified[1]);
+
+    cairo_pdf_ft_font_write_be16 (font, head->xMin);
+    cairo_pdf_ft_font_write_be16 (font, head->yMin);
+    cairo_pdf_ft_font_write_be16 (font, head->xMax);
+    cairo_pdf_ft_font_write_be16 (font, head->yMax);
+
+    cairo_pdf_ft_font_write_be16 (font, head->Mac_Style);
+    cairo_pdf_ft_font_write_be16 (font, head->Lowest_Rec_PPEM);
+
+    cairo_pdf_ft_font_write_be16 (font, head->Font_Direction);
+    cairo_pdf_ft_font_write_be16 (font, head->Index_To_Loc_Format);
+    cairo_pdf_ft_font_write_be16 (font, head->Glyph_Data_Format);
+
+    return font->status;
+}
+
+static int cairo_pdf_ft_font_write_hhea_table (cairo_pdf_ft_font_t *font, unsigned long tag)
+{
+    TT_HoriHeader *hhea;
+
+    hhea = FT_Get_Sfnt_Table (font->face, ft_sfnt_hhea);
+
+    cairo_pdf_ft_font_write_be32 (font, hhea->Version);
+    cairo_pdf_ft_font_write_be16 (font, hhea->Ascender);
+    cairo_pdf_ft_font_write_be16 (font, hhea->Descender);
+    cairo_pdf_ft_font_write_be16 (font, hhea->Line_Gap);
+
+    cairo_pdf_ft_font_write_be16 (font, hhea->advance_Width_Max);
+
+    cairo_pdf_ft_font_write_be16 (font, hhea->min_Left_Side_Bearing);
+    cairo_pdf_ft_font_write_be16 (font, hhea->min_Right_Side_Bearing);
+    cairo_pdf_ft_font_write_be16 (font, hhea->xMax_Extent);
+    cairo_pdf_ft_font_write_be16 (font, hhea->caret_Slope_Rise);
+    cairo_pdf_ft_font_write_be16 (font, hhea->caret_Slope_Run);
+    cairo_pdf_ft_font_write_be16 (font, hhea->caret_Offset);
+
+    cairo_pdf_ft_font_write_be16 (font, 0);
+    cairo_pdf_ft_font_write_be16 (font, 0);
+    cairo_pdf_ft_font_write_be16 (font, 0);
+    cairo_pdf_ft_font_write_be16 (font, 0);
+
+    cairo_pdf_ft_font_write_be16 (font, hhea->metric_Data_Format);
+    cairo_pdf_ft_font_write_be16 (font, font->base.num_glyphs);
+
+    return font->status;
+}
+
+static int
+cairo_pdf_ft_font_write_hmtx_table (cairo_pdf_ft_font_t *font,
+				    unsigned long tag)
+{
+    unsigned long entry_size;
+    short *p;
+    int i;
+
+    for (i = 0; i < font->base.num_glyphs; i++) {
+	entry_size = 2 * sizeof (short);
+	p = cairo_pdf_ft_font_write (font, NULL, entry_size);
+	FT_Load_Sfnt_Table (font->face, TTAG_hmtx, 
+			    font->glyphs[i].parent_index * entry_size,
+			    (FT_Byte *) p, &entry_size);
+	font->base.widths[i] = be16_to_cpu (p[0]);
+    }
+
+    return font->status;
+}
+
+static int
+cairo_pdf_ft_font_write_loca_table (cairo_pdf_ft_font_t *font,
+				    unsigned long tag)
+{
+    int i;
+    TT_Header *header;
+
+    header = FT_Get_Sfnt_Table (font->face, ft_sfnt_head);
+
+    if (header->Index_To_Loc_Format == 0) {
+	for (i = 0; i < font->base.num_glyphs + 1; i++)
+	    cairo_pdf_ft_font_write_be16 (font, font->glyphs[i].location / 2);
+    }
+    else {
+	for (i = 0; i < font->base.num_glyphs + 1; i++)
+	    cairo_pdf_ft_font_write_be32 (font, font->glyphs[i].location);
+    }
+
+    return font->status;
+}
+
+static int
+cairo_pdf_ft_font_write_maxp_table (cairo_pdf_ft_font_t *font,
+				    unsigned long tag)
+{
+    TT_MaxProfile *maxp;
+
+    maxp = FT_Get_Sfnt_Table (font->face, ft_sfnt_maxp);
+    
+    cairo_pdf_ft_font_write_be32 (font, maxp->version);
+    cairo_pdf_ft_font_write_be16 (font, font->base.num_glyphs);
+    cairo_pdf_ft_font_write_be16 (font, maxp->maxPoints);
+    cairo_pdf_ft_font_write_be16 (font, maxp->maxContours);
+    cairo_pdf_ft_font_write_be16 (font, maxp->maxCompositePoints);
+    cairo_pdf_ft_font_write_be16 (font, maxp->maxCompositeContours);
+    cairo_pdf_ft_font_write_be16 (font, maxp->maxZones);
+    cairo_pdf_ft_font_write_be16 (font, maxp->maxTwilightPoints);
+    cairo_pdf_ft_font_write_be16 (font, maxp->maxStorage);
+    cairo_pdf_ft_font_write_be16 (font, maxp->maxFunctionDefs);
+    cairo_pdf_ft_font_write_be16 (font, maxp->maxInstructionDefs);
+    cairo_pdf_ft_font_write_be16 (font, maxp->maxStackElements);
+    cairo_pdf_ft_font_write_be16 (font, maxp->maxSizeOfInstructions);
+    cairo_pdf_ft_font_write_be16 (font, maxp->maxComponentElements);
+    cairo_pdf_ft_font_write_be16 (font, maxp->maxComponentDepth);
+
+    return font->status;
+}
+
+typedef struct table table_t;
+struct table {
+    unsigned long tag;
+    int (*write) (cairo_pdf_ft_font_t *font, unsigned long tag);
+};
+
+static const table_t truetype_tables[] = {
+    { TTAG_cmap, cairo_pdf_ft_font_write_cmap_table },
+    { TTAG_cvt,  cairo_pdf_ft_font_write_generic_table },
+    { TTAG_fpgm, cairo_pdf_ft_font_write_generic_table },
+    { TTAG_glyf, cairo_pdf_ft_font_write_glyf_table },
+    { TTAG_head, cairo_pdf_ft_font_write_head_table },
+    { TTAG_hhea, cairo_pdf_ft_font_write_hhea_table },
+    { TTAG_hmtx, cairo_pdf_ft_font_write_hmtx_table },
+    { TTAG_loca, cairo_pdf_ft_font_write_loca_table },
+    { TTAG_maxp, cairo_pdf_ft_font_write_maxp_table },
+    { TTAG_name, cairo_pdf_ft_font_write_generic_table },
+    { TTAG_prep, cairo_pdf_ft_font_write_generic_table },
+};
+
+static cairo_status_t
+cairo_pdf_ft_font_write_offset_table (cairo_pdf_ft_font_t *font)
+{
+    unsigned short search_range, entry_selector, range_shift;
+    int num_tables;
+
+    num_tables = ARRAY_LENGTH (truetype_tables);
+    search_range = 1;
+    entry_selector = 0;
+    while (search_range * 2 <= num_tables) {
+	search_range *= 2;
+	entry_selector++;
+    }
+    search_range *= 16;
+    range_shift = num_tables * 16 - search_range;
+
+    cairo_pdf_ft_font_write_be32 (font, SFNT_VERSION);
+    cairo_pdf_ft_font_write_be16 (font, num_tables);
+    cairo_pdf_ft_font_write_be16 (font, search_range);
+    cairo_pdf_ft_font_write_be16 (font, entry_selector);
+    cairo_pdf_ft_font_write_be16 (font, range_shift);
+
+    cairo_pdf_ft_font_write (font, NULL, ARRAY_LENGTH (truetype_tables) * 16);
+
+    return font->status;
+}    
+
+static unsigned long
+cairo_pdf_ft_font_calculate_checksum (cairo_pdf_ft_font_t *font,
+			   unsigned long start, unsigned long end)
+{
+    unsigned long *padded_end;
+    unsigned long *p;
+    unsigned long checksum;
+    char *data;
+
+    checksum = 0; 
+    data = _cairo_array_index (&font->output, 0);
+    p = (unsigned long *) (data + start);
+    padded_end = (unsigned long *) (data + ((end + 3) & ~3));
+    while (p < padded_end)
+	checksum += *p++;
+
+    return checksum;
+}
+
+static void
+cairo_pdf_ft_font_update_entry (cairo_pdf_ft_font_t *font, int index, unsigned long tag,
+			unsigned long start, unsigned long end)
+{
+    unsigned long *entry;
+
+    entry = _cairo_array_index (&font->output, 12 + 16 * index);
+    entry[0] = cpu_to_be32 (tag);
+    entry[1] = cpu_to_be32 (cairo_pdf_ft_font_calculate_checksum (font, start, end));
+    entry[2] = cpu_to_be32 (start);
+    entry[3] = cpu_to_be32 (end - start);
+}
+
+static cairo_status_t
+cairo_pdf_ft_font_generate (void *abstract_font,
+			    const char **data, unsigned long *length)
+{
+    cairo_pdf_ft_font_t *font = abstract_font;
+    unsigned long start, end, next, checksum;
+    int i;
+
+    font->face = _cairo_ft_unscaled_font_lock_face (font->base.unscaled_font);
+
+    if (cairo_pdf_ft_font_write_offset_table (font))
+	goto fail;
+
+    start = cairo_pdf_ft_font_align_output (font);
+    end = start;
+
+    end = 0;
+    for (i = 0; i < ARRAY_LENGTH (truetype_tables); i++) {
+	if (truetype_tables[i].write (font, truetype_tables[i].tag))
+	    goto fail;
+
+	end = _cairo_array_num_elements (&font->output);
+	next = cairo_pdf_ft_font_align_output (font);
+	cairo_pdf_ft_font_update_entry (font, i, truetype_tables[i].tag,
+					start, end);
+	start = next;
+    }
+
+    checksum =
+	0xb1b0afba - cairo_pdf_ft_font_calculate_checksum (font, 0, end);
+    *font->checksum_location = cpu_to_be32 (checksum);
+
+    *data = _cairo_array_index (&font->output, 0);
+    *length = _cairo_array_num_elements (&font->output);
+
+ fail:
+    _cairo_ft_unscaled_font_unlock_face (font->base.unscaled_font);      
+    font->face = NULL;
+
+    return font->status;
+}
+
+static int
+cairo_pdf_ft_font_use_glyph (void *abstract_font, int glyph)
+{
+    cairo_pdf_ft_font_t *font = abstract_font;
+
+    if (font->parent_to_subset[glyph] == 0) {
+	font->parent_to_subset[glyph] = font->base.num_glyphs;
+	font->glyphs[font->base.num_glyphs].parent_index = glyph;
+	font->base.num_glyphs++;
+    }
+
+    return font->parent_to_subset[glyph];
+}
+
+static cairo_font_subset_backend_t cairo_pdf_ft_font_backend = {
+    cairo_pdf_ft_font_use_glyph,
+    cairo_pdf_ft_font_generate,
+    cairo_pdf_ft_font_destroy
+};
--- /dev/null	2005-06-17 06:38:10.873695992 -0400
+++ src/cairo-font-subset-private.h	2005-06-07 15:34:27.000000000 -0400
@@ -0,0 +1,68 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *	Kristian Høgsberg <krh at redhat.com>
+ */
+
+#include "cairoint.h"
+
+#ifndef CAIRO_FONT_SUBSET_PRIVATE_H
+#define CAIRO_FONT_SUBSET_PRIVATE_H
+
+typedef struct cairo_font_subset_backend cairo_font_subset_backend_t;
+typedef struct cairo_font_subset cairo_font_subset_t;
+struct cairo_font_subset {
+    cairo_font_subset_backend_t *backend;
+    cairo_unscaled_font_t *unscaled_font;
+    unsigned int font_id;
+    char *base_font;
+    int num_glyphs;
+    int *widths;
+    long x_min, y_min, x_max, y_max;
+    long ascent, descent;
+};
+
+
+cairo_private int
+cairo_font_subset_use_glyph (cairo_font_subset_t *font, int glyph);
+
+cairo_private cairo_status_t
+cairo_font_subset_generate (cairo_font_subset_t *font,
+			    const char **data, unsigned long *length);
+
+cairo_private void
+cairo_font_subset_destroy (cairo_font_subset_t *font);
+
+cairo_private cairo_font_subset_t *
+cairo_font_subset_create (cairo_unscaled_font_t *unscaled_font);
+
+#endif /* CAIRO_FONT_SUBSET_PRIVATE_H */


More information about the cairo mailing list