bug 638764 - print DirectWrite text via ExtTextOut using a GDI font, to get decent PDF output. r=bas
authorJonathan Kew <jfkthame@gmail.com>
Mon, 11 Apr 2011 13:02:31 +0100
changeset 67839 6cfaf3ab8b96d90a3c637e9bbb8f1310b0f51702
parent 67838 e1167451033fc589bb37c69fd697195488ce83ef
child 67840 364b5727594a5c7b089dc6e36c70e35140accf86
push idunknown
push userunknown
push dateunknown
reviewersbas
bugs638764
milestone2.2a1pre
bug 638764 - print DirectWrite text via ExtTextOut using a GDI font, to get decent PDF output. r=bas
gfx/cairo/cairo/src/cairo-d2d-surface.cpp
gfx/cairo/cairo/src/cairo-dwrite-font.cpp
gfx/cairo/cairo/src/cairo-win32-printing-surface.c
gfx/cairo/cairo/src/cairo-win32-private.h
--- a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
+++ b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
@@ -30,17 +30,17 @@
  *
  * The Initial Developer of the Original Code is the Mozilla Foundation
  *
  * Contributor(s):
  *	Bas Schouten <bschouten@mozilla.com>
  */
 #define INITGUID
 
-#include "cairo.h"
+#include "cairoint.h"
 #include "cairo-d2d-private.h"
 #include "cairo-dwrite-private.h"
 
 extern "C" {
 #include "cairo-win32.h"
 #include "cairo-analysis-surface-private.h"
 }
 
--- a/gfx/cairo/cairo/src/cairo-dwrite-font.cpp
+++ b/gfx/cairo/cairo/src/cairo-dwrite-font.cpp
@@ -1364,8 +1364,72 @@ cairo_int_status_t
 #endif
 
     delete [] indices;
     delete [] offsets;
     delete [] advances;
 
     return CAIRO_INT_STATUS_SUCCESS;
 }
