1 /* Copyright (C) 1997, 2000 artofcode LLC. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify it
4 under the terms of the GNU General Public License as published by the
5 Free Software Foundation; either version 2 of the License, or (at your
6 option) any later version.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program; if not, write to the Free Software Foundation, Inc.,
15 59 Temple Place, Suite 330, Boston, MA, 02111-1307.
16
17 */
18
19 /*$Id: gdevpdfd.c,v 1.16.2.1.2.1 2003/01/17 00:49:01 giles Exp $ */
20 /* Path drawing procedures for pdfwrite driver */
21 #include "math_.h"
22 #include "gx.h"
23 #include "gxdevice.h"
24 #include "gxfixed.h"
25 #include "gxistate.h"
26 #include "gxpaint.h"
27 #include "gzpath.h"
28 #include "gzcpath.h"
29 #include "gdevpdfx.h"
30 #include "gdevpdfg.h"
31
32 /* ---------------- Drawing ---------------- */
33
34 /* Fill a rectangle. */
35 int
gdev_pdf_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)36 gdev_pdf_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
37 gx_color_index color)
38 {
39 gx_device_pdf *pdev = (gx_device_pdf *) dev;
40 int code;
41
42 /* Make a special check for the initial fill with white, */
43 /* which shouldn't cause the page to be opened. */
44 if (color == pdev->white && !is_in_page(pdev))
45 return 0;
46 code = pdf_open_page(pdev, PDF_IN_STREAM);
47 if (code < 0)
48 return code;
49 /* Make sure we aren't being clipped. */
50 pdf_put_clip_path(pdev, NULL);
51 pdf_set_pure_color(pdev, color, &pdev->fill_color,
52 &psdf_set_fill_color_commands);
53 pprintd4(pdev->strm, "%d %d %d %d re f\n", x, y, w, h);
54 return 0;
55 }
56
57 /* ---------------- Path drawing ---------------- */
58
59 /* ------ Vector device implementation ------ */
60
61 private int
pdf_setlinewidth(gx_device_vector * vdev,floatp width)62 pdf_setlinewidth(gx_device_vector * vdev, floatp width)
63 {
64 /* Acrobat Reader doesn't accept negative line widths. */
65 return psdf_setlinewidth(vdev, fabs(width));
66 }
67
68 private int
pdf_setfillcolor(gx_device_vector * vdev,const gx_drawing_color * pdc)69 pdf_setfillcolor(gx_device_vector * vdev, const gx_drawing_color * pdc)
70 {
71 gx_device_pdf *const pdev = (gx_device_pdf *)vdev;
72
73 return pdf_set_drawing_color(pdev, pdc, &pdev->fill_color,
74 &psdf_set_fill_color_commands);
75 }
76
77 private int
pdf_setstrokecolor(gx_device_vector * vdev,const gx_drawing_color * pdc)78 pdf_setstrokecolor(gx_device_vector * vdev, const gx_drawing_color * pdc)
79 {
80 gx_device_pdf *const pdev = (gx_device_pdf *)vdev;
81
82 return pdf_set_drawing_color(pdev, pdc, &pdev->stroke_color,
83 &psdf_set_stroke_color_commands);
84 }
85
86 private int
pdf_dorect(gx_device_vector * vdev,fixed x0,fixed y0,fixed x1,fixed y1,gx_path_type_t type)87 pdf_dorect(gx_device_vector * vdev, fixed x0, fixed y0, fixed x1, fixed y1,
88 gx_path_type_t type)
89 {
90 fixed xmax = int2fixed(vdev->width), ymax = int2fixed(vdev->height);
91 fixed xmin = 0, ymin = 0;
92
93 /*
94 * If we're doing a stroke operation, expand the checking box by the
95 * stroke width.
96 */
97 if (type & gx_path_type_stroke) {
98 double w = vdev->state.line_params.half_width;
99 double xw = w * (fabs(vdev->state.ctm.xx) + fabs(vdev->state.ctm.yx));
100 double yw = w * (fabs(vdev->state.ctm.xy) + fabs(vdev->state.ctm.yy));
101
102 xmin = -(float2fixed(xw) + fixed_1);
103 xmax -= xmin;
104 ymin = -(float2fixed(yw) + fixed_1);
105 ymax -= ymin;
106 }
107 if (!(type & gx_path_type_clip) &&
108 (x0 > xmax || x1 < xmin || y0 > ymax || y1 < ymin ||
109 x0 > x1 || y0 > y1)
110 )
111 return 0; /* nothing to fill or stroke */
112 /*
113 * Clamp coordinates to avoid tripping over Acrobat Reader's limit
114 * of 32K on user coordinate values.
115 */
116 if (x0 < xmin)
117 x0 = xmin;
118 if (x1 > xmax)
119 x1 = xmax;
120 if (y0 < ymin)
121 y0 = ymin;
122 if (y1 > ymax)
123 y1 = ymax;
124 return psdf_dorect(vdev, x0, y0, x1, y1, type);
125 }
126
127 private int
pdf_endpath(gx_device_vector * vdev,gx_path_type_t type)128 pdf_endpath(gx_device_vector * vdev, gx_path_type_t type)
129 {
130 return 0; /* always handled by caller */
131 }
132
133 const gx_device_vector_procs pdf_vector_procs = {
134 /* Page management */
135 NULL,
136 /* Imager state */
137 pdf_setlinewidth,
138 psdf_setlinecap,
139 psdf_setlinejoin,
140 psdf_setmiterlimit,
141 psdf_setdash,
142 psdf_setflat,
143 psdf_setlogop,
144 /* Other state */
145 pdf_setfillcolor,
146 pdf_setstrokecolor,
147 /* Paths */
148 psdf_dopath,
149 pdf_dorect,
150 psdf_beginpath,
151 psdf_moveto,
152 psdf_lineto,
153 psdf_curveto,
154 psdf_closepath,
155 pdf_endpath
156 };
157
158 /* ------ Utilities ------ */
159
160 /* Test whether we will need to put the clipping path. */
161 bool
pdf_must_put_clip_path(gx_device_pdf * pdev,const gx_clip_path * pcpath)162 pdf_must_put_clip_path(gx_device_pdf * pdev, const gx_clip_path * pcpath)
163 {
164 if (pcpath == NULL)
165 return pdev->clip_path_id != pdev->no_clip_path_id;
166 if (pdev->clip_path_id == pcpath->id)
167 return false;
168 if (gx_cpath_includes_rectangle(pcpath, fixed_0, fixed_0,
169 int2fixed(pdev->width),
170 int2fixed(pdev->height))
171 )
172 return pdev->clip_path_id != pdev->no_clip_path_id;
173 return true;
174 }
175
176 /* Put a clipping path on the output file. */
177 int
pdf_put_clip_path(gx_device_pdf * pdev,const gx_clip_path * pcpath)178 pdf_put_clip_path(gx_device_pdf * pdev, const gx_clip_path * pcpath)
179 {
180 stream *s = pdev->strm;
181
182 if (pcpath == NULL) {
183 if (pdev->clip_path_id == pdev->no_clip_path_id)
184 return 0;
185 stream_puts(s, "Q\nq\n");
186 pdev->clip_path_id = pdev->no_clip_path_id;
187 } else {
188 if (pdev->clip_path_id == pcpath->id)
189 return 0;
190 if (gx_cpath_includes_rectangle(pcpath, fixed_0, fixed_0,
191 int2fixed(pdev->width),
192 int2fixed(pdev->height))
193 ) {
194 if (pdev->clip_path_id == pdev->no_clip_path_id)
195 return 0;
196 stream_puts(s, "Q\nq\n");
197 pdev->clip_path_id = pdev->no_clip_path_id;
198 } else {
199 gdev_vector_dopath_state_t state;
200 gs_cpath_enum cenum;
201 gs_fixed_point vs[3];
202 int pe_op;
203
204 stream_puts(s, "Q\nq\n");
205 gdev_vector_dopath_init(&state, (gx_device_vector *)pdev,
206 gx_path_type_fill, NULL);
207 /*
208 * We have to break 'const' here because the clip path
209 * enumeration logic uses some internal mark bits.
210 * This is very unfortunate, but until we can come up with
211 * a better algorithm, it's necessary.
212 */
213 gx_cpath_enum_init(&cenum, (gx_clip_path *) pcpath);
214 while ((pe_op = gx_cpath_enum_next(&cenum, vs)) > 0)
215 gdev_vector_dopath_segment(&state, pe_op, vs);
216 pprints1(s, "%s n\n", (pcpath->rule <= 0 ? "W" : "W*"));
217 if (pe_op < 0)
218 return pe_op;
219 pdev->clip_path_id = pcpath->id;
220 }
221 }
222 pdev->text.font = 0;
223 if (pdev->context == PDF_IN_TEXT)
224 pdev->context = PDF_IN_STREAM;
225 pdf_reset_graphics(pdev);
226 return 0;
227 }
228
229 /*
230 * Compute the scaling to ensure that user coordinates for a path are within
231 * Acrobat's range. Return true if scaling was needed. In this case, the
232 * CTM will be multiplied by *pscale, and all coordinates will be divided by
233 * *pscale.
234 */
235 private bool
make_path_scaling(const gx_device_pdf * pdev,gx_path * ppath,floatp prescale,double * pscale)236 make_path_scaling(const gx_device_pdf *pdev, gx_path *ppath,
237 floatp prescale, double *pscale)
238 {
239 gs_fixed_rect bbox;
240 double bmin, bmax;
241
242 gx_path_bbox(ppath, &bbox);
243 bmin = min(bbox.p.x / pdev->scale.x, bbox.p.y / pdev->scale.y) * prescale;
244 bmax = max(bbox.q.x / pdev->scale.x, bbox.q.y / pdev->scale.y) * prescale;
245 if (bmin <= int2fixed(-MAX_USER_COORD) ||
246 bmax > int2fixed(MAX_USER_COORD)
247 ) {
248 /* Rescale the path. */
249 *pscale = max(bmin / int2fixed(-MAX_USER_COORD),
250 bmax / int2fixed(MAX_USER_COORD));
251 return true;
252 } else {
253 *pscale = 1;
254 return false;
255 }
256 #undef MAX_USER_COORD
257 }
258
259 /* ------ Driver procedures ------ */
260
261 /* Fill a path. */
262 int
gdev_pdf_fill_path(gx_device * dev,const gs_imager_state * pis,gx_path * ppath,const gx_fill_params * params,const gx_drawing_color * pdcolor,const gx_clip_path * pcpath)263 gdev_pdf_fill_path(gx_device * dev, const gs_imager_state * pis, gx_path * ppath,
264 const gx_fill_params * params,
265 const gx_drawing_color * pdcolor, const gx_clip_path * pcpath)
266 {
267 gx_device_pdf *pdev = (gx_device_pdf *) dev;
268 int code;
269 /*
270 * HACK: we fill an empty path in order to set the clipping path
271 * and the color for writing text. If it weren't for this, we
272 * could detect and skip empty paths before putting out the clip
273 * path or the color. We also clip with an empty path in order
274 * to advance currentpoint for show operations without actually
275 * drawing anything.
276 */
277 bool have_path;
278
279 /*
280 * Check for an empty clipping path.
281 */
282 if (pcpath) {
283 gs_fixed_rect box;
284
285 gx_cpath_outer_box(pcpath, &box);
286 if (box.p.x >= box.q.x || box.p.y >= box.q.y)
287 return 0; /* empty clipping path */
288 }
289 code = pdf_prepare_fill(pdev, pis);
290 if (code < 0)
291 return code;
292 if (gx_dc_is_pure(pdcolor)) {
293 /*
294 * Make a special check for the initial fill with white,
295 * which shouldn't cause the page to be opened.
296 */
297 if (gx_dc_pure_color(pdcolor) == pdev->white && !is_in_page(pdev))
298 return 0;
299 }
300 have_path = !gx_path_is_void(ppath);
301 if (have_path || pdev->context == PDF_IN_NONE ||
302 pdf_must_put_clip_path(pdev, pcpath)
303 ) {
304 code = pdf_open_page(pdev, PDF_IN_STREAM);
305 if (code < 0)
306 return code;
307 }
308 pdf_put_clip_path(pdev, pcpath);
309 if (pdf_setfillcolor((gx_device_vector *)pdev, pdcolor) < 0)
310 return gx_default_fill_path(dev, pis, ppath, params, pdcolor,
311 pcpath);
312 if (have_path) {
313 stream *s = pdev->strm;
314 double scale;
315 gs_matrix smat;
316 gs_matrix *psmat = NULL;
317
318 if (params->flatness != pdev->state.flatness) {
319 pprintg1(s, "%g i\n", params->flatness);
320 pdev->state.flatness = params->flatness;
321 }
322 if (make_path_scaling(pdev, ppath, 1.0, &scale)) {
323 gs_make_scaling(pdev->scale.x * scale, pdev->scale.y * scale,
324 &smat);
325 pdf_put_matrix(pdev, "q ", &smat, "cm\n");
326 psmat = &smat;
327 }
328 gdev_vector_dopath((gx_device_vector *)pdev, ppath,
329 gx_path_type_fill | gx_path_type_optimize,
330 psmat);
331 stream_puts(s, (params->rule < 0 ? "f\n" : "f*\n"));
332 if (psmat)
333 stream_puts(s, "Q\n");
334 }
335 return 0;
336 }
337
338 /* Stroke a path. */
339 int
gdev_pdf_stroke_path(gx_device * dev,const gs_imager_state * pis,gx_path * ppath,const gx_stroke_params * params,const gx_drawing_color * pdcolor,const gx_clip_path * pcpath)340 gdev_pdf_stroke_path(gx_device * dev, const gs_imager_state * pis,
341 gx_path * ppath, const gx_stroke_params * params,
342 const gx_drawing_color * pdcolor, const gx_clip_path * pcpath)
343 {
344 gx_device_pdf *pdev = (gx_device_pdf *) dev;
345 stream *s;
346 int code;
347 double scale, path_scale;
348 bool set_ctm;
349 gs_matrix mat;
350 double prescale = 1;
351
352 if (gx_path_is_void(ppath))
353 return 0; /* won't mark the page */
354 code = pdf_prepare_stroke(pdev, pis);
355 if (code < 0)
356 return code;
357 code = pdf_open_page(pdev, PDF_IN_STREAM);
358 if (code < 0)
359 return code;
360 /*
361 * If the CTM is not uniform, stroke width depends on angle.
362 * We'd like to avoid resetting the CTM, so we check for uniform
363 * CTMs explicitly. Note that in PDF, unlike PostScript, it is
364 * the CTM at the time of the stroke operation, not the CTM at
365 * the time the path was constructed, that is used for transforming
366 * the points of the path; so if we have to reset the CTM, we must
367 * do it before constructing the path, and inverse-transform all
368 * the coordinates.
369 */
370 set_ctm = (bool)gdev_vector_stroke_scaling((gx_device_vector *)pdev,
371 pis, &scale, &mat);
372 if (set_ctm) {
373 /*
374 * We want a scaling factor that will bring the largest reasonable
375 * user coordinate within bounds. We choose a factor based on the
376 * minor axis of the transformation. Thanks to Raph Levien for
377 * the following formula.
378 */
379 double a = mat.xx, b = mat.xy, c = mat.yx, d = mat.yy;
380 double u = fabs(a * d - b * c);
381 double v = a * a + b * b + c * c + d * d;
382 double minor = (sqrt(v + 2 * u) - sqrt(v - 2 * u)) * 0.5;
383
384 prescale = (minor == 0 || minor > 1 ? 1 : 1 / minor);
385 }
386 if (make_path_scaling(pdev, ppath, prescale, &path_scale)) {
387 scale /= path_scale;
388 if (set_ctm)
389 gs_matrix_scale(&mat, path_scale, path_scale, &mat);
390 else {
391 gs_make_scaling(path_scale, path_scale, &mat);
392 set_ctm = true;
393 }
394 }
395 pdf_put_clip_path(pdev, pcpath);
396 code = gdev_vector_prepare_stroke((gx_device_vector *)pdev, pis, params,
397 pdcolor, scale);
398 if (code < 0)
399 return gx_default_stroke_path(dev, pis, ppath, params, pdcolor,
400 pcpath);
401
402 if (set_ctm)
403 pdf_put_matrix(pdev, "q ", &mat, "cm\n");
404 code = gdev_vector_dopath((gx_device_vector *)pdev, ppath,
405 gx_path_type_stroke | gx_path_type_optimize,
406 (set_ctm ? &mat : (const gs_matrix *)0));
407 if (code < 0)
408 return code;
409 s = pdev->strm;
410 stream_puts(s, (code ? "s" : "S"));
411 stream_puts(s, (set_ctm ? " Q\n" : "\n"));
412 return 0;
413 }
414