Merge lp:~matttbe/ubuntu/raring/poppler/lp1072129 into lp:ubuntu/raring/poppler

Proposed by Matthieu Baerts
Status: Merged
Merged at revision: 120
Proposed branch: lp:~matttbe/ubuntu/raring/poppler/lp1072129
Merge into: lp:ubuntu/raring/poppler
Diff against target: 3231 lines (+3184/-4)
6 files modified
.pc/applied-patches (+1/-0)
.pc/git_gouraud_shading_support.patch/poppler/CairoOutputDev.cc (+3121/-0)
debian/changelog (+8/-0)
debian/patches/git_gouraud_shading_support.patch (+38/-0)
debian/patches/series (+1/-0)
poppler/CairoOutputDev.cc (+15/-4)
To merge this branch: bzr merge lp:~matttbe/ubuntu/raring/poppler/lp1072129
Reviewer Review Type Date Requested Status
Ubuntu Sponsors Pending
Review via email: mp+134177@code.launchpad.net

Description of the change

Hello,

Due to a bug in poppler, Evince crash when loading some PDF files (LP: #1072129).
This new version simply adds the upstream patch in order to fix this bug.

For more details about this new version, please have a look at this bug #1072129.

Thank you for your help! :)

PS: You can easily test the new version by using these packages:
  - Ubuntu Raring 13.04: https://launchpad.net/~matttbe/+archive/ppa/+sourcepub/2781218/+listing-archive-extra
  - Ubuntu Quantal 12.10: https://launchpad.net/~matttbe/+archive/ppa/+sourcepub/2781220/+listing-archive-extra

To post a comment you must log in.

Preview Diff

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

Subscribers

People subscribed via source and target branches