OSDN Git Service

d0a6e91c0f4deb2fc5718b1dc1f22e8c08c46cc2
[putex/putex.git] / src / dvipdfmx-pu / src / spc_tpic.c
1 /*  
2
3     This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks.
4
5     Copyright (C) 2002-2012 by Jin-Hwan Cho and Shunsaku Hirata,
6     the dvipdfmx project team.
7     
8     Copyright (C) 1998, 1999 by Mark A. Wicks <mwicks@kettering.edu>
9
10     This program is free software; you can redistribute it and/or modify
11     it under the terms of the GNU General Public License as published by
12     the Free Software Foundation; either version 2 of the License, or
13     (at your option) any later version.
14     
15     This program is distributed in the hope that it will be useful,
16     but WITHOUT ANY WARRANTY; without even the implied warranty of
17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18     GNU General Public License for more details.
19     
20     You should have received a copy of the GNU General Public License
21     along with this program; if not, write to the Free Software
22     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
23 */
24
25 #if HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include "system.h"
30 #include "mem.h"
31 #include "error.h"
32
33 #include "numbers.h"
34 #include "dpxutil.h"
35
36 #include "pdfdoc.h"
37
38 #include "pdfdraw.h"
39 #include "pdfdev.h"
40
41 #include "specials.h"
42 #include "spc_tpic.h"
43
44 #define  DEBUG 1
45 #define  ENABLE_SPC_NAMESPACE 1
46
47 /*
48  * Following "constant" converts milli-inches to
49  * device (in this case PDF stream) coordinates.
50  */
51
52 #define MI2DEV (0.072/pdf_dev_scale())
53
54 /*
55  * Value for 'sh' command 'g' is interpreted as
56  * 
57  *   gray color value 1-g for "solid"
58  *   opacity value g for "opacity"
59  *   shape value g for "shape"
60  */
61 #define TPIC_MODE__FILL_SOLID   0
62 #define TPIC_MODE__FILL_OPACITY 1
63 #define TPIC_MODE__FILL_SHAPE   2
64
65 #ifndef ISBLANK
66 #  define ISBLANK(c) ( (c) == ' ' || (c) == '\t' )
67 #endif
68
69 static void
70 skip_blank (const char **pp, const char *endptr)
71 {
72   const char  *p = *pp;
73   for ( ; p < endptr && ISBLANK(*p); p++);
74   *pp = p;
75 }
76
77 struct spc_tpic_
78 {
79   struct {
80     int   fill;
81   } mode;
82
83   /* state */
84   double     pen_size;
85   int        fill_shape; /* boolean */
86   double     fill_color;
87
88   pdf_coord *points;
89   int        num_points;
90   int        max_points;
91 };
92
93 #if  1
94 static struct spc_tpic_ _tpic_state;
95 #endif
96
97 /* We use pdf_doc_add_page_content() here
98  * since we always draw isolated graphics.
99  */
100 static void
101 tpic__clear (struct spc_tpic_ *tp) 
102 {
103   if (tp->points) {
104     RELEASE(tp->points);
105     tp->points = NULL;
106   }
107   tp->num_points = 0;
108   tp->max_points = 0;
109   tp->fill_shape = 0;
110   tp->fill_color = 0.0;
111 }
112
113
114 static pdf_obj *
115 create_xgstate (double a /* alpha */, int f_ais /* alpha is shape */)
116 {
117   pdf_obj  *dict;
118
119   dict = pdf_new_dict();
120   pdf_add_dict(dict,
121                pdf_new_name("Type"),
122                pdf_new_name("ExtGState"));
123   if (f_ais) {
124     pdf_add_dict(dict,
125                  pdf_new_name("AIS"),
126                  pdf_new_boolean(1));
127   }
128   pdf_add_dict(dict,
129                pdf_new_name("ca"),
130                pdf_new_number(a));
131
132   return  dict;
133 }
134
135 static int
136 check_resourcestatus (const char *category, const char *resname)
137 {
138   pdf_obj  *dict1, *dict2;
139
140   dict1 = pdf_doc_current_page_resources();
141   if (!dict1)
142     return  0;
143
144   dict2 = pdf_lookup_dict(dict1, category);
145   if (dict2 &&
146       pdf_obj_typeof(dict2) == PDF_DICT) {
147     if (pdf_lookup_dict(dict2, resname))
148       return  1;
149   }
150   return  0;
151 }
152
153 static int
154 set_linestyle (double pn, double da)
155 {
156   double  dp[2]; /* dash pattern */
157
158   pdf_dev_setlinejoin(1);
159   pdf_dev_setmiterlimit(1.4);
160   pdf_dev_setlinewidth(pn);
161   if (da > 0.0) {
162     dp[0] =  da * 72.0;
163     pdf_dev_setdash(1, dp, 0);
164     pdf_dev_setlinecap(0);
165   } else if (da < 0.0) {
166     dp[0] =  pn;
167     dp[1] = -da * 72.0;
168     pdf_dev_setdash(2, dp, 0);
169     pdf_dev_setlinecap(1);
170   } else {
171     pdf_dev_setlinecap(0);
172   }
173
174   return  0;
175 }
176
177 static int
178 set_fillstyle (double g, double a, int f_ais)
179 {
180   pdf_obj *dict;
181   char     resname[32];
182   char     buf[32];
183   int      alp, len = 0;
184
185   if (a > 0.0) {
186     alp = round(100.0 * a);
187     sprintf(resname, "_Tps_a%03d_", alp);
188     if (!check_resourcestatus("ExtGState", resname)) {
189       dict = create_xgstate(ROUND(0.01 * alp, 0.01), f_ais);
190       pdf_doc_add_page_resource("ExtGState",
191                                 resname, pdf_ref_obj(dict));
192       pdf_release_obj(dict);
193     }
194     len += sprintf(buf + len, " /%s gs", resname);
195
196     pdf_doc_add_page_content(buf, len);  /* op: gs */
197   }
198
199   {
200     pdf_color *sc, *fc, new_fc;
201
202     pdf_color_get_current (&sc, &fc); /* get stroking and fill colors */
203     pdf_color_brighten_color(&new_fc, fc, g);
204     pdf_dev_set_nonstrokingcolor(&new_fc);
205   }
206
207   return  0;
208 }
209
210 static void
211 set_styles (struct spc_tpic_ *tp,
212             const pdf_coord  *c,
213             int               f_fs,
214             int               f_vp,
215             double            pn,
216             double            da) {
217   pdf_tmatrix M;
218
219   pdf_setmatrix (&M, 1.0, 0.0, 0.0, -1.0, c->x, c->y);
220   pdf_dev_concat(&M);
221
222   if (f_vp)
223     set_linestyle(pn, da);
224
225   if (f_fs) {
226     double g, a;
227     int f_ais;
228
229     if (tp->mode.fill == TPIC_MODE__FILL_SOLID || !tp->fill_color) {
230       g = 1.0 - tp->fill_color;
231       a = 0.0;
232     } else {
233       g = 0.0;
234       a = tp->fill_color;
235     }
236
237     f_ais = (tp->mode.fill == TPIC_MODE__FILL_SHAPE) ? 1 : 0;
238
239     set_fillstyle(g, a, f_ais);
240   }
241 }
242
243 static void
244 showpath (int f_vp, int f_fs) /* visible_path, fill_shape */
245 {
246   if (f_vp) {
247     if (f_fs)
248       pdf_dev_flushpath('b', PDF_FILL_RULE_NONZERO);
249     else {
250       pdf_dev_flushpath('S', PDF_FILL_RULE_NONZERO);
251     }
252   } else {
253     /*
254      * Acrobat claims 'Q' as illegal operation when there are unfinished
255      * path (a path without path-painting operator applied)?
256      */
257     if (f_fs)
258       pdf_dev_flushpath('f', PDF_FILL_RULE_NONZERO);
259     else {
260       pdf_dev_newpath();
261     }
262   }
263 }
264
265 #define CLOSED_PATH(s) (\
266   (s)->points[0].x == (s)->points[(s)->num_points-1].x && \
267   (s)->points[0].y == (s)->points[(s)->num_points-1].y \
268 )
269
270 static int
271 tpic__polyline (struct spc_tpic_ *tp,
272                 const pdf_coord  *c,
273                 int               f_vp,
274                 double            da)
275 {
276   double       pn    = tp->pen_size;
277   int          f_fs  = tp->fill_shape;
278   int          i, error = 0;
279
280   /* Shading is applied only to closed path. */
281   f_fs  = CLOSED_PATH(tp) ? f_fs : 0;
282   f_vp  = (pn > 0.0) ? f_vp : 0;
283
284   if (f_vp || f_fs) {
285     pdf_dev_gsave();
286
287     set_styles(tp, c, f_fs, f_vp, pn, da);
288
289     pdf_dev_moveto(tp->points[0].x, tp->points[0].y);
290     for (i = 0; i < tp->num_points; i++)
291       pdf_dev_lineto(tp->points[i].x, tp->points[i].y);
292
293     showpath(f_vp, f_fs);
294
295     pdf_dev_grestore();
296   }
297
298   tpic__clear(tp);
299
300   return  error;
301 }
302
303 /*
304  * Accroding to
305  * "Tpic: Pic for TEX", Tim Morgan, Original by Brian Kernighan, p.20:
306  * 
307  *  A spline is a smooth curve guided by a set of straight lines just
308  *  like the line above. It begins at the same place, ends at the same
309  *  place, and in between is tangent to the mid-point of each guiding
310  *  line. The syntax for a spline is identical to a (path) line except
311  *  for using spline instead of line.
312  *
313  * Spline is not a curve drawn by spline-fitting points p0, p1, ..., pn,
314  * defined by tpic special "pa" command. Instead, a path defined by set
315  * of points p0, p1, ... is guiding line mentioned above.
316  *
317  * Dvipsk draws them as a straight line from p0 to q1 = (p0 + p1)/2,
318  * followed by a quadratic B-spline curve with starting point q1, (off-
319  * curve) control point p1, end point q2 = (p1 + p2)/2, ..., and a
320  * straight line from qn to pn.
321  */
322
323 static int
324 tpic__spline (struct spc_tpic_ *tp,
325               const pdf_coord  *c,
326               int               f_vp,
327               double            da)
328 {
329   double       v[6];
330   double       pn    = tp->pen_size;
331   int          f_fs  = tp->fill_shape;
332   int          i, error = 0;
333
334   f_fs  = CLOSED_PATH(tp) ? f_fs : 0;
335   f_vp  = (pn > 0.0) ? f_vp : 0;
336
337   if (f_vp || f_fs) {
338     pdf_dev_gsave();
339
340     set_styles(tp, c, f_fs, f_vp, pn, da);
341
342     pdf_dev_moveto(tp->points[0].x, tp->points[0].y);
343
344     v[0] = 0.5 * (tp->points[0].x + tp->points[1].x);
345     v[1] = 0.5 * (tp->points[0].y + tp->points[1].y);
346     pdf_dev_lineto(v[0], v[1]);
347     for (i = 1; i < tp->num_points - 1; i++) {
348       /* B-spline control points */
349       v[0] = 0.5 * (tp->points[i-1].x + tp->points[i].x);
350       v[1] = 0.5 * (tp->points[i-1].y + tp->points[i].y);
351       v[2] = tp->points[i].x;
352       v[3] = tp->points[i].y;
353       v[4] = 0.5 * (tp->points[i].x + tp->points[i+1].x);
354       v[5] = 0.5 * (tp->points[i].y + tp->points[i+1].y);
355       pdf_dev_bspline(v[0], v[1], v[2], v[3], v[4], v[5]);
356     }
357     pdf_dev_lineto(tp->points[i].x, tp->points[i].y);
358
359     showpath(f_vp, f_fs);
360
361     pdf_dev_grestore();
362   }
363   tpic__clear(tp);
364
365   return  error;
366 }
367
368 static int
369 tpic__arc (struct spc_tpic_ *tp,
370            const pdf_coord  *c,
371            int               f_vp,
372            double            da,
373            double           *v /* 6 numbers */ )
374 {
375   double       pn    = tp->pen_size;
376   int          f_fs  = tp->fill_shape;
377
378   f_fs  = (round(fabs(v[4] - v[5]) + 0.5) >= 360) ? f_fs : 0;
379   f_vp  = (pn > 0.0) ? f_vp : 0;
380
381   if (f_vp || f_fs) {
382     pdf_dev_gsave();
383
384     set_styles(tp, c, f_fs, f_vp, pn, da);
385
386     pdf_dev_arcx(v[0], v[1], v[2], v[3], v[4], v[5], +1, 0.0);
387
388     showpath(f_vp, f_fs);
389
390     pdf_dev_grestore();
391   }
392   tpic__clear(tp);
393
394   return  0;
395 }
396
397 #if  1
398 static int
399 spc_currentpoint (struct spc_env *spe, long *pg, pdf_coord *cp)
400 {
401   *pg = 0;
402   cp->x = spe->x_user;
403   cp->y = spe->y_user;
404   return  0;
405 }
406 #endif
407
408 static int
409 spc_handler_tpic_pn (struct spc_env *spe,
410                      struct spc_arg *ap ) /* , void *dp) */
411 {
412   struct spc_tpic_ *tp = &_tpic_state;
413   char  *q;
414
415   ASSERT(spe && ap && tp);
416
417   skip_blank(&ap->curptr, ap->endptr);
418   q = parse_float_decimal(&ap->curptr, ap->endptr);
419   if (!q) {
420     spc_warn(spe, "Invalid pen size specified?");
421     return -1;
422   }
423   tp->pen_size = atof(q) * MI2DEV;
424   RELEASE(q);
425
426   return  0;
427 }
428
429 static int
430 spc_handler_tpic_pa (struct spc_env *spe,
431                      struct spc_arg *ap ) /* , void *dp) */
432 {
433   struct spc_tpic_ *tp = &_tpic_state;
434   char   *q;
435   int     i;
436   double  v[2];
437
438   ASSERT(spe && ap && tp);
439
440   skip_blank(&ap->curptr, ap->endptr);
441   for (i = 0;
442        i < 2 && ap->curptr < ap->endptr; i++) {
443     q = parse_float_decimal(&ap->curptr, ap->endptr);
444     if (!q) {
445       spc_warn(spe, "Missing numbers for TPIC \"pa\" command.");
446       return  -1;
447     }
448     v[i] = atof(q);
449     RELEASE(q);
450     skip_blank(&ap->curptr, ap->endptr);
451   }
452   if (i != 2) {
453     spc_warn(spe, "Invalid arg for TPIC \"pa\" command.");
454     return  -1;
455   }
456
457   if (tp->num_points >= tp->max_points) {
458     tp->max_points += 256;
459     tp->points = RENEW(tp->points, tp->max_points, pdf_coord);
460   }
461   tp->points[tp->num_points].x = v[0] * MI2DEV;
462   tp->points[tp->num_points].y = v[1] * MI2DEV;
463   tp->num_points += 1;
464
465   return  0;
466 }
467
468 static int
469 spc_handler_tpic_fp (struct spc_env *spe,
470                      struct spc_arg *ap ) /* , void *dp) */
471 {
472   struct spc_tpic_ *tp = &_tpic_state;
473   pdf_coord  cp;
474   long       pg;
475
476   ASSERT(spe && ap && tp);
477
478   if (tp->num_points <= 1) {
479     spc_warn(spe, "Too few points (< 2) for polyline path.");
480     return  -1;
481   }
482
483   spc_currentpoint(spe, &pg, &cp);
484
485   return  tpic__polyline(tp, &cp, 1, 0.0);
486 }
487
488 static int
489 spc_handler_tpic_ip (struct spc_env *spe,
490                      struct spc_arg *ap ) /* , void *dp) */
491 {
492   struct spc_tpic_ *tp = &_tpic_state;
493   pdf_coord  cp;
494   long       pg;
495
496   ASSERT(spe && ap && tp);
497
498   if (tp->num_points <= 1) {
499     spc_warn(spe, "Too few points (< 2) for polyline path.");
500     return  -1;
501   }
502
503   spc_currentpoint(spe, &pg, &cp);
504
505   return  tpic__polyline(tp, &cp, 0, 0.0);
506 }
507
508 static int
509 spc_handler_tpic_da (struct spc_env *spe,
510                      struct spc_arg *ap ) /* , void *dp) */
511 {
512   struct  spc_tpic_ *tp = &_tpic_state;
513   char      *q;
514   double     da = 0.0;
515   pdf_coord  cp;
516   long       pg;
517
518   ASSERT(spe && ap && tp);
519
520   skip_blank(&ap->curptr, ap->endptr);
521   q = parse_float_decimal(&ap->curptr, ap->endptr);
522   if (q) {
523     da = atof(q);
524     RELEASE(q);
525   }
526   if (tp->num_points <= 1) {
527     spc_warn(spe, "Too few points (< 2) for polyline path.");
528     return  -1;
529   }
530
531   spc_currentpoint(spe, &pg, &cp);
532
533   return  tpic__polyline(tp, &cp, 1, da);
534 }
535
536 static int
537 spc_handler_tpic_dt (struct spc_env *spe,
538                      struct spc_arg *ap ) /* , void *dp) */
539 {
540   struct  spc_tpic_ *tp = &_tpic_state;
541   char      *q;
542   double     da = 0.0;
543   pdf_coord  cp;
544   long       pg;
545
546   ASSERT(spe && ap && tp);
547
548   skip_blank(&ap->curptr, ap->endptr);
549   q = parse_float_decimal(&ap->curptr, ap->endptr);
550   if (q) {
551     da = -atof(q);
552     RELEASE(q);
553   }
554   if (tp->num_points <= 1) {
555     spc_warn(spe, "Too few points (< 2) for polyline path.");
556     return  -1;
557   }
558
559   spc_currentpoint(spe, &pg, &cp);
560
561   return  tpic__polyline(tp, &cp, 1, da);
562 }
563
564 static int
565 spc_handler_tpic_sp (struct spc_env *spe,
566                      struct spc_arg *ap ) /* , void *dp) */
567 {
568   struct  spc_tpic_ *tp = &_tpic_state;
569   char      *q;
570   double     da = 0.0;
571   pdf_coord  cp;
572   long       pg;
573
574   ASSERT(spe && ap && tp);
575
576   skip_blank(&ap->curptr, ap->endptr);
577   q = parse_float_decimal(&ap->curptr, ap->endptr);
578   if (q) {
579     da = atof(q);
580     RELEASE(q);
581   }
582   if (tp->num_points <= 2) {
583     spc_warn(spe, "Too few points (< 3) for spline path.");
584     return  -1;
585   }
586
587   spc_currentpoint(spe, &pg, &cp);
588
589   return  tpic__spline(tp, &cp, 1, da);
590 }
591
592 static int
593 spc_handler_tpic_ar (struct spc_env *spe,
594                      struct spc_arg *ap ) /* , void *dp) */
595 {
596   struct  spc_tpic_ *tp = &_tpic_state;
597   double     v[6];
598   pdf_coord  cp;
599   long       pg;
600   char      *q;
601   int        i;
602
603   ASSERT(spe && ap && tp);
604
605   skip_blank(&ap->curptr, ap->endptr);
606   for (i = 0;
607        i < 6 && ap->curptr < ap->endptr; i++) {
608     q = parse_float_decimal(&ap->curptr, ap->endptr);
609     if (!q) {
610       spc_warn(spe, "Invalid args. in TPIC \"ar\" command.");
611       return  -1;
612     }
613     v[i] = atof(q);
614     RELEASE(q);
615     skip_blank(&ap->curptr, ap->endptr);
616   }
617   if (i != 6) {
618     spc_warn(spe, "Invalid arg for TPIC \"ar\" command.");
619     return  -1;
620   }
621
622   v[0] *= MI2DEV; v[1] *= MI2DEV;
623   v[2] *= MI2DEV; v[3] *= MI2DEV;
624   v[4] *= 180.0 / M_PI;
625   v[5] *= 180.0 / M_PI;
626
627   spc_currentpoint(spe, &pg, &cp);
628
629   return  tpic__arc(tp, &cp, 1, 0.0, v);
630 }
631
632 static int
633 spc_handler_tpic_ia (struct spc_env *spe,
634                      struct spc_arg *ap ) /* , void *dp) */
635 {
636   struct  spc_tpic_ *tp = &_tpic_state;
637   double     v[6];
638   pdf_coord  cp;
639   long       pg;
640   char      *q;
641   int        i;
642
643   ASSERT(spe && ap && tp);
644
645   skip_blank(&ap->curptr, ap->endptr);
646   for (i = 0;
647        i < 6 && ap->curptr < ap->endptr; i++) {
648     q = parse_float_decimal(&ap->curptr, ap->endptr);
649     if (!q) {
650       spc_warn(spe, "Invalid args. in TPIC \"ia\" command.");
651       return  -1;
652     }
653     v[i] = atof(q);
654     RELEASE(q);
655     skip_blank(&ap->curptr, ap->endptr);
656   }
657   if (i != 6) {
658     spc_warn(spe, "Invalid arg for TPIC \"ia\" command.");
659     return  -1;
660   }
661
662   v[0] *= MI2DEV; v[1] *= MI2DEV;
663   v[2] *= MI2DEV; v[3] *= MI2DEV;
664   v[4] *= 180.0 / M_PI;
665   v[5] *= 180.0 / M_PI;
666
667   spc_currentpoint(spe, &pg, &cp);
668
669   return  tpic__arc(tp, &cp, 0, 0.0, v);
670 }
671
672 static int
673 spc_handler_tpic_sh (struct spc_env *spe,
674                      struct spc_arg *ap ) /* , void *dp) */
675 {
676   struct  spc_tpic_ *tp = &_tpic_state;
677   char   *q;
678
679   ASSERT(spe && ap && tp);
680
681   tp->fill_shape = 1;
682   tp->fill_color = 0.5;
683
684   skip_blank(&ap->curptr, ap->endptr);
685   q = parse_float_decimal(&ap->curptr, ap->endptr);
686   if (q) {
687     double g = atof(q);
688     RELEASE(q);
689     if (g >= 0.0 && g <= 1.0)
690       tp->fill_color = g;
691     else {
692       WARN("Invalid fill color specified: %g\n", g);
693       return -1;
694     }      
695   }
696
697   return  0;
698 }
699
700 static int
701 spc_handler_tpic_wh (struct spc_env *spe,
702                      struct spc_arg *ap ) /* , void *dp) */
703 {
704   struct  spc_tpic_ *tp = &_tpic_state;
705
706   ASSERT(spe && ap && tp);
707
708   tp->fill_shape = 1;
709   tp->fill_color = 0.0;
710
711   return  0;
712 }
713
714 static int
715 spc_handler_tpic_bk (struct spc_env *spe,
716                      struct spc_arg *ap ) /* , void *dp) */
717 {
718   struct  spc_tpic_ *tp = &_tpic_state;
719
720   ASSERT(spe && ap && tp);
721
722   tp->fill_shape = 1;
723   tp->fill_color = 1.0;
724
725   return  0;
726 }
727
728 static int
729 spc_handler_tpic_tx (struct spc_env *spe,
730                      struct spc_arg *ap ) /* , void *dp) */
731 {
732   struct  spc_tpic_ *tp = &_tpic_state;
733
734   ASSERT(spe && ap && tp);
735
736   spc_warn(spe, "TPIC command \"tx\" not supported.");
737
738   return  -1;
739 }
740
741
742 static int
743 spc_handler_tpic__init (struct spc_env *spe,
744                         struct spc_arg *ap, void *dp)
745 {
746   struct spc_tpic_ *tp = dp;
747
748 #if  0
749   tp->mode.fill  = TPIC_MODE__FILL_SOLID;
750 #endif 
751   tp->pen_size   = 1.0;
752   tp->fill_shape = 0;
753   tp->fill_color = 0.0;
754
755   tp->points     = NULL;
756   tp->num_points = 0;
757   tp->max_points = 0;
758
759   if (tp->mode.fill != TPIC_MODE__FILL_SOLID && pdf_get_version() < 4) {
760       spc_warn(spe, "Tpic shading support requires PDF version 1.4.");
761     tp->mode.fill = TPIC_MODE__FILL_SOLID;
762   }
763
764   return  0;
765 }
766
767 static int
768 spc_handler_tpic__bophook (struct spc_env *spe,
769                            struct spc_arg *ap, void *dp)
770 {
771   struct spc_tpic_ *tp = dp;
772
773   ASSERT(tp);
774
775   tpic__clear(tp);
776
777   return  0;
778 }
779
780 static int
781 spc_handler_tpic__eophook (struct spc_env *spe,
782                            struct spc_arg *ap, void *dp)
783 {
784   struct spc_tpic_ *tp = dp;
785
786   ASSERT(tp);
787
788   if (tp->num_points > 0)
789     spc_warn(spe, "Unflushed tpic path at end of the page.");
790   tpic__clear(tp);
791
792   return  0;
793 }
794
795 static int
796 spc_handler_tpic__clean (struct spc_env *spe,
797                          struct spc_arg *ap, void *dp)
798 {
799   struct spc_tpic_ *tp = dp;
800
801   ASSERT(tp);
802
803   if (tp->num_points > 0)
804     spc_warn(spe, "Unflushed tpic path at end of the document.");
805
806   tpic__clear(tp);
807 #if  0
808   RELEASE(tp);
809 #endif
810
811   return  0;
812 }
813
814 void
815 tpic_set_fill_mode (int mode)
816 {
817   struct spc_tpic_ *tp = &_tpic_state;
818   tp->mode.fill = mode;
819 }
820
821
822 int
823 spc_tpic_at_begin_page (void)
824 {
825   struct spc_tpic_ *tp = &_tpic_state;
826   return  spc_handler_tpic__bophook(NULL, NULL, tp);
827 }
828
829 int
830 spc_tpic_at_end_page (void)
831 {
832   struct spc_tpic_ *tp = &_tpic_state;
833   return  spc_handler_tpic__eophook(NULL, NULL, tp);
834 }
835
836
837 int
838 spc_tpic_at_begin_document (void)
839 {
840   struct spc_tpic_ *tp = &_tpic_state;
841   return  spc_handler_tpic__init(NULL, NULL, tp);
842 }
843
844 int
845 spc_tpic_at_end_document (void)
846 {
847   struct spc_tpic_ *tp = &_tpic_state;
848   return  spc_handler_tpic__clean(NULL, NULL, tp);
849 }
850
851
852 #if  DEBUG
853 #include "pdfparse.h" /* parse_val_ident :( */
854
855 static pdf_obj *
856 spc_parse_kvpairs (struct spc_env *spe, struct spc_arg *ap)
857 {
858   pdf_obj *dict;
859   char    *kp, *vp;
860   int      error = 0;
861
862   dict = pdf_new_dict();
863
864   skip_blank(&ap->curptr, ap->endptr);
865   while (!error && ap->curptr < ap->endptr) {
866     kp = parse_val_ident(&ap->curptr, ap->endptr);
867     if (!kp)
868       break;
869     skip_blank(&ap->curptr, ap->endptr);
870     if (ap->curptr < ap->endptr &&
871         ap->curptr[0] == '=') {
872       ap->curptr++;
873       skip_blank(&ap->curptr, ap->endptr);
874       if (ap->curptr == ap->endptr) {
875         RELEASE(kp);
876         error = -1;
877         break;
878       }
879       vp = parse_c_string(&ap->curptr, ap->endptr);
880       if (!vp)
881         error = -1;
882       else {
883         pdf_add_dict(dict,
884                      pdf_new_name(kp),
885                      pdf_new_string(vp, strlen(vp) + 1)); /* NULL terminate */
886         RELEASE(vp);
887       }
888     } else {
889       /* Treated as 'flag' */
890       pdf_add_dict(dict,
891                    pdf_new_name(kp),
892                    pdf_new_boolean(1));
893     }
894     RELEASE(kp);
895     if (!error)
896       skip_blank(&ap->curptr, ap->endptr);
897   }
898
899   if (error) {
900     pdf_release_obj(dict);
901     dict = NULL;
902   }
903
904   return  dict;
905 }
906
907 static int
908 tpic_filter_getopts (pdf_obj *kp, pdf_obj *vp, void *dp)
909 {
910   struct spc_tpic_ *tp = dp;
911   char  *k, *v;
912   int    error = 0;
913
914   ASSERT( kp && vp && tp );
915
916   k = pdf_name_value(kp);
917   if (!strcmp(k, "fill-mode")) {
918     if (pdf_obj_typeof(vp) != PDF_STRING) {
919       WARN("Invalid value for TPIC option fill-mode...");
920       error = -1;
921     } else {
922       v = pdf_string_value(vp);
923       if (!strcmp(v, "shape"))
924         tp->mode.fill = TPIC_MODE__FILL_SHAPE;
925       else if (!strcmp(v, "opacity"))
926         tp->mode.fill = TPIC_MODE__FILL_OPACITY;
927       else if (!strcmp(v, "solid"))
928         tp->mode.fill = TPIC_MODE__FILL_SOLID;
929       else {
930         WARN("Invalid value for TPIC option fill-mode: %s", v);
931         error = -1;
932       }
933     }
934   } else {
935     WARN("Unrecognized option for TPIC special handler: %s", k);
936     error = -1;
937   }
938
939   return  error;
940 }
941
942 static int
943 spc_handler_tpic__setopts (struct spc_env *spe,
944                            struct spc_arg *ap ) /* , void *dp) */
945 {
946   struct spc_tpic_ *tp = &_tpic_state;
947   pdf_obj  *dict;
948   int       error = 0;
949
950   dict  = spc_parse_kvpairs(spe, ap);
951   if (!dict)
952     return  -1;
953   error = pdf_foreach_dict(dict, tpic_filter_getopts, tp);
954   if (!error) {
955     if (tp->mode.fill != TPIC_MODE__FILL_SOLID &&
956         pdf_get_version() < 4) {
957       spc_warn(spe, "Transparent fill mode requires PDF version 1.4.");
958       tp->mode.fill = TPIC_MODE__FILL_SOLID;
959     }
960   }
961
962   return  error;
963 }
964 #endif  /* DEBUG */
965
966
967 static struct spc_handler tpic_handlers[] = {
968   {"pn", spc_handler_tpic_pn},
969   {"pa", spc_handler_tpic_pa},
970   {"fp", spc_handler_tpic_fp},
971   {"ip", spc_handler_tpic_ip},
972   {"da", spc_handler_tpic_da},
973   {"dt", spc_handler_tpic_dt},
974   {"sp", spc_handler_tpic_sp},
975   {"ar", spc_handler_tpic_ar},
976   {"ia", spc_handler_tpic_ia},
977   {"sh", spc_handler_tpic_sh},
978   {"wh", spc_handler_tpic_wh},
979   {"bk", spc_handler_tpic_bk},
980   {"tx", spc_handler_tpic_tx}
981 };
982
983 int
984 spc_tpic_check_special (const char *buf, long len)
985 {
986   int    istpic = 0;
987   char  *q;
988   const char *p, *endptr;
989   int    i, hasnsp = 0;
990
991   p      = buf;
992   endptr = p + len;
993
994   skip_blank(&p, endptr);
995 #if  ENABLE_SPC_NAMESPACE
996   if (p + strlen("tpic:") < endptr &&
997       !memcmp(p, "tpic:", strlen("tpic:")))
998   {
999     p += strlen("tpic:");
1000     hasnsp = 1;
1001   }
1002 #endif
1003   q = parse_c_ident(&p, endptr);
1004
1005   if (!q)
1006     istpic = 0;
1007   else if (q && hasnsp && !strcmp(q, "__setopt__")) {
1008 #if  DEBUG
1009     istpic = 1;
1010 #endif
1011     RELEASE(q);
1012   } else {
1013     for (i = 0;
1014          i < sizeof(tpic_handlers)/sizeof(struct spc_handler); i++) {
1015       if (!strcmp(q, tpic_handlers[i].key)) {
1016         istpic = 1;
1017         break;
1018       }
1019     }
1020     RELEASE(q);
1021   }
1022
1023   return  istpic;
1024 }
1025
1026
1027 int
1028 spc_tpic_setup_handler (struct spc_handler *sph,
1029                         struct spc_env *spe, struct spc_arg *ap)
1030 {
1031   char  *q;
1032   int    i, hasnsp = 0, error = -1;
1033
1034   ASSERT(sph && spe && ap);
1035
1036   skip_blank(&ap->curptr, ap->endptr);
1037 #if  ENABLE_SPC_NAMESPACE
1038   if (ap->curptr + strlen("tpic:") < ap->endptr &&
1039       !memcmp(ap->curptr, "tpic:", strlen("tpic:")))
1040   {
1041     ap->curptr += strlen("tpic:");
1042     hasnsp = 1;
1043   }
1044 #endif
1045   q = parse_c_ident(&ap->curptr, ap->endptr);
1046
1047   if (!q)
1048     error = -1;
1049   else if (q && hasnsp && !strcmp(q, "__setopt__")) {
1050 #if  DEBUG
1051     ap->command = "__setopt__";
1052     sph->key    = "tpic:";
1053     sph->exec   = spc_handler_tpic__setopts;
1054     skip_blank(&ap->curptr, ap->endptr);
1055     error = 0;
1056 #endif
1057     RELEASE(q);
1058   } else {
1059     for (i = 0;
1060          i < sizeof(tpic_handlers)/sizeof(struct spc_handler); i++) {
1061       if (!strcmp(q, tpic_handlers[i].key)) {
1062         ap->command = tpic_handlers[i].key;
1063         sph->key    = "tpic:";
1064         sph->exec   = tpic_handlers[i].exec;
1065         skip_blank(&ap->curptr, ap->endptr);
1066         error = 0;
1067         break;
1068       }
1069     }
1070     RELEASE(q);
1071   }
1072
1073   return  error;
1074 }
1075
1076
1077 #if  0
1078 int
1079 spc_load_tpic_special  (struct spc_env *spe, pdf_obj *lopts)
1080 {
1081   struct spc_def   *spd;
1082   struct spc_tpic_ *sd;
1083
1084   sd  = NEW(1, struct spc_tpic_);
1085
1086   spd = NEW(1, struct spc_def);
1087   spc_init_def(spd);
1088
1089   spc_def_init   (spd, &spc_handler_tpic__init);
1090   spc_def_setopts(spd, &spc_handler_tpic__setopts);
1091   spc_def_bophook(spd, &spc_handler_tpic__bophook);
1092   spc_def_eophook(spd, &spc_handler_tpic__eophook);
1093   spc_def_clean  (spd, &spc_handler_tpic__clean);
1094
1095   spc_def_func(spd, "pn", &spc_handler_tpic_pn);
1096   spc_def_func(spd, "pa", &spc_handler_tpic_pa);
1097   spc_def_func(spd, "fp", &spc_handler_tpic_fp);
1098   spc_def_func(spd, "ip", &spc_handler_tpic_ip);
1099   spc_def_func(spd, "da", &spc_handler_tpic_da);
1100   spc_def_func(spd, "dt", &spc_handler_tpic_dt);
1101   spc_def_func(spd, "sp", &spc_handler_tpic_sp);
1102   spc_def_func(spd, "ar", &spc_handler_tpic_ar);
1103   spc_def_func(spd, "ia", &spc_handler_tpic_ia);
1104   spc_def_func(spd, "sh", &spc_handler_tpic_sh);
1105   spc_def_func(spd, "wh", &spc_handler_tpic_wh);
1106   spc_def_func(spd, "bk", &spc_handler_tpic_bk);
1107   spc_def_func(spd, "tx", &spc_handler_tpic_tx);
1108
1109   spc_add_special(spe, "tpic", spd, sd);
1110
1111   return  0;
1112 }
1113 #endif /* 0 */
1114