Back to index

texmacs  1.0.7.15
pdfdraw.c
Go to the documentation of this file.
00001 /*  $Header: /home/cvsroot/dvipdfmx/src/pdfdraw.c,v 1.20 2009/11/28 17:17:30 matthias Exp $
00002     
00003     This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks.
00004 
00005     Copyright (C) 2002 by Jin-Hwan Cho and Shunsaku Hirata,
00006     the dvipdfmx project team <dvipdfmx@project.ktug.or.kr>
00007     
00008     Copyright (C) 1998, 1999 by Mark A. Wicks <mwicks@kettering.edu>
00009 
00010     This program is free software; you can redistribute it and/or modify
00011     it under the terms of the GNU General Public License as published by
00012     the Free Software Foundation; either version 2 of the License, or
00013     (at your option) any later version.
00014     
00015     This program is distributed in the hope that it will be useful,
00016     but WITHOUT ANY WARRANTY; without even the implied warranty of
00017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018     GNU General Public License for more details.
00019     
00020     You should have received a copy of the GNU General Public License
00021     along with this program; if not, write to the Free Software
00022     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00023 */
00024 
00025 #ifdef HAVE_CONFIG_H
00026 #include "config.h"
00027 #endif
00028 
00029 #include <math.h>
00030 
00031 #include "system.h"
00032 #include "error.h"
00033 #include "mem.h"
00034 #include "mfileio.h"
00035 #include "dpxutil.h"
00036 #include "numbers.h"
00037 
00038 #include "pdfdoc.h"
00039 #include "pdfdev.h"
00040 #include "pdfcolor.h"
00041 
00042 #include "pdfdraw.h"
00043 
00044 #if 1
00045 typedef  void  pdf_dev;
00046 #endif
00047 
00048 
00049 /*
00050  * Numbers are rounding to 0-5 fractional digits
00051  * in output routine. 
00052  */
00053 #define detM(M) ((M).a * (M).d - (M).b * (M).c)
00054 #define detP(M) ((M)->a * (M)->d - (M)->b * (M)->c)
00055 
00056 
00057 static /* __inline__ */ int
00058 inversematrix (pdf_tmatrix *W, const pdf_tmatrix *M)
00059 {
00060   double  det;
00061 
00062   det = detP(M);
00063   if (fabs(det) < 1.e-8) {
00064 #if 1
00065     WARN("Inverting matrix with zero determinant...");
00066 #endif 
00067     return -1; /* result is undefined. */
00068   }
00069 
00070   W->a =  (M->d) / det;  W->b = -(M->b) / det;
00071   W->c = -(M->c) / det;  W->d =  (M->a) / det;
00072   W->e =  (M->c) * (M->f) - (M->d) * (M->e);
00073   W->f =  (M->b) * (M->e) - (M->a) * (M->f);
00074 
00075   return 0;
00076 }
00077 
00078 /* pdf_coord as vector */
00079 #define vecprd(v,w) ((v).x * (w).x + (v).y * (w).y)
00080 #define vecrot(v,w) ((v).x * (w).y - (v).y * (w).x)
00081 #define dsign(v)    (((v) >= 0.0) ? 1.0 : -1.0)
00082 /* acos => [0, pi] */
00083 #define vecang(v,w) ( \
00084   dsign(vecrot((v),(w))) * \
00085     acos(vecprd((v),(w)) / sqrt(vecprd((v),(v)) * vecprd((w),(w)))) \
00086 )
00087 
00088 static /* __inline__ */ int
00089 pdf_coord__equal (const pdf_coord *p1, const pdf_coord *p2)
00090 {
00091   if (fabs(p1->x - p2->x) < 1.e-7 &&
00092       fabs(p1->y - p2->y) < 1.e-7)
00093     return 1;
00094   return 0;
00095 }
00096 #define COORD_EQUAL(p,q) pdf_coord__equal((p),(q))
00097 
00098 #if 0
00099 static int
00100 pdf_coord__sort_compar_X (const void *pp1, const void *pp2)
00101 {
00102   pdf_coord *p1 = (pdf_coord *)pp1;
00103   pdf_coord *p2 = (pdf_coord *)pp2;
00104 
00105   if (pdf_coord__equal(p1, p2))
00106     return 0;
00107   else
00108     return (int) dsign(p1->x - p2->x);
00109  
00110   return 1;
00111 }
00112 
00113 static int
00114 pdf_coord__sort_compar_Y (const void *pp1, const void *pp2)
00115 {
00116   pdf_coord *p1 = (pdf_coord *)pp1;
00117   pdf_coord *p2 = (pdf_coord *)pp2;
00118 
00119   if (pdf_coord__equal(p1, p2))
00120     return 0;
00121   else
00122     return (int) dsign(p1->y - p2->y);
00123  
00124   return 1;
00125 }
00126 #endif
00127 
00128 
00129 static /* __inline__ */ int
00130 pdf_coord__transform (pdf_coord *p, const pdf_tmatrix *M)
00131 {
00132   double x, y;
00133 
00134   x = p->x; y = p->y;
00135   p->x = x * M->a + y * M->c + M->e;
00136   p->y = x * M->b + y * M->d + M->f;
00137 
00138   return 0;
00139 }
00140 
00141 #if 0
00142 static /* __inline__ */ int
00143 pdf_coord__itransform (pdf_coord *p, const pdf_tmatrix *M)
00144 {
00145   pdf_tmatrix W;  
00146   double      x, y;
00147   int         error;
00148 
00149   error = inversematrix(&W, M);
00150   if (error) 
00151     return error;
00152 
00153   x = p->x;  y = p->y;
00154   p->x = x * W.a + y * W.c + W.e;
00155   p->y = x * W.b + y * W.d + W.f;  
00156 
00157   return 0;
00158 }
00159 #endif
00160 
00161 static /* __inline__ */ int
00162 pdf_coord__dtransform (pdf_coord *p, const pdf_tmatrix *M)
00163 {
00164   double x, y;
00165 
00166   x = p->x; y = p->y;
00167   p->x = x * M->a + y * M->c;
00168   p->y = x * M->b + y * M->d;
00169 
00170   return 0;
00171 }
00172 
00173 static /* __inline__ */ int
00174 pdf_coord__idtransform (pdf_coord *p, const pdf_tmatrix *M)
00175 {
00176   pdf_tmatrix W;  
00177   double      x, y;
00178   int         error;
00179 
00180   error = inversematrix(&W, M);
00181   if (error) 
00182     return error;
00183 
00184   x = p->x;  y = p->y;
00185   p->x = x * W.a + y * W.c;
00186   p->y = x * W.b + y * W.d;
00187 
00188   return 0;
00189 }
00190 
00191 
00192 /* Modify M itself */
00193 void
00194 pdf_invertmatrix (pdf_tmatrix *M)
00195 {
00196   pdf_tmatrix W;  
00197   double      det;
00198 
00199   ASSERT(M);
00200 
00201   det = detP(M);
00202   if (fabs(det) < 1.e-8) {
00203     WARN("Inverting matrix with zero determinant...");
00204     W.a = 1.0; W.c = 0.0;
00205     W.b = 0.0; W.d = 1.0;
00206     W.e = 0.0; W.f = 0.0;
00207   } else {
00208     W.a =  (M->d) / det;  W.b = -(M->b) / det;
00209     W.c = -(M->c) / det;  W.d =  (M->a) / det;
00210     W.e =  (M->c) * (M->f) - (M->d) * (M->e);
00211     W.f =  (M->b) * (M->e) - (M->a) * (M->f);
00212   }
00213 
00214   pdf_copymatrix(M, &W);
00215 
00216   return;
00217 }
00218 
00219 
00220 typedef struct pa_elem_
00221 {
00222   int       type;
00223   pdf_coord p[3];
00224 } pa_elem;
00225 
00226 /* each subpath delimitted by moveto */
00227 struct pdf_path_
00228 {
00229   int       num_paths;
00230   int       max_paths;
00231   pa_elem  *path;
00232 };
00233 
00234 static const struct {
00235   char        opchr;  /* PDF operator char  */
00236   int         n_pts;  /* number of *points* */
00237   const char *strkey;
00238 } petypes[] = {
00239 #define PE_TYPE__INVALID  -1
00240 #define PE_TYPE__MOVETO    0
00241   {'m', 1, "moveto"  },
00242 #define PE_TYPE__LINETO    1 
00243   {'l', 1, "lineto"  },
00244 #define PE_TYPE__CURVETO   2 
00245   {'c', 3, "curveto" },
00246   /* no PS correspondence for v and y */
00247 #define PE_TYPE__CURVETO_V 3 
00248   {'v', 2, "vcurveto"}, /* current point replicated */
00249 #define PE_TYPE__CURVETO_Y 4 
00250   {'y', 2, "ycurveto"}, /* last point replicated */
00251 #define PE_TYPE__CLOSEPATH 5
00252   {'h', 0, "closepath"}, 
00253 #define PE_TYPE__TERMINATE 6
00254   {' ', 0,  NULL}
00255 };
00256 
00257 #define PE_VALID(p) ((p) && \
00258   (p)->type > PE_TYPE__INVALID && (p)->type < PE_TYPE__TERMINATE)
00259 #define PE_N_PTS(p)  (PE_VALID((p)) ? petypes[(p)->type].n_pts : 0)
00260 #define PE_OPCHR(p)  (PE_VALID((p)) ? petypes[(p)->type].opchr : ' ')
00261 
00262 #define PA_LENGTH(pa) ((pa)->num_paths)
00263 
00264 #define GS_FLAG_CURRENTPOINT_SET (1 << 0)
00265 
00266 
00267 static char    fmt_buf[1024]; /* FIXME */
00268 #define FORMAT_BUFF_PTR(p) fmt_buf
00269 #define FORMAT_BUFF_LEN(p) 1024
00270 
00271 static void
00272 init_a_path (pdf_path *p)
00273 {
00274   ASSERT(p);
00275 
00276   p->num_paths = 0;
00277   p->max_paths = 0;
00278   p->path      = NULL;
00279 
00280   return;
00281 }
00282 
00283 static void
00284 pdf_path__clearpath (pdf_path *p)
00285 {
00286   ASSERT(p);
00287 
00288   p->num_paths = 0;
00289   
00290   return;
00291 }
00292 
00293 static int
00294 pdf_path__growpath  (pdf_path *p, int max_pe)
00295 {
00296   if (max_pe < p->max_paths)
00297     return 0;
00298 
00299   p->max_paths = MAX(p->max_paths + 8, max_pe);
00300   p->path = RENEW(p->path, p->max_paths, pa_elem);
00301 
00302   return 0;
00303 }
00304     
00305 static void
00306 clear_a_path (pdf_path *p)
00307 {
00308   ASSERT(p);
00309 
00310   if (p->path)
00311     RELEASE(p->path);
00312   p->path = NULL;
00313   p->num_paths = 0;
00314   p->max_paths = 0;
00315 
00316   return;
00317 }
00318 
00319 static int
00320 pdf_path__copypath (pdf_path *p1, const pdf_path *p0)
00321 {
00322   pa_elem  *pe0, *pe1;
00323   int       i;
00324 
00325   pdf_path__growpath(p1, PA_LENGTH(p0));
00326   for (i = 0; i < PA_LENGTH(p0); i++) {
00327     pe1 = &(p1->path[i]);
00328     pe0 = &(p0->path[i]);
00329     /* FIXME */
00330     pe1->type   = pe0->type;
00331     pe1->p[0].x = pe0->p[0].x;
00332     pe1->p[0].y = pe0->p[0].y;
00333     pe1->p[1].x = pe0->p[1].x;
00334     pe1->p[1].y = pe0->p[1].y;
00335     pe1->p[2].x = pe0->p[2].x;
00336     pe1->p[2].y = pe0->p[2].y;
00337   }
00338   p1->num_paths = PA_LENGTH(p0);
00339 
00340   return 0;
00341 }
00342 
00343 
00344 /* start new subpath */
00345 static int
00346 pdf_path__moveto  (pdf_path        *pa,
00347                    pdf_coord       *cp,
00348                    const pdf_coord *p0)
00349 {
00350   pa_elem  *pe;
00351   
00352   pdf_path__growpath(pa, PA_LENGTH(pa) + 1);
00353   if (PA_LENGTH(pa) > 0) {
00354     pe = &pa->path[pa->num_paths-1];
00355     if (pe->type == PE_TYPE__MOVETO) {
00356       pe->p[0].x = cp->x = p0->x;
00357       pe->p[0].y = cp->y = p0->y;
00358       return 0;
00359     }
00360   }
00361   pe = &pa->path[pa->num_paths++];
00362   pe->type   = PE_TYPE__MOVETO;
00363   pe->p[0].x = cp->x = p0->x;
00364   pe->p[0].y = cp->y = p0->y;
00365 
00366   return 0;  
00367 }
00368 
00369 /* Do 'compression' of path while adding new path elements.
00370  * Sequantial moveto command will be replaced with a
00371  * single moveto. If cp is not equal to the last point in pa,
00372  * then moveto is inserted (starting new subpath).
00373  * FIXME: 
00374  * 'moveto' must be used to enforce starting new path.
00375  * This affects how 'closepath' is treated.     
00376  */
00377 static pa_elem *
00378 pdf_path__next_pe (pdf_path *pa, const pdf_coord *cp)
00379 {
00380   pa_elem  *pe;
00381 
00382   pdf_path__growpath(pa, PA_LENGTH(pa) + 2);
00383   if (PA_LENGTH(pa) == 0) {
00384     pe = &pa->path[pa->num_paths++];
00385     pe->type   = PE_TYPE__MOVETO;
00386     pe->p[0].x = cp->x;
00387     pe->p[0].y = cp->y;
00388 
00389     return &pa->path[pa->num_paths++];
00390   }
00391     
00392   pe = &pa->path[pa->num_paths-1];
00393   switch (pe->type) {
00394   case PE_TYPE__MOVETO:
00395     pe->p[0].x = cp->x;
00396     pe->p[0].y = cp->y;
00397     break;
00398   case PE_TYPE__LINETO:
00399     if (!COORD_EQUAL(&pe->p[0], cp)) {
00400       pe = &pa->path[pa->num_paths++];
00401       pe->type   = PE_TYPE__MOVETO;
00402       pe->p[0].x = cp->x;
00403       pe->p[0].y = cp->y;
00404     }
00405     break;
00406   case PE_TYPE__CURVETO:
00407     if (!COORD_EQUAL(&pe->p[2], cp)) {
00408       pe = &pa->path[pa->num_paths++];
00409       pe->type   = PE_TYPE__MOVETO;
00410       pe->p[0].x = cp->x;
00411       pe->p[0].y = cp->y;
00412     }
00413     break;
00414   case PE_TYPE__CURVETO_Y:
00415   case PE_TYPE__CURVETO_V:
00416     if (!COORD_EQUAL(&pe->p[1], cp)) {
00417       pe = &pa->path[pa->num_paths++];
00418       pe->type   = PE_TYPE__MOVETO;
00419       pe->p[0].x = cp->x;
00420       pe->p[0].y = cp->y;
00421     }
00422     break;
00423   case PE_TYPE__CLOSEPATH:
00424     pe = &pa->path[pa->num_paths++];
00425     pe->type   = PE_TYPE__MOVETO;
00426     pe->p[0].x = cp->x;
00427     pe->p[0].y = cp->y;
00428     break;
00429   }
00430 
00431   return &pa->path[pa->num_paths++];
00432 }
00433 
00434 static int
00435 pdf_path__transform (pdf_path *pa, const pdf_tmatrix *M)
00436 {
00437   pa_elem *pe;
00438   int      n = 0, i;
00439 
00440   ASSERT(pa && M);
00441 
00442   for (i = 0; i < PA_LENGTH(pa); i++) {
00443     pe = &(pa->path[i]);
00444     n  = PE_N_PTS(pe);
00445     while (n-- > 0)
00446       pdf_coord__transform(&(pe->p[n]), M);
00447   }
00448 
00449   return 0;
00450 }
00451 
00452 
00453 /* Path Construction */
00454 static int
00455 pdf_path__lineto (pdf_path        *pa,
00456                   pdf_coord       *cp,
00457                   const pdf_coord *p0)
00458 {
00459   pa_elem  *pe;
00460 
00461   pe = pdf_path__next_pe(pa, cp);
00462   pe->type   = PE_TYPE__LINETO;
00463   pe->p[0].x = cp->x = p0->x;
00464   pe->p[0].y = cp->y = p0->y;
00465 
00466   return 0;
00467 }
00468 
00469 static int
00470 pdf_path__curveto (pdf_path        *pa,
00471                    pdf_coord       *cp,
00472                    const pdf_coord *p0,
00473                    const pdf_coord *p1,
00474                    const pdf_coord *p2
00475                   )
00476 {
00477   pa_elem *pe;
00478 
00479   pe = pdf_path__next_pe(pa, cp);
00480   if (COORD_EQUAL(cp, p0)) {
00481     pe->type   = PE_TYPE__CURVETO_V;
00482     pe->p[0].x = p1->x;
00483     pe->p[0].y = p1->y;
00484     pe->p[1].x = cp->x = p2->x;
00485     pe->p[1].y = cp->y = p2->y;
00486   } else if (COORD_EQUAL(p1, p2)) {
00487     pe->type   = PE_TYPE__CURVETO_Y;
00488     pe->p[0].x = p0->x;
00489     pe->p[0].y = p0->y;
00490     pe->p[1].x = cp->x = p1->x;
00491     pe->p[1].y = cp->y = p1->y;
00492   } else {
00493     pe->type   = PE_TYPE__CURVETO;
00494     pe->p[0].x = p0->x;
00495     pe->p[0].y = p0->y;
00496     pe->p[1].x = p1->x;
00497     pe->p[1].y = p1->y;
00498     pe->p[2].x = cp->x = p2->x;
00499     pe->p[2].y = cp->y = p2->y;
00500   }
00501 
00502   return 0;
00503 }
00504 
00505 #if 0
00506 #define QB_TWO_THIRD (2.0/3.0)
00507 #define QB_ONE_THIRD (1.0/3.0)
00508 
00509 static int
00510 pdf_path__curveto_QB (pdf_path        *pa,
00511                       pdf_coord       *cp,
00512                       const pdf_coord *p0,
00513                       const pdf_coord *p1
00514                      )
00515 {
00516   pdf_coord  q0, q1;
00517 
00518   q0.x = cp->x + QB_TWO_THIRD * (p0->x - cp->x);
00519   q0.y = cp->y + QB_TWO_THIRD * (p0->y - cp->y);
00520   q1.x = p0->x + QB_ONE_THIRD * (p1->x - p0->x);
00521   q1.y = p0->y + QB_ONE_THIRD * (p1->y - p0->y);
00522   /* q2 == p1 */
00523  
00524   return pdf_path__curveto(pa, cp, &q0, &q1, p1);
00525 }
00526 #endif
00527 
00528 
00529 /* This isn't specified as cp to somewhere. */
00530 static int
00531 pdf_path__elliptarc (pdf_path         *pa,
00532                      pdf_coord        *cp,
00533                      const pdf_coord  *ca,   /* ellipsis center        */
00534                      double            r_x,  /* x radius               */
00535                      double            r_y,  /* y radius               */
00536                      double            xar,  /* x-axis-rotation (deg!) */
00537                      double            a_0,  /* start angle            */
00538                      double            a_1,  /* stop angle             */
00539                      int               a_d   /* arc orientation        */
00540                     )
00541 {
00542   double      b, b_x, b_y;
00543   double      d_a, q;
00544   pdf_coord   p0, p1, p2, p3;
00545   pdf_coord   e0, e1;
00546   pdf_tmatrix T;
00547   int         n_c; /* number of segments */
00548   int         i, error = 0;
00549 
00550   if (fabs(r_x) < 1.e-8 ||
00551       fabs(r_y) < 1.e-8)
00552     return -1;
00553 
00554   if (a_d < 0) {
00555     for ( ; a_1 > a_0; a_1 -= 360.0);
00556   } else {
00557     for ( ; a_1 < a_0; a_0 -= 360.0);
00558   }
00559 
00560   d_a  = a_1 - a_0;
00561   for (n_c = 1; fabs(d_a) > 90.0 * n_c; n_c++);
00562   d_a /= n_c;
00563   if (fabs(d_a) < 1.e-8)
00564     return -1;
00565 
00566   a_0 *= M_PI / 180.0;
00567   a_1 *= M_PI / 180.0;
00568   d_a *= M_PI / 180.0;
00569   xar *= M_PI / 180.0;
00570   T.a  = cos(xar);  T.c = -sin(xar);
00571   T.b  = -T.c    ;  T.d = T.a;
00572   T.e  = 0.0     ;  T.f = 0.0;
00573 
00574   /* A parameter that controls cb-curve (off-curve) points */
00575   b    = 4.0 * (1.0 - cos(.5 * d_a)) / (3.0 * sin(.5 * d_a));
00576   b_x  = r_x * b;
00577   b_y  = r_y * b;
00578 
00579   p0.x = r_x * cos(a_0);
00580   p0.y = r_y * sin(a_0);
00581   pdf_coord__transform(&p0, &T);
00582   p0.x += ca->x; p0.y += ca->y;
00583   if (PA_LENGTH(pa) == 0) {
00584     pdf_path__moveto(pa, cp, &p0);
00585   } else if (!COORD_EQUAL(cp, &p0)) {
00586     pdf_path__lineto(pa, cp, &p0); /* add line seg */
00587   } 
00588   for (i = 0; !error && i < n_c; i++) {
00589     q = a_0 + i * d_a;
00590     e0.x = cos(q); e0.y = sin(q);
00591     e1.x = cos(q + d_a); e1.y = sin(q + d_a);
00592 
00593    /* Condition for tangent vector requirs
00594     *  d1 = p1 - p0 = f ( sin a, -cos a)
00595     *  d2 = p2 - p3 = g ( sin b, -cos b)
00596     * and from symmetry
00597     *  g^2 = f^2
00598     */ 
00599     p0.x = r_x * e0.x; /* s.p. */
00600     p0.y = r_y * e0.y;
00601     p3.x = r_x * e1.x; /* e.p. */
00602     p3.y = r_y * e1.y;
00603 
00604     p1.x = -b_x * e0.y;
00605     p1.y =  b_y * e0.x;
00606     p2.x =  b_x * e1.y;
00607     p2.y = -b_y * e1.x;
00608 
00609     pdf_coord__transform(&p0, &T);
00610     pdf_coord__transform(&p1, &T);
00611     pdf_coord__transform(&p2, &T);
00612     pdf_coord__transform(&p3, &T);
00613     p0.x += ca->x; p0.y += ca->y;
00614     p3.x += ca->x; p3.y += ca->y;
00615     p1.x += p0.x ; p1.y += p0.y ;
00616     p2.x += p3.x ; p2.y += p3.y ;
00617 
00618     error = pdf_path__curveto(pa, &p0, &p1, &p2, &p3);
00619     cp->x = p3.x; cp->y = p3.y;
00620   }
00621 
00622   return error;
00623 }
00624 
00625 static int
00626 pdf_path__closepath (pdf_path *pa, pdf_coord *cp /* no arg */)
00627 {
00628   pa_elem  *pe = NULL;
00629   int       i;
00630 
00631   /* search for start point of the last subpath */
00632   for (i = PA_LENGTH(pa) - 1; i >= 0; i--) {
00633     pe = &pa->path[i];
00634     if (pe->type == PE_TYPE__MOVETO)
00635       break;
00636   }
00637 
00638   if (!pe || i < 0)
00639     return -1; /* No path or no start point(!) */
00640 
00641   cp->x = pe->p[0].x;
00642   cp->y = pe->p[0].y;
00643 
00644   pdf_path__growpath(pa, PA_LENGTH(pa) + 1);
00645 
00646   /* NOTE:
00647    *  Manually closed path without closepath is not
00648    *  affected by linejoin. A path with coincidental
00649    *  starting and ending point is not the same as
00650    *  'closed' path.
00651    */
00652   pe = &pa->path[pa->num_paths++];
00653   pe->type = PE_TYPE__CLOSEPATH;
00654 
00655   return 0;
00656 }
00657 
00658 /*
00659  *  x y width height re
00660  * 
00661  * is equivalent to
00662  *
00663  *  x y m
00664  *  (x + width) y l
00665  *  (x + width) (y + height) l
00666  *  x (y + height) l
00667  *  h
00668  */
00669 /* Just for quick test */ 
00670 static /* __inline__ */ int
00671 pdf_path__isarect (pdf_path *pa,
00672                    int       f_ir /* fill-rule is ignorable */
00673                   )
00674 {
00675   pa_elem *pe0, *pe1, *pe2, *pe3, *pe4;
00676 
00677   if (PA_LENGTH(pa) == 5) {
00678     pe0 = &(pa->path[0]);
00679     pe1 = &(pa->path[1]);
00680     pe2 = &(pa->path[2]);
00681     pe3 = &(pa->path[3]);
00682     pe4 = &(pa->path[4]);
00683     if (pe0->type == PE_TYPE__MOVETO &&
00684         pe1->type == PE_TYPE__LINETO &&
00685         pe2->type == PE_TYPE__LINETO &&
00686         pe3->type == PE_TYPE__LINETO &&
00687         pe4->type == PE_TYPE__CLOSEPATH) {
00688       if (pe1->p[0].y - pe0->p[0].y == 0 &&
00689           pe2->p[0].x - pe1->p[0].x == 0 &&
00690           pe3->p[0].y - pe2->p[0].y == 0) {
00691         if (pe1->p[0].x - pe0->p[0].x
00692             == pe2->p[0].x - pe3->p[0].x) {
00693           return 1;
00694         }
00695     /* Winding number is different but ignore it here. */
00696       } else if (f_ir &&
00697                  pe1->p[0].x - pe0->p[0].x == 0 &&
00698                  pe2->p[0].y - pe1->p[0].y == 0 &&
00699                  pe3->p[0].x - pe2->p[0].x == 0) {
00700         if (pe1->p[0].y - pe0->p[0].y
00701               == pe2->p[0].y - pe3->p[0].y) {
00702           return 1;
00703         }
00704       }
00705     }
00706   }
00707 
00708   return 0;
00709 }
00710 
00711 /* Path Painting */
00712 /* F is obsoleted */
00713 #define PT_OP_VALID(c) ( \
00714  (c) == 'f' || (c) == 'F' || \
00715  (c) == 's' || (c) == 'S' || \
00716  (c) == 'b' || (c) == 'B' || \
00717  (c) == 'W' \
00718 )
00719 
00720 static /* __inline__ */ int
00721 INVERTIBLE_MATRIX (const pdf_tmatrix *M)
00722 {
00723   if (fabs(detP(M)) < 1.e-8) {
00724     WARN("Transformation matrix not invertible.");
00725     WARN("--- M = [%g %g %g %g %g %g]",
00726          M->a, M->b, M->c, M->d, M->e, M->f);
00727     return -1;
00728   }
00729   return 0;
00730 }
00731 
00732 /* rectfill, rectstroke, rectclip, recteoclip
00733  *
00734  * Draw isolated rectangle without actually doing
00735  * gsave/grestore operation.
00736  * 
00737  * TODO:
00738  *  linestyle, fill-opacity, stroke-opacity,....
00739  *  As this routine draw a single graphics object
00740  *  each time, there should be options for specifying
00741  *  various drawing styles, which might inherite
00742  *  current graphcs state parameter.
00743  */ 
00744 static int
00745 pdf_dev__rectshape (pdf_dev           *P,
00746                     const pdf_rect    *r,
00747                     const pdf_tmatrix *M,
00748                     char               opchr
00749                    )
00750 {
00751   char     *buf = FORMAT_BUFF_PTR(P);
00752   int       len = 0;
00753   int       isclip = 0;
00754   pdf_coord p;
00755   double    wd, ht;
00756 
00757   ASSERT(r && PT_OP_VALID(opchr));
00758 
00759   isclip = (opchr == 'W') ? 1 : 0;
00760 
00761   /* disallow matrix for clipping.
00762    * q ... clip Q does nothing and
00763    * n M cm ... clip n alter CTM.
00764    */
00765   if (M && (isclip ||
00766             !INVERTIBLE_MATRIX(M)))
00767     return -1;
00768 
00769   graphics_mode();
00770 
00771   buf[len++] = ' ';
00772   if (!isclip) {
00773     buf[len++] = 'q';
00774     if (M) {
00775       buf[len++] = ' ';
00776       len += pdf_sprint_matrix(buf + len, M);
00777       buf[len++] = ' ';
00778       buf[len++] = 'c'; buf[len++] = 'm';
00779     }
00780     buf[len++] = ' ';
00781   }
00782   buf[len++] = 'n';
00783 
00784   p.x = r->llx; p.y = r->lly;
00785   wd  = r->urx - r->llx;
00786   ht  = r->ury - r->lly;
00787   buf[len++] = ' ';
00788   len += pdf_sprint_coord (buf + len, &p);
00789   buf[len++] = ' ';
00790   len += pdf_sprint_length(buf + len, wd);
00791   buf[len++] = ' ';
00792   len += pdf_sprint_length(buf + len, ht);
00793   buf[len++] = ' ';
00794   buf[len++] = 'r'; buf[len++] = 'e';
00795 
00796   buf[len++] = ' ';
00797   buf[len++] = opchr;
00798 
00799   buf[len++] = ' ';
00800   buf[len++] = isclip ? 'n' : 'Q';
00801 
00802   pdf_doc_add_page_content(buf, len);  /* op: q cm n re Q */
00803 
00804   return 0;
00805 }
00806 
00807 /* FIXME */
00808 static int
00809 pdf_dev__flushpath (pdf_dev   *P,
00810                     pdf_path  *pa,
00811                     char       opchr,
00812                     int        rule,
00813                     int        ignore_rule)
00814 {
00815   pa_elem   *pe, *pe1;
00816   char      *b      = FORMAT_BUFF_PTR(P);
00817   long       b_len  = FORMAT_BUFF_LEN(P);
00818   pdf_rect   r; /* FIXME */
00819   pdf_coord *pt;
00820   int        n_pts, n_seg;
00821   int        len = 0;
00822   int        isrect, i, j;
00823 
00824   ASSERT(pa && PT_OP_VALID(opchr));
00825 
00826   if (PA_LENGTH(pa) <= 0)
00827     return 0;
00828 
00829   graphics_mode();
00830   isrect = pdf_path__isarect(pa, ignore_rule); 
00831   if (isrect) {
00832     pe  = &(pa->path[0]);
00833     pe1 = &(pa->path[2]);
00834 
00835     r.llx = pe->p[0].x;
00836     r.lly = pe->p[0].y;
00837     r.urx = pe1->p[0].x - pe->p[0].x; /* width...  */
00838     r.ury = pe1->p[0].y - pe->p[0].y; /* height... */
00839 
00840     b[len++] = ' ';
00841     len += pdf_sprint_rect(b + len, &r);
00842     b[len++] = ' ';
00843     b[len++] = 'r';
00844     b[len++] = 'e';
00845     pdf_doc_add_page_content(b, len);  /* op: re */
00846     len = 0;
00847   } else {
00848     n_seg = PA_LENGTH(pa);
00849     for (i = 0, len = 0, pe = &pa->path[0];
00850          i < n_seg; pe++, i++) {
00851       n_pts = PE_N_PTS(pe);
00852       for (j = 0, pt = &pe->p[0];
00853            j < n_pts; j++, pt++) {
00854         b[len++] = ' ';
00855         len += pdf_sprint_coord(b + len, pt);
00856       }
00857       b[len++] = ' ';
00858       b[len++] = PE_OPCHR(pe);
00859       if (len + 128 > b_len) {
00860         pdf_doc_add_page_content(b, len);  /* op: m l c v y h */
00861        len = 0;
00862       }
00863     }
00864     if (len > 0) {
00865       pdf_doc_add_page_content(b, len);  /* op: m l c v y h */
00866       len = 0;
00867     }
00868   }
00869 
00870   b[len++] = ' ';
00871   b[len++] = opchr;
00872   if (rule == PDF_FILL_RULE_EVENODD)
00873     b[len++] = '*';
00874 
00875   pdf_doc_add_page_content(b, len);  /* op: f F s S b B W f* F* s* S* b* B* W* */
00876 
00877   return 0;
00878 }
00879 
00880 
00881 /* Graphics State */
00882 typedef struct pdf_gstate_
00883 {
00884   pdf_coord   cp;
00885 
00886   pdf_tmatrix matrix;   /* cm,  - */
00887 
00888   pdf_color   strokecolor;
00889   pdf_color   fillcolor;
00890   /* colorspace here */
00891 
00892   struct {
00893     int     num_dash;
00894     double  pattern[PDF_DASH_SIZE_MAX];
00895     double  offset;
00896   } linedash;           /* d,  D  */
00897 
00898   double    linewidth;  /* w,  LW */
00899 
00900   int       linecap;    /* J,  LC */
00901   int       linejoin;   /* j,  LJ */
00902   double    miterlimit; /* M,  ML */
00903 
00904   int       flatness;   /* i,  FL, 0 to 100 (0 for use device-default) */
00905 
00906   /* internal */
00907   pdf_path  path;
00908   long      flags;
00909 } pdf_gstate;
00910 
00911 
00912 typedef struct m_stack_elem
00913 {
00914   void                *data;
00915   struct m_stack_elem *prev;
00916 } m_stack_elem;
00917 
00918 typedef struct m_stack
00919 {
00920   int           size;
00921   m_stack_elem *top;
00922   m_stack_elem *bottom;
00923 } m_stack;
00924 
00925 static void
00926 m_stack_init (m_stack *stack)
00927 {
00928   ASSERT(stack);
00929 
00930   stack->size   = 0;
00931   stack->top    = NULL;
00932   stack->bottom = NULL;
00933 
00934   return;
00935 }
00936 
00937 static void
00938 m_stack_push (m_stack *stack, void *data)
00939 {
00940   m_stack_elem  *elem;
00941 
00942   ASSERT(stack);
00943 
00944   elem = NEW(1, m_stack_elem);
00945   elem->prev = stack->top;
00946   elem->data = data;
00947 
00948   stack->top = elem;
00949   if (stack->size == 0)
00950     stack->bottom = elem;
00951 
00952   stack->size++;
00953 
00954   return;
00955 }
00956 
00957 static void *
00958 m_stack_pop (m_stack *stack)
00959 {
00960   m_stack_elem *elem;
00961   void         *data;
00962 
00963   ASSERT(stack);
00964 
00965   if (stack->size == 0)
00966     return NULL;
00967 
00968   data = stack->top->data;
00969   elem = stack->top;
00970   stack->top = elem->prev;
00971   if (stack->size == 1)
00972     stack->bottom = NULL;
00973   RELEASE(elem);
00974 
00975   stack->size--;
00976 
00977   return data;
00978 }
00979 
00980 static void *
00981 m_stack_top (m_stack *stack)
00982 {
00983   void  *data;
00984 
00985   ASSERT(stack);
00986 
00987   if (stack->size == 0)
00988     return NULL;
00989 
00990   data = stack->top->data;
00991 
00992   return data;
00993 }
00994 
00995 #define m_stack_depth(s)    (((s) != NULL) ? (s)->size : 0)
00996 
00997 static m_stack gs_stack;
00998 
00999 static void
01000 init_a_gstate (pdf_gstate *gs)
01001 {
01002   gs->cp.x = 0.0;
01003   gs->cp.y = 0.0;
01004 
01005   pdf_setmatrix(&gs->matrix, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
01006 
01007   pdf_color_black(&gs->strokecolor);
01008   pdf_color_black(&gs->fillcolor);
01009 
01010   gs->linedash.num_dash = 0;
01011   gs->linedash.offset   = 0;
01012   gs->linecap    = 0;
01013   gs->linejoin   = 0;
01014   gs->linewidth  = 1.0;
01015   gs->miterlimit = 10.0;
01016 
01017   gs->flatness   = 1; /* default to 1 in PDF */
01018 
01019   /* Internal variables */
01020   gs->flags = 0;
01021   init_a_path(&gs->path);
01022 
01023   return;
01024 }
01025 
01026 static void
01027 clear_a_gstate (pdf_gstate *gs)
01028 {
01029   clear_a_path(&gs->path);
01030   memset(gs, 0, sizeof(pdf_gstate));
01031 
01032   return;
01033 }
01034 
01035 static void
01036 copy_a_gstate (pdf_gstate *gs1, pdf_gstate *gs2)
01037 {
01038   int   i;
01039 
01040   ASSERT(gs1 && gs2);
01041 
01042   gs1->cp.x = gs2->cp.x;
01043   gs1->cp.y = gs2->cp.y;
01044 
01045   pdf_copymatrix(&gs1->matrix, &gs2->matrix);
01046 
01047   /* TODO:
01048    * Path should be linked list and gsave only
01049    * record starting point within path rather than
01050    * copying whole path.
01051    */
01052   pdf_path__copypath(&gs1->path, &gs2->path);
01053 
01054   gs1->linedash.num_dash = gs2->linedash.num_dash;
01055   for (i = 0; i < gs2->linedash.num_dash; i++) {
01056     gs1->linedash.pattern[i] = gs2->linedash.pattern[i];
01057   }
01058   gs1->linedash.offset = gs2->linedash.offset;
01059 
01060   gs1->linecap    = gs2->linecap;
01061   gs1->linejoin   = gs2->linejoin;
01062   gs1->linewidth  = gs2->linewidth;
01063   gs1->miterlimit = gs2->miterlimit;
01064   gs1->flatness   = gs2->flatness;
01065 
01066   pdf_color_copycolor(&gs1->fillcolor  , &gs2->fillcolor);
01067   pdf_color_copycolor(&gs1->strokecolor, &gs2->strokecolor);
01068 
01069   return;
01070 }
01071     
01072 void
01073 pdf_dev_init_gstates (void)
01074 {
01075   pdf_gstate *gs;
01076 
01077   m_stack_init(&gs_stack);
01078 
01079   gs = NEW(1, pdf_gstate);
01080   init_a_gstate(gs);
01081 
01082   m_stack_push(&gs_stack, gs); /* Initial state */
01083 
01084   return;
01085 }
01086 
01087 void
01088 pdf_dev_clear_gstates (void)
01089 {
01090   pdf_gstate *gs;
01091 
01092   if (m_stack_depth(&gs_stack) > 1) /* at least 1 elem. */
01093     WARN("GS stack depth is not zero at the end of the document.");
01094 
01095   while ((gs = m_stack_pop(&gs_stack)) != NULL) {
01096     clear_a_gstate(gs);
01097     RELEASE(gs);
01098   }
01099   return;
01100 }
01101 
01102 int
01103 pdf_dev_gsave (void)
01104 {
01105   pdf_gstate *gs0, *gs1;
01106 
01107   gs0 = m_stack_top(&gs_stack);
01108   gs1 = NEW(1, pdf_gstate);
01109   init_a_gstate(gs1);
01110   copy_a_gstate(gs1, gs0);
01111   m_stack_push(&gs_stack, gs1);
01112 
01113   pdf_doc_add_page_content(" q", 2);  /* op: q */
01114 
01115   return 0;
01116 }
01117 
01118 int
01119 pdf_dev_grestore (void)
01120 {
01121   pdf_gstate *gs;
01122 
01123   if (m_stack_depth(&gs_stack) <= 1) { /* Initial state at bottom */
01124     WARN("Too many grestores.");
01125     return  -1;
01126   }
01127 
01128   gs = m_stack_pop(&gs_stack);
01129   clear_a_gstate(gs);
01130   RELEASE(gs);
01131 
01132   pdf_doc_add_page_content(" Q", 2);  /* op: Q */
01133 
01134   pdf_dev_reset_fonts();
01135 
01136   return  0;
01137 }
01138 
01139 
01140 int
01141 pdf_dev_push_gstate (void)
01142 {
01143   m_stack    *gss = &gs_stack;
01144   pdf_gstate *gs0;
01145 
01146   gs0 = NEW(1, pdf_gstate);
01147 
01148   init_a_gstate(gs0);
01149 
01150   m_stack_push(gss, gs0);
01151 
01152   return 0;
01153 }
01154 
01155 
01156 int
01157 pdf_dev_pop_gstate (void)
01158 {
01159   m_stack    *gss = &gs_stack;
01160   pdf_gstate *gs;
01161 
01162   if (m_stack_depth(gss) <= 1) { /* Initial state at bottom */
01163     WARN("Too many grestores.");
01164     return  -1;
01165   }
01166 
01167   gs = m_stack_pop(gss);
01168   clear_a_gstate(gs);
01169   RELEASE(gs);
01170 
01171   return  0;
01172 }
01173 
01174 
01175 int
01176 pdf_dev_current_depth (void)
01177 {
01178   return (m_stack_depth(&gs_stack) - 1); /* 0 means initial state */
01179 }
01180 
01181 void
01182 pdf_dev_grestore_to (int depth)
01183 {
01184   m_stack    *gss = &gs_stack;
01185   pdf_gstate *gs;
01186 
01187   ASSERT(depth >= 0);
01188 
01189   if (m_stack_depth(gss) > depth + 1) {
01190     WARN("Closing pending transformations at end of page/XObject.");
01191   }
01192 
01193   while (m_stack_depth(gss) > depth + 1) {
01194     pdf_doc_add_page_content(" Q", 2);  /* op: Q */
01195     gs = m_stack_pop(gss);
01196     clear_a_gstate(gs);
01197     RELEASE(gs);
01198   }
01199   pdf_dev_reset_fonts();
01200 
01201   return;
01202 }
01203 
01204 int
01205 pdf_dev_currentpoint (pdf_coord *p)
01206 {
01207   m_stack    *gss = &gs_stack;
01208   pdf_gstate *gs  = m_stack_top(gss);
01209   pdf_coord  *cpt = &gs->cp;
01210 
01211   ASSERT(p);
01212 
01213   p->x = cpt->x; p->y = cpt->y;
01214 
01215   return 0;
01216 }
01217 
01218 int
01219 pdf_dev_currentmatrix (pdf_tmatrix *M)
01220 {
01221   m_stack     *gss = &gs_stack;
01222   pdf_gstate  *gs  = m_stack_top(gss);
01223   pdf_tmatrix *CTM = &gs->matrix;
01224 
01225   ASSERT(M);
01226 
01227   pdf_copymatrix(M, CTM);
01228 
01229   return 0;
01230 }
01231 
01232 #if  0
01233 int
01234 pdf_dev_currentcolor (pdf_color *color, int is_fill)
01235 {
01236   m_stack    *gss = &gs_stack;
01237   pdf_gstate *gs  = m_stack_top(gss);
01238   pdf_color  *fcl = &gs->fillcolor;
01239   pdf_color  *scl = &gs->strokecolor;
01240 
01241   ASSERT(color);
01242 
01243   pdf_color_copycolor(color, is_fill ? fcl : scl);
01244 
01245   return 0;
01246 }
01247 #endif /* 0 */
01248 
01249 /*
01250  * mask == 0 means stroking color, mask == 0x20 nonstroking color
01251  *
01252  * force == 1 means that operators will be generated even if
01253  *   the color is the same as the current graphics state color
01254  */
01255 void
01256 pdf_dev_set_color (const pdf_color *color, char mask, int force)
01257 {
01258   int len;
01259 
01260   pdf_gstate *gs  = m_stack_top(&gs_stack);
01261   pdf_color *current = mask ? &gs->fillcolor : &gs->strokecolor;
01262 
01263   ASSERT(pdf_color_is_valid(color));
01264 
01265   if (!(pdf_dev_get_param(PDF_DEV_PARAM_COLORMODE) &&
01266        (force || pdf_color_compare(color, current))))
01267     /* If "color" is already the current color, then do nothing
01268      * unless a color operator is forced
01269      */
01270     return;
01271 
01272   graphics_mode();
01273   len = pdf_color_to_string(color, fmt_buf);
01274   fmt_buf[len++] = ' ';
01275   switch (pdf_color_type(color)) {
01276   case  PDF_COLORSPACE_TYPE_RGB:
01277     fmt_buf[len++] = 'R' | mask;
01278     fmt_buf[len++] = 'G' | mask;
01279     break;
01280   case  PDF_COLORSPACE_TYPE_CMYK:
01281     fmt_buf[len++] = 'K' | mask;
01282     break;
01283   case  PDF_COLORSPACE_TYPE_GRAY:
01284     fmt_buf[len++] = 'G' | mask;
01285     break;
01286   default: /* already verified the given color */
01287     break;
01288   }
01289   pdf_doc_add_page_content(fmt_buf, len);  /* op: RG K G rg k g */
01290 
01291   pdf_color_copycolor(current, color);
01292 }
01293 
01294 void
01295 pdf_dev_reset_color (int force)
01296 {
01297   pdf_color *sc, *fc;
01298 
01299   pdf_color_get_current(&sc, &fc);
01300   pdf_dev_set_color(sc,    0, force);
01301   pdf_dev_set_color(fc, 0x20, force);
01302 }
01303 
01304 int
01305 pdf_dev_concat (const pdf_tmatrix *M)
01306 {
01307   m_stack     *gss = &gs_stack;
01308   pdf_gstate  *gs  = m_stack_top(gss);
01309   pdf_path    *cpa = &gs->path;
01310   pdf_coord   *cpt = &gs->cp;
01311   pdf_tmatrix *CTM = &gs->matrix;
01312   pdf_tmatrix  W   = {0, 0, 0, 0, 0, 0};  /* Init to avoid compiler warning */
01313   char        *buf = FORMAT_BUFF_PTR(NULL);
01314   int          len = 0;
01315 
01316   ASSERT(M);
01317 
01318   /* Adobe Reader erases page content if there are
01319    * non invertible transformation.
01320    */
01321   if (fabs(detP(M)) < 1.0e-8) {
01322     WARN("Transformation matrix not invertible.");
01323     WARN("--- M = [%g %g %g %g %g %g]",
01324          M->a, M->b, M->c, M->d, M->e, M->f);
01325     return -1;
01326   }
01327 
01328   buf[len++] = ' ';
01329   len += pdf_sprint_matrix(buf + len, M);
01330   buf[len++] = ' ';
01331   buf[len++] = 'c';
01332   buf[len++] = 'm';
01333   pdf_doc_add_page_content(buf, len);  /* op: cm */
01334 
01335   pdf_concatmatrix(CTM, M);
01336 
01337   inversematrix(&W, M);
01338 
01339   pdf_path__transform (cpa, &W);
01340   pdf_coord__transform(cpt, &W);
01341 
01342   return 0;
01343 }
01344 
01345 /*
01346  * num w        LW  linewidth (g.t. 0)
01347  * int J        LC  linecap
01348  * int j        LJ  linejoin
01349  * num M        ML  miter limit (g.t. 0)
01350  * array num d  D   line dash
01351  * int ri       RI  renderint intnet
01352  * int i        FL  flatness tolerance (0-100)
01353  * name gs      --  name: res. name of ExtGState dict.  
01354  */      
01355 int
01356 pdf_dev_setmiterlimit (double mlimit)
01357 {
01358   m_stack    *gss = &gs_stack;
01359   pdf_gstate *gs  = m_stack_top(gss);
01360   int         len = 0;
01361   char       *buf = FORMAT_BUFF_PTR(NULL);
01362 
01363   if (gs->miterlimit != mlimit) {
01364     buf[len++] = ' ';
01365     len += pdf_sprint_length(buf + len, mlimit);
01366     buf[len++] = ' ';
01367     buf[len++] = 'M';
01368     pdf_doc_add_page_content(buf, len);  /* op: M */
01369     gs->miterlimit = mlimit;
01370   }
01371 
01372   return 0;
01373 }
01374 
01375 int
01376 pdf_dev_setlinecap (int capstyle)
01377 {
01378   m_stack    *gss = &gs_stack;
01379   pdf_gstate *gs  = m_stack_top(gss);
01380   int         len = 0;
01381   char       *buf = FORMAT_BUFF_PTR(NULL);
01382 
01383   if (gs->linecap != capstyle) {
01384     len = sprintf(buf, " %d J", capstyle);
01385     pdf_doc_add_page_content(buf, len);  /* op: J */
01386     gs->linecap = capstyle;
01387   }
01388 
01389   return 0;
01390 }
01391 
01392 int
01393 pdf_dev_setlinejoin (int joinstyle)
01394 {
01395   m_stack    *gss = &gs_stack;
01396   pdf_gstate *gs  = m_stack_top(gss);
01397   int         len = 0;
01398   char       *buf = FORMAT_BUFF_PTR(NULL);
01399 
01400   if (gs->linejoin != joinstyle) {
01401     len = sprintf(buf, " %d j", joinstyle);
01402     pdf_doc_add_page_content(buf, len);  /* op: j */
01403     gs->linejoin = joinstyle;
01404   }
01405 
01406   return 0;
01407 }
01408 
01409 int
01410 pdf_dev_setlinewidth (double width)
01411 {
01412   m_stack    *gss = &gs_stack;
01413   pdf_gstate *gs  = m_stack_top(gss);  
01414   int         len = 0;
01415   char       *buf = FORMAT_BUFF_PTR(NULL);
01416 
01417   if (gs->linewidth != width) {
01418     buf[len++] = ' ';
01419     len += pdf_sprint_length(buf + len, width);
01420     buf[len++] = ' ';
01421     buf[len++] = 'w';
01422     pdf_doc_add_page_content(buf, len);  /* op: w */
01423     gs->linewidth = width;
01424   }
01425 
01426   return 0;
01427 }
01428 
01429 int
01430 pdf_dev_setdash (int count, double *pattern, double offset)
01431 {
01432   m_stack    *gss = &gs_stack;
01433   pdf_gstate *gs  = m_stack_top(gss);
01434   int         len = 0;
01435   char       *buf = FORMAT_BUFF_PTR(NULL);
01436   int         i;
01437 
01438   gs->linedash.num_dash = count;
01439   gs->linedash.offset   = offset;
01440   pdf_doc_add_page_content(" [", 2);  /* op: */
01441   for (i = 0; i < count; i++) {
01442     buf[0] = ' ';
01443     len = pdf_sprint_length (buf + 1, pattern[i]);
01444     pdf_doc_add_page_content(buf, len + 1);  /* op: */
01445     gs->linedash.pattern[i] = pattern[i];
01446   }
01447   pdf_doc_add_page_content("] ", 2);  /* op: */
01448   len = pdf_sprint_length (buf, offset);
01449   pdf_doc_add_page_content(buf, len);  /* op: */
01450   pdf_doc_add_page_content(" d", 2);  /* op: d */
01451 
01452   return 0;
01453 }
01454 
01455 #if 0
01456 int
01457 pdf_dev_setflat (int flatness)
01458 {
01459   m_stack    *gss = &gs_stack;
01460   pdf_gstate *gs  = m_stack_top(gss);
01461   int         len = 0;
01462   char       *buf = FORMAT_BUFF_PTR(NULL);
01463 
01464   if (flatness < 0 || flatness > 100)
01465     return -1;
01466 
01467   if (gs->flatness != flatness) {
01468     gs->flatness = flatness;
01469     len = sprintf(buf, " %d i", flatness);
01470     pdf_doc_add_page_content(buf, len);  /* op: i */
01471   }
01472 
01473   return 0;
01474 }
01475 #endif
01476 
01477 /* ZSYUEDVEDEOF */
01478 int
01479 pdf_dev_clip (void)
01480 {
01481   m_stack    *gss = &gs_stack;
01482   pdf_gstate *gs  = m_stack_top(gss);
01483   pdf_path   *cpa = &gs->path;
01484 
01485   return pdf_dev__flushpath(NULL, cpa, 'W', PDF_FILL_RULE_NONZERO, 0);
01486 }
01487 
01488 int
01489 pdf_dev_eoclip (void)
01490 {
01491   m_stack    *gss = &gs_stack;
01492   pdf_gstate *gs  = m_stack_top(gss);
01493   pdf_path   *cpa = &gs->path;
01494 
01495   return pdf_dev__flushpath(NULL, cpa, 'W', PDF_FILL_RULE_EVENODD, 0);
01496 }
01497 
01498 int
01499 pdf_dev_flushpath (char p_op, int fill_rule)
01500 {
01501   m_stack    *gss   = &gs_stack;
01502   pdf_gstate *gs    = m_stack_top(gss);
01503   pdf_path   *cpa   = &gs->path;
01504   int         error = 0;
01505 
01506   /* last arg 'ignore_rule' is only for single object
01507    * that can be converted to a rect where fill rule
01508    * is inessential.
01509    */
01510   error = pdf_dev__flushpath(NULL, cpa, p_op, fill_rule, 1);
01511   pdf_path__clearpath(cpa);
01512 
01513   gs->flags &= ~GS_FLAG_CURRENTPOINT_SET;
01514 
01515   return error;
01516 }
01517 
01518 int
01519 pdf_dev_newpath (void)
01520 {
01521   m_stack    *gss = &gs_stack;
01522   pdf_gstate *gs  = m_stack_top(gss);
01523   pdf_path   *p   = &gs->path;
01524 
01525   if (PA_LENGTH(p) > 0) {
01526     pdf_path__clearpath (p);
01527   }
01528   /* The following is required for "newpath" operator in mpost.c. */
01529   pdf_doc_add_page_content(" n", 2);  /* op: n */
01530 
01531   return 0;
01532 }
01533 
01534 int
01535 pdf_dev_moveto (double x, double y)
01536 {
01537   m_stack    *gss = &gs_stack;
01538   pdf_gstate *gs  = m_stack_top(gss);
01539   pdf_path   *cpa = &gs->path;
01540   pdf_coord  *cpt = &gs->cp;
01541   pdf_coord   p;
01542 
01543   p.x = x; p.y = y;
01544   return pdf_path__moveto(cpa, cpt, &p); /* cpt updated */
01545 }
01546 
01547 int
01548 pdf_dev_rmoveto (double x, double y)
01549 {
01550   m_stack    *gss = &gs_stack;
01551   pdf_gstate *gs  = m_stack_top(gss);
01552   pdf_path   *cpa = &gs->path;
01553   pdf_coord  *cpt = &gs->cp;
01554   pdf_coord   p;
01555 
01556   p.x = cpt->x + x;
01557   p.y = cpt->y + y;
01558   return pdf_path__moveto(cpa, cpt, &p); /* cpt updated */
01559 }
01560 
01561 int
01562 pdf_dev_lineto (double x, double y)
01563 {
01564   m_stack    *gss = &gs_stack;
01565   pdf_gstate *gs  = m_stack_top(gss);
01566   pdf_path   *cpa = &gs->path;
01567   pdf_coord  *cpt = &gs->cp;
01568   pdf_coord   p0;
01569 
01570   p0.x = x; p0.y = y;
01571 
01572   return pdf_path__lineto(cpa, cpt, &p0);
01573 }
01574 
01575 int
01576 pdf_dev_rlineto (double x, double y)
01577 {
01578   m_stack    *gss = &gs_stack;
01579   pdf_gstate *gs  = m_stack_top(gss);
01580   pdf_path   *cpa = &gs->path;
01581   pdf_coord  *cpt = &gs->cp;
01582   pdf_coord   p0;
01583 
01584   p0.x = x + cpt->x; p0.y = y + cpt->y;
01585 
01586   return pdf_path__lineto(cpa, cpt, &p0);
01587 }
01588 
01589 int
01590 pdf_dev_curveto  (double x0, double y0,
01591                   double x1, double y1,
01592                   double x2, double y2)
01593 {
01594   m_stack    *gss = &gs_stack;
01595   pdf_gstate *gs  = m_stack_top(gss);
01596   pdf_path   *cpa = &gs->path;
01597   pdf_coord  *cpt = &gs->cp;
01598   pdf_coord   p0, p1, p2;
01599 
01600   p0.x = x0; p0.y = y0;
01601   p1.x = x1; p1.y = y1;
01602   p2.x = x2; p2.y = y2;
01603 
01604   return pdf_path__curveto(cpa, cpt, &p0, &p1, &p2);
01605 }
01606 
01607 int
01608 pdf_dev_rcurveto (double x0, double y0,
01609                   double x1, double y1,
01610                   double x2, double y2)
01611 {
01612   m_stack    *gss = &gs_stack;
01613   pdf_gstate *gs  = m_stack_top(gss);
01614   pdf_path   *cpa = &gs->path;
01615   pdf_coord  *cpt = &gs->cp;
01616   pdf_coord   p0, p1, p2;
01617 
01618   p0.x = x0 + cpt->x; p0.y = y0 + cpt->y;
01619   p1.x = x1 + cpt->x; p1.y = y1 + cpt->y;
01620   p2.x = x2 + cpt->x; p2.y = y2 + cpt->y;
01621 
01622   return pdf_path__curveto(cpa, cpt, &p0, &p1, &p2);
01623 }
01624 
01625 
01626 int
01627 pdf_dev_closepath (void)
01628 {
01629   m_stack    *gss = &gs_stack;
01630   pdf_gstate *gs  = m_stack_top(gss);
01631   pdf_coord  *cpt = &gs->cp;
01632   pdf_path   *cpa = &gs->path;
01633 
01634   return pdf_path__closepath(cpa, cpt);
01635 }
01636 
01637 
01638 void
01639 pdf_dev_dtransform (pdf_coord *p, const pdf_tmatrix *M)
01640 {
01641   m_stack     *gss = &gs_stack;
01642   pdf_gstate  *gs  = m_stack_top(gss);
01643   pdf_tmatrix *CTM = &gs->matrix;
01644 
01645   ASSERT(p);
01646 
01647   pdf_coord__dtransform(p, (M ? M : CTM));
01648 
01649   return;
01650 }
01651 
01652 void
01653 pdf_dev_idtransform (pdf_coord *p, const pdf_tmatrix *M)
01654 {
01655   m_stack     *gss = &gs_stack;
01656   pdf_gstate  *gs  = m_stack_top(gss);
01657   pdf_tmatrix *CTM = &gs->matrix;
01658 
01659   ASSERT(p);
01660 
01661   pdf_coord__idtransform(p, (M ? M : CTM));
01662 
01663   return;
01664 }
01665 
01666 void
01667 pdf_dev_transform (pdf_coord *p, const pdf_tmatrix *M)
01668 {
01669   m_stack     *gss = &gs_stack;
01670   pdf_gstate  *gs  = m_stack_top(gss);
01671   pdf_tmatrix *CTM = &gs->matrix;
01672 
01673   ASSERT(p);
01674 
01675   pdf_coord__transform(p, (M ? M : CTM));
01676 
01677   return;
01678 }
01679 
01680 #if 0
01681 void
01682 pdf_dev_itransform (pdf_coord *p, const pdf_tmatrix *M)
01683 {
01684   m_stack     *gss = &gs_stack;
01685   pdf_gstate  *gs  = m_stack_top(gss);
01686   pdf_tmatrix *CTM = &gs->matrix;
01687 
01688   ASSERT(p);
01689 
01690   pdf_coord__itransform(p, (M ? M : CTM));
01691 
01692   return;
01693 }
01694 #endif
01695 
01696 int
01697 pdf_dev_arc  (double c_x , double c_y, double r,
01698               double a_0 , double a_1)
01699 {
01700   m_stack    *gss = &gs_stack;
01701   pdf_gstate *gs  = m_stack_top(gss);
01702   pdf_path   *cpa = &gs->path;
01703   pdf_coord  *cpt = &gs->cp;
01704   pdf_coord   c;
01705 
01706   c.x = c_x; c.y = c_y;
01707 
01708   return  pdf_path__elliptarc(cpa, cpt, &c, r, r, 0.0, a_0, a_1, +1);
01709 }
01710 
01711 /* *negative* arc */
01712 int
01713 pdf_dev_arcn (double c_x , double c_y, double r,
01714               double a_0 , double a_1)
01715 {
01716   m_stack    *gss = &gs_stack;
01717   pdf_gstate *gs  = m_stack_top(gss);
01718   pdf_path   *cpa = &gs->path;
01719   pdf_coord  *cpt = &gs->cp;
01720   pdf_coord   c;
01721 
01722   c.x = c_x; c.y = c_y;
01723 
01724   return  pdf_path__elliptarc(cpa, cpt, &c, r, r, 0.0, a_0, a_1, -1);
01725 }
01726 
01727 int
01728 pdf_dev_arcx (double c_x , double c_y,
01729               double r_x , double r_y,
01730               double a_0 , double a_1,
01731               int    a_d ,
01732               double xar)
01733 {
01734   m_stack    *gss = &gs_stack;
01735   pdf_gstate *gs  = m_stack_top(gss);
01736   pdf_path   *cpa = &gs->path;
01737   pdf_coord  *cpt = &gs->cp;
01738   pdf_coord   c;
01739 
01740   c.x = c_x; c.y = c_y;
01741 
01742   return  pdf_path__elliptarc(cpa, cpt, &c, r_x, r_y, xar, a_0, a_1, a_d);
01743 }
01744 
01745 /* Required by Tpic */
01746 int
01747 pdf_dev_bspline (double x0, double y0,
01748                  double x1, double y1, double x2, double y2)
01749 {
01750   m_stack    *gss = &gs_stack;
01751   pdf_gstate *gs  = m_stack_top(gss);
01752   pdf_path   *cpa = &gs->path;
01753   pdf_coord  *cpt = &gs->cp;  
01754   pdf_coord   p1, p2, p3;
01755 
01756   p1.x = x0 + 2.0 * (x1 - x0) / 3.0;
01757   p1.y = y0 + 2.0 * (y1 - y0) / 3.0;
01758   p2.x = x1 + (x2 - x1) / 3.0;
01759   p2.y = y1 + (y2 - y1) / 3.0;
01760   p3.x = x2;
01761   p3.y = y2;
01762 
01763   return  pdf_path__curveto(cpa, cpt, &p1, &p2, &p3);
01764 }
01765 
01766 
01767 #if 0
01768 int
01769 pdf_dev_rectstroke (double x, double y,
01770                     double w, double h,
01771                     const pdf_tmatrix *M  /* optional */
01772                    )
01773 {
01774   pdf_rect r;
01775 
01776   r.llx = x;
01777   r.lly = y;
01778   r.urx = x + w;
01779   r.ury = y + h;
01780 
01781   return  pdf_dev__rectshape(NULL, &r, M, 'S');
01782 }
01783 #endif
01784 
01785 int
01786 pdf_dev_rectfill  (double x, double y,
01787                    double w, double h)
01788 {
01789   pdf_rect r;
01790 
01791   r.llx = x;
01792   r.lly = y;
01793   r.urx = x + w;
01794   r.ury = y + h;
01795 
01796   return  pdf_dev__rectshape(NULL, &r, NULL, 'f');
01797 }
01798 
01799 int
01800 pdf_dev_rectclip (double x, double y,
01801                   double w, double h)
01802 {
01803   pdf_rect r;
01804 
01805   r.llx = x;
01806   r.lly = y;
01807   r.urx = x + w;
01808   r.ury = y + h;
01809   
01810   return  pdf_dev__rectshape(NULL, &r, NULL, 'W');
01811 }