CairoOutputDev.cc 102 KB
Newer Older
Kristian Høgsberg's avatar
Kristian Høgsberg committed
1 2 3 4 5 6 7 8
//========================================================================
//
// CairoOutputDev.cc
//
// Copyright 2003 Glyph & Cog, LLC
// Copyright 2004 Red Hat, Inc
//
//========================================================================
9 10 11 12 13

//========================================================================
//
// Modified under the Poppler project - http://poppler.freedesktop.org
//
14 15 16
// All changes made under the Poppler project to this file are licensed
// under GPL version 2 or later
//
17 18
// Copyright (C) 2005-2008 Jeff Muizelaar <jeff@infidigm.net>
// Copyright (C) 2005, 2006 Kristian Høgsberg <krh@redhat.com>
Albert Astals Cid's avatar
Albert Astals Cid committed
19
// Copyright (C) 2005, 2009, 2012, 2017 Albert Astals Cid <aacid@kde.org>
20
// Copyright (C) 2005 Nickolay V. Shmyrev <nshmyrev@yandex.ru>
Albert Astals Cid's avatar
Albert Astals Cid committed
21
// Copyright (C) 2006-2011, 2013, 2014, 2017 Carlos Garcia Campos <carlosgc@gnome.org>
22
// Copyright (C) 2008 Carl Worth <cworth@cworth.org>
Albert Astals Cid's avatar
Albert Astals Cid committed
23
// Copyright (C) 2008-2017 Adrian Johnson <ajohnson@redneon.com>
24
// Copyright (C) 2008 Michael Vrable <mvrable@cs.ucsd.edu>
Albert Astals Cid's avatar
Albert Astals Cid committed
25
// Copyright (C) 2008, 2009 Chris Wilson <chris@chris-wilson.co.uk>
26
// Copyright (C) 2008, 2012 Hib Eris <hib@hiberis.nl>
27
// Copyright (C) 2009, 2010 David Benjamin <davidben@mit.edu>
28
// Copyright (C) 2011-2014 Thomas Freitag <Thomas.Freitag@alfa.de>
Albert Astals Cid's avatar
Albert Astals Cid committed
29
// Copyright (C) 2012 Patrick Pfeifer <p2000@mailinator.com>
Albert Astals Cid's avatar
Albert Astals Cid committed
30
// Copyright (C) 2012, 2015, 2016 Jason Crain <jason@aquaticape.us>
Albert Astals Cid's avatar
Albert Astals Cid committed
31
// Copyright (C) 2015 Suzuki Toshiya <mpsuzuki@hiroshima-u.ac.jp>
32 33 34 35 36
//
// To see a description of the changes please see the Changelog file that
// came with your tarball or type make ChangeLog if you are building from git
//
//========================================================================
Kristian Høgsberg's avatar
Kristian Høgsberg committed
37 38 39 40 41 42 43

#include <config.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

44
#include <cstdint>
Kristian Høgsberg's avatar
Kristian Høgsberg committed
45 46
#include <string.h>
#include <math.h>
47
#include <assert.h>
Kristian Høgsberg's avatar
Kristian Høgsberg committed
48 49 50 51 52 53
#include <cairo.h>

#include "goo/gfile.h"
#include "GlobalParams.h"
#include "Error.h"
#include "Object.h"
54
#include "Gfx.h"
Kristian Høgsberg's avatar
Kristian Høgsberg committed
55 56
#include "GfxState.h"
#include "GfxFont.h"
57
#include "Page.h"
Kristian Høgsberg's avatar
Kristian Høgsberg committed
58 59
#include "Link.h"
#include "FontEncodingTables.h"
60
#include "PDFDocEncoding.h"
Kristian Høgsberg's avatar
Kristian Høgsberg committed
61 62 63 64
#include <fofi/FoFiTrueType.h>
#include <splash/SplashBitmap.h>
#include "CairoOutputDev.h"
#include "CairoFontEngine.h"
65
#include "CairoRescaleBox.h"
66
#include "UnicodeMap.h"
67
#include "JBIG2Stream.h"
Kristian Høgsberg's avatar
Kristian Høgsberg committed
68 69
//------------------------------------------------------------------------

70
// #define LOG_CAIRO
Kristian Høgsberg's avatar
Kristian Høgsberg committed
71

72 73 74 75 76
// To limit memory usage and improve performance when printing, limit
// cairo images to this size. 8192 is sufficient for an A2 sized
// 300ppi image.
#define MAX_PRINT_IMAGE_SIZE 8192

Kristian Høgsberg's avatar
Kristian Høgsberg committed
77 78 79 80 81 82
#ifdef LOG_CAIRO
#define LOG(x) (x)
#else
#define LOG(x)
#endif

83 84 85 86 87 88
static inline void printMatrix(cairo_matrix_t *matrix){
	printf("%f %f, %f %f (%f %f)\n", matrix->xx, matrix->yx,
			matrix->xy, matrix->yy,
			matrix->x0, matrix->y0);
}

Adrian Johnson's avatar
Adrian Johnson committed
89 90 91 92 93

#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#define MAX(a,b) (((a) > (b)) ? (a) : (b))


94 95 96 97
//------------------------------------------------------------------------
// CairoImage
//------------------------------------------------------------------------

98
CairoImage::CairoImage (double x1, double y1, double x2, double y2) {
99
  this->image = nullptr;
100 101 102 103 104 105 106 107 108 109
  this->x1 = x1;
  this->y1 = y1;
  this->x2 = x2;
  this->y2 = y2;
}

CairoImage::~CairoImage () {
  if (image)
    cairo_surface_destroy (image);
}
Kristian Høgsberg's avatar
Kristian Høgsberg committed
110

111 112 113 114 115 116
void CairoImage::setImage (cairo_surface_t *image) {
  if (this->image)
    cairo_surface_destroy (this->image);
  this->image = cairo_surface_reference (image);
}

Kristian Høgsberg's avatar
Kristian Høgsberg committed
117 118 119 120
//------------------------------------------------------------------------
// CairoOutputDev
//------------------------------------------------------------------------

121 122 123 124 125 126 127 128 129
// We cannot tie the lifetime of an FT_Library object to that of
// CairoOutputDev, since any FT_Faces created with it may end up with a
// reference by Cairo which can be held long after the CairoOutputDev is
// deleted.  The simplest way to avoid problems is to never tear down the
// FT_Library instance; to avoid leaks, just use a single global instance
// initialized the first time it is needed.
FT_Library CairoOutputDev::ft_lib;
GBool CairoOutputDev::ft_lib_initialized = gFalse;

