1 //========================================================================
5 // Copyright 2003 Glyph & Cog, LLC
6 // Copyright 2004 Red Hat, Inc
8 //========================================================================
10 //========================================================================
12 // Modified under the Poppler project - http://poppler.freedesktop.org
14 // All changes made under the Poppler project to this file are licensed
15 // under GPL version 2 or later
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 Jason Crain <jason@aquaticape.us>
31 // Copyright (C) 2015 Suzuki Toshiya <mpsuzuki@hiroshima-u.ac.jp>
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
36 //========================================================================
40 #ifdef USE_GCC_PRAGMAS
41 #pragma implementation
49 #include "goo/gfile.h"
50 #include "goo/gtypes_p.h"
51 #include "GlobalParams.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 //------------------------------------------------------------------------
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
);
85 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
86 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
89 //------------------------------------------------------------------------
91 //------------------------------------------------------------------------
93 CairoImage::CairoImage (double x1
, double y1
, double x2
, double y2
) {
101 CairoImage::~CairoImage () {
103 cairo_surface_destroy (image
);
106 void CairoImage::setImage (cairo_surface_t
*image
) {
108 cairo_surface_destroy (this->image
);
109 this->image
= cairo_surface_reference (image
);
112 //------------------------------------------------------------------------
114 //------------------------------------------------------------------------
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
;
125 CairoOutputDev::CairoOutputDev() {
128 if (!ft_lib_initialized
) {
129 FT_Init_FreeType(&ft_lib
);
130 ft_lib_initialized
= gTrue
;
134 fontEngine_owner
= gFalse
;
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;
143 strokePathClip
= NULL
;
146 prescaleImages
= gTrue
;
148 use_show_text_glyphs
= gFalse
;
149 inUncoloredPattern
= gFalse
;
150 inType3Char
= gFalse
;
151 t3_glyph_has_bbox
= gFalse
;
152 text_matrix_valid
= gTrue
;
154 groupColorSpaceStack
= NULL
;
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
;
173 CairoOutputDev::~CairoOutputDev() {
174 if (fontEngine_owner
&& fontEngine
) {
179 cairo_destroy (cairo
);
180 cairo_pattern_destroy (stroke_pattern
);
181 cairo_pattern_destroy (fill_pattern
);
183 cairo_pattern_destroy (group
);
185 cairo_pattern_destroy (mask
);
187 cairo_pattern_destroy (shape
);
194 void CairoOutputDev::setCairo(cairo_t
*cairo
)
196 if (this->cairo
!= NULL
) {
197 cairo_status_t status
= cairo_status (this->cairo
);
199 error(errInternal
, -1, "cairo context error: {0:s}\n", cairo_status_to_string(status
));
201 cairo_destroy (this->cairo
);
202 assert(!cairo_shape
);
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
);
211 this->cairo_shape
= NULL
;
215 void CairoOutputDev::setTextPage(TextPage
*text
)
218 this->text
->decRefCnt();
223 this->text
->incRefCnt();
224 actualText
= new ActualText(text
);
231 void CairoOutputDev::startDoc(PDFDoc
*docA
,
232 CairoFontEngine
*parentFontEngine
) {
234 if (parentFontEngine
) {
235 fontEngine
= parentFontEngine
;
240 fontEngine
= new CairoFontEngine(ft_lib
);
241 fontEngine_owner
= gTrue
;
243 xref
= doc
->getXRef();
246 void CairoOutputDev::startPage(int pageNum
, GfxState
*state
, XRef
*xrefA
) {
247 /* set up some per page defaults */
248 cairo_pattern_destroy(fill_pattern
);
249 cairo_pattern_destroy(stroke_pattern
);
251 fill_pattern
= cairo_pattern_create_rgb(0., 0., 0.);
252 fill_color
.r
= fill_color
.g
= fill_color
.b
= 0;
253 stroke_pattern
= cairo_pattern_reference(fill_pattern
);
254 stroke_color
.r
= stroke_color
.g
= stroke_color
.b
= 0;
257 text
->startPage(state
);
263 void CairoOutputDev::endPage() {
266 text
->coalesce(gTrue
, 0, gFalse
);
270 void CairoOutputDev::saveState(GfxState
*state
) {
271 LOG(printf ("save\n"));
274 cairo_save (cairo_shape
);
276 MaskStack
*ms
= new MaskStack
;
277 ms
->mask
= cairo_pattern_reference(mask
);
278 ms
->mask_matrix
= mask_matrix
;
279 ms
->next
= maskStack
;
283 void CairoOutputDev::restoreState(GfxState
*state
) {
284 LOG(printf ("restore\n"));
285 cairo_restore (cairo
);
287 cairo_restore (cairo_shape
);
289 text_matrix_valid
= gTrue
;
291 /* These aren't restored by cairo_restore() since we keep them in
292 * the output device. */
293 updateFillColor(state
);
294 updateStrokeColor(state
);
295 updateFillOpacity(state
);
296 updateStrokeOpacity(state
);
297 updateBlendMode(state
);
299 MaskStack
* ms
= maskStack
;
302 cairo_pattern_destroy(mask
);
304 mask_matrix
= ms
->mask_matrix
;
305 maskStack
= ms
->next
;
310 void CairoOutputDev::updateAll(GfxState
*state
) {
311 updateLineDash(state
);
312 updateLineJoin(state
);
313 updateLineCap(state
);
314 updateLineWidth(state
);
315 updateFlatness(state
);
316 updateMiterLimit(state
);
317 updateFillColor(state
);
318 updateStrokeColor(state
);
319 updateFillOpacity(state
);
320 updateStrokeOpacity(state
);
321 updateBlendMode(state
);
322 needFontUpdate
= gTrue
;
324 text
->updateFont(state
);
327 void CairoOutputDev::setDefaultCTM(double *ctm
) {
328 cairo_matrix_t matrix
;
336 cairo_transform (cairo
, &matrix
);
338 cairo_transform (cairo_shape
, &matrix
);
340 OutputDev::setDefaultCTM(ctm
);
343 void CairoOutputDev::updateCTM(GfxState
*state
, double m11
, double m12
,
344 double m21
, double m22
,
345 double m31
, double m32
) {
346 cairo_matrix_t matrix
, invert_matrix
;
354 /* Make sure the matrix is invertible before setting it.
355 * cairo will blow up if we give it a matrix that's not
356 * invertible, so we need to check before passing it
357 * to cairo_transform. Ignoring it is likely to give better
358 * results than not rendering anything at all. See #14398
360 * Ideally, we could do the cairo_transform
361 * and then check if anything went wrong and fix it then
362 * instead of having to invert the matrix. */
363 invert_matrix
= matrix
;
364 if (cairo_matrix_invert(&invert_matrix
)) {
365 error(errSyntaxWarning
, -1, "matrix not invertible\n");
369 cairo_transform (cairo
, &matrix
);
371 cairo_transform (cairo_shape
, &matrix
);
372 updateLineDash(state
);
373 updateLineJoin(state
);
374 updateLineCap(state
);
375 updateLineWidth(state
);
378 void CairoOutputDev::updateLineDash(GfxState
*state
) {
383 state
->getLineDash(&dashPattern
, &dashLength
, &dashStart
);
384 cairo_set_dash (cairo
, dashPattern
, dashLength
, dashStart
);
386 cairo_set_dash (cairo_shape
, dashPattern
, dashLength
, dashStart
);
389 void CairoOutputDev::updateFlatness(GfxState
*state
) {
390 // cairo_set_tolerance (cairo, state->getFlatness());
393 void CairoOutputDev::updateLineJoin(GfxState
*state
) {
394 switch (state
->getLineJoin()) {
396 cairo_set_line_join (cairo
, CAIRO_LINE_JOIN_MITER
);
399 cairo_set_line_join (cairo
, CAIRO_LINE_JOIN_ROUND
);
402 cairo_set_line_join (cairo
, CAIRO_LINE_JOIN_BEVEL
);
406 cairo_set_line_join (cairo_shape
, cairo_get_line_join(cairo
));
409 void CairoOutputDev::updateLineCap(GfxState
*state
) {
410 switch (state
->getLineCap()) {
412 cairo_set_line_cap (cairo
, CAIRO_LINE_CAP_BUTT
);
415 cairo_set_line_cap (cairo
, CAIRO_LINE_CAP_ROUND
);
418 cairo_set_line_cap (cairo
, CAIRO_LINE_CAP_SQUARE
);
422 cairo_set_line_cap (cairo_shape
, cairo_get_line_cap(cairo
));
425 void CairoOutputDev::updateMiterLimit(GfxState
*state
) {
426 cairo_set_miter_limit (cairo
, state
->getMiterLimit());
428 cairo_set_miter_limit (cairo_shape
, state
->getMiterLimit());
431 void CairoOutputDev::updateLineWidth(GfxState
*state
) {
432 LOG(printf ("line width: %f\n", state
->getLineWidth()));
433 adjusted_stroke_width
= gFalse
;
434 double width
= state
->getLineWidth();
435 if (stroke_adjust
&& !printing
) {
439 /* find out line width in device units */
440 cairo_user_to_device_distance(cairo
, &x
, &y
);
441 if (fabs(x
) <= 1.0 && fabs(y
) <= 1.0) {
442 /* adjust width to at least one device pixel */
444 cairo_device_to_user_distance(cairo
, &x
, &y
);
445 width
= MIN(fabs(x
),fabs(y
));
446 adjusted_stroke_width
= gTrue
;
448 } else if (width
== 0.0) {
449 /* Cairo does not support 0 line width == 1 device pixel. Find out
450 * how big pixels (device unit) are in the x and y
451 * directions. Choose the smaller of the two as our line width.
453 double x
= 1.0, y
= 1.0;
455 // assume printer pixel size is 1/600 inch
459 cairo_device_to_user_distance(cairo
, &x
, &y
);
460 width
= MIN(fabs(x
),fabs(y
));
462 cairo_set_line_width (cairo
, width
);
464 cairo_set_line_width (cairo_shape
, cairo_get_line_width (cairo
));
467 void CairoOutputDev::updateFillColor(GfxState
*state
) {
468 GfxRGB color
= fill_color
;
470 if (inUncoloredPattern
)
473 state
->getFillRGB(&fill_color
);
474 if (cairo_pattern_get_type (fill_pattern
) != CAIRO_PATTERN_TYPE_SOLID
||
475 color
.r
!= fill_color
.r
||
476 color
.g
!= fill_color
.g
||
477 color
.b
!= fill_color
.b
)
479 cairo_pattern_destroy(fill_pattern
);
480 fill_pattern
= cairo_pattern_create_rgba(colToDbl(fill_color
.r
),
481 colToDbl(fill_color
.g
),
482 colToDbl(fill_color
.b
),
485 LOG(printf ("fill color: %d %d %d\n",
486 fill_color
.r
, fill_color
.g
, fill_color
.b
));
490 void CairoOutputDev::updateStrokeColor(GfxState
*state
) {
491 GfxRGB color
= stroke_color
;
493 if (inUncoloredPattern
)
496 state
->getStrokeRGB(&stroke_color
);
497 if (cairo_pattern_get_type (fill_pattern
) != CAIRO_PATTERN_TYPE_SOLID
||
498 color
.r
!= stroke_color
.r
||
499 color
.g
!= stroke_color
.g
||
500 color
.b
!= stroke_color
.b
)
502 cairo_pattern_destroy(stroke_pattern
);
503 stroke_pattern
= cairo_pattern_create_rgba(colToDbl(stroke_color
.r
),
504 colToDbl(stroke_color
.g
),
505 colToDbl(stroke_color
.b
),
508 LOG(printf ("stroke color: %d %d %d\n",
509 stroke_color
.r
, stroke_color
.g
, stroke_color
.b
));
513 void CairoOutputDev::updateFillOpacity(GfxState
*state
) {
514 double opacity
= fill_opacity
;
516 if (inUncoloredPattern
)
519 fill_opacity
= state
->getFillOpacity();
520 if (opacity
!= fill_opacity
) {
521 cairo_pattern_destroy(fill_pattern
);
522 fill_pattern
= cairo_pattern_create_rgba(colToDbl(fill_color
.r
),
523 colToDbl(fill_color
.g
),
524 colToDbl(fill_color
.b
),
527 LOG(printf ("fill opacity: %f\n", fill_opacity
));
531 void CairoOutputDev::updateStrokeOpacity(GfxState
*state
) {
532 double opacity
= stroke_opacity
;
534 if (inUncoloredPattern
)
537 stroke_opacity
= state
->getStrokeOpacity();
538 if (opacity
!= stroke_opacity
) {
539 cairo_pattern_destroy(stroke_pattern
);
540 stroke_pattern
= cairo_pattern_create_rgba(colToDbl(stroke_color
.r
),
541 colToDbl(stroke_color
.g
),
542 colToDbl(stroke_color
.b
),
545 LOG(printf ("stroke opacity: %f\n", stroke_opacity
));
549 void CairoOutputDev::updateFillColorStop(GfxState
*state
, double offset
) {
550 if (inUncoloredPattern
)
553 state
->getFillRGB(&fill_color
);
555 cairo_pattern_add_color_stop_rgba(fill_pattern
, offset
,
556 colToDbl(fill_color
.r
),
557 colToDbl(fill_color
.g
),
558 colToDbl(fill_color
.b
),
560 LOG(printf ("fill color stop: %f (%d, %d, %d)\n",
561 offset
, fill_color
.r
, fill_color
.g
, fill_color
.b
));
564 void CairoOutputDev::updateBlendMode(GfxState
*state
) {
565 switch (state
->getBlendMode()) {
568 cairo_set_operator (cairo
, CAIRO_OPERATOR_OVER
);
570 case gfxBlendMultiply
:
571 cairo_set_operator (cairo
, CAIRO_OPERATOR_MULTIPLY
);
574 cairo_set_operator (cairo
, CAIRO_OPERATOR_SCREEN
);
576 case gfxBlendOverlay
:
577 cairo_set_operator (cairo
, CAIRO_OPERATOR_OVERLAY
);
580 cairo_set_operator (cairo
, CAIRO_OPERATOR_DARKEN
);
582 case gfxBlendLighten
:
583 cairo_set_operator (cairo
, CAIRO_OPERATOR_LIGHTEN
);
585 case gfxBlendColorDodge
:
586 cairo_set_operator (cairo
, CAIRO_OPERATOR_COLOR_DODGE
);
588 case gfxBlendColorBurn
:
589 cairo_set_operator (cairo
, CAIRO_OPERATOR_COLOR_BURN
);
591 case gfxBlendHardLight
:
592 cairo_set_operator (cairo
, CAIRO_OPERATOR_HARD_LIGHT
);
594 case gfxBlendSoftLight
:
595 cairo_set_operator (cairo
, CAIRO_OPERATOR_SOFT_LIGHT
);
597 case gfxBlendDifference
:
598 cairo_set_operator (cairo
, CAIRO_OPERATOR_DIFFERENCE
);
600 case gfxBlendExclusion
:
601 cairo_set_operator (cairo
, CAIRO_OPERATOR_EXCLUSION
);
604 cairo_set_operator (cairo
, CAIRO_OPERATOR_HSL_HUE
);
606 case gfxBlendSaturation
:
607 cairo_set_operator (cairo
, CAIRO_OPERATOR_HSL_SATURATION
);
610 cairo_set_operator (cairo
, CAIRO_OPERATOR_HSL_COLOR
);
612 case gfxBlendLuminosity
:
613 cairo_set_operator (cairo
, CAIRO_OPERATOR_HSL_LUMINOSITY
);
616 LOG(printf ("blend mode: %d\n", (int)state
->getBlendMode()));
619 void CairoOutputDev::updateFont(GfxState
*state
) {
620 cairo_font_face_t
*font_face
;
621 cairo_matrix_t matrix
, invert_matrix
;
623 LOG(printf ("updateFont() font=%s\n", state
->getFont()->getName()->getCString()));
625 needFontUpdate
= gFalse
;
627 //FIXME: use cairo font engine?
629 text
->updateFont(state
);
631 currentFont
= fontEngine
->getFont (state
->getFont(), doc
, printing
, xref
);
636 font_face
= currentFont
->getFontFace();
637 cairo_set_font_face (cairo
, font_face
);
639 use_show_text_glyphs
= state
->getFont()->hasToUnicodeCMap() &&
640 cairo_surface_has_show_text_glyphs (cairo_get_target (cairo
));
642 double fontSize
= state
->getFontSize();
643 double *m
= state
->getTextMat();
644 /* NOTE: adjusting by a constant is hack. The correct solution
645 * is probably to use user-fonts and compute the scale on a per
646 * glyph basis instead of for the entire font */
647 double w
= currentFont
->getSubstitutionCorrection(state
->getFont());
648 matrix
.xx
= m
[0] * fontSize
* state
->getHorizScaling() * w
;
649 matrix
.yx
= m
[1] * fontSize
* state
->getHorizScaling() * w
;
650 matrix
.xy
= -m
[2] * fontSize
;
651 matrix
.yy
= -m
[3] * fontSize
;
655 LOG(printf ("font matrix: %f %f %f %f\n", matrix
.xx
, matrix
.yx
, matrix
.xy
, matrix
.yy
));
657 /* Make sure the font matrix is invertible before setting it. cairo
658 * will blow up if we give it a matrix that's not invertible, so we
659 * need to check before passing it to cairo_set_font_matrix. Ignoring it
660 * is likely to give better results than not rendering anything at
663 invert_matrix
= matrix
;
664 if (cairo_matrix_invert(&invert_matrix
)) {
665 error(errSyntaxWarning
, -1, "font matrix not invertible");
666 text_matrix_valid
= gFalse
;
670 cairo_set_font_matrix (cairo
, &matrix
);
671 text_matrix_valid
= gTrue
;
674 /* Tolerance in pixels for checking if strokes are horizontal or vertical
675 * lines in device space */
676 #define STROKE_COORD_TOLERANCE 0.5
678 /* Align stroke coordinate i if the point is the start or end of a
679 * horizontal or vertical line */
680 void CairoOutputDev::alignStrokeCoords(GfxSubpath
*subpath
, int i
, double *x
, double *y
)
682 double x1
, y1
, x2
, y2
;
683 GBool align
= gFalse
;
685 x1
= subpath
->getX(i
);
686 y1
= subpath
->getY(i
);
687 cairo_user_to_device (cairo
, &x1
, &y1
);
689 // Does the current coord and prev coord form a horiz or vert line?
690 if (i
> 0 && !subpath
->getCurve(i
- 1)) {
691 x2
= subpath
->getX(i
- 1);
692 y2
= subpath
->getY(i
- 1);
693 cairo_user_to_device (cairo
, &x2
, &y2
);
694 if (fabs(x2
- x1
) < STROKE_COORD_TOLERANCE
|| fabs(y2
- y1
) < STROKE_COORD_TOLERANCE
)
698 // Does the current coord and next coord form a horiz or vert line?
699 if (i
< subpath
->getNumPoints() - 1 && !subpath
->getCurve(i
+ 1)) {
700 x2
= subpath
->getX(i
+ 1);
701 y2
= subpath
->getY(i
+ 1);
702 cairo_user_to_device (cairo
, &x2
, &y2
);
703 if (fabs(x2
- x1
) < STROKE_COORD_TOLERANCE
|| fabs(y2
- y1
) < STROKE_COORD_TOLERANCE
)
707 *x
= subpath
->getX(i
);
708 *y
= subpath
->getY(i
);
710 /* see http://www.cairographics.org/FAQ/#sharp_lines */
711 cairo_user_to_device (cairo
, x
, y
);
712 *x
= floor(*x
) + 0.5;
713 *y
= floor(*y
) + 0.5;
714 cairo_device_to_user (cairo
, x
, y
);
718 #undef STROKE_COORD_TOLERANCE
720 void CairoOutputDev::doPath(cairo_t
*cairo
, GfxState
*state
, GfxPath
*path
) {
724 cairo_new_path (cairo
);
725 for (i
= 0; i
< path
->getNumSubpaths(); ++i
) {
726 subpath
= path
->getSubpath(i
);
727 if (subpath
->getNumPoints() > 0) {
728 if (align_stroke_coords
) {
729 alignStrokeCoords(subpath
, 0, &x
, &y
);
731 x
= subpath
->getX(0);
732 y
= subpath
->getY(0);
734 cairo_move_to (cairo
, x
, y
);
736 while (j
< subpath
->getNumPoints()) {
737 if (subpath
->getCurve(j
)) {
738 if (align_stroke_coords
) {
739 alignStrokeCoords(subpath
, j
+ 2, &x
, &y
);
741 x
= subpath
->getX(j
+2);
742 y
= subpath
->getY(j
+2);
744 cairo_curve_to( cairo
,
745 subpath
->getX(j
), subpath
->getY(j
),
746 subpath
->getX(j
+1), subpath
->getY(j
+1),
751 if (align_stroke_coords
) {
752 alignStrokeCoords(subpath
, j
, &x
, &y
);
754 x
= subpath
->getX(j
);
755 y
= subpath
->getY(j
);
757 cairo_line_to (cairo
, x
, y
);
761 if (subpath
->isClosed()) {
762 LOG (printf ("close\n"));
763 cairo_close_path (cairo
);
769 void CairoOutputDev::stroke(GfxState
*state
) {
772 state
->getFillGray(&gray
);
773 if (colToDbl(gray
) > 0.5)
777 if (adjusted_stroke_width
)
778 align_stroke_coords
= gTrue
;
779 doPath (cairo
, state
, state
->getPath());
780 align_stroke_coords
= gFalse
;
781 cairo_set_source (cairo
, stroke_pattern
);
782 LOG(printf ("stroke\n"));
783 cairo_stroke (cairo
);
785 doPath (cairo_shape
, state
, state
->getPath());
786 cairo_stroke (cairo_shape
);
790 void CairoOutputDev::fill(GfxState
*state
) {
793 state
->getFillGray(&gray
);
794 if (colToDbl(gray
) > 0.5)
798 doPath (cairo
, state
, state
->getPath());
799 cairo_set_fill_rule (cairo
, CAIRO_FILL_RULE_WINDING
);
800 cairo_set_source (cairo
, fill_pattern
);
801 LOG(printf ("fill\n"));
802 //XXX: how do we get the path
806 cairo_set_matrix (cairo
, &mask_matrix
);
807 cairo_mask (cairo
, mask
);
808 cairo_restore (cairo
);
809 } else if (strokePathClip
) {
810 fillToStrokePathClip(state
);
815 cairo_set_fill_rule (cairo_shape
, CAIRO_FILL_RULE_WINDING
);
816 doPath (cairo_shape
, state
, state
->getPath());
817 cairo_fill (cairo_shape
);
821 void CairoOutputDev::eoFill(GfxState
*state
) {
822 doPath (cairo
, state
, state
->getPath());
823 cairo_set_fill_rule (cairo
, CAIRO_FILL_RULE_EVEN_ODD
);
824 cairo_set_source (cairo
, fill_pattern
);
825 LOG(printf ("fill-eo\n"));
830 cairo_set_matrix (cairo
, &mask_matrix
);
831 cairo_mask (cairo
, mask
);
832 cairo_restore (cairo
);
837 cairo_set_fill_rule (cairo_shape
, CAIRO_FILL_RULE_EVEN_ODD
);
838 doPath (cairo_shape
, state
, state
->getPath());
839 cairo_fill (cairo_shape
);
844 GBool
CairoOutputDev::tilingPatternFill(GfxState
*state
, Gfx
*gfxA
, Catalog
*cat
, Object
*str
,
845 double *pmat
, int paintType
, int /*tilingType*/, Dict
*resDict
,
846 double *mat
, double *bbox
,
847 int x0
, int y0
, int x1
, int y1
,
848 double xStep
, double yStep
)
852 cairo_pattern_t
*pattern
;
853 cairo_surface_t
*surface
;
854 cairo_matrix_t matrix
;
855 cairo_matrix_t pattern_matrix
;
857 double xMin
, yMin
, xMax
, yMax
;
858 double width
, height
;
859 int surface_width
, surface_height
;
860 StrokePathClip
*strokePathTmp
;
861 GBool adjusted_stroke_width_tmp
;
863 width
= bbox
[2] - bbox
[0];
864 height
= bbox
[3] - bbox
[1];
866 if (xStep
!= width
|| yStep
!= height
)
868 /* TODO: implement the other cases here too */
870 // Find the width and height of the transformed pattern
871 cairo_get_matrix (cairo
, &matrix
);
872 cairo_matrix_init (&pattern_matrix
, mat
[0], mat
[1], mat
[2], mat
[3], mat
[4], mat
[5]);
873 cairo_matrix_multiply (&matrix
, &matrix
, &pattern_matrix
);
875 double widthX
= width
, widthY
= 0;
876 cairo_matrix_transform_distance (&matrix
, &widthX
, &widthY
);
877 surface_width
= ceil (sqrt (widthX
* widthX
+ widthY
* widthY
));
879 double heightX
= 0, heightY
= height
;
880 cairo_matrix_transform_distance (&matrix
, &heightX
, &heightY
);
881 surface_height
= ceil (sqrt (heightX
* heightX
+ heightY
* heightY
));
883 surface
= cairo_surface_create_similar (cairo_get_target (cairo
),
884 CAIRO_CONTENT_COLOR_ALPHA
,
885 surface_width
, surface_height
);
886 if (cairo_surface_status (surface
))
890 cairo
= cairo_create (surface
);
891 cairo_surface_destroy (surface
);
892 cairo_scale (cairo
, surface_width
/ width
, surface_height
/ height
);
894 box
.x1
= bbox
[0]; box
.y1
= bbox
[1];
895 box
.x2
= bbox
[2]; box
.y2
= bbox
[3];
896 strokePathTmp
= strokePathClip
;
897 strokePathClip
= NULL
;
898 adjusted_stroke_width_tmp
= adjusted_stroke_width
;
899 gfx
= new Gfx(doc
, this, resDict
, &box
, NULL
, NULL
, NULL
, gfxA
->getXRef());
901 inUncoloredPattern
= gTrue
;
904 inUncoloredPattern
= gFalse
;
906 strokePathClip
= strokePathTmp
;
907 adjusted_stroke_width
= adjusted_stroke_width_tmp
;
909 pattern
= cairo_pattern_create_for_surface (cairo_get_target (cairo
));
910 cairo_destroy (cairo
);
912 if (cairo_pattern_status (pattern
))
915 state
->getUserClipBBox(&xMin
, &yMin
, &xMax
, &yMax
);
916 cairo_rectangle (cairo
, xMin
, yMin
, xMax
- xMin
, yMax
- yMin
);
918 cairo_matrix_init_scale (&matrix
, surface_width
/ width
, surface_height
/ height
);
919 cairo_pattern_set_matrix (pattern
, &matrix
);
921 cairo_transform (cairo
, &pattern_matrix
);
922 cairo_set_source (cairo
, pattern
);
923 cairo_pattern_set_extend (pattern
, CAIRO_EXTEND_REPEAT
);
924 if (strokePathClip
) {
925 fillToStrokePathClip(state
);
930 cairo_pattern_destroy (pattern
);
935 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0)
936 GBool
CairoOutputDev::functionShadedFill(GfxState
*state
, GfxFunctionShading
*shading
)
938 // Function shaded fills are subdivided to rectangles that are the
939 // following size in device space. Note when printing this size is
941 const int subdivide_pixels
= 10;
943 double x_begin
, x_end
, x1
, x2
;
944 double y_begin
, y_end
, y1
, y2
;
952 matrix
= shading
->getMatrix();
959 if (cairo_matrix_invert(&mat
)) {
960 error(errSyntaxWarning
, -1, "matrix not invertible\n");
964 // get cell size in pattern space
965 x_step
= y_step
= subdivide_pixels
;
966 cairo_matrix_transform_distance (&mat
, &x_step
, &y_step
);
968 cairo_pattern_destroy(fill_pattern
);
969 fill_pattern
= cairo_pattern_create_mesh ();
970 cairo_pattern_set_matrix(fill_pattern
, &mat
);
971 shading
->getDomain(&x_begin
, &y_begin
, &x_end
, &y_end
);
973 for (x1
= x_begin
; x1
< x_end
; x1
+= x_step
) {
978 for (y1
= y_begin
; y1
< y_end
; y1
+= y_step
) {
983 cairo_mesh_pattern_begin_patch (fill_pattern
);
984 cairo_mesh_pattern_move_to (fill_pattern
, x1
, y1
);
985 cairo_mesh_pattern_line_to (fill_pattern
, x2
, y1
);
986 cairo_mesh_pattern_line_to (fill_pattern
, x2
, y2
);
987 cairo_mesh_pattern_line_to (fill_pattern
, x1
, y2
);
989 shading
->getColor(x1
, y1
, &color
);
990 shading
->getColorSpace()->getRGB(&color
, &rgb
);
991 cairo_mesh_pattern_set_corner_color_rgb (fill_pattern
, 0,
996 shading
->getColor(x2
, y1
, &color
);
997 shading
->getColorSpace()->getRGB(&color
, &rgb
);
998 cairo_mesh_pattern_set_corner_color_rgb (fill_pattern
, 1,
1003 shading
->getColor(x2
, y2
, &color
);
1004 shading
->getColorSpace()->getRGB(&color
, &rgb
);
1005 cairo_mesh_pattern_set_corner_color_rgb (fill_pattern
, 2,
1010 shading
->getColor(x1
, y2
, &color
);
1011 shading
->getColorSpace()->getRGB(&color
, &rgb
);
1012 cairo_mesh_pattern_set_corner_color_rgb (fill_pattern
, 3,
1017 cairo_mesh_pattern_end_patch (fill_pattern
);
1021 double xMin
, yMin
, xMax
, yMax
;
1022 // get the clip region bbox
1023 state
->getUserClipBBox(&xMin
, &yMin
, &xMax
, &yMax
);
1024 state
->moveTo(xMin
, yMin
);
1025 state
->lineTo(xMin
, yMax
);
1026 state
->lineTo(xMax
, yMax
);
1027 state
->lineTo(xMax
, yMin
);
1034 #endif /* CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) */
1036 GBool
CairoOutputDev::axialShadedFill(GfxState
*state
, GfxAxialShading
*shading
, double tMin
, double tMax
) {
1037 double x0
, y0
, x1
, y1
;
1040 shading
->getCoords(&x0
, &y0
, &x1
, &y1
);
1044 cairo_pattern_destroy(fill_pattern
);
1045 fill_pattern
= cairo_pattern_create_linear (x0
+ tMin
* dx
, y0
+ tMin
* dy
,
1046 x0
+ tMax
* dx
, y0
+ tMax
* dy
);
1047 if (!shading
->getExtend0() && !shading
->getExtend1())
1048 cairo_pattern_set_extend (fill_pattern
, CAIRO_EXTEND_NONE
);
1050 cairo_pattern_set_extend (fill_pattern
, CAIRO_EXTEND_PAD
);
1052 LOG (printf ("axial-sh\n"));
1054 // TODO: use the actual stops in the shading in the case
1055 // of linear interpolation (Type 2 Exponential functions with N=1)
1059 GBool
CairoOutputDev::axialShadedSupportExtend(GfxState
*state
, GfxAxialShading
*shading
)
1061 return (shading
->getExtend0() == shading
->getExtend1());
1064 GBool
CairoOutputDev::radialShadedFill(GfxState
*state
, GfxRadialShading
*shading
, double sMin
, double sMax
) {
1065 double x0
, y0
, r0
, x1
, y1
, r1
;
1067 cairo_matrix_t matrix
;
1070 shading
->getCoords(&x0
, &y0
, &r0
, &x1
, &y1
, &r1
);
1075 // Cairo/pixman do not work well with a very large or small scaled
1076 // matrix. See cairo bug #81657.
1078 // As a workaround, scale the pattern by the average of the vertical
1079 // and horizontal scaling of the current transformation matrix.
1080 cairo_get_matrix(cairo
, &matrix
);
1081 scale
= (sqrt(matrix
.xx
* matrix
.xx
+ matrix
.yx
* matrix
.yx
)
1082 + sqrt(matrix
.xy
* matrix
.xy
+ matrix
.yy
* matrix
.yy
)) / 2;
1083 cairo_matrix_init_scale(&matrix
, scale
, scale
);
1085 cairo_pattern_destroy(fill_pattern
);
1086 fill_pattern
= cairo_pattern_create_radial ((x0
+ sMin
* dx
) * scale
,
1087 (y0
+ sMin
* dy
) * scale
,
1088 (r0
+ sMin
* dr
) * scale
,
1089 (x0
+ sMax
* dx
) * scale
,
1090 (y0
+ sMax
* dy
) * scale
,
1091 (r0
+ sMax
* dr
) * scale
);
1092 cairo_pattern_set_matrix(fill_pattern
, &matrix
);
1093 if (shading
->getExtend0() && shading
->getExtend1())
1094 cairo_pattern_set_extend (fill_pattern
, CAIRO_EXTEND_PAD
);
1096 cairo_pattern_set_extend (fill_pattern
, CAIRO_EXTEND_NONE
);
1098 LOG (printf ("radial-sh\n"));
1103 GBool
CairoOutputDev::radialShadedSupportExtend(GfxState
*state
, GfxRadialShading
*shading
)
1105 return (shading
->getExtend0() == shading
->getExtend1());
1108 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0)
1109 GBool
CairoOutputDev::gouraudTriangleShadedFill(GfxState
*state
, GfxGouraudTriangleShading
*shading
)
1111 double x0
, y0
, x1
, y1
, x2
, y2
;
1116 cairo_pattern_destroy(fill_pattern
);
1117 fill_pattern
= cairo_pattern_create_mesh ();
1119 for (i
= 0; i
< shading
->getNTriangles(); i
++) {
1120 if (shading
->isParameterized()) {
1121 double color0
, color1
, color2
;
1122 shading
->getTriangle(i
, &x0
, &y0
, &color0
,
1125 shading
->getParameterizedColor(color0
, &color
[0]);
1126 shading
->getParameterizedColor(color1
, &color
[1]);
1127 shading
->getParameterizedColor(color2
, &color
[2]);
1129 shading
->getTriangle(i
,
1130 &x0
, &y0
, &color
[0],
1131 &x1
, &y1
, &color
[1],
1132 &x2
, &y2
, &color
[2]);
1136 cairo_mesh_pattern_begin_patch (fill_pattern
);
1138 cairo_mesh_pattern_move_to (fill_pattern
, x0
, y0
);
1139 cairo_mesh_pattern_line_to (fill_pattern
, x1
, y1
);
1140 cairo_mesh_pattern_line_to (fill_pattern
, x2
, y2
);
1142 for (j
= 0; j
< 3; j
++) {
1143 shading
->getColorSpace()->getRGB(&color
[j
], &rgb
);
1144 cairo_mesh_pattern_set_corner_color_rgb (fill_pattern
, j
,
1150 cairo_mesh_pattern_end_patch (fill_pattern
);
1153 double xMin
, yMin
, xMax
, yMax
;
1154 // get the clip region bbox
1155 state
->getUserClipBBox(&xMin
, &yMin
, &xMax
, &yMax
);
1156 state
->moveTo(xMin
, yMin
);
1157 state
->lineTo(xMin
, yMax
);
1158 state
->lineTo(xMax
, yMax
);
1159 state
->lineTo(xMax
, yMin
);
1167 GBool
CairoOutputDev::patchMeshShadedFill(GfxState
*state
, GfxPatchMeshShading
*shading
)
1171 cairo_pattern_destroy(fill_pattern
);
1172 fill_pattern
= cairo_pattern_create_mesh ();
1174 for (i
= 0; i
< shading
->getNPatches(); i
++) {
1175 GfxPatch
*patch
= shading
->getPatch(i
);
1179 cairo_mesh_pattern_begin_patch (fill_pattern
);
1181 cairo_mesh_pattern_move_to (fill_pattern
, patch
->x
[0][0], patch
->y
[0][0]);
1182 cairo_mesh_pattern_curve_to (fill_pattern
,
1183 patch
->x
[0][1], patch
->y
[0][1],
1184 patch
->x
[0][2], patch
->y
[0][2],
1185 patch
->x
[0][3], patch
->y
[0][3]);
1187 cairo_mesh_pattern_curve_to (fill_pattern
,
1188 patch
->x
[1][3], patch
->y
[1][3],
1189 patch
->x
[2][3], patch
->y
[2][3],
1190 patch
->x
[3][3], patch
->y
[3][3]);
1192 cairo_mesh_pattern_curve_to (fill_pattern
,
1193 patch
->x
[3][2], patch
->y
[3][2],
1194 patch
->x
[3][1], patch
->y
[3][1],
1195 patch
->x
[3][0], patch
->y
[3][0]);
1197 cairo_mesh_pattern_curve_to (fill_pattern
,
1198 patch
->x
[2][0], patch
->y
[2][0],
1199 patch
->x
[1][0], patch
->y
[1][0],
1200 patch
->x
[0][0], patch
->y
[0][0]);
1202 cairo_mesh_pattern_set_control_point (fill_pattern
, 0, patch
->x
[1][1], patch
->y
[1][1]);
1203 cairo_mesh_pattern_set_control_point (fill_pattern
, 1, patch
->x
[1][2], patch
->y
[1][2]);
1204 cairo_mesh_pattern_set_control_point (fill_pattern
, 2, patch
->x
[2][2], patch
->y
[2][2]);
1205 cairo_mesh_pattern_set_control_point (fill_pattern
, 3, patch
->x
[2][1], patch
->y
[2][1]);
1207 for (j
= 0; j
< 4; j
++) {
1225 if (shading
->isParameterized()) {
1226 shading
->getParameterizedColor (patch
->color
[u
][v
].c
[0], &color
);
1228 for (k
= 0; k
< shading
->getColorSpace()->getNComps(); k
++) {
1229 // simply cast to the desired type; that's all what is needed.
1230 color
.c
[k
] = GfxColorComp (patch
->color
[u
][v
].c
[k
]);
1234 shading
->getColorSpace()->getRGB(&color
, &rgb
);
1235 cairo_mesh_pattern_set_corner_color_rgb (fill_pattern
, j
,
1240 cairo_mesh_pattern_end_patch (fill_pattern
);
1243 double xMin
, yMin
, xMax
, yMax
;
1244 // get the clip region bbox
1245 state
->getUserClipBBox(&xMin
, &yMin
, &xMax
, &yMax
);
1246 state
->moveTo(xMin
, yMin
);
1247 state
->lineTo(xMin
, yMax
);
1248 state
->lineTo(xMax
, yMax
);
1249 state
->lineTo(xMax
, yMin
);
1256 #endif /* CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) */
1258 void CairoOutputDev::clip(GfxState
*state
) {
1259 doPath (cairo
, state
, state
->getPath());
1260 cairo_set_fill_rule (cairo
, CAIRO_FILL_RULE_WINDING
);
1262 LOG (printf ("clip\n"));
1264 doPath (cairo_shape
, state
, state
->getPath());
1265 cairo_set_fill_rule (cairo_shape
, CAIRO_FILL_RULE_WINDING
);
1266 cairo_clip (cairo_shape
);
1270 void CairoOutputDev::eoClip(GfxState
*state
) {
1271 doPath (cairo
, state
, state
->getPath());
1272 cairo_set_fill_rule (cairo
, CAIRO_FILL_RULE_EVEN_ODD
);
1274 LOG (printf ("clip-eo\n"));
1276 doPath (cairo_shape
, state
, state
->getPath());
1277 cairo_set_fill_rule (cairo_shape
, CAIRO_FILL_RULE_EVEN_ODD
);
1278 cairo_clip (cairo_shape
);
1283 void CairoOutputDev::clipToStrokePath(GfxState
*state
) {
1284 LOG(printf("clip-to-stroke-path\n"));
1285 strokePathClip
= (StrokePathClip
*)gmalloc (sizeof(*strokePathClip
));
1286 strokePathClip
->path
= state
->getPath()->copy();
1287 cairo_get_matrix (cairo
, &strokePathClip
->ctm
);
1288 strokePathClip
->line_width
= cairo_get_line_width (cairo
);
1289 strokePathClip
->dash_count
= cairo_get_dash_count (cairo
);
1290 if (strokePathClip
->dash_count
) {
1291 strokePathClip
->dashes
= (double*) gmallocn (sizeof(double), strokePathClip
->dash_count
);
1292 cairo_get_dash (cairo
, strokePathClip
->dashes
, &strokePathClip
->dash_offset
);
1294 strokePathClip
->dashes
= NULL
;
1296 strokePathClip
->cap
= cairo_get_line_cap (cairo
);
1297 strokePathClip
->join
= cairo_get_line_join (cairo
);
1298 strokePathClip
->miter
= cairo_get_miter_limit (cairo
);
1301 void CairoOutputDev::fillToStrokePathClip(GfxState
*state
) {
1304 cairo_set_matrix (cairo
, &strokePathClip
->ctm
);
1305 cairo_set_line_width (cairo
, strokePathClip
->line_width
);
1306 strokePathClip
->dash_count
= cairo_get_dash_count (cairo
);
1307 cairo_set_dash (cairo
, strokePathClip
->dashes
, strokePathClip
->dash_count
, strokePathClip
->dash_offset
);
1308 cairo_set_line_cap (cairo
, strokePathClip
->cap
);
1309 cairo_set_line_join (cairo
, strokePathClip
->join
);
1310 cairo_set_miter_limit (cairo
, strokePathClip
->miter
);
1311 doPath (cairo
, state
, strokePathClip
->path
);
1312 cairo_stroke (cairo
);
1314 cairo_restore (cairo
);
1316 delete strokePathClip
->path
;
1317 if (strokePathClip
->dashes
)
1318 gfree (strokePathClip
->dashes
);
1319 gfree (strokePathClip
);
1320 strokePathClip
= NULL
;
1323 void CairoOutputDev::beginString(GfxState
*state
, GooString
*s
)
1325 int len
= s
->getLength();
1333 glyphs
= (cairo_glyph_t
*) gmallocn (len
, sizeof (cairo_glyph_t
));
1335 if (use_show_text_glyphs
) {
1336 clusters
= (cairo_text_cluster_t
*) gmallocn (len
, sizeof (cairo_text_cluster_t
));
1338 utf8Max
= len
*2; // start with twice the number of glyphs. we will realloc if we need more.
1339 utf8
= (char *) gmalloc (utf8Max
);
1344 void CairoOutputDev::drawChar(GfxState
*state
, double x
, double y
,
1345 double dx
, double dy
,
1346 double originX
, double originY
,
1347 CharCode code
, int nBytes
, Unicode
*u
, int uLen
)
1350 glyphs
[glyphCount
].index
= currentFont
->getGlyph (code
, u
, uLen
);
1351 glyphs
[glyphCount
].x
= x
- originX
;
1352 glyphs
[glyphCount
].y
= y
- originY
;
1354 if (use_show_text_glyphs
) {
1355 GooString
enc("UTF-8");
1356 UnicodeMap
*utf8Map
= globalParams
->getUnicodeMap(&enc
);
1357 if (utf8Max
- utf8Count
< uLen
*6) {
1358 // utf8 encoded characters can be up to 6 bytes
1359 if (utf8Max
> uLen
*6)
1362 utf8Max
+= 2*uLen
*6;
1363 utf8
= (char *) grealloc (utf8
, utf8Max
);
1365 clusters
[clusterCount
].num_bytes
= 0;
1366 for (int i
= 0; i
< uLen
; i
++) {
1367 int size
= utf8Map
->mapUnicode(u
[i
], utf8
+ utf8Count
, utf8Max
- utf8Count
);
1369 clusters
[clusterCount
].num_bytes
+= size
;
1371 clusters
[clusterCount
].num_glyphs
= 1;
1378 actualText
->addChar (state
, x
, y
, dx
, dy
, code
, nBytes
, u
, uLen
);
1381 void CairoOutputDev::endString(GfxState
*state
)
1388 // endString can be called without a corresponding beginString. If this
1389 // happens glyphs will be null so don't draw anything, just return.
1390 // XXX: OutputDevs should probably not have to deal with this...
1394 // ignore empty strings and invisible text -- this is used by
1396 render
= state
->getRender();
1397 if (render
== 3 || glyphCount
== 0 || !text_matrix_valid
) {
1401 if (!(render
& 1)) {
1402 LOG (printf ("fill string\n"));
1403 cairo_set_source (cairo
, fill_pattern
);
1404 if (use_show_text_glyphs
)
1405 cairo_show_text_glyphs (cairo
, utf8
, utf8Count
, glyphs
, glyphCount
, clusters
, clusterCount
, (cairo_text_cluster_flags_t
)0);
1407 cairo_show_glyphs (cairo
, glyphs
, glyphCount
);
1409 cairo_show_glyphs (cairo_shape
, glyphs
, glyphCount
);
1413 if ((render
& 3) == 1 || (render
& 3) == 2) {
1414 LOG (printf ("stroke string\n"));
1415 cairo_set_source (cairo
, stroke_pattern
);
1416 cairo_glyph_path (cairo
, glyphs
, glyphCount
);
1417 cairo_stroke (cairo
);
1419 cairo_glyph_path (cairo_shape
, glyphs
, glyphCount
);
1420 cairo_stroke (cairo_shape
);
1426 LOG (printf ("clip string\n"));
1427 // append the glyph path to textClipPath.
1429 // set textClipPath as the currentPath
1431 cairo_append_path (cairo
, textClipPath
);
1433 cairo_append_path (cairo_shape
, textClipPath
);
1435 cairo_path_destroy (textClipPath
);
1438 // append the glyph path
1439 cairo_glyph_path (cairo
, glyphs
, glyphCount
);
1441 // move the path back into textClipPath
1442 // and clear the current path
1443 textClipPath
= cairo_copy_path (cairo
);
1444 cairo_new_path (cairo
);
1446 cairo_new_path (cairo_shape
);
1453 if (use_show_text_glyphs
) {
1462 GBool
CairoOutputDev::beginType3Char(GfxState
*state
, double x
, double y
,
1463 double dx
, double dy
,
1464 CharCode code
, Unicode
*u
, int uLen
) {
1468 cairo_matrix_t matrix
;
1470 ctm
= state
->getCTM();
1477 /* Restore the original matrix and then transform to matrix needed for the
1478 * type3 font. This is ugly but seems to work. Perhaps there is a better way to do it?*/
1479 cairo_set_matrix(cairo
, &orig_matrix
);
1480 cairo_transform(cairo
, &matrix
);
1482 cairo_save (cairo_shape
);
1483 cairo_set_matrix(cairo_shape
, &orig_matrix
);
1484 cairo_transform(cairo_shape
, &matrix
);
1486 cairo_pattern_destroy(stroke_pattern
);
1487 cairo_pattern_reference(fill_pattern
);
1488 stroke_pattern
= fill_pattern
;
1492 void CairoOutputDev::endType3Char(GfxState
*state
) {
1493 cairo_restore (cairo
);
1495 cairo_restore (cairo_shape
);
1499 void CairoOutputDev::type3D0(GfxState
*state
, double wx
, double wy
) {
1504 void CairoOutputDev::type3D1(GfxState
*state
, double wx
, double wy
,
1505 double llx
, double lly
, double urx
, double ury
) {
1508 t3_glyph_bbox
[0] = llx
;
1509 t3_glyph_bbox
[1] = lly
;
1510 t3_glyph_bbox
[2] = urx
;
1511 t3_glyph_bbox
[3] = ury
;
1512 t3_glyph_has_bbox
= gTrue
;
1515 void CairoOutputDev::beginTextObject(GfxState
*state
) {
1518 void CairoOutputDev::endTextObject(GfxState
*state
) {
1520 // clip the accumulated text path
1521 cairo_append_path (cairo
, textClipPath
);
1524 cairo_append_path (cairo_shape
, textClipPath
);
1525 cairo_clip (cairo_shape
);
1527 cairo_path_destroy (textClipPath
);
1528 textClipPath
= NULL
;
1532 void CairoOutputDev::beginActualText(GfxState
*state
, GooString
*text
)
1535 actualText
->begin(state
, text
);
1538 void CairoOutputDev::endActualText(GfxState
*state
)
1541 actualText
->end(state
);
1544 static inline int splashRound(SplashCoord x
) {
1545 return (int)floor(x
+ 0.5);
1548 static inline int splashCeil(SplashCoord x
) {
1549 return (int)ceil(x
);
1552 static inline int splashFloor(SplashCoord x
) {
1553 return (int)floor(x
);
1557 cairo_surface_t
*cairo_surface_create_similar_clip (cairo_t
*cairo
, cairo_content_t content
)
1559 cairo_pattern_t
*pattern
;
1560 cairo_surface_t
*surface
= NULL
;
1562 cairo_push_group_with_content (cairo
, content
);
1563 pattern
= cairo_pop_group (cairo
);
1564 cairo_pattern_get_surface (pattern
, &surface
);
1565 cairo_surface_reference (surface
);
1566 cairo_pattern_destroy (pattern
);
1572 void CairoOutputDev::beginTransparencyGroup(GfxState
* /*state*/, double * /*bbox*/,
1573 GfxColorSpace
* blendingColorSpace
,
1574 GBool
/*isolated*/, GBool knockout
,
1575 GBool forSoftMask
) {
1576 /* push color space */
1577 ColorSpaceStack
* css
= new ColorSpaceStack
;
1578 css
->cs
= blendingColorSpace
;
1579 css
->knockout
= knockout
;
1580 cairo_get_matrix(cairo
, &css
->group_matrix
);
1581 css
->next
= groupColorSpaceStack
;
1582 groupColorSpaceStack
= css
;
1584 LOG(printf ("begin transparency group. knockout: %s\n", knockout
? "yes":"no"));
1589 /* create a surface for tracking the shape */
1590 cairo_surface_t
*cairo_shape_surface
= cairo_surface_create_similar_clip (cairo
, CAIRO_CONTENT_ALPHA
);
1591 cairo_shape
= cairo_create (cairo_shape_surface
);
1592 cairo_surface_destroy (cairo_shape_surface
);
1594 /* the color doesn't matter as long as it is opaque */
1595 cairo_set_source_rgb (cairo_shape
, 0, 0, 0);
1596 cairo_matrix_t matrix
;
1597 cairo_get_matrix (cairo
, &matrix
);
1598 //printMatrix(&matrix);
1599 cairo_set_matrix (cairo_shape
, &matrix
);
1602 if (groupColorSpaceStack
->next
&& groupColorSpaceStack
->next
->knockout
) {
1603 /* we need to track the shape */
1604 cairo_push_group (cairo_shape
);
1606 if (0 && forSoftMask
)
1607 cairo_push_group_with_content (cairo
, CAIRO_CONTENT_ALPHA
);
1609 cairo_push_group (cairo
);
1611 /* push_group has an implicit cairo_save() */
1613 /*XXX: let's hope this matches the semantics needed */
1614 cairo_set_operator(cairo
, CAIRO_OPERATOR_SOURCE
);
1616 cairo_set_operator(cairo
, CAIRO_OPERATOR_OVER
);
1620 void CairoOutputDev::endTransparencyGroup(GfxState
* /*state*/) {
1622 cairo_pattern_destroy(group
);
1623 group
= cairo_pop_group (cairo
);
1625 LOG(printf ("end transparency group\n"));
1627 if (groupColorSpaceStack
->next
&& groupColorSpaceStack
->next
->knockout
) {
1629 cairo_pattern_destroy(shape
);
1630 shape
= cairo_pop_group (cairo_shape
);
1634 void CairoOutputDev::paintTransparencyGroup(GfxState
* /*state*/, double * /*bbox*/) {
1635 LOG(printf ("paint transparency group\n"));
1638 cairo_set_matrix (cairo
, &groupColorSpaceStack
->group_matrix
);
1641 /* OPERATOR_SOURCE w/ a mask is defined as (src IN mask) ADD (dest OUT mask)
1642 * however our source has already been clipped to mask so we only need to
1645 /* clear the shape mask */
1646 cairo_set_source (cairo
, shape
);
1647 cairo_set_operator (cairo
, CAIRO_OPERATOR_DEST_OUT
);
1648 cairo_paint (cairo
);
1649 cairo_set_operator (cairo
, CAIRO_OPERATOR_ADD
);
1651 cairo_set_source (cairo
, group
);
1654 cairo_paint_with_alpha (cairo
, fill_opacity
);
1655 cairo_status_t status
= cairo_status(cairo
);
1657 printf("BAD status: %s\n", cairo_status_to_string(status
));
1659 if (fill_opacity
< 1.0) {
1660 cairo_push_group(cairo
);
1663 cairo_set_matrix(cairo
, &mask_matrix
);
1664 cairo_mask(cairo
, mask
);
1665 cairo_restore(cairo
);
1666 if (fill_opacity
< 1.0) {
1667 cairo_pop_group_to_source(cairo
);
1668 cairo_paint_with_alpha (cairo
, fill_opacity
);
1670 cairo_pattern_destroy(mask
);
1676 cairo_set_source (cairo_shape
, shape
);
1677 cairo_paint (cairo_shape
);
1678 cairo_set_source_rgb (cairo_shape
, 0, 0, 0);
1680 cairo_pattern_destroy (shape
);
1684 popTransparencyGroup();
1685 cairo_restore(cairo
);
1688 static int luminocity(uint32_t x
)
1690 int r
= (x
>> 16) & 0xff;
1691 int g
= (x
>> 8) & 0xff;
1692 int b
= (x
>> 0) & 0xff;
1693 // an arbitrary integer approximation of .3*r + .59*g + .11*b
1694 int y
= (r
*19661+g
*38666+b
*7209 + 32829)>>16;
1699 /* XXX: do we need to deal with shape here? */
1700 void CairoOutputDev::setSoftMask(GfxState
* state
, double * bbox
, GBool alpha
,
1701 Function
* transferFunc
, GfxColor
* backdropColor
) {
1702 cairo_pattern_destroy(mask
);
1704 LOG(printf ("set softMask\n"));
1706 if (!alpha
|| transferFunc
) {
1707 /* We need to mask according to the luminocity of the group.
1708 * So we paint the group to an image surface convert it to a luminocity map
1709 * and then use that as the mask. */
1711 /* Get clip extents in device space */
1712 double x1
, y1
, x2
, y2
, x_min
, y_min
, x_max
, y_max
;
1713 cairo_clip_extents(cairo
, &x1
, &y1
, &x2
, &y2
);
1714 cairo_user_to_device(cairo
, &x1
, &y1
);
1715 cairo_user_to_device(cairo
, &x2
, &y2
);
1716 x_min
= MIN(x1
, x2
);
1717 y_min
= MIN(y1
, y2
);
1718 x_max
= MAX(x1
, x2
);
1719 y_max
= MAX(y1
, y2
);
1720 cairo_clip_extents(cairo
, &x1
, &y1
, &x2
, &y2
);
1721 cairo_user_to_device(cairo
, &x1
, &y2
);
1722 cairo_user_to_device(cairo
, &x2
, &y1
);
1723 x_min
= MIN(x_min
,MIN(x1
, x2
));
1724 y_min
= MIN(y_min
,MIN(y1
, y2
));
1725 x_max
= MAX(x_max
,MAX(x1
, x2
));
1726 y_max
= MAX(y_max
,MAX(y1
, y2
));
1728 int width
= (int)(ceil(x_max
) - floor(x_min
));
1729 int height
= (int)(ceil(y_max
) - floor(y_min
));
1731 /* Get group device offset */
1732 double x_offset
, y_offset
;
1733 if (cairo_get_group_target(cairo
) == cairo_get_target(cairo
)) {
1734 cairo_surface_get_device_offset(cairo_get_group_target(cairo
), &x_offset
, &y_offset
);
1736 cairo_surface_t
*pats
;
1737 cairo_pattern_get_surface(group
, &pats
);
1738 cairo_surface_get_device_offset(pats
, &x_offset
, &y_offset
);
1741 /* Adjust extents by group offset */
1745 cairo_surface_t
*source
= cairo_image_surface_create(CAIRO_FORMAT_ARGB32
, width
, height
);
1746 cairo_t
*maskCtx
= cairo_create(source
);
1748 //XXX: hopefully this uses the correct color space */
1749 if (!alpha
&& groupColorSpaceStack
->cs
) {
1750 GfxRGB backdropColorRGB
;
1751 groupColorSpaceStack
->cs
->getRGB(backdropColor
, &backdropColorRGB
);
1752 /* paint the backdrop */
1753 cairo_set_source_rgb(maskCtx
,
1754 colToDbl(backdropColorRGB
.r
),
1755 colToDbl(backdropColorRGB
.g
),
1756 colToDbl(backdropColorRGB
.b
));
1758 cairo_paint(maskCtx
);
1760 /* Copy source ctm to mask ctm and translate origin so that the
1761 * mask appears it the same location on the source surface. */
1762 cairo_matrix_t mat
, tmat
;
1763 cairo_matrix_init_translate(&tmat
, -x_min
, -y_min
);
1764 cairo_get_matrix(cairo
, &mat
);
1765 cairo_matrix_multiply(&mat
, &mat
, &tmat
);
1766 cairo_set_matrix(maskCtx
, &mat
);
1768 /* make the device offset of the new mask match that of the group */
1769 cairo_surface_set_device_offset(source
, x_offset
, y_offset
);
1771 /* paint the group */
1772 cairo_set_source(maskCtx
, group
);
1773 cairo_paint(maskCtx
);
1775 /* XXX status = cairo_status(maskCtx); */
1776 cairo_destroy(maskCtx
);
1778 /* convert to a luminocity map */
1779 uint32_t *source_data
= (uint32_t*)cairo_image_surface_get_data(source
);
1780 /* get stride in units of 32 bits */
1781 int stride
= cairo_image_surface_get_stride(source
)/4;
1782 for (int y
=0; y
<height
; y
++) {
1783 for (int x
=0; x
<width
; x
++) {
1784 int lum
= alpha
? fill_opacity
: luminocity(source_data
[y
*stride
+ x
]);
1786 double lum_in
, lum_out
;
1788 transferFunc
->transform(&lum_in
, &lum_out
);
1789 lum
= (int)(lum_out
* 255.0 + 0.5);
1791 source_data
[y
*stride
+ x
] = lum
<< 24;
1794 cairo_surface_mark_dirty (source
);
1796 /* setup the new mask pattern */
1797 mask
= cairo_pattern_create_for_surface(source
);
1798 cairo_get_matrix(cairo
, &mask_matrix
);
1800 if (cairo_get_group_target(cairo
) == cairo_get_target(cairo
)) {
1801 cairo_pattern_set_matrix(mask
, &mat
);
1803 cairo_matrix_t patMatrix
;
1804 cairo_pattern_get_matrix(group
, &patMatrix
);
1805 /* Apply x_min, y_min offset to it appears in the same location as source. */
1806 cairo_matrix_multiply(&patMatrix
, &patMatrix
, &tmat
);
1807 cairo_pattern_set_matrix(mask
, &patMatrix
);
1810 cairo_surface_destroy(source
);
1812 mask
= cairo_pattern_reference(group
);
1813 cairo_get_matrix(cairo
, &mask_matrix
);
1816 popTransparencyGroup();
1819 void CairoOutputDev::popTransparencyGroup() {
1820 /* pop color space */
1821 ColorSpaceStack
*css
= groupColorSpaceStack
;
1822 if (css
->knockout
) {
1824 if (!knockoutCount
) {
1825 /* we don't need to track the shape anymore because
1826 * we are not above any knockout groups */
1827 cairo_destroy(cairo_shape
);
1831 groupColorSpaceStack
= css
->next
;
1836 void CairoOutputDev::clearSoftMask(GfxState
* /*state*/) {
1838 cairo_pattern_destroy(mask
);
1842 /* Taken from cairo/doc/tutorial/src/singular.c */
1844 get_singular_values (const cairo_matrix_t
*matrix
,
1848 double xx
= matrix
->xx
, xy
= matrix
->xy
;
1849 double yx
= matrix
->yx
, yy
= matrix
->yy
;
1851 double a
= xx
*xx
+yx
*yx
;
1852 double b
= xy
*xy
+yy
*yy
;
1853 double k
= xx
*xy
+yx
*yy
;
1855 double f
= (a
+b
) * .5;
1856 double g
= (a
-b
) * .5;
1857 double delta
= sqrt (g
*g
+ k
*k
);
1860 *major
= sqrt (f
+ delta
);
1862 *minor
= sqrt (f
- delta
);
1865 void CairoOutputDev::getScaledSize(const cairo_matrix_t
*matrix
,
1873 if (orig_width
> orig_height
)
1874 get_singular_values (matrix
, &xScale
, &yScale
);
1876 get_singular_values (matrix
, &yScale
, &xScale
);
1878 int tx
, tx2
, ty
, ty2
; /* the integer co-oridinates of the resulting image */
1880 tx
= splashRound(matrix
->x0
- 0.01);
1881 tx2
= splashRound(matrix
->x0
+ xScale
+ 0.01) - 1;
1883 tx
= splashRound(matrix
->x0
+ 0.01) - 1;
1884 tx2
= splashRound(matrix
->x0
+ xScale
- 0.01);
1886 *scaledWidth
= abs(tx2
- tx
) + 1;
1887 //scaledWidth = splashRound(fabs(xScale));
1888 if (*scaledWidth
== 0) {
1889 // technically, this should draw nothing, but it generally seems
1890 // better to draw a one-pixel-wide stripe rather than throwing it
1895 ty
= splashFloor(matrix
->y0
+ 0.01);
1896 ty2
= splashCeil(matrix
->y0
+ yScale
- 0.01);
1898 ty
= splashCeil(matrix
->y0
- 0.01);
1899 ty2
= splashFloor(matrix
->y0
+ yScale
+ 0.01);
1901 *scaledHeight
= abs(ty2
- ty
);
1902 if (*scaledHeight
== 0) {
1908 CairoOutputDev::getFilterForSurface(cairo_surface_t
*image
,
1912 return CAIRO_FILTER_BILINEAR
;
1914 int orig_width
= cairo_image_surface_get_width (image
);
1915 int orig_height
= cairo_image_surface_get_height (image
);
1916 if (orig_width
== 0 || orig_height
== 0)
1917 return CAIRO_FILTER_NEAREST
;
1919 /* When printing, don't change the interpolation. */
1921 return CAIRO_FILTER_NEAREST
;
1923 cairo_matrix_t matrix
;
1924 cairo_get_matrix(cairo
, &matrix
);
1925 int scaled_width
, scaled_height
;
1926 getScaledSize (&matrix
, orig_width
, orig_height
, &scaled_width
, &scaled_height
);
1928 /* When scale factor is >= 400% we don't interpolate. See bugs #25268, #9860 */
1929 if (scaled_width
/ orig_width
>= 4 || scaled_height
/ orig_height
>= 4)
1930 return CAIRO_FILTER_NEAREST
;
1932 return CAIRO_FILTER_BILINEAR
;
1935 void CairoOutputDev::drawImageMask(GfxState
*state
, Object
*ref
, Stream
*str
,
1936 int width
, int height
, GBool invert
,
1937 GBool interpolate
, GBool inlineImg
) {
1939 /* FIXME: Doesn't the image mask support any colorspace? */
1940 cairo_set_source (cairo
, fill_pattern
);
1942 /* work around a cairo bug when scaling 1x1 surfaces */
1943 if (width
== 1 && height
== 1) {
1944 ImageStream
*imgStr
;
1948 imgStr
= new ImageStream(str
, width
, 1, 1);
1950 imgStr
->getPixel(&pix
);
1954 invert_bit
= invert
? 1 : 0;
1955 if (pix
^ invert_bit
)
1959 cairo_rectangle (cairo
, 0., 0., width
, height
);
1961 cairo_restore (cairo
);
1963 cairo_save (cairo_shape
);
1964 cairo_rectangle (cairo_shape
, 0., 0., width
, height
);
1965 cairo_fill (cairo_shape
);
1966 cairo_restore (cairo_shape
);
1971 /* shape is 1.0 for painted areas, 0.0 for unpainted ones */
1973 cairo_matrix_t matrix
;
1974 cairo_get_matrix (cairo
, &matrix
);
1975 //XXX: it is possible that we should only do sub pixel positioning if
1976 // we are rendering fonts */
1977 if (!printing
&& prescaleImages
1979 && matrix
.xy
== 0 && matrix
.yx
== 0
1980 /* axes not flipped / not 180 deg rotated */
1981 && matrix
.xx
> 0 && (upsideDown() ? -1 : 1) * matrix
.yy
> 0) {
1982 drawImageMaskPrescaled(state
, ref
, str
, width
, height
, invert
, interpolate
, inlineImg
);
1984 drawImageMaskRegular(state
, ref
, str
, width
, height
, invert
, interpolate
, inlineImg
);
1989 void CairoOutputDev::setSoftMaskFromImageMask(GfxState
*state
, Object
*ref
, Stream
*str
,
1990 int width
, int height
, GBool invert
,
1991 GBool inlineImg
, double *baseMatrix
) {
1993 /* FIXME: Doesn't the image mask support any colorspace? */
1994 cairo_set_source (cairo
, fill_pattern
);
1996 /* work around a cairo bug when scaling 1x1 surfaces */
1997 if (width
== 1 && height
== 1) {
1998 ImageStream
*imgStr
;
2002 imgStr
= new ImageStream(str
, width
, 1, 1);
2004 imgStr
->getPixel(&pix
);
2008 invert_bit
= invert
? 1 : 0;
2009 if (!(pix
^ invert_bit
)) {
2011 cairo_rectangle (cairo
, 0., 0., width
, height
);
2013 cairo_restore (cairo
);
2015 cairo_save (cairo_shape
);
2016 cairo_rectangle (cairo_shape
, 0., 0., width
, height
);
2017 cairo_fill (cairo_shape
);
2018 cairo_restore (cairo_shape
);
2022 cairo_push_group_with_content (cairo
, CAIRO_CONTENT_ALPHA
);
2024 /* shape is 1.0 for painted areas, 0.0 for unpainted ones */
2026 cairo_matrix_t matrix
;
2027 cairo_get_matrix (cairo
, &matrix
);
2028 //XXX: it is possible that we should only do sub pixel positioning if
2029 // we are rendering fonts */
2030 if (!printing
&& prescaleImages
&& matrix
.xy
== 0.0 && matrix
.yx
== 0.0) {
2031 drawImageMaskPrescaled(state
, ref
, str
, width
, height
, invert
, gFalse
, inlineImg
);
2033 drawImageMaskRegular(state
, ref
, str
, width
, height
, invert
, gFalse
, inlineImg
);
2036 if (state
->getFillColorSpace()->getMode() == csPattern
) {
2037 cairo_set_source_rgb (cairo
, 1, 1, 1);
2038 cairo_set_matrix (cairo
, &mask_matrix
);
2039 cairo_mask (cairo
, mask
);
2043 cairo_pattern_destroy (mask
);
2044 mask
= cairo_pop_group (cairo
);
2048 double bbox
[4] = {0,0,1,1}; // dummy
2049 beginTransparencyGroup(state
, bbox
, state
->getFillColorSpace(),
2050 gTrue
, gFalse
, gFalse
);
2053 void CairoOutputDev::unsetSoftMaskFromImageMask(GfxState
*state
, double *baseMatrix
) {
2054 double bbox
[4] = {0,0,1,1}; // dummy
2056 endTransparencyGroup(state
);
2057 restoreState(state
);
2058 paintTransparencyGroup(state
, bbox
);
2059 clearSoftMask(state
);
2062 void CairoOutputDev::drawImageMaskRegular(GfxState
*state
, Object
*ref
, Stream
*str
,
2063 int width
, int height
, GBool invert
,
2064 GBool interpolate
, GBool inlineImg
) {
2065 unsigned char *buffer
;
2066 unsigned char *dest
;
2067 cairo_surface_t
*image
;
2068 cairo_pattern_t
*pattern
;
2070 ImageStream
*imgStr
;
2072 cairo_matrix_t matrix
;
2075 cairo_filter_t filter
;
2077 /* TODO: Do we want to cache these? */
2078 imgStr
= new ImageStream(str
, width
, 1, 1);
2081 image
= cairo_image_surface_create (CAIRO_FORMAT_A1
, width
, height
);
2082 if (cairo_surface_status (image
))
2085 buffer
= cairo_image_surface_get_data (image
);
2086 row_stride
= cairo_image_surface_get_stride (image
);
2088 invert_bit
= invert
? 1 : 0;
2090 for (y
= 0; y
< height
; y
++) {
2091 pix
= imgStr
->getLine();
2092 dest
= buffer
+ y
* row_stride
;
2095 for (x
= 0; x
< width
; x
++) {
2098 if (!(pix
[x
] ^ invert_bit
)) {
2099 #ifdef WORDS_BIGENDIAN
2100 dest
[i
] |= (1 << (7 - bit
));
2102 dest
[i
] |= (1 << bit
);
2113 filter
= getFilterForSurface (image
, interpolate
);
2115 cairo_surface_mark_dirty (image
);
2116 pattern
= cairo_pattern_create_for_surface (image
);
2117 cairo_surface_destroy (image
);
2118 if (cairo_pattern_status (pattern
))
2121 LOG (printf ("drawImageMask %dx%d\n", width
, height
));
2123 cairo_pattern_set_filter (pattern
, filter
);
2126 cairo_pattern_set_extend (pattern
, CAIRO_EXTEND_PAD
);
2128 cairo_matrix_init_translate (&matrix
, 0, height
);
2129 cairo_matrix_scale (&matrix
, width
, -height
);
2130 cairo_pattern_set_matrix (pattern
, &matrix
);
2131 if (cairo_pattern_status (pattern
)) {
2132 cairo_pattern_destroy (pattern
);
2136 if (state
->getFillColorSpace()->getMode() == csPattern
) {
2137 mask
= cairo_pattern_reference (pattern
);
2138 cairo_get_matrix (cairo
, &mask_matrix
);
2139 } else if (!printing
) {
2141 cairo_rectangle (cairo
, 0., 0., 1., 1.);
2143 cairo_mask (cairo
, pattern
);
2144 cairo_restore (cairo
);
2146 cairo_mask (cairo
, pattern
);
2150 cairo_save (cairo_shape
);
2151 cairo_set_source (cairo_shape
, pattern
);
2153 cairo_rectangle (cairo_shape
, 0., 0., 1., 1.);
2154 cairo_fill (cairo_shape
);
2156 cairo_mask (cairo_shape
, pattern
);
2158 cairo_restore (cairo_shape
);
2161 cairo_pattern_destroy (pattern
);
2169 void CairoOutputDev::drawImageMaskPrescaled(GfxState
*state
, Object
*ref
, Stream
*str
,
2170 int width
, int height
, GBool invert
,
2171 GBool interpolate
, GBool inlineImg
) {
2172 unsigned char *buffer
;
2173 cairo_surface_t
*image
;
2174 cairo_pattern_t
*pattern
;
2175 ImageStream
*imgStr
;
2177 cairo_matrix_t matrix
;
2181 /* cairo does a very poor job of scaling down images so we scale them ourselves */
2183 LOG (printf ("drawImageMaskPrescaled %dx%d\n", width
, height
));
2185 /* this scaling code is adopted from the splash image scaling code */
2186 cairo_get_matrix(cairo
, &matrix
);
2188 printf("[%f %f], [%f %f], %f %f\n", matrix
.xx
, matrix
.xy
, matrix
.yx
, matrix
.yy
, matrix
.x0
, matrix
.y0
);
2190 /* this whole computation should be factored out */
2191 double xScale
= matrix
.xx
;
2192 double yScale
= matrix
.yy
;
2193 int tx
, tx2
, ty
, ty2
; /* the integer co-oridinates of the resulting image */
2197 tx
= splashRound(matrix
.x0
- 0.01);
2198 tx2
= splashRound(matrix
.x0
+ xScale
+ 0.01) - 1;
2200 tx
= splashRound(matrix
.x0
+ 0.01) - 1;
2201 tx2
= splashRound(matrix
.x0
+ xScale
- 0.01);
2203 scaledWidth
= abs(tx2
- tx
) + 1;
2204 //scaledWidth = splashRound(fabs(xScale));
2205 if (scaledWidth
== 0) {
2206 // technically, this should draw nothing, but it generally seems
2207 // better to draw a one-pixel-wide stripe rather than throwing it
2212 ty
= splashFloor(matrix
.y0
+ 0.01);
2213 ty2
= splashCeil(matrix
.y0
+ yScale
- 0.01);
2215 ty
= splashCeil(matrix
.y0
- 0.01);
2216 ty2
= splashFloor(matrix
.y0
+ yScale
+ 0.01);
2218 scaledHeight
= abs(ty2
- ty
);
2219 if (scaledHeight
== 0) {
2223 printf("xscale: %g, yscale: %g\n", xScale
, yScale
);
2224 printf("width: %d, height: %d\n", width
, height
);
2225 printf("scaledWidth: %d, scaledHeight: %d\n", scaledWidth
, scaledHeight
);
2228 /* compute the required padding */
2229 /* Padding is used to preserve the aspect ratio.
2230 We compute total_pad to make (height+total_pad)/scaledHeight as close to height/yScale as possible */
2233 int total_pad
= splashRound(height
*(scaledHeight
/fabs(yScale
)) - height
);
2235 /* compute the two pieces of padding */
2236 if (total_pad
> 0) {
2237 //XXX: i'm not positive fabs() is correct
2238 float tail_error
= fabs(matrix
.y0
- ty
);
2239 float head_error
= fabs(ty2
- (matrix
.y0
+ yScale
));
2240 float tail_fraction
= tail_error
/(tail_error
+ head_error
);
2241 tail_pad
= splashRound(total_pad
*tail_fraction
);
2242 head_pad
= total_pad
- tail_pad
;
2247 int origHeight
= height
;
2251 printf("head_pad: %d tail_pad: %d\n", head_pad
, tail_pad
);
2252 printf("origHeight: %d height: %d\n", origHeight
, height
);
2253 printf("ty: %d, ty2: %d\n", ty
, ty2
);
2256 /* TODO: Do we want to cache these? */
2257 imgStr
= new ImageStream(str
, width
, 1, 1);
2260 invert_bit
= invert
? 1 : 0;
2262 image
= cairo_image_surface_create (CAIRO_FORMAT_A8
, scaledWidth
, scaledHeight
);
2263 if (cairo_surface_status (image
)) {
2269 buffer
= cairo_image_surface_get_data (image
);
2270 row_stride
= cairo_image_surface_get_stride (image
);
2272 int yp
= height
/ scaledHeight
;
2273 int yq
= height
% scaledHeight
;
2274 int xp
= width
/ scaledWidth
;
2275 int xq
= width
% scaledWidth
;
2277 int origHeight_c
= origHeight
;
2278 /* use MIN() because yp might be > origHeight because of padding */
2279 unsigned char *pixBuf
= (unsigned char *)malloc(MIN(yp
+1, origHeight
)*width
);
2282 for (int y
= 0; y
< scaledHeight
; y
++) {
2283 // y scale Bresenham
2287 if (yt
>= scaledHeight
) {
2292 // read row (s) from image ignoring the padding as appropriate
2294 int n
= (yp
> 0) ? yStep
: lastYStep
;
2297 unsigned char *p
= pixBuf
;
2298 int head_pad_count
= head_pad
;
2299 int origHeight_count
= origHeight
;
2300 int tail_pad_count
= tail_pad
;
2301 for (int i
=0; i
<n
; i
++) {
2303 if (head_pad_count
) {
2305 } else if (origHeight_count
) {
2306 pix
= imgStr
->getLine();
2307 for (int j
=0; j
<width
; j
++) {
2308 if (pix
[j
] ^ invert_bit
)
2315 } else if (tail_pad_count
) {
2318 printf("%d %d\n", n
, total
);
2319 assert(0 && "over run\n");
2331 int n
= yStep
> 0 ? yStep
: 1;
2334 /* compute the size of padding and pixels that will be used for this row */
2335 int head_pad_size
= MIN(n
, head_pad
);
2337 head_pad
-= MIN(head_pad_size
, yStep
);
2339 int pix_size
= MIN(n
, origHeight
);
2341 origHeight
-= MIN(pix_size
, yStep
);
2343 int tail_pad_size
= MIN(n
, tail_pad
);
2345 tail_pad
-= MIN(tail_pad_size
, yStep
);
2347 printf("n = %d (%d %d %d)\n", n
, head_pad_size
, pix_size
, tail_pad_size
);
2351 for (int x
= 0; x
< scaledWidth
; ++x
) {
2354 if (xt
>= scaledWidth
) {
2358 int m
= xStep
> 0 ? xStep
: 1;
2360 /* could m * head_pad_size * tail_pad_size overflow? */
2362 pixAcc0
+= m
* head_pad_size
* tail_pad_size
* 255;
2364 pixAcc0
+= m
* head_pad_size
* tail_pad_size
* 0;
2366 /* Accumulate all of the source pixels for the destination pixel */
2367 for (int i
= 0; i
< pix_size
; ++i
) {
2368 for (int j
= 0; j
< m
; ++j
) {
2369 if (xSrc
+ i
*width
+ j
> MIN(yp
+ 1, origHeight_c
)*width
) {
2370 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
);
2371 printf("%d %d %d\n", head_pad_size
, pix_size
, tail_pad_size
);
2372 assert(0 && "bad access\n");
2374 pixAcc0
+= pixBuf
[xSrc
+ i
*width
+ j
];
2377 buffer
[y
* row_stride
+ x
] = splashFloor(pixAcc0
/ (origN
*m
));
2385 cairo_surface_mark_dirty (image
);
2386 pattern
= cairo_pattern_create_for_surface (image
);
2387 cairo_surface_destroy (image
);
2388 if (cairo_pattern_status (pattern
)) {
2394 /* we should actually be using CAIRO_FILTER_NEAREST here. However,
2395 * cairo doesn't yet do minifaction filtering causing scaled down
2396 * images with CAIRO_FILTER_NEAREST to look really bad */
2397 cairo_pattern_set_filter (pattern
,
2398 interpolate
? CAIRO_FILTER_BEST
: CAIRO_FILTER_FAST
);
2399 cairo_pattern_set_extend (pattern
, CAIRO_EXTEND_PAD
);
2401 if (state
->getFillColorSpace()->getMode() == csPattern
) {
2402 cairo_matrix_init_translate (&matrix
, 0, scaledHeight
);
2403 cairo_matrix_scale (&matrix
, scaledWidth
, -scaledHeight
);
2404 cairo_pattern_set_matrix (pattern
, &matrix
);
2405 if (cairo_pattern_status (pattern
)) {
2406 cairo_pattern_destroy (pattern
);
2412 mask
= cairo_pattern_reference (pattern
);
2413 cairo_get_matrix (cairo
, &mask_matrix
);
2417 /* modify our current transformation so that the prescaled image
2418 * goes where it is supposed to */
2419 cairo_get_matrix(cairo
, &matrix
);
2420 cairo_scale(cairo
, 1.0/matrix
.xx
, 1.0/matrix
.yy
);
2421 // get integer co-ords
2422 cairo_translate (cairo
, tx
- matrix
.x0
, ty2
- matrix
.y0
);
2424 cairo_scale(cairo
, 1, -1);
2426 cairo_rectangle (cairo
, 0., 0., scaledWidth
, scaledHeight
);
2428 cairo_mask (cairo
, pattern
);
2430 //cairo_get_matrix(cairo, &matrix);
2431 //printf("mask at: [%f %f], [%f %f], %f %f\n\n", matrix.xx, matrix.xy, matrix.yx, matrix.yy, matrix.x0, matrix.y0);
2432 cairo_restore(cairo
);
2436 cairo_save (cairo_shape
);
2438 /* modify our current transformation so that the prescaled image
2439 * goes where it is supposed to */
2440 cairo_get_matrix(cairo_shape
, &matrix
);
2441 cairo_scale(cairo_shape
, 1.0/matrix
.xx
, 1.0/matrix
.yy
);
2442 // get integer co-ords
2443 cairo_translate (cairo_shape
, tx
- matrix
.x0
, ty2
- matrix
.y0
);
2445 cairo_scale(cairo_shape
, 1, -1);
2447 cairo_rectangle (cairo_shape
, 0., 0., scaledWidth
, scaledHeight
);
2448 cairo_fill (cairo_shape
);
2450 cairo_restore(cairo_shape
);
2453 cairo_pattern_destroy (pattern
);
2459 void CairoOutputDev::drawMaskedImage(GfxState
*state
, Object
*ref
,
2460 Stream
*str
, int width
, int height
,
2461 GfxImageColorMap
*colorMap
,
2463 Stream
*maskStr
, int maskWidth
,
2464 int maskHeight
, GBool maskInvert
,
2465 GBool maskInterpolate
)
2467 ImageStream
*maskImgStr
, *imgStr
;
2469 unsigned char *maskBuffer
, *buffer
;
2470 unsigned char *maskDest
;
2472 cairo_surface_t
*maskImage
, *image
;
2473 cairo_pattern_t
*maskPattern
, *pattern
;
2474 cairo_matrix_t matrix
;
2475 cairo_matrix_t maskMatrix
;
2479 cairo_filter_t filter
;
2480 cairo_filter_t maskFilter
;
2482 maskImgStr
= new ImageStream(maskStr
, maskWidth
, 1, 1);
2483 maskImgStr
->reset();
2485 maskImage
= cairo_image_surface_create (CAIRO_FORMAT_A8
, maskWidth
, maskHeight
);
2486 if (cairo_surface_status (maskImage
)) {
2487 maskImgStr
->close();
2492 maskBuffer
= cairo_image_surface_get_data (maskImage
);
2493 row_stride
= cairo_image_surface_get_stride (maskImage
);
2495 invert_bit
= maskInvert
? 1 : 0;
2497 for (y
= 0; y
< maskHeight
; y
++) {
2498 pix
= maskImgStr
->getLine();
2499 maskDest
= maskBuffer
+ y
* row_stride
;
2500 for (x
= 0; x
< maskWidth
; x
++) {
2501 if (pix
[x
] ^ invert_bit
)
2508 maskImgStr
->close();
2511 maskFilter
= getFilterForSurface (maskImage
, maskInterpolate
);
2513 cairo_surface_mark_dirty (maskImage
);
2514 maskPattern
= cairo_pattern_create_for_surface (maskImage
);
2515 cairo_surface_destroy (maskImage
);
2516 if (cairo_pattern_status (maskPattern
))
2520 /* ICCBased color space doesn't do any color correction
2521 * so check its underlying color space as well */
2522 int is_identity_transform
;
2523 is_identity_transform
= colorMap
->getColorSpace()->getMode() == csDeviceRGB
||
2524 (colorMap
->getColorSpace()->getMode() == csICCBased
&&
2525 ((GfxICCBasedColorSpace
*)colorMap
->getColorSpace())->getAlt()->getMode() == csDeviceRGB
);
2528 /* TODO: Do we want to cache these? */
2529 imgStr
= new ImageStream(str
, width
,
2530 colorMap
->getNumPixelComps(),
2531 colorMap
->getBits());
2534 image
= cairo_image_surface_create (CAIRO_FORMAT_RGB24
, width
, height
);
2535 if (cairo_surface_status (image
))
2538 buffer
= cairo_image_surface_get_data (image
);
2539 row_stride
= cairo_image_surface_get_stride (image
);
2540 for (y
= 0; y
< height
; y
++) {
2541 dest
= (unsigned int *) (buffer
+ y
* row_stride
);
2542 pix
= imgStr
->getLine();
2543 colorMap
->getRGBLine (pix
, dest
, width
);
2546 filter
= getFilterForSurface (image
, interpolate
);
2548 cairo_surface_mark_dirty (image
);
2549 pattern
= cairo_pattern_create_for_surface (image
);
2550 cairo_surface_destroy (image
);
2551 if (cairo_pattern_status (pattern
))
2554 LOG (printf ("drawMaskedImage %dx%d\n", width
, height
));
2556 cairo_pattern_set_filter (pattern
, filter
);
2557 cairo_pattern_set_filter (maskPattern
, maskFilter
);
2560 cairo_pattern_set_extend (pattern
, CAIRO_EXTEND_PAD
);
2561 cairo_pattern_set_extend (maskPattern
, CAIRO_EXTEND_PAD
);
2564 cairo_matrix_init_translate (&matrix
, 0, height
);
2565 cairo_matrix_scale (&matrix
, width
, -height
);
2566 cairo_pattern_set_matrix (pattern
, &matrix
);
2567 if (cairo_pattern_status (pattern
)) {
2568 cairo_pattern_destroy (pattern
);
2569 cairo_pattern_destroy (maskPattern
);
2573 cairo_matrix_init_translate (&maskMatrix
, 0, maskHeight
);
2574 cairo_matrix_scale (&maskMatrix
, maskWidth
, -maskHeight
);
2575 cairo_pattern_set_matrix (maskPattern
, &maskMatrix
);
2576 if (cairo_pattern_status (maskPattern
)) {
2577 cairo_pattern_destroy (maskPattern
);
2578 cairo_pattern_destroy (pattern
);
2584 cairo_set_source (cairo
, pattern
);
2585 cairo_rectangle (cairo
, 0., 0., 1., 1.);
2587 cairo_mask (cairo
, maskPattern
);
2588 cairo_restore (cairo
);
2590 cairo_set_source (cairo
, pattern
);
2591 cairo_mask (cairo
, maskPattern
);
2595 cairo_save (cairo_shape
);
2596 cairo_set_source (cairo_shape
, pattern
);
2598 cairo_rectangle (cairo_shape
, 0., 0., 1., 1.);
2599 cairo_fill (cairo_shape
);
2601 cairo_mask (cairo_shape
, pattern
);
2603 cairo_restore (cairo_shape
);
2606 cairo_pattern_destroy (maskPattern
);
2607 cairo_pattern_destroy (pattern
);
2615 //XXX: is this affect by AIS(alpha is shape)?
2616 void CairoOutputDev::drawSoftMaskedImage(GfxState
*state
, Object
*ref
, Stream
*str
,
2617 int width
, int height
,
2618 GfxImageColorMap
*colorMap
,
2621 int maskWidth
, int maskHeight
,
2622 GfxImageColorMap
*maskColorMap
,
2623 GBool maskInterpolate
)
2625 ImageStream
*maskImgStr
, *imgStr
;
2627 unsigned char *maskBuffer
, *buffer
;
2628 unsigned char *maskDest
;
2630 cairo_surface_t
*maskImage
, *image
;
2631 cairo_pattern_t
*maskPattern
, *pattern
;
2632 cairo_matrix_t maskMatrix
, matrix
;
2635 cairo_filter_t filter
;
2636 cairo_filter_t maskFilter
;
2638 maskImgStr
= new ImageStream(maskStr
, maskWidth
,
2639 maskColorMap
->getNumPixelComps(),
2640 maskColorMap
->getBits());
2641 maskImgStr
->reset();
2643 maskImage
= cairo_image_surface_create (CAIRO_FORMAT_A8
, maskWidth
, maskHeight
);
2644 if (cairo_surface_status (maskImage
)) {
2645 maskImgStr
->close();
2650 maskBuffer
= cairo_image_surface_get_data (maskImage
);
2651 row_stride
= cairo_image_surface_get_stride (maskImage
);
2652 for (y
= 0; y
< maskHeight
; y
++) {
2653 maskDest
= (unsigned char *) (maskBuffer
+ y
* row_stride
);
2654 pix
= maskImgStr
->getLine();
2655 maskColorMap
->getGrayLine (pix
, maskDest
, maskWidth
);
2658 maskImgStr
->close();
2661 maskFilter
= getFilterForSurface (maskImage
, maskInterpolate
);
2663 cairo_surface_mark_dirty (maskImage
);
2664 maskPattern
= cairo_pattern_create_for_surface (maskImage
);
2665 cairo_surface_destroy (maskImage
);
2666 if (cairo_pattern_status (maskPattern
))
2670 /* ICCBased color space doesn't do any color correction
2671 * so check its underlying color space as well */
2672 int is_identity_transform
;
2673 is_identity_transform
= colorMap
->getColorSpace()->getMode() == csDeviceRGB
||
2674 (colorMap
->getColorSpace()->getMode() == csICCBased
&&
2675 ((GfxICCBasedColorSpace
*)colorMap
->getColorSpace())->getAlt()->getMode() == csDeviceRGB
);
2678 /* TODO: Do we want to cache these? */
2679 imgStr
= new ImageStream(str
, width
,
2680 colorMap
->getNumPixelComps(),
2681 colorMap
->getBits());
2684 image
= cairo_image_surface_create (CAIRO_FORMAT_RGB24
, width
, height
);
2685 if (cairo_surface_status (image
))
2688 buffer
= cairo_image_surface_get_data (image
);
2689 row_stride
= cairo_image_surface_get_stride (image
);
2690 for (y
= 0; y
< height
; y
++) {
2691 dest
= (unsigned int *) (buffer
+ y
* row_stride
);
2692 pix
= imgStr
->getLine();
2693 colorMap
->getRGBLine (pix
, dest
, width
);
2696 filter
= getFilterForSurface (image
, interpolate
);
2698 cairo_surface_mark_dirty (image
);
2700 setMimeData(state
, str
, ref
, colorMap
, image
);
2702 pattern
= cairo_pattern_create_for_surface (image
);
2703 cairo_surface_destroy (image
);
2704 if (cairo_pattern_status (pattern
))
2707 LOG (printf ("drawSoftMaskedImage %dx%d\n", width
, height
));
2709 cairo_pattern_set_filter (pattern
, filter
);
2710 cairo_pattern_set_filter (maskPattern
, maskFilter
);
2713 cairo_pattern_set_extend (pattern
, CAIRO_EXTEND_PAD
);
2714 cairo_pattern_set_extend (maskPattern
, CAIRO_EXTEND_PAD
);
2717 cairo_matrix_init_translate (&matrix
, 0, height
);
2718 cairo_matrix_scale (&matrix
, width
, -height
);
2719 cairo_pattern_set_matrix (pattern
, &matrix
);
2720 if (cairo_pattern_status (pattern
)) {
2721 cairo_pattern_destroy (pattern
);
2722 cairo_pattern_destroy (maskPattern
);
2726 cairo_matrix_init_translate (&maskMatrix
, 0, maskHeight
);
2727 cairo_matrix_scale (&maskMatrix
, maskWidth
, -maskHeight
);
2728 cairo_pattern_set_matrix (maskPattern
, &maskMatrix
);
2729 if (cairo_pattern_status (maskPattern
)) {
2730 cairo_pattern_destroy (maskPattern
);
2731 cairo_pattern_destroy (pattern
);
2735 if (fill_opacity
!= 1.0)
2736 cairo_push_group (cairo
);
2740 cairo_set_source (cairo
, pattern
);
2742 cairo_rectangle (cairo
, 0., 0., 1., 1.);
2745 cairo_mask (cairo
, maskPattern
);
2747 if (fill_opacity
!= 1.0) {
2748 cairo_pop_group_to_source (cairo
);
2751 cairo_rectangle (cairo
, 0., 0., 1., 1.);
2754 cairo_paint_with_alpha (cairo
, fill_opacity
);
2756 cairo_restore (cairo
);
2759 cairo_save (cairo_shape
);
2760 cairo_set_source (cairo_shape
, pattern
);
2762 cairo_rectangle (cairo_shape
, 0., 0., 1., 1.);
2763 cairo_fill (cairo_shape
);
2765 cairo_mask (cairo_shape
, pattern
);
2767 cairo_restore (cairo_shape
);
2770 cairo_pattern_destroy (maskPattern
);
2771 cairo_pattern_destroy (pattern
);
2778 GBool
CairoOutputDev::getStreamData (Stream
*str
, char **buffer
, int *length
)
2786 while (str
->getChar() != EOF
) len
++;
2790 strBuffer
= (char *)gmalloc (len
);
2794 for (i
= 0; i
< len
; ++i
)
2795 strBuffer
[i
] = str
->getChar();
2797 *buffer
= strBuffer
;
2803 static GBool
colorMapHasIdentityDecodeMap(GfxImageColorMap
*colorMap
)
2805 for (int i
= 0; i
< colorMap
->getNumPixelComps(); i
++) {
2806 if (colorMap
->getDecodeLow(i
) != 0.0 || colorMap
->getDecodeHigh(i
) != 1.0)
2812 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 11, 2)
2813 static cairo_status_t
setMimeIdFromRef(cairo_surface_t
*surface
,
2814 const char *mime_type
,
2815 const char *mime_id_prefix
,
2820 cairo_status_t status
;
2822 mime_id
= new GooString
;
2825 mime_id
->append(mime_id_prefix
);
2827 mime_id
->appendf("{0:d}-{1:d}", ref
.gen
, ref
.num
);
2829 idBuffer
= copyString(mime_id
->getCString());
2830 status
= cairo_surface_set_mime_data (surface
, mime_type
,
2831 (const unsigned char *)idBuffer
,
2832 mime_id
->getLength(),
2841 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0)
2842 GBool
CairoOutputDev::setMimeDataForJBIG2Globals(Stream
*str
,
2843 cairo_surface_t
*image
)
2845 JBIG2Stream
*jb2Str
= static_cast<JBIG2Stream
*>(str
);
2846 Object
* globalsStr
= jb2Str
->getGlobalsStream();
2847 char *globalsBuffer
;
2850 // nothing to do for JBIG2 stream without Globals
2851 if (!globalsStr
->isStream())
2854 if (setMimeIdFromRef(image
, CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID
, NULL
,
2855 jb2Str
->getGlobalsStreamRef()))
2858 if (!getStreamData(globalsStr
->getStream(), &globalsBuffer
, &globalsLength
))
2861 if (cairo_surface_set_mime_data (image
, CAIRO_MIME_TYPE_JBIG2_GLOBAL
,
2862 (const unsigned char*)globalsBuffer
,
2864 gfree
, (void*)globalsBuffer
))
2866 gfree (globalsBuffer
);
2874 void CairoOutputDev::setMimeData(GfxState
*state
, Stream
*str
, Object
*ref
,
2875 GfxImageColorMap
*colorMap
, cairo_surface_t
*image
)
2880 GfxColorSpace
*colorSpace
;
2881 StreamKind strKind
= str
->getKind();
2882 const char *mime_type
;
2889 mime_type
= CAIRO_MIME_TYPE_JPEG
;
2892 mime_type
= CAIRO_MIME_TYPE_JP2
;
2894 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0)
2896 mime_type
= CAIRO_MIME_TYPE_JBIG2
;
2903 str
->getDict()->lookup("ColorSpace", &obj
);
2904 colorSpace
= GfxColorSpace::parse(NULL
, &obj
, this, state
);
2907 // colorspace in stream dict may be different from colorspace in jpx
2909 if (strKind
== strJPX
&& colorSpace
)
2912 // only embed mime data for gray, rgb, and cmyk colorspaces.
2914 GfxColorSpaceMode mode
= colorSpace
->getMode();
2934 if (!colorMapHasIdentityDecodeMap(colorMap
))
2937 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0)
2938 if (strKind
== strJBIG2
&& !setMimeDataForJBIG2Globals(str
, image
))
2942 if (getStreamData (str
->getNextStream(), &strBuffer
, &len
)) {
2943 cairo_status_t status
= CAIRO_STATUS_SUCCESS
;
2945 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 11, 2)
2946 if (ref
&& ref
->isRef()) {
2947 status
= setMimeIdFromRef(image
, CAIRO_MIME_TYPE_UNIQUE_ID
,
2948 "poppler-surface-", ref
->getRef());
2952 status
= cairo_surface_set_mime_data (image
, mime_type
,
2953 (const unsigned char *)strBuffer
, len
,
2962 class RescaleDrawImage
: public CairoRescaleBox
{
2964 ImageStream
*imgStr
;
2967 GfxImageColorMap
*colorMap
;
2973 cairo_surface_t
*getSourceImage(Stream
*str
,
2974 int widthA
, int height
,
2975 int scaledWidth
, int scaledHeight
,
2977 GfxImageColorMap
*colorMapA
,
2979 cairo_surface_t
*image
= NULL
;
2983 colorMap
= colorMapA
;
2984 maskColors
= maskColorsA
;
2987 imageError
= gFalse
;
2989 /* TODO: Do we want to cache these? */
2990 imgStr
= new ImageStream(str
, width
,
2991 colorMap
->getNumPixelComps(),
2992 colorMap
->getBits());
2996 /* ICCBased color space doesn't do any color correction
2997 * so check its underlying color space as well */
2998 int is_identity_transform
;
2999 is_identity_transform
= colorMap
->getColorSpace()->getMode() == csDeviceRGB
||
3000 (colorMap
->getColorSpace()->getMode() == csICCBased
&&
3001 ((GfxICCBasedColorSpace
*)colorMap
->getColorSpace())->getAlt()->getMode() == csDeviceRGB
);
3004 // special case for one-channel (monochrome/gray/separation) images:
3005 // build a lookup table here
3006 if (colorMap
->getNumPixelComps() == 1) {
3010 n
= 1 << colorMap
->getBits();
3011 lookup
= (GfxRGB
*)gmallocn(n
, sizeof(GfxRGB
));
3012 for (i
= 0; i
< n
; ++i
) {
3015 colorMap
->getRGB(&pix
, &lookup
[i
]);
3019 if (printing
|| scaledWidth
>= width
|| scaledHeight
>= height
) {
3020 // No downscaling. Create cairo image containing the source image data.
3021 unsigned char *buffer
;
3024 image
= cairo_image_surface_create (maskColors
?
3025 CAIRO_FORMAT_ARGB32
:
3028 if (cairo_surface_status (image
))
3031 buffer
= cairo_image_surface_get_data (image
);
3032 stride
= cairo_image_surface_get_stride (image
);
3033 for (int y
= 0; y
< height
; y
++) {
3034 uint32_t *dest
= (uint32_t *) (buffer
+ y
* stride
);
3038 // // Downscaling required. Create cairo image the size of the
3039 // rescaled image and // downscale the source image data into
3040 // the cairo image. downScaleImage() will call getRow() to read
3041 // source image data from the image stream. This avoids having
3042 // to create an image the size of the source image which may
3043 // exceed cairo's 32676x32767 image size limit (and also saves a
3045 image
= cairo_image_surface_create (maskColors
?
3046 CAIRO_FORMAT_ARGB32
:
3048 scaledWidth
, scaledHeight
);
3049 if (cairo_surface_status (image
))
3052 downScaleImage(width
, height
,
3053 scaledWidth
, scaledHeight
,
3054 0, 0, scaledWidth
, scaledHeight
,
3057 cairo_surface_mark_dirty (image
);
3066 void getRow(int row_num
, uint32_t *row_data
) {
3070 if (row_num
<= current_row
)
3073 while (current_row
< row_num
) {
3074 pix
= imgStr
->getLine();
3078 if (unlikely(pix
== NULL
)) {
3079 memset(row_data
, 0, width
*4);
3081 error(errInternal
, -1, "Bad image stream");
3084 } else if (lookup
) {
3088 for (i
= 0; i
< width
; i
++) {
3091 ((int) colToByte(rgb
.r
) << 16) |
3092 ((int) colToByte(rgb
.g
) << 8) |
3093 ((int) colToByte(rgb
.b
) << 0);
3097 colorMap
->getRGBLine (pix
, row_data
, width
);
3101 for (int x
= 0; x
< width
; x
++) {
3102 bool is_opaque
= false;
3103 for (int i
= 0; i
< colorMap
->getNumPixelComps(); ++i
) {
3104 if (pix
[i
] < maskColors
[2*i
] ||
3105 pix
[i
] > maskColors
[2*i
+1]) {
3111 *row_data
|= 0xff000000;
3115 pix
+= colorMap
->getNumPixelComps();
3122 void CairoOutputDev::drawImage(GfxState
*state
, Object
*ref
, Stream
*str
,
3123 int widthA
, int heightA
,
3124 GfxImageColorMap
*colorMap
,
3126 int *maskColors
, GBool inlineImg
)
3128 cairo_surface_t
*image
;
3129 cairo_pattern_t
*pattern
, *maskPattern
;
3130 cairo_matrix_t matrix
;
3132 int scaledWidth
, scaledHeight
;
3133 cairo_filter_t filter
= CAIRO_FILTER_BILINEAR
;
3134 RescaleDrawImage rescale
;
3136 LOG (printf ("drawImage %dx%d\n", widthA
, heightA
));
3138 cairo_get_matrix(cairo
, &matrix
);
3139 getScaledSize (&matrix
, widthA
, heightA
, &scaledWidth
, &scaledHeight
);
3140 image
= rescale
.getSourceImage(str
, widthA
, heightA
, scaledWidth
, scaledHeight
, printing
, colorMap
, maskColors
);
3144 width
= cairo_image_surface_get_width (image
);
3145 height
= cairo_image_surface_get_height (image
);
3146 if (width
== widthA
&& height
== heightA
)
3147 filter
= getFilterForSurface (image
, interpolate
);
3149 if (!inlineImg
) /* don't read stream twice if it is an inline image */
3150 setMimeData(state
, str
, ref
, colorMap
, image
);
3152 pattern
= cairo_pattern_create_for_surface (image
);
3153 cairo_surface_destroy (image
);
3154 if (cairo_pattern_status (pattern
))
3157 cairo_pattern_set_filter (pattern
, filter
);
3160 cairo_pattern_set_extend (pattern
, CAIRO_EXTEND_PAD
);
3162 cairo_matrix_init_translate (&matrix
, 0, height
);
3163 cairo_matrix_scale (&matrix
, width
, -height
);
3164 cairo_pattern_set_matrix (pattern
, &matrix
);
3165 if (cairo_pattern_status (pattern
)) {
3166 cairo_pattern_destroy (pattern
);
3170 if (!mask
&& fill_opacity
!= 1.0) {
3171 maskPattern
= cairo_pattern_create_rgba (1., 1., 1., fill_opacity
);
3173 maskPattern
= cairo_pattern_reference (mask
);
3179 cairo_set_source (cairo
, pattern
);
3181 cairo_rectangle (cairo
, 0., 0., 1., 1.);
3186 cairo_set_matrix (cairo
, &mask_matrix
);
3187 cairo_mask (cairo
, maskPattern
);
3190 cairo_paint (cairo
);
3194 cairo_restore (cairo
);
3196 cairo_pattern_destroy (maskPattern
);
3199 cairo_save (cairo_shape
);
3200 cairo_set_source (cairo_shape
, pattern
);
3202 cairo_paint (cairo_shape
);
3204 cairo_rectangle (cairo_shape
, 0., 0., 1., 1.);
3205 cairo_fill (cairo_shape
);
3207 cairo_restore (cairo_shape
);
3210 cairo_pattern_destroy (pattern
);
3214 //------------------------------------------------------------------------
3216 //------------------------------------------------------------------------
3218 CairoImageOutputDev::CairoImageOutputDev()
3224 imgDrawCbkData
= NULL
;
3227 CairoImageOutputDev::~CairoImageOutputDev()
3231 for (i
= 0; i
< numImages
; i
++)
3236 void CairoImageOutputDev::saveImage(CairoImage
*image
)
3238 if (numImages
>= size
) {
3240 images
= (CairoImage
**) greallocn (images
, size
, sizeof (CairoImage
*));
3242 images
[numImages
++] = image
;
3245 void CairoImageOutputDev::getBBox(GfxState
*state
, int width
, int height
,
3246 double *x1
, double *y1
, double *x2
, double *y2
)
3248 double *ctm
= state
->getCTM();
3249 cairo_matrix_t matrix
;
3250 cairo_matrix_init(&matrix
,
3253 ctm
[2] + ctm
[4], ctm
[3] + ctm
[5]);
3255 int scaledWidth
, scaledHeight
;
3256 getScaledSize (&matrix
, width
, height
, &scaledWidth
, &scaledHeight
);
3258 if (matrix
.xx
>= 0) {
3261 *x1
= matrix
.x0
- scaledWidth
;
3263 *x2
= *x1
+ scaledWidth
;
3265 if (matrix
.yy
>= 0) {
3268 *y1
= matrix
.y0
- scaledHeight
;
3270 *y2
= *y1
+ scaledHeight
;
3273 void CairoImageOutputDev::drawImageMask(GfxState
*state
, Object
*ref
, Stream
*str
,
3274 int width
, int height
, GBool invert
,
3275 GBool interpolate
, GBool inlineImg
)
3278 cairo_surface_t
*surface
;
3279 double x1
, y1
, x2
, y2
;
3282 getBBox(state
, width
, height
, &x1
, &y1
, &x2
, &y2
);
3284 image
= new CairoImage (x1
, y1
, x2
, y2
);
3287 if (imgDrawCbk
&& imgDrawCbk (numImages
- 1, imgDrawCbkData
)) {
3288 surface
= cairo_image_surface_create (CAIRO_FORMAT_ARGB32
, width
, height
);
3289 cr
= cairo_create (surface
);
3291 cairo_translate (cr
, 0, height
);
3292 cairo_scale (cr
, width
, -height
);
3294 CairoOutputDev::drawImageMask(state
, ref
, str
, width
, height
, invert
, interpolate
, inlineImg
);
3295 image
->setImage (surface
);
3298 cairo_surface_destroy (surface
);
3303 void CairoImageOutputDev::setSoftMaskFromImageMask(GfxState
*state
, Object
*ref
, Stream
*str
,
3304 int width
, int height
, GBool invert
,
3305 GBool inlineImg
, double *baseMatrix
)
3308 cairo_surface_t
*surface
;
3309 double x1
, y1
, x2
, y2
;
3312 getBBox(state
, width
, height
, &x1
, &y1
, &x2
, &y2
);
3314 image
= new CairoImage (x1
, y1
, x2
, y2
);
3317 if (imgDrawCbk
&& imgDrawCbk (numImages
- 1, imgDrawCbkData
)) {
3318 surface
= cairo_image_surface_create (CAIRO_FORMAT_ARGB32
, width
, height
);
3319 cr
= cairo_create (surface
);
3321 cairo_translate (cr
, 0, height
);
3322 cairo_scale (cr
, width
, -height
);
3324 CairoOutputDev::drawImageMask(state
, ref
, str
, width
, height
, invert
, inlineImg
, gFalse
);
3325 if (state
->getFillColorSpace()->getMode() == csPattern
) {
3326 cairo_mask (cairo
, mask
);
3328 image
->setImage (surface
);
3331 cairo_surface_destroy (surface
);
3336 void CairoImageOutputDev::drawImage(GfxState
*state
, Object
*ref
, Stream
*str
,
3337 int width
, int height
, GfxImageColorMap
*colorMap
,
3338 GBool interpolate
, int *maskColors
, GBool inlineImg
)
3341 cairo_surface_t
*surface
;
3342 double x1
, y1
, x2
, y2
;
3345 getBBox(state
, width
, height
, &x1
, &y1
, &x2
, &y2
);
3347 image
= new CairoImage (x1
, y1
, x2
, y2
);
3350 if (imgDrawCbk
&& imgDrawCbk (numImages
- 1, imgDrawCbkData
)) {
3351 surface
= cairo_image_surface_create (CAIRO_FORMAT_ARGB32
, width
, height
);
3352 cr
= cairo_create (surface
);
3354 cairo_translate (cr
, 0, height
);
3355 cairo_scale (cr
, width
, -height
);
3357 CairoOutputDev::drawImage(state
, ref
, str
, width
, height
, colorMap
, interpolate
, maskColors
, inlineImg
);
3358 image
->setImage (surface
);
3361 cairo_surface_destroy (surface
);
3366 void CairoImageOutputDev::drawSoftMaskedImage(GfxState
*state
, Object
*ref
, Stream
*str
,
3367 int width
, int height
,
3368 GfxImageColorMap
*colorMap
,
3371 int maskWidth
, int maskHeight
,
3372 GfxImageColorMap
*maskColorMap
,
3373 GBool maskInterpolate
)
3376 cairo_surface_t
*surface
;
3377 double x1
, y1
, x2
, y2
;
3380 getBBox(state
, width
, height
, &x1
, &y1
, &x2
, &y2
);
3382 image
= new CairoImage (x1
, y1
, x2
, y2
);
3385 if (imgDrawCbk
&& imgDrawCbk (numImages
- 1, imgDrawCbkData
)) {
3386 surface
= cairo_image_surface_create (CAIRO_FORMAT_ARGB32
, width
, height
);
3387 cr
= cairo_create (surface
);
3389 cairo_translate (cr
, 0, height
);
3390 cairo_scale (cr
, width
, -height
);
3392 CairoOutputDev::drawSoftMaskedImage(state
, ref
, str
, width
, height
, colorMap
, interpolate
,
3393 maskStr
, maskWidth
, maskHeight
, maskColorMap
, maskInterpolate
);
3394 image
->setImage (surface
);
3397 cairo_surface_destroy (surface
);
3402 void CairoImageOutputDev::drawMaskedImage(GfxState
*state
, Object
*ref
, Stream
*str
,
3403 int width
, int height
,
3404 GfxImageColorMap
*colorMap
,
3407 int maskWidth
, int maskHeight
,
3408 GBool maskInvert
, GBool maskInterpolate
)
3411 cairo_surface_t
*surface
;
3412 double x1
, y1
, x2
, y2
;
3415 getBBox(state
, width
, height
, &x1
, &y1
, &x2
, &y2
);
3417 image
= new CairoImage (x1
, y1
, x2
, y2
);
3420 if (imgDrawCbk
&& imgDrawCbk (numImages
- 1, imgDrawCbkData
)) {
3421 surface
= cairo_image_surface_create (CAIRO_FORMAT_ARGB32
, width
, height
);
3422 cr
= cairo_create (surface
);
3424 cairo_translate (cr
, 0, height
);
3425 cairo_scale (cr
, width
, -height
);
3427 CairoOutputDev::drawMaskedImage(state
, ref
, str
, width
, height
, colorMap
, interpolate
,
3428 maskStr
, maskWidth
, maskHeight
, maskInvert
, maskInterpolate
);
3429 image
->setImage (surface
);
3432 cairo_surface_destroy (surface
);