root/middleware-offline/trunk/_src/eidmw/pteid-poppler/poppler/CairoOutputDev.cc @ 226

Revision 226, 87.1 KB (checked in by vsilva, 8 years ago)

Improve PDF native Signature

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