1/*---------------------------------------------------------------------------*
2 |              PDFlib - A library for generating PDF on the fly             |
3 +---------------------------------------------------------------------------+
4 | Copyright (c) 1997-2004 Thomas Merz and PDFlib GmbH. All rights reserved. |
5 +---------------------------------------------------------------------------+
6 |                                                                           |
7 |    This software is subject to the PDFlib license. It is NOT in the       |
8 |    public domain. Extended versions and commercial licenses are           |
9 |    available, please check http://www.pdflib.com.                         |
10 |                                                                           |
11 *---------------------------------------------------------------------------*/
12
13/* $Id: p_draw.c 14574 2005-10-29 16:27:43Z bonefish $
14 *
15 * PDFlib drawing routines
16 *
17 */
18
19#include "p_intern.h"
20
21/* Path segment operators */
22
23void
24pdf_begin_path(PDF *p)
25{
26    if (PDF_GET_STATE(p) == pdf_state_path)
27	return;
28
29    pdf_end_text(p);
30    p->contents = c_path;
31    PDF_PUSH_STATE(p, "pdf_begin_path", pdf_state_path);
32}
33
34void
35pdf_end_path(PDF *p)
36{
37    p->contents = c_page;
38    PDF_POP_STATE(p, "pdf_end_path");
39
40    p->gstate[p->sl].x = (float) 0.0;
41    p->gstate[p->sl].y = (float) 0.0;
42}
43
44/* ----------------- Basic functions for API functions --------------*/
45
46void
47pdf__moveto(PDF *p, float x, float y)
48{
49    p->gstate[p->sl].startx = p->gstate[p->sl].x = x;
50    p->gstate[p->sl].starty = p->gstate[p->sl].y = y;
51
52    pdf_begin_path(p);
53    pdc_printf(p->out, "%f %f m\n", x, y);
54}
55
56void
57pdf__rmoveto(PDF *p, float x, float y)
58{
59    float x_0 = p->gstate[p->sl].x;
60    float y_0 = p->gstate[p->sl].y;
61
62    pdf__moveto(p, x_0 + x, y_0 + y);
63}
64
65void
66pdf__lineto(PDF *p, float x, float y)
67{
68    pdc_printf(p->out, "%f %f l\n", x, y);
69
70    p->gstate[p->sl].x = x;
71    p->gstate[p->sl].y = y;
72}
73
74void
75pdf__rlineto(PDF *p, float x, float y)
76{
77    float x_0 = p->gstate[p->sl].x;
78    float y_0 = p->gstate[p->sl].y;
79
80    pdf__lineto(p, x_0 + x, y_0 + y);
81}
82
83void
84pdf__curveto(PDF *p,
85    float x_1, float y_1,
86    float x_2, float y_2,
87    float x_3, float y_3)
88{
89    if (x_2 == x_3 && y_2 == y_3)  /* second c.p. coincides with final point */
90        pdc_printf(p->out, "%f %f %f %f y\n", x_1, y_1, x_3, y_3);
91
92    else                        /* general case with four distinct points */
93        pdc_printf(p->out, "%f %f %f %f %f %f c\n",
94                    x_1, y_1, x_2, y_2, x_3, y_3);
95
96    p->gstate[p->sl].x = x_3;
97    p->gstate[p->sl].y = y_3;
98}
99
100void
101pdf__rcurveto(PDF *p,
102    float x_1, float y_1,
103    float x_2, float y_2,
104    float x_3, float y_3)
105{
106    float x_0 = p->gstate[p->sl].x;
107    float y_0 = p->gstate[p->sl].y;
108
109    pdf__curveto(p, x_0 + x_1, y_0 + y_1,
110                    x_0 + x_2, y_0 + y_2,
111                    x_0 + x_3, y_0 + y_3);
112}
113
114void
115pdf__rrcurveto(PDF *p,
116    float x_1, float y_1,
117    float x_2, float y_2,
118    float x_3, float y_3)
119{
120    pdf__rcurveto(p, x_1, y_1,
121                     x_1 + x_2, y_1 + y_2,
122                     x_1 + x_2 + x_3, y_1 + y_2 + y_3);
123}
124
125void
126pdf__hvcurveto(PDF *p, float x_1, float x_2, float y_2, float y_3)
127{
128    pdf__rrcurveto(p, x_1, 0, x_2, y_2, 0, y_3);
129}
130
131void
132pdf__vhcurveto(PDF *p, float y_1, float x_2, float y_2, float x_3)
133{
134    pdf__rrcurveto(p, 0, y_1, x_2, y_2, x_3, 0);
135}
136
137void
138pdf__rect(PDF *p, float x, float y, float width, float height)
139{
140    p->gstate[p->sl].startx = p->gstate[p->sl].x = x;
141    p->gstate[p->sl].starty = p->gstate[p->sl].y = y;
142
143    pdf_begin_path(p);
144    pdc_printf(p->out, "%f %f %f %f re\n", x, y, width, p->ydirection * height);
145}
146
147/* 4/3 * (1-cos 45�)/sin 45� = 4/3 * sqrt(2) - 1 */
148#define ARC_MAGIC       ((float) 0.552284749)
149
150static void
151pdf_short_arc(PDF *p, float x, float y, float r, float alpha, float beta)
152{
153    float bcp;
154    float cos_alpha, cos_beta, sin_alpha, sin_beta;
155
156    alpha = (float) (alpha * PDC_M_PI / 180);
157    beta  = (float) (beta * PDC_M_PI / 180);
158
159    /* This formula yields ARC_MAGIC for alpha == 0, beta == 90 degrees */
160    bcp = (float) (4.0/3 * (1 - cos((beta - alpha)/2)) / sin((beta - alpha)/2));
161
162    sin_alpha = (float) sin(alpha);
163    sin_beta = (float) sin(beta);
164    cos_alpha = (float) cos(alpha);
165    cos_beta = (float) cos(beta);
166
167    pdf__curveto(p,
168                x + r * (cos_alpha - bcp * sin_alpha),          /* p1 */
169                y + r * (sin_alpha + bcp * cos_alpha),
170                x + r * (cos_beta + bcp * sin_beta),            /* p2 */
171                y + r * (sin_beta - bcp * cos_beta),
172                x + r * cos_beta,                               /* p3 */
173                y + r * sin_beta);
174}
175
176static void
177pdf_orient_arc(PDF *p, float x, float y, float r, float alpha, float beta,
178               float orient)
179{
180    float rad_a = (float) (alpha * PDC_M_PI / 180);
181    float startx = (float) (x + r * cos(rad_a));
182    float starty = (float) (y + r * sin(rad_a));
183
184    if (r < 0)
185        pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
186            "r", pdc_errprintf(p->pdc, "%f", r), 0, 0);
187
188    if (p->contents != c_path)
189        pdf__moveto(p, startx, starty);
190    else if ((p->gstate[p->sl].x != startx || p->gstate[p->sl].y != starty))
191        pdf__lineto(p, startx, starty);
192
193    if (orient > 0)
194    {
195        while (beta < alpha)
196            beta += 360;
197
198        if (alpha == beta)
199            return;
200
201        while (beta - alpha > 90)
202        {
203            pdf_short_arc(p, x, y, r, alpha, alpha + 90);
204            alpha += 90;
205        }
206    }
207    else
208    {
209        while (alpha < beta)
210            alpha += 360;
211
212        if (alpha == beta)
213            return;
214
215        while (alpha - beta > 90)
216        {
217            pdf_short_arc(p, x, y, r, alpha, alpha - 90);
218            alpha -= 90;
219        }
220    }
221
222    if (alpha != beta)
223        pdf_short_arc(p, x, y, r, alpha, beta);
224}
225
226void
227pdf__arc(PDF *p, float x, float y, float r, float alpha, float beta)
228{
229    pdf_orient_arc(p, x, y, r,
230                   p->ydirection * alpha, p->ydirection * beta, p->ydirection);
231}
232
233void
234pdf__arcn(PDF *p, float x, float y, float r, float alpha, float beta)
235{
236    pdf_orient_arc(p, x, y, r,
237                   p->ydirection * alpha, p->ydirection * beta, -p->ydirection);
238}
239
240void
241pdf__circle(PDF *p, float x, float y, float r)
242{
243    if (r < 0)
244        pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
245            "r", pdc_errprintf(p->pdc, "%f", r), 0, 0);
246
247    /*
248     * pdf_begin_path() not required since we descend to other
249     * path segment functions.
250     */
251
252    /* draw four Bezier curves to approximate a circle */
253    pdf__moveto(p, x + r, y);
254    pdf__curveto(p, x + r, y + r*ARC_MAGIC, x + r*ARC_MAGIC, y + r, x, y + r);
255    pdf__curveto(p, x - r*ARC_MAGIC, y + r, x - r, y + r*ARC_MAGIC, x - r, y);
256    pdf__curveto(p, x - r, y - r*ARC_MAGIC, x - r*ARC_MAGIC, y - r, x, y - r);
257    pdf__curveto(p, x + r*ARC_MAGIC, y - r, x + r, y - r*ARC_MAGIC, x + r, y);
258
259    pdf__closepath(p);
260}
261
262void
263pdf__closepath(PDF *p)
264{
265    pdc_puts(p->out, "h\n");
266
267    p->gstate[p->sl].x = p->gstate[p->sl].startx;
268    p->gstate[p->sl].y = p->gstate[p->sl].starty;
269}
270
271void
272pdf__endpath(PDF *p)
273{
274    pdc_puts(p->out, "n\n");
275    pdf_end_path(p);
276}
277
278void
279pdf__stroke(PDF *p)
280{
281    pdc_puts(p->out, "S\n");
282    pdf_end_path(p);
283}
284
285void
286pdf__closepath_stroke(PDF *p)
287{
288    pdc_puts(p->out, "s\n");
289    pdf_end_path(p);
290}
291
292void
293pdf__fill(PDF *p)
294{
295    if (p->fillrule == pdf_fill_winding)
296        pdc_puts(p->out, "f\n");
297    else if (p->fillrule == pdf_fill_evenodd)
298        pdc_puts(p->out, "f*\n");
299
300    pdf_end_path(p);
301}
302
303void
304pdf__fill_stroke(PDF *p)
305{
306    if (p->fillrule == pdf_fill_winding)
307        pdc_puts(p->out, "B\n");
308    else if (p->fillrule == pdf_fill_evenodd)
309        pdc_puts(p->out, "B*\n");
310
311    pdf_end_path(p);
312}
313
314void
315pdf__closepath_fill_stroke(PDF *p)
316{
317    if (p->fillrule == pdf_fill_winding)
318        pdc_puts(p->out, "b\n");
319    else if (p->fillrule == pdf_fill_evenodd)
320        pdc_puts(p->out, "b*\n");
321
322    pdf_end_path(p);
323}
324
325void
326pdf__clip(PDF *p)
327{
328    if (p->fillrule == pdf_fill_winding)
329        pdc_puts(p->out, "W n\n");
330    else if (p->fillrule == pdf_fill_evenodd)
331        pdc_puts(p->out, "W* n\n");
332
333    pdf_end_path(p);
334}
335
336/* --------------------------- API functions ------------------------------*/
337
338PDFLIB_API void PDFLIB_CALL
339PDF_moveto(PDF *p, float x, float y)
340{
341    static const char fn[] = "PDF_moveto";
342
343    if (pdf_enter_api(p, fn,
344        (pdf_state) (pdf_state_content | pdf_state_path),
345	"(p[%p], %g, %g)\n", (void *) p, x, y))
346    {
347        pdf__moveto(p, x, y);
348    }
349}
350
351PDFLIB_API void PDFLIB_CALL
352PDF_rmoveto(PDF *p, float x, float y)
353{
354    static const char fn[] = "PDF_rmoveto";
355
356    if (pdf_enter_api(p, fn,
357        (pdf_state) (pdf_state_content | pdf_state_path),
358	"(p[%p], %g, %g)\n", (void *) p, x, y))
359    {
360        pdf__rmoveto(p, x, y);
361    }
362}
363
364PDFLIB_API void PDFLIB_CALL
365PDF_lineto(PDF *p, float x, float y)
366{
367    static const char fn[] = "PDF_lineto";
368
369    if (pdf_enter_api(p, fn, pdf_state_path, "(p[%p], %g, %g)\n",
370	(void *) p, x, y))
371    {
372        pdf__lineto(p, x, y);
373    }
374}
375
376PDFLIB_API void PDFLIB_CALL
377PDF_rlineto(PDF *p, float x, float y)
378{
379    static const char fn[] = "PDF_rlineto";
380
381    if (pdf_enter_api(p, fn, pdf_state_path, "(p[%p], %g, %g)\n",
382        (void *) p, x, y))
383    {
384        pdf__rlineto(p, x, y);
385    }
386}
387
388PDFLIB_API void PDFLIB_CALL
389PDF_curveto(PDF *p,
390    float x_1, float y_1, float x_2, float y_2, float x_3, float y_3)
391{
392    static const char fn[] = "PDF_curveto";
393
394    if (pdf_enter_api(p, fn, pdf_state_path,
395	"(p[%p], %g, %g, %g, %g, %g, %g)\n",
396	(void *) p, x_1, y_1, x_2, y_2, x_3, y_3))
397    {
398        pdf__curveto(p, x_1, y_1, x_2, y_2, x_3, y_3);
399    }
400}
401
402PDFLIB_API void PDFLIB_CALL
403PDF_rcurveto(PDF *p,
404    float x_1, float y_1, float x_2, float y_2, float x_3, float y_3)
405{
406    static const char fn[] = "PDF_rcurveto";
407
408    if (pdf_enter_api(p, fn, pdf_state_path,
409	"(p[%p], %g, %g, %g, %g, %g, %g)\n",
410	(void *) p, x_1, y_1, x_2, y_2, x_3, y_3))
411    {
412        pdf__rcurveto(p, x_1, y_1, x_2, y_2, x_3, y_3);
413    }
414}
415
416PDFLIB_API void PDFLIB_CALL
417PDF_rect(PDF *p, float x, float y, float width, float height)
418{
419    static const char fn[] = "PDF_rect";
420
421    if (pdf_enter_api(p, fn,
422        (pdf_state) (pdf_state_content | pdf_state_path),
423        "(p[%p], %g, %g, %g, %g)\n", (void *) p, x, y, width, height))
424    {
425        pdf__rect(p, x, y, width, height);
426    }
427}
428
429PDFLIB_API void PDFLIB_CALL
430PDF_arc(PDF *p, float x, float y, float r, float alpha, float beta)
431{
432    static const char fn[] = "PDF_arc";
433
434    if (pdf_enter_api(p, fn,
435        (pdf_state) (pdf_state_content | pdf_state_path),
436	"(p[%p], %g, %g, %g, %g, %g)\n", (void *) p, x, y, r, alpha, beta))
437    {
438	pdf__arc(p, x, y, r, alpha, beta);
439    }
440}
441
442PDFLIB_API void PDFLIB_CALL
443PDF_arcn(PDF *p, float x, float y, float r, float alpha, float beta)
444{
445    static const char fn[] = "PDF_arcn";
446
447    if (pdf_enter_api(p, fn,
448        (pdf_state) (pdf_state_content | pdf_state_path),
449	"(p[%p], %g, %g, %g, %g, %g)\n", (void *) p, x, y, r, alpha, beta))
450    {
451	pdf__arcn(p, x, y, r, alpha, beta);
452    }
453}
454
455PDFLIB_API void PDFLIB_CALL
456PDF_circle(PDF *p, float x, float y, float r)
457{
458    static const char fn[] = "PDF_circle";
459
460    if (pdf_enter_api(p, fn,
461        (pdf_state) (pdf_state_content | pdf_state_path),
462	"(p[%p], %g, %g, %g)\n", (void *) p, x, y, r))
463    {
464        pdf__circle(p, x, y, r);
465    }
466}
467
468PDFLIB_API void PDFLIB_CALL
469PDF_closepath(PDF *p)
470{
471    static const char fn[] = "PDF_closepath";
472
473    if (pdf_enter_api(p, fn, pdf_state_path, "(p[%p])\n", (void *) p))
474    {
475        pdf__closepath(p);
476    }
477}
478
479PDFLIB_API void PDFLIB_CALL
480PDF_endpath(PDF *p)
481{
482    static const char fn[] = "PDF_endpath";
483
484    if (pdf_enter_api(p, fn, pdf_state_path, "(p[%p])\n", (void *) p))
485    {
486        pdf__endpath(p);
487    }
488}
489
490PDFLIB_API void PDFLIB_CALL
491PDF_stroke(PDF *p)
492{
493    static const char fn[] = "PDF_stroke";
494
495    if (pdf_enter_api(p, fn, pdf_state_path, "(p[%p])\n", (void *) p))
496    {
497        pdf__stroke(p);
498    }
499}
500
501PDFLIB_API void PDFLIB_CALL
502PDF_closepath_stroke(PDF *p)
503{
504    static const char fn[] = "PDF_closepath_stroke";
505
506    if (pdf_enter_api(p, fn, pdf_state_path, "(p[%p])\n", (void *) p))
507    {
508        pdf__closepath_stroke(p);
509    }
510}
511
512PDFLIB_API void PDFLIB_CALL
513PDF_fill(PDF *p)
514{
515    static const char fn[] = "PDF_fill";
516
517    if (pdf_enter_api(p, fn, pdf_state_path, "(p[%p])\n", (void *) p))
518    {
519        pdf__fill(p);
520    }
521}
522
523PDFLIB_API void PDFLIB_CALL
524PDF_fill_stroke(PDF *p)
525{
526    static const char fn[] = "PDF_fill_stroke";
527
528    if (pdf_enter_api(p, fn, pdf_state_path, "(p[%p])\n", (void *) p))
529    {
530        pdf__fill_stroke(p);
531    }
532}
533
534PDFLIB_API void PDFLIB_CALL
535PDF_closepath_fill_stroke(PDF *p)
536{
537    static const char fn[] = "PDF_closepath_fill_stroke";
538
539    if (pdf_enter_api(p, fn, pdf_state_path, "(p[%p])\n", (void *) p))
540    {
541        pdf__closepath_fill_stroke(p);
542    }
543}
544
545PDFLIB_API void PDFLIB_CALL
546PDF_clip(PDF *p)
547{
548    static const char fn[] = "PDF_clip";
549
550    if (pdf_enter_api(p, fn, pdf_state_path, "(p[%p])\n", (void *) p))
551    {
552        pdf__clip(p);
553    }
554}
555