130
CairoOutputDev::CairoOutputDev() {
131
  doc = nullptr;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
132

133 134 135 136 137
  if (!ft_lib_initialized) {
    FT_Init_FreeType(&ft_lib);
    ft_lib_initialized = gTrue;
  }

138
  fontEngine = nullptr;
139
  fontEngine_owner = gFalse;
140 141
  glyphs = nullptr;
  fill_pattern = nullptr;
142
  fill_color.r = fill_color.g = fill_color.b = 0;
143
  stroke_pattern = nullptr;
144
  stroke_color.r = stroke_color.g = stroke_color.b = 0;
145 146
  stroke_opacity = 1.0;
  fill_opacity = 1.0;
147 148 149 150
  textClipPath = nullptr;
  strokePathClip = nullptr;
  cairo = nullptr;
  currentFont = nullptr;
151 152 153
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0)
  prescaleImages = gFalse;
#else
154
  prescaleImages = gTrue;
155
#endif
156
  printing = gTrue;
157
  use_show_text_glyphs = gFalse;
158
  inUncoloredPattern = gFalse;
159
  inType3Char = gFalse;
160
  t3_glyph_has_bbox = gFalse;
161
  text_matrix_valid = gTrue;
162
  antialias = CAIRO_ANTIALIAS_DEFAULT;
163

164 165 166 167 168 169
  groupColorSpaceStack = nullptr;
  maskStack = nullptr;
  group = nullptr;
  mask = nullptr;
  shape = nullptr;
  cairo_shape = nullptr;
170
  knockoutCount = 0;
171

172 173
  text = nullptr;
  actualText = nullptr;
174 175 176

  // the SA parameter supposedly defaults to false, but Acrobat
  // apparently hardwires it to true
177
  stroke_adjust = gTrue;
178 179
  align_stroke_coords = gFalse;
  adjusted_stroke_width = gFalse;
180
  xref = nullptr;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
181 182 183
}

CairoOutputDev::~CairoOutputDev() {
184
  if (fontEngine_owner && fontEngine) {
Kristian Høgsberg's avatar
Kristian Høgsberg committed
185 186
    delete fontEngine;
  }
187

188 189
  if (cairo)
    cairo_destroy (cairo);
190 191
  cairo_pattern_destroy (stroke_pattern);
  cairo_pattern_destroy (fill_pattern);
192 193
  if (group)
    cairo_pattern_destroy (group);
Chris Wilson's avatar
Chris Wilson committed
194 195
  if (mask)
    cairo_pattern_destroy (mask);
196 197
  if (shape)
    cairo_pattern_destroy (shape);
198
  if (text) 
199 200 201
    text->decRefCnt();
  if (actualText)
    delete actualText;  
202
}
Kristian Høgsberg's avatar
Kristian Høgsberg committed
203

204
void CairoOutputDev::setCairo(cairo_t *cairo)
205
{
206
  if (this->cairo != nullptr) {
207 208
    cairo_status_t status = cairo_status (this->cairo);
    if (status) {
209
      error(errInternal, -1, "cairo context error: {0:s}\n", cairo_status_to_string(status));
210
    }
211
    cairo_destroy (this->cairo);
212
    assert(!cairo_shape);
213
  }
214
  if (cairo != nullptr) {
215
    this->cairo = cairo_reference (cairo);
216 217 218
	/* save the initial matrix so that we can use it for type3 fonts. */
	//XXX: is this sufficient? could we miss changes to the matrix somehow?
	cairo_get_matrix(cairo, &orig_matrix);
219
	setContextAntialias(cairo, antialias);
220
  } else {
221 222
    this->cairo = nullptr;
    this->cairo_shape = nullptr;
223
  }
Kristian Høgsberg's avatar
Kristian Høgsberg committed
224 225
}

226 227 228 229
void CairoOutputDev::setTextPage(TextPage *text)
{
  if (this->text) 
    this->text->decRefCnt();
230 231
  if (actualText)
    delete actualText;
232 233 234
  if (text) {
    this->text = text;
    this->text->incRefCnt();
235
    actualText = new ActualText(text);
236
  } else {
237 238
    this->text = nullptr;
    actualText = nullptr;
239 240 241
  }
}

242 243 244 245
void CairoOutputDev::setAntialias(cairo_antialias_t antialias)
{
  this->antialias = antialias;
  if (cairo)
246
    setContextAntialias (cairo, antialias);
247
  if (cairo_shape)
248
    setContextAntialias (cairo_shape, antialias);
249 250
}

251
void CairoOutputDev::setContextAntialias(cairo_t *cr, cairo_antialias_t antialias)
252 253
{
  cairo_font_options_t *font_options;
254
  cairo_set_antialias (cr, antialias);
255 256 257 258 259 260 261
  font_options = cairo_font_options_create ();
  cairo_get_font_options (cr, font_options);
  cairo_font_options_set_antialias (font_options, antialias);
  cairo_set_font_options (cr, font_options);
  cairo_font_options_destroy (font_options);
}

262
void CairoOutputDev::startDoc(PDFDoc *docA,
263
			      CairoFontEngine *parentFontEngine) {
264
  doc = docA;
265 266 267 268 269 270 271 272
  if (parentFontEngine) {
    fontEngine = parentFontEngine;
  } else {
    if (fontEngine) {
      delete fontEngine;
    }
    fontEngine = new CairoFontEngine(ft_lib);
    fontEngine_owner = gTrue;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
273
  }
Thomas Freitag's avatar
Thomas Freitag committed
274
  xref = doc->getXRef();
Kristian Høgsberg's avatar
Kristian Høgsberg committed
275 276
}

Thomas Freitag's avatar
Thomas Freitag committed
277
void CairoOutputDev::startPage(int pageNum, GfxState *state, XRef *xrefA) {
278 279 280
  /* set up some per page defaults */
  cairo_pattern_destroy(fill_pattern);
  cairo_pattern_destroy(stroke_pattern);
281 282

  fill_pattern = cairo_pattern_create_rgb(0., 0., 0.);
283
  fill_color.r = fill_color.g = fill_color.b = 0;
284
  stroke_pattern = cairo_pattern_reference(fill_pattern);
285
  stroke_color.r = stroke_color.g = stroke_color.b = 0;
286 287 288

  if (text)
    text->startPage(state);
289
  if (xrefA != nullptr) {
Thomas Freitag's avatar
Thomas Freitag committed
290 291
    xref = xrefA;
  }
292 293 294 295 296
}

void CairoOutputDev::endPage() {
  if (text) {
    text->endPage();
297
    text->coalesce(gTrue, 0, gFalse);
298
  }
299 300
}

