1/***********************************************************************
2 *                                                                     *
3 * $Id: hpgspath.c 352 2006-10-10 20:58:26Z softadm $
4 *                                                                     *
5 * hpgs - HPGl Script, a hpgl/2 interpreter, which uses a Postscript   *
6 *        API for rendering a scene and thus renders to a variety of   *
7 *        devices and fileformats.                                     *
8 *                                                                     *
9 * (C) 2004-2006 ev-i Informationstechnologie GmbH  http://www.ev-i.at *
10 *                                                                     *
11 * Author: Wolfgang Glas                                               *
12 *                                                                     *
13 *  hpgs is free software; you can redistribute it and/or              *
14 * modify it under the terms of the GNU Lesser General Public          *
15 * License as published by the Free Software Foundation; either        *
16 * version 2.1 of the License, or (at your option) any later version.  *
17 *                                                                     *
18 * hpgs is distributed in the hope that it will be useful,             *
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of      *
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU   *
21 * Lesser General Public License for more details.                     *
22 *                                                                     *
23 * You should have received a copy of the GNU Lesser General Public    *
24 * License along with this library; if not, write to the               *
25 * Free Software  Foundation, Inc., 59 Temple Place, Suite 330,        *
26 * Boston, MA  02111-1307  USA                                         *
27 *                                                                     *
28 ***********************************************************************
29 *                                                                     *
30 * The implementation of the HPGL reader.                              *
31 *                                                                     *
32 ***********************************************************************/
33
34#include <hpgsreader.h>
35#include <math.h>
36#include <string.h>
37#include <ctype.h>
38
39#ifndef M_PI
40#define M_PI        3.14159265358979323846264338327950288
41#endif
42
43/*
44  Do check for an open path.
45*/
46
47int hpgs_reader_checkpath(hpgs_reader *reader)
48{
49  if (!reader->polygon_mode && reader->polygon_open >= 2)
50    return hpgs_reader_stroke(reader);
51
52  return 0;
53}
54
55static int push_poly_point(hpgs_reader *reader, hpgs_point *p, int flag)
56{
57	if (flag == 0 && reader->poly_buffer_size>0 &&
58	    reader->poly_buffer[reader->poly_buffer_size-1].flag == 0 &&
59	    reader->poly_buffer[reader->poly_buffer_size-1].p.x == p->x &&
60		  reader->poly_buffer[reader->poly_buffer_size-1].p.y == p->y   )
61		return 0;
62
63  if (reader->poly_buffer_size >= reader->poly_buffer_alloc_size)
64    {
65      reader->poly_buffer_alloc_size *= 2;
66      reader->poly_buffer = (hpgs_reader_poly_point *)
67	realloc(reader->poly_buffer,
68		sizeof(hpgs_reader_poly_point)*reader->poly_buffer_alloc_size);
69
70      if (!reader->poly_buffer)
71	return hpgs_set_error(hpgs_i18n("out of memory in push_poly_point."));
72    }
73
74  reader->poly_buffer[reader->poly_buffer_size].p = *p;
75  reader->poly_buffer[reader->poly_buffer_size].flag = flag;
76  ++reader->poly_buffer_size;
77
78  return 0;
79}
80
81static void add_path_point(hpgs_reader *reader, hpgs_point *p)
82{
83  if (p->x < reader->min_path_point.x) reader->min_path_point.x = p->x;
84  if (p->y < reader->min_path_point.y) reader->min_path_point.y = p->y;
85  if (p->x > reader->max_path_point.x) reader->max_path_point.x = p->x;
86  if (p->y > reader->max_path_point.y) reader->max_path_point.y = p->y;
87}
88
89static int do_polygon(hpgs_reader *reader, hpgs_bool fill)
90{
91  int ret = 0;
92
93  int i0 = 0;
94
95  while (i0 < reader->poly_buffer_size)
96    {
97      // find endpoint of current loop.
98      int i1 = i0;
99
100      while (i1 < reader->poly_buffer_size && reader->poly_buffer[i1].flag != 3)
101        ++i1;
102
103      if (i1 > i0+1)
104        {
105          int i=i0;
106          int open = 0;
107          hpgs_point *start=0;
108
109          // Do optimize away a polygon like (PostScript notation):
110          //  p1 moveto p2 moveto p3 lineto p4 lineto p2 lineto closepath fill
111          // Such polygons are easily created if the pushed currentpoint of PM 0
112          // is followed by a closed polygon.
113          // This optimization avoids some strange graphical effects caused by the
114          // line of thickness zero produced by the final closepath.
115          if (fill && reader->poly_buffer[i+1].flag==0 && i1 > i+2 &&
116              fabs(reader->poly_buffer[i+1].p.x - reader->poly_buffer[i1-1].p.x) < 1.0e-8 &&
117              fabs(reader->poly_buffer[i+1].p.y - reader->poly_buffer[i1-1].p.y) < 1.0e-8   )
118        	   ++i;
119
120          while (i<i1)
121            {
122              switch (reader->poly_buffer[i].flag)
123	              {
124	              case 0:
125	              	// Well, there are HPGL files in the wild, which write polygons with
126                	// the pen up all the time and do a fill afterwards.
127	              	if (open && fill)
128	                	{
129	                  	if (open == 1)
130	                    	{
131	                      	if (hpgs_moveto(reader->device,start))
132	                        	return -1;
133	    		    						open = 2;
134	        							}
135
136	      							if (hpgs_lineto(reader->device,&reader->poly_buffer[i].p))
137	       					 			return -1;
138	    							}
139									else
140										{
141											start = &reader->poly_buffer[i].p;
142											open = 1;
143										}
144
145									if (i)
146	    							add_path_point(reader,&reader->poly_buffer[i].p);
147	  							else
148	    							{
149	      							reader->min_path_point = reader->poly_buffer[0].p;
150	      							reader->max_path_point = reader->poly_buffer[0].p;
151	    							}
152	  							break;
153
154								case 1:
155							    if (open == 0)
156								    return hpgs_set_error(hpgs_i18n("Missing moveto in do_polygon."));
157
158									if (open == 1 && hpgs_moveto(reader->device,start))
159									  return -1;
160
161									if (hpgs_lineto(reader->device,&reader->poly_buffer[i].p))
162										return -1;
163
164									add_path_point(reader,&reader->poly_buffer[i].p);
165									open = 2;
166									break;
167							  case 2:
168							    if (open == 0)
169							      return hpgs_set_error(hpgs_i18n("Missing moveto in do_polygon."));
170
171							    if (open == 1 && hpgs_moveto(reader->device,start))
172							      return -1;
173
174							    if (i+2>=reader->poly_buffer_size)
175							      return hpgs_set_error(hpgs_i18n("curveto error in do_polygon."));
176
177							    if (hpgs_curveto(reader->device,
178							        &reader->poly_buffer[i].p,
179											&reader->poly_buffer[i+1].p,
180											&reader->poly_buffer[i+2].p))
181										return -1;
182
183									add_path_point(reader,&reader->poly_buffer[i].p);
184									add_path_point(reader,&reader->poly_buffer[i+1].p);
185									add_path_point(reader,&reader->poly_buffer[i+2].p);
186									i+=2;
187	                open = 2;
188                  break;
189                default:
190                  return hpgs_set_error(hpgs_i18n("internal error in do_polygon."));
191                }
192              ++i;
193            }
194
195          if (open >= 2)
196	          {
197	            if (i < reader->poly_buffer_size &&
198	                hpgs_closepath(reader->device))
199	              return -1;
200              ret = 1;
201	         }
202        }
203
204      i0=i1+1;
205
206    }
207
208  return ret;
209}
210
211/*
212  Basic path operations.
213*/
214int hpgs_reader_moveto(hpgs_reader *reader, hpgs_point *p)
215{
216  reader->current_point = *p;
217  reader->cr_point = *p;
218
219  if (!reader->polygon_open)
220    {
221      reader->first_path_point = reader->current_point;
222      reader->min_path_point = reader->current_point;
223      reader->max_path_point = reader->current_point;
224    }
225
226  if (reader->polygon_mode)
227    {
228      if (push_poly_point(reader,&reader->current_point,0))
229        return -1;
230
231      if (reader->polygon_open == 0)
232         reader->polygon_open = 1;
233      else if (reader->polygon_open == 1)
234         reader->polygon_open = 2;
235    }
236
237  return 0;
238}
239
240int hpgs_reader_lineto(hpgs_reader *reader, hpgs_point *p)
241{
242  if (!reader->polygon_open)
243    {
244      if (reader->polygon_mode)
245	{
246	  if (push_poly_point(reader,&reader->current_point,0))
247	    return -1;
248	}
249      else
250	{
251	  if (hpgs_moveto(reader->device,&reader->current_point))
252	    return -1;
253	}
254
255      reader->first_path_point = reader->current_point;
256
257      reader->min_path_point = reader->current_point;
258      reader->max_path_point = reader->current_point;
259    }
260
261  if (reader->polygon_mode)
262    {
263      if (push_poly_point(reader,p,1))
264	return -1;
265    }
266  else
267    {
268      if (hpgs_lineto(reader->device,p))
269	return -1;
270      add_path_point(reader,p);
271    }
272
273  reader->polygon_open = 2;
274  reader->current_point = *p;
275  reader->cr_point = *p;
276
277  return 0;
278}
279
280int hpgs_reader_curveto(hpgs_reader *reader,
281			hpgs_point *p1, hpgs_point *p2, hpgs_point *p3)
282{
283  if (!reader->polygon_open)
284    {
285      if (reader->polygon_mode)
286	{
287	  if (push_poly_point(reader,&reader->current_point,0))
288	    return -1;
289	}
290      else
291	{
292	  if (hpgs_moveto(reader->device,&reader->current_point))
293	    return -1;
294	}
295
296      reader->first_path_point = reader->current_point;
297      reader->min_path_point = reader->current_point;
298      reader->max_path_point = reader->current_point;
299    }
300
301  if (reader->polygon_mode)
302    {
303      if (push_poly_point(reader,p1,2) ||
304	  push_poly_point(reader,p2,2) ||
305	  push_poly_point(reader,p3,2)   )
306	return -1;
307    }
308  else
309    {
310      if (hpgs_curveto(reader->device,p1,p2,p3))
311	return -1;
312
313      add_path_point(reader,p1);
314      add_path_point(reader,p2);
315      add_path_point(reader,p3);
316    }
317
318  reader->polygon_open = 2;
319  reader->current_point = *p3;
320  reader->cr_point = *p3;
321
322  return 0;
323}
324
325int hpgs_reader_closepath(hpgs_reader *reader)
326{
327  if (reader->polygon_open < 2)
328    {
329      reader->polygon_open = 0;
330      return 0;
331    }
332
333  reader->current_point = reader->first_path_point;
334  reader->polygon_open = 0;
335
336  if (reader->polygon_mode)
337    return push_poly_point(reader,&reader->first_path_point,3);
338  else
339    return hpgs_closepath(reader->device);
340}
341
342int hpgs_reader_stroke(hpgs_reader *reader)
343{
344  reader->polygon_open = 0;
345  reader->have_current_point = 0;
346  return hpgs_stroke(reader->device);
347}
348
349static int hatch(hpgs_reader *reader, double spacing, double angle, int cross, hpgs_bool winding)
350{
351  int i;
352  double ca = cos (angle*M_PI/180.0);
353  double sa = sin (angle*M_PI/180.0);
354  hpgs_point h_min,h_max;
355  hpgs_point p,ph;
356
357  if (spacing <= 1.0)
358    spacing= hypot(reader->P2.x-reader->P1.x,
359		   reader->P2.y-reader->P1.y ) * 0.01 * HP_TO_PT;
360
361  // rotate corner points to hatch coordinates and calculate min/max hatches.
362  p.x = reader->min_path_point.x-reader->anchor_point.x;
363  p.y = reader->min_path_point.y-reader->anchor_point.y;
364
365  h_max.x = h_min.x = (p.x * ca + p.y * sa)/spacing;
366  h_max.y = h_min.y = (p.y * ca - p.x * sa)/spacing;
367
368  p.x = reader->min_path_point.x-reader->anchor_point.x;
369  p.y = reader->max_path_point.y-reader->anchor_point.y;
370
371  ph.x = (p.x * ca + p.y * sa)/spacing;
372  ph.y = (p.y * ca - p.x * sa)/spacing;
373
374  if (ph.x < h_min.x) h_min.x = ph.x;
375  if (ph.y < h_min.y) h_min.y = ph.y;
376  if (ph.x > h_max.x) h_max.x = ph.x;
377  if (ph.y > h_max.y) h_max.y = ph.y;
378
379  p.x = reader->max_path_point.x-reader->anchor_point.x;
380  p.y = reader->max_path_point.y-reader->anchor_point.y;
381
382  ph.x = (p.x * ca + p.y * sa)/spacing;
383  ph.y = (p.y * ca - p.x * sa)/spacing;
384
385  if (ph.x < h_min.x) h_min.x = ph.x;
386  if (ph.y < h_min.y) h_min.y = ph.y;
387  if (ph.x > h_max.x) h_max.x = ph.x;
388  if (ph.y > h_max.y) h_max.y = ph.y;
389
390  p.x = reader->max_path_point.x-reader->anchor_point.x;
391  p.y = reader->min_path_point.y-reader->anchor_point.y;
392
393  ph.x = (p.x * ca + p.y * sa)/spacing;
394  ph.y = (p.y * ca - p.x * sa)/spacing;
395
396  if (ph.x < h_min.x) h_min.x = ph.x;
397  if (ph.y < h_min.y) h_min.y = ph.y;
398  if (ph.x > h_max.x) h_max.x = ph.x;
399  if (ph.y > h_max.y) h_max.y = ph.y;
400
401  if (hpgs_clipsave(reader->device)) return -1;
402
403  if (hpgs_clip(reader->device,winding)) return -1;
404
405  if (hpgs_newpath(reader->device)) return -1;
406
407  // go through verticaltal hatches hatches.
408  for (i = (int)ceil(h_min.y); i <= (int)floor(h_max.y); ++i)
409    {
410      ph.y = i;
411      ph.x = h_min.x;
412
413      p.x = (ph.x * ca - ph.y * sa) * spacing + reader->anchor_point.x;
414      p.y = (ph.y * ca + ph.x * sa) * spacing + reader->anchor_point.y;
415
416      if (hpgs_moveto(reader->device,&p)) return -1;
417
418      ph.x = h_max.x;
419
420      p.x = (ph.x * ca - ph.y * sa) * spacing + reader->anchor_point.x;
421      p.y = (ph.y * ca + ph.x * sa) * spacing + reader->anchor_point.y;
422
423      if (hpgs_lineto(reader->device,&p)) return -1;
424      if (hpgs_stroke(reader->device)) return -1;
425    }
426
427  if (cross)
428    // go through horizontal hatches hatches.
429    for (i = (int)ceil(h_min.x); i <= (int)floor(h_max.x); ++i)
430      {
431	ph.x = i;
432	ph.y = h_min.y;
433
434	p.x = (ph.x * ca - ph.y * sa) * spacing + reader->anchor_point.x;
435	p.y = (ph.y * ca + ph.x * sa) * spacing + reader->anchor_point.y;
436
437	if (hpgs_moveto(reader->device,&p)) return -1;
438
439	ph.y = h_max.y;
440
441	p.x = (ph.x * ca - ph.y * sa) * spacing + reader->anchor_point.x;
442	p.y = (ph.y * ca + ph.x * sa) * spacing + reader->anchor_point.y;
443
444	if (hpgs_lineto(reader->device,&p)) return -1;
445	if (hpgs_stroke(reader->device)) return -1;
446      }
447
448  if (hpgs_cliprestore(reader->device))
449    return -1;
450
451  return hpgs_newpath(reader->device);
452}
453
454// filltype 10.
455static int shade(hpgs_reader *reader, double level, hpgs_bool winding)
456{
457  int pen = reader->current_pen;
458  double alpha = level * 0.01;
459
460  if (alpha < 0.0) alpha = 0.0;
461  if (alpha > 1.0) alpha = 1.0;
462
463  if  (alpha != 1.0)
464    {
465      hpgs_color rgb;
466
467      rgb.r = (1.0-alpha) + reader->pen_colors[pen].r*alpha;
468      rgb.g = (1.0-alpha) + reader->pen_colors[pen].g*alpha;
469      rgb.b = (1.0-alpha) + reader->pen_colors[pen].b*alpha;
470
471      if (hpgs_setrgbcolor(reader->device,&rgb))
472        return -1;
473    }
474
475
476  if (hpgs_fill(reader->device,winding)) return -1;
477
478  if (alpha == 1.0) return 0;
479
480  return hpgs_setrgbcolor(reader->device,
481			  &reader->pen_colors[pen]);
482}
483
484int hpgs_reader_fill(hpgs_reader *reader, hpgs_bool winding)
485{
486  reader->polygon_open = 0;
487  reader->have_current_point = 0;
488
489  switch (reader->current_ft)
490    {
491    case 3:
492      return hatch(reader,reader->ft3_spacing,reader->ft3_angle,0,winding);
493    case 4:
494      return hatch(reader,reader->ft4_spacing,reader->ft4_angle,1,winding);
495    case 10:
496      return shade(reader,reader->ft10_level,winding);
497    default:
498      return hpgs_fill(reader->device,winding);
499    }
500  return 0;
501}
502
503/*
504 HPGL Command AC (AnChor Point)
505*/
506int hpgs_reader_do_AC (hpgs_reader *reader)
507{
508  if (reader->eoc)
509    {
510      reader->anchor_point.x = 0.0;
511      reader->anchor_point.y = 0.0;
512      return 0;
513    }
514  else
515    return hpgs_reader_read_point(reader,&reader->anchor_point,1);
516}
517
518static double sqr(double x);
519__inline__ double sqr(double x) { return x*x; }
520
521/*
522  Transform an arc from the current point through p2 to p3 to
523  center/sweep.
524
525  Return value: 0 ... arc successfully constructed.
526                1 ... points lie on a straight line.
527*/
528
529static int arc_by_points(hpgs_reader *reader,
530			 hpgs_point *p2,
531			 hpgs_point *p3,
532			 hpgs_point *center,
533			 double *r,
534			 double *sweep )
535{
536  hpgs_point * p1 = &reader->current_point;
537
538  double dx12 = p1->x-p2->x;
539  double dy12 = p1->y-p2->y;
540  double dx23 = p2->x-p3->x;
541  double dy23 = p2->y-p3->y;
542  double dx13 = p1->x-p3->x;
543  double dy13 = p1->y-p3->y;
544
545  double det=dy12*dx23-dy23*dx12;
546  double angle1,angle2;
547
548  // don't ask for scaling, since graphical coordinates are
549  // reasonably scaled in most situations.
550  if (fabs(det)<1.0e-8)
551    {
552      // circle detection
553      // end points coincide
554      if (hypot(dx13,dy13)<1.0e-8)
555	{
556	  center->x=0.5*(p1->x+p2->x);
557	  center->y=0.5*(p1->y+p2->y);
558	  *sweep=360.0;
559	  *r=0.5*hypot(dx12,dy12);
560	  return 0;
561	}
562
563      // other points coincide or points lie on a straight line
564      center->x=0.5*(p1->x+p3->x);
565      center->y=0.5*(p1->y+p3->y);
566      *sweep=360.0;
567      *r=0.5*hypot(dx13,dy13);
568      return 1;
569    }
570
571  center->x=dy13*dy12*dy23
572    +(sqr(p1->x)-sqr(p2->x))*dy23
573    -(sqr(p2->x)-sqr(p3->x))*dy12;
574
575  center->y=dx13*dx12*dx23
576    +(sqr(p1->y)-sqr(p2->y))*dx23
577    -(sqr(p2->y)-sqr(p3->y))*dx12;
578
579  center->x /= -2.0 * det;
580  center->y /=  2.0 * det;
581
582  *r=hypot(p1->x-center->x,p1->y-center->y);
583
584  angle1 = atan2(p1->y-center->y,p1->x-center->x)*180.0/M_PI;
585  angle2 = atan2(p2->y-center->y,p2->x-center->x)*180.0/M_PI;
586
587  // det telsl us, whether the assertion
588  // angle1<angle2 or angle2<angle1 holds.
589  if (det>0)
590    {
591      // This is necessary, because atan2 breaks at M_PI.
592      if (angle2 > angle1) angle2-=360.0;
593    }
594  else
595    {
596      // This is necessary, because atan2 breaks at M_PI.
597      if (angle2 < angle1) angle2+=360.0;
598    }
599
600  *sweep = angle2-angle1;
601
602  return 0;
603}
604
605
606// draw bezier spline parts for an elliptical arc.
607static int arc_to_bezier  (hpgs_reader *reader,
608			   hpgs_point *center,
609			   double sweep)
610{
611  int nseg = (int)ceil(fabs(sweep)/90.0);
612  double a,seg_alpha_2,beta;
613  int i;
614  hpgs_point *p0,axis;
615
616  if (!nseg) return 0;
617
618  axis.x = reader->current_point.x - center->x;
619  axis.y = reader->current_point.y - center->y;
620
621  seg_alpha_2 = 0.5*M_PI/180.0*sweep/nseg;
622  beta = 4.0/3.0 * (1.0-cos(seg_alpha_2))/sin(seg_alpha_2);
623
624  a=0.0;
625
626  p0 = &reader->current_point;
627
628  for (i=1;i<=nseg;i++)
629    {
630      hpgs_point p1=*p0;
631      hpgs_point p2;
632      hpgs_point p3=*center;
633
634      p1.x -= sin(a)*beta*axis.x;
635      p1.y -= sin(a)*beta*axis.y;
636
637      p1.x -= cos(a)*beta*axis.y;
638      p1.y += cos(a)*beta*axis.x;
639
640      a=M_PI/180.0 * i*sweep/nseg;
641
642      p3.x += cos(a)*axis.x;
643      p3.y += cos(a)*axis.y;
644
645      p3.x -= sin(a)*axis.y;
646      p3.y += sin(a)*axis.x;
647
648      p2=p3;
649
650      p2.x += sin(a)*beta*axis.x;
651      p2.y += sin(a)*beta*axis.y;
652
653      p2.x += cos(a)*beta*axis.y;
654      p2.y -= cos(a)*beta*axis.x;
655
656      if (hpgs_reader_curveto(reader,&p1,&p2,&p3))
657	return -1;
658
659      *p0=p3;
660    }
661
662  return 0;
663}
664
665/*
666 HPGL Command AA (Arc Absolute)
667*/
668int hpgs_reader_do_AA (hpgs_reader *reader)
669{
670  hpgs_point center;
671  double sweep;
672  double chord=0.0;
673
674  if (hpgs_reader_read_point(reader,&center,1)) return -1;
675  if (hpgs_reader_read_double(reader,&sweep)) return -1;
676  if (!reader->eoc &&
677      hpgs_reader_read_double(reader,&chord)) return -1;
678
679  // Be careful about the direction of the rotation for
680  // transformation matrices with neg. determinant.
681  if (reader->world_matrix.mxx * reader->world_matrix.myy -
682      reader->world_matrix.mxy * reader->world_matrix.myx   < 0.0)
683    sweep=-sweep;
684
685  return  arc_to_bezier(reader,&center,sweep);
686}
687
688/*
689 HPGL Command AR (Arc Relative)
690*/
691int hpgs_reader_do_AR (hpgs_reader *reader)
692{
693  hpgs_point center;
694  double sweep;
695  double chord=0.0;
696
697  if (hpgs_reader_read_point(reader,&center,-1)) return -1;
698  if (hpgs_reader_read_double(reader,&sweep)) return -1;
699  if (!reader->eoc &&
700      hpgs_reader_read_double(reader,&chord)) return -1;
701
702  center.x += reader->current_point.x;
703  center.y += reader->current_point.y;
704
705  // Be careful about the direction of the rotation for
706  // transformation matrices with neg. determinant.
707  if (reader->world_matrix.mxx * reader->world_matrix.myy -
708      reader->world_matrix.mxy * reader->world_matrix.myx   < 0.0)
709    sweep=-sweep;
710
711  return  arc_to_bezier(reader,&center,sweep);
712}
713
714/*
715 HPGL Command AT (Absolute Arc Three Points)
716*/
717int hpgs_reader_do_AT (hpgs_reader *reader)
718{
719  hpgs_point p2,p3,center;
720  double r,sweep,chord=0.0;
721
722  if (hpgs_reader_read_point(reader,&p2,1)) return -1;
723  if (hpgs_reader_read_point(reader,&p3,1)) return -1;
724  if (!reader->eoc &&
725      hpgs_reader_read_double(reader,&chord)) return -1;
726
727  reader->polygon_open = 1;
728
729  if (arc_by_points(reader,&p2,&p3,&center,&r,&sweep))
730    return hpgs_reader_lineto(reader,&p3);
731  else
732    return  arc_to_bezier(reader,&center,sweep);
733}
734
735/*
736 HPGL Command RT (Relative arc Three points)
737*/
738int hpgs_reader_do_RT (hpgs_reader *reader)
739{
740  hpgs_point p2,p3,center;
741  double r,sweep,chord=0.0;
742
743  if (hpgs_reader_read_point(reader,&p2,-1)) return -1;
744  if (hpgs_reader_read_point(reader,&p3,-1)) return -1;
745
746  p2.x += reader->current_point.x;
747  p2.y += reader->current_point.y;
748  p3.x += reader->current_point.x;
749  p3.y += reader->current_point.y;
750
751  if (!reader->eoc &&
752      hpgs_reader_read_double(reader,&chord)) return -1;
753
754  if (arc_by_points(reader,&p2,&p3,&center,&r,&sweep))
755    return hpgs_reader_lineto(reader,&p3);
756  else
757    return  arc_to_bezier(reader,&center,sweep);
758}
759
760/*
761 HPGL Command CI (CIrcle)
762*/
763int hpgs_reader_do_CI (hpgs_reader *reader)
764{
765  double r;
766  double chord=0.0;
767  hpgs_point p0,center = reader->current_point;
768
769  // read input point
770  if (hpgs_reader_read_double(reader,&r)) return -1;
771  if (!reader->eoc &&
772      hpgs_reader_read_double(reader,&chord)) return -1;
773
774  if (reader->polygon_mode)
775    {
776      if (hpgs_reader_closepath(reader)) return -1;
777    }
778
779  // scale r with the sqrt down to the paper space.
780  r *= reader->total_scale;
781
782  p0.x = reader->current_point.x + r;
783  p0.y = reader->current_point.y;
784  reader->pen_down = 1;
785
786  if (hpgs_reader_moveto(reader,&p0))
787    return -1;
788
789  if (arc_to_bezier(reader,&center,360.0)) return -1;
790
791  if (reader->polygon_mode)
792    {
793      if (hpgs_reader_closepath(reader)) return -1;
794    }
795
796  return hpgs_reader_moveto(reader,&center);
797}
798
799/*
800 HPGL Command BZ (BeZier absolute)
801*/
802int hpgs_reader_do_BZ (hpgs_reader *reader)
803{
804  hpgs_point p1;
805  hpgs_point p2;
806  hpgs_point p3;
807
808  while (!reader->eoc)
809    {
810      if (hpgs_reader_read_point(reader,&p1,1)) return -1;
811      if (hpgs_reader_read_point(reader,&p2,1)) return -1;
812      if (hpgs_reader_read_point(reader,&p3,1)) return -1;
813
814      if (hpgs_reader_curveto(reader,&p1,&p2,&p3)) return -1;
815   }
816
817  return 0;
818}
819
820/*
821 HPGL Command BR (Bezier Relative)
822*/
823int hpgs_reader_do_BR (hpgs_reader *reader)
824{
825  hpgs_point p1;
826  hpgs_point p2;
827  hpgs_point p3;
828
829  while (!reader->eoc)
830    {
831      if (hpgs_reader_read_point(reader,&p1,-1)) return -1;
832      if (hpgs_reader_read_point(reader,&p2,-1)) return -1;
833      if (hpgs_reader_read_point(reader,&p3,-1)) return -1;
834
835      p1.x += reader->current_point.x;
836      p1.y += reader->current_point.y;
837      p2.x += reader->current_point.x;
838      p2.y += reader->current_point.y;
839      p3.x += reader->current_point.x;
840      p3.y += reader->current_point.y;
841
842      if (hpgs_reader_curveto(reader,&p1,&p2,&p3)) return -1;
843   }
844
845  return 0;
846}
847
848/*
849 HPGL Command PA (Plot Absolute)
850*/
851int hpgs_reader_do_PA (hpgs_reader *reader)
852{
853  hpgs_point p;
854
855  if (!reader->pen_down &&
856      hpgs_reader_checkpath(reader)) return -1;
857
858  while (!reader->eoc)
859    {
860      if (hpgs_reader_read_point(reader,&p,1)) return -1;
861
862      if (reader->pen_down)
863	{
864	  if (hpgs_reader_lineto(reader,&p)) return -1;
865	}
866      else
867	{
868	  if (hpgs_reader_moveto(reader,&p)) return -1;
869	}
870    }
871
872  reader->absolute_plotting = 1;
873
874  return 0;
875}
876
877/*
878 HPGL Command PD (Pen Down)
879*/
880int hpgs_reader_do_PD (hpgs_reader *reader)
881{
882  hpgs_point p;
883
884  while (!reader->eoc)
885    {
886      if (hpgs_reader_read_point(reader,&p,
887				 reader->absolute_plotting ? 1 : -1)) return -1;
888
889      if (!reader->absolute_plotting)
890	{
891	  p.x += reader->current_point.x;
892	  p.y += reader->current_point.y;
893	}
894
895      if (hpgs_reader_lineto(reader,&p)) return -1;
896   }
897
898  reader->pen_down = 1;
899
900  return 0;
901}
902
903/*
904 HPGL Command PR (Plot Relative)
905*/
906int hpgs_reader_do_PR (hpgs_reader *reader)
907{
908  hpgs_point p;
909
910  if (!reader->pen_down &&
911      hpgs_reader_checkpath(reader)) return -1;
912
913  while (!reader->eoc)
914    {
915      if (hpgs_reader_read_point(reader,&p,-1)) return -1;
916
917      p.x += reader->current_point.x;
918      p.y += reader->current_point.y;
919
920      if (reader->pen_down)
921	{
922	  if (hpgs_reader_lineto(reader,&p)) return -1;
923	}
924      else
925	{
926	  if (hpgs_reader_moveto(reader,&p)) return -1;
927	}
928    }
929
930  reader->absolute_plotting = 0;
931
932  return 0;
933}
934
935/*
936 HPGL Command PU (Pen Up)
937*/
938int hpgs_reader_do_PU (hpgs_reader *reader)
939{
940  hpgs_point p;
941
942  if (hpgs_reader_checkpath(reader)) return -1;
943
944  while (!reader->eoc)
945    {
946      if (hpgs_reader_read_point(reader,&p,
947				 reader->absolute_plotting ? 1 : -1)) return -1;
948
949      if (!reader->absolute_plotting)
950	{
951	  p.x += reader->current_point.x;
952	  p.y += reader->current_point.y;
953	}
954
955      if (hpgs_reader_moveto(reader,&p)) return -1;
956   }
957
958  reader->pen_down = 0;
959
960  return 0;
961}
962
963/*
964 HPGL Command PM (Polygon Mode)
965*/
966int hpgs_reader_do_PM (hpgs_reader *reader)
967{
968  int mode=0;
969
970  if (!reader->eoc &&
971      hpgs_reader_read_int(reader,&mode)) return -1;
972
973  switch(mode)
974    {
975    case 0:
976      if (hpgs_reader_checkpath(reader)) return -1;
977      reader->poly_buffer_size=0;
978      reader->polygon_mode=1;
979      reader->polygon_open=0;
980      // push the current point to the polygon buffer.
981      if (hpgs_reader_moveto(reader,&reader->current_point)) return -1;
982      break;
983    case 1:
984      if (hpgs_reader_closepath(reader)) return -1;
985      break;
986    case 2:
987      if (hpgs_reader_closepath(reader)) return -1;
988      reader->polygon_mode=0;
989      if (reader->poly_buffer_size > 0)
990        reader->current_point = reader->poly_buffer[0].p;
991      break;
992    default:
993      return hpgs_set_error(hpgs_i18n("PM: Illegal mode %d."),mode);
994    };
995
996  return 0;
997}
998
999/*
1000 HPGL Command FP (Fill Polygon)
1001*/
1002int hpgs_reader_do_FP (hpgs_reader *reader)
1003{
1004  hpgs_bool winding = HPGS_FALSE;
1005
1006  if (!reader->eoc &&
1007      hpgs_reader_read_int(reader,&winding)) return -1;
1008
1009  if (hpgs_reader_checkpath(reader)) return -1;
1010
1011  if (reader->poly_buffer_size <= 0) return 0;
1012
1013  switch (do_polygon(reader,HPGS_TRUE))
1014    {
1015    case 0:
1016      return 0;
1017    case 1:
1018      return hpgs_reader_fill(reader,winding);
1019    default:
1020      return -1;
1021    }
1022}
1023
1024/*
1025 HPGL Command PP (Pixel Placement)
1026*/
1027int hpgs_reader_do_PP (hpgs_reader *reader)
1028{
1029  int dummy = 0;
1030
1031  if (!reader->eoc &&
1032      hpgs_reader_read_int(reader,&dummy)) return -1;
1033
1034  if (reader->verbosity > 1)
1035    hpgs_log("PP %d: unimplemented.\n",dummy);
1036
1037  return 0;
1038}
1039
1040/*
1041 HPGL Command FT (Fill Type)
1042*/
1043int hpgs_reader_do_FT (hpgs_reader *reader)
1044{
1045  double option;
1046
1047  if (reader->eoc)
1048    {
1049      reader->current_ft = 1;
1050      return 0;
1051    }
1052
1053  if (hpgs_reader_read_int(reader,&reader->current_ft)) return -1;
1054
1055  if (!reader->eoc)
1056    {
1057      if (hpgs_reader_read_double(reader,&option)) return -1;
1058
1059      switch (reader->current_ft)
1060	{
1061	case 3:
1062	  // spacing is measured in current units along the x axis.
1063	  reader->ft3_spacing = option * hypot(reader->total_matrix.mxx,reader->total_matrix.myx);
1064	  break;
1065	case 4:
1066	  // spacing is measured in current units along the x axis.
1067	  reader->ft4_spacing = option * hypot(reader->total_matrix.mxx,reader->total_matrix.myx);
1068	  break;
1069	case 10:
1070	  reader->ft10_level = option;
1071	}
1072    }
1073
1074  if (!reader->eoc)
1075    {
1076      if (hpgs_reader_read_double(reader,&option)) return -1;
1077
1078      switch (reader->current_ft)
1079	{
1080	case 3:
1081	  reader->ft3_angle = option;
1082	  break;
1083	case 4:
1084	  reader->ft4_angle = option;
1085	}
1086    }
1087
1088  return 0;
1089}
1090
1091/*
1092 HPGL Command EA (Edge rectangle Absolute)
1093*/
1094int hpgs_reader_do_EA (hpgs_reader *reader)
1095{
1096  hpgs_point p,pp,cp;
1097
1098  if (hpgs_reader_read_point(reader,&p,1)) return -1;
1099
1100  if (hpgs_reader_checkpath(reader)) return -1;
1101  reader->poly_buffer_size = 0;
1102  reader->polygon_mode = 1;
1103
1104  cp = reader->current_point;
1105
1106  if (hpgs_reader_moveto(reader,&cp)) return -1;
1107
1108  pp.x = cp.x;
1109  pp.y = p.y;
1110
1111  if (hpgs_reader_lineto(reader,&pp)) return -1;
1112  if (hpgs_reader_lineto(reader,&p)) return -1;
1113
1114  pp.x = p.x;
1115  pp.y = cp.y;
1116
1117  if (hpgs_reader_lineto(reader,&pp)) return -1;
1118  if (hpgs_reader_closepath(reader)) return -1;
1119
1120  switch (do_polygon(reader,HPGS_FALSE))
1121    {
1122    case 1:
1123      if (hpgs_reader_fill(reader,HPGS_TRUE))
1124        return -1;
1125
1126      if (hpgs_reader_stroke(reader))
1127        return -1;
1128
1129    case 0:
1130      reader->polygon_mode = 0;
1131      return 0;
1132
1133    default:
1134      return -1;
1135    }
1136}
1137
1138/*
1139 HPGL Command RA (fill Rectangle Absolute)
1140*/
1141int hpgs_reader_do_RA (hpgs_reader *reader)
1142{
1143  hpgs_point p,pp,cp;
1144
1145  if (hpgs_reader_read_point(reader,&p,1)) return -1;
1146
1147  if (hpgs_reader_checkpath(reader)) return -1;
1148  reader->poly_buffer_size = 0;
1149  reader->polygon_mode = 1;
1150
1151  cp = reader->current_point;
1152
1153  if (hpgs_reader_moveto(reader,&cp)) return -1;
1154
1155  pp.x = cp.x;
1156  pp.y = p.y;
1157
1158  if (hpgs_reader_lineto(reader,&pp)) return -1;
1159  if (hpgs_reader_lineto(reader,&p)) return -1;
1160
1161  pp.x = p.x;
1162  pp.y = cp.y;
1163
1164  if (hpgs_reader_lineto(reader,&pp)) return -1;
1165  if (hpgs_reader_closepath(reader)) return -1;
1166
1167  switch (do_polygon(reader,HPGS_TRUE))
1168    {
1169    case 1:
1170      if (hpgs_reader_fill(reader,1))
1171        return -1;
1172
1173    case 0:
1174      reader->polygon_mode = 0;
1175      return 0;
1176
1177    default:
1178      return -1;
1179    }
1180}
1181
1182/*
1183 HPGL Command ER (Edge rectangle Relative)
1184*/
1185int hpgs_reader_do_ER (hpgs_reader *reader)
1186{
1187  hpgs_point p,pp,cp;
1188
1189  if (hpgs_reader_read_point(reader,&p,-1)) return -1;
1190
1191  p.x += reader->current_point.x;
1192  p.y += reader->current_point.y;
1193
1194  if (hpgs_reader_checkpath(reader)) return -1;
1195  reader->poly_buffer_size = 0;
1196  reader->polygon_mode = 1;
1197
1198  cp = reader->current_point;
1199
1200  if (hpgs_reader_moveto(reader,&cp)) return -1;
1201
1202  pp.x = cp.x;
1203  pp.y = p.y;
1204
1205  if (hpgs_reader_lineto(reader,&pp)) return -1;
1206  if (hpgs_reader_lineto(reader,&p)) return -1;
1207
1208  pp.x = p.x;
1209  pp.y = cp.y;
1210
1211  if (hpgs_reader_lineto(reader,&pp)) return -1;
1212  if (hpgs_reader_closepath(reader)) return -1;
1213
1214  switch (do_polygon(reader,HPGS_FALSE))
1215    {
1216    case 1:
1217      if (hpgs_reader_stroke(reader))
1218        return -1;
1219
1220    case 0:
1221      reader->polygon_mode = 0;
1222      return 0;
1223
1224    default:
1225      return -1;
1226    }
1227}
1228
1229/*
1230 HPGL Command RR (fill Rectangle Relative)
1231*/
1232int hpgs_reader_do_RR (hpgs_reader *reader)
1233{
1234  hpgs_point p,pp,cp;
1235
1236  if (hpgs_reader_read_point(reader,&p,-1)) return -1;
1237
1238  p.x += reader->current_point.x;
1239  p.y += reader->current_point.y;
1240
1241  if (hpgs_reader_checkpath(reader)) return -1;
1242  reader->poly_buffer_size = 0;
1243  reader->polygon_mode = 1;
1244
1245  cp = reader->current_point;
1246
1247  if (hpgs_reader_moveto(reader,&cp)) return -1;
1248
1249  pp.x = cp.x;
1250  pp.y = p.y;
1251
1252  if (hpgs_reader_lineto(reader,&pp)) return -1;
1253  if (hpgs_reader_lineto(reader,&p)) return -1;
1254
1255  pp.x = p.x;
1256  pp.y = cp.y;
1257
1258  if (hpgs_reader_lineto(reader,&pp)) return -1;
1259  if (hpgs_reader_closepath(reader)) return -1;
1260
1261  switch (do_polygon(reader,HPGS_TRUE))
1262    {
1263    case 1:
1264      if (hpgs_reader_fill(reader,1))
1265        return -1;
1266
1267    case 0:
1268      reader->polygon_mode = 0;
1269      return 0;
1270
1271    default:
1272      return -1;
1273    }
1274}
1275
1276/*
1277 HPGL Command EP (Edge Polygon)
1278*/
1279int hpgs_reader_do_EP (hpgs_reader *reader)
1280{
1281  if (hpgs_reader_checkpath(reader)) return -1;
1282
1283  if (reader->poly_buffer_size <= 0) return 0;
1284
1285  switch (do_polygon(reader,HPGS_FALSE))
1286    {
1287    case 1:
1288      if (hpgs_stroke(reader->device))
1289        return -1;
1290
1291    case 0:
1292      return 0;
1293
1294    default:
1295      return -1;
1296    }
1297}
1298
1299/*
1300 HPGL Command EW (Edge Wedge)
1301*/
1302int hpgs_reader_do_EW (hpgs_reader *reader)
1303{
1304  double r,start,sweep,chord;
1305  hpgs_point p,center;
1306
1307  if (hpgs_reader_read_double(reader,&r)) return -1;
1308  if (hpgs_reader_read_double(reader,&start)) return -1;
1309  if (hpgs_reader_read_double(reader,&sweep)) return -1;
1310  if (!reader->eoc &&
1311      hpgs_reader_read_double(reader,&chord)) return -1;
1312
1313  if (hpgs_reader_checkpath(reader)) return -1;
1314  reader->poly_buffer_size = 0;
1315  reader->polygon_mode = 1;
1316
1317  // scale r with the sqrt down to the paper space.
1318  r *= reader->total_scale;
1319
1320  center = reader->current_point;
1321  p=reader->current_point;
1322  p.x+=cos(start*M_PI/180.0)*r;
1323  p.y+=sin(start*M_PI/180.0)*r;
1324
1325  if (hpgs_reader_moveto(reader,&reader->current_point)) return -1;
1326  if (hpgs_reader_lineto(reader,&p)) return -1;
1327
1328  if (arc_to_bezier(reader,&center,sweep))
1329
1330  if (hpgs_reader_closepath(reader)) return -1;
1331
1332  switch (do_polygon(reader,HPGS_FALSE))
1333    {
1334    case 1:
1335      if (hpgs_reader_stroke(reader))
1336        return -1;
1337
1338    case 0:
1339      reader->polygon_mode = 0;
1340      return 0;
1341
1342    default:
1343      return -1;
1344    }
1345}
1346
1347/*
1348 HPGL Command WG (fill WedGe)
1349*/
1350int hpgs_reader_do_WG (hpgs_reader *reader)
1351{
1352  double r,start,sweep,chord;
1353  hpgs_point p,center;
1354
1355  if (hpgs_reader_read_double(reader,&r)) return -1;
1356  if (hpgs_reader_read_double(reader,&start)) return -1;
1357  if (hpgs_reader_read_double(reader,&sweep)) return -1;
1358  if (!reader->eoc &&
1359      hpgs_reader_read_double(reader,&chord)) return -1;
1360
1361  if (hpgs_reader_checkpath(reader)) return -1;
1362  reader->poly_buffer_size = 0;
1363  reader->polygon_mode = 1;
1364
1365  // scale r with the sqrt down to the paper space.
1366  r *= reader->total_scale;
1367
1368  center = reader->current_point;
1369  p=reader->current_point;
1370  p.x+=cos(start*M_PI/180.0)*r;
1371  p.y+=sin(start*M_PI/180.0)*r;
1372
1373  if (hpgs_reader_moveto(reader,&reader->current_point)) return -1;
1374  if (hpgs_reader_lineto(reader,&p)) return -1;
1375
1376  if (arc_to_bezier(reader,&center,sweep))
1377
1378  if (hpgs_reader_closepath(reader)) return -1;
1379
1380 switch (do_polygon(reader,HPGS_TRUE))
1381    {
1382    case 1:
1383      if (hpgs_reader_fill(reader,1))
1384        return -1;
1385
1386    case 0:
1387      reader->polygon_mode = 0;
1388      return 0;
1389
1390    default:
1391      return -1;
1392    }
1393}
1394