+
+// Helper for _cairo_win32_printing_surface_show_glyphs to create a win32 equivalent
+// of a dwrite scaled_font so that we can print using ExtTextOut instead of drawing
+// paths or blitting glyph bitmaps.
+cairo_int_status_t
+_cairo_dwrite_scaled_font_create_win32_scaled_font (cairo_scaled_font_t *scaled_font,
+                                                    cairo_scaled_font_t **new_font)
+{
+    if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_DWRITE) {
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    cairo_font_face_t *face = cairo_scaled_font_get_font_face (scaled_font);
+    cairo_dwrite_font_face_t *dwface = reinterpret_cast<cairo_dwrite_font_face_t*>(face);
+
+    RefPtr<IDWriteGdiInterop> gdiInterop;
+    DWriteFactory::Instance()->GetGdiInterop(&gdiInterop);
+    if (!gdiInterop) {
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    LOGFONTW logfont;
+    if (FAILED(gdiInterop->ConvertFontFaceToLOGFONT (dwface->dwriteface, &logfont))) {
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+    // DW must have been using an outline font, so we want GDI to use the same,
+    // even if there's also a bitmap face available
+    logfont.lfOutPrecision = OUT_OUTLINE_PRECIS;
+
+    cairo_font_face_t *win32_face = cairo_win32_font_face_create_for_logfontw (&logfont);
+    if (!win32_face) {
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    cairo_matrix_t font_matrix;
+    cairo_scaled_font_get_font_matrix (scaled_font, &font_matrix);
+
+    cairo_matrix_t ctm;
+    cairo_scaled_font_get_ctm (scaled_font, &ctm);
+
+    cairo_font_options_t options;
+    cairo_scaled_font_get_font_options (scaled_font, &options);
+
+    cairo_scaled_font_t *font = cairo_scaled_font_create (win32_face,
+			                                  &font_matrix,
+			                                  &ctm,
+			                                  &options);
+    cairo_font_face_destroy (win32_face);
+
+    if (!font) {
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    if (_cairo_win32_scaled_font_is_type1 (font) || _cairo_win32_scaled_font_is_bitmap (font)) {
+        // If we somehow got a Type1 or bitmap font, it can't be the same physical font
+        // as directwrite was using, so glyph IDs will not match; best we can do is to
+        // throw it away and fall back on rendering paths or blitting bitmaps instead.
+        cairo_scaled_font_destroy (font);
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    *new_font = font;
+    return CAIRO_INT_STATUS_SUCCESS;
+}
--- a/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
+++ b/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
@@ -1446,16 +1446,17 @@ static cairo_int_status_t
     cairo_win32_surface_t *surface = abstract_surface;
     cairo_status_t status = CAIRO_STATUS_SUCCESS;
     cairo_scaled_glyph_t *scaled_glyph;
     cairo_pattern_t *opaque = NULL;
     int i;
     cairo_matrix_t old_ctm;
     cairo_bool_t old_has_ctm;
     cairo_solid_pattern_t clear;
+    cairo_scaled_font_t *local_scaled_font = NULL;
 
     status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
     if (status)
 	return status;
 
     if (op == CAIRO_OPERATOR_CLEAR) {
 	_cairo_win32_printing_surface_init_clear_color (surface, &clear);
 	source = (cairo_pattern_t*) &clear;
@@ -1476,16 +1477,22 @@ static cairo_int_status_t
 	if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_WIN32) {
 	    if (_cairo_win32_scaled_font_is_bitmap (scaled_font))
 		return CAIRO_INT_STATUS_UNSUPPORTED;
 	    else
 		return _cairo_win32_printing_surface_analyze_operation (surface, op, source);
 	}
 #endif
 
+#if CAIRO_HAS_DWRITE_FONT
+        if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_DWRITE) {
+	    return _cairo_win32_printing_surface_analyze_operation (surface, op, source);
+        }
+#endif
+
 	/* For non win32 fonts we need to check that each glyph has a
 	 * path available. If a path is not available,
 	 * _cairo_scaled_glyph_lookup() will return
 	 * CAIRO_INT_STATUS_UNSUPPORTED and a fallback image will be
 	 * used.
 	 */
 	for (i = 0; i < num_glyphs; i++) {
 	    status = _cairo_scaled_glyph_lookup (scaled_font,
@@ -1508,16 +1515,33 @@ static cairo_int_status_t
 	opaque = cairo_pattern_create_rgb (GetRValue (color) / 255.0,
 					   GetGValue (color) / 255.0,
 					   GetBValue (color) / 255.0);
 	if (opaque->status)
 	    return opaque->status;
 	source = opaque;
     }
 
+#if CAIRO_HAS_DWRITE_FONT
+    /* For a printer, the dwrite path is not desirable as it goes through the
+     * bitmap-blitting GDI interop route. Better to create a win32 (GDI) font
+     * so that ExtTextOut can be used, giving the printer driver the chance
+     * to do the right thing with the text.
+     */
+    if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_DWRITE) {
+        status = _cairo_dwrite_scaled_font_create_win32_scaled_font (scaled_font, &local_scaled_font);
+        if (status == CAIRO_STATUS_SUCCESS) {
+            scaled_font = local_scaled_font;
+        } else {
+            /* Reset status; we'll fall back to drawing glyphs as paths */
+            status = CAIRO_STATUS_SUCCESS;
+        }
+    }
+#endif
+
 #if CAIRO_HAS_WIN32_FONT
     if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_WIN32 &&
 	source->type == CAIRO_PATTERN_TYPE_SOLID)
     {
 	cairo_matrix_t ctm;
 	cairo_glyph_t  *type1_glyphs = NULL;
 	cairo_scaled_font_subsets_glyph_t subset_glyph;
 
@@ -1533,28 +1557,29 @@ static cairo_int_status_t
 	 * As _cairo_win32_scaled_font_index_to_ucs4() is a slow
 	 * operation, the font subsetting function
 	 * _cairo_scaled_font_subsets_map_glyph() is used to obtain
 	 * the unicode value because it caches the reverse mapping in
 	 * the subsets.
 	 */
 	if (_cairo_win32_scaled_font_is_type1 (scaled_font)) {
 	    type1_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
-	    if (type1_glyphs == NULL)
-		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-
+	    if (type1_glyphs == NULL) {
+		status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+		goto FINISH;
+	    }
 	    memcpy (type1_glyphs, glyphs, num_glyphs * sizeof (cairo_glyph_t));
 	    for (i = 0; i < num_glyphs; i++) {
 		status = _cairo_scaled_font_subsets_map_glyph (surface->font_subsets,
 							       scaled_font,
 							       type1_glyphs[i].index,
 							       NULL, 0,
 							       &subset_glyph);
 		if (status)
-		    return status;
+		    goto FINISH;
 
 		type1_glyphs[i].index = subset_glyph.unicode;
 	    }
 	    glyphs = type1_glyphs;
 	}
 
 	if (surface->has_ctm || surface->has_gdi_ctm) {
 	    cairo_matrix_multiply (&ctm, &surface->ctm, &surface->gdi_ctm);
@@ -1566,23 +1591,23 @@ static cairo_int_status_t
 						    &ctm,
 						    &scaled_font->options);
 	}
 	status = _cairo_win32_surface_show_glyphs (surface, op,
 						   source, glyphs,
 						   num_glyphs, scaled_font,
 						   clip,
 						   remaining_glyphs);
-	if (surface->has_ctm)
+	if (surface->has_ctm || surface->has_gdi_ctm)
 	    cairo_scaled_font_destroy (scaled_font);
 
 	if (type1_glyphs != NULL)
 	    free (type1_glyphs);
 
-	return status;
+	goto FINISH;
     }
 #endif
 
     SaveDC (surface->dc);
     old_ctm = surface->ctm;
     old_has_ctm = surface->has_ctm;
     surface->has_ctm = TRUE;
     surface->path_empty = TRUE;
@@ -1600,31 +1625,35 @@ static cairo_int_status_t
     }
     EndPath (surface->dc);
     surface->ctm = old_ctm;
     surface->has_ctm = old_has_ctm;
     if (status == CAIRO_STATUS_SUCCESS && surface->path_empty == FALSE) {
 	if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
 	    status = _cairo_win32_printing_surface_select_solid_brush (surface, source);
 	    if (status)
-		return status;
+		goto FINISH;
 
 	    SetPolyFillMode (surface->dc, WINDING);
 	    FillPath (surface->dc);
 	    _cairo_win32_printing_surface_done_solid_brush (surface);
 	} else {
 	    SelectClipPath (surface->dc, RGN_AND);
 	    status = _cairo_win32_printing_surface_paint_pattern (surface, source);
 	}
     }
     RestoreDC (surface->dc, -1);
 
     if (opaque)
 	cairo_pattern_destroy (opaque);
 
+FINISH:
+    if (local_scaled_font)
+        cairo_scaled_font_destroy (local_scaled_font);
+
     return status;
 }
 
 static cairo_surface_t *
 _cairo_win32_printing_surface_create_similar (void		*abstract_surface,
 					      cairo_content_t	 content,
 					      int		 width,
 					      int		 height)
--- a/gfx/cairo/cairo/src/cairo-win32-private.h
+++ b/gfx/cairo/cairo/src/cairo-win32-private.h
@@ -241,12 +241,15 @@ cairo_int_status_t
 _cairo_dwrite_show_glyphs_on_surface(void			*surface,
 				     cairo_operator_t		 op,
 				     const cairo_pattern_t	*source,
 				     cairo_glyph_t		*glyphs,
 				     int			 num_glyphs,
 				     cairo_scaled_font_t	*scaled_font,
 				     cairo_clip_t               *clip);
 
+cairo_int_status_t
+_cairo_dwrite_scaled_font_create_win32_scaled_font(cairo_scaled_font_t *scaled_font,
+                                                   cairo_scaled_font_t **new_font);
 
 CAIRO_END_DECLS
 #endif /* CAIRO_HAS_DWRITE_FONT */
 #endif /* CAIRO_WIN32_PRIVATE_H */