Kristian Høgsberg's avatar
Kristian Høgsberg committed
301 302 303
void CairoOutputDev::saveState(GfxState *state) {
  LOG(printf ("save\n"));
  cairo_save (cairo);
304 305
  if (cairo_shape)
      cairo_save (cairo_shape);
306 307 308

  MaskStack *ms = new MaskStack;
  ms->mask = cairo_pattern_reference(mask);
Adrian Johnson's avatar
Adrian Johnson committed
309
  ms->mask_matrix = mask_matrix;
310 311
  ms->next = maskStack;
  maskStack = ms;
312 313 314

  if (strokePathClip)
    strokePathClip->ref_count++;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
315 316 317 318 319
}

void CairoOutputDev::restoreState(GfxState *state) {
  LOG(printf ("restore\n"));
  cairo_restore (cairo);
320 321
  if (cairo_shape)
      cairo_restore (cairo_shape);
322

323 324
  text_matrix_valid = gTrue;

325 326 327 328 329 330
  /* These aren't restored by cairo_restore() since we keep them in
   * the output device. */
  updateFillColor(state);
  updateStrokeColor(state);
  updateFillOpacity(state);
  updateStrokeOpacity(state);
331
  updateBlendMode(state);
332 333

  MaskStack* ms = maskStack;
334
  if (ms) {
335 336
    if (mask)
      cairo_pattern_destroy(mask);
337
    mask = ms->mask;
Adrian Johnson's avatar
Adrian Johnson committed
338
    mask_matrix = ms->mask_matrix;
339 340 341
    maskStack = ms->next;
    delete ms;
  }
342 343 344 345 346 347

  if (strokePathClip && --strokePathClip->ref_count == 0) {
    delete strokePathClip->path;
    if (strokePathClip->dashes)
      gfree (strokePathClip->dashes);
    gfree (strokePathClip);
348
    strokePathClip = nullptr;
349
  }
Kristian Høgsberg's avatar
Kristian Høgsberg committed
350 351 352 353 354 355 356 357 358 359 360
}

void CairoOutputDev::updateAll(GfxState *state) {
  updateLineDash(state);
  updateLineJoin(state);
  updateLineCap(state);
  updateLineWidth(state);
  updateFlatness(state);
  updateMiterLimit(state);
  updateFillColor(state);
  updateStrokeColor(state);
361 362
  updateFillOpacity(state);
  updateStrokeOpacity(state);
363
  updateBlendMode(state);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
364
  needFontUpdate = gTrue;
365 366
  if (text)
    text->updateFont(state);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
367 368
}

369 370 371 372 373 374 375 376 377
void CairoOutputDev::setDefaultCTM(double *ctm) {
  cairo_matrix_t matrix;
  matrix.xx = ctm[0];
  matrix.yx = ctm[1];
  matrix.xy = ctm[2];
  matrix.yy = ctm[3];
  matrix.x0 = ctm[4];
  matrix.y0 = ctm[5];

378
  cairo_transform (cairo, &matrix);
379
  if (cairo_shape)
380
      cairo_transform (cairo_shape, &matrix);
381 382 383 384

  OutputDev::setDefaultCTM(ctm);
}

Kristian Høgsberg's avatar
Kristian Høgsberg committed
385 386 387
void CairoOutputDev::updateCTM(GfxState *state, double m11, double m12,
				double m21, double m22,
				double m31, double m32) {
388
  cairo_matrix_t matrix, invert_matrix;
389 390 391 392 393 394 395
  matrix.xx = m11;
  matrix.yx = m12;
  matrix.xy = m21;
  matrix.yy = m22;
  matrix.x0 = m31;
  matrix.y0 = m32;

396 397 398 399 400 401 402 403
  /* Make sure the matrix is invertible before setting it.
   * cairo will blow up if we give it a matrix that's not
   * invertible, so we need to check before passing it
   * to cairo_transform. Ignoring it is likely to give better
   * results than not rendering anything at all. See #14398
   *
   * Ideally, we could do the cairo_transform
   * and then check if anything went wrong and fix it then
404 405 406
   * instead of having to invert the matrix. */
  invert_matrix = matrix;
  if (cairo_matrix_invert(&invert_matrix)) {
407
    error(errSyntaxWarning, -1, "matrix not invertible\n");
408 409 410
    return;
  }

411
  cairo_transform (cairo, &matrix);
412 413
  if (cairo_shape)
    cairo_transform (cairo_shape, &matrix);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
414 415 416 417 418 419 420 421 422 423 424 425
  updateLineDash(state);
  updateLineJoin(state);
  updateLineCap(state);
  updateLineWidth(state);
}

void CairoOutputDev::updateLineDash(GfxState *state) {
  double *dashPattern;
  int dashLength;
  double dashStart;

  state->getLineDash(&dashPattern, &dashLength, &dashStart);
426
  cairo_set_dash (cairo, dashPattern, dashLength, dashStart);
427 428
  if (cairo_shape)
    cairo_set_dash (cairo_shape, dashPattern, dashLength, dashStart);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
429 430 431
}

void CairoOutputDev::updateFlatness(GfxState *state) {
432
  // cairo_set_tolerance (cairo, state->getFlatness());
Kristian Høgsberg's avatar
Kristian Høgsberg committed
433 434 435 436 437 438 439 440 441 442 443 444 445 446
}

void CairoOutputDev::updateLineJoin(GfxState *state) {
  switch (state->getLineJoin()) {
  case 0:
    cairo_set_line_join (cairo, CAIRO_LINE_JOIN_MITER);
    break;
  case 1:
    cairo_set_line_join (cairo, CAIRO_LINE_JOIN_ROUND);
    break;
  case 2:
    cairo_set_line_join (cairo, CAIRO_LINE_JOIN_BEVEL);
    break;
  }
447 448
  if (cairo_shape)
    cairo_set_line_join (cairo_shape, cairo_get_line_join(cairo));
Kristian Høgsberg's avatar
Kristian Høgsberg committed
449 450 451 452 453 454 455 456 457 458 459 460 461 462
}

void CairoOutputDev::updateLineCap(GfxState *state) {
  switch (state->getLineCap()) {
  case 0:
    cairo_set_line_cap (cairo, CAIRO_LINE_CAP_BUTT);
    break;
  case 1:
    cairo_set_line_cap (cairo, CAIRO_LINE_CAP_ROUND);
    break;
  case 2:
    cairo_set_line_cap (cairo, CAIRO_LINE_CAP_SQUARE);
    break;
  }
463 464
  if (cairo_shape)
    cairo_set_line_cap (cairo_shape, cairo_get_line_cap(cairo));
Kristian Høgsberg's avatar
Kristian Høgsberg committed
465 466 467 468
}

void CairoOutputDev::updateMiterLimit(GfxState *state) {
  cairo_set_miter_limit (cairo, state->getMiterLimit());
469 470
  if (cairo_shape)
    cairo_set_miter_limit (cairo_shape, state->getMiterLimit());
Kristian Høgsberg's avatar
Kristian Høgsberg committed
471 472 473
}

