1# HG changeset patch
2# User Lee Salzman <lsalzman@mozilla.com>
3# Date 1445463645 14400
4#      Wed Oct 21 17:40:45 2015 -0400
5# Node ID 9e84563cbd73c5b0993dfd018ca25b660b667e94
6# Parent  2d3fd51c4182c253a2f102655e8e9e466032853f
7workaround for Windows printer drivers that can't handle swapped X and Y axes
8
9diff --git a/gfx/cairo/cairo/src/cairo-matrix.c b/gfx/cairo/cairo/src/cairo-matrix.c
10--- a/gfx/cairo/cairo/src/cairo-matrix.c
11+++ b/gfx/cairo/cairo/src/cairo-matrix.c
12@@ -873,42 +873,56 @@ cairo_bool_t
13   (Note that the minor axis length is at the minimum of the above solution,
14   which is just sqrt ( f - sqrt(g² + h²) ) given the symmetry of (D)).
15
16
17   For another derivation of the same result, using Singular Value Decomposition,
18   see doc/tutorial/src/singular.c.
19 */
20
21-/* determine the length of the major axis of a circle of the given radius
22-   after applying the transformation matrix. */
23-double
24-_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
25-					     double radius)
26+/* determine the length of the major and minor axes of a circle of the given
27+   radius after applying the transformation matrix. */
28+void
29+_cairo_matrix_transformed_circle_axes (const cairo_matrix_t *matrix,
30+				       double radius,
31+				       double *major,
32+				       double *minor)
33 {
34-    double  a, b, c, d, f, g, h, i, j;
35+    double  a, b, c, d, f, g, h, i, j, k;
36
37     _cairo_matrix_get_affine (matrix,
38                               &a, &b,
39                               &c, &d,
40                               NULL, NULL);
41
42     i = a*a + b*b;
43     j = c*c + d*d;
44+    k = a*c + b*d;
45
46     f = 0.5 * (i + j);
47     g = 0.5 * (i - j);
48-    h = a*c + b*d;
49+    h = hypot (g, k);
50
51-    return radius * sqrt (f + hypot (g, h));
52+    if (major)
53+	*major = radius * sqrt (f + h);
54+    if (minor)
55+	*minor = radius * sqrt (f - h);
56+}
57
58-    /*
59-     * we don't need the minor axis length, which is
60-     * double min = radius * sqrt (f - sqrt (g*g+h*h));
61-     */
62+/* determine the length of the major axis of a circle of the given radius
63+   after applying the transformation matrix. */
64+double
65+_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
66+					     double radius)
67+{
68+    double major;
69+
70+    _cairo_matrix_transformed_circle_axes (matrix, radius, &major, NULL);
71+
72+    return major;
73 }
74
75 void
76 _cairo_matrix_to_pixman_matrix (const cairo_matrix_t	*matrix,
77 				pixman_transform_t	*pixman_transform,
78 				double xc,
79 				double yc)
80 {
81diff --git a/gfx/cairo/cairo/src/cairo-win32-printing-surface.c b/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
82--- a/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
83+++ b/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
84@@ -610,16 +610,17 @@ static cairo_status_t
85     int x_tile, y_tile, left, right, top, bottom;
86     RECT clip;
87     const cairo_color_t *background_color;
88     const unsigned char *mime_data;
89     unsigned long mime_size;
90     cairo_image_info_t mime_info;
91     cairo_bool_t use_mime;
92     DWORD mime_type;
93+    cairo_bool_t axis_swap;
94
95     /* If we can't use StretchDIBits with this surface, we can't do anything
96      * here.
97      */
98     if (!(surface->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHDIB))
99 	return CAIRO_INT_STATUS_UNSUPPORTED;
100
101     if (surface->content == CAIRO_CONTENT_COLOR_ALPHA)
102@@ -658,39 +659,65 @@ static cairo_status_t
103 							  &mime_size,
104 							  &mime_info);
105     }
106     if (_cairo_status_is_error (status))
107 	return status;
108
109     use_mime = (status == CAIRO_STATUS_SUCCESS);
110
111-    if (!use_mime && image->format != CAIRO_FORMAT_RGB24) {
112+    m = pattern->base.matrix;
113+    status = cairo_matrix_invert (&m);
114+    /* _cairo_pattern_set_matrix guarantees invertibility */
115+    assert (status == CAIRO_STATUS_SUCCESS);
116+    cairo_matrix_multiply (&m, &m, &surface->ctm);
117+    cairo_matrix_multiply (&m, &m, &surface->gdi_ctm);
118+    /* Check if the matrix swaps the X and Y axes by checking if the diagonal
119+     * is effectively zero. This can happen, for example, if it was composed
120+     * with a rotation such as a landscape transform. Some printing devices
121+     * don't support such transforms in StretchDIBits.
122+     */
123+    axis_swap = fabs (m.xx*image->width) < 1 && fabs (m.yy*image->height) < 1;
124+
125+    if (!use_mime && (image->format != CAIRO_FORMAT_RGB24 || axis_swap)) {
126 	cairo_surface_t *opaque_surface;
127 	cairo_surface_pattern_t image_pattern;
128 	cairo_solid_pattern_t background_pattern;
129+	int width = image->width, height = image->height;
130
131+	if (axis_swap) {
132+	    width = image->height;
133+	    height = image->width;
134+	}
135 	opaque_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
136-						     image->width,
137-						     image->height);
138+						     width,
139+						     height);
140 	if (opaque_surface->status) {
141 	    status = opaque_surface->status;
142 	    goto CLEANUP_OPAQUE_IMAGE;
143 	}
144
145-	_cairo_pattern_init_solid (&background_pattern,
146-				   background_color);
147-	status = _cairo_surface_paint (opaque_surface,
148-				       CAIRO_OPERATOR_SOURCE,
149-				       &background_pattern.base,
150-				       NULL);
151-	if (status)
152-	    goto CLEANUP_OPAQUE_IMAGE;
153+	if (image->format != CAIRO_FORMAT_RGB24) {
154+	    _cairo_pattern_init_solid (&background_pattern,
155+				       background_color);
156+	    status = _cairo_surface_paint (opaque_surface,
157+					   CAIRO_OPERATOR_SOURCE,
158+					   &background_pattern.base,
159+					   NULL);
160+	    if (status)
161+		goto CLEANUP_OPAQUE_IMAGE;
162+	}
163
164 	_cairo_pattern_init_for_surface (&image_pattern, &image->base);
165+	if (axis_swap) {
166+	    /* swap the X and Y axes to undo the axis swap in the matrix */
167+	    cairo_matrix_t swap_xy = { 0, 1, 1, 0, 0, 0 };
168+	    cairo_pattern_set_matrix (&image_pattern.base, &swap_xy);
169+	    cairo_matrix_multiply (&m, &swap_xy, &m);
170+	}
171 	status = _cairo_surface_paint (opaque_surface,
172 				       CAIRO_OPERATOR_OVER,
173 				       &image_pattern.base,
174 				       NULL);
175 	_cairo_pattern_fini (&image_pattern.base);
176 	if (status)
177 	    goto CLEANUP_OPAQUE_IMAGE;
178
179@@ -706,23 +733,16 @@ static cairo_status_t
180     bi.bmiHeader.biXPelsPerMeter = PELS_72DPI;
181     bi.bmiHeader.biYPelsPerMeter = PELS_72DPI;
182     bi.bmiHeader.biPlanes = 1;
183     bi.bmiHeader.biBitCount = 32;
184     bi.bmiHeader.biCompression = use_mime ? mime_type : BI_RGB;
185     bi.bmiHeader.biClrUsed = 0;
186     bi.bmiHeader.biClrImportant = 0;
187
188-    m = pattern->base.matrix;
189-    status = cairo_matrix_invert (&m);
190-    /* _cairo_pattern_set_matrix guarantees invertibility */
191-    assert (status == CAIRO_STATUS_SUCCESS);
192-
193-    cairo_matrix_multiply (&m, &m, &surface->gdi_ctm);
194-    cairo_matrix_multiply(&m, &m, &surface->ctm);
195     SaveDC (surface->dc);
196     _cairo_matrix_to_win32_xform (&m, &xform);
197
198     if (! SetWorldTransform (surface->dc, &xform)) {
199 	status = _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_paint_image_pattern");
200 	goto CLEANUP_OPAQUE_IMAGE;
201     }
202
203@@ -1260,16 +1280,17 @@ static cairo_int_status_t
204     DWORD pen_width;
205     DWORD *dash_array;
206     HGDIOBJ obj;
207     unsigned int i;
208     cairo_solid_pattern_t clear;
209     cairo_matrix_t mat;
210     double scale;
211     double scaled_width;
212+    double major, minor;
213
214     status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
215     if (status)
216 	return status;
217
218     if (op == CAIRO_OPERATOR_CLEAR) {
219 	_cairo_win32_printing_surface_init_clear_color (surface, &clear);
220 	source = (cairo_pattern_t*) &clear;
221@@ -1350,22 +1371,40 @@ static cairo_int_status_t
222     if (status)
223 	return status;
224
225     /*
226      * Switch to user space to set line parameters
227      */
228     SaveDC (surface->dc);
229
230-    _cairo_matrix_to_win32_xform (&mat, &xform);
231-    xform.eDx = 0.0f;
232-    xform.eDy = 0.0f;
233+    /* Some printers don't handle transformed strokes. Avoid the transform
234+     * if not required for the pen shape. Use the SVD here to find the major
235+     * and minor scales then check if they differ by more than 1 device unit.
236+     * If the difference is smaller, then just treat the scaling as uniform.
237+     * This check ignores rotations as the pen shape is symmetric before
238+     * transformation.
239+     */
240+    _cairo_matrix_transformed_circle_axes (&mat, scale, &major, &minor);
241+    if (fabs (major - minor) > 1) {
242+	/* Check if the matrix swaps the X and Y axes such that the diagonal
243+	 * is nearly zero. This was observed to cause problems with XPS export.
244+	 */
245+	if (fabs (mat.xx) < 1e-6 && fabs (mat.yy) < 1e-6) {
246+	    /* swap the X and Y axes to undo the axis swap in the matrix */
247+	    cairo_matrix_t swap_xy = { 0, 1, 1, 0, 0, 0 };
248+	    cairo_matrix_multiply (&mat, &swap_xy, &mat);
249+	}
250+	_cairo_matrix_to_win32_xform (&mat, &xform);
251+	xform.eDx = 0.0f;
252+	xform.eDy = 0.0f;
253
254-    if (!ModifyWorldTransform (surface->dc, &xform, MWT_LEFTMULTIPLY))
255-	return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SetWorldTransform");
256+	if (!ModifyWorldTransform (surface->dc, &xform, MWT_LEFTMULTIPLY))
257+	    return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SetWorldTransform");
258+    }
259
260     if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
261 	StrokePath (surface->dc);
262     } else {
263 	if (!WidenPath (surface->dc))
264 	    return _cairo_win32_print_gdi_error ("_win32_surface_stroke:WidenPath");
265 	if (!SelectClipPath (surface->dc, RGN_AND))
266 	    return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SelectClipPath");
267diff --git a/gfx/cairo/cairo/src/cairoint.h b/gfx/cairo/cairo/src/cairoint.h
268--- a/gfx/cairo/cairo/src/cairoint.h
269+++ b/gfx/cairo/cairo/src/cairoint.h
270@@ -2115,16 +2115,22 @@ cairo_private cairo_bool_t
271 				     int *itx, int *ity);
272
273 cairo_private cairo_bool_t
274 _cairo_matrix_has_unity_scale (const cairo_matrix_t *matrix);
275
276 cairo_private cairo_bool_t
277 _cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix) cairo_pure;
278
279+cairo_private void
280+_cairo_matrix_transformed_circle_axes (const cairo_matrix_t *matrix,
281+				       double radius,
282+				       double *major,
283+				       double *minor);
284+
285 cairo_private double
286 _cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
287 					     double radius) cairo_pure;
288
289 cairo_private void
290 _cairo_matrix_to_pixman_matrix (const cairo_matrix_t	*matrix,
291 				pixman_transform_t	*pixman_transform,
292 				double                   xc,
293