| 1 | #include <mupdf/fitz.h> |
| 2 | |
| 3 | typedef struct fz_test_device_s |
| 4 | { |
| 5 | fz_device super; |
| 6 | int *is_color; |
| 7 | float threshold; |
| 8 | int options; |
| 9 | fz_device *passthrough; |
| 10 | int resolved; |
| 11 | } fz_test_device; |
| 12 | |
| 13 | static int |
| 14 | is_rgb_color(float threshold, float r, float g, float b) |
| 15 | { |
| 16 | float rg_diff = fz_abs(r - g); |
| 17 | float rb_diff = fz_abs(r - b); |
| 18 | float gb_diff = fz_abs(g - b); |
| 19 | return rg_diff > threshold || rb_diff > threshold || gb_diff > threshold; |
| 20 | } |
| 21 | |
| 22 | static int |
| 23 | is_rgb_color_u8(int threshold_u8, int r, int g, int b) |
| 24 | { |
| 25 | int rg_diff = fz_absi(r - g); |
| 26 | int rb_diff = fz_absi(r - b); |
| 27 | int gb_diff = fz_absi(g - b); |
| 28 | return rg_diff > threshold_u8 || rb_diff > threshold_u8 || gb_diff > threshold_u8; |
| 29 | } |
| 30 | |
| 31 | static void |
| 32 | fz_test_color(fz_context *ctx, fz_test_device *t, fz_colorspace *colorspace, const float *color, fz_color_params color_params) |
| 33 | { |
| 34 | if (!*t->is_color && colorspace && fz_colorspace_type(ctx, colorspace) != FZ_COLORSPACE_GRAY) |
| 35 | { |
| 36 | if (colorspace == fz_device_rgb(ctx)) |
| 37 | { |
| 38 | if (is_rgb_color(t->threshold, color[0], color[1], color[2])) |
| 39 | { |
| 40 | *t->is_color = 2; |
| 41 | t->resolved = 1; |
| 42 | if (t->passthrough == NULL) |
| 43 | fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation" ); |
| 44 | } |
| 45 | } |
| 46 | else |
| 47 | { |
| 48 | float rgb[3]; |
| 49 | fz_convert_color(ctx, colorspace, color, fz_device_rgb(ctx), rgb, NULL, color_params); |
| 50 | if (is_rgb_color(t->threshold, rgb[0], rgb[1], rgb[2])) |
| 51 | { |
| 52 | *t->is_color = 2; |
| 53 | t->resolved = 1; |
| 54 | if (t->passthrough == NULL) |
| 55 | { |
| 56 | fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation" ); |
| 57 | } |
| 58 | } |
| 59 | } |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | static void |
| 64 | fz_test_fill_path(fz_context *ctx, fz_device *dev_, const fz_path *path, int even_odd, fz_matrix ctm, |
| 65 | fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params) |
| 66 | { |
| 67 | fz_test_device *dev = (fz_test_device*)dev_; |
| 68 | |
| 69 | if (dev->resolved == 0 && alpha != 0.0f) |
| 70 | fz_test_color(ctx, dev, colorspace, color, color_params); |
| 71 | if (dev->passthrough) |
| 72 | fz_fill_path(ctx, dev->passthrough, path, even_odd, ctm, colorspace, color, alpha, color_params); |
| 73 | } |
| 74 | |
| 75 | static void |
| 76 | fz_test_stroke_path(fz_context *ctx, fz_device *dev_, const fz_path *path, const fz_stroke_state *stroke, |
| 77 | fz_matrix ctm, fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params) |
| 78 | { |
| 79 | fz_test_device *dev = (fz_test_device*)dev_; |
| 80 | |
| 81 | if (dev->resolved == 0 && alpha != 0.0f) |
| 82 | fz_test_color(ctx, dev, colorspace, color, color_params); |
| 83 | if (dev->passthrough) |
| 84 | fz_stroke_path(ctx, dev->passthrough, path, stroke, ctm, colorspace, color, alpha, color_params); |
| 85 | } |
| 86 | |
| 87 | static void |
| 88 | fz_test_fill_text(fz_context *ctx, fz_device *dev_, const fz_text *text, fz_matrix ctm, |
| 89 | fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params) |
| 90 | { |
| 91 | fz_test_device *dev = (fz_test_device*)dev_; |
| 92 | |
| 93 | if (dev->resolved == 0 && alpha != 0.0f) |
| 94 | fz_test_color(ctx, dev, colorspace, color, color_params); |
| 95 | if (dev->passthrough) |
| 96 | fz_fill_text(ctx, dev->passthrough, text, ctm, colorspace, color, alpha, color_params); |
| 97 | } |
| 98 | |
| 99 | static void |
| 100 | fz_test_stroke_text(fz_context *ctx, fz_device *dev_, const fz_text *text, const fz_stroke_state *stroke, |
| 101 | fz_matrix ctm, fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params) |
| 102 | { |
| 103 | fz_test_device *dev = (fz_test_device*)dev_; |
| 104 | |
| 105 | if (dev->resolved == 0 && alpha != 0.0f) |
| 106 | fz_test_color(ctx, dev, colorspace, color, color_params); |
| 107 | if (dev->passthrough) |
| 108 | fz_stroke_text(ctx, dev->passthrough, text, stroke, ctm, colorspace, color, alpha, color_params); |
| 109 | } |
| 110 | |
| 111 | struct shadearg |
| 112 | { |
| 113 | fz_test_device *dev; |
| 114 | fz_shade *shade; |
| 115 | fz_color_params color_params; |
| 116 | }; |
| 117 | |
| 118 | static void |
| 119 | prepare_vertex(fz_context *ctx, void *arg_, fz_vertex *v, const float *color) |
| 120 | { |
| 121 | struct shadearg *arg = arg_; |
| 122 | fz_test_device *dev = arg->dev; |
| 123 | fz_shade *shade = arg->shade; |
| 124 | if (!shade->use_function) |
| 125 | fz_test_color(ctx, dev, shade->colorspace, color, arg->color_params); |
| 126 | } |
| 127 | |
| 128 | static void |
| 129 | fz_test_fill_shade(fz_context *ctx, fz_device *dev_, fz_shade *shade, fz_matrix ctm, float alpha, fz_color_params color_params) |
| 130 | { |
| 131 | fz_test_device *dev = (fz_test_device*)dev_; |
| 132 | |
| 133 | if (dev->resolved == 0) |
| 134 | { |
| 135 | if ((dev->options & FZ_TEST_OPT_SHADINGS) == 0) |
| 136 | { |
| 137 | if (fz_colorspace_type(ctx, shade->colorspace) != FZ_COLORSPACE_GRAY) |
| 138 | { |
| 139 | /* Don't test every pixel. Upgrade us from "black and white" to "probably color" */ |
| 140 | if (*dev->is_color == 0) |
| 141 | *dev->is_color = 1; |
| 142 | dev->resolved = 1; |
| 143 | if (dev->passthrough == NULL) |
| 144 | fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation" ); |
| 145 | } |
| 146 | } |
| 147 | else |
| 148 | { |
| 149 | if (shade->use_function) |
| 150 | { |
| 151 | int i; |
| 152 | for (i = 0; i < 256; i++) |
| 153 | fz_test_color(ctx, dev, shade->colorspace, shade->function[i], color_params); |
| 154 | } |
| 155 | else |
| 156 | { |
| 157 | struct shadearg arg; |
| 158 | arg.dev = dev; |
| 159 | arg.shade = shade; |
| 160 | arg.color_params = color_params; |
| 161 | fz_process_shade(ctx, shade, ctm, fz_device_current_scissor(ctx, dev_), prepare_vertex, NULL, &arg); |
| 162 | } |
| 163 | } |
| 164 | } |
| 165 | if (dev->passthrough) |
| 166 | fz_fill_shade(ctx, dev->passthrough, shade, ctm, alpha, color_params); |
| 167 | } |
| 168 | |
| 169 | static void fz_test_fill_compressed_8bpc_image(fz_context *ctx, fz_test_device *dev, fz_image *image, fz_stream *stream, fz_color_params color_params) |
| 170 | { |
| 171 | unsigned int count = (unsigned int)image->w * (unsigned int)image->h; |
| 172 | unsigned int i; |
| 173 | |
| 174 | if (image->colorspace == fz_device_rgb(ctx)) |
| 175 | { |
| 176 | int threshold_u8 = dev->threshold * 255; |
| 177 | for (i = 0; i < count; i++) |
| 178 | { |
| 179 | int r = fz_read_byte(ctx, stream); |
| 180 | int g = fz_read_byte(ctx, stream); |
| 181 | int b = fz_read_byte(ctx, stream); |
| 182 | if (is_rgb_color_u8(threshold_u8, r, g, b)) |
| 183 | { |
| 184 | *dev->is_color = 1; |
| 185 | dev->resolved = 1; |
| 186 | if (dev->passthrough == NULL) |
| 187 | fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation" ); |
| 188 | break; |
| 189 | } |
| 190 | } |
| 191 | } |
| 192 | else |
| 193 | { |
| 194 | fz_color_converter cc; |
| 195 | unsigned int n = (unsigned int)image->n; |
| 196 | |
| 197 | fz_init_cached_color_converter(ctx, &cc, image->colorspace, fz_device_rgb(ctx), NULL, color_params); |
| 198 | |
| 199 | fz_try(ctx) |
| 200 | { |
| 201 | for (i = 0; i < count; i++) |
| 202 | { |
| 203 | float cs[FZ_MAX_COLORS]; |
| 204 | float ds[FZ_MAX_COLORS]; |
| 205 | unsigned int k; |
| 206 | |
| 207 | for (k = 0; k < n; k++) |
| 208 | cs[k] = fz_read_byte(ctx, stream) / 255.0f; |
| 209 | |
| 210 | cc.convert(ctx, &cc, ds, cs); |
| 211 | |
| 212 | if (is_rgb_color(dev->threshold, ds[0], ds[1], ds[2])) |
| 213 | { |
| 214 | *dev->is_color = 1; |
| 215 | dev->resolved = 1; |
| 216 | if (dev->passthrough == NULL) |
| 217 | fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation" ); |
| 218 | break; |
| 219 | } |
| 220 | } |
| 221 | } |
| 222 | fz_always(ctx) |
| 223 | fz_fin_cached_color_converter(ctx, &cc); |
| 224 | fz_catch(ctx) |
| 225 | fz_rethrow(ctx); |
| 226 | } |
| 227 | } |
| 228 | |
| 229 | static void |
| 230 | fz_test_fill_other_image(fz_context *ctx, fz_test_device *dev, fz_pixmap *pix, fz_color_params color_params) |
| 231 | { |
| 232 | unsigned int count, i, k, h, sa, ss; |
| 233 | unsigned char *s; |
| 234 | |
| 235 | count = pix->w; |
| 236 | h = pix->h; |
| 237 | s = pix->samples; |
| 238 | sa = pix->alpha; |
| 239 | ss = pix->stride - pix->w * pix->n; |
| 240 | |
| 241 | if (pix->colorspace == fz_device_rgb(ctx)) |
| 242 | { |
| 243 | int threshold_u8 = dev->threshold * 255; |
| 244 | while (h--) |
| 245 | { |
| 246 | for (i = 0; i < count; i++) |
| 247 | { |
| 248 | if ((!sa || s[3] != 0) && is_rgb_color_u8(threshold_u8, s[0], s[1], s[2])) |
| 249 | { |
| 250 | *dev->is_color = 1; |
| 251 | dev->resolved = 1; |
| 252 | if (dev->passthrough == NULL) |
| 253 | fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation" ); |
| 254 | break; |
| 255 | } |
| 256 | s += 3 + sa; |
| 257 | } |
| 258 | s += ss; |
| 259 | } |
| 260 | } |
| 261 | else |
| 262 | { |
| 263 | fz_color_converter cc; |
| 264 | unsigned int n = (unsigned int)pix->n-1; |
| 265 | |
| 266 | fz_init_cached_color_converter(ctx, &cc, pix->colorspace, fz_device_rgb(ctx), NULL, color_params); |
| 267 | |
| 268 | fz_try(ctx) |
| 269 | { |
| 270 | while (h--) |
| 271 | { |
| 272 | for (i = 0; i < count; i++) |
| 273 | { |
| 274 | float cs[FZ_MAX_COLORS]; |
| 275 | float ds[FZ_MAX_COLORS]; |
| 276 | |
| 277 | for (k = 0; k < n; k++) |
| 278 | cs[k] = (*s++) / 255.0f; |
| 279 | if (sa && *s++ == 0) |
| 280 | continue; |
| 281 | |
| 282 | cc.convert(ctx, &cc, ds, cs); |
| 283 | |
| 284 | if (is_rgb_color(dev->threshold, ds[0], ds[1], ds[2])) |
| 285 | { |
| 286 | *dev->is_color = 1; |
| 287 | dev->resolved = 1; |
| 288 | if (dev->passthrough == NULL) |
| 289 | fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation" ); |
| 290 | break; |
| 291 | } |
| 292 | } |
| 293 | s += ss; |
| 294 | } |
| 295 | } |
| 296 | fz_always(ctx) |
| 297 | fz_fin_cached_color_converter(ctx, &cc); |
| 298 | fz_catch(ctx) |
| 299 | fz_rethrow(ctx); |
| 300 | } |
| 301 | } |
| 302 | |
| 303 | |
| 304 | static void |
| 305 | fz_test_fill_image(fz_context *ctx, fz_device *dev_, fz_image *image, fz_matrix ctm, float alpha, fz_color_params color_params) |
| 306 | { |
| 307 | fz_test_device *dev = (fz_test_device*)dev_; |
| 308 | |
| 309 | while (dev->resolved == 0) /* So we can break out */ |
| 310 | { |
| 311 | fz_compressed_buffer *buffer; |
| 312 | |
| 313 | if (*dev->is_color || !image->colorspace || fz_colorspace_is_gray(ctx, image->colorspace)) |
| 314 | break; |
| 315 | |
| 316 | if ((dev->options & FZ_TEST_OPT_IMAGES) == 0) |
| 317 | { |
| 318 | /* Don't test every pixel. Upgrade us from "black and white" to "probably color" */ |
| 319 | if (*dev->is_color == 0) |
| 320 | *dev->is_color = 1; |
| 321 | dev->resolved = 1; |
| 322 | if (dev->passthrough == NULL) |
| 323 | fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation" ); |
| 324 | break; |
| 325 | } |
| 326 | |
| 327 | buffer = fz_compressed_image_buffer(ctx, image); |
| 328 | if (buffer && image->bpc == 8) |
| 329 | { |
| 330 | fz_stream *stream = fz_open_compressed_buffer(ctx, buffer); |
| 331 | fz_try(ctx) |
| 332 | fz_test_fill_compressed_8bpc_image(ctx, dev, image, stream, color_params); |
| 333 | fz_always(ctx) |
| 334 | fz_drop_stream(ctx, stream); |
| 335 | fz_catch(ctx) |
| 336 | fz_rethrow(ctx); |
| 337 | } |
| 338 | else |
| 339 | { |
| 340 | fz_pixmap *pix = fz_get_pixmap_from_image(ctx, image, NULL, NULL, 0, 0); |
| 341 | if (pix == NULL) /* Should never happen really, but... */ |
| 342 | break; |
| 343 | |
| 344 | fz_try(ctx) |
| 345 | fz_test_fill_other_image(ctx, dev, pix, color_params); |
| 346 | fz_always(ctx) |
| 347 | fz_drop_pixmap(ctx, pix); |
| 348 | fz_catch(ctx) |
| 349 | fz_rethrow(ctx); |
| 350 | } |
| 351 | break; |
| 352 | } |
| 353 | if (dev->passthrough) |
| 354 | fz_fill_image(ctx, dev->passthrough, image, ctm, alpha, color_params); |
| 355 | } |
| 356 | |
| 357 | static void |
| 358 | fz_test_fill_image_mask(fz_context *ctx, fz_device *dev_, fz_image *image, fz_matrix ctm, |
| 359 | fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params) |
| 360 | { |
| 361 | fz_test_device *dev = (fz_test_device*)dev_; |
| 362 | |
| 363 | if (dev->resolved == 0) |
| 364 | { |
| 365 | /* We assume that at least some of the image pixels are non-zero */ |
| 366 | fz_test_color(ctx, dev, colorspace, color, color_params); |
| 367 | } |
| 368 | if (dev->passthrough) |
| 369 | fz_fill_image_mask(ctx, dev->passthrough, image, ctm, colorspace, color, alpha, color_params); |
| 370 | } |
| 371 | |
| 372 | static void |
| 373 | fz_test_clip_path(fz_context *ctx, fz_device *dev_, const fz_path *path, int even_odd, fz_matrix ctm, fz_rect scissor) |
| 374 | { |
| 375 | fz_test_device *dev = (fz_test_device*)dev_; |
| 376 | |
| 377 | if (dev->passthrough) |
| 378 | fz_clip_path(ctx, dev->passthrough, path, even_odd, ctm, scissor); |
| 379 | } |
| 380 | |
| 381 | static void |
| 382 | fz_test_clip_stroke_path(fz_context *ctx, fz_device *dev_, const fz_path *path, const fz_stroke_state *stroke, fz_matrix ctm, fz_rect scissor) |
| 383 | { |
| 384 | fz_test_device *dev = (fz_test_device*)dev_; |
| 385 | |
| 386 | if (dev->passthrough) |
| 387 | fz_clip_stroke_path(ctx, dev->passthrough, path, stroke, ctm, scissor); |
| 388 | } |
| 389 | |
| 390 | static void |
| 391 | fz_test_clip_text(fz_context *ctx, fz_device *dev_, const fz_text *text, fz_matrix ctm, fz_rect scissor) |
| 392 | { |
| 393 | fz_test_device *dev = (fz_test_device*)dev_; |
| 394 | |
| 395 | if (dev->passthrough) |
| 396 | fz_clip_text(ctx, dev->passthrough, text, ctm, scissor); |
| 397 | } |
| 398 | |
| 399 | static void |
| 400 | fz_test_clip_stroke_text(fz_context *ctx, fz_device *dev_, const fz_text *text, const fz_stroke_state *stroke, fz_matrix ctm, fz_rect scissor) |
| 401 | { |
| 402 | fz_test_device *dev = (fz_test_device*)dev_; |
| 403 | |
| 404 | if (dev->passthrough) |
| 405 | fz_clip_stroke_text(ctx, dev->passthrough, text, stroke, ctm, scissor); |
| 406 | } |
| 407 | |
| 408 | static void |
| 409 | fz_test_ignore_text(fz_context *ctx, fz_device *dev_, const fz_text *text, fz_matrix ctm) |
| 410 | { |
| 411 | fz_test_device *dev = (fz_test_device*)dev_; |
| 412 | |
| 413 | if (dev->passthrough) |
| 414 | fz_ignore_text(ctx, dev->passthrough, text, ctm); |
| 415 | } |
| 416 | |
| 417 | static void |
| 418 | fz_test_clip_image_mask(fz_context *ctx, fz_device *dev_, fz_image *img, fz_matrix ctm, fz_rect scissor) |
| 419 | { |
| 420 | fz_test_device *dev = (fz_test_device*)dev_; |
| 421 | |
| 422 | if (dev->passthrough) |
| 423 | fz_clip_image_mask(ctx, dev->passthrough, img, ctm, scissor); |
| 424 | } |
| 425 | |
| 426 | static void |
| 427 | fz_test_pop_clip(fz_context *ctx, fz_device *dev_) |
| 428 | { |
| 429 | fz_test_device *dev = (fz_test_device*)dev_; |
| 430 | |
| 431 | if (dev->passthrough) |
| 432 | fz_pop_clip(ctx, dev->passthrough); |
| 433 | } |
| 434 | |
| 435 | static void |
| 436 | fz_test_begin_mask(fz_context *ctx, fz_device *dev_, fz_rect rect, int luminosity, fz_colorspace *cs, const float *bc, fz_color_params color_params) |
| 437 | { |
| 438 | fz_test_device *dev = (fz_test_device*)dev_; |
| 439 | |
| 440 | if (dev->passthrough) |
| 441 | fz_begin_mask(ctx, dev->passthrough, rect, luminosity, cs, bc, color_params); |
| 442 | } |
| 443 | |
| 444 | static void |
| 445 | fz_test_end_mask(fz_context *ctx, fz_device *dev_) |
| 446 | { |
| 447 | fz_test_device *dev = (fz_test_device*)dev_; |
| 448 | |
| 449 | if (dev->passthrough) |
| 450 | fz_end_mask(ctx, dev->passthrough); |
| 451 | } |
| 452 | |
| 453 | static void |
| 454 | fz_test_begin_group(fz_context *ctx, fz_device *dev_, fz_rect rect, fz_colorspace *cs, int isolated, int knockout, int blendmode, float alpha) |
| 455 | { |
| 456 | fz_test_device *dev = (fz_test_device*)dev_; |
| 457 | |
| 458 | if (dev->passthrough) |
| 459 | fz_begin_group(ctx, dev->passthrough, rect, cs, isolated, knockout, blendmode, alpha); |
| 460 | } |
| 461 | |
| 462 | static void |
| 463 | fz_test_end_group(fz_context *ctx, fz_device *dev_) |
| 464 | { |
| 465 | fz_test_device *dev = (fz_test_device*)dev_; |
| 466 | |
| 467 | if (dev->passthrough) |
| 468 | fz_end_group(ctx, dev->passthrough); |
| 469 | } |
| 470 | |
| 471 | static int |
| 472 | fz_test_begin_tile(fz_context *ctx, fz_device *dev_, fz_rect area, fz_rect view, float xstep, float ystep, fz_matrix ctm, int id) |
| 473 | { |
| 474 | fz_test_device *dev = (fz_test_device*)dev_; |
| 475 | |
| 476 | if (dev->passthrough) |
| 477 | return fz_begin_tile_id(ctx, dev->passthrough, area, view, xstep, ystep, ctm, id); |
| 478 | else |
| 479 | return 0; |
| 480 | } |
| 481 | |
| 482 | static void |
| 483 | fz_test_end_tile(fz_context *ctx, fz_device *dev_) |
| 484 | { |
| 485 | fz_test_device *dev = (fz_test_device*)dev_; |
| 486 | |
| 487 | if (dev->passthrough) |
| 488 | fz_end_tile(ctx, dev->passthrough); |
| 489 | } |
| 490 | |
| 491 | /* |
| 492 | Create a device to test for features. |
| 493 | |
| 494 | Currently only tests for the presence of non-grayscale colors. |
| 495 | |
| 496 | is_color: Possible values returned: |
| 497 | 0: Definitely greyscale |
| 498 | 1: Probably color (all colors were grey, but there |
| 499 | were images or shadings in a non grey colorspace). |
| 500 | 2: Definitely color |
| 501 | |
| 502 | threshold: The difference from grayscale that will be tolerated. |
| 503 | Typical values to use are either 0 (be exact) and 0.02 (allow an |
| 504 | imperceptible amount of slop). |
| 505 | |
| 506 | options: A set of bitfield options, from the FZ_TEST_OPT set. |
| 507 | |
| 508 | passthrough: A device to pass all calls through to, or NULL. |
| 509 | If set, then the test device can both test and pass through to |
| 510 | an underlying device (like, say, the display list device). This |
| 511 | means that a display list can be created and at the end we'll |
| 512 | know if it's colored or not. |
| 513 | |
| 514 | In the absence of a passthrough device, the device will throw |
| 515 | an exception to stop page interpretation when color is found. |
| 516 | */ |
| 517 | fz_device * |
| 518 | fz_new_test_device(fz_context *ctx, int *is_color, float threshold, int options, fz_device *passthrough) |
| 519 | { |
| 520 | fz_test_device *dev = fz_new_derived_device(ctx, fz_test_device); |
| 521 | |
| 522 | dev->super.fill_path = fz_test_fill_path; |
| 523 | dev->super.stroke_path = fz_test_stroke_path; |
| 524 | dev->super.fill_text = fz_test_fill_text; |
| 525 | dev->super.stroke_text = fz_test_stroke_text; |
| 526 | dev->super.fill_shade = fz_test_fill_shade; |
| 527 | dev->super.fill_image = fz_test_fill_image; |
| 528 | dev->super.fill_image_mask = fz_test_fill_image_mask; |
| 529 | |
| 530 | if (passthrough) |
| 531 | { |
| 532 | dev->super.clip_path = fz_test_clip_path; |
| 533 | dev->super.clip_stroke_path = fz_test_clip_stroke_path; |
| 534 | dev->super.clip_text = fz_test_clip_text; |
| 535 | dev->super.clip_stroke_text = fz_test_clip_stroke_text; |
| 536 | dev->super.ignore_text = fz_test_ignore_text; |
| 537 | dev->super.clip_image_mask = fz_test_clip_image_mask; |
| 538 | dev->super.pop_clip = fz_test_pop_clip; |
| 539 | dev->super.begin_mask = fz_test_begin_mask; |
| 540 | dev->super.end_mask = fz_test_end_mask; |
| 541 | dev->super.begin_group = fz_test_begin_group; |
| 542 | dev->super.end_group = fz_test_end_group; |
| 543 | dev->super.begin_tile = fz_test_begin_tile; |
| 544 | dev->super.end_tile = fz_test_end_tile; |
| 545 | } |
| 546 | |
| 547 | dev->is_color = is_color; |
| 548 | dev->options = options; |
| 549 | dev->threshold = threshold; |
| 550 | dev->passthrough = passthrough; |
| 551 | dev->resolved = 0; |
| 552 | |
| 553 | *dev->is_color = 0; |
| 554 | |
| 555 | return (fz_device*)dev; |
| 556 | } |
| 557 | |