void CairoOutputDev::updateLineWidth(GfxState *state) {
474
  LOG(printf ("line width: %f\n", state->getLineWidth()));
475
  adjusted_stroke_width = gFalse;
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
  double width = state->getLineWidth();
  if (stroke_adjust && !printing) {
    double x, y;
    x = y = width;

    /* find out line width in device units */
    cairo_user_to_device_distance(cairo, &x, &y);
    if (fabs(x) <= 1.0 && fabs(y) <= 1.0) {
      /* adjust width to at least one device pixel */
      x = y = 1.0;
      cairo_device_to_user_distance(cairo, &x, &y);
      width = MIN(fabs(x),fabs(y));
      adjusted_stroke_width = gTrue;
    }
  } else if (width == 0.0) {
    /* Cairo does not support 0 line width == 1 device pixel. Find out
     * how big pixels (device unit) are in the x and y
     * directions. Choose the smaller of the two as our line width.
     */
495
    double x = 1.0, y = 1.0;
496 497 498 499 500
    if (printing) {
      // assume printer pixel size is 1/600 inch
      x = 72.0/600;
      y = 72.0/600;
    }
501
    cairo_device_to_user_distance(cairo, &x, &y);
502
    width = MIN(fabs(x),fabs(y));
503
  }
504
  cairo_set_line_width (cairo, width);
505 506
  if (cairo_shape)
    cairo_set_line_width (cairo_shape, cairo_get_line_width (cairo));
Kristian Høgsberg's avatar
Kristian Høgsberg committed
507 508 509
}

void CairoOutputDev::updateFillColor(GfxState *state) {
510
  GfxRGB color = fill_color;
511

512 513 514
  if (inUncoloredPattern)
    return;

515
  state->getFillRGB(&fill_color);
516 517
  if (cairo_pattern_get_type (fill_pattern) != CAIRO_PATTERN_TYPE_SOLID ||
      color.r != fill_color.r ||
518 519 520 521
      color.g != fill_color.g ||
      color.b != fill_color.b)
  {
    cairo_pattern_destroy(fill_pattern);
522 523 524
    fill_pattern = cairo_pattern_create_rgba(colToDbl(fill_color.r),
					     colToDbl(fill_color.g),
					     colToDbl(fill_color.b),
525 526 527 528 529
					     fill_opacity);

    LOG(printf ("fill color: %d %d %d\n",
		fill_color.r, fill_color.g, fill_color.b));
  }
Kristian Høgsberg's avatar
Kristian Høgsberg committed
530 531 532
}

void CairoOutputDev::updateStrokeColor(GfxState *state) {
533
  GfxRGB color = stroke_color;
534

535 536 537
  if (inUncoloredPattern)
    return;

538
  state->getStrokeRGB(&stroke_color);
539 540
  if (cairo_pattern_get_type (fill_pattern) != CAIRO_PATTERN_TYPE_SOLID ||
      color.r != stroke_color.r ||
541 542 543 544
      color.g != stroke_color.g ||
      color.b != stroke_color.b)
  {
    cairo_pattern_destroy(stroke_pattern);
545 546 547
    stroke_pattern = cairo_pattern_create_rgba(colToDbl(stroke_color.r),
					       colToDbl(stroke_color.g),
					       colToDbl(stroke_color.b),
548 549 550 551 552
					       stroke_opacity);

    LOG(printf ("stroke color: %d %d %d\n",
		stroke_color.r, stroke_color.g, stroke_color.b));
  }
Kristian Høgsberg's avatar
Kristian Høgsberg committed
553 554
}

555
void CairoOutputDev::updateFillOpacity(GfxState *state) {
556
  double opacity = fill_opacity;
557

558 559 560
  if (inUncoloredPattern)
    return;

561 562 563
  fill_opacity = state->getFillOpacity();
  if (opacity != fill_opacity) {
    cairo_pattern_destroy(fill_pattern);
564 565 566
    fill_pattern = cairo_pattern_create_rgba(colToDbl(fill_color.r),
					     colToDbl(fill_color.g),
					     colToDbl(fill_color.b),
567 568 569 570
					     fill_opacity);

    LOG(printf ("fill opacity: %f\n", fill_opacity));
  }
571 572 573
}

void CairoOutputDev::updateStrokeOpacity(GfxState *state) {
574
  double opacity = stroke_opacity;
575

576 577 578
  if (inUncoloredPattern)
    return;

579 580 581
  stroke_opacity = state->getStrokeOpacity();
  if (opacity != stroke_opacity) {
    cairo_pattern_destroy(stroke_pattern);
582 583 584
    stroke_pattern = cairo_pattern_create_rgba(colToDbl(stroke_color.r),
					       colToDbl(stroke_color.g),
					       colToDbl(stroke_color.b),
585 586 587 588
					       stroke_opacity);

    LOG(printf ("stroke opacity: %f\n", stroke_opacity));
  }
589 590
}

591
void CairoOutputDev::updateFillColorStop(GfxState *state, double offset) {
592 593 594
  if (inUncoloredPattern)
    return;

595 596 597
  state->getFillRGB(&fill_color);

  cairo_pattern_add_color_stop_rgba(fill_pattern, offset,
598 599 600
				    colToDbl(fill_color.r),
				    colToDbl(fill_color.g),
				    colToDbl(fill_color.b),
601 602 603 604 605
				    fill_opacity);
  LOG(printf ("fill color stop: %f (%d, %d, %d)\n",
	      offset, fill_color.r, fill_color.g, fill_color.b));
}

