1 //========================================================================
2 //
3 // CairoOutputDev.cc
4 //
5 // Copyright 2003 Glyph & Cog, LLC
6 // Copyright 2004 Red Hat, Inc
7 //
8 //========================================================================
9 
10 //========================================================================
11 //
12 // Modified under the Poppler project - http://poppler.freedesktop.org
13 //
14 // All changes made under the Poppler project to this file are licensed
15 // under GPL version 2 or later
16 //
17 // Copyright (C) 2005-2008 Jeff Muizelaar <jeff@infidigm.net>
18 // Copyright (C) 2005, 2006 Kristian Høgsberg <krh@redhat.com>
19 // Copyright (C) 2005, 2009, 2012 Albert Astals Cid <aacid@kde.org>
20 // Copyright (C) 2005 Nickolay V. Shmyrev <nshmyrev@yandex.ru>
21 // Copyright (C) 2006-2011, 2013, 2014 Carlos Garcia Campos <carlosgc@gnome.org>
22 // Copyright (C) 2008 Carl Worth <cworth@cworth.org>
23 // Copyright (C) 2008-2015 Adrian Johnson <ajohnson@redneon.com>
24 // Copyright (C) 2008 Michael Vrable <mvrable@cs.ucsd.edu>
25 // Copyright (C) 2008, 2009 Chris Wilson <chris@chris-wilson.co.uk>
26 // Copyright (C) 2008, 2012 Hib Eris <hib@hiberis.nl>
27 // Copyright (C) 2009, 2010 David Benjamin <davidben@mit.edu>
28 // Copyright (C) 2011-2014 Thomas Freitag <Thomas.Freitag@alfa.de>
29 // Copyright (C) 2012 Patrick Pfeifer <p2000@mailinator.com>
30 // Copyright (C) 2012, 2015, 2016 Jason Crain <jason@aquaticape.us>
31 // Copyright (C) 2015 Suzuki Toshiya <mpsuzuki@hiroshima-u.ac.jp>
32 //
33 // To see a description of the changes please see the Changelog file that
34 // came with your tarball or type make ChangeLog if you are building from git
35 //
36 //========================================================================
37 
38 #include <config.h>
39 
40 #ifdef USE_GCC_PRAGMAS
41 #pragma implementation
42 #endif
43 
44 #include <string.h>
45 #include <math.h>
46 #include <assert.h>
47 #include <cairo.h>
48 
49 #include "goo/gfile.h"
50 #include "goo/gtypes_p.h"
51 #include "GlobalParams.h"
52 #include "Error.h"
53 #include "Object.h"
54 #include "Gfx.h"
55 #include "GfxState.h"
56 #include "GfxFont.h"
57 #include "Page.h"
58 #include "Link.h"
59 #include "FontEncodingTables.h"
60 #include "PDFDocEncoding.h"
61 #include <fofi/FoFiTrueType.h>
62 #include <splash/SplashBitmap.h>
63 #include "CairoOutputDev.h"
64 #include "CairoFontEngine.h"
65 #include "CairoRescaleBox.h"
66 #include "UnicodeMap.h"
67 #include "JBIG2Stream.h"
68 //------------------------------------------------------------------------
69 
70 // #define LOG_CAIRO
71 
72 #ifdef LOG_CAIRO
73 #define LOG(x) (x)
74 #else
75 #define LOG(x)
76 #endif
77 
printMatrix(cairo_matrix_t * matrix)78 static inline void printMatrix(cairo_matrix_t *matrix){
79 	printf("%f %f, %f %f (%f %f)\n", matrix->xx, matrix->yx,
80 			matrix->xy, matrix->yy,
81 			matrix->x0, matrix->y0);
82 }
83 
84 
85 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
86 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
87 
88 
89 //------------------------------------------------------------------------
90 // CairoImage
91 //------------------------------------------------------------------------
92 
CairoImage(double x1,double y1,double x2,double y2)93 CairoImage::CairoImage (double x1, double y1, double x2, double y2) {
94   this->image = NULL;
95   this->x1 = x1;
96   this->y1 = y1;
97   this->x2 = x2;
98   this->y2 = y2;
99 }
100 
~CairoImage()101 CairoImage::~CairoImage () {
102   if (image)
103     cairo_surface_destroy (image);
104 }
105 
setImage(cairo_surface_t * image)106 void CairoImage::setImage (cairo_surface_t *image) {
107   if (this->image)
108     cairo_surface_destroy (this->image);
109   this->image = cairo_surface_reference (image);
110 }
111 
112 //------------------------------------------------------------------------
113 // CairoOutputDev
114 //------------------------------------------------------------------------
115 
116 // We cannot tie the lifetime of an FT_Library object to that of
117 // CairoOutputDev, since any FT_Faces created with it may end up with a
118 // reference by Cairo which can be held long after the CairoOutputDev is
119 // deleted.  The simplest way to avoid problems is to never tear down the
120 // FT_Library instance; to avoid leaks, just use a single global instance
121 // initialized the first time it is needed.
122 FT_Library CairoOutputDev::ft_lib;
123 GBool CairoOutputDev::ft_lib_initialized = gFalse;
124 
CairoOutputDev()125 CairoOutputDev::CairoOutputDev() {
126   doc = NULL;
127 
128   if (!ft_lib_initialized) {
129     FT_Init_FreeType(&ft_lib);
130     ft_lib_initialized = gTrue;
131   }
132 
133   fontEngine = NULL;
134   fontEngine_owner = gFalse;
135   glyphs = NULL;
136   fill_pattern = NULL;
137   fill_color.r = fill_color.g = fill_color.b = 0;
138   stroke_pattern = NULL;
139   stroke_color.r = stroke_color.g = stroke_color.b = 0;
140   stroke_opacity = 1.0;
141   fill_opacity = 1.0;
142   textClipPath = NULL;
143   strokePathClip = NULL;
144   cairo = NULL;
145   currentFont = NULL;
146   prescaleImages = gTrue;
147   printing = gTrue;
148   use_show_text_glyphs = gFalse;
149   inUncoloredPattern = gFalse;
150   inType3Char = gFalse;
151   t3_glyph_has_bbox = gFalse;
152   text_matrix_valid = gTrue;
153 
154   groupColorSpaceStack = NULL;
155   maskStack = NULL;
156   group = NULL;
157   mask = NULL;
158   shape = NULL;
159   cairo_shape = NULL;
160   knockoutCount = 0;
161 
162   text = NULL;
163   actualText = NULL;
164 
165   // the SA parameter supposedly defaults to false, but Acrobat
166   // apparently hardwires it to true
167   stroke_adjust = globalParams->getStrokeAdjust();
168   align_stroke_coords = gFalse;
169   adjusted_stroke_width = gFalse;
170   xref = NULL;
171 }
172 
~CairoOutputDev()173 CairoOutputDev::~CairoOutputDev() {
174   if (fontEngine_owner && fontEngine) {
175     delete fontEngine;
176   }
177 
178   if (cairo)
179     cairo_destroy (cairo);
180   cairo_pattern_destroy (stroke_pattern);
181   cairo_pattern_destroy (fill_pattern);
182   if (group)
183     cairo_pattern_destroy (group);
184   if (mask)
185     cairo_pattern_destroy (mask);
186   if (shape)
187     cairo_pattern_destroy (shape);
188   if (text)
189     text->decRefCnt();
190   if (actualText)
191     delete actualText;
192 }
193 
setCairo(cairo_t * cairo)194 void CairoOutputDev::setCairo(cairo_t *cairo)
195 {
196   if (this->cairo != NULL) {
197     cairo_status_t status = cairo_status (this->cairo);
198     if (status) {
199       error(errInternal, -1, "cairo context error: {0:s}\n", cairo_status_to_string(status));
200     }
201     cairo_destroy (this->cairo);
202     assert(!cairo_shape);
203   }
204   if (cairo != NULL) {
205     this->cairo = cairo_reference (cairo);
206 	/* save the initial matrix so that we can use it for type3 fonts. */
207 	//XXX: is this sufficient? could we miss changes to the matrix somehow?
208 	cairo_get_matrix(cairo, &orig_matrix);
209 	setAntialias(cairo, antialias);
210   } else {
211     this->cairo = NULL;
212     this->cairo_shape = NULL;
213   }
214 }
215 
setTextPage(TextPage * text)216 void CairoOutputDev::setTextPage(TextPage *text)
217 {
218   if (this->text)
219     this->text->decRefCnt();
220   if (actualText)
221     delete actualText;
222   if (text) {
223     this->text = text;
224     this->text->incRefCnt();
225     actualText = new ActualText(text);
226   } else {
227     this->text = NULL;
228     actualText = NULL;
229   }
230 }
231 
setAntialias(cairo_antialias_t antialias)232 void CairoOutputDev::setAntialias(cairo_antialias_t antialias)
233 {
234   this->antialias = antialias;
235   if (cairo)
236     setAntialias (cairo, antialias);
237   if (cairo_shape)
238     setAntialias (cairo_shape, antialias);
239 }
240 
setAntialias(cairo_t * cr,cairo_antialias_t antialias)241 void CairoOutputDev::setAntialias(cairo_t *cr, cairo_antialias_t antialias)
242 {
243   cairo_font_options_t *font_options;
244   cairo_set_antialias (cairo, antialias);
245   font_options = cairo_font_options_create ();
246   cairo_get_font_options (cr, font_options);
247   cairo_font_options_set_antialias (font_options, antialias);
248   cairo_set_font_options (cr, font_options);
249   cairo_font_options_destroy (font_options);
250 }
251 
startDoc(PDFDoc * docA,CairoFontEngine * parentFontEngine)252 void CairoOutputDev::startDoc(PDFDoc *docA,
253 			      CairoFontEngine *parentFontEngine) {
254   doc = docA;
255   if (parentFontEngine) {
256     fontEngine = parentFontEngine;
257   } else {
258     if (fontEngine) {
259       delete fontEngine;
260     }
261     fontEngine = new CairoFontEngine(ft_lib);
262     fontEngine_owner = gTrue;
263   }
264   xref = doc->getXRef();
265 }
266 
startPage(int pageNum,GfxState * state,XRef * xrefA)267 void CairoOutputDev::startPage(int pageNum, GfxState *state, XRef *xrefA) {
268   /* set up some per page defaults */
269   cairo_pattern_destroy(fill_pattern);
270   cairo_pattern_destroy(stroke_pattern);
271 
272   fill_pattern = cairo_pattern_create_rgb(0., 0., 0.);
273   fill_color.r = fill_color.g = fill_color.b = 0;
274   stroke_pattern = cairo_pattern_reference(fill_pattern);
275   stroke_color.r = stroke_color.g = stroke_color.b = 0;
276 
277   if (text)
278     text->startPage(state);
279   if (xrefA != NULL) {
280     xref = xrefA;
281   }
282 }
283 
endPage()284 void CairoOutputDev::endPage() {
285   if (text) {
286     text->endPage();
287     text->coalesce(gTrue, 0, gFalse);
288   }
289 }
290 
saveState(GfxState * state)291 void CairoOutputDev::saveState(GfxState *state) {
292   LOG(printf ("save\n"));
293   cairo_save (cairo);
294   if (cairo_shape)
295       cairo_save (cairo_shape);
296 
297   MaskStack *ms = new MaskStack;
298   ms->mask = cairo_pattern_reference(mask);
299   ms->mask_matrix = mask_matrix;
300   ms->next = maskStack;
301   maskStack = ms;
302 
303   if (strokePathClip)
304     strokePathClip->ref_count++;
305 }
306 
restoreState(GfxState * state)307 void CairoOutputDev::restoreState(GfxState *state) {
308   LOG(printf ("restore\n"));
309   cairo_restore (cairo);
310   if (cairo_shape)
311       cairo_restore (cairo_shape);
312 
313   text_matrix_valid = gTrue;
314 
315   /* These aren't restored by cairo_restore() since we keep them in
316    * the output device. */
317   updateFillColor(state);
318   updateStrokeColor(state);
319   updateFillOpacity(state);
320   updateStrokeOpacity(state);
321   updateBlendMode(state);
322 
323   MaskStack* ms = maskStack;
324   if (ms) {
325     if (mask)
326       cairo_pattern_destroy(mask);
327     mask = ms->mask;
328     mask_matrix = ms->mask_matrix;
329     maskStack = ms->next;
330     delete ms;
331   }
332 
333   if (strokePathClip && --strokePathClip->ref_count == 0) {
334     delete strokePathClip->path;
335     if (strokePathClip->dashes)
336       gfree (strokePathClip->dashes);
337     gfree (strokePathClip);
338     strokePathClip = NULL;
339   }
340 }
341 
updateAll(GfxState * state)342 void CairoOutputDev::updateAll(GfxState *state) {
343   updateLineDash(state);
344   updateLineJoin(state);
345   updateLineCap(state);
346   updateLineWidth(state);
347   updateFlatness(state);
348   updateMiterLimit(state);
349   updateFillColor(state);
350   updateStrokeColor(state);
351   updateFillOpacity(state);
352   updateStrokeOpacity(state);
353   updateBlendMode(state);
354   needFontUpdate = gTrue;
355   if (text)
356     text->updateFont(state);
357 }
358 
setDefaultCTM(double * ctm)359 void CairoOutputDev::setDefaultCTM(double *ctm) {
360   cairo_matrix_t matrix;
361   matrix.xx = ctm[0];
362   matrix.yx = ctm[1];
363   matrix.xy = ctm[2];
364   matrix.yy = ctm[3];
365   matrix.x0 = ctm[4];
366   matrix.y0 = ctm[5];
367 
368   cairo_transform (cairo, &matrix);
369   if (cairo_shape)
370       cairo_transform (cairo_shape, &matrix);
371 
372   OutputDev::setDefaultCTM(ctm);
373 }
374 
updateCTM(GfxState * state,double m11,double m12,double m21,double m22,double m31,double m32)375 void CairoOutputDev::updateCTM(GfxState *state, double m11, double m12,
376 				double m21, double m22,
377 				double m31, double m32) {
378   cairo_matrix_t matrix, invert_matrix;
379   matrix.xx = m11;
380   matrix.yx = m12;
381   matrix.xy = m21;
382   matrix.yy = m22;
383   matrix.x0 = m31;
384   matrix.y0 = m32;
385 
386   /* Make sure the matrix is invertible before setting it.
387    * cairo will blow up if we give it a matrix that's not
388    * invertible, so we need to check before passing it
389    * to cairo_transform. Ignoring it is likely to give better
390    * results than not rendering anything at all. See #14398
391    *
392    * Ideally, we could do the cairo_transform
393    * and then check if anything went wrong and fix it then
394    * instead of having to invert the matrix. */
395   invert_matrix = matrix;
396   if (cairo_matrix_invert(&invert_matrix)) {
397     error(errSyntaxWarning, -1, "matrix not invertible\n");
398     return;
399   }
400 
401   cairo_transform (cairo, &matrix);
402   if (cairo_shape)
403     cairo_transform (cairo_shape, &matrix);
404   updateLineDash(state);
405   updateLineJoin(state);
406   updateLineCap(state);
407   updateLineWidth(state);
408 }
409 
updateLineDash(GfxState * state)410 void CairoOutputDev::updateLineDash(GfxState *state) {
411   double *dashPattern;
412   int dashLength;
413   double dashStart;
414 
415   state->getLineDash(&dashPattern, &dashLength, &dashStart);
416   cairo_set_dash (cairo, dashPattern, dashLength, dashStart);
417   if (cairo_shape)
418     cairo_set_dash (cairo_shape, dashPattern, dashLength, dashStart);
419 }
420 
updateFlatness(GfxState * state)421 void CairoOutputDev::updateFlatness(GfxState *state) {
422   // cairo_set_tolerance (cairo, state->getFlatness());
423 }
424 
updateLineJoin(GfxState * state)425 void CairoOutputDev::updateLineJoin(GfxState *state) {
426   switch (state->getLineJoin()) {
427   case 0:
428     cairo_set_line_join (cairo, CAIRO_LINE_JOIN_MITER);
429     break;
430   case 1:
431     cairo_set_line_join (cairo, CAIRO_LINE_JOIN_ROUND);
432     break;
433   case 2:
434     cairo_set_line_join (cairo, CAIRO_LINE_JOIN_BEVEL);
435     break;
436   }
437   if (cairo_shape)
438     cairo_set_line_join (cairo_shape, cairo_get_line_join(cairo));
439 }
440 
updateLineCap(GfxState * state)441 void CairoOutputDev::updateLineCap(GfxState *state) {
442   switch (state->getLineCap()) {
443   case 0:
444     cairo_set_line_cap (cairo, CAIRO_LINE_CAP_BUTT);
445     break;
446   case 1:
447     cairo_set_line_cap (cairo, CAIRO_LINE_CAP_ROUND);
448     break;
449   case 2:
450     cairo_set_line_cap (cairo, CAIRO_LINE_CAP_SQUARE);
451     break;
452   }
453   if (cairo_shape)
454     cairo_set_line_cap (cairo_shape, cairo_get_line_cap(cairo));
455 }
456 
updateMiterLimit(GfxState * state)457 void CairoOutputDev::updateMiterLimit(GfxState *state) {
458   cairo_set_miter_limit (cairo, state->getMiterLimit());
459   if (cairo_shape)
460     cairo_set_miter_limit (cairo_shape, state->getMiterLimit());
461 }
462 
updateLineWidth(GfxState * state)463 void CairoOutputDev::updateLineWidth(GfxState *state) {
464   LOG(printf ("line width: %f\n", state->getLineWidth()));
465   adjusted_stroke_width = gFalse;
466   double width = state->getLineWidth();
467   if (stroke_adjust && !printing) {
468     double x, y;
469     x = y = width;
470 
471     /* find out line width in device units */
472     cairo_user_to_device_distance(cairo, &x, &y);
473     if (fabs(x) <= 1.0 && fabs(y) <= 1.0) {
474       /* adjust width to at least one device pixel */
475       x = y = 1.0;
476       cairo_device_to_user_distance(cairo, &x, &y);
477       width = MIN(fabs(x),fabs(y));
478       adjusted_stroke_width = gTrue;
479     }
480   } else if (width == 0.0) {
481     /* Cairo does not support 0 line width == 1 device pixel. Find out
482      * how big pixels (device unit) are in the x and y
483      * directions. Choose the smaller of the two as our line width.
484      */
485     double x = 1.0, y = 1.0;
486     if (printing) {
487       // assume printer pixel size is 1/600 inch
488       x = 72.0/600;
489       y = 72.0/600;
490     }
491     cairo_device_to_user_distance(cairo, &x, &y);
492     width = MIN(fabs(x),fabs(y));
493   }
494   cairo_set_line_width (cairo, width);
495   if (cairo_shape)
496     cairo_set_line_width (cairo_shape, cairo_get_line_width (cairo));
497 }
498 
updateFillColor(GfxState * state)499 void CairoOutputDev::updateFillColor(GfxState *state) {
500   GfxRGB color = fill_color;
501 
502   if (inUncoloredPattern)
503     return;
504 
505   state->getFillRGB(&fill_color);
506   if (cairo_pattern_get_type (fill_pattern) != CAIRO_PATTERN_TYPE_SOLID ||
507       color.r != fill_color.r ||
508       color.g != fill_color.g ||
509       color.b != fill_color.b)
510   {
511     cairo_pattern_destroy(fill_pattern);
512     fill_pattern = cairo_pattern_create_rgba(colToDbl(fill_color.r),
513 					     colToDbl(fill_color.g),
514 					     colToDbl(fill_color.b),
515 					     fill_opacity);
516 
517     LOG(printf ("fill color: %d %d %d\n",
518 		fill_color.r, fill_color.g, fill_color.b));
519   }
520 }
521 
updateStrokeColor(GfxState * state)522 void CairoOutputDev::updateStrokeColor(GfxState *state) {
523   GfxRGB color = stroke_color;
524 
525   if (inUncoloredPattern)
526     return;
527 
528   state->getStrokeRGB(&stroke_color);
529   if (cairo_pattern_get_type (fill_pattern) != CAIRO_PATTERN_TYPE_SOLID ||
530       color.r != stroke_color.r ||
531       color.g != stroke_color.g ||
532       color.b != stroke_color.b)
533   {
534     cairo_pattern_destroy(stroke_pattern);
535     stroke_pattern = cairo_pattern_create_rgba(colToDbl(stroke_color.r),
536 					       colToDbl(stroke_color.g),
537 					       colToDbl(stroke_color.b),
538 					       stroke_opacity);
539 
540     LOG(printf ("stroke color: %d %d %d\n",
541 		stroke_color.r, stroke_color.g, stroke_color.b));
542   }
543 }
544 
updateFillOpacity(GfxState * state)545 void CairoOutputDev::updateFillOpacity(GfxState *state) {
546   double opacity = fill_opacity;
547 
548   if (inUncoloredPattern)
549     return;
550 
551   fill_opacity = state->getFillOpacity();
552   if (opacity != fill_opacity) {
553     cairo_pattern_destroy(fill_pattern);
554     fill_pattern = cairo_pattern_create_rgba(colToDbl(fill_color.r),
555 					     colToDbl(fill_color.g),
556 					     colToDbl(fill_color.b),
557 					     fill_opacity);
558 
559     LOG(printf ("fill opacity: %f\n", fill_opacity));
560   }
561 }
562 
updateStrokeOpacity(GfxState * state)563 void CairoOutputDev::updateStrokeOpacity(GfxState *state) {
564   double opacity = stroke_opacity;
565 
566   if (inUncoloredPattern)
567     return;
568 
569   stroke_opacity = state->getStrokeOpacity();
570   if (opacity != stroke_opacity) {
571     cairo_pattern_destroy(stroke_pattern);
572     stroke_pattern = cairo_pattern_create_rgba(colToDbl(stroke_color.r),
573 					       colToDbl(stroke_color.g),
574 					       colToDbl(stroke_color.b),
575 					       stroke_opacity);
576 
577     LOG(printf ("stroke opacity: %f\n", stroke_opacity));
578   }
579 }
580 
updateFillColorStop(GfxState * state,double offset)581 void CairoOutputDev::updateFillColorStop(GfxState *state, double offset) {
582   if (inUncoloredPattern)
583     return;
584 
585   state->getFillRGB(&fill_color);
586 
587   cairo_pattern_add_color_stop_rgba(fill_pattern, offset,
588 				    colToDbl(fill_color.r),
589 				    colToDbl(fill_color.g),
590 				    colToDbl(fill_color.b),
591 				    fill_opacity);
592   LOG(printf ("fill color stop: %f (%d, %d, %d)\n",
593 	      offset, fill_color.r, fill_color.g, fill_color.b));
594 }
595 
updateBlendMode(GfxState * state)596 void CairoOutputDev::updateBlendMode(GfxState *state) {
597   switch (state->getBlendMode()) {
598   default:
599   case gfxBlendNormal:
600     cairo_set_operator (cairo, CAIRO_OPERATOR_OVER);
601     break;
602   case gfxBlendMultiply:
603     cairo_set_operator (cairo, CAIRO_OPERATOR_MULTIPLY);
604     break;
605   case gfxBlendScreen:
606     cairo_set_operator (cairo, CAIRO_OPERATOR_SCREEN);
607     break;
608   case gfxBlendOverlay:
609     cairo_set_operator (cairo, CAIRO_OPERATOR_OVERLAY);
610     break;
611   case gfxBlendDarken:
612     cairo_set_operator (cairo, CAIRO_OPERATOR_DARKEN);
613     break;
614   case gfxBlendLighten:
615     cairo_set_operator (cairo, CAIRO_OPERATOR_LIGHTEN);
616     break;
617   case gfxBlendColorDodge:
618     cairo_set_operator (cairo, CAIRO_OPERATOR_COLOR_DODGE);
619     break;
620   case gfxBlendColorBurn:
621     cairo_set_operator (cairo, CAIRO_OPERATOR_COLOR_BURN);
622     break;
623   case gfxBlendHardLight:
624     cairo_set_operator (cairo, CAIRO_OPERATOR_HARD_LIGHT);
625     break;
626   case gfxBlendSoftLight:
627     cairo_set_operator (cairo, CAIRO_OPERATOR_SOFT_LIGHT);
628     break;
629   case gfxBlendDifference:
630     cairo_set_operator (cairo, CAIRO_OPERATOR_DIFFERENCE);
631     break;
632   case gfxBlendExclusion:
633     cairo_set_operator (cairo, CAIRO_OPERATOR_EXCLUSION);
634     break;
635   case gfxBlendHue:
636     cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_HUE);
637     break;
638   case gfxBlendSaturation:
639     cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_SATURATION);
640     break;
641   case gfxBlendColor:
642     cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_COLOR);
643     break;
644   case gfxBlendLuminosity:
645     cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_LUMINOSITY);
646     break;
647   }
648   LOG(printf ("blend mode: %d\n", (int)state->getBlendMode()));
649 }
650 
updateFont(GfxState * state)651 void CairoOutputDev::updateFont(GfxState *state) {
652   cairo_font_face_t *font_face;
653   cairo_matrix_t matrix, invert_matrix;
654 
655   LOG(printf ("updateFont() font=%s\n", state->getFont()->getName()->getCString()));
656 
657   needFontUpdate = gFalse;
658 
659   //FIXME: use cairo font engine?
660   if (text)
661     text->updateFont(state);
662 
663   currentFont = fontEngine->getFont (state->getFont(), doc, printing, xref);
664 
665   if (!currentFont)
666     return;
667 
668   font_face = currentFont->getFontFace();
669   cairo_set_font_face (cairo, font_face);
670 
671   use_show_text_glyphs = state->getFont()->hasToUnicodeCMap() &&
672     cairo_surface_has_show_text_glyphs (cairo_get_target (cairo));
673 
674   double fontSize = state->getFontSize();
675   double *m = state->getTextMat();
676   /* NOTE: adjusting by a constant is hack. The correct solution
677    * is probably to use user-fonts and compute the scale on a per
678    * glyph basis instead of for the entire font */
679   double w = currentFont->getSubstitutionCorrection(state->getFont());
680   matrix.xx = m[0] * fontSize * state->getHorizScaling() * w;
681   matrix.yx = m[1] * fontSize * state->getHorizScaling() * w;
682   matrix.xy = -m[2] * fontSize;
683   matrix.yy = -m[3] * fontSize;
684   matrix.x0 = 0;
685   matrix.y0 = 0;
686 
687   LOG(printf ("font matrix: %f %f %f %f\n", matrix.xx, matrix.yx, matrix.xy, matrix.yy));
688 
689  /* Make sure the font matrix is invertible before setting it.  cairo
690   * will blow up if we give it a matrix that's not invertible, so we
691   * need to check before passing it to cairo_set_font_matrix. Ignoring it
692   * is likely to give better results than not rendering anything at
693   * all. See #18254.
694   */
695   invert_matrix = matrix;
696   if (cairo_matrix_invert(&invert_matrix)) {
697     error(errSyntaxWarning, -1, "font matrix not invertible");
698     text_matrix_valid = gFalse;
699     return;
700   }
701 
702   cairo_set_font_matrix (cairo, &matrix);
703   text_matrix_valid = gTrue;
704 }
705 
706 /* Tolerance in pixels for checking if strokes are horizontal or vertical
707  * lines in device space */
708 #define STROKE_COORD_TOLERANCE 0.5
709 
710 /* Align stroke coordinate i if the point is the start or end of a
711  * horizontal or vertical line */
alignStrokeCoords(GfxSubpath * subpath,int i,double * x,double * y)712 void CairoOutputDev::alignStrokeCoords(GfxSubpath *subpath, int i, double *x, double *y)
713 {
714   double x1, y1, x2, y2;
715   GBool align = gFalse;
716 
717   x1 = subpath->getX(i);
718   y1 = subpath->getY(i);
719   cairo_user_to_device (cairo, &x1, &y1);
720 
721   // Does the current coord and prev coord form a horiz or vert line?
722   if (i > 0 && !subpath->getCurve(i - 1)) {
723     x2 = subpath->getX(i - 1);
724     y2 = subpath->getY(i - 1);
725     cairo_user_to_device (cairo, &x2, &y2);
726     if (fabs(x2 - x1) < STROKE_COORD_TOLERANCE || fabs(y2 - y1) < STROKE_COORD_TOLERANCE)
727       align = gTrue;
728   }
729 
730   // Does the current coord and next coord form a horiz or vert line?
731   if (i < subpath->getNumPoints() - 1 && !subpath->getCurve(i + 1)) {
732     x2 = subpath->getX(i + 1);
733     y2 = subpath->getY(i + 1);
734     cairo_user_to_device (cairo, &x2, &y2);
735     if (fabs(x2 - x1) < STROKE_COORD_TOLERANCE || fabs(y2 - y1) < STROKE_COORD_TOLERANCE)
736       align = gTrue;
737   }
738 
739   *x = subpath->getX(i);
740   *y = subpath->getY(i);
741   if (align) {
742     /* see http://www.cairographics.org/FAQ/#sharp_lines */
743     cairo_user_to_device (cairo, x, y);
744     *x = floor(*x) + 0.5;
745     *y = floor(*y) + 0.5;
746     cairo_device_to_user (cairo, x, y);
747   }
748 }
749 
750 #undef STROKE_COORD_TOLERANCE
751 
doPath(cairo_t * cairo,GfxState * state,GfxPath * path)752 void CairoOutputDev::doPath(cairo_t *cairo, GfxState *state, GfxPath *path) {
753   GfxSubpath *subpath;
754   int i, j;
755   double x, y;
756   cairo_new_path (cairo);
757   for (i = 0; i < path->getNumSubpaths(); ++i) {
758     subpath = path->getSubpath(i);
759     if (subpath->getNumPoints() > 0) {
760       if (align_stroke_coords) {
761         alignStrokeCoords(subpath, 0, &x, &y);
762       } else {
763         x = subpath->getX(0);
764         y = subpath->getY(0);
765       }
766       cairo_move_to (cairo, x, y);
767       j = 1;
768       while (j < subpath->getNumPoints()) {
769 	if (subpath->getCurve(j)) {
770 	  if (align_stroke_coords) {
771             alignStrokeCoords(subpath, j + 2, &x, &y);
772           } else {
773             x = subpath->getX(j+2);
774             y = subpath->getY(j+2);
775           }
776 	  cairo_curve_to( cairo,
777 			  subpath->getX(j), subpath->getY(j),
778 			  subpath->getX(j+1), subpath->getY(j+1),
779 			  x, y);
780 
781 	  j += 3;
782 	} else {
783 	  if (align_stroke_coords) {
784             alignStrokeCoords(subpath, j, &x, &y);
785           } else {
786             x = subpath->getX(j);
787             y = subpath->getY(j);
788           }
789           cairo_line_to (cairo, x, y);
790 	  ++j;
791 	}
792       }
793       if (subpath->isClosed()) {
794 	LOG (printf ("close\n"));
795 	cairo_close_path (cairo);
796       }
797     }
798   }
799 }
800 
stroke(GfxState * state)801 void CairoOutputDev::stroke(GfxState *state) {
802   if (inType3Char) {
803       GfxGray gray;
804       state->getFillGray(&gray);
805       if (colToDbl(gray) > 0.5)
806 	  return;
807   }
808 
809   if (adjusted_stroke_width)
810     align_stroke_coords = gTrue;
811   doPath (cairo, state, state->getPath());
812   align_stroke_coords = gFalse;
813   cairo_set_source (cairo, stroke_pattern);
814   LOG(printf ("stroke\n"));
815   if (strokePathClip) {
816     cairo_push_group (cairo);
817     cairo_stroke (cairo);
818     cairo_pop_group_to_source (cairo);
819     fillToStrokePathClip (state);
820   } else {
821     cairo_stroke (cairo);
822   }
823   if (cairo_shape) {
824     doPath (cairo_shape, state, state->getPath());
825     cairo_stroke (cairo_shape);
826   }
827 }
828 
fill(GfxState * state)829 void CairoOutputDev::fill(GfxState *state) {
830   if (inType3Char) {
831       GfxGray gray;
832       state->getFillGray(&gray);
833       if (colToDbl(gray) > 0.5)
834 	  return;
835   }
836 
837   doPath (cairo, state, state->getPath());
838   cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_WINDING);
839   cairo_set_source (cairo, fill_pattern);
840   LOG(printf ("fill\n"));
841   //XXX: how do we get the path
842   if (mask) {
843     cairo_save (cairo);
844     cairo_clip (cairo);
845     if (strokePathClip) {
846       cairo_push_group (cairo);
847       fillToStrokePathClip (state);
848       cairo_pop_group_to_source (cairo);
849     }
850     cairo_set_matrix (cairo, &mask_matrix);
851     cairo_mask (cairo, mask);
852     cairo_restore (cairo);
853   } else if (strokePathClip) {
854     fillToStrokePathClip(state);
855   } else {
856     cairo_fill (cairo);
857   }
858   if (cairo_shape) {
859     cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_WINDING);
860     doPath (cairo_shape, state, state->getPath());
861     cairo_fill (cairo_shape);
862   }
863 }
864 
eoFill(GfxState * state)865 void CairoOutputDev::eoFill(GfxState *state) {
866   doPath (cairo, state, state->getPath());
867   cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_EVEN_ODD);
868   cairo_set_source (cairo, fill_pattern);
869   LOG(printf ("fill-eo\n"));
870 
871   if (mask) {
872     cairo_save (cairo);
873     cairo_clip (cairo);
874     cairo_set_matrix (cairo, &mask_matrix);
875     cairo_mask (cairo, mask);
876     cairo_restore (cairo);
877   } else {
878     cairo_fill (cairo);
879   }
880   if (cairo_shape) {
881     cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_EVEN_ODD);
882     doPath (cairo_shape, state, state->getPath());
883     cairo_fill (cairo_shape);
884   }
885 
886 }
887 
tilingPatternFill(GfxState * state,Gfx * gfxA,Catalog * cat,Object * str,double * pmat,int paintType,int,Dict * resDict,double * mat,double * bbox,int x0,int y0,int x1,int y1,double xStep,double yStep)888 GBool CairoOutputDev::tilingPatternFill(GfxState *state, Gfx *gfxA, Catalog *cat, Object *str,
889 					double *pmat, int paintType, int /*tilingType*/, Dict *resDict,
890 					double *mat, double *bbox,
891 					int x0, int y0, int x1, int y1,
892 					double xStep, double yStep)
893 {
894   PDFRectangle box;
895   Gfx *gfx;
896   cairo_pattern_t *pattern;
897   cairo_surface_t *surface;
898   cairo_matrix_t matrix;
899   cairo_matrix_t pattern_matrix;
900   cairo_t *old_cairo;
901   double xMin, yMin, xMax, yMax;
902   double width, height;
903   int surface_width, surface_height;
904   StrokePathClip *strokePathTmp;
905   GBool adjusted_stroke_width_tmp;
906   cairo_pattern_t *maskTmp;
907 
908   width = bbox[2] - bbox[0];
909   height = bbox[3] - bbox[1];
910 
911   if (xStep != width || yStep != height)
912     return gFalse;
913   /* TODO: implement the other cases here too */
914 
915   // Find the width and height of the transformed pattern
916   cairo_get_matrix (cairo, &matrix);
917   cairo_matrix_init (&pattern_matrix, mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
918   cairo_matrix_multiply (&matrix, &matrix, &pattern_matrix);
919 
920   double widthX = width, widthY = 0;
921   cairo_matrix_transform_distance (&matrix, &widthX, &widthY);
922   surface_width = ceil (sqrt (widthX * widthX + widthY * widthY));
923 
924   double heightX = 0, heightY = height;
925   cairo_matrix_transform_distance (&matrix, &heightX, &heightY);
926   surface_height = ceil (sqrt (heightX * heightX + heightY * heightY));
927 
928   surface = cairo_surface_create_similar (cairo_get_target (cairo),
929 					  CAIRO_CONTENT_COLOR_ALPHA,
930 					  surface_width, surface_height);
931   if (cairo_surface_status (surface))
932     return gFalse;
933 
934   old_cairo = cairo;
935   cairo = cairo_create (surface);
936   cairo_surface_destroy (surface);
937   setAntialias(cairo, antialias);
938   cairo_scale (cairo, surface_width / width, surface_height / height);
939 
940   box.x1 = bbox[0]; box.y1 = bbox[1];
941   box.x2 = bbox[2]; box.y2 = bbox[3];
942   strokePathTmp = strokePathClip;
943   strokePathClip = NULL;
944   adjusted_stroke_width_tmp = adjusted_stroke_width;
945   maskTmp = mask;
946   mask = NULL;
947   gfx = new Gfx(doc, this, resDict, &box, NULL, NULL, NULL, gfxA->getXRef());
948   if (paintType == 2)
949     inUncoloredPattern = gTrue;
950   gfx->display(str);
951   if (paintType == 2)
952     inUncoloredPattern = gFalse;
953   delete gfx;
954   strokePathClip = strokePathTmp;
955   adjusted_stroke_width = adjusted_stroke_width_tmp;
956   mask = maskTmp;
957 
958   pattern = cairo_pattern_create_for_surface (cairo_get_target (cairo));
959   cairo_destroy (cairo);
960   cairo = old_cairo;
961   if (cairo_pattern_status (pattern))
962     return gFalse;
963 
964   state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
965   cairo_rectangle (cairo, xMin, yMin, xMax - xMin, yMax - yMin);
966 
967   cairo_matrix_init_scale (&matrix, surface_width / width, surface_height / height);
968   cairo_pattern_set_matrix (pattern, &matrix);
969 
970   cairo_transform (cairo, &pattern_matrix);
971   cairo_set_source (cairo, pattern);
972   cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
973   if (strokePathClip) {
974     fillToStrokePathClip(state);
975   } else {
976     cairo_fill (cairo);
977   }
978 
979   cairo_pattern_destroy (pattern);
980 
981   return gTrue;
982 }
983 
984 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0)
functionShadedFill(GfxState * state,GfxFunctionShading * shading)985 GBool CairoOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
986 {
987   // Function shaded fills are subdivided to rectangles that are the
988   // following size in device space.  Note when printing this size is
989   // in points.
990   const int subdivide_pixels = 10;
991 
992   double x_begin, x_end, x1, x2;
993   double y_begin, y_end, y1, y2;
994   double x_step;
995   double y_step;
996   GfxColor color;
997   GfxRGB rgb;
998   double *matrix;
999   cairo_matrix_t mat;
1000 
1001   matrix = shading->getMatrix();
1002   mat.xx = matrix[0];
1003   mat.yx = matrix[1];
1004   mat.xy = matrix[2];
1005   mat.yy = matrix[3];
1006   mat.x0 = matrix[4];
1007   mat.y0 = matrix[5];
1008   if (cairo_matrix_invert(&mat)) {
1009     error(errSyntaxWarning, -1, "matrix not invertible\n");
1010     return gFalse;
1011     }
1012 
1013   // get cell size in pattern space
1014   x_step = y_step = subdivide_pixels;
1015   cairo_matrix_transform_distance (&mat, &x_step, &y_step);
1016 
1017   cairo_pattern_destroy(fill_pattern);
1018   fill_pattern = cairo_pattern_create_mesh ();
1019   cairo_pattern_set_matrix(fill_pattern, &mat);
1020   shading->getDomain(&x_begin, &y_begin, &x_end, &y_end);
1021 
1022   for (x1 = x_begin; x1 < x_end; x1 += x_step) {
1023     x2 = x1 + x_step;
1024     if (x2 > x_end)
1025       x2 = x_end;
1026 
1027     for (y1 = y_begin; y1 < y_end; y1 += y_step) {
1028       y2 = y1 + y_step;
1029       if (y2 > y_end)
1030 	y2 = y_end;
1031 
1032       cairo_mesh_pattern_begin_patch (fill_pattern);
1033       cairo_mesh_pattern_move_to (fill_pattern, x1, y1);
1034       cairo_mesh_pattern_line_to (fill_pattern, x2, y1);
1035       cairo_mesh_pattern_line_to (fill_pattern, x2, y2);
1036       cairo_mesh_pattern_line_to (fill_pattern, x1, y2);
1037 
1038       shading->getColor(x1, y1, &color);
1039       shading->getColorSpace()->getRGB(&color, &rgb);
1040       cairo_mesh_pattern_set_corner_color_rgb (fill_pattern, 0,
1041 					       colToDbl(rgb.r),
1042 					       colToDbl(rgb.g),
1043 					       colToDbl(rgb.b));
1044 
1045       shading->getColor(x2, y1, &color);
1046       shading->getColorSpace()->getRGB(&color, &rgb);
1047       cairo_mesh_pattern_set_corner_color_rgb (fill_pattern, 1,
1048 					       colToDbl(rgb.r),
1049 					       colToDbl(rgb.g),
1050 					       colToDbl(rgb.b));
1051 
1052       shading->getColor(x2, y2, &color);
1053       shading->getColorSpace()->getRGB(&color, &rgb);
1054       cairo_mesh_pattern_set_corner_color_rgb (fill_pattern, 2,
1055 					       colToDbl(rgb.r),
1056 					       colToDbl(rgb.g),
1057 					       colToDbl(rgb.b));
1058 
1059       shading->getColor(x1, y2, &color);
1060       shading->getColorSpace()->getRGB(&color, &rgb);
1061       cairo_mesh_pattern_set_corner_color_rgb (fill_pattern, 3,
1062 					       colToDbl(rgb.r),
1063 					       colToDbl(rgb.g),
1064 					       colToDbl(rgb.b));
1065 
1066       cairo_mesh_pattern_end_patch (fill_pattern);
1067     }
1068   }
1069 
1070   double xMin, yMin, xMax, yMax;
1071   // get the clip region bbox
1072   state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1073   state->moveTo(xMin, yMin);
1074   state->lineTo(xMin, yMax);
1075   state->lineTo(xMax, yMax);
1076   state->lineTo(xMax, yMin);
1077   state->closePath();
1078   fill(state);
1079   state->clearPath();
1080 
1081   return gTrue;
1082 }
1083 #endif /* CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) */
1084 
axialShadedFill(GfxState * state,GfxAxialShading * shading,double tMin,double tMax)1085 GBool CairoOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) {
1086   double x0, y0, x1, y1;
1087   double dx, dy;
1088 
1089   shading->getCoords(&x0, &y0, &x1, &y1);
1090   dx = x1 - x0;
1091   dy = y1 - y0;
1092 
1093   cairo_pattern_destroy(fill_pattern);
1094   fill_pattern = cairo_pattern_create_linear (x0 + tMin * dx, y0 + tMin * dy,
1095 					      x0 + tMax * dx, y0 + tMax * dy);
1096   if (!shading->getExtend0() && !shading->getExtend1())
1097     cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_NONE);
1098   else
1099     cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_PAD);
1100 
1101   LOG (printf ("axial-sh\n"));
1102 
1103   // TODO: use the actual stops in the shading in the case
1104   // of linear interpolation (Type 2 Exponential functions with N=1)
1105   return gFalse;
1106 }
1107 
axialShadedSupportExtend(GfxState * state,GfxAxialShading * shading)1108 GBool CairoOutputDev::axialShadedSupportExtend(GfxState *state, GfxAxialShading *shading)
1109 {
1110   return (shading->getExtend0() == shading->getExtend1());
1111 }
1112 
radialShadedFill(GfxState * state,GfxRadialShading * shading,double sMin,double sMax)1113 GBool CairoOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading, double sMin, double sMax) {
1114   double x0, y0, r0, x1, y1, r1;
1115   double dx, dy, dr;
1116   cairo_matrix_t matrix;
1117   double scale;
1118 
1119   shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
1120   dx = x1 - x0;
1121   dy = y1 - y0;
1122   dr = r1 - r0;
1123 
1124   // Cairo/pixman do not work well with a very large or small scaled
1125   // matrix.  See cairo bug #81657.
1126   //
1127   // As a workaround, scale the pattern by the average of the vertical
1128   // and horizontal scaling of the current transformation matrix.
1129   cairo_get_matrix(cairo, &matrix);
1130   scale = (sqrt(matrix.xx * matrix.xx + matrix.yx * matrix.yx)
1131 	   + sqrt(matrix.xy * matrix.xy + matrix.yy * matrix.yy)) / 2;
1132   cairo_matrix_init_scale(&matrix, scale, scale);
1133 
1134   cairo_pattern_destroy(fill_pattern);
1135   fill_pattern = cairo_pattern_create_radial ((x0 + sMin * dx) * scale,
1136 					      (y0 + sMin * dy) * scale,
1137 					      (r0 + sMin * dr) * scale,
1138 					      (x0 + sMax * dx) * scale,
1139 					      (y0 + sMax * dy) * scale,
1140 					      (r0 + sMax * dr) * scale);
1141   cairo_pattern_set_matrix(fill_pattern, &matrix);
1142   if (shading->getExtend0() && shading->getExtend1())
1143     cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_PAD);
1144   else
1145     cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_NONE);
1146 
1147   LOG (printf ("radial-sh\n"));
1148 
1149   return gFalse;
1150 }
1151 
radialShadedSupportExtend(GfxState * state,GfxRadialShading * shading)1152 GBool CairoOutputDev::radialShadedSupportExtend(GfxState *state, GfxRadialShading *shading)
1153 {
1154   return (shading->getExtend0() == shading->getExtend1());
1155 }
1156 
1157 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0)
gouraudTriangleShadedFill(GfxState * state,GfxGouraudTriangleShading * shading)1158 GBool CairoOutputDev::gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading)
1159 {
1160   double x0, y0, x1, y1, x2, y2;
1161   GfxColor color[3];
1162   int i, j;
1163   GfxRGB rgb;
1164 
1165   cairo_pattern_destroy(fill_pattern);
1166   fill_pattern = cairo_pattern_create_mesh ();
1167 
1168   for (i = 0; i < shading->getNTriangles(); i++) {
1169     if (shading->isParameterized()) {
1170       double color0, color1, color2;
1171       shading->getTriangle(i, &x0, &y0, &color0,
1172                               &x1, &y1, &color1,
1173                               &x2, &y2, &color2);
1174       shading->getParameterizedColor(color0, &color[0]);
1175       shading->getParameterizedColor(color1, &color[1]);
1176       shading->getParameterizedColor(color2, &color[2]);
1177     } else {
1178       shading->getTriangle(i,
1179                            &x0, &y0, &color[0],
1180                            &x1, &y1, &color[1],
1181                            &x2, &y2, &color[2]);
1182 
1183     }
1184 
1185     cairo_mesh_pattern_begin_patch (fill_pattern);
1186 
1187     cairo_mesh_pattern_move_to (fill_pattern, x0, y0);
1188     cairo_mesh_pattern_line_to (fill_pattern, x1, y1);
1189     cairo_mesh_pattern_line_to (fill_pattern, x2, y2);
1190 
1191     for (j = 0; j < 3; j++) {
1192 	shading->getColorSpace()->getRGB(&color[j], &rgb);
1193 	cairo_mesh_pattern_set_corner_color_rgb (fill_pattern, j,
1194 						 colToDbl(rgb.r),
1195 						 colToDbl(rgb.g),
1196 						 colToDbl(rgb.b));
1197     }
1198 
1199     cairo_mesh_pattern_end_patch (fill_pattern);
1200   }
1201 
1202   double xMin, yMin, xMax, yMax;
1203   // get the clip region bbox
1204   state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1205   state->moveTo(xMin, yMin);
1206   state->lineTo(xMin, yMax);
1207   state->lineTo(xMax, yMax);
1208   state->lineTo(xMax, yMin);
1209   state->closePath();
1210   fill(state);
1211   state->clearPath();
1212 
1213   return gTrue;
1214 }
1215 
patchMeshShadedFill(GfxState * state,GfxPatchMeshShading * shading)1216 GBool CairoOutputDev::patchMeshShadedFill(GfxState *state, GfxPatchMeshShading *shading)
1217 {
1218   int i, j, k;
1219 
1220   cairo_pattern_destroy(fill_pattern);
1221   fill_pattern = cairo_pattern_create_mesh ();
1222 
1223   for (i = 0; i < shading->getNPatches(); i++) {
1224     GfxPatch *patch = shading->getPatch(i);
1225     GfxColor color;
1226     GfxRGB rgb;
1227 
1228     cairo_mesh_pattern_begin_patch (fill_pattern);
1229 
1230     cairo_mesh_pattern_move_to (fill_pattern, patch->x[0][0], patch->y[0][0]);
1231     cairo_mesh_pattern_curve_to (fill_pattern,
1232 			    patch->x[0][1], patch->y[0][1],
1233 			    patch->x[0][2], patch->y[0][2],
1234 			    patch->x[0][3], patch->y[0][3]);
1235 
1236     cairo_mesh_pattern_curve_to (fill_pattern,
1237 			    patch->x[1][3], patch->y[1][3],
1238 			    patch->x[2][3], patch->y[2][3],
1239 			    patch->x[3][3], patch->y[3][3]);
1240 
1241     cairo_mesh_pattern_curve_to (fill_pattern,
1242 			    patch->x[3][2], patch->y[3][2],
1243 			    patch->x[3][1], patch->y[3][1],
1244 			    patch->x[3][0], patch->y[3][0]);
1245 
1246     cairo_mesh_pattern_curve_to (fill_pattern,
1247 			    patch->x[2][0], patch->y[2][0],
1248 			    patch->x[1][0], patch->y[1][0],
1249 			    patch->x[0][0], patch->y[0][0]);
1250 
1251     cairo_mesh_pattern_set_control_point (fill_pattern, 0, patch->x[1][1], patch->y[1][1]);
1252     cairo_mesh_pattern_set_control_point (fill_pattern, 1, patch->x[1][2], patch->y[1][2]);
1253     cairo_mesh_pattern_set_control_point (fill_pattern, 2, patch->x[2][2], patch->y[2][2]);
1254     cairo_mesh_pattern_set_control_point (fill_pattern, 3, patch->x[2][1], patch->y[2][1]);
1255 
1256     for (j = 0; j < 4; j++) {
1257       int u, v;
1258 
1259       switch (j) {
1260 	case 0:
1261 	  u = 0; v = 0;
1262 	  break;
1263 	case 1:
1264 	  u = 0; v = 1;
1265 	  break;
1266 	case 2:
1267 	  u = 1; v = 1;
1268 	  break;
1269 	case 3:
1270 	  u = 1; v = 0;
1271 	  break;
1272       }
1273 
1274       if (shading->isParameterized()) {
1275 	shading->getParameterizedColor (patch->color[u][v].c[0], &color);
1276       } else {
1277 	for (k = 0; k < shading->getColorSpace()->getNComps(); k++) {
1278           // simply cast to the desired type; that's all what is needed.
1279 	  color.c[k] = GfxColorComp (patch->color[u][v].c[k]);
1280 	}
1281       }
1282 
1283       shading->getColorSpace()->getRGB(&color, &rgb);
1284       cairo_mesh_pattern_set_corner_color_rgb (fill_pattern, j,
1285 					       colToDbl(rgb.r),
1286 					       colToDbl(rgb.g),
1287 					       colToDbl(rgb.b));
1288     }
1289     cairo_mesh_pattern_end_patch (fill_pattern);
1290   }
1291 
1292   double xMin, yMin, xMax, yMax;
1293   // get the clip region bbox
1294   state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1295   state->moveTo(xMin, yMin);
1296   state->lineTo(xMin, yMax);
1297   state->lineTo(xMax, yMax);
1298   state->lineTo(xMax, yMin);
1299   state->closePath();
1300   fill(state);
1301   state->clearPath();
1302 
1303   return gTrue;
1304 }
1305 #endif /* CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) */
1306 
clip(GfxState * state)1307 void CairoOutputDev::clip(GfxState *state) {
1308   doPath (cairo, state, state->getPath());
1309   cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_WINDING);
1310   cairo_clip (cairo);
1311   LOG (printf ("clip\n"));
1312   if (cairo_shape) {
1313     doPath (cairo_shape, state, state->getPath());
1314     cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_WINDING);
1315     cairo_clip (cairo_shape);
1316   }
1317 }
1318 
eoClip(GfxState * state)1319 void CairoOutputDev::eoClip(GfxState *state) {
1320   doPath (cairo, state, state->getPath());
1321   cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_EVEN_ODD);
1322   cairo_clip (cairo);
1323   LOG (printf ("clip-eo\n"));
1324   if (cairo_shape) {
1325     doPath (cairo_shape, state, state->getPath());
1326     cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_EVEN_ODD);
1327     cairo_clip (cairo_shape);
1328   }
1329 
1330 }
1331 
clipToStrokePath(GfxState * state)1332 void CairoOutputDev::clipToStrokePath(GfxState *state) {
1333   LOG(printf("clip-to-stroke-path\n"));
1334   strokePathClip = (StrokePathClip*)gmalloc (sizeof(*strokePathClip));
1335   strokePathClip->path = state->getPath()->copy();
1336   cairo_get_matrix (cairo, &strokePathClip->ctm);
1337   strokePathClip->line_width = cairo_get_line_width (cairo);
1338   strokePathClip->dash_count = cairo_get_dash_count (cairo);
1339   if (strokePathClip->dash_count) {
1340     strokePathClip->dashes = (double*) gmallocn (sizeof(double), strokePathClip->dash_count);
1341     cairo_get_dash (cairo, strokePathClip->dashes, &strokePathClip->dash_offset);
1342   } else {
1343     strokePathClip->dashes = NULL;
1344   }
1345   strokePathClip->cap = cairo_get_line_cap (cairo);
1346   strokePathClip->join = cairo_get_line_join (cairo);
1347   strokePathClip->miter = cairo_get_miter_limit (cairo);
1348   strokePathClip->ref_count = 1;
1349 }
1350 
fillToStrokePathClip(GfxState * state)1351 void CairoOutputDev::fillToStrokePathClip(GfxState *state) {
1352   cairo_save (cairo);
1353 
1354   cairo_set_matrix (cairo, &strokePathClip->ctm);
1355   cairo_set_line_width (cairo, strokePathClip->line_width);
1356   cairo_set_dash (cairo, strokePathClip->dashes, strokePathClip->dash_count, strokePathClip->dash_offset);
1357   cairo_set_line_cap (cairo, strokePathClip->cap);
1358   cairo_set_line_join (cairo, strokePathClip->join);
1359   cairo_set_miter_limit (cairo, strokePathClip->miter);
1360   doPath (cairo, state, strokePathClip->path);
1361   cairo_stroke (cairo);
1362 
1363   cairo_restore (cairo);
1364 }
1365 
beginString(GfxState * state,GooString * s)1366 void CairoOutputDev::beginString(GfxState *state, GooString *s)
1367 {
1368   int len = s->getLength();
1369 
1370   if (needFontUpdate)
1371     updateFont(state);
1372 
1373   if (!currentFont)
1374     return;
1375 
1376   glyphs = (cairo_glyph_t *) gmallocn (len, sizeof (cairo_glyph_t));
1377   glyphCount = 0;
1378   if (use_show_text_glyphs) {
1379     clusters = (cairo_text_cluster_t *) gmallocn (len, sizeof (cairo_text_cluster_t));
1380     clusterCount = 0;
1381     utf8Max = len*2; // start with twice the number of glyphs. we will realloc if we need more.
1382     utf8 = (char *) gmalloc (utf8Max);
1383     utf8Count = 0;
1384   }
1385 }
1386 
drawChar(GfxState * state,double x,double y,double dx,double dy,double originX,double originY,CharCode code,int nBytes,Unicode * u,int uLen)1387 void CairoOutputDev::drawChar(GfxState *state, double x, double y,
1388 			      double dx, double dy,
1389 			      double originX, double originY,
1390 			      CharCode code, int nBytes, Unicode *u, int uLen)
1391 {
1392   if (currentFont) {
1393     glyphs[glyphCount].index = currentFont->getGlyph (code, u, uLen);
1394     glyphs[glyphCount].x = x - originX;
1395     glyphs[glyphCount].y = y - originY;
1396     glyphCount++;
1397     if (use_show_text_glyphs) {
1398       GooString enc("UTF-8");
1399       UnicodeMap *utf8Map = globalParams->getUnicodeMap(&enc);
1400       if (utf8Max - utf8Count < uLen*6) {
1401         // utf8 encoded characters can be up to 6 bytes
1402 	if (utf8Max > uLen*6)
1403 	  utf8Max *= 2;
1404 	else
1405 	  utf8Max += 2*uLen*6;
1406 	utf8 = (char *) grealloc (utf8, utf8Max);
1407       }
1408       clusters[clusterCount].num_bytes = 0;
1409       for (int i = 0; i < uLen; i++) {
1410 	int size = utf8Map->mapUnicode(u[i], utf8 + utf8Count, utf8Max - utf8Count);
1411 	utf8Count += size;
1412 	clusters[clusterCount].num_bytes += size;
1413       }
1414       clusters[clusterCount].num_glyphs = 1;
1415       clusterCount++;
1416     }
1417   }
1418 
1419   if (!text)
1420     return;
1421   actualText->addChar (state, x, y, dx, dy, code, nBytes, u, uLen);
1422 }
1423 
endString(GfxState * state)1424 void CairoOutputDev::endString(GfxState *state)
1425 {
1426   int render;
1427 
1428   if (!currentFont)
1429     return;
1430 
1431   // endString can be called without a corresponding beginString. If this
1432   // happens glyphs will be null so don't draw anything, just return.
1433   // XXX: OutputDevs should probably not have to deal with this...
1434   if (!glyphs)
1435     return;
1436 
1437   // ignore empty strings and invisible text -- this is used by
1438   // Acrobat Capture
1439   render = state->getRender();
1440   if (render == 3 || glyphCount == 0 || !text_matrix_valid) {
1441     goto finish;
1442   }
1443 
1444   if (!(render & 1)) {
1445     LOG (printf ("fill string\n"));
1446     cairo_set_source (cairo, fill_pattern);
1447     if (use_show_text_glyphs)
1448       cairo_show_text_glyphs (cairo, utf8, utf8Count, glyphs, glyphCount, clusters, clusterCount, (cairo_text_cluster_flags_t)0);
1449     else
1450         cairo_show_glyphs (cairo, glyphs, glyphCount);
1451     if (cairo_shape)
1452       cairo_show_glyphs (cairo_shape, glyphs, glyphCount);
1453   }
1454 
1455   // stroke
1456   if ((render & 3) == 1 || (render & 3) == 2) {
1457     LOG (printf ("stroke string\n"));
1458     cairo_set_source (cairo, stroke_pattern);
1459     cairo_glyph_path (cairo, glyphs, glyphCount);
1460     cairo_stroke (cairo);
1461     if (cairo_shape) {
1462       cairo_glyph_path (cairo_shape, glyphs, glyphCount);
1463       cairo_stroke (cairo_shape);
1464     }
1465   }
1466 
1467   // clip
1468   if ((render & 4)) {
1469     LOG (printf ("clip string\n"));
1470     // append the glyph path to textClipPath.
1471 
1472     // set textClipPath as the currentPath
1473     if (textClipPath) {
1474       cairo_append_path (cairo, textClipPath);
1475       if (cairo_shape) {
1476 	cairo_append_path (cairo_shape, textClipPath);
1477       }
1478       cairo_path_destroy (textClipPath);
1479     }
1480 
1481     // append the glyph path
1482     cairo_glyph_path (cairo, glyphs, glyphCount);
1483 
1484     // move the path back into textClipPath
1485     // and clear the current path
1486     textClipPath = cairo_copy_path (cairo);
1487     cairo_new_path (cairo);
1488     if (cairo_shape) {
1489       cairo_new_path (cairo_shape);
1490     }
1491   }
1492 
1493 finish:
1494   gfree (glyphs);
1495   glyphs = NULL;
1496   if (use_show_text_glyphs) {
1497     gfree (clusters);
1498     clusters = NULL;
1499     gfree (utf8);
1500     utf8 = NULL;
1501   }
1502 }
1503 
1504 
beginType3Char(GfxState * state,double x,double y,double dx,double dy,CharCode code,Unicode * u,int uLen)1505 GBool CairoOutputDev::beginType3Char(GfxState *state, double x, double y,
1506 				      double dx, double dy,
1507 				      CharCode code, Unicode *u, int uLen) {
1508 
1509   cairo_save (cairo);
1510   double *ctm;
1511   cairo_matrix_t matrix;
1512 
1513   ctm = state->getCTM();
1514   matrix.xx = ctm[0];
1515   matrix.yx = ctm[1];
1516   matrix.xy = ctm[2];
1517   matrix.yy = ctm[3];
1518   matrix.x0 = ctm[4];
1519   matrix.y0 = ctm[5];
1520   /* Restore the original matrix and then transform to matrix needed for the
1521    * type3 font. This is ugly but seems to work. Perhaps there is a better way to do it?*/
1522   cairo_set_matrix(cairo, &orig_matrix);
1523   cairo_transform(cairo, &matrix);
1524   if (cairo_shape) {
1525     cairo_save (cairo_shape);
1526     cairo_set_matrix(cairo_shape, &orig_matrix);
1527     cairo_transform(cairo_shape, &matrix);
1528   }
1529   cairo_pattern_destroy(stroke_pattern);
1530   cairo_pattern_reference(fill_pattern);
1531   stroke_pattern = fill_pattern;
1532   return gFalse;
1533 }
1534 
endType3Char(GfxState * state)1535 void CairoOutputDev::endType3Char(GfxState *state) {
1536   cairo_restore (cairo);
1537   if (cairo_shape) {
1538     cairo_restore (cairo_shape);
1539   }
1540 }
1541 
type3D0(GfxState * state,double wx,double wy)1542 void CairoOutputDev::type3D0(GfxState *state, double wx, double wy) {
1543   t3_glyph_wx = wx;
1544   t3_glyph_wy = wy;
1545 }
1546 
type3D1(GfxState * state,double wx,double wy,double llx,double lly,double urx,double ury)1547 void CairoOutputDev::type3D1(GfxState *state, double wx, double wy,
1548 			     double llx, double lly, double urx, double ury) {
1549   t3_glyph_wx = wx;
1550   t3_glyph_wy = wy;
1551   t3_glyph_bbox[0] = llx;
1552   t3_glyph_bbox[1] = lly;
1553   t3_glyph_bbox[2] = urx;
1554   t3_glyph_bbox[3] = ury;
1555   t3_glyph_has_bbox = gTrue;
1556 }
1557 
beginTextObject(GfxState * state)1558 void CairoOutputDev::beginTextObject(GfxState *state) {
1559 }
1560 
endTextObject(GfxState * state)1561 void CairoOutputDev::endTextObject(GfxState *state) {
1562   if (textClipPath) {
1563     // clip the accumulated text path
1564     cairo_append_path (cairo, textClipPath);
1565     cairo_clip (cairo);
1566     if (cairo_shape) {
1567       cairo_append_path (cairo_shape, textClipPath);
1568       cairo_clip (cairo_shape);
1569     }
1570     cairo_path_destroy (textClipPath);
1571     textClipPath = NULL;
1572   }
1573 }
1574 
beginActualText(GfxState * state,GooString * text)1575 void CairoOutputDev::beginActualText(GfxState *state, GooString *text)
1576 {
1577   if (this->text)
1578     actualText->begin(state, text);
1579 }
1580 
endActualText(GfxState * state)1581 void CairoOutputDev::endActualText(GfxState *state)
1582 {
1583   if (text)
1584     actualText->end(state);
1585 }
1586 
splashRound(SplashCoord x)1587 static inline int splashRound(SplashCoord x) {
1588   return (int)floor(x + 0.5);
1589 }
1590 
splashCeil(SplashCoord x)1591 static inline int splashCeil(SplashCoord x) {
1592   return (int)ceil(x);
1593 }
1594 
splashFloor(SplashCoord x)1595 static inline int splashFloor(SplashCoord x) {
1596   return (int)floor(x);
1597 }
1598 
1599 static
cairo_surface_create_similar_clip(cairo_t * cairo,cairo_content_t content)1600 cairo_surface_t *cairo_surface_create_similar_clip (cairo_t *cairo, cairo_content_t content)
1601 {
1602   cairo_pattern_t *pattern;
1603   cairo_surface_t *surface = NULL;
1604 
1605   cairo_push_group_with_content (cairo, content);
1606   pattern = cairo_pop_group (cairo);
1607   cairo_pattern_get_surface (pattern, &surface);
1608   cairo_surface_reference (surface);
1609   cairo_pattern_destroy (pattern);
1610   return surface;
1611 }
1612 
1613 
1614 
beginTransparencyGroup(GfxState *,double *,GfxColorSpace * blendingColorSpace,GBool,GBool knockout,GBool forSoftMask)1615 void CairoOutputDev::beginTransparencyGroup(GfxState * /*state*/, double * /*bbox*/,
1616                                       GfxColorSpace * blendingColorSpace,
1617                                       GBool /*isolated*/, GBool knockout,
1618 				      GBool forSoftMask) {
1619   /* push color space */
1620   ColorSpaceStack* css = new ColorSpaceStack;
1621   css->cs = blendingColorSpace;
1622   css->knockout = knockout;
1623   cairo_get_matrix(cairo, &css->group_matrix);
1624   css->next = groupColorSpaceStack;
1625   groupColorSpaceStack = css;
1626 
1627   LOG(printf ("begin transparency group. knockout: %s\n", knockout ? "yes":"no"));
1628 
1629   if (knockout) {
1630     knockoutCount++;
1631     if (!cairo_shape) {
1632       /* create a surface for tracking the shape */
1633       cairo_surface_t *cairo_shape_surface = cairo_surface_create_similar_clip (cairo, CAIRO_CONTENT_ALPHA);
1634       cairo_shape = cairo_create (cairo_shape_surface);
1635       cairo_surface_destroy (cairo_shape_surface);
1636       setAntialias(cairo_shape, antialias);
1637 
1638       /* the color doesn't matter as long as it is opaque */
1639       cairo_set_source_rgb (cairo_shape, 0, 0, 0);
1640       cairo_matrix_t matrix;
1641       cairo_get_matrix (cairo, &matrix);
1642       //printMatrix(&matrix);
1643       cairo_set_matrix (cairo_shape, &matrix);
1644     }
1645   }
1646   if (groupColorSpaceStack->next && groupColorSpaceStack->next->knockout) {
1647     /* we need to track the shape */
1648     cairo_push_group (cairo_shape);
1649   }
1650   if (0 && forSoftMask)
1651     cairo_push_group_with_content (cairo, CAIRO_CONTENT_ALPHA);
1652   else
1653     cairo_push_group (cairo);
1654 
1655   /* push_group has an implicit cairo_save() */
1656   if (knockout) {
1657     /*XXX: let's hope this matches the semantics needed */
1658     cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
1659   } else {
1660     cairo_set_operator(cairo, CAIRO_OPERATOR_OVER);
1661   }
1662 }
1663 
endTransparencyGroup(GfxState *)1664 void CairoOutputDev::endTransparencyGroup(GfxState * /*state*/) {
1665   if (group)
1666     cairo_pattern_destroy(group);
1667   group = cairo_pop_group (cairo);
1668 
1669   LOG(printf ("end transparency group\n"));
1670 
1671   if (groupColorSpaceStack->next && groupColorSpaceStack->next->knockout) {
1672     if (shape)
1673       cairo_pattern_destroy(shape);
1674     shape = cairo_pop_group (cairo_shape);
1675   }
1676 }
1677 
paintTransparencyGroup(GfxState *,double *)1678 void CairoOutputDev::paintTransparencyGroup(GfxState * /*state*/, double * /*bbox*/) {
1679   LOG(printf ("paint transparency group\n"));
1680 
1681   cairo_save (cairo);
1682   cairo_set_matrix (cairo, &groupColorSpaceStack->group_matrix);
1683 
1684   if (shape) {
1685     /* OPERATOR_SOURCE w/ a mask is defined as (src IN mask) ADD (dest OUT mask)
1686      * however our source has already been clipped to mask so we only need to
1687      * do ADD and OUT */
1688 
1689     /* clear the shape mask */
1690     cairo_set_source (cairo, shape);
1691     cairo_set_operator (cairo, CAIRO_OPERATOR_DEST_OUT);
1692     cairo_paint (cairo);
1693     cairo_set_operator (cairo, CAIRO_OPERATOR_ADD);
1694   }
1695   cairo_set_source (cairo, group);
1696 
1697   if (!mask) {
1698     cairo_paint_with_alpha (cairo, fill_opacity);
1699     cairo_status_t status = cairo_status(cairo);
1700     if (status)
1701       printf("BAD status: %s\n", cairo_status_to_string(status));
1702   } else {
1703     if (fill_opacity < 1.0) {
1704       cairo_push_group(cairo);
1705     }
1706     cairo_save(cairo);
1707     cairo_set_matrix(cairo, &mask_matrix);
1708     cairo_mask(cairo, mask);
1709     cairo_restore(cairo);
1710     if (fill_opacity < 1.0) {
1711       cairo_pop_group_to_source(cairo);
1712       cairo_paint_with_alpha (cairo, fill_opacity);
1713     }
1714     cairo_pattern_destroy(mask);
1715     mask = NULL;
1716   }
1717 
1718   if (shape) {
1719     if (cairo_shape) {
1720       cairo_set_source (cairo_shape, shape);
1721       cairo_paint (cairo_shape);
1722       cairo_set_source_rgb (cairo_shape, 0, 0, 0);
1723     }
1724     cairo_pattern_destroy (shape);
1725     shape = NULL;
1726   }
1727 
1728   popTransparencyGroup();
1729   cairo_restore(cairo);
1730 }
1731 
luminocity(uint32_t x)1732 static int luminocity(uint32_t x)
1733 {
1734   int r = (x >> 16) & 0xff;
1735   int g = (x >>  8) & 0xff;
1736   int b = (x >>  0) & 0xff;
1737   // an arbitrary integer approximation of .3*r + .59*g + .11*b
1738   int y = (r*19661+g*38666+b*7209 + 32829)>>16;
1739   return y;
1740 }
1741 
1742 
1743 /* XXX: do we need to deal with shape here? */
setSoftMask(GfxState * state,double * bbox,GBool alpha,Function * transferFunc,GfxColor * backdropColor)1744 void CairoOutputDev::setSoftMask(GfxState * state, double * bbox, GBool alpha,
1745                                  Function * transferFunc, GfxColor * backdropColor) {
1746   cairo_pattern_destroy(mask);
1747 
1748   LOG(printf ("set softMask\n"));
1749 
1750   if (!alpha || transferFunc) {
1751     /* We need to mask according to the luminocity of the group.
1752      * So we paint the group to an image surface convert it to a luminocity map
1753      * and then use that as the mask. */
1754 
1755     /* Get clip extents in device space */
1756     double x1, y1, x2, y2, x_min, y_min, x_max, y_max;
1757     cairo_clip_extents(cairo, &x1, &y1, &x2, &y2);
1758     cairo_user_to_device(cairo, &x1, &y1);
1759     cairo_user_to_device(cairo, &x2, &y2);
1760     x_min = MIN(x1, x2);
1761     y_min = MIN(y1, y2);
1762     x_max = MAX(x1, x2);
1763     y_max = MAX(y1, y2);
1764     cairo_clip_extents(cairo, &x1, &y1, &x2, &y2);
1765     cairo_user_to_device(cairo, &x1, &y2);
1766     cairo_user_to_device(cairo, &x2, &y1);
1767     x_min = MIN(x_min,MIN(x1, x2));
1768     y_min = MIN(y_min,MIN(y1, y2));
1769     x_max = MAX(x_max,MAX(x1, x2));
1770     y_max = MAX(y_max,MAX(y1, y2));
1771 
1772     int width = (int)(ceil(x_max) - floor(x_min));
1773     int height = (int)(ceil(y_max) - floor(y_min));
1774 
1775     /* Get group device offset */
1776     double x_offset, y_offset;
1777     if (cairo_get_group_target(cairo) == cairo_get_target(cairo)) {
1778       cairo_surface_get_device_offset(cairo_get_group_target(cairo), &x_offset, &y_offset);
1779     } else {
1780       cairo_surface_t *pats;
1781       cairo_pattern_get_surface(group, &pats);
1782       cairo_surface_get_device_offset(pats, &x_offset, &y_offset);
1783     }
1784 
1785     /* Adjust extents by group offset */
1786     x_min += x_offset;
1787     y_min += y_offset;
1788 
1789     cairo_surface_t *source = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
1790     cairo_t *maskCtx = cairo_create(source);
1791     setAntialias(maskCtx, antialias);
1792 
1793     //XXX: hopefully this uses the correct color space */
1794     if (!alpha && groupColorSpaceStack->cs) {
1795       GfxRGB backdropColorRGB;
1796       groupColorSpaceStack->cs->getRGB(backdropColor, &backdropColorRGB);
1797       /* paint the backdrop */
1798       cairo_set_source_rgb(maskCtx,
1799                            colToDbl(backdropColorRGB.r),
1800                            colToDbl(backdropColorRGB.g),
1801                            colToDbl(backdropColorRGB.b));
1802     }
1803     cairo_paint(maskCtx);
1804 
1805     /* Copy source ctm to mask ctm and translate origin so that the
1806      * mask appears it the same location on the source surface.  */
1807     cairo_matrix_t mat, tmat;
1808     cairo_matrix_init_translate(&tmat, -x_min, -y_min);
1809     cairo_get_matrix(cairo, &mat);
1810     cairo_matrix_multiply(&mat, &mat, &tmat);
1811     cairo_set_matrix(maskCtx, &mat);
1812 
1813     /* make the device offset of the new mask match that of the group */
1814     cairo_surface_set_device_offset(source, x_offset, y_offset);
1815 
1816     /* paint the group */
1817     cairo_set_source(maskCtx, group);
1818     cairo_paint(maskCtx);
1819 
1820     /* XXX status = cairo_status(maskCtx); */
1821     cairo_destroy(maskCtx);
1822 
1823     /* convert to a luminocity map */
1824     uint32_t *source_data = (uint32_t*)cairo_image_surface_get_data(source);
1825     /* get stride in units of 32 bits */
1826     int stride = cairo_image_surface_get_stride(source)/4;
1827     for (int y=0; y<height; y++) {
1828       for (int x=0; x<width; x++) {
1829 	int lum = alpha ? fill_opacity : luminocity(source_data[y*stride + x]);
1830         if (transferFunc) {
1831           double lum_in, lum_out;
1832           lum_in = lum/256.0;
1833           transferFunc->transform(&lum_in, &lum_out);
1834           lum = (int)(lum_out * 255.0 + 0.5);
1835         }
1836         source_data[y*stride + x] = lum << 24;
1837       }
1838     }
1839     cairo_surface_mark_dirty (source);
1840 
1841     /* setup the new mask pattern */
1842     mask = cairo_pattern_create_for_surface(source);
1843     cairo_get_matrix(cairo, &mask_matrix);
1844 
1845     if (cairo_get_group_target(cairo) == cairo_get_target(cairo)) {
1846       cairo_pattern_set_matrix(mask, &mat);
1847     } else {
1848       cairo_matrix_t patMatrix;
1849       cairo_pattern_get_matrix(group, &patMatrix);
1850       /* Apply x_min, y_min offset to it appears in the same location as source. */
1851       cairo_matrix_multiply(&patMatrix, &patMatrix, &tmat);
1852       cairo_pattern_set_matrix(mask, &patMatrix);
1853     }
1854 
1855     cairo_surface_destroy(source);
1856   } else if (alpha) {
1857     mask = cairo_pattern_reference(group);
1858     cairo_get_matrix(cairo, &mask_matrix);
1859   }
1860 
1861   popTransparencyGroup();
1862 }
1863 
popTransparencyGroup()1864 void CairoOutputDev::popTransparencyGroup() {
1865   /* pop color space */
1866   ColorSpaceStack *css = groupColorSpaceStack;
1867   if (css->knockout) {
1868     knockoutCount--;
1869     if (!knockoutCount) {
1870       /* we don't need to track the shape anymore because
1871        * we are not above any knockout groups */
1872       cairo_destroy(cairo_shape);
1873       cairo_shape = NULL;
1874     }
1875   }
1876   groupColorSpaceStack = css->next;
1877   delete css;
1878 }
1879 
1880 
clearSoftMask(GfxState *)1881 void CairoOutputDev::clearSoftMask(GfxState * /*state*/) {
1882   if (mask)
1883     cairo_pattern_destroy(mask);
1884   mask = NULL;
1885 }
1886 
1887 /* Taken from cairo/doc/tutorial/src/singular.c */
1888 static void
get_singular_values(const cairo_matrix_t * matrix,double * major,double * minor)1889 get_singular_values (const cairo_matrix_t *matrix,
1890 		     double               *major,
1891 		     double               *minor)
1892 {
1893 	double xx = matrix->xx, xy = matrix->xy;
1894 	double yx = matrix->yx, yy = matrix->yy;
1895 
1896 	double a = xx*xx+yx*yx;
1897 	double b = xy*xy+yy*yy;
1898 	double k = xx*xy+yx*yy;
1899 
1900 	double f = (a+b) * .5;
1901 	double g = (a-b) * .5;
1902 	double delta = sqrt (g*g + k*k);
1903 
1904 	if (major)
1905 		*major = sqrt (f + delta);
1906 	if (minor)
1907 		*minor = sqrt (f - delta);
1908 }
1909 
getScaledSize(const cairo_matrix_t * matrix,int orig_width,int orig_height,int * scaledWidth,int * scaledHeight)1910 void CairoOutputDev::getScaledSize(const cairo_matrix_t *matrix,
1911                                    int                   orig_width,
1912 				   int                   orig_height,
1913 				   int                  *scaledWidth,
1914 				   int                  *scaledHeight)
1915 {
1916   double xScale;
1917   double yScale;
1918   if (orig_width > orig_height)
1919     get_singular_values (matrix, &xScale, &yScale);
1920   else
1921     get_singular_values (matrix, &yScale, &xScale);
1922 
1923   int tx, tx2, ty, ty2; /* the integer co-oridinates of the resulting image */
1924   if (xScale >= 0) {
1925     tx = splashRound(matrix->x0 - 0.01);
1926     tx2 = splashRound(matrix->x0 + xScale + 0.01) - 1;
1927   } else {
1928     tx = splashRound(matrix->x0 + 0.01) - 1;
1929     tx2 = splashRound(matrix->x0 + xScale - 0.01);
1930   }
1931   *scaledWidth = abs(tx2 - tx) + 1;
1932   //scaledWidth = splashRound(fabs(xScale));
1933   if (*scaledWidth == 0) {
1934     // technically, this should draw nothing, but it generally seems
1935     // better to draw a one-pixel-wide stripe rather than throwing it
1936     // away
1937     *scaledWidth = 1;
1938   }
1939   if (yScale >= 0) {
1940     ty = splashFloor(matrix->y0 + 0.01);
1941     ty2 = splashCeil(matrix->y0 + yScale - 0.01);
1942   } else {
1943     ty = splashCeil(matrix->y0 - 0.01);
1944     ty2 = splashFloor(matrix->y0 + yScale + 0.01);
1945   }
1946   *scaledHeight = abs(ty2 - ty);
1947   if (*scaledHeight == 0) {
1948     *scaledHeight = 1;
1949   }
1950 }
1951 
1952 cairo_filter_t
getFilterForSurface(cairo_surface_t * image,GBool interpolate)1953 CairoOutputDev::getFilterForSurface(cairo_surface_t *image,
1954 				    GBool interpolate)
1955 {
1956   if (interpolate)
1957     return CAIRO_FILTER_BILINEAR;
1958 
1959   int orig_width = cairo_image_surface_get_width (image);
1960   int orig_height = cairo_image_surface_get_height (image);
1961   if (orig_width == 0 || orig_height == 0)
1962 	  return CAIRO_FILTER_NEAREST;
1963 
1964   /* When printing, don't change the interpolation. */
1965   if (printing)
1966     return CAIRO_FILTER_NEAREST;
1967 
1968   cairo_matrix_t matrix;
1969   cairo_get_matrix(cairo, &matrix);
1970   int scaled_width, scaled_height;
1971   getScaledSize (&matrix, orig_width, orig_height, &scaled_width, &scaled_height);
1972 
1973   /* When scale factor is >= 400% we don't interpolate. See bugs #25268, #9860 */
1974   if (scaled_width / orig_width >= 4 || scaled_height / orig_height >= 4)
1975 	  return CAIRO_FILTER_NEAREST;
1976 
1977   return CAIRO_FILTER_BILINEAR;
1978 }
1979 
drawImageMask(GfxState * state,Object * ref,Stream * str,int width,int height,GBool invert,GBool interpolate,GBool inlineImg)1980 void CairoOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1981 				   int width, int height, GBool invert,
1982 				   GBool interpolate, GBool inlineImg) {
1983 
1984   /* FIXME: Doesn't the image mask support any colorspace? */
1985   cairo_set_source (cairo, fill_pattern);
1986 
1987   /* work around a cairo bug when scaling 1x1 surfaces */
1988   if (width == 1 && height == 1) {
1989     ImageStream *imgStr;
1990     Guchar pix;
1991     int invert_bit;
1992 
1993     imgStr = new ImageStream(str, width, 1, 1);
1994     imgStr->reset();
1995     imgStr->getPixel(&pix);
1996     imgStr->close();
1997     delete imgStr;
1998 
1999     invert_bit = invert ? 1 : 0;
2000     if (pix ^ invert_bit)
2001       return;
2002 
2003     cairo_save (cairo);
2004     cairo_rectangle (cairo, 0., 0., width, height);
2005     cairo_fill (cairo);
2006     cairo_restore (cairo);
2007     if (cairo_shape) {
2008       cairo_save (cairo_shape);
2009       cairo_rectangle (cairo_shape, 0., 0., width, height);
2010       cairo_fill (cairo_shape);
2011       cairo_restore (cairo_shape);
2012     }
2013     return;
2014   }
2015 
2016   /* shape is 1.0 for painted areas, 0.0 for unpainted ones */
2017 
2018   cairo_matrix_t matrix;
2019   cairo_get_matrix (cairo, &matrix);
2020   //XXX: it is possible that we should only do sub pixel positioning if
2021   // we are rendering fonts */
2022   if (!printing && prescaleImages
2023       /* not rotated */
2024       && matrix.xy == 0 && matrix.yx == 0
2025       /* axes not flipped / not 180 deg rotated */
2026       && matrix.xx > 0 && (upsideDown() ? -1 : 1) * matrix.yy > 0) {
2027     drawImageMaskPrescaled(state, ref, str, width, height, invert, interpolate, inlineImg);
2028   } else {
2029     drawImageMaskRegular(state, ref, str, width, height, invert, interpolate, inlineImg);
2030   }
2031 
2032 }
2033 
setSoftMaskFromImageMask(GfxState * state,Object * ref,Stream * str,int width,int height,GBool invert,GBool inlineImg,double * baseMatrix)2034 void CairoOutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str,
2035 				   int width, int height, GBool invert,
2036 				   GBool inlineImg, double *baseMatrix) {
2037 
2038   /* FIXME: Doesn't the image mask support any colorspace? */
2039   cairo_set_source (cairo, fill_pattern);
2040 
2041   /* work around a cairo bug when scaling 1x1 surfaces */
2042   if (width == 1 && height == 1) {
2043     ImageStream *imgStr;
2044     Guchar pix;
2045     int invert_bit;
2046 
2047     imgStr = new ImageStream(str, width, 1, 1);
2048     imgStr->reset();
2049     imgStr->getPixel(&pix);
2050     imgStr->close();
2051     delete imgStr;
2052 
2053     invert_bit = invert ? 1 : 0;
2054     if (!(pix ^ invert_bit)) {
2055       cairo_save (cairo);
2056       cairo_rectangle (cairo, 0., 0., width, height);
2057       cairo_fill (cairo);
2058       cairo_restore (cairo);
2059       if (cairo_shape) {
2060         cairo_save (cairo_shape);
2061         cairo_rectangle (cairo_shape, 0., 0., width, height);
2062         cairo_fill (cairo_shape);
2063         cairo_restore (cairo_shape);
2064       }
2065     }
2066   } else {
2067     cairo_push_group_with_content (cairo, CAIRO_CONTENT_ALPHA);
2068 
2069     /* shape is 1.0 for painted areas, 0.0 for unpainted ones */
2070 
2071     cairo_matrix_t matrix;
2072     cairo_get_matrix (cairo, &matrix);
2073     //XXX: it is possible that we should only do sub pixel positioning if
2074     // we are rendering fonts */
2075     if (!printing && prescaleImages && matrix.xy == 0.0 && matrix.yx == 0.0) {
2076       drawImageMaskPrescaled(state, ref, str, width, height, invert, gFalse, inlineImg);
2077     } else {
2078       drawImageMaskRegular(state, ref, str, width, height, invert, gFalse, inlineImg);
2079     }
2080 
2081     if (state->getFillColorSpace()->getMode() == csPattern) {
2082       cairo_set_source_rgb (cairo, 1, 1, 1);
2083       cairo_set_matrix (cairo, &mask_matrix);
2084       cairo_mask (cairo, mask);
2085     }
2086 
2087     if (mask)
2088       cairo_pattern_destroy (mask);
2089     mask = cairo_pop_group (cairo);
2090   }
2091 
2092   saveState(state);
2093   double bbox[4] = {0,0,1,1}; // dummy
2094   beginTransparencyGroup(state, bbox, state->getFillColorSpace(),
2095                          gTrue, gFalse, gFalse);
2096 }
2097 
unsetSoftMaskFromImageMask(GfxState * state,double * baseMatrix)2098 void CairoOutputDev::unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix) {
2099   double bbox[4] = {0,0,1,1}; // dummy
2100 
2101   endTransparencyGroup(state);
2102   restoreState(state);
2103   paintTransparencyGroup(state, bbox);
2104   clearSoftMask(state);
2105 }
2106 
drawImageMaskRegular(GfxState * state,Object * ref,Stream * str,int width,int height,GBool invert,GBool interpolate,GBool inlineImg)2107 void CairoOutputDev::drawImageMaskRegular(GfxState *state, Object *ref, Stream *str,
2108 					  int width, int height, GBool invert,
2109 					  GBool interpolate, GBool inlineImg) {
2110   unsigned char *buffer;
2111   unsigned char *dest;
2112   cairo_surface_t *image;
2113   cairo_pattern_t *pattern;
2114   int x, y, i, bit;
2115   ImageStream *imgStr;
2116   Guchar *pix;
2117   cairo_matrix_t matrix;
2118   int invert_bit;
2119   int row_stride;
2120   cairo_filter_t filter;
2121 
2122   /* TODO: Do we want to cache these? */
2123   imgStr = new ImageStream(str, width, 1, 1);
2124   imgStr->reset();
2125 
2126   image = cairo_image_surface_create (CAIRO_FORMAT_A1, width, height);
2127   if (cairo_surface_status (image))
2128     goto cleanup;
2129 
2130   buffer = cairo_image_surface_get_data (image);
2131   row_stride = cairo_image_surface_get_stride (image);
2132 
2133   invert_bit = invert ? 1 : 0;
2134 
2135   for (y = 0; y < height; y++) {
2136     pix = imgStr->getLine();
2137     dest = buffer + y * row_stride;
2138     i = 0;
2139     bit = 0;
2140     for (x = 0; x < width; x++) {
2141       if (bit == 0)
2142 	dest[i] = 0;
2143       if (!(pix[x] ^ invert_bit)) {
2144 #ifdef WORDS_BIGENDIAN
2145 	dest[i] |= (1 << (7 - bit));
2146 #else
2147 	dest[i] |= (1 << bit);
2148 #endif
2149       }
2150       bit++;
2151       if (bit > 7) {
2152 	bit = 0;
2153 	i++;
2154       }
2155     }
2156   }
2157 
2158   filter = getFilterForSurface (image, interpolate);
2159 
2160   cairo_surface_mark_dirty (image);
2161   pattern = cairo_pattern_create_for_surface (image);
2162   cairo_surface_destroy (image);
2163   if (cairo_pattern_status (pattern))
2164     goto cleanup;
2165 
2166   LOG (printf ("drawImageMask %dx%d\n", width, height));
2167 
2168   cairo_pattern_set_filter (pattern, filter);
2169 
2170   if (!printing)
2171     cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
2172 
2173   cairo_matrix_init_translate (&matrix, 0, height);
2174   cairo_matrix_scale (&matrix, width, -height);
2175   cairo_pattern_set_matrix (pattern, &matrix);
2176   if (cairo_pattern_status (pattern)) {
2177     cairo_pattern_destroy (pattern);
2178     goto cleanup;
2179   }
2180 
2181   if (state->getFillColorSpace()->getMode() == csPattern) {
2182     mask = cairo_pattern_reference (pattern);
2183     cairo_get_matrix (cairo, &mask_matrix);
2184   } else if (!printing) {
2185     cairo_save (cairo);
2186     cairo_rectangle (cairo, 0., 0., 1., 1.);
2187     cairo_clip (cairo);
2188     cairo_mask (cairo, pattern);
2189     cairo_restore (cairo);
2190   } else {
2191     cairo_mask (cairo, pattern);
2192   }
2193 
2194   if (cairo_shape) {
2195     cairo_save (cairo_shape);
2196     cairo_set_source (cairo_shape, pattern);
2197     if (!printing) {
2198       cairo_rectangle (cairo_shape, 0., 0., 1., 1.);
2199       cairo_fill (cairo_shape);
2200     } else {
2201       cairo_mask (cairo_shape, pattern);
2202     }
2203     cairo_restore (cairo_shape);
2204   }
2205 
2206   cairo_pattern_destroy (pattern);
2207 
2208 cleanup:
2209   imgStr->close();
2210   delete imgStr;
2211 }
2212 
2213 
drawImageMaskPrescaled(GfxState * state,Object * ref,Stream * str,int width,int height,GBool invert,GBool interpolate,GBool inlineImg)2214 void CairoOutputDev::drawImageMaskPrescaled(GfxState *state, Object *ref, Stream *str,
2215 					    int width, int height, GBool invert,
2216 					    GBool interpolate, GBool inlineImg) {
2217   unsigned char *buffer;
2218   cairo_surface_t *image;
2219   cairo_pattern_t *pattern;
2220   ImageStream *imgStr;
2221   Guchar *pix;
2222   cairo_matrix_t matrix;
2223   int invert_bit;
2224   int row_stride;
2225 
2226   /* cairo does a very poor job of scaling down images so we scale them ourselves */
2227 
2228   LOG (printf ("drawImageMaskPrescaled %dx%d\n", width, height));
2229 
2230   /* this scaling code is adopted from the splash image scaling code */
2231   cairo_get_matrix(cairo, &matrix);
2232 #if 0
2233   printf("[%f %f], [%f %f], %f %f\n", matrix.xx, matrix.xy, matrix.yx, matrix.yy, matrix.x0, matrix.y0);
2234 #endif
2235   /* this whole computation should be factored out */
2236   double xScale = matrix.xx;
2237   double yScale = matrix.yy;
2238   int tx, tx2, ty, ty2; /* the integer co-oridinates of the resulting image */
2239   int scaledHeight;
2240   int scaledWidth;
2241   if (xScale >= 0) {
2242     tx = splashRound(matrix.x0 - 0.01);
2243     tx2 = splashRound(matrix.x0 + xScale + 0.01) - 1;
2244   } else {
2245     tx = splashRound(matrix.x0 + 0.01) - 1;
2246     tx2 = splashRound(matrix.x0 + xScale - 0.01);
2247   }
2248   scaledWidth = abs(tx2 - tx) + 1;
2249   //scaledWidth = splashRound(fabs(xScale));
2250   if (scaledWidth == 0) {
2251     // technically, this should draw nothing, but it generally seems
2252     // better to draw a one-pixel-wide stripe rather than throwing it
2253     // away
2254     scaledWidth = 1;
2255   }
2256   if (yScale >= 0) {
2257     ty = splashFloor(matrix.y0 + 0.01);
2258     ty2 = splashCeil(matrix.y0 + yScale - 0.01);
2259   } else {
2260     ty = splashCeil(matrix.y0 - 0.01);
2261     ty2 = splashFloor(matrix.y0 + yScale + 0.01);
2262   }
2263   scaledHeight = abs(ty2 - ty);
2264   if (scaledHeight == 0) {
2265     scaledHeight = 1;
2266   }
2267 #if 0
2268   printf("xscale: %g, yscale: %g\n", xScale, yScale);
2269   printf("width: %d, height: %d\n", width, height);
2270   printf("scaledWidth: %d, scaledHeight: %d\n", scaledWidth, scaledHeight);
2271 #endif
2272 
2273   /* compute the required padding */
2274   /* Padding is used to preserve the aspect ratio.
2275      We compute total_pad to make (height+total_pad)/scaledHeight as close to height/yScale as possible */
2276   int head_pad = 0;
2277   int tail_pad = 0;
2278   int total_pad = splashRound(height*(scaledHeight/fabs(yScale)) - height);
2279 
2280   /* compute the two pieces of padding */
2281   if (total_pad > 0) {
2282     //XXX: i'm not positive fabs() is correct
2283     float tail_error = fabs(matrix.y0 - ty);
2284     float head_error = fabs(ty2 - (matrix.y0 + yScale));
2285     float tail_fraction = tail_error/(tail_error + head_error);
2286     tail_pad = splashRound(total_pad*tail_fraction);
2287     head_pad = total_pad - tail_pad;
2288   } else {
2289     tail_pad = 0;
2290     head_pad = 0;
2291   }
2292   int origHeight = height;
2293   height += tail_pad;
2294   height += head_pad;
2295 #if 0
2296   printf("head_pad: %d tail_pad: %d\n", head_pad, tail_pad);
2297   printf("origHeight: %d height: %d\n", origHeight, height);
2298   printf("ty: %d, ty2: %d\n", ty, ty2);
2299 #endif
2300 
2301   /* TODO: Do we want to cache these? */
2302   imgStr = new ImageStream(str, width, 1, 1);
2303   imgStr->reset();
2304 
2305   invert_bit = invert ? 1 : 0;
2306 
2307   image = cairo_image_surface_create (CAIRO_FORMAT_A8, scaledWidth, scaledHeight);
2308   if (cairo_surface_status (image)) {
2309     imgStr->close();
2310     delete imgStr;
2311     return;
2312   }
2313 
2314   buffer = cairo_image_surface_get_data (image);
2315   row_stride = cairo_image_surface_get_stride (image);
2316 
2317   int yp = height / scaledHeight;
2318   int yq = height % scaledHeight;
2319   int xp = width / scaledWidth;
2320   int xq = width % scaledWidth;
2321   int yt = 0;
2322   int origHeight_c = origHeight;
2323   /* use MIN() because yp might be > origHeight because of padding */
2324   unsigned char *pixBuf = (unsigned char *)malloc(MIN(yp+1, origHeight)*width);
2325   int lastYStep = 1;
2326   int total = 0;
2327   for (int y = 0; y < scaledHeight; y++) {
2328     // y scale Bresenham
2329     int yStep = yp;
2330     yt += yq;
2331 
2332     if (yt >= scaledHeight) {
2333       yt -= scaledHeight;
2334       ++yStep;
2335     }
2336 
2337     // read row (s) from image ignoring the padding as appropriate
2338     {
2339       int n = (yp > 0) ? yStep : lastYStep;
2340       total += n;
2341       if (n > 0) {
2342 	unsigned char *p = pixBuf;
2343 	int head_pad_count = head_pad;
2344 	int origHeight_count = origHeight;
2345 	int tail_pad_count = tail_pad;
2346 	for (int i=0; i<n; i++) {
2347 	  // get row
2348 	  if (head_pad_count) {
2349 	    head_pad_count--;
2350 	  } else if (origHeight_count) {
2351 	    pix = imgStr->getLine();
2352 	    for (int j=0; j<width; j++) {
2353 	      if (pix[j] ^ invert_bit)
2354 		p[j] = 0;
2355 	      else
2356 		p[j] = 255;
2357 	    }
2358 	    origHeight_count--;
2359 	    p += width;
2360 	  } else if (tail_pad_count) {
2361 	    tail_pad_count--;
2362 	  } else {
2363 	    printf("%d %d\n", n, total);
2364 	    assert(0 && "over run\n");
2365 	  }
2366 	}
2367       }
2368     }
2369 
2370     lastYStep = yStep;
2371     int k1 = y;
2372 
2373     int xt = 0;
2374     int xSrc = 0;
2375     int x1 = k1;
2376     int n = yStep > 0 ? yStep : 1;
2377     int origN = n;
2378 
2379     /* compute the size of padding and pixels that will be used for this row */
2380     int head_pad_size = MIN(n, head_pad);
2381     n -= head_pad_size;
2382     head_pad -= MIN(head_pad_size, yStep);
2383 
2384     int pix_size = MIN(n, origHeight);
2385     n -= pix_size;
2386     origHeight -= MIN(pix_size, yStep);
2387 
2388     int tail_pad_size = MIN(n, tail_pad);
2389     n -= tail_pad_size;
2390     tail_pad -= MIN(tail_pad_size, yStep);
2391     if (n != 0) {
2392       printf("n = %d (%d %d %d)\n", n, head_pad_size, pix_size, tail_pad_size);
2393       assert(n == 0);
2394     }
2395 
2396     for (int x = 0; x < scaledWidth; ++x) {
2397       int xStep = xp;
2398       xt += xq;
2399       if (xt >= scaledWidth) {
2400 	xt -= scaledWidth;
2401 	++xStep;
2402       }
2403       int m = xStep > 0 ? xStep : 1;
2404       float pixAcc0 = 0;
2405       /* could m * head_pad_size * tail_pad_size  overflow? */
2406       if (invert_bit) {
2407 	pixAcc0 += m * head_pad_size * tail_pad_size * 255;
2408       } else {
2409 	pixAcc0 += m * head_pad_size * tail_pad_size * 0;
2410       }
2411       /* Accumulate all of the source pixels for the destination pixel */
2412       for (int i = 0; i < pix_size; ++i) {
2413 	for (int j = 0; j< m; ++j) {
2414 	  if (xSrc + i*width + j > MIN(yp + 1, origHeight_c)*width) {
2415 	    printf("%d > %d (%d %d %d %d) (%d %d %d)\n", xSrc + i*width + j, MIN(yp + 1, origHeight_c)*width, xSrc, i , width, j, yp, origHeight_c, width);
2416 	    printf("%d %d %d\n", head_pad_size, pix_size, tail_pad_size);
2417 	    assert(0 && "bad access\n");
2418 	  }
2419 	  pixAcc0 += pixBuf[xSrc + i*width + j];
2420 	}
2421       }
2422       buffer[y * row_stride + x] = splashFloor(pixAcc0 / (origN*m));
2423       xSrc += xStep;
2424       x1 += 1;
2425     }
2426 
2427   }
2428   free(pixBuf);
2429 
2430   cairo_surface_mark_dirty (image);
2431   pattern = cairo_pattern_create_for_surface (image);
2432   cairo_surface_destroy (image);
2433   if (cairo_pattern_status (pattern)) {
2434     imgStr->close();
2435     delete imgStr;
2436     return;
2437   }
2438 
2439   /* we should actually be using CAIRO_FILTER_NEAREST here. However,
2440    * cairo doesn't yet do minifaction filtering causing scaled down
2441    * images with CAIRO_FILTER_NEAREST to look really bad */
2442   cairo_pattern_set_filter (pattern,
2443 			    interpolate ? CAIRO_FILTER_BEST : CAIRO_FILTER_FAST);
2444 
2445   if (state->getFillColorSpace()->getMode() == csPattern) {
2446     cairo_matrix_init_translate (&matrix, 0, scaledHeight);
2447     cairo_matrix_scale (&matrix, scaledWidth, -scaledHeight);
2448     cairo_pattern_set_matrix (pattern, &matrix);
2449     if (cairo_pattern_status (pattern)) {
2450       cairo_pattern_destroy (pattern);
2451       imgStr->close();
2452       delete imgStr;
2453       return;
2454     }
2455 
2456     mask = cairo_pattern_reference (pattern);
2457     cairo_get_matrix (cairo, &mask_matrix);
2458   } else {
2459     cairo_save (cairo);
2460 
2461     /* modify our current transformation so that the prescaled image
2462      * goes where it is supposed to */
2463     cairo_get_matrix(cairo, &matrix);
2464     cairo_scale(cairo, 1.0/matrix.xx, 1.0/matrix.yy);
2465     // get integer co-ords
2466     cairo_translate (cairo, tx - matrix.x0, ty2 - matrix.y0);
2467     if (yScale > 0)
2468       cairo_scale(cairo, 1, -1);
2469 
2470     cairo_rectangle (cairo, 0., 0., scaledWidth, scaledHeight);
2471     cairo_clip (cairo);
2472     if (strokePathClip) {
2473       cairo_push_group (cairo);
2474       fillToStrokePathClip (state);
2475       cairo_pop_group_to_source (cairo);
2476     }
2477     cairo_mask (cairo, pattern);
2478 
2479     //cairo_get_matrix(cairo, &matrix);
2480     //printf("mask at: [%f %f], [%f %f], %f %f\n\n", matrix.xx, matrix.xy, matrix.yx, matrix.yy, matrix.x0, matrix.y0);
2481     cairo_restore(cairo);
2482   }
2483 
2484   if (cairo_shape) {
2485     cairo_save (cairo_shape);
2486 
2487     /* modify our current transformation so that the prescaled image
2488      * goes where it is supposed to */
2489     cairo_get_matrix(cairo_shape, &matrix);
2490     cairo_scale(cairo_shape, 1.0/matrix.xx, 1.0/matrix.yy);
2491     // get integer co-ords
2492     cairo_translate (cairo_shape, tx - matrix.x0, ty2 - matrix.y0);
2493     if (yScale > 0)
2494       cairo_scale(cairo_shape, 1, -1);
2495 
2496     cairo_rectangle (cairo_shape, 0., 0., scaledWidth, scaledHeight);
2497     cairo_fill (cairo_shape);
2498 
2499     cairo_restore(cairo_shape);
2500   }
2501 
2502   cairo_pattern_destroy (pattern);
2503 
2504   imgStr->close();
2505   delete imgStr;
2506 }
2507 
drawMaskedImage(GfxState * state,Object * ref,Stream * str,int width,int height,GfxImageColorMap * colorMap,GBool interpolate,Stream * maskStr,int maskWidth,int maskHeight,GBool maskInvert,GBool maskInterpolate)2508 void CairoOutputDev::drawMaskedImage(GfxState *state, Object *ref,
2509 				     Stream *str, int width, int height,
2510 				     GfxImageColorMap *colorMap,
2511 				     GBool interpolate,
2512 				     Stream *maskStr, int maskWidth,
2513 				     int maskHeight, GBool maskInvert,
2514 				     GBool maskInterpolate)
2515 {
2516   ImageStream *maskImgStr, *imgStr;
2517   int row_stride;
2518   unsigned char *maskBuffer, *buffer;
2519   unsigned char *maskDest;
2520   unsigned int *dest;
2521   cairo_surface_t *maskImage, *image;
2522   cairo_pattern_t *maskPattern, *pattern;
2523   cairo_matrix_t matrix;
2524   cairo_matrix_t maskMatrix;
2525   Guchar *pix;
2526   int x, y;
2527   int invert_bit;
2528   cairo_filter_t filter;
2529   cairo_filter_t maskFilter;
2530 
2531   maskImgStr = new ImageStream(maskStr, maskWidth, 1, 1);
2532   maskImgStr->reset();
2533 
2534   maskImage = cairo_image_surface_create (CAIRO_FORMAT_A8, maskWidth, maskHeight);
2535   if (cairo_surface_status (maskImage)) {
2536     maskImgStr->close();
2537     delete maskImgStr;
2538     return;
2539   }
2540 
2541   maskBuffer = cairo_image_surface_get_data (maskImage);
2542   row_stride = cairo_image_surface_get_stride (maskImage);
2543 
2544   invert_bit = maskInvert ? 1 : 0;
2545 
2546   for (y = 0; y < maskHeight; y++) {
2547     pix = maskImgStr->getLine();
2548     maskDest = maskBuffer + y * row_stride;
2549     for (x = 0; x < maskWidth; x++) {
2550       if (pix[x] ^ invert_bit)
2551 	*maskDest++ = 0;
2552       else
2553 	*maskDest++ = 255;
2554     }
2555   }
2556 
2557   maskImgStr->close();
2558   delete maskImgStr;
2559 
2560   maskFilter = getFilterForSurface (maskImage, maskInterpolate);
2561 
2562   cairo_surface_mark_dirty (maskImage);
2563   maskPattern = cairo_pattern_create_for_surface (maskImage);
2564   cairo_surface_destroy (maskImage);
2565   if (cairo_pattern_status (maskPattern))
2566     return;
2567 
2568 #if 0
2569   /* ICCBased color space doesn't do any color correction
2570    * so check its underlying color space as well */
2571   int is_identity_transform;
2572   is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB ||
2573 		  (colorMap->getColorSpace()->getMode() == csICCBased &&
2574 		   ((GfxICCBasedColorSpace*)colorMap->getColorSpace())->getAlt()->getMode() == csDeviceRGB);
2575 #endif
2576 
2577   /* TODO: Do we want to cache these? */
2578   imgStr = new ImageStream(str, width,
2579 			   colorMap->getNumPixelComps(),
2580 			   colorMap->getBits());
2581   imgStr->reset();
2582 
2583   image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
2584   if (cairo_surface_status (image))
2585     goto cleanup;
2586 
2587   buffer = cairo_image_surface_get_data (image);
2588   row_stride = cairo_image_surface_get_stride (image);
2589   for (y = 0; y < height; y++) {
2590     dest = (unsigned int *) (buffer + y * row_stride);
2591     pix = imgStr->getLine();
2592     colorMap->getRGBLine (pix, dest, width);
2593   }
2594 
2595   filter = getFilterForSurface (image, interpolate);
2596 
2597   cairo_surface_mark_dirty (image);
2598   pattern = cairo_pattern_create_for_surface (image);
2599   cairo_surface_destroy (image);
2600   if (cairo_pattern_status (pattern))
2601     goto cleanup;
2602 
2603   LOG (printf ("drawMaskedImage %dx%d\n", width, height));
2604 
2605   cairo_pattern_set_filter (pattern, filter);
2606   cairo_pattern_set_filter (maskPattern, maskFilter);
2607 
2608   if (!printing) {
2609     cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
2610     cairo_pattern_set_extend (maskPattern, CAIRO_EXTEND_PAD);
2611   }
2612 
2613   cairo_matrix_init_translate (&matrix, 0, height);
2614   cairo_matrix_scale (&matrix, width, -height);
2615   cairo_pattern_set_matrix (pattern, &matrix);
2616   if (cairo_pattern_status (pattern)) {
2617     cairo_pattern_destroy (pattern);
2618     cairo_pattern_destroy (maskPattern);
2619     goto cleanup;
2620   }
2621 
2622   cairo_matrix_init_translate (&maskMatrix, 0, maskHeight);
2623   cairo_matrix_scale (&maskMatrix, maskWidth, -maskHeight);
2624   cairo_pattern_set_matrix (maskPattern, &maskMatrix);
2625   if (cairo_pattern_status (maskPattern)) {
2626     cairo_pattern_destroy (maskPattern);
2627     cairo_pattern_destroy (pattern);
2628     goto cleanup;
2629   }
2630 
2631   if (!printing) {
2632     cairo_save (cairo);
2633     cairo_set_source (cairo, pattern);
2634     cairo_rectangle (cairo, 0., 0., 1., 1.);
2635     cairo_clip (cairo);
2636     cairo_mask (cairo, maskPattern);
2637     cairo_restore (cairo);
2638   } else {
2639     cairo_set_source (cairo, pattern);
2640     cairo_mask (cairo, maskPattern);
2641   }
2642 
2643   if (cairo_shape) {
2644     cairo_save (cairo_shape);
2645     cairo_set_source (cairo_shape, pattern);
2646     if (!printing) {
2647       cairo_rectangle (cairo_shape, 0., 0., 1., 1.);
2648       cairo_fill (cairo_shape);
2649     } else {
2650       cairo_mask (cairo_shape, pattern);
2651     }
2652     cairo_restore (cairo_shape);
2653   }
2654 
2655   cairo_pattern_destroy (maskPattern);
2656   cairo_pattern_destroy (pattern);
2657 
2658 cleanup:
2659   imgStr->close();
2660   delete imgStr;
2661 }
2662 
2663 
2664 //XXX: is this affect by AIS(alpha is shape)?
drawSoftMaskedImage(GfxState * state,Object * ref,Stream * str,int width,int height,GfxImageColorMap * colorMap,GBool interpolate,Stream * maskStr,int maskWidth,int maskHeight,GfxImageColorMap * maskColorMap,GBool maskInterpolate)2665 void CairoOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2666 					 int width, int height,
2667 					 GfxImageColorMap *colorMap,
2668 					 GBool interpolate,
2669 					 Stream *maskStr,
2670 					 int maskWidth, int maskHeight,
2671 					 GfxImageColorMap *maskColorMap,
2672 					 GBool maskInterpolate)
2673 {
2674   ImageStream *maskImgStr, *imgStr;
2675   int row_stride;
2676   unsigned char *maskBuffer, *buffer;
2677   unsigned char *maskDest;
2678   unsigned int *dest;
2679   cairo_surface_t *maskImage, *image;
2680   cairo_pattern_t *maskPattern, *pattern;
2681   cairo_matrix_t maskMatrix, matrix;
2682   Guchar *pix;
2683   int y;
2684   cairo_filter_t filter;
2685   cairo_filter_t maskFilter;
2686 
2687   maskImgStr = new ImageStream(maskStr, maskWidth,
2688 			       maskColorMap->getNumPixelComps(),
2689 			       maskColorMap->getBits());
2690   maskImgStr->reset();
2691 
2692   maskImage = cairo_image_surface_create (CAIRO_FORMAT_A8, maskWidth, maskHeight);
2693   if (cairo_surface_status (maskImage)) {
2694     maskImgStr->close();
2695     delete maskImgStr;
2696     return;
2697   }
2698 
2699   maskBuffer = cairo_image_surface_get_data (maskImage);
2700   row_stride = cairo_image_surface_get_stride (maskImage);
2701   for (y = 0; y < maskHeight; y++) {
2702     maskDest = (unsigned char *) (maskBuffer + y * row_stride);
2703     pix = maskImgStr->getLine();
2704     maskColorMap->getGrayLine (pix, maskDest, maskWidth);
2705   }
2706 
2707   maskImgStr->close();
2708   delete maskImgStr;
2709 
2710   maskFilter = getFilterForSurface (maskImage, maskInterpolate);
2711 
2712   cairo_surface_mark_dirty (maskImage);
2713   maskPattern = cairo_pattern_create_for_surface (maskImage);
2714   cairo_surface_destroy (maskImage);
2715   if (cairo_pattern_status (maskPattern))
2716     return;
2717 
2718 #if 0
2719   /* ICCBased color space doesn't do any color correction
2720    * so check its underlying color space as well */
2721   int is_identity_transform;
2722   is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB ||
2723 		  (colorMap->getColorSpace()->getMode() == csICCBased &&
2724 		   ((GfxICCBasedColorSpace*)colorMap->getColorSpace())->getAlt()->getMode() == csDeviceRGB);
2725 #endif
2726 
2727   /* TODO: Do we want to cache these? */
2728   imgStr = new ImageStream(str, width,
2729 			   colorMap->getNumPixelComps(),
2730 			   colorMap->getBits());
2731   imgStr->reset();
2732 
2733   image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
2734   if (cairo_surface_status (image))
2735     goto cleanup;
2736 
2737   buffer = cairo_image_surface_get_data (image);
2738   row_stride = cairo_image_surface_get_stride (image);
2739   for (y = 0; y < height; y++) {
2740     dest = (unsigned int *) (buffer + y * row_stride);
2741     pix = imgStr->getLine();
2742     colorMap->getRGBLine (pix, dest, width);
2743   }
2744 
2745   filter = getFilterForSurface (image, interpolate);
2746 
2747   cairo_surface_mark_dirty (image);
2748 
2749   setMimeData(state, str, ref, colorMap, image);
2750 
2751   pattern = cairo_pattern_create_for_surface (image);
2752   cairo_surface_destroy (image);
2753   if (cairo_pattern_status (pattern))
2754     goto cleanup;
2755 
2756   LOG (printf ("drawSoftMaskedImage %dx%d\n", width, height));
2757 
2758   cairo_pattern_set_filter (pattern, filter);
2759   cairo_pattern_set_filter (maskPattern, maskFilter);
2760 
2761   if (!printing) {
2762     cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
2763     cairo_pattern_set_extend (maskPattern, CAIRO_EXTEND_PAD);
2764   }
2765 
2766   cairo_matrix_init_translate (&matrix, 0, height);
2767   cairo_matrix_scale (&matrix, width, -height);
2768   cairo_pattern_set_matrix (pattern, &matrix);
2769   if (cairo_pattern_status (pattern)) {
2770     cairo_pattern_destroy (pattern);
2771     cairo_pattern_destroy (maskPattern);
2772     goto cleanup;
2773   }
2774 
2775   cairo_matrix_init_translate (&maskMatrix, 0, maskHeight);
2776   cairo_matrix_scale (&maskMatrix, maskWidth, -maskHeight);
2777   cairo_pattern_set_matrix (maskPattern, &maskMatrix);
2778   if (cairo_pattern_status (maskPattern)) {
2779     cairo_pattern_destroy (maskPattern);
2780     cairo_pattern_destroy (pattern);
2781     goto cleanup;
2782   }
2783 
2784   if (fill_opacity != 1.0)
2785     cairo_push_group (cairo);
2786   else
2787     cairo_save (cairo);
2788 
2789   cairo_set_source (cairo, pattern);
2790   if (!printing) {
2791     cairo_rectangle (cairo, 0., 0., 1., 1.);
2792     cairo_clip (cairo);
2793   }
2794   cairo_mask (cairo, maskPattern);
2795 
2796   if (fill_opacity != 1.0) {
2797     cairo_pop_group_to_source (cairo);
2798     cairo_save (cairo);
2799     if (!printing) {
2800       cairo_rectangle (cairo, 0., 0., 1., 1.);
2801       cairo_clip (cairo);
2802     }
2803     cairo_paint_with_alpha (cairo, fill_opacity);
2804   }
2805   cairo_restore (cairo);
2806 
2807   if (cairo_shape) {
2808     cairo_save (cairo_shape);
2809     cairo_set_source (cairo_shape, pattern);
2810     if (!printing) {
2811       cairo_rectangle (cairo_shape, 0., 0., 1., 1.);
2812       cairo_fill (cairo_shape);
2813     } else {
2814       cairo_mask (cairo_shape, pattern);
2815     }
2816     cairo_restore (cairo_shape);
2817   }
2818 
2819   cairo_pattern_destroy (maskPattern);
2820   cairo_pattern_destroy (pattern);
2821 
2822 cleanup:
2823   imgStr->close();
2824   delete imgStr;
2825 }
2826 
getStreamData(Stream * str,char ** buffer,int * length)2827 GBool CairoOutputDev::getStreamData (Stream *str, char **buffer, int *length)
2828 {
2829   int len, i;
2830   char *strBuffer;
2831 
2832   len = 0;
2833   str->close();
2834   str->reset();
2835   while (str->getChar() != EOF) len++;
2836   if (len == 0)
2837     return gFalse;
2838 
2839   strBuffer = (char *)gmalloc (len);
2840 
2841   str->close();
2842   str->reset();
2843   for (i = 0; i < len; ++i)
2844     strBuffer[i] = str->getChar();
2845 
2846   *buffer = strBuffer;
2847   *length = len;
2848 
2849   return gTrue;
2850 }
2851 
colorMapHasIdentityDecodeMap(GfxImageColorMap * colorMap)2852 static GBool colorMapHasIdentityDecodeMap(GfxImageColorMap *colorMap)
2853 {
2854   for (int i = 0; i < colorMap->getNumPixelComps(); i++) {
2855     if (colorMap->getDecodeLow(i) != 0.0 || colorMap->getDecodeHigh(i) != 1.0)
2856       return gFalse;
2857   }
2858   return gTrue;
2859 }
2860 
2861 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 11, 2)
setMimeIdFromRef(cairo_surface_t * surface,const char * mime_type,const char * mime_id_prefix,Ref ref)2862 static cairo_status_t setMimeIdFromRef(cairo_surface_t *surface,
2863 				       const char *mime_type,
2864 				       const char *mime_id_prefix,
2865 				       Ref ref)
2866 {
2867   GooString *mime_id;
2868   char *idBuffer;
2869   cairo_status_t status;
2870 
2871   mime_id = new GooString;
2872 
2873   if (mime_id_prefix)
2874     mime_id->append(mime_id_prefix);
2875 
2876   mime_id->appendf("{0:d}-{1:d}", ref.gen, ref.num);
2877 
2878   idBuffer = copyString(mime_id->getCString());
2879   status = cairo_surface_set_mime_data (surface, mime_type,
2880                                         (const unsigned char *)idBuffer,
2881                                         mime_id->getLength(),
2882                                         gfree, idBuffer);
2883   delete mime_id;
2884   if (status)
2885     gfree (idBuffer);
2886   return status;
2887 }
2888 #endif
2889 
2890 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0)
setMimeDataForJBIG2Globals(Stream * str,cairo_surface_t * image)2891 GBool CairoOutputDev::setMimeDataForJBIG2Globals(Stream  *str,
2892                                                  cairo_surface_t *image)
2893 {
2894   JBIG2Stream *jb2Str = static_cast<JBIG2Stream *>(str);
2895   Object* globalsStr = jb2Str->getGlobalsStream();
2896   char *globalsBuffer;
2897   int globalsLength;
2898 
2899   // nothing to do for JBIG2 stream without Globals
2900   if (!globalsStr->isStream())
2901     return gTrue;
2902 
2903   if (setMimeIdFromRef(image, CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID, NULL,
2904                        jb2Str->getGlobalsStreamRef()))
2905     return gFalse;
2906 
2907   if (!getStreamData(globalsStr->getStream(), &globalsBuffer, &globalsLength))
2908     return gFalse;
2909 
2910   if (cairo_surface_set_mime_data (image, CAIRO_MIME_TYPE_JBIG2_GLOBAL,
2911                                    (const unsigned char*)globalsBuffer,
2912                                    globalsLength,
2913                                    gfree, (void*)globalsBuffer))
2914   {
2915     gfree (globalsBuffer);
2916     return gFalse;
2917   }
2918 
2919   return gTrue;
2920 }
2921 #endif
2922 
setMimeData(GfxState * state,Stream * str,Object * ref,GfxImageColorMap * colorMap,cairo_surface_t * image)2923 void CairoOutputDev::setMimeData(GfxState *state, Stream *str, Object *ref,
2924 				 GfxImageColorMap *colorMap, cairo_surface_t *image)
2925 {
2926   char *strBuffer;
2927   int len;
2928   Object obj;
2929   GfxColorSpace *colorSpace;
2930   StreamKind  strKind = str->getKind();
2931   const char *mime_type;
2932 
2933   if (!printing)
2934     return;
2935 
2936   switch (strKind) {
2937     case strDCT:
2938       mime_type = CAIRO_MIME_TYPE_JPEG;
2939       break;
2940     case strJPX:
2941       mime_type = CAIRO_MIME_TYPE_JP2;
2942       break;
2943 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0)
2944     case strJBIG2:
2945       mime_type = CAIRO_MIME_TYPE_JBIG2;
2946       break;
2947 #endif
2948     default:
2949       return;
2950   }
2951 
2952   str->getDict()->lookup("ColorSpace", &obj);
2953   colorSpace = GfxColorSpace::parse(NULL, &obj, this, state);
2954   obj.free();
2955 
2956   // colorspace in stream dict may be different from colorspace in jpx
2957   // data
2958   if (strKind == strJPX && colorSpace)
2959     return;
2960 
2961   // only embed mime data for gray, rgb, and cmyk colorspaces.
2962   if (colorSpace) {
2963     GfxColorSpaceMode mode = colorSpace->getMode();
2964     delete colorSpace;
2965     switch (mode) {
2966       case csDeviceGray:
2967       case csCalGray:
2968       case csDeviceRGB:
2969       case csCalRGB:
2970       case csDeviceCMYK:
2971       case csICCBased:
2972 	break;
2973 
2974       case csLab:
2975       case csIndexed:
2976       case csSeparation:
2977       case csDeviceN:
2978       case csPattern:
2979 	return;
2980     }
2981   }
2982 
2983   if (!colorMapHasIdentityDecodeMap(colorMap))
2984     return;
2985 
2986 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0)
2987   if (strKind == strJBIG2 && !setMimeDataForJBIG2Globals(str, image))
2988     return;
2989 #endif
2990 
2991   if (getStreamData (str->getNextStream(), &strBuffer, &len)) {
2992     cairo_status_t status = CAIRO_STATUS_SUCCESS;
2993 
2994 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 11, 2)
2995     if (ref && ref->isRef()) {
2996       status = setMimeIdFromRef(image, CAIRO_MIME_TYPE_UNIQUE_ID,
2997                                 "poppler-surface-", ref->getRef());
2998     }
2999 #endif
3000     if (!status) {
3001       status = cairo_surface_set_mime_data (image, mime_type,
3002 					    (const unsigned char *)strBuffer, len,
3003 					    gfree, strBuffer);
3004     }
3005 
3006     if (status)
3007       gfree (strBuffer);
3008   }
3009 }
3010 
3011 class RescaleDrawImage : public CairoRescaleBox {
3012 private:
3013   ImageStream *imgStr;
3014   GfxRGB *lookup;
3015   int width;
3016   GfxImageColorMap *colorMap;
3017   int *maskColors;
3018   int current_row;
3019   GBool imageError;
3020 
3021 public:
getSourceImage(Stream * str,int widthA,int height,int scaledWidth,int scaledHeight,GBool printing,GfxImageColorMap * colorMapA,int * maskColorsA)3022   cairo_surface_t *getSourceImage(Stream *str,
3023                                   int widthA, int height,
3024                                   int scaledWidth, int scaledHeight,
3025                                   GBool printing,
3026                                   GfxImageColorMap *colorMapA,
3027                                   int *maskColorsA) {
3028     cairo_surface_t *image = NULL;
3029     int i;
3030 
3031     lookup = NULL;
3032     colorMap = colorMapA;
3033     maskColors = maskColorsA;
3034     width = widthA;
3035     current_row = -1;
3036     imageError = gFalse;
3037 
3038     /* TODO: Do we want to cache these? */
3039     imgStr = new ImageStream(str, width,
3040                              colorMap->getNumPixelComps(),
3041                              colorMap->getBits());
3042     imgStr->reset();
3043 
3044 #if 0
3045     /* ICCBased color space doesn't do any color correction
3046      * so check its underlying color space as well */
3047     int is_identity_transform;
3048     is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB ||
3049       (colorMap->getColorSpace()->getMode() == csICCBased &&
3050        ((GfxICCBasedColorSpace*)colorMap->getColorSpace())->getAlt()->getMode() == csDeviceRGB);
3051 #endif
3052 
3053     // special case for one-channel (monochrome/gray/separation) images:
3054     // build a lookup table here
3055     if (colorMap->getNumPixelComps() == 1) {
3056       int n;
3057       Guchar pix;
3058 
3059       n = 1 << colorMap->getBits();
3060       lookup = (GfxRGB *)gmallocn(n, sizeof(GfxRGB));
3061       for (i = 0; i < n; ++i) {
3062         pix = (Guchar)i;
3063 
3064         colorMap->getRGB(&pix, &lookup[i]);
3065       }
3066     }
3067 
3068     if (printing || scaledWidth >= width || scaledHeight >= height) {
3069       // No downscaling. Create cairo image containing the source image data.
3070       unsigned char *buffer;
3071       int stride;
3072 
3073       image = cairo_image_surface_create (maskColors ?
3074                                           CAIRO_FORMAT_ARGB32 :
3075                                           CAIRO_FORMAT_RGB24,
3076                                           width, height);
3077       if (cairo_surface_status (image))
3078         goto cleanup;
3079 
3080       buffer = cairo_image_surface_get_data (image);
3081       stride = cairo_image_surface_get_stride (image);
3082       for (int y = 0; y < height; y++) {
3083         uint32_t *dest = (uint32_t *) (buffer + y * stride);
3084         getRow(y, dest);
3085       }
3086     } else {
3087       // // Downscaling required. Create cairo image the size of the
3088       // rescaled image and // downscale the source image data into
3089       // the cairo image. downScaleImage() will call getRow() to read
3090       // source image data from the image stream. This avoids having
3091       // to create an image the size of the source image which may
3092       // exceed cairo's 32676x32767 image size limit (and also saves a
3093       // lot of memory).
3094       image = cairo_image_surface_create (maskColors ?
3095                                           CAIRO_FORMAT_ARGB32 :
3096                                           CAIRO_FORMAT_RGB24,
3097                                           scaledWidth, scaledHeight);
3098       if (cairo_surface_status (image))
3099         goto cleanup;
3100 
3101       downScaleImage(width, height,
3102                      scaledWidth, scaledHeight,
3103                      0, 0, scaledWidth, scaledHeight,
3104                      image);
3105     }
3106     cairo_surface_mark_dirty (image);
3107 
3108   cleanup:
3109     gfree(lookup);
3110     imgStr->close();
3111     delete imgStr;
3112     return image;
3113   }
3114 
getRow(int row_num,uint32_t * row_data)3115   void getRow(int row_num, uint32_t *row_data) {
3116     int i;
3117     Guchar *pix;
3118 
3119     if (row_num <= current_row)
3120       return;
3121 
3122     while (current_row  < row_num) {
3123       pix = imgStr->getLine();
3124       current_row++;
3125     }
3126 
3127     if (unlikely(pix == NULL)) {
3128       memset(row_data, 0, width*4);
3129       if (!imageError) {
3130 	error(errInternal, -1, "Bad image stream");
3131 	imageError = gTrue;
3132       }
3133     } else if (lookup) {
3134       Guchar *p = pix;
3135       GfxRGB rgb;
3136 
3137       for (i = 0; i < width; i++) {
3138         rgb = lookup[*p];
3139         row_data[i] =
3140           ((int) colToByte(rgb.r) << 16) |
3141           ((int) colToByte(rgb.g) << 8) |
3142           ((int) colToByte(rgb.b) << 0);
3143         p++;
3144       }
3145     } else {
3146       colorMap->getRGBLine (pix, row_data, width);
3147     }
3148 
3149     if (maskColors) {
3150       for (int x = 0; x < width; x++) {
3151         bool is_opaque = false;
3152         for (int i = 0; i < colorMap->getNumPixelComps(); ++i) {
3153           if (pix[i] < maskColors[2*i] ||
3154               pix[i] > maskColors[2*i+1]) {
3155             is_opaque = true;
3156             break;
3157           }
3158         }
3159         if (is_opaque)
3160           *row_data |= 0xff000000;
3161         else
3162           *row_data = 0;
3163         row_data++;
3164         pix += colorMap->getNumPixelComps();
3165       }
3166     }
3167   }
3168 
3169 };
3170 
drawImage(GfxState * state,Object * ref,Stream * str,int widthA,int heightA,GfxImageColorMap * colorMap,GBool interpolate,int * maskColors,GBool inlineImg)3171 void CairoOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
3172 			       int widthA, int heightA,
3173 			       GfxImageColorMap *colorMap,
3174 			       GBool interpolate,
3175 			       int *maskColors, GBool inlineImg)
3176 {
3177   cairo_surface_t *image;
3178   cairo_pattern_t *pattern, *maskPattern;
3179   cairo_matrix_t matrix;
3180   int width, height;
3181   int scaledWidth, scaledHeight;
3182   cairo_filter_t filter = CAIRO_FILTER_BILINEAR;
3183   RescaleDrawImage rescale;
3184 
3185   LOG (printf ("drawImage %dx%d\n", widthA, heightA));
3186 
3187   cairo_get_matrix(cairo, &matrix);
3188   getScaledSize (&matrix, widthA, heightA, &scaledWidth, &scaledHeight);
3189   image = rescale.getSourceImage(str, widthA, heightA, scaledWidth, scaledHeight, printing, colorMap, maskColors);
3190   if (!image)
3191     return;
3192 
3193   width = cairo_image_surface_get_width (image);
3194   height = cairo_image_surface_get_height (image);
3195   if (width == widthA && height == heightA)
3196     filter = getFilterForSurface (image, interpolate);
3197 
3198   if (!inlineImg) /* don't read stream twice if it is an inline image */
3199     setMimeData(state, str, ref, colorMap, image);
3200 
3201   pattern = cairo_pattern_create_for_surface (image);
3202   cairo_surface_destroy (image);
3203   if (cairo_pattern_status (pattern))
3204     return;
3205 
3206   cairo_pattern_set_filter (pattern, filter);
3207 
3208   if (!printing)
3209     cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
3210 
3211   cairo_matrix_init_translate (&matrix, 0, height);
3212   cairo_matrix_scale (&matrix, width, -height);
3213   cairo_pattern_set_matrix (pattern, &matrix);
3214   if (cairo_pattern_status (pattern)) {
3215     cairo_pattern_destroy (pattern);
3216     return;
3217   }
3218 
3219   if (!mask && fill_opacity != 1.0) {
3220     maskPattern = cairo_pattern_create_rgba (1., 1., 1., fill_opacity);
3221   } else if (mask) {
3222     maskPattern = cairo_pattern_reference (mask);
3223   } else {
3224     maskPattern = NULL;
3225   }
3226 
3227   cairo_save (cairo);
3228   cairo_set_source (cairo, pattern);
3229   if (!printing)
3230     cairo_rectangle (cairo, 0., 0., 1., 1.);
3231   if (maskPattern) {
3232     if (!printing)
3233       cairo_clip (cairo);
3234     if (mask)
3235       cairo_set_matrix (cairo, &mask_matrix);
3236     cairo_mask (cairo, maskPattern);
3237   } else {
3238     if (printing)
3239       cairo_paint (cairo);
3240     else
3241       cairo_fill (cairo);
3242   }
3243   cairo_restore (cairo);
3244 
3245   cairo_pattern_destroy (maskPattern);
3246 
3247   if (cairo_shape) {
3248     cairo_save (cairo_shape);
3249     cairo_set_source (cairo_shape, pattern);
3250     if (printing) {
3251       cairo_paint (cairo_shape);
3252     } else {
3253       cairo_rectangle (cairo_shape, 0., 0., 1., 1.);
3254       cairo_fill (cairo_shape);
3255     }
3256     cairo_restore (cairo_shape);
3257   }
3258 
3259   cairo_pattern_destroy (pattern);
3260 }
3261 
3262 
3263 //------------------------------------------------------------------------
3264 // ImageOutputDev
3265 //------------------------------------------------------------------------
3266 
CairoImageOutputDev()3267 CairoImageOutputDev::CairoImageOutputDev()
3268 {
3269   images = NULL;
3270   numImages = 0;
3271   size = 0;
3272   imgDrawCbk = NULL;
3273   imgDrawCbkData = NULL;
3274 }
3275 
~CairoImageOutputDev()3276 CairoImageOutputDev::~CairoImageOutputDev()
3277 {
3278   int i;
3279 
3280   for (i = 0; i < numImages; i++)
3281     delete images[i];
3282   gfree (images);
3283 }
3284 
saveImage(CairoImage * image)3285 void CairoImageOutputDev::saveImage(CairoImage *image)
3286 {
3287   if (numImages >= size) {
3288 	  size += 16;
3289 	  images = (CairoImage **) greallocn (images, size, sizeof (CairoImage *));
3290   }
3291   images[numImages++] = image;
3292 }
3293 
getBBox(GfxState * state,int width,int height,double * x1,double * y1,double * x2,double * y2)3294 void CairoImageOutputDev::getBBox(GfxState *state, int width, int height,
3295                                   double *x1, double *y1, double *x2, double *y2)
3296 {
3297   double *ctm = state->getCTM();
3298   cairo_matrix_t matrix;
3299   cairo_matrix_init(&matrix,
3300                     ctm[0], ctm[1],
3301                     -ctm[2], -ctm[3],
3302                     ctm[2] + ctm[4], ctm[3] + ctm[5]);
3303 
3304   int scaledWidth, scaledHeight;
3305   getScaledSize (&matrix, width, height, &scaledWidth, &scaledHeight);
3306 
3307   if (matrix.xx >= 0) {
3308     *x1 = matrix.x0;
3309   } else {
3310     *x1 = matrix.x0 - scaledWidth;
3311   }
3312   *x2 = *x1 + scaledWidth;
3313 
3314   if (matrix.yy >= 0) {
3315     *y1 = matrix.y0;
3316   } else {
3317     *y1 = matrix.y0 - scaledHeight;
3318   }
3319   *y2 = *y1 + scaledHeight;
3320 }
3321 
drawImageMask(GfxState * state,Object * ref,Stream * str,int width,int height,GBool invert,GBool interpolate,GBool inlineImg)3322 void CairoImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
3323 					int width, int height, GBool invert,
3324 					GBool interpolate, GBool inlineImg)
3325 {
3326   cairo_t *cr;
3327   cairo_surface_t *surface;
3328   double x1, y1, x2, y2;
3329   CairoImage *image;
3330 
3331   getBBox(state, width, height, &x1, &y1, &x2, &y2);
3332 
3333   image = new CairoImage (x1, y1, x2, y2);
3334   saveImage (image);
3335 
3336   if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) {
3337     surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
3338     cr = cairo_create (surface);
3339     setCairo (cr);
3340     cairo_translate (cr, 0, height);
3341     cairo_scale (cr, width, -height);
3342 
3343     CairoOutputDev::drawImageMask(state, ref, str, width, height, invert, interpolate, inlineImg);
3344     image->setImage (surface);
3345 
3346     setCairo (NULL);
3347     cairo_surface_destroy (surface);
3348     cairo_destroy (cr);
3349   }
3350 }
3351 
setSoftMaskFromImageMask(GfxState * state,Object * ref,Stream * str,int width,int height,GBool invert,GBool inlineImg,double * baseMatrix)3352 void CairoImageOutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str,
3353                                                    int width, int height, GBool invert,
3354                                                    GBool inlineImg, double *baseMatrix)
3355 {
3356   cairo_t *cr;
3357   cairo_surface_t *surface;
3358   double x1, y1, x2, y2;
3359   CairoImage *image;
3360 
3361   getBBox(state, width, height, &x1, &y1, &x2, &y2);
3362 
3363   image = new CairoImage (x1, y1, x2, y2);
3364   saveImage (image);
3365 
3366   if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) {
3367     surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
3368     cr = cairo_create (surface);
3369     setCairo (cr);
3370     cairo_translate (cr, 0, height);
3371     cairo_scale (cr, width, -height);
3372 
3373     CairoOutputDev::drawImageMask(state, ref, str, width, height, invert, inlineImg, gFalse);
3374     if (state->getFillColorSpace()->getMode() == csPattern) {
3375       cairo_mask (cairo, mask);
3376     }
3377     image->setImage (surface);
3378 
3379     setCairo (NULL);
3380     cairo_surface_destroy (surface);
3381     cairo_destroy (cr);
3382   }
3383 }
3384 
drawImage(GfxState * state,Object * ref,Stream * str,int width,int height,GfxImageColorMap * colorMap,GBool interpolate,int * maskColors,GBool inlineImg)3385 void CairoImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
3386 				    int width, int height, GfxImageColorMap *colorMap,
3387 				    GBool interpolate, int *maskColors, GBool inlineImg)
3388 {
3389   cairo_t *cr;
3390   cairo_surface_t *surface;
3391   double x1, y1, x2, y2;
3392   CairoImage *image;
3393 
3394   getBBox(state, width, height, &x1, &y1, &x2, &y2);
3395 
3396   image = new CairoImage (x1, y1, x2, y2);
3397   saveImage (image);
3398 
3399   if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) {
3400     surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
3401     cr = cairo_create (surface);
3402     setCairo (cr);
3403     cairo_translate (cr, 0, height);
3404     cairo_scale (cr, width, -height);
3405 
3406     CairoOutputDev::drawImage(state, ref, str, width, height, colorMap, interpolate, maskColors, inlineImg);
3407     image->setImage (surface);
3408 
3409     setCairo (NULL);
3410     cairo_surface_destroy (surface);
3411     cairo_destroy (cr);
3412   }
3413 }
3414 
drawSoftMaskedImage(GfxState * state,Object * ref,Stream * str,int width,int height,GfxImageColorMap * colorMap,GBool interpolate,Stream * maskStr,int maskWidth,int maskHeight,GfxImageColorMap * maskColorMap,GBool maskInterpolate)3415 void CairoImageOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
3416 					      int width, int height,
3417 					      GfxImageColorMap *colorMap,
3418 					      GBool interpolate,
3419 					      Stream *maskStr,
3420 					      int maskWidth, int maskHeight,
3421 					      GfxImageColorMap *maskColorMap,
3422 					      GBool maskInterpolate)
3423 {
3424   cairo_t *cr;
3425   cairo_surface_t *surface;
3426   double x1, y1, x2, y2;
3427   CairoImage *image;
3428 
3429   getBBox(state, width, height, &x1, &y1, &x2, &y2);
3430 
3431   image = new CairoImage (x1, y1, x2, y2);
3432   saveImage (image);
3433 
3434   if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) {
3435     surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
3436     cr = cairo_create (surface);
3437     setCairo (cr);
3438     cairo_translate (cr, 0, height);
3439     cairo_scale (cr, width, -height);
3440 
3441     CairoOutputDev::drawSoftMaskedImage(state, ref, str, width, height, colorMap, interpolate,
3442 					maskStr, maskWidth, maskHeight, maskColorMap, maskInterpolate);
3443     image->setImage (surface);
3444 
3445     setCairo (NULL);
3446     cairo_surface_destroy (surface);
3447     cairo_destroy (cr);
3448   }
3449 }
3450 
drawMaskedImage(GfxState * state,Object * ref,Stream * str,int width,int height,GfxImageColorMap * colorMap,GBool interpolate,Stream * maskStr,int maskWidth,int maskHeight,GBool maskInvert,GBool maskInterpolate)3451 void CairoImageOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
3452 					  int width, int height,
3453 					  GfxImageColorMap *colorMap,
3454 					  GBool interpolate,
3455 					  Stream *maskStr,
3456 					  int maskWidth, int maskHeight,
3457 					  GBool maskInvert, GBool maskInterpolate)
3458 {
3459   cairo_t *cr;
3460   cairo_surface_t *surface;
3461   double x1, y1, x2, y2;
3462   CairoImage *image;
3463 
3464   getBBox(state, width, height, &x1, &y1, &x2, &y2);
3465 
3466   image = new CairoImage (x1, y1, x2, y2);
3467   saveImage (image);
3468 
3469   if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) {
3470     surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
3471     cr = cairo_create (surface);
3472     setCairo (cr);
3473     cairo_translate (cr, 0, height);
3474     cairo_scale (cr, width, -height);
3475 
3476     CairoOutputDev::drawMaskedImage(state, ref, str, width, height, colorMap, interpolate,
3477 				    maskStr, maskWidth, maskHeight, maskInvert, maskInterpolate);
3478     image->setImage (surface);
3479 
3480     setCairo (NULL);
3481     cairo_surface_destroy (surface);
3482     cairo_destroy (cr);
3483   }
3484 }
3485