Rev 1897 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1897 | serge | 1 | /* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ |
2 | /* cairo - a vector graphics library with display and print output |
||
3 | * |
||
4 | * Copyright © 2004 Red Hat, Inc |
||
5 | * Copyright © 2006 Red Hat, Inc |
||
6 | * Copyright © 2007, 2008 Adrian Johnson |
||
7 | * |
||
8 | * This library is free software; you can redistribute it and/or |
||
9 | * modify it either under the terms of the GNU Lesser General Public |
||
10 | * License version 2.1 as published by the Free Software Foundation |
||
11 | * (the "LGPL") or, at your option, under the terms of the Mozilla |
||
12 | * Public License Version 1.1 (the "MPL"). If you do not alter this |
||
13 | * notice, a recipient may use your version of this file under either |
||
14 | * the MPL or the LGPL. |
||
15 | * |
||
16 | * You should have received a copy of the LGPL along with this library |
||
17 | * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
||
18 | * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA |
||
19 | * You should have received a copy of the MPL along with this library |
||
20 | * in the file COPYING-MPL-1.1 |
||
21 | * |
||
22 | * The contents of this file are subject to the Mozilla Public License |
||
23 | * Version 1.1 (the "License"); you may not use this file except in |
||
24 | * compliance with the License. You may obtain a copy of the License at |
||
25 | * http://www.mozilla.org/MPL/ |
||
26 | * |
||
27 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
||
28 | * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
||
29 | * the specific language governing rights and limitations. |
||
30 | * |
||
31 | * The Original Code is the cairo graphics library. |
||
32 | * |
||
33 | * The Initial Developer of the Original Code is University of Southern |
||
34 | * California. |
||
35 | * |
||
36 | * Contributor(s): |
||
37 | * Kristian Høgsberg |
||
38 | * Carl Worth |
||
39 | * Adrian Johnson |
||
40 | */ |
||
41 | |||
42 | #include "cairoint.h" |
||
43 | |||
44 | #if CAIRO_HAS_PDF_OPERATORS |
||
45 | |||
46 | #include "cairo-error-private.h" |
||
47 | #include "cairo-pdf-operators-private.h" |
||
48 | #include "cairo-path-fixed-private.h" |
||
49 | #include "cairo-output-stream-private.h" |
||
50 | #include "cairo-scaled-font-subsets-private.h" |
||
51 | |||
52 | static cairo_status_t |
||
53 | _cairo_pdf_operators_end_text (cairo_pdf_operators_t *pdf_operators); |
||
54 | |||
55 | |||
56 | void |
||
57 | _cairo_pdf_operators_init (cairo_pdf_operators_t *pdf_operators, |
||
58 | cairo_output_stream_t *stream, |
||
59 | cairo_matrix_t *cairo_to_pdf, |
||
60 | cairo_scaled_font_subsets_t *font_subsets) |
||
61 | { |
||
62 | pdf_operators->stream = stream; |
||
63 | pdf_operators->cairo_to_pdf = *cairo_to_pdf; |
||
64 | pdf_operators->font_subsets = font_subsets; |
||
65 | pdf_operators->use_font_subset = NULL; |
||
66 | pdf_operators->use_font_subset_closure = NULL; |
||
67 | pdf_operators->in_text_object = FALSE; |
||
68 | pdf_operators->num_glyphs = 0; |
||
69 | pdf_operators->has_line_style = FALSE; |
||
70 | pdf_operators->use_actual_text = FALSE; |
||
71 | } |
||
72 | |||
73 | cairo_status_t |
||
74 | _cairo_pdf_operators_fini (cairo_pdf_operators_t *pdf_operators) |
||
75 | { |
||
76 | return _cairo_pdf_operators_flush (pdf_operators); |
||
77 | } |
||
78 | |||
79 | void |
||
80 | _cairo_pdf_operators_set_font_subsets_callback (cairo_pdf_operators_t *pdf_operators, |
||
81 | cairo_pdf_operators_use_font_subset_t use_font_subset, |
||
82 | void *closure) |
||
83 | { |
||
84 | pdf_operators->use_font_subset = use_font_subset; |
||
85 | pdf_operators->use_font_subset_closure = closure; |
||
86 | } |
||
87 | |||
88 | /* Change the output stream to a different stream. |
||
89 | * _cairo_pdf_operators_flush() should always be called before calling |
||
90 | * this function. |
||
91 | */ |
||
92 | void |
||
93 | _cairo_pdf_operators_set_stream (cairo_pdf_operators_t *pdf_operators, |
||
94 | cairo_output_stream_t *stream) |
||
95 | { |
||
96 | pdf_operators->stream = stream; |
||
97 | pdf_operators->has_line_style = FALSE; |
||
98 | } |
||
99 | |||
100 | void |
||
101 | _cairo_pdf_operators_set_cairo_to_pdf_matrix (cairo_pdf_operators_t *pdf_operators, |
||
102 | cairo_matrix_t *cairo_to_pdf) |
||
103 | { |
||
104 | pdf_operators->cairo_to_pdf = *cairo_to_pdf; |
||
105 | pdf_operators->has_line_style = FALSE; |
||
106 | } |
||
107 | |||
108 | cairo_private void |
||
109 | _cairo_pdf_operators_enable_actual_text (cairo_pdf_operators_t *pdf_operators, |
||
110 | cairo_bool_t enable) |
||
111 | { |
||
112 | pdf_operators->use_actual_text = enable; |
||
113 | } |
||
114 | |||
115 | /* Finish writing out any pending commands to the stream. This |
||
116 | * function must be called by the surface before emitting anything |
||
117 | * into the PDF stream. |
||
118 | * |
||
119 | * pdf_operators may leave the emitted PDF for some operations |
||
120 | * unfinished in case subsequent operations can be merged. This |
||
121 | * function will finish off any incomplete operation so the stream |
||
122 | * will be in a state where the surface may emit its own PDF |
||
123 | * operations (eg changing patterns). |
||
124 | * |
||
125 | */ |
||
126 | cairo_status_t |
||
127 | _cairo_pdf_operators_flush (cairo_pdf_operators_t *pdf_operators) |
||
128 | { |
||
129 | cairo_status_t status = CAIRO_STATUS_SUCCESS; |
||
130 | |||
131 | if (pdf_operators->in_text_object) |
||
132 | status = _cairo_pdf_operators_end_text (pdf_operators); |
||
133 | |||
134 | return status; |
||
135 | } |
||
136 | |||
137 | /* Reset the known graphics state of the PDF consumer. ie no |
||
138 | * assumptions will be made about the state. The next time a |
||
139 | * particular graphics state is required (eg line width) the state |
||
140 | * operator is always emitted and then remembered for subsequent |
||
141 | * operatations. |
||
142 | * |
||
143 | * This should be called when starting a new stream or after emitting |
||
144 | * the 'Q' operator (where pdf-operators functions were called inside |
||
145 | * the q/Q pair). |
||
146 | */ |
||
147 | void |
||
148 | _cairo_pdf_operators_reset (cairo_pdf_operators_t *pdf_operators) |
||
149 | { |
||
150 | pdf_operators->has_line_style = FALSE; |
||
151 | } |
||
152 | |||
153 | /* A word wrap stream can be used as a filter to do word wrapping on |
||
154 | * top of an existing output stream. The word wrapping is quite |
||
155 | * simple, using isspace to determine characters that separate |
||
156 | * words. Any word that will cause the column count exceed the given |
||
157 | * max_column will have a '\n' character emitted before it. |
||
158 | * |
||
159 | * The stream is careful to maintain integrity for words that cross |
||
160 | * the boundary from one call to write to the next. |
||
161 | * |
||
162 | * Note: This stream does not guarantee that the output will never |
||
163 | * exceed max_column. In particular, if a single word is larger than |
||
164 | * max_column it will not be broken up. |
||
165 | */ |
||
3959 | Serge | 166 | |
167 | typedef enum _cairo_word_wrap_state { |
||
168 | WRAP_STATE_DELIMITER, |
||
169 | WRAP_STATE_WORD, |
||
170 | WRAP_STATE_STRING, |
||
171 | WRAP_STATE_HEXSTRING |
||
172 | } cairo_word_wrap_state_t; |
||
173 | |||
174 | |||
1897 | serge | 175 | typedef struct _word_wrap_stream { |
176 | cairo_output_stream_t base; |
||
177 | cairo_output_stream_t *output; |
||
178 | int max_column; |
||
179 | int column; |
||
3959 | Serge | 180 | cairo_word_wrap_state_t state; |
181 | cairo_bool_t in_escape; |
||
182 | int escape_digits; |
||
1897 | serge | 183 | } word_wrap_stream_t; |
184 | |||
3959 | Serge | 185 | |
186 | |||
187 | /* Emit word bytes up to the next delimiter character */ |
||
1897 | serge | 188 | static int |
3959 | Serge | 189 | _word_wrap_stream_count_word_up_to (word_wrap_stream_t *stream, |
190 | const unsigned char *data, int length) |
||
1897 | serge | 191 | { |
3959 | Serge | 192 | const unsigned char *s = data; |
193 | int count = 0; |
||
1897 | serge | 194 | |
195 | while (length--) { |
||
3959 | Serge | 196 | if (_cairo_isspace (*s) || *s == '<' || *s == '(') { |
197 | stream->state = WRAP_STATE_DELIMITER; |
||
198 | break; |
||
1897 | serge | 199 | } |
3959 | Serge | 200 | |
201 | count++; |
||
202 | stream->column++; |
||
203 | s++; |
||
1897 | serge | 204 | } |
205 | |||
3959 | Serge | 206 | if (count) |
207 | _cairo_output_stream_write (stream->output, data, count); |
||
208 | |||
209 | return count; |
||
1897 | serge | 210 | } |
211 | |||
212 | |||
3959 | Serge | 213 | /* Emit hexstring bytes up to either the end of the ASCII hexstring or the number |
1897 | serge | 214 | * of columns remaining. |
215 | */ |
||
216 | static int |
||
3959 | Serge | 217 | _word_wrap_stream_count_hexstring_up_to (word_wrap_stream_t *stream, |
218 | const unsigned char *data, int length) |
||
1897 | serge | 219 | { |
3959 | Serge | 220 | const unsigned char *s = data; |
221 | int count = 0; |
||
222 | cairo_bool_t newline = FALSE; |
||
1897 | serge | 223 | |
224 | while (length--) { |
||
3959 | Serge | 225 | count++; |
226 | stream->column++; |
||
227 | if (*s == '>') { |
||
228 | stream->state = WRAP_STATE_DELIMITER; |
||
229 | break; |
||
230 | } |
||
1897 | serge | 231 | |
3959 | Serge | 232 | if (stream->column > stream->max_column) { |
233 | newline = TRUE; |
||
234 | break; |
||
235 | } |
||
236 | s++; |
||
1897 | serge | 237 | } |
238 | |||
3959 | Serge | 239 | if (count) |
240 | _cairo_output_stream_write (stream->output, data, count); |
||
241 | |||
242 | if (newline) { |
||
243 | _cairo_output_stream_printf (stream->output, "\n"); |
||
244 | stream->column = 0; |
||
245 | } |
||
246 | |||
247 | return count; |
||
1897 | serge | 248 | } |
249 | |||
3959 | Serge | 250 | /* Count up to either the end of the string or the number of columns |
251 | * remaining. |
||
252 | */ |
||
253 | static int |
||
254 | _word_wrap_stream_count_string_up_to (word_wrap_stream_t *stream, |
||
255 | const unsigned char *data, int length) |
||
256 | { |
||
257 | const unsigned char *s = data; |
||
258 | int count = 0; |
||
259 | cairo_bool_t newline = FALSE; |
||
260 | |||
261 | while (length--) { |
||
262 | count++; |
||
263 | stream->column++; |
||
264 | if (!stream->in_escape) { |
||
265 | if (*s == ')') { |
||
266 | stream->state = WRAP_STATE_DELIMITER; |
||
267 | break; |
||
268 | } |
||
269 | if (*s == '\\') { |
||
270 | stream->in_escape = TRUE; |
||
271 | stream->escape_digits = 0; |
||
272 | } else if (stream->column > stream->max_column) { |
||
273 | newline = TRUE; |
||
274 | break; |
||
275 | } |
||
276 | } else { |
||
277 | if (!_cairo_isdigit(*s) || ++stream->escape_digits == 3) |
||
278 | stream->in_escape = FALSE; |
||
279 | } |
||
280 | s++; |
||
281 | } |
||
282 | |||
283 | if (count) |
||
284 | _cairo_output_stream_write (stream->output, data, count); |
||
285 | |||
286 | if (newline) { |
||
287 | _cairo_output_stream_printf (stream->output, "\\\n"); |
||
288 | stream->column = 0; |
||
289 | } |
||
290 | |||
291 | return count; |
||
292 | } |
||
293 | |||
1897 | serge | 294 | static cairo_status_t |
295 | _word_wrap_stream_write (cairo_output_stream_t *base, |
||
296 | const unsigned char *data, |
||
297 | unsigned int length) |
||
298 | { |
||
299 | word_wrap_stream_t *stream = (word_wrap_stream_t *) base; |
||
3959 | Serge | 300 | int count; |
1897 | serge | 301 | |
302 | while (length) { |
||
3959 | Serge | 303 | switch (stream->state) { |
304 | case WRAP_STATE_WORD: |
||
305 | count = _word_wrap_stream_count_word_up_to (stream, data, length); |
||
306 | break; |
||
307 | case WRAP_STATE_HEXSTRING: |
||
308 | count = _word_wrap_stream_count_hexstring_up_to (stream, data, length); |
||
309 | break; |
||
310 | case WRAP_STATE_STRING: |
||
311 | count = _word_wrap_stream_count_string_up_to (stream, data, length); |
||
312 | break; |
||
313 | case WRAP_STATE_DELIMITER: |
||
314 | count = 1; |
||
1897 | serge | 315 | stream->column++; |
3959 | Serge | 316 | if (*data == '\n' || stream->column >= stream->max_column) { |
1897 | serge | 317 | _cairo_output_stream_printf (stream->output, "\n"); |
318 | stream->column = 0; |
||
3959 | Serge | 319 | } else if (*data == '<') { |
320 | stream->state = WRAP_STATE_HEXSTRING; |
||
321 | } else if (*data == '(') { |
||
322 | stream->state = WRAP_STATE_STRING; |
||
323 | } else if (!_cairo_isspace (*data)) { |
||
324 | stream->state = WRAP_STATE_WORD; |
||
1897 | serge | 325 | } |
3959 | Serge | 326 | if (*data != '\n') |
327 | _cairo_output_stream_write (stream->output, data, 1); |
||
328 | break; |
||
329 | |||
330 | default: |
||
331 | ASSERT_NOT_REACHED; |
||
332 | count = length; |
||
333 | break; |
||
1897 | serge | 334 | } |
3959 | Serge | 335 | data += count; |
336 | length -= count; |
||
1897 | serge | 337 | } |
338 | |||
339 | return _cairo_output_stream_get_status (stream->output); |
||
340 | } |
||
341 | |||
342 | static cairo_status_t |
||
343 | _word_wrap_stream_close (cairo_output_stream_t *base) |
||
344 | { |
||
345 | word_wrap_stream_t *stream = (word_wrap_stream_t *) base; |
||
346 | |||
347 | return _cairo_output_stream_get_status (stream->output); |
||
348 | } |
||
349 | |||
350 | static cairo_output_stream_t * |
||
351 | _word_wrap_stream_create (cairo_output_stream_t *output, int max_column) |
||
352 | { |
||
353 | word_wrap_stream_t *stream; |
||
354 | |||
355 | if (output->status) |
||
356 | return _cairo_output_stream_create_in_error (output->status); |
||
357 | |||
358 | stream = malloc (sizeof (word_wrap_stream_t)); |
||
359 | if (unlikely (stream == NULL)) { |
||
360 | _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); |
||
361 | return (cairo_output_stream_t *) &_cairo_output_stream_nil; |
||
362 | } |
||
363 | |||
364 | _cairo_output_stream_init (&stream->base, |
||
365 | _word_wrap_stream_write, |
||
366 | NULL, |
||
367 | _word_wrap_stream_close); |
||
368 | stream->output = output; |
||
369 | stream->max_column = max_column; |
||
370 | stream->column = 0; |
||
3959 | Serge | 371 | stream->state = WRAP_STATE_DELIMITER; |
372 | stream->in_escape = FALSE; |
||
373 | stream->escape_digits = 0; |
||
1897 | serge | 374 | |
375 | return &stream->base; |
||
376 | } |
||
377 | |||
378 | typedef struct _pdf_path_info { |
||
379 | cairo_output_stream_t *output; |
||
380 | cairo_matrix_t *path_transform; |
||
381 | cairo_line_cap_t line_cap; |
||
382 | cairo_point_t last_move_to_point; |
||
383 | cairo_bool_t has_sub_path; |
||
384 | } pdf_path_info_t; |
||
385 | |||
386 | static cairo_status_t |
||
387 | _cairo_pdf_path_move_to (void *closure, |
||
388 | const cairo_point_t *point) |
||
389 | { |
||
390 | pdf_path_info_t *info = closure; |
||
391 | double x = _cairo_fixed_to_double (point->x); |
||
392 | double y = _cairo_fixed_to_double (point->y); |
||
393 | |||
394 | info->last_move_to_point = *point; |
||
395 | info->has_sub_path = FALSE; |
||
396 | cairo_matrix_transform_point (info->path_transform, &x, &y); |
||
397 | _cairo_output_stream_printf (info->output, |
||
398 | "%g %g m ", x, y); |
||
399 | |||
400 | return _cairo_output_stream_get_status (info->output); |
||
401 | } |
||
402 | |||
403 | static cairo_status_t |
||
404 | _cairo_pdf_path_line_to (void *closure, |
||
405 | const cairo_point_t *point) |
||
406 | { |
||
407 | pdf_path_info_t *info = closure; |
||
408 | double x = _cairo_fixed_to_double (point->x); |
||
409 | double y = _cairo_fixed_to_double (point->y); |
||
410 | |||
411 | if (info->line_cap != CAIRO_LINE_CAP_ROUND && |
||
412 | ! info->has_sub_path && |
||
413 | point->x == info->last_move_to_point.x && |
||
414 | point->y == info->last_move_to_point.y) |
||
415 | { |
||
416 | return CAIRO_STATUS_SUCCESS; |
||
417 | } |
||
418 | |||
419 | info->has_sub_path = TRUE; |
||
420 | cairo_matrix_transform_point (info->path_transform, &x, &y); |
||
421 | _cairo_output_stream_printf (info->output, |
||
422 | "%g %g l ", x, y); |
||
423 | |||
424 | return _cairo_output_stream_get_status (info->output); |
||
425 | } |
||
426 | |||
427 | static cairo_status_t |
||
428 | _cairo_pdf_path_curve_to (void *closure, |
||
429 | const cairo_point_t *b, |
||
430 | const cairo_point_t *c, |
||
431 | const cairo_point_t *d) |
||
432 | { |
||
433 | pdf_path_info_t *info = closure; |
||
434 | double bx = _cairo_fixed_to_double (b->x); |
||
435 | double by = _cairo_fixed_to_double (b->y); |
||
436 | double cx = _cairo_fixed_to_double (c->x); |
||
437 | double cy = _cairo_fixed_to_double (c->y); |
||
438 | double dx = _cairo_fixed_to_double (d->x); |
||
439 | double dy = _cairo_fixed_to_double (d->y); |
||
440 | |||
441 | info->has_sub_path = TRUE; |
||
442 | cairo_matrix_transform_point (info->path_transform, &bx, &by); |
||
443 | cairo_matrix_transform_point (info->path_transform, &cx, &cy); |
||
444 | cairo_matrix_transform_point (info->path_transform, &dx, &dy); |
||
445 | _cairo_output_stream_printf (info->output, |
||
446 | "%g %g %g %g %g %g c ", |
||
447 | bx, by, cx, cy, dx, dy); |
||
448 | return _cairo_output_stream_get_status (info->output); |
||
449 | } |
||
450 | |||
451 | static cairo_status_t |
||
452 | _cairo_pdf_path_close_path (void *closure) |
||
453 | { |
||
454 | pdf_path_info_t *info = closure; |
||
455 | |||
456 | if (info->line_cap != CAIRO_LINE_CAP_ROUND && |
||
457 | ! info->has_sub_path) |
||
458 | { |
||
459 | return CAIRO_STATUS_SUCCESS; |
||
460 | } |
||
461 | |||
462 | _cairo_output_stream_printf (info->output, |
||
463 | "h\n"); |
||
464 | |||
465 | return _cairo_output_stream_get_status (info->output); |
||
466 | } |
||
467 | |||
468 | static cairo_status_t |
||
469 | _cairo_pdf_path_rectangle (pdf_path_info_t *info, cairo_box_t *box) |
||
470 | { |
||
471 | double x1 = _cairo_fixed_to_double (box->p1.x); |
||
472 | double y1 = _cairo_fixed_to_double (box->p1.y); |
||
473 | double x2 = _cairo_fixed_to_double (box->p2.x); |
||
474 | double y2 = _cairo_fixed_to_double (box->p2.y); |
||
475 | |||
476 | cairo_matrix_transform_point (info->path_transform, &x1, &y1); |
||
477 | cairo_matrix_transform_point (info->path_transform, &x2, &y2); |
||
478 | _cairo_output_stream_printf (info->output, |
||
479 | "%g %g %g %g re ", |
||
480 | x1, y1, x2 - x1, y2 - y1); |
||
481 | |||
482 | return _cairo_output_stream_get_status (info->output); |
||
483 | } |
||
484 | |||
485 | /* The line cap value is needed to workaround the fact that PostScript |
||
486 | * and PDF semantics for stroking degenerate sub-paths do not match |
||
487 | * cairo semantics. (PostScript draws something for any line cap |
||
488 | * value, while cairo draws something only for round caps). |
||
489 | * |
||
490 | * When using this function to emit a path to be filled, rather than |
||
491 | * stroked, simply pass %CAIRO_LINE_CAP_ROUND which will guarantee that |
||
492 | * the stroke workaround will not modify the path being emitted. |
||
493 | */ |
||
494 | static cairo_status_t |
||
495 | _cairo_pdf_operators_emit_path (cairo_pdf_operators_t *pdf_operators, |
||
3959 | Serge | 496 | const cairo_path_fixed_t*path, |
1897 | serge | 497 | cairo_matrix_t *path_transform, |
498 | cairo_line_cap_t line_cap) |
||
499 | { |
||
500 | cairo_output_stream_t *word_wrap; |
||
501 | cairo_status_t status, status2; |
||
502 | pdf_path_info_t info; |
||
503 | cairo_box_t box; |
||
504 | |||
505 | word_wrap = _word_wrap_stream_create (pdf_operators->stream, 72); |
||
506 | status = _cairo_output_stream_get_status (word_wrap); |
||
507 | if (unlikely (status)) |
||
508 | return _cairo_output_stream_destroy (word_wrap); |
||
509 | |||
510 | info.output = word_wrap; |
||
511 | info.path_transform = path_transform; |
||
512 | info.line_cap = line_cap; |
||
513 | if (_cairo_path_fixed_is_rectangle (path, &box)) { |
||
514 | status = _cairo_pdf_path_rectangle (&info, &box); |
||
515 | } else { |
||
516 | status = _cairo_path_fixed_interpret (path, |
||
517 | _cairo_pdf_path_move_to, |
||
518 | _cairo_pdf_path_line_to, |
||
519 | _cairo_pdf_path_curve_to, |
||
520 | _cairo_pdf_path_close_path, |
||
521 | &info); |
||
522 | } |
||
523 | |||
524 | status2 = _cairo_output_stream_destroy (word_wrap); |
||
525 | if (status == CAIRO_STATUS_SUCCESS) |
||
526 | status = status2; |
||
527 | |||
528 | return status; |
||
529 | } |
||
530 | |||
531 | cairo_int_status_t |
||
532 | _cairo_pdf_operators_clip (cairo_pdf_operators_t *pdf_operators, |
||
3959 | Serge | 533 | const cairo_path_fixed_t *path, |
1897 | serge | 534 | cairo_fill_rule_t fill_rule) |
535 | { |
||
536 | const char *pdf_operator; |
||
537 | cairo_status_t status; |
||
538 | |||
539 | if (pdf_operators->in_text_object) { |
||
540 | status = _cairo_pdf_operators_end_text (pdf_operators); |
||
541 | if (unlikely (status)) |
||
542 | return status; |
||
543 | } |
||
544 | |||
545 | if (! path->has_current_point) { |
||
546 | /* construct an empty path */ |
||
547 | _cairo_output_stream_printf (pdf_operators->stream, "0 0 m "); |
||
548 | } else { |
||
549 | status = _cairo_pdf_operators_emit_path (pdf_operators, |
||
550 | path, |
||
551 | &pdf_operators->cairo_to_pdf, |
||
552 | CAIRO_LINE_CAP_ROUND); |
||
553 | if (unlikely (status)) |
||
554 | return status; |
||
555 | } |
||
556 | |||
557 | switch (fill_rule) { |
||
558 | default: |
||
559 | ASSERT_NOT_REACHED; |
||
560 | case CAIRO_FILL_RULE_WINDING: |
||
561 | pdf_operator = "W"; |
||
562 | break; |
||
563 | case CAIRO_FILL_RULE_EVEN_ODD: |
||
564 | pdf_operator = "W*"; |
||
565 | break; |
||
566 | } |
||
567 | |||
568 | _cairo_output_stream_printf (pdf_operators->stream, |
||
569 | "%s n\n", |
||
570 | pdf_operator); |
||
571 | |||
572 | return _cairo_output_stream_get_status (pdf_operators->stream); |
||
573 | } |
||
574 | |||
575 | static int |
||
576 | _cairo_pdf_line_cap (cairo_line_cap_t cap) |
||
577 | { |
||
578 | switch (cap) { |
||
579 | case CAIRO_LINE_CAP_BUTT: |
||
580 | return 0; |
||
581 | case CAIRO_LINE_CAP_ROUND: |
||
582 | return 1; |
||
583 | case CAIRO_LINE_CAP_SQUARE: |
||
584 | return 2; |
||
585 | default: |
||
586 | ASSERT_NOT_REACHED; |
||
587 | return 0; |
||
588 | } |
||
589 | } |
||
590 | |||
591 | static int |
||
592 | _cairo_pdf_line_join (cairo_line_join_t join) |
||
593 | { |
||
594 | switch (join) { |
||
595 | case CAIRO_LINE_JOIN_MITER: |
||
596 | return 0; |
||
597 | case CAIRO_LINE_JOIN_ROUND: |
||
598 | return 1; |
||
599 | case CAIRO_LINE_JOIN_BEVEL: |
||
600 | return 2; |
||
601 | default: |
||
602 | ASSERT_NOT_REACHED; |
||
603 | return 0; |
||
604 | } |
||
605 | } |
||
606 | |||
607 | cairo_int_status_t |
||
608 | _cairo_pdf_operators_emit_stroke_style (cairo_pdf_operators_t *pdf_operators, |
||
609 | const cairo_stroke_style_t *style, |
||
610 | double scale) |
||
611 | { |
||
612 | double *dash = style->dash; |
||
613 | int num_dashes = style->num_dashes; |
||
614 | double dash_offset = style->dash_offset; |
||
615 | double line_width = style->line_width * scale; |
||
616 | |||
617 | /* PostScript has "special needs" when it comes to zero-length |
||
618 | * dash segments with butt caps. It apparently (at least |
||
619 | * according to ghostscript) draws hairlines for this |
||
620 | * case. That's not what the cairo semantics want, so we first |
||
621 | * touch up the array to eliminate any 0.0 values that will |
||
622 | * result in "on" segments. |
||
623 | */ |
||
624 | if (num_dashes && style->line_cap == CAIRO_LINE_CAP_BUTT) { |
||
625 | int i; |
||
626 | |||
627 | /* If there's an odd number of dash values they will each get |
||
628 | * interpreted as both on and off. So we first explicitly |
||
629 | * expand the array to remove the duplicate usage so that we |
||
630 | * can modify some of the values. |
||
631 | */ |
||
632 | if (num_dashes % 2) { |
||
633 | dash = _cairo_malloc_abc (num_dashes, 2, sizeof (double)); |
||
634 | if (unlikely (dash == NULL)) |
||
635 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
636 | |||
637 | memcpy (dash, style->dash, num_dashes * sizeof (double)); |
||
638 | memcpy (dash + num_dashes, style->dash, num_dashes * sizeof (double)); |
||
639 | |||
640 | num_dashes *= 2; |
||
641 | } |
||
642 | |||
643 | for (i = 0; i < num_dashes; i += 2) { |
||
644 | if (dash[i] == 0.0) { |
||
645 | /* Do not modify the dashes in-place, as we may need to also |
||
646 | * replay this stroke to an image fallback. |
||
647 | */ |
||
648 | if (dash == style->dash) { |
||
649 | dash = _cairo_malloc_ab (num_dashes, sizeof (double)); |
||
650 | if (unlikely (dash == NULL)) |
||
651 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
652 | memcpy (dash, style->dash, num_dashes * sizeof (double)); |
||
653 | } |
||
654 | |||
655 | /* If we're at the front of the list, we first rotate |
||
656 | * two elements from the end of the list to the front |
||
657 | * of the list before folding away the 0.0. Or, if |
||
658 | * there are only two dash elements, then there is |
||
659 | * nothing at all to draw. |
||
660 | */ |
||
661 | if (i == 0) { |
||
662 | double last_two[2]; |
||
663 | |||
664 | if (num_dashes == 2) { |
||
665 | free (dash); |
||
666 | return CAIRO_INT_STATUS_NOTHING_TO_DO; |
||
667 | } |
||
668 | |||
669 | /* The cases of num_dashes == 0, 1, or 3 elements |
||
670 | * cannot exist, so the rotation of 2 elements |
||
671 | * will always be safe */ |
||
672 | memcpy (last_two, dash + num_dashes - 2, sizeof (last_two)); |
||
673 | memmove (dash + 2, dash, (num_dashes - 2) * sizeof (double)); |
||
674 | memcpy (dash, last_two, sizeof (last_two)); |
||
675 | dash_offset += dash[0] + dash[1]; |
||
676 | i = 2; |
||
677 | } |
||
678 | dash[i-1] += dash[i+1]; |
||
679 | num_dashes -= 2; |
||
680 | memmove (dash + i, dash + i + 2, (num_dashes - i) * sizeof (double)); |
||
681 | /* If we might have just rotated, it's possible that |
||
682 | * we rotated a 0.0 value to the front of the list. |
||
683 | * Set i to -2 so it will get incremented to 0. */ |
||
684 | if (i == 2) |
||
685 | i = -2; |
||
686 | } |
||
687 | } |
||
688 | } |
||
689 | |||
690 | if (!pdf_operators->has_line_style || pdf_operators->line_width != line_width) { |
||
691 | _cairo_output_stream_printf (pdf_operators->stream, |
||
692 | "%f w\n", |
||
693 | line_width); |
||
694 | pdf_operators->line_width = line_width; |
||
695 | } |
||
696 | |||
697 | if (!pdf_operators->has_line_style || pdf_operators->line_cap != style->line_cap) { |
||
698 | _cairo_output_stream_printf (pdf_operators->stream, |
||
699 | "%d J\n", |
||
700 | _cairo_pdf_line_cap (style->line_cap)); |
||
701 | pdf_operators->line_cap = style->line_cap; |
||
702 | } |
||
703 | |||
704 | if (!pdf_operators->has_line_style || pdf_operators->line_join != style->line_join) { |
||
705 | _cairo_output_stream_printf (pdf_operators->stream, |
||
706 | "%d j\n", |
||
707 | _cairo_pdf_line_join (style->line_join)); |
||
708 | pdf_operators->line_join = style->line_join; |
||
709 | } |
||
710 | |||
711 | if (num_dashes) { |
||
712 | int d; |
||
713 | |||
714 | _cairo_output_stream_printf (pdf_operators->stream, "["); |
||
715 | for (d = 0; d < num_dashes; d++) |
||
716 | _cairo_output_stream_printf (pdf_operators->stream, " %f", dash[d] * scale); |
||
717 | _cairo_output_stream_printf (pdf_operators->stream, "] %f d\n", |
||
718 | dash_offset * scale); |
||
719 | pdf_operators->has_dashes = TRUE; |
||
720 | } else if (!pdf_operators->has_line_style || pdf_operators->has_dashes) { |
||
721 | _cairo_output_stream_printf (pdf_operators->stream, "[] 0.0 d\n"); |
||
722 | pdf_operators->has_dashes = FALSE; |
||
723 | } |
||
724 | if (dash != style->dash) |
||
725 | free (dash); |
||
726 | |||
727 | if (!pdf_operators->has_line_style || pdf_operators->miter_limit != style->miter_limit) { |
||
728 | _cairo_output_stream_printf (pdf_operators->stream, |
||
729 | "%f M ", |
||
730 | style->miter_limit < 1.0 ? 1.0 : style->miter_limit); |
||
731 | pdf_operators->miter_limit = style->miter_limit; |
||
732 | } |
||
733 | pdf_operators->has_line_style = TRUE; |
||
734 | |||
735 | return _cairo_output_stream_get_status (pdf_operators->stream); |
||
736 | } |
||
737 | |||
738 | /* Scale the matrix so the largest absolute value of the non |
||
739 | * translation components is 1.0. Return the scale required to restore |
||
740 | * the matrix to the original values. |
||
741 | * |
||
742 | * eg the matrix [ 100 0 0 50 20 10 ] |
||
743 | * |
||
744 | * is rescaled to [ 1 0 0 0.5 0.2 0.1 ] |
||
745 | * and the scale returned is 100 |
||
746 | */ |
||
747 | static void |
||
748 | _cairo_matrix_factor_out_scale (cairo_matrix_t *m, double *scale) |
||
749 | { |
||
750 | double s; |
||
751 | |||
752 | s = fabs (m->xx); |
||
753 | if (fabs (m->xy) > s) |
||
754 | s = fabs (m->xy); |
||
755 | if (fabs (m->yx) > s) |
||
756 | s = fabs (m->yx); |
||
757 | if (fabs (m->yy) > s) |
||
758 | s = fabs (m->yy); |
||
759 | *scale = s; |
||
760 | s = 1.0/s; |
||
761 | cairo_matrix_scale (m, s, s); |
||
762 | } |
||
763 | |||
764 | static cairo_int_status_t |
||
765 | _cairo_pdf_operators_emit_stroke (cairo_pdf_operators_t *pdf_operators, |
||
3959 | Serge | 766 | const cairo_path_fixed_t *path, |
1897 | serge | 767 | const cairo_stroke_style_t *style, |
768 | const cairo_matrix_t *ctm, |
||
769 | const cairo_matrix_t *ctm_inverse, |
||
770 | const char *pdf_operator) |
||
771 | { |
||
3959 | Serge | 772 | cairo_int_status_t status; |
1897 | serge | 773 | cairo_matrix_t m, path_transform; |
774 | cairo_bool_t has_ctm = TRUE; |
||
775 | double scale = 1.0; |
||
776 | |||
777 | if (pdf_operators->in_text_object) { |
||
778 | status = _cairo_pdf_operators_end_text (pdf_operators); |
||
779 | if (unlikely (status)) |
||
780 | return status; |
||
781 | } |
||
782 | |||
783 | /* Optimize away the stroke ctm when it does not affect the |
||
784 | * stroke. There are other ctm cases that could be optimized |
||
785 | * however this is the most common. |
||
786 | */ |
||
787 | if (fabs(ctm->xx) == 1.0 && fabs(ctm->yy) == 1.0 && |
||
788 | fabs(ctm->xy) == 0.0 && fabs(ctm->yx) == 0.0) |
||
789 | { |
||
790 | has_ctm = FALSE; |
||
791 | } |
||
792 | |||
793 | /* The PDF CTM is transformed to the user space CTM when stroking |
||
794 | * so the corect pen shape will be used. This also requires that |
||
795 | * the path be transformed to user space when emitted. The |
||
796 | * conversion of path coordinates to user space may cause rounding |
||
797 | * errors. For example the device space point (1.234, 3.142) when |
||
798 | * transformed to a user space CTM of [100 0 0 100 0 0] will be |
||
799 | * emitted as (0.012, 0.031). |
||
800 | * |
||
801 | * To avoid the rounding problem we scale the user space CTM |
||
802 | * matrix so that all the non translation components of the matrix |
||
803 | * are <= 1. The line width and and dashes are scaled by the |
||
804 | * inverse of the scale applied to the CTM. This maintains the |
||
805 | * shape of the stroke pen while keeping the user space CTM within |
||
806 | * the range that maximizes the precision of the emitted path. |
||
807 | */ |
||
808 | if (has_ctm) { |
||
809 | m = *ctm; |
||
810 | /* Zero out the translation since it does not affect the pen |
||
811 | * shape however it may cause unnecessary digits to be emitted. |
||
812 | */ |
||
813 | m.x0 = 0.0; |
||
814 | m.y0 = 0.0; |
||
815 | _cairo_matrix_factor_out_scale (&m, &scale); |
||
816 | path_transform = m; |
||
817 | status = cairo_matrix_invert (&path_transform); |
||
818 | if (unlikely (status)) |
||
819 | return status; |
||
820 | |||
821 | cairo_matrix_multiply (&m, &m, &pdf_operators->cairo_to_pdf); |
||
822 | } |
||
823 | |||
824 | status = _cairo_pdf_operators_emit_stroke_style (pdf_operators, style, scale); |
||
825 | if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) |
||
826 | return CAIRO_STATUS_SUCCESS; |
||
827 | if (unlikely (status)) |
||
828 | return status; |
||
829 | |||
830 | if (has_ctm) { |
||
831 | _cairo_output_stream_printf (pdf_operators->stream, |
||
832 | "q %f %f %f %f %f %f cm\n", |
||
833 | m.xx, m.yx, m.xy, m.yy, |
||
834 | m.x0, m.y0); |
||
835 | } else { |
||
836 | path_transform = pdf_operators->cairo_to_pdf; |
||
837 | } |
||
838 | |||
839 | status = _cairo_pdf_operators_emit_path (pdf_operators, |
||
840 | path, |
||
841 | &path_transform, |
||
842 | style->line_cap); |
||
843 | if (unlikely (status)) |
||
844 | return status; |
||
845 | |||
846 | _cairo_output_stream_printf (pdf_operators->stream, "%s", pdf_operator); |
||
847 | if (has_ctm) |
||
848 | _cairo_output_stream_printf (pdf_operators->stream, " Q"); |
||
849 | |||
850 | _cairo_output_stream_printf (pdf_operators->stream, "\n"); |
||
851 | |||
852 | return _cairo_output_stream_get_status (pdf_operators->stream); |
||
853 | } |
||
854 | |||
855 | cairo_int_status_t |
||
856 | _cairo_pdf_operators_stroke (cairo_pdf_operators_t *pdf_operators, |
||
3959 | Serge | 857 | const cairo_path_fixed_t *path, |
1897 | serge | 858 | const cairo_stroke_style_t *style, |
859 | const cairo_matrix_t *ctm, |
||
860 | const cairo_matrix_t *ctm_inverse) |
||
861 | { |
||
862 | return _cairo_pdf_operators_emit_stroke (pdf_operators, |
||
863 | path, |
||
864 | style, |
||
865 | ctm, |
||
866 | ctm_inverse, |
||
867 | "S"); |
||
868 | } |
||
869 | |||
870 | cairo_int_status_t |
||
871 | _cairo_pdf_operators_fill (cairo_pdf_operators_t *pdf_operators, |
||
3959 | Serge | 872 | const cairo_path_fixed_t *path, |
1897 | serge | 873 | cairo_fill_rule_t fill_rule) |
874 | { |
||
875 | const char *pdf_operator; |
||
876 | cairo_status_t status; |
||
877 | |||
878 | if (pdf_operators->in_text_object) { |
||
879 | status = _cairo_pdf_operators_end_text (pdf_operators); |
||
880 | if (unlikely (status)) |
||
881 | return status; |
||
882 | } |
||
883 | |||
884 | status = _cairo_pdf_operators_emit_path (pdf_operators, |
||
885 | path, |
||
886 | &pdf_operators->cairo_to_pdf, |
||
887 | CAIRO_LINE_CAP_ROUND); |
||
888 | if (unlikely (status)) |
||
889 | return status; |
||
890 | |||
891 | switch (fill_rule) { |
||
892 | default: |
||
893 | ASSERT_NOT_REACHED; |
||
894 | case CAIRO_FILL_RULE_WINDING: |
||
895 | pdf_operator = "f"; |
||
896 | break; |
||
897 | case CAIRO_FILL_RULE_EVEN_ODD: |
||
898 | pdf_operator = "f*"; |
||
899 | break; |
||
900 | } |
||
901 | |||
902 | _cairo_output_stream_printf (pdf_operators->stream, |
||
903 | "%s\n", |
||
904 | pdf_operator); |
||
905 | |||
906 | return _cairo_output_stream_get_status (pdf_operators->stream); |
||
907 | } |
||
908 | |||
909 | cairo_int_status_t |
||
910 | _cairo_pdf_operators_fill_stroke (cairo_pdf_operators_t *pdf_operators, |
||
3959 | Serge | 911 | const cairo_path_fixed_t *path, |
1897 | serge | 912 | cairo_fill_rule_t fill_rule, |
913 | const cairo_stroke_style_t *style, |
||
914 | const cairo_matrix_t *ctm, |
||
915 | const cairo_matrix_t *ctm_inverse) |
||
916 | { |
||
917 | const char *operator; |
||
918 | |||
919 | switch (fill_rule) { |
||
920 | default: |
||
921 | ASSERT_NOT_REACHED; |
||
922 | case CAIRO_FILL_RULE_WINDING: |
||
923 | operator = "B"; |
||
924 | break; |
||
925 | case CAIRO_FILL_RULE_EVEN_ODD: |
||
926 | operator = "B*"; |
||
927 | break; |
||
928 | } |
||
929 | |||
930 | return _cairo_pdf_operators_emit_stroke (pdf_operators, |
||
931 | path, |
||
932 | style, |
||
933 | ctm, |
||
934 | ctm_inverse, |
||
935 | operator); |
||
936 | } |
||
937 | |||
3959 | Serge | 938 | static void |
939 | _cairo_pdf_operators_emit_glyph_index (cairo_pdf_operators_t *pdf_operators, |
||
940 | cairo_output_stream_t *stream, |
||
941 | unsigned int glyph) |
||
942 | { |
||
943 | if (pdf_operators->is_latin) { |
||
944 | if (glyph == '(' || glyph == ')' || glyph == '\\') |
||
945 | _cairo_output_stream_printf (stream, "\\%c", glyph); |
||
946 | else if (glyph >= 0x20 && glyph <= 0x7e) |
||
947 | _cairo_output_stream_printf (stream, "%c", glyph); |
||
948 | else |
||
949 | _cairo_output_stream_printf (stream, "\\%03o", glyph); |
||
950 | } else { |
||
951 | _cairo_output_stream_printf (stream, |
||
952 | "%0*x", |
||
953 | pdf_operators->hex_width, |
||
954 | glyph); |
||
955 | } |
||
956 | } |
||
957 | |||
1897 | serge | 958 | #define GLYPH_POSITION_TOLERANCE 0.001 |
959 | |||
960 | /* Emit the string of glyphs using the 'Tj' operator. This requires |
||
961 | * that the glyphs are positioned at their natural glyph advances. */ |
||
962 | static cairo_status_t |
||
963 | _cairo_pdf_operators_emit_glyph_string (cairo_pdf_operators_t *pdf_operators, |
||
964 | cairo_output_stream_t *stream) |
||
965 | { |
||
966 | int i; |
||
967 | |||
3959 | Serge | 968 | _cairo_output_stream_printf (stream, "%s", pdf_operators->is_latin ? "(" : "<"); |
1897 | serge | 969 | for (i = 0; i < pdf_operators->num_glyphs; i++) { |
3959 | Serge | 970 | _cairo_pdf_operators_emit_glyph_index (pdf_operators, |
971 | stream, |
||
972 | pdf_operators->glyphs[i].glyph_index); |
||
1897 | serge | 973 | pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance; |
974 | } |
||
3959 | Serge | 975 | _cairo_output_stream_printf (stream, "%sTj\n", pdf_operators->is_latin ? ")" : ">"); |
1897 | serge | 976 | |
977 | return _cairo_output_stream_get_status (stream); |
||
978 | } |
||
979 | |||
980 | /* Emit the string of glyphs using the 'TJ' operator. |
||
981 | * |
||
982 | * The TJ operator takes an array of strings of glyphs. Each string of |
||
983 | * glyphs is displayed using the glyph advances of each glyph to |
||
984 | * position the glyphs. A relative adjustment to the glyph advance may |
||
985 | * be specified by including the adjustment between two strings. The |
||
986 | * adjustment is in units of text space * -1000. |
||
987 | */ |
||
988 | static cairo_status_t |
||
989 | _cairo_pdf_operators_emit_glyph_string_with_positioning ( |
||
990 | cairo_pdf_operators_t *pdf_operators, |
||
991 | cairo_output_stream_t *stream) |
||
992 | { |
||
993 | int i; |
||
994 | |||
3959 | Serge | 995 | _cairo_output_stream_printf (stream, "[%s", pdf_operators->is_latin ? "(" : "<"); |
1897 | serge | 996 | for (i = 0; i < pdf_operators->num_glyphs; i++) { |
997 | if (pdf_operators->glyphs[i].x_position != pdf_operators->cur_x) |
||
998 | { |
||
999 | double delta = pdf_operators->glyphs[i].x_position - pdf_operators->cur_x; |
||
1000 | int rounded_delta; |
||
1001 | |||
1002 | delta = -1000.0*delta; |
||
1003 | /* As the delta is in 1/1000 of a unit of text space, |
||
1004 | * rounding to an integer should still provide sufficient |
||
1005 | * precision. We round the delta before adding to Tm_x so |
||
1006 | * that we keep track of the accumulated rounding error in |
||
1007 | * the PDF interpreter and compensate for it when |
||
1008 | * calculating subsequent deltas. |
||
1009 | */ |
||
1010 | rounded_delta = _cairo_lround (delta); |
||
3959 | Serge | 1011 | if (abs(rounded_delta) < 3) |
1012 | rounded_delta = 0; |
||
1897 | serge | 1013 | if (rounded_delta != 0) { |
3959 | Serge | 1014 | if (pdf_operators->is_latin) { |
1015 | _cairo_output_stream_printf (stream, |
||
1016 | ")%d(", |
||
1017 | rounded_delta); |
||
1018 | } else { |
||
1019 | _cairo_output_stream_printf (stream, |
||
1020 | ">%d<", |
||
1021 | rounded_delta); |
||
1022 | } |
||
1897 | serge | 1023 | } |
1024 | |||
1025 | /* Convert the rounded delta back to text |
||
1026 | * space before adding to the current text |
||
1027 | * position. */ |
||
1028 | delta = rounded_delta/-1000.0; |
||
1029 | pdf_operators->cur_x += delta; |
||
1030 | } |
||
1031 | |||
3959 | Serge | 1032 | _cairo_pdf_operators_emit_glyph_index (pdf_operators, |
1033 | stream, |
||
1034 | pdf_operators->glyphs[i].glyph_index); |
||
1897 | serge | 1035 | pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance; |
1036 | } |
||
3959 | Serge | 1037 | _cairo_output_stream_printf (stream, "%s]TJ\n", pdf_operators->is_latin ? ")" : ">"); |
1897 | serge | 1038 | |
1039 | return _cairo_output_stream_get_status (stream); |
||
1040 | } |
||
1041 | |||
1042 | static cairo_status_t |
||
1043 | _cairo_pdf_operators_flush_glyphs (cairo_pdf_operators_t *pdf_operators) |
||
1044 | { |
||
1045 | cairo_output_stream_t *word_wrap_stream; |
||
1046 | cairo_status_t status, status2; |
||
1047 | int i; |
||
1048 | double x; |
||
1049 | |||
1050 | if (pdf_operators->num_glyphs == 0) |
||
1051 | return CAIRO_STATUS_SUCCESS; |
||
1052 | |||
1053 | word_wrap_stream = _word_wrap_stream_create (pdf_operators->stream, 72); |
||
1054 | status = _cairo_output_stream_get_status (word_wrap_stream); |
||
1055 | if (unlikely (status)) |
||
1056 | return _cairo_output_stream_destroy (word_wrap_stream); |
||
1057 | |||
1058 | /* Check if glyph advance used to position every glyph */ |
||
1059 | x = pdf_operators->cur_x; |
||
1060 | for (i = 0; i < pdf_operators->num_glyphs; i++) { |
||
1061 | if (fabs(pdf_operators->glyphs[i].x_position - x) > GLYPH_POSITION_TOLERANCE) |
||
1062 | break; |
||
1063 | x += pdf_operators->glyphs[i].x_advance; |
||
1064 | } |
||
1065 | if (i == pdf_operators->num_glyphs) { |
||
1066 | status = _cairo_pdf_operators_emit_glyph_string (pdf_operators, |
||
1067 | word_wrap_stream); |
||
1068 | } else { |
||
1069 | status = _cairo_pdf_operators_emit_glyph_string_with_positioning ( |
||
1070 | pdf_operators, word_wrap_stream); |
||
1071 | } |
||
1072 | |||
1073 | pdf_operators->num_glyphs = 0; |
||
1074 | pdf_operators->glyph_buf_x_pos = pdf_operators->cur_x; |
||
1075 | status2 = _cairo_output_stream_destroy (word_wrap_stream); |
||
1076 | if (status == CAIRO_STATUS_SUCCESS) |
||
1077 | status = status2; |
||
1078 | |||
1079 | return status; |
||
1080 | } |
||
1081 | |||
1082 | static cairo_status_t |
||
1083 | _cairo_pdf_operators_add_glyph (cairo_pdf_operators_t *pdf_operators, |
||
1084 | cairo_scaled_font_subsets_glyph_t *glyph, |
||
1085 | double x_position) |
||
1086 | { |
||
1087 | double x, y; |
||
1088 | |||
1089 | x = glyph->x_advance; |
||
1090 | y = glyph->y_advance; |
||
1091 | if (glyph->is_scaled) |
||
1092 | cairo_matrix_transform_distance (&pdf_operators->font_matrix_inverse, &x, &y); |
||
1093 | |||
1094 | pdf_operators->glyphs[pdf_operators->num_glyphs].x_position = x_position; |
||
1095 | pdf_operators->glyphs[pdf_operators->num_glyphs].glyph_index = glyph->subset_glyph_index; |
||
1096 | pdf_operators->glyphs[pdf_operators->num_glyphs].x_advance = x; |
||
1097 | pdf_operators->glyph_buf_x_pos += x; |
||
1098 | pdf_operators->num_glyphs++; |
||
1099 | if (pdf_operators->num_glyphs == PDF_GLYPH_BUFFER_SIZE) |
||
1100 | return _cairo_pdf_operators_flush_glyphs (pdf_operators); |
||
1101 | |||
1102 | return CAIRO_STATUS_SUCCESS; |
||
1103 | } |
||
1104 | |||
1105 | /* Use 'Tm' operator to set the PDF text matrix. */ |
||
1106 | static cairo_status_t |
||
1107 | _cairo_pdf_operators_set_text_matrix (cairo_pdf_operators_t *pdf_operators, |
||
1108 | cairo_matrix_t *matrix) |
||
1109 | { |
||
1110 | cairo_matrix_t inverse; |
||
1111 | cairo_status_t status; |
||
1112 | |||
1113 | /* We require the matrix to be invertable. */ |
||
1114 | inverse = *matrix; |
||
1115 | status = cairo_matrix_invert (&inverse); |
||
1116 | if (unlikely (status)) |
||
1117 | return status; |
||
1118 | |||
1119 | pdf_operators->text_matrix = *matrix; |
||
1120 | pdf_operators->cur_x = 0; |
||
1121 | pdf_operators->cur_y = 0; |
||
1122 | pdf_operators->glyph_buf_x_pos = 0; |
||
1123 | _cairo_output_stream_printf (pdf_operators->stream, |
||
1124 | "%f %f %f %f %f %f Tm\n", |
||
1125 | pdf_operators->text_matrix.xx, |
||
1126 | pdf_operators->text_matrix.yx, |
||
1127 | pdf_operators->text_matrix.xy, |
||
1128 | pdf_operators->text_matrix.yy, |
||
1129 | pdf_operators->text_matrix.x0, |
||
1130 | pdf_operators->text_matrix.y0); |
||
1131 | |||
1132 | pdf_operators->cairo_to_pdftext = *matrix; |
||
1133 | status = cairo_matrix_invert (&pdf_operators->cairo_to_pdftext); |
||
1134 | assert (status == CAIRO_STATUS_SUCCESS); |
||
1135 | cairo_matrix_multiply (&pdf_operators->cairo_to_pdftext, |
||
1136 | &pdf_operators->cairo_to_pdf, |
||
1137 | &pdf_operators->cairo_to_pdftext); |
||
1138 | |||
1139 | return _cairo_output_stream_get_status (pdf_operators->stream); |
||
1140 | } |
||
1141 | |||
1142 | #define TEXT_MATRIX_TOLERANCE 1e-6 |
||
1143 | |||
1144 | /* Set the translation components of the PDF text matrix to x, y. The |
||
1145 | * 'Td' operator is used to transform the text matrix. |
||
1146 | */ |
||
1147 | static cairo_status_t |
||
1148 | _cairo_pdf_operators_set_text_position (cairo_pdf_operators_t *pdf_operators, |
||
1149 | double x, |
||
1150 | double y) |
||
1151 | { |
||
1152 | cairo_matrix_t translate, inverse; |
||
1153 | cairo_status_t status; |
||
1154 | |||
1155 | /* The Td operator transforms the text_matrix with: |
||
1156 | * |
||
1157 | * text_matrix' = T x text_matrix |
||
1158 | * |
||
1159 | * where T is a translation matrix with the translation components |
||
1160 | * set to the Td operands tx and ty. |
||
1161 | */ |
||
1162 | inverse = pdf_operators->text_matrix; |
||
1163 | status = cairo_matrix_invert (&inverse); |
||
1164 | assert (status == CAIRO_STATUS_SUCCESS); |
||
1165 | pdf_operators->text_matrix.x0 = x; |
||
1166 | pdf_operators->text_matrix.y0 = y; |
||
1167 | cairo_matrix_multiply (&translate, &pdf_operators->text_matrix, &inverse); |
||
1168 | if (fabs(translate.x0) < TEXT_MATRIX_TOLERANCE) |
||
1169 | translate.x0 = 0.0; |
||
1170 | if (fabs(translate.y0) < TEXT_MATRIX_TOLERANCE) |
||
1171 | translate.y0 = 0.0; |
||
1172 | _cairo_output_stream_printf (pdf_operators->stream, |
||
1173 | "%f %f Td\n", |
||
1174 | translate.x0, |
||
1175 | translate.y0); |
||
1176 | pdf_operators->cur_x = 0; |
||
1177 | pdf_operators->cur_y = 0; |
||
1178 | pdf_operators->glyph_buf_x_pos = 0; |
||
1179 | |||
1180 | pdf_operators->cairo_to_pdftext = pdf_operators->text_matrix; |
||
1181 | status = cairo_matrix_invert (&pdf_operators->cairo_to_pdftext); |
||
1182 | assert (status == CAIRO_STATUS_SUCCESS); |
||
1183 | cairo_matrix_multiply (&pdf_operators->cairo_to_pdftext, |
||
1184 | &pdf_operators->cairo_to_pdf, |
||
1185 | &pdf_operators->cairo_to_pdftext); |
||
1186 | |||
1187 | return _cairo_output_stream_get_status (pdf_operators->stream); |
||
1188 | } |
||
1189 | |||
1190 | /* Select the font using the 'Tf' operator. The font size is set to 1 |
||
1191 | * as we use the 'Tm' operator to set the font scale. |
||
1192 | */ |
||
1193 | static cairo_status_t |
||
1194 | _cairo_pdf_operators_set_font_subset (cairo_pdf_operators_t *pdf_operators, |
||
1195 | cairo_scaled_font_subsets_glyph_t *subset_glyph) |
||
1196 | { |
||
1197 | cairo_status_t status; |
||
1198 | |||
1199 | _cairo_output_stream_printf (pdf_operators->stream, |
||
1200 | "/f-%d-%d 1 Tf\n", |
||
1201 | subset_glyph->font_id, |
||
1202 | subset_glyph->subset_id); |
||
1203 | if (pdf_operators->use_font_subset) { |
||
1204 | status = pdf_operators->use_font_subset (subset_glyph->font_id, |
||
1205 | subset_glyph->subset_id, |
||
1206 | pdf_operators->use_font_subset_closure); |
||
1207 | if (unlikely (status)) |
||
1208 | return status; |
||
1209 | } |
||
1210 | pdf_operators->font_id = subset_glyph->font_id; |
||
1211 | pdf_operators->subset_id = subset_glyph->subset_id; |
||
3959 | Serge | 1212 | pdf_operators->is_latin = subset_glyph->is_latin; |
1897 | serge | 1213 | |
1214 | if (subset_glyph->is_composite) |
||
1215 | pdf_operators->hex_width = 4; |
||
1216 | else |
||
1217 | pdf_operators->hex_width = 2; |
||
1218 | |||
1219 | return CAIRO_STATUS_SUCCESS; |
||
1220 | } |
||
1221 | |||
1222 | static cairo_status_t |
||
1223 | _cairo_pdf_operators_begin_text (cairo_pdf_operators_t *pdf_operators) |
||
1224 | { |
||
1225 | _cairo_output_stream_printf (pdf_operators->stream, "BT\n"); |
||
1226 | |||
1227 | pdf_operators->in_text_object = TRUE; |
||
1228 | pdf_operators->num_glyphs = 0; |
||
1229 | pdf_operators->glyph_buf_x_pos = 0; |
||
1230 | |||
1231 | return _cairo_output_stream_get_status (pdf_operators->stream); |
||
1232 | } |
||
1233 | |||
1234 | static cairo_status_t |
||
1235 | _cairo_pdf_operators_end_text (cairo_pdf_operators_t *pdf_operators) |
||
1236 | { |
||
1237 | cairo_status_t status; |
||
1238 | |||
1239 | status = _cairo_pdf_operators_flush_glyphs (pdf_operators); |
||
1240 | if (unlikely (status)) |
||
1241 | return status; |
||
1242 | |||
1243 | _cairo_output_stream_printf (pdf_operators->stream, "ET\n"); |
||
1244 | |||
1245 | pdf_operators->in_text_object = FALSE; |
||
1246 | |||
1247 | return _cairo_output_stream_get_status (pdf_operators->stream); |
||
1248 | } |
||
1249 | |||
1250 | /* Compare the scale components of two matrices. The translation |
||
1251 | * components are ignored. */ |
||
1252 | static cairo_bool_t |
||
1253 | _cairo_matrix_scale_equal (cairo_matrix_t *a, cairo_matrix_t *b) |
||
1254 | { |
||
1255 | return (a->xx == b->xx && |
||
1256 | a->xy == b->xy && |
||
1257 | a->yx == b->yx && |
||
1258 | a->yy == b->yy); |
||
1259 | } |
||
1260 | |||
1261 | static cairo_status_t |
||
1262 | _cairo_pdf_operators_begin_actualtext (cairo_pdf_operators_t *pdf_operators, |
||
1263 | const char *utf8, |
||
1264 | int utf8_len) |
||
1265 | { |
||
1266 | uint16_t *utf16; |
||
1267 | int utf16_len; |
||
1268 | cairo_status_t status; |
||
1269 | int i; |
||
1270 | |||
1271 | _cairo_output_stream_printf (pdf_operators->stream, "/Span << /ActualText |
||
1272 | if (utf8_len) { |
||
1273 | status = _cairo_utf8_to_utf16 (utf8, utf8_len, &utf16, &utf16_len); |
||
1274 | if (unlikely (status)) |
||
1275 | return status; |
||
1276 | |||
1277 | for (i = 0; i < utf16_len; i++) { |
||
1278 | _cairo_output_stream_printf (pdf_operators->stream, |
||
1279 | "%04x", (int) (utf16[i])); |
||
1280 | } |
||
1281 | free (utf16); |
||
1282 | } |
||
1283 | _cairo_output_stream_printf (pdf_operators->stream, "> >> BDC\n"); |
||
1284 | |||
1285 | return _cairo_output_stream_get_status (pdf_operators->stream); |
||
1286 | } |
||
1287 | |||
1288 | static cairo_status_t |
||
1289 | _cairo_pdf_operators_end_actualtext (cairo_pdf_operators_t *pdf_operators) |
||
1290 | { |
||
1291 | _cairo_output_stream_printf (pdf_operators->stream, "EMC\n"); |
||
1292 | |||
1293 | return _cairo_output_stream_get_status (pdf_operators->stream); |
||
1294 | } |
||
1295 | |||
1296 | static cairo_status_t |
||
1297 | _cairo_pdf_operators_emit_glyph (cairo_pdf_operators_t *pdf_operators, |
||
1298 | cairo_glyph_t *glyph, |
||
1299 | cairo_scaled_font_subsets_glyph_t *subset_glyph) |
||
1300 | { |
||
1301 | double x, y; |
||
1302 | cairo_status_t status; |
||
1303 | |||
1304 | if (pdf_operators->is_new_text_object || |
||
1305 | pdf_operators->font_id != subset_glyph->font_id || |
||
1306 | pdf_operators->subset_id != subset_glyph->subset_id) |
||
1307 | { |
||
1308 | status = _cairo_pdf_operators_flush_glyphs (pdf_operators); |
||
1309 | if (unlikely (status)) |
||
1310 | return status; |
||
1311 | |||
1312 | status = _cairo_pdf_operators_set_font_subset (pdf_operators, subset_glyph); |
||
1313 | if (unlikely (status)) |
||
1314 | return status; |
||
1315 | |||
1316 | pdf_operators->is_new_text_object = FALSE; |
||
1317 | } |
||
1318 | |||
1319 | x = glyph->x; |
||
1320 | y = glyph->y; |
||
1321 | cairo_matrix_transform_point (&pdf_operators->cairo_to_pdftext, &x, &y); |
||
1322 | |||
1323 | /* The TJ operator for displaying text strings can only set |
||
1324 | * the horizontal position of the glyphs. If the y position |
||
1325 | * (in text space) changes, use the Td operator to change the |
||
1326 | * current position to the next glyph. We also use the Td |
||
1327 | * operator to move the current position if the horizontal |
||
1328 | * position changes by more than 10 (in text space |
||
1329 | * units). This is becauses the horizontal glyph positioning |
||
1330 | * in the TJ operator is intended for kerning and there may be |
||
1331 | * PDF consumers that do not handle very large position |
||
1332 | * adjustments in TJ. |
||
1333 | */ |
||
1334 | if (fabs(x - pdf_operators->glyph_buf_x_pos) > 10 || |
||
1335 | fabs(y - pdf_operators->cur_y) > GLYPH_POSITION_TOLERANCE) |
||
1336 | { |
||
1337 | status = _cairo_pdf_operators_flush_glyphs (pdf_operators); |
||
1338 | if (unlikely (status)) |
||
1339 | return status; |
||
1340 | |||
1341 | x = glyph->x; |
||
1342 | y = glyph->y; |
||
1343 | cairo_matrix_transform_point (&pdf_operators->cairo_to_pdf, &x, &y); |
||
1344 | status = _cairo_pdf_operators_set_text_position (pdf_operators, x, y); |
||
1345 | if (unlikely (status)) |
||
1346 | return status; |
||
1347 | |||
1348 | x = 0.0; |
||
1349 | y = 0.0; |
||
1350 | } |
||
1351 | |||
1352 | status = _cairo_pdf_operators_add_glyph (pdf_operators, |
||
1353 | subset_glyph, |
||
1354 | x); |
||
1355 | return status; |
||
1356 | } |
||
1357 | |||
1358 | /* A utf8_len of -1 indicates no unicode text. A utf8_len = 0 is an |
||
1359 | * empty string. |
||
1360 | */ |
||
1361 | static cairo_int_status_t |
||
1362 | _cairo_pdf_operators_emit_cluster (cairo_pdf_operators_t *pdf_operators, |
||
1363 | const char *utf8, |
||
1364 | int utf8_len, |
||
1365 | cairo_glyph_t *glyphs, |
||
1366 | int num_glyphs, |
||
1367 | cairo_text_cluster_flags_t cluster_flags, |
||
1368 | cairo_scaled_font_t *scaled_font) |
||
1369 | { |
||
1370 | cairo_scaled_font_subsets_glyph_t subset_glyph; |
||
1371 | cairo_glyph_t *cur_glyph; |
||
1372 | cairo_status_t status = CAIRO_STATUS_SUCCESS; |
||
1373 | int i; |
||
1374 | |||
1375 | /* If the cluster maps 1 glyph to 1 or more unicode characters, we |
||
1376 | * first try _map_glyph() with the unicode string to see if it can |
||
1377 | * use toUnicode to map our glyph to the unicode. This will fail |
||
1378 | * if the glyph is already mapped to a different unicode string. |
||
1379 | * |
||
1380 | * We also go through this path if no unicode mapping was |
||
1381 | * supplied (utf8_len < 0). |
||
1382 | * |
||
1383 | * Mapping a glyph to a zero length unicode string requires the |
||
1384 | * use of ActualText. |
||
1385 | */ |
||
1386 | if (num_glyphs == 1 && utf8_len != 0) { |
||
1387 | status = _cairo_scaled_font_subsets_map_glyph (pdf_operators->font_subsets, |
||
1388 | scaled_font, |
||
1389 | glyphs->index, |
||
1390 | utf8, |
||
1391 | utf8_len, |
||
1392 | &subset_glyph); |
||
1393 | if (unlikely (status)) |
||
1394 | return status; |
||
1395 | |||
1396 | if (subset_glyph.utf8_is_mapped || utf8_len < 0) { |
||
1397 | status = _cairo_pdf_operators_emit_glyph (pdf_operators, |
||
1398 | glyphs, |
||
1399 | &subset_glyph); |
||
1400 | if (unlikely (status)) |
||
1401 | return status; |
||
1402 | |||
1403 | return CAIRO_STATUS_SUCCESS; |
||
1404 | } |
||
1405 | } |
||
1406 | |||
1407 | if (pdf_operators->use_actual_text) { |
||
1408 | /* Fallback to using ActualText to map zero or more glyphs to a |
||
1409 | * unicode string. */ |
||
1410 | status = _cairo_pdf_operators_flush_glyphs (pdf_operators); |
||
1411 | if (unlikely (status)) |
||
1412 | return status; |
||
1413 | |||
1414 | status = _cairo_pdf_operators_begin_actualtext (pdf_operators, utf8, utf8_len); |
||
1415 | if (unlikely (status)) |
||
1416 | return status; |
||
1417 | } |
||
1418 | |||
1419 | cur_glyph = glyphs; |
||
1420 | /* XXX |
||
1421 | * If no glyphs, we should put *something* here for the text to be selectable. */ |
||
1422 | for (i = 0; i < num_glyphs; i++) { |
||
1423 | status = _cairo_scaled_font_subsets_map_glyph (pdf_operators->font_subsets, |
||
1424 | scaled_font, |
||
1425 | cur_glyph->index, |
||
1426 | NULL, -1, |
||
1427 | &subset_glyph); |
||
1428 | if (unlikely (status)) |
||
1429 | return status; |
||
1430 | |||
1431 | status = _cairo_pdf_operators_emit_glyph (pdf_operators, |
||
1432 | cur_glyph, |
||
1433 | &subset_glyph); |
||
1434 | if (unlikely (status)) |
||
1435 | return status; |
||
1436 | |||
1437 | if ((cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)) |
||
1438 | cur_glyph--; |
||
1439 | else |
||
1440 | cur_glyph++; |
||
1441 | } |
||
1442 | |||
1443 | if (pdf_operators->use_actual_text) { |
||
1444 | status = _cairo_pdf_operators_flush_glyphs (pdf_operators); |
||
1445 | if (unlikely (status)) |
||
1446 | return status; |
||
1447 | |||
1448 | status = _cairo_pdf_operators_end_actualtext (pdf_operators); |
||
1449 | } |
||
1450 | |||
1451 | return status; |
||
1452 | } |
||
1453 | |||
1454 | cairo_int_status_t |
||
1455 | _cairo_pdf_operators_show_text_glyphs (cairo_pdf_operators_t *pdf_operators, |
||
1456 | const char *utf8, |
||
1457 | int utf8_len, |
||
1458 | cairo_glyph_t *glyphs, |
||
1459 | int num_glyphs, |
||
1460 | const cairo_text_cluster_t *clusters, |
||
1461 | int num_clusters, |
||
1462 | cairo_text_cluster_flags_t cluster_flags, |
||
1463 | cairo_scaled_font_t *scaled_font) |
||
1464 | { |
||
1465 | cairo_status_t status; |
||
1466 | int i; |
||
1467 | cairo_matrix_t text_matrix, invert_y_axis; |
||
1468 | double x, y; |
||
1469 | const char *cur_text; |
||
1470 | cairo_glyph_t *cur_glyph; |
||
1471 | |||
1472 | pdf_operators->font_matrix_inverse = scaled_font->font_matrix; |
||
1473 | status = cairo_matrix_invert (&pdf_operators->font_matrix_inverse); |
||
1474 | if (status == CAIRO_STATUS_INVALID_MATRIX) |
||
1475 | return CAIRO_STATUS_SUCCESS; |
||
1476 | assert (status == CAIRO_STATUS_SUCCESS); |
||
1477 | |||
1478 | pdf_operators->is_new_text_object = FALSE; |
||
1479 | if (pdf_operators->in_text_object == FALSE) { |
||
1480 | status = _cairo_pdf_operators_begin_text (pdf_operators); |
||
1481 | if (unlikely (status)) |
||
1482 | return status; |
||
1483 | |||
1484 | /* Force Tm and Tf to be emitted when starting a new text |
||
1485 | * object.*/ |
||
1486 | pdf_operators->is_new_text_object = TRUE; |
||
1487 | } |
||
1488 | |||
1489 | cairo_matrix_init_scale (&invert_y_axis, 1, -1); |
||
1490 | text_matrix = scaled_font->scale; |
||
1491 | |||
1492 | /* Invert y axis in font space */ |
||
1493 | cairo_matrix_multiply (&text_matrix, &text_matrix, &invert_y_axis); |
||
1494 | |||
1495 | /* Invert y axis in device space */ |
||
1496 | cairo_matrix_multiply (&text_matrix, &invert_y_axis, &text_matrix); |
||
1497 | |||
1498 | if (pdf_operators->is_new_text_object || |
||
1499 | ! _cairo_matrix_scale_equal (&pdf_operators->text_matrix, &text_matrix)) |
||
1500 | { |
||
1501 | status = _cairo_pdf_operators_flush_glyphs (pdf_operators); |
||
1502 | if (unlikely (status)) |
||
1503 | return status; |
||
1504 | |||
1505 | x = glyphs[0].x; |
||
1506 | y = glyphs[0].y; |
||
1507 | cairo_matrix_transform_point (&pdf_operators->cairo_to_pdf, &x, &y); |
||
1508 | text_matrix.x0 = x; |
||
1509 | text_matrix.y0 = y; |
||
1510 | status = _cairo_pdf_operators_set_text_matrix (pdf_operators, &text_matrix); |
||
1511 | if (status == CAIRO_STATUS_INVALID_MATRIX) |
||
1512 | return CAIRO_STATUS_SUCCESS; |
||
1513 | if (unlikely (status)) |
||
1514 | return status; |
||
1515 | } |
||
1516 | |||
1517 | if (num_clusters > 0) { |
||
1518 | cur_text = utf8; |
||
1519 | if ((cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)) |
||
1520 | cur_glyph = glyphs + num_glyphs; |
||
1521 | else |
||
1522 | cur_glyph = glyphs; |
||
1523 | for (i = 0; i < num_clusters; i++) { |
||
1524 | if ((cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)) |
||
1525 | cur_glyph -= clusters[i].num_glyphs; |
||
1526 | status = _cairo_pdf_operators_emit_cluster (pdf_operators, |
||
1527 | cur_text, |
||
1528 | clusters[i].num_bytes, |
||
1529 | cur_glyph, |
||
1530 | clusters[i].num_glyphs, |
||
1531 | cluster_flags, |
||
1532 | scaled_font); |
||
1533 | if (unlikely (status)) |
||
1534 | return status; |
||
1535 | |||
1536 | cur_text += clusters[i].num_bytes; |
||
1537 | if (!(cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)) |
||
1538 | cur_glyph += clusters[i].num_glyphs; |
||
1539 | } |
||
1540 | } else { |
||
1541 | for (i = 0; i < num_glyphs; i++) { |
||
1542 | status = _cairo_pdf_operators_emit_cluster (pdf_operators, |
||
1543 | NULL, |
||
1544 | -1, /* no unicode string available */ |
||
1545 | &glyphs[i], |
||
1546 | 1, |
||
1547 | FALSE, |
||
1548 | scaled_font); |
||
1549 | if (unlikely (status)) |
||
1550 | return status; |
||
1551 | } |
||
1552 | } |
||
1553 | |||
1554 | return _cairo_output_stream_get_status (pdf_operators->stream); |
||
1555 | } |
||
1556 | |||
1557 | #endif /* CAIRO_HAS_PDF_OPERATORS */>>>>>> |