606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660
void CairoOutputDev::updateBlendMode(GfxState *state) {
  switch (state->getBlendMode()) {
  default:
  case gfxBlendNormal:
    cairo_set_operator (cairo, CAIRO_OPERATOR_OVER);
    break;
  case gfxBlendMultiply:
    cairo_set_operator (cairo, CAIRO_OPERATOR_MULTIPLY);
    break;
  case gfxBlendScreen:
    cairo_set_operator (cairo, CAIRO_OPERATOR_SCREEN);
    break;
  case gfxBlendOverlay:
    cairo_set_operator (cairo, CAIRO_OPERATOR_OVERLAY);
    break;
  case gfxBlendDarken:
    cairo_set_operator (cairo, CAIRO_OPERATOR_DARKEN);
    break;
  case gfxBlendLighten:
    cairo_set_operator (cairo, CAIRO_OPERATOR_LIGHTEN);
    break;
  case gfxBlendColorDodge:
    cairo_set_operator (cairo, CAIRO_OPERATOR_COLOR_DODGE);
    break;
  case gfxBlendColorBurn:
    cairo_set_operator (cairo, CAIRO_OPERATOR_COLOR_BURN);
    break;
  case gfxBlendHardLight:
    cairo_set_operator (cairo, CAIRO_OPERATOR_HARD_LIGHT);
    break;
  case gfxBlendSoftLight:
    cairo_set_operator (cairo, CAIRO_OPERATOR_SOFT_LIGHT);
    break;
  case gfxBlendDifference:
    cairo_set_operator (cairo, CAIRO_OPERATOR_DIFFERENCE);
    break;
  case gfxBlendExclusion:
    cairo_set_operator (cairo, CAIRO_OPERATOR_EXCLUSION);
    break;
  case gfxBlendHue:
    cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_HUE);
    break;
  case gfxBlendSaturation:
    cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_SATURATION);
    break;
  case gfxBlendColor:
    cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_COLOR);
    break;
  case gfxBlendLuminosity:
    cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_LUMINOSITY);
    break;
  }
  LOG(printf ("blend mode: %d\n", (int)state->getBlendMode()));
}

Kristian Høgsberg's avatar
Kristian Høgsberg committed
661
void CairoOutputDev::updateFont(GfxState *state) {
662
  cairo_font_face_t *font_face;
663
  cairo_matrix_t matrix, invert_matrix;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
664 665

  LOG(printf ("updateFont() font=%s\n", state->getFont()->getName()->getCString()));
666

Kristian Høgsberg's avatar
Kristian Høgsberg committed
667 668
  needFontUpdate = gFalse;

669 670 671 672
  //FIXME: use cairo font engine?
  if (text)
    text->updateFont(state);
  
Thomas Freitag's avatar
Thomas Freitag committed
673
  currentFont = fontEngine->getFont (state->getFont(), doc, printing, xref);
674

675 676
  if (!currentFont)
    return;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
677

678 679
  font_face = currentFont->getFontFace();
  cairo_set_font_face (cairo, font_face);
680 681 682

  use_show_text_glyphs = state->getFont()->hasToUnicodeCMap() &&
    cairo_surface_has_show_text_glyphs (cairo_get_target (cairo));
683 684 685
 
  double fontSize = state->getFontSize();
  double *m = state->getTextMat();
686 687 688 689 690 691
  /* NOTE: adjusting by a constant is hack. The correct solution
   * is probably to use user-fonts and compute the scale on a per
   * glyph basis instead of for the entire font */
  double w = currentFont->getSubstitutionCorrection(state->getFont());
  matrix.xx = m[0] * fontSize * state->getHorizScaling() * w;
  matrix.yx = m[1] * fontSize * state->getHorizScaling() * w;
692 693
  matrix.xy = -m[2] * fontSize;
  matrix.yy = -m[3] * fontSize;
694 695
  matrix.x0 = 0;
  matrix.y0 = 0;
696

697 698
  LOG(printf ("font matrix: %f %f %f %f\n", matrix.xx, matrix.yx, matrix.xy, matrix.yy));

699 700 701 702 703 704 705 706
 /* Make sure the font matrix is invertible before setting it.  cairo
  * will blow up if we give it a matrix that's not invertible, so we
  * need to check before passing it to cairo_set_font_matrix. Ignoring it
  * is likely to give better results than not rendering anything at
  * all. See #18254.
  */
  invert_matrix = matrix;
  if (cairo_matrix_invert(&invert_matrix)) {
707 708
    error(errSyntaxWarning, -1, "font matrix not invertible");
    text_matrix_valid = gFalse;
709 710 711
    return;
  }

712
  cairo_set_font_matrix (cairo, &matrix);
713
  text_matrix_valid = gTrue;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
714 715
}

716 717 718 719 720 721 722
/* Tolerance in pixels for checking if strokes are horizontal or vertical
 * lines in device space */
#define STROKE_COORD_TOLERANCE 0.5

/* Align stroke coordinate i if the point is the start or end of a
 * horizontal or vertical line */
void CairoOutputDev::alignStrokeCoords(GfxSubpath *subpath, int i, double *x, double *y)
723
{
724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757
  double x1, y1, x2, y2;
  GBool align = gFalse;

  x1 = subpath->getX(i);
  y1 = subpath->getY(i);
  cairo_user_to_device (cairo, &x1, &y1);

  // Does the current coord and prev coord form a horiz or vert line?
  if (i > 0 && !subpath->getCurve(i - 1)) {
    x2 = subpath->getX(i - 1);
    y2 = subpath->getY(i - 1);
    cairo_user_to_device (cairo, &x2, &y2);
    if (fabs(x2 - x1) < STROKE_COORD_TOLERANCE || fabs(y2 - y1) < STROKE_COORD_TOLERANCE)
      align = gTrue;
  }

  // Does the current coord and next coord form a horiz or vert line?
  if (i < subpath->getNumPoints() - 1 && !subpath->getCurve(i + 1)) {
    x2 = subpath->getX(i + 1);
    y2 = subpath->getY(i + 1);
    cairo_user_to_device (cairo, &x2, &y2);
    if (fabs(x2 - x1) < STROKE_COORD_TOLERANCE || fabs(y2 - y1) < STROKE_COORD_TOLERANCE)
      align = gTrue;
  }

  *x = subpath->getX(i);
  *y = subpath->getY(i);
  if (align) {
    /* see http://www.cairographics.org/FAQ/#sharp_lines */
    cairo_user_to_device (cairo, x, y);
    *x = floor(*x) + 0.5;
    *y = floor(*y) + 0.5;
    cairo_device_to_user (cairo, x, y);
  }
758 759
}

760 761
#undef STROKE_COORD_TOLERANCE

762
void CairoOutputDev::doPath(cairo_t *cairo, GfxState *state, GfxPath *path) {
Kristian Høgsberg's avatar
Kristian Høgsberg committed
763 764
  GfxSubpath *subpath;
  int i, j;
765
  double x, y;
766
  cairo_new_path (cairo);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
767 768 769
  for (i = 0; i < path->getNumSubpaths(); ++i) {
    subpath = path->getSubpath(i);
    if (subpath->getNumPoints() > 0) {
770
      if (align_stroke_coords) {
771
        alignStrokeCoords(subpath, 0, &x, &y);
772
      } else {
773 774
        x = subpath->getX(0);
        y = subpath->getY(0);
775
      }
776
      cairo_move_to (cairo, x, y);
777
      j = 1;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
778 779
      while (j < subpath->getNumPoints()) {
	if (subpath->getCurve(j)) {
780 781 782 783 784 785
	  if (align_stroke_coords) {
            alignStrokeCoords(subpath, j + 2, &x, &y);
          } else {
            x = subpath->getX(j+2);
            y = subpath->getY(j+2);
          }
786 787 788
	  cairo_curve_to( cairo,
			  subpath->getX(j), subpath->getY(j),
			  subpath->getX(j+1), subpath->getY(j+1),
789
			  x, y);
790

Kristian Høgsberg's avatar
Kristian Høgsberg committed
791 792
	  j += 3;
	} else {
793
	  if (align_stroke_coords) {
794 795 796 797 798 799
            alignStrokeCoords(subpath, j, &x, &y);
          } else {
            x = subpath->getX(j);
            y = subpath->getY(j);
          }
          cairo_line_to (cairo, x, y);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
800 801 802 803 804 805 806 807 808 809 810 811
	  ++j;
	}
      }
      if (subpath->isClosed()) {
	LOG (printf ("close\n"));
	cairo_close_path (cairo);
      }
    }
  }
}

