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 id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbas
bugs638764
milestone2.2a1pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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 */