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