void CairoOutputDev::stroke(GfxState *state) {
812 813 814 815 816 817 818
  if (inType3Char) {
      GfxGray gray;
      state->getFillGray(&gray);
      if (colToDbl(gray) > 0.5)
	  return;
  }

819 820
  if (adjusted_stroke_width)
    align_stroke_coords = gTrue;
821
  doPath (cairo, state, state->getPath());
822
  align_stroke_coords = gFalse;
823
  cairo_set_source (cairo, stroke_pattern);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
824
  LOG(printf ("stroke\n"));
825 826 827 828 829 830 831 832
  if (strokePathClip) {
    cairo_push_group (cairo);
    cairo_stroke (cairo);
    cairo_pop_group_to_source (cairo);
    fillToStrokePathClip (state);
  } else {
    cairo_stroke (cairo);
  }
833 834 835 836
  if (cairo_shape) {
    doPath (cairo_shape, state, state->getPath());
    cairo_stroke (cairo_shape);
  }
Kristian Høgsberg's avatar
Kristian Høgsberg committed
837 838 839
}

void CairoOutputDev::fill(GfxState *state) {
840 841 842 843 844 845 846
  if (inType3Char) {
      GfxGray gray;
      state->getFillGray(&gray);
      if (colToDbl(gray) > 0.5)
	  return;
  }

847
  doPath (cairo, state, state->getPath());
Kristian Høgsberg's avatar
Kristian Høgsberg committed
848
  cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_WINDING);
849
  cairo_set_source (cairo, fill_pattern);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
850
  LOG(printf ("fill\n"));
851
  //XXX: how do we get the path
852
  if (mask) {
Adrian Johnson's avatar
Adrian Johnson committed
853
    cairo_save (cairo);
854
    cairo_clip (cairo);
855 856 857 858 859
    if (strokePathClip) {
      cairo_push_group (cairo);
      fillToStrokePathClip (state);
      cairo_pop_group_to_source (cairo);
    }
Adrian Johnson's avatar
Adrian Johnson committed
860
    cairo_set_matrix (cairo, &mask_matrix);
861
    cairo_mask (cairo, mask);
Adrian Johnson's avatar
Adrian Johnson committed
862
    cairo_restore (cairo);
Adrian Johnson's avatar
Adrian Johnson committed
863
  } else if (strokePathClip) {
864
    fillToStrokePathClip(state);
865 866 867
  } else {
    cairo_fill (cairo);
  }
868 869 870 871 872
  if (cairo_shape) {
    cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_WINDING);
    doPath (cairo_shape, state, state->getPath());
    cairo_fill (cairo_shape);
  }
Kristian Høgsberg's avatar
Kristian Høgsberg committed
873 874 875
}

void CairoOutputDev::eoFill(GfxState *state) {
876
  doPath (cairo, state, state->getPath());
Kristian Høgsberg's avatar
Kristian Høgsberg committed
877
  cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_EVEN_ODD);
878
  cairo_set_source (cairo, fill_pattern);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
879
  LOG(printf ("fill-eo\n"));
880

881 882 883 884 885 886 887 888 889
  if (mask) {
    cairo_save (cairo);
    cairo_clip (cairo);
    cairo_set_matrix (cairo, &mask_matrix);
    cairo_mask (cairo, mask);
    cairo_restore (cairo);
  } else {
    cairo_fill (cairo);
  }
890 891 892 893 894 895
  if (cairo_shape) {
    cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_EVEN_ODD);
    doPath (cairo_shape, state, state->getPath());
    cairo_fill (cairo_shape);
  }

Kristian Høgsberg's avatar
Kristian Høgsberg committed
896 897
}

