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_gstate.c 14574 2005-10-29 16:27:43Z bonefish $
14 *
15 * PDFlib routines dealing with the graphics states
16 *
17 */
18
19#include "p_intern.h"
20
21/* ---------------------- matrix functions ----------------------------- */
22
23void
24pdf_concat_raw(PDF *p, pdc_matrix *m)
25{
26    if (pdc_is_identity_matrix(m))
27	return;
28
29    pdf_end_text(p);
30
31    pdc_printf(p->out, "%f %f %f %f %f %f cm\n",
32		    m->a, m->b, m->c, m->d, m->e, m->f);
33
34    pdc_multiply_matrix(m, &p->gstate[p->sl].ctm);
35}
36
37void
38pdf_concat_raw_ob(PDF *p, pdc_matrix *m, pdc_bool blind)
39{
40    if (!blind)
41        pdf_concat_raw(p, m);
42    else
43        pdc_multiply_matrix(m, &p->gstate[p->sl].ctm);
44}
45
46void
47pdf_set_topdownsystem(PDF *p, float height)
48{
49    if (p->ydirection < (float) 0.0)
50    {
51        pdc_matrix m;
52        pdc_translation_matrix(0, height, &m);
53        pdf_concat_raw(p, &m);
54        pdc_scale_matrix(1, -1, &m);
55        pdf_concat_raw(p, &m);
56        pdf_set_horiz_scaling(p, 100);
57    }
58}
59
60/* -------------------- Special graphics state ---------------------------- */
61
62void
63pdf_init_gstate(PDF *p)
64{
65    pdf_gstate *gs = &p->gstate[p->sl];
66
67    gs->ctm.a = (float) 1;
68    gs->ctm.b = (float) 0;
69    gs->ctm.c = (float) 0;
70    gs->ctm.d = (float) 1;
71    gs->ctm.e = (float) 0.0;
72    gs->ctm.f = (float) 0.0;
73
74    gs->x = (float) 0.0;
75    gs->y = (float) 0.0;
76
77    p->fillrule = pdf_fill_winding;
78
79    gs->lwidth = (float) 1;
80    gs->lcap = 0;
81    gs->ljoin = 0;
82    gs->miter = (float) 10;
83    gs->flatness = (float) -1;	/* -1 means "has not been set" */
84    gs->dashed = pdc_false;
85}
86
87void
88pdf__save(PDF *p)
89{
90    if (p->sl == PDF_MAX_SAVE_LEVEL - 1)
91	pdc_error(p->pdc, PDF_E_GSTATE_SAVELEVEL,
92	    pdc_errprintf(p->pdc, "%d", PDF_MAX_SAVE_LEVEL - 1), 0, 0, 0);
93
94    pdf_end_text(p);
95
96    pdc_puts(p->out, "q\n");
97
98    /* propagate states to next level */
99    p->sl++;
100    memcpy(&p->gstate[p->sl], &p->gstate[p->sl - 1], sizeof(pdf_gstate));
101    memcpy(&p->tstate[p->sl], &p->tstate[p->sl - 1], sizeof(pdf_tstate));
102    memcpy(&p->cstate[p->sl], &p->cstate[p->sl - 1], sizeof(pdf_cstate));
103}
104
105
106void
107pdf__restore(PDF *p)
108{
109    if (p->sl == 0)
110	pdc_error(p->pdc, PDF_E_GSTATE_RESTORE, 0, 0, 0, 0);
111
112    pdf_end_text(p);
113
114    pdc_puts(p->out, "Q\n");
115
116    p->sl--;
117}
118
119PDFLIB_API void PDFLIB_CALL
120PDF_save(PDF *p)
121{
122    static const char fn[] = "PDF_save";
123
124    if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p])\n", (void *) p))
125	return;
126
127    pdf__save(p);
128}
129
130PDFLIB_API void PDFLIB_CALL
131PDF_restore(PDF *p)
132{
133    static const char fn[] = "PDF_restore";
134
135    if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p])\n", (void *) p))
136	return;
137
138    pdf__restore(p);
139}
140
141PDFLIB_API void PDFLIB_CALL
142PDF_translate(PDF *p, float tx, float ty)
143{
144    static const char fn[] = "PDF_translate";
145    pdc_matrix m;
146
147    if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %g, %g)\n",
148	(void *) p, tx, ty))
149	return;
150
151    if (tx == (float) 0 && ty == (float) 0)
152	return;
153
154    pdc_translation_matrix(tx, ty, &m);
155
156    pdf_concat_raw(p, &m);
157}
158
159PDFLIB_API void PDFLIB_CALL
160PDF_scale(PDF *p, float sx, float sy)
161{
162    static const char fn[] = "PDF_scale";
163    pdc_matrix m;
164
165    if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %g, %g)\n",
166	(void *) p, sx, sy))
167	return;
168
169    if (sx == (float) 0)
170	pdc_error(p->pdc, PDC_E_ILLARG_FLOAT, "sx", "0", 0, 0);
171
172    if (sy == (float) 0)
173	pdc_error(p->pdc, PDC_E_ILLARG_FLOAT, "sy", "0", 0, 0);
174
175    if (sx == (float) 1 && sy == (float) 1)
176	return;
177
178    pdc_scale_matrix(sx, sy, &m);
179
180    pdf_concat_raw(p, &m);
181}
182
183PDFLIB_API void PDFLIB_CALL
184PDF_rotate(PDF *p, float phi)
185{
186    static const char fn[] = "PDF_rotate";
187    pdc_matrix m;
188
189    if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %g)\n",
190	(void *) p, phi))
191	return;
192
193    if (phi == (float) 0)
194	return;
195
196    pdc_rotation_matrix(p->ydirection * phi, &m);
197
198    pdf_concat_raw(p, &m);
199}
200
201PDFLIB_API void PDFLIB_CALL
202PDF_skew(PDF *p, float alpha, float beta)
203{
204    static const char fn[] = "PDF_skew";
205    pdc_matrix m;
206
207    if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %g, %g)\n",
208	(void *) p, alpha, beta))
209	return;
210
211    if (alpha == (float) 0 && beta == (float) 0)
212	return;
213
214    if (alpha > (float) 360 || alpha < (float) -360 ||
215	alpha == (float) -90 || alpha == (float) -270 ||
216	alpha == (float) 90 || alpha == (float) 270) {
217	pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
218	    "alpha", pdc_errprintf(p->pdc, "%f", alpha), 0, 0);
219    }
220
221    if (beta > (float) 360 || beta < (float) -360 ||
222	beta == (float) -90 || beta == (float) -270 ||
223	beta == (float) 90 || beta == (float) 270) {
224	pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
225	    "beta", pdc_errprintf(p->pdc, "%f", beta), 0, 0);
226    }
227
228    pdc_skew_matrix(p->ydirection * alpha, p->ydirection * beta, &m);
229
230    pdf_concat_raw(p, &m);
231}
232
233PDFLIB_API void PDFLIB_CALL
234PDF_concat(PDF *p, float a, float b, float c, float d, float e, float f)
235{
236    static const char fn[] = "PDF_concat";
237    pdc_matrix m;
238    float det = a * d - b * c;
239
240    if (!pdf_enter_api(p, fn, pdf_state_content,
241	"(p[%p], %g, %g, %g, %g, %g, %g)\n", (void *) p, a, b, c, d, e, f))
242    {
243	return;
244    }
245
246    if (fabs(det) < (float) PDF_SMALLREAL)
247	pdc_error(p->pdc, PDC_E_ILLARG_MATRIX,
248	    pdc_errprintf(p->pdc, "%f %f %f %f %f %f", a, b, c, d, e, f),
249	    0, 0, 0);
250
251    m.a = (float) a;
252    m.b = (float) b;
253    m.c = (float) c;
254    m.d = (float) d;
255    m.e = (float) e;
256    m.f = (float) f;
257
258    pdf_concat_raw(p, &m);
259}
260
261void
262pdf__setmatrix(PDF *p, pdc_matrix *n)
263{
264    pdc_matrix m;
265    float det = n->a * n->d - n->b * n->c;
266
267    if (fabs(det) < (float) PDF_SMALLREAL)
268	pdc_error(p->pdc, PDC_E_ILLARG_MATRIX,
269	    pdc_errprintf(p->pdc, "%f %f %f %f %f %f",
270		n->a, n->b, n->c, n->d, n->e, n->f),
271	    0, 0, 0);
272
273    pdc_invert_matrix(p->pdc, &m, &p->gstate[p->sl].ctm);
274    pdf_concat_raw(p, &m);
275    pdf_concat_raw(p, n);
276}
277
278PDFLIB_API void PDFLIB_CALL
279PDF_setmatrix(PDF *p, float a, float b, float c, float d, float e, float f)
280{
281    static const char fn[] = "PDF_setmatrix";
282    pdc_matrix m;
283    float det = a * d - b * c;
284
285    if (!pdf_enter_api(p, fn, pdf_state_content,
286	"(p[%p], %g, %g, %g, %g, %g, %g)\n", (void *) p, a, b, c, d, e, f))
287    {
288	return;
289    }
290
291    if (fabs(det) < (float) PDF_SMALLREAL)
292	pdc_error(p->pdc, PDC_E_ILLARG_MATRIX,
293	    pdc_errprintf(p->pdc, "%f %f %f %f %f %f", a, b, c, d, e, f),
294	    0, 0, 0);
295
296    pdc_invert_matrix(p->pdc, &m, &p->gstate[p->sl].ctm);
297    pdf_concat_raw(p, &m);
298
299    m.a = (float) a;
300    m.b = (float) b;
301    m.c = (float) c;
302    m.d = (float) d;
303    m.e = (float) e;
304    m.f = (float) f;
305
306    pdf_concat_raw(p, &m);
307}
308
309/* -------------------- General graphics state ---------------------------- */
310
311/* definitions of dash options */
312static const pdc_defopt pdf_dashoptions[] =
313{
314    {"dasharray", pdc_floatlist, 0, 2, MAX_DASH_LENGTH,
315      PDC_FLOAT_PREC, PDC_FLOAT_MAX, NULL},
316
317    {"dashphase", pdc_floatlist, 0, 1, 1, 0.0, PDC_FLOAT_MAX, NULL},
318
319    PDC_OPT_TERMINATE
320};
321
322static void
323pdf__setdashpattern(PDF *p, float *darray, int length, float phase)
324{
325    pdf_gstate *gs = &p->gstate[p->sl];
326
327    /* length == 0 or 1 means solid line */
328    if (length < 2)
329    {
330        if (gs->dashed || PDF_FORCE_OUTPUT())
331        {
332            pdc_puts(p->out, "[] 0 d\n");
333            gs->dashed = pdc_false;
334        }
335    }
336    else
337    {
338        int i;
339
340        pdc_puts(p->out, "[");
341        for (i = 0; i < length; i++)
342        {
343            pdc_printf(p->out, "%f ", darray[i]);
344        }
345        pdc_printf(p->out, "] %f d\n", phase);
346        gs->dashed = pdc_true;
347    }
348}
349
350void
351pdf__setdash(PDF *p, float b, float w)
352{
353    float darray[2];
354    int length = 2;
355
356     /* both zero means solid line */
357    if (b == 0.0 && w == 0.0)
358    {
359        length = 0;
360    }
361    else
362    {
363        darray[0] = b;
364        darray[1] = w;
365    }
366    pdf__setdashpattern(p, darray, length, (float) 0.0);
367}
368
369void
370pdf__setflat(PDF *p, float flat)
371{
372    pdf_gstate *gs = &p->gstate[p->sl];
373
374    if (flat != gs->flatness || PDF_FORCE_OUTPUT())
375    {
376	gs->flatness = flat;
377	pdc_printf(p->out, "%f i\n", flat);
378    }
379}
380
381void
382pdf__setlinejoin(PDF *p, int join)
383{
384    pdf_gstate *gs = &p->gstate[p->sl];
385
386    if (join != gs->ljoin || PDF_FORCE_OUTPUT())
387    {
388	gs->ljoin = join;
389	pdc_printf(p->out, "%d j\n", join);
390    }
391}
392
393void
394pdf__setlinecap(PDF *p, int cap)
395{
396    pdf_gstate *gs = &p->gstate[p->sl];
397
398    if (cap != gs->lcap || PDF_FORCE_OUTPUT())
399    {
400	gs->lcap = cap;
401	pdc_printf(p->out, "%d J\n", cap);
402    }
403}
404
405void
406pdf__setlinewidth(PDF *p, float width)
407{
408    pdf_gstate *gs = &p->gstate[p->sl];
409
410    if (width != gs->lwidth || PDF_FORCE_OUTPUT())
411    {
412	gs->lwidth = width;
413	pdc_printf(p->out, "%f w\n", width);
414    }
415}
416
417void
418pdf__setmiterlimit(PDF *p, float miter)
419{
420    pdf_gstate *gs = &p->gstate[p->sl];
421
422    if (miter != gs->miter || PDF_FORCE_OUTPUT())
423    {
424	gs->miter = miter;
425	pdc_printf(p->out, "%f M\n", miter);
426    }
427}
428
429
430PDFLIB_API void PDFLIB_CALL
431PDF_setdash(PDF *p, float b, float w)
432{
433    static const char fn[] = "PDF_setdash";
434
435    if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %g, %g)\n",
436	(void *) p, b, w))
437	return;
438
439    if (b < (float) 0.0)
440	pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
441	    "b", pdc_errprintf(p->pdc, "%f", b), 0, 0);
442
443    if (w < (float) 0.0)
444	pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
445	    "w", pdc_errprintf(p->pdc, "%f", w), 0, 0);
446
447    pdf__setdash(p, b, w);
448}
449
450PDFLIB_API void PDFLIB_CALL
451PDF_setpolydash(PDF *p, float *darray, int length)
452{
453    static const char fn[] = "PDF_setpolydash";
454
455    int i;
456
457    for (i = 0; i < length; i++)
458	pdc_trace(p->pdc, "*(darray+%d) = %g;\n", i, darray[i]);
459
460    if (!pdf_enter_api(p, fn, pdf_state_content,
461	"(p[%p], darray[%p], %d)\n", (void *) p, (void *) darray, length))
462    {
463	return;
464    }
465
466    if (length > 1)
467    {
468        /* sanity checks */
469        if (!darray)
470            pdc_error(p->pdc, PDC_E_ILLARG_EMPTY, "darray", 0, 0, 0);
471
472        if (length < 0 || length > MAX_DASH_LENGTH)
473            pdc_error(p->pdc, PDC_E_ILLARG_INT,
474                "length", pdc_errprintf(p->pdc, "%d", length), 0, 0);
475
476        for (i = 0; i < length; i++) {
477            if (darray[i] < (float) 0.0)
478                pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
479                    "darray[i]", pdc_errprintf(p->pdc, "%f", darray[i]), 0, 0);
480        }
481    }
482
483    pdf__setdashpattern(p, darray, length, (float) 0.0);
484}
485
486PDFLIB_API void PDFLIB_CALL
487PDF_setdashpattern(PDF *p, const char *optlist)
488{
489    static const char fn[] = "PDF_setdashpattern";
490    pdc_resopt *results;
491    float *darray, phase;
492    int length;
493
494    if (!pdf_enter_api(p, fn, pdf_state_content,
495        "(p[%p], \"%s\")\n", (void *) p, optlist))
496        return;
497
498    /* parsing optlist */
499    results = pdc_parse_optionlist(p->pdc, optlist, pdf_dashoptions, NULL,
500                                   pdc_true);
501
502    length = pdc_get_optvalues(p->pdc, "dasharray", results,
503                               NULL, (void **) &darray);
504
505    phase = (float) 0.0;
506    (void) pdc_get_optvalues(p->pdc, "dashphase", results, &phase, NULL);
507
508    pdc_cleanup_optionlist(p->pdc, results);
509
510    pdf__setdashpattern(p, darray, length, phase);
511
512    if (darray)
513        pdc_free(p->pdc, darray);
514}
515
516PDFLIB_API void PDFLIB_CALL
517PDF_setflat(PDF *p, float flat)
518{
519    static const char fn[] = "PDF_setflat";
520
521    if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %g)\n",
522	(void *) p, flat))
523	return;
524
525    if (flat < 0.0 || flat > 100.0)
526	pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
527	    "flat", pdc_errprintf(p->pdc, "%f", flat), 0, 0);
528
529    pdf__setflat(p, flat);
530}
531
532
533PDFLIB_API void PDFLIB_CALL
534PDF_setlinejoin(PDF *p, int join)
535{
536    static const char fn[] = "PDF_setlinejoin";
537    const int LAST_JOIN = 2;
538
539    if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %d)\n",
540	(void *) p, join))
541	return;
542
543    if (join < 0 || join > LAST_JOIN)
544	pdc_error(p->pdc, PDC_E_ILLARG_INT,
545	    "join", pdc_errprintf(p->pdc, "%d", join), 0, 0);
546
547    pdf__setlinejoin(p, join);
548}
549
550
551PDFLIB_API void PDFLIB_CALL
552PDF_setlinecap(PDF *p, int cap)
553{
554    static const char fn[] = "PDF_setlinecap";
555    const int LAST_CAP = 2;
556
557    if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %d)\n",
558	(void *) p, cap))
559	return;
560
561    if (cap < 0 || cap > LAST_CAP)
562	pdc_error(p->pdc, PDC_E_ILLARG_INT,
563	    "cap", pdc_errprintf(p->pdc, "%d", cap), 0, 0);
564
565    pdf__setlinecap(p, cap);
566}
567
568PDFLIB_API void PDFLIB_CALL
569PDF_setmiterlimit(PDF *p, float miter)
570{
571    static const char fn[] = "PDF_setmiterlimit";
572
573    if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %g)\n",
574	(void *) p, miter))
575	return;
576
577    if (miter < (float) 1.0)
578	pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
579	    "miter", pdc_errprintf(p->pdc, "%f", miter), 0, 0);
580
581    pdf__setmiterlimit(p, miter);
582}
583
584PDFLIB_API void PDFLIB_CALL
585PDF_setlinewidth(PDF *p, float width)
586{
587    static const char fn[] = "PDF_setlinewidth";
588
589    if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %g)\n",
590	(void *) p, width))
591	return;
592
593    if (width <= (float) 0.0)
594	pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
595	    "width", pdc_errprintf(p->pdc, "%f", width), 0, 0);
596
597    pdf__setlinewidth(p, width);
598}
599
600/* reset all gstate parameters except CTM
601*/
602void
603pdf_reset_gstate(PDF *p)
604{
605    pdf_gstate *gs = &p->gstate[p->sl];
606
607
608    pdf__setcolor(p, "fillstroke", "gray",
609	(float) 0, (float) 0, (float) 0, (float) 0);
610
611
612    pdf__setlinewidth(p, 1);
613    pdf__setlinecap(p, 0);
614    pdf__setlinejoin(p, 0);
615    pdf__setmiterlimit(p, 10);
616    pdf__setdash(p, 0, 0);
617
618    if (gs->flatness != (float) -1)
619	pdf__setflat(p, (float) 1.0);
620}
621
622void
623pdf__initgraphics(PDF *p)
624{
625    pdc_matrix inv_ctm;
626
627    pdf_reset_gstate(p);
628
629    pdc_invert_matrix(p->pdc, &inv_ctm, &p->gstate[p->sl].ctm);
630    pdf_concat_raw(p, &inv_ctm);
631
632    /* This also resets the CTM which guards against rounding artifacts. */
633    pdf_init_gstate(p);
634}
635
636PDFLIB_API void PDFLIB_CALL
637PDF_initgraphics(PDF *p)
638{
639    static const char fn[] = "PDF_initgraphics";
640
641    if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p])\n", (void *) p))
642	return;
643
644    pdf__initgraphics(p);
645}
646