Thomas Freitag's avatar
Thomas Freitag committed
898
GBool CairoOutputDev::tilingPatternFill(GfxState *state, Gfx *gfxA, Catalog *cat, Object *str,
899
					double *pmat, int paintType, int /*tilingType*/, Dict *resDict,
900 901 902 903 904 905 906 907 908
					double *mat, double *bbox,
					int x0, int y0, int x1, int y1,
					double xStep, double yStep)
{
  PDFRectangle box;
  Gfx *gfx;
  cairo_pattern_t *pattern;
  cairo_surface_t *surface;
  cairo_matrix_t matrix;
909
  cairo_matrix_t pattern_matrix;
910 911
  cairo_t *old_cairo;
  double xMin, yMin, xMax, yMax;
912
  double width, height;
913
  double scaleX, scaleY;
914
  int surface_width, surface_height;
Adrian Johnson's avatar
Adrian Johnson committed
915
  StrokePathClip *strokePathTmp;
916
  GBool adjusted_stroke_width_tmp;
917
  cairo_pattern_t *maskTmp;
918

919 920 921 922
  width = bbox[2] - bbox[0];
  height = bbox[3] - bbox[1];

  if (xStep != width || yStep != height)
923 924 925
    return gFalse;
  /* TODO: implement the other cases here too */

926 927 928 929 930 931 932 933 934 935 936 937
  // Find the width and height of the transformed pattern
  cairo_get_matrix (cairo, &matrix);
  cairo_matrix_init (&pattern_matrix, mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
  cairo_matrix_multiply (&matrix, &matrix, &pattern_matrix);

  double widthX = width, widthY = 0;
  cairo_matrix_transform_distance (&matrix, &widthX, &widthY);
  surface_width = ceil (sqrt (widthX * widthX + widthY * widthY));

  double heightX = 0, heightY = height;
  cairo_matrix_transform_distance (&matrix, &heightX, &heightY);
  surface_height = ceil (sqrt (heightX * heightX + heightY * heightY));
938 939
  scaleX = surface_width / width;
  scaleY = surface_height / height;
940

941 942
  surface = cairo_surface_create_similar (cairo_get_target (cairo),
					  CAIRO_CONTENT_COLOR_ALPHA,
943
					  surface_width, surface_height);
944 945 946 947 948 949
  if (cairo_surface_status (surface))
    return gFalse;

  old_cairo = cairo;
  cairo = cairo_create (surface);
  cairo_surface_destroy (surface);
950
  setContextAntialias(cairo, antialias);
951 952 953

  box.x1 = bbox[0]; box.y1 = bbox[1];
  box.x2 = bbox[2]; box.y2 = bbox[3];
954 955 956
  cairo_scale (cairo, scaleX, scaleY);
  cairo_translate (cairo, -box.x1, -box.y1);

Adrian Johnson's avatar
Adrian Johnson committed
957
  strokePathTmp = strokePathClip;
958
  strokePathClip = nullptr;
959
  adjusted_stroke_width_tmp = adjusted_stroke_width;
960
  maskTmp = mask;
961 962
  mask = nullptr;
  gfx = new Gfx(doc, this, resDict, &box, nullptr, nullptr, nullptr, gfxA->getXRef());
963 964
  if (paintType == 2)
    inUncoloredPattern = gTrue;
965
  gfx->display(str);
966 967
  if (paintType == 2)
    inUncoloredPattern = gFalse;
968
  delete gfx;
Adrian Johnson's avatar
Adrian Johnson committed
969
  strokePathClip = strokePathTmp;
970
  adjusted_stroke_width = adjusted_stroke_width_tmp;
971
  mask = maskTmp;
972 973 974 975 976 977 978 979 980 981

  pattern = cairo_pattern_create_for_surface (cairo_get_target (cairo));
  cairo_destroy (cairo);
  cairo = old_cairo;
  if (cairo_pattern_status (pattern))
    return gFalse;

  state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
  cairo_rectangle (cairo, xMin, yMin, xMax - xMin, yMax - yMin);

982 983
  cairo_matrix_init_scale (&matrix, scaleX, scaleY);
  cairo_matrix_translate (&matrix, -box.x1, -box.y1);
984 985
  cairo_pattern_set_matrix (pattern, &matrix);

986
  cairo_transform (cairo, &pattern_matrix);
987 988
  cairo_set_source (cairo, pattern);
  cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
Adrian Johnson's avatar
Adrian Johnson committed
989
  if (strokePathClip) {
990
    fillToStrokePathClip(state);
Adrian Johnson's avatar
Adrian Johnson committed
991 992 993
  } else {
    cairo_fill (cairo);
  }
994 995 996 997 998 999

  cairo_pattern_destroy (pattern);

  return gTrue;
}

1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0)
GBool CairoOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
{
  // Function shaded fills are subdivided to rectangles that are the
  // following size in device space.  Note when printing this size is
  // in points.
  const int subdivide_pixels = 10;

  double x_begin, x_end, x1, x2;
  double y_begin, y_end, y1, y2;
  double x_step;
  double y_step;
  GfxColor color;
  GfxRGB rgb;
  double *matrix;
  cairo_matrix_t mat;

  matrix = shading->getMatrix();
  mat.xx = matrix[0];
  mat.yx = matrix[1];
  mat.xy = matrix[2];
  mat.yy = matrix[3];
  mat.x0 = matrix[4];
  mat.y0 = matrix[5];
  if (cairo_matrix_invert(&mat)) {
    error(errSyntaxWarning, -1, "matrix not invertible\n");
    return gFalse;
    }

  // get cell size in pattern space
  x_step = y_step = subdivide_pixels;
  cairo_matrix_transform_distance (&mat, &x_step, &y_step);

  cairo_pattern_destroy(fill_pattern);
  fill_pattern = cairo_pattern_create_mesh ();
  cairo_pattern_set_matrix(fill_pattern, &mat);
  shading->getDomain(&x_begin, &y_begin, &x_end, &y_end);

  for (x1 = x_begin; x1 < x_end; x1 += x_step) {
    x2 = x1 + x_step;
    if (x2 > x_end)
      x2 = x_end;

    for (y1 = y_begin; y1 < y_end; y1 += y_step) {
      y2 = y1 + y_step;
      if (y2 > y_end)
	y2 = y_end;

      cairo_mesh_pattern_begin_patch (fill_pattern);
      cairo_mesh_pattern_move_to (fill_pattern, x1, y1);
      cairo_mesh_pattern_line_to (fill_pattern, x2, y1);
      cairo_mesh_pattern_line_to (fill_pattern, x2, y2);
      cairo_mesh_pattern_line_to (fill_pattern, x1, y2);

      shading->getColor(x1, y1, &color);
      shading->getColorSpace()->getRGB(&color, &rgb);
      cairo_mesh_pattern_set_corner_color_rgb (fill_pattern, 0,
					       colToDbl(rgb.r),
					       colToDbl(rgb.g),
					       colToDbl(rgb.b));

      shading->getColor(x2, y1, &color);
      shading->getColorSpace()->getRGB(&color, &rgb);
      cairo_mesh_pattern_set_corner_color_rgb (fill_pattern, 1,
					       colToDbl(rgb.r),
					       colToDbl(rgb.g),
					       colToDbl(rgb.b));

      shading->getColor(x2, y2, &color);
      shading->getColorSpace()->getRGB(&color, &rgb);
      cairo_mesh_pattern_set_corner_color_rgb (fill_pattern, 2,
					       colToDbl(rgb.r),
					       colToDbl(rgb.g),
					       colToDbl(rgb.b));

      shading->getColor(x1, y2, &color);
      shading->getColorSpace()->getRGB(&color, &rgb);
      cairo_mesh_pattern_set_corner_color_rgb (fill_pattern, 3,
					       colToDbl(rgb.r),
					       colToDbl(rgb.g),
					       colToDbl(rgb.b));

      cairo_mesh_pattern_end_patch (fill_pattern);
    }
  }

  double xMin, yMin, xMax, yMax;
  // get the clip region bbox
  state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
  state->moveTo(xMin, yMin);
  state->lineTo(xMin, yMax);
  state->lineTo(xMax, yMax);
  state->lineTo(xMax, yMin);
  state->closePath();
  fill(state);
  state->clearPath();

  return gTrue;
}
#endif /* CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) */

1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111
GBool CairoOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) {
  double x0, y0, x1, y1;
  double dx, dy;

  shading->getCoords(&x0, &y0, &x1, &y1);
  dx = x1 - x0;
  dy = y1 - y0;

  cairo_pattern_destroy(fill_pattern);
  fill_pattern = cairo_pattern_create_linear (x0 + tMin * dx, y0 + tMin * dy,
					      x0 + tMax * dx, y0 + tMax * dy);
1112 1113 1114 1115
  if (!shading->getExtend0() && !shading->getExtend1())
    cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_NONE);
  else
    cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_PAD);
1116

1117 1118
  LOG (printf ("axial-sh\n"));

1119 1120 1121 1122 1123
  // TODO: use the actual stops in the shading in the case
  // of linear interpolation (Type 2 Exponential functions with N=1)
  return gFalse;
}

1124 1125 1126 1127 1128
GBool CairoOutputDev::axialShadedSupportExtend(GfxState *state, GfxAxialShading *shading)
{
  return (shading->getExtend0() == shading->getExtend1());
}

1129 1130 1131
GBool CairoOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading, double sMin, double sMax) {
  double x0, y0, r0, x1, y1, r1;
  double dx, dy, dr;
Jason Crain's avatar
Jason Crain committed
1132 1133
  cairo_matrix_t matrix;
  double scale;
1134 1135 1136 1137 1138

  shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
  dx = x1 - x0;
  dy = y1 - y0;
  dr = r1 - r0;
Jason Crain's avatar
Jason Crain committed
1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149

  // Cairo/pixman do not work well with a very large or small scaled
  // matrix.  See cairo bug #81657.
  //
  // As a workaround, scale the pattern by the average of the vertical
  // and horizontal scaling of the current transformation matrix.
  cairo_get_matrix(cairo, &matrix);
  scale = (sqrt(matrix.xx * matrix.xx + matrix.yx * matrix.yx)
	   + sqrt(matrix.xy * matrix.xy + matrix.yy * matrix.yy)) / 2;
  cairo_matrix_init_scale(&matrix, scale, scale);

1150
  cairo_pattern_destroy(fill_pattern);
Jason Crain's avatar
Jason Crain committed
1151 1152 1153 1154 1155 1156 1157
  fill_pattern = cairo_pattern_create_radial ((x0 + sMin * dx) * scale,
					      (y0 + sMin * dy) * scale,
					      (r0 + sMin * dr) * scale,
					      (x0 + sMax * dx) * scale,
					      (y0 + sMax * dy) * scale,
					      (r0 + sMax * dr) * scale);
  cairo_pattern_set_matrix(fill_pattern, &matrix);
1158 1159 1160 1161 1162
  if (shading->getExtend0() && shading->getExtend1())
    cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_PAD);
  else
    cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_NONE);

1163 1164
  LOG (printf ("radial-sh\n"));

1165 1166 1167 1168 1169 1170 1171 1172
  return gFalse;
}

GBool CairoOutputDev::radialShadedSupportExtend(GfxState *state, GfxRadialShading *shading)
{
  return (shading->getExtend0() == shading->getExtend1());
}

1173
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0)
1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184
GBool CairoOutputDev::gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading)
{
  double x0, y0, x1, y1, x2, y2;
  GfxColor color[3];
  int i, j;
  GfxRGB rgb;

  cairo_pattern_destroy(fill_pattern);
  fill_pattern = cairo_pattern_create_mesh ();

  for (i = 0; i < shading->getNTriangles(); i++) {
1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199
    if (shading->isParameterized()) {
      double color0, color1, color2;
      shading->getTriangle(i, &x0, &y0, &color0,
                              &x1, &y1, &color1,
                              &x2, &y2, &color2);
      shading->getParameterizedColor(color0, &color[0]);
      shading->getParameterizedColor(color1, &color[1]);
      shading->getParameterizedColor(color2, &color[2]);
    } else {
      shading->getTriangle(i,
                           &x0, &y0, &color[0],
                           &x1, &y1, &color[1],
                           &x2, &y2, &color[2]);

    }
1200

1201
    cairo_mesh_pattern_begin_patch (fill_pattern);
1202

1203 1204 1205
    cairo_mesh_pattern_move_to (fill_pattern, x0, y0);
    cairo_mesh_pattern_line_to (fill_pattern, x1, y1);
    cairo_mesh_pattern_line_to (fill_pattern, x2, y2);
1206 1207 1208

    for (j = 0; j < 3; j++) {
	shading->getColorSpace()->getRGB(&color[j], &rgb);
1209
	cairo_mesh_pattern_set_corner_color_rgb (fill_pattern, j,
1210 1211 1212 1213 1214
						 colToDbl(rgb.r),
						 colToDbl(rgb.g),
						 colToDbl(rgb.b));
    }

1215
    cairo_mesh_pattern_end_patch (fill_pattern);
1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243
  }

  double xMin, yMin, xMax, yMax;
  // get the clip region bbox
  state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
  state->moveTo(xMin, yMin);
  state->lineTo(xMin, yMax);
  state->lineTo(xMax, yMax);
  state->lineTo(xMax, yMin);
  state->closePath();
  fill(state);
  state->clearPath();

  return gTrue;
}

GBool CairoOutputDev::patchMeshShadedFill(GfxState *state, GfxPatchMeshShading *shading)
{
  int i, j, k;

  cairo_pattern_destroy(fill_pattern);
  fill_pattern = cairo_pattern_create_mesh ();

  for (i = 0; i < shading->getNPatches(); i++) {
    GfxPatch *patch = shading->getPatch(i);
    GfxColor color;
    GfxRGB rgb;

1244
    cairo_mesh_pattern_begin_patch (fill_pattern);
1245

1246 1247
    cairo_mesh_pattern_move_to (fill_pattern, patch->x[0][0], patch->y[0][0]);
    cairo_mesh_pattern_curve_to (fill_pattern,
1248 1249 1250 1251
			    patch->x[0][1], patch->y[0][1],
			    patch->x[0][2], patch->y[0][2],
			    patch->x[0][3], patch->y[0][3]);

1252
    cairo_mesh_pattern_curve_to (fill_pattern,
1253 1254 1255 1256
			    patch->x[1][3], patch->y[1][3],
			    patch->x[2][3], patch->y[2][3],
			    patch->x[3][3], patch->y[3][3]);

1257
    cairo_mesh_pattern_curve_to (fill_pattern,
1258 1259 1260 1261
			    patch->x[3][2], patch->y[3][2],
			    patch->x[3][1], patch->y[3][1],
			    patch->x[3][0], patch->y[3][0]);

1262
    cairo_mesh_pattern_curve_to (fill_pattern,
1263 1264 1265 1266
			    patch->x[2][0], patch->y[2][0],
			    patch->x[1][0], patch->y[1][0],
			    patch->x[0][0], patch->y[0][0]);

1267 1268 1269 1270
    cairo_mesh_pattern_set_control_point (fill_pattern, 0, patch->x[1][1], patch->y[1][1]);
    cairo_mesh_pattern_set_control_point (fill_pattern, 1, patch->x[1][2], patch->y[1][2]);
    cairo_mesh_pattern_set_control_point (fill_pattern,