1// -*- C++ -*-
2/* Copyright (C) 1989, 1990, 1991, 1992, 2003 Free Software Foundation, Inc.
3     Written by James Clark (jjc@jclark.com)
4
5This file is part of groff.
6
7groff is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 2, or (at your option) any later
10version.
11
12groff is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License along
18with groff; see the file COPYING.  If not, write to the Free Software
19Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
20
21#include "pic.h"
22
23#ifdef TEX_SUPPORT
24
25#include "common.h"
26
27class tex_output : public common_output {
28public:
29  tex_output();
30  ~tex_output();
31  void start_picture(double, const position &ll, const position &ur);
32  void finish_picture();
33  void text(const position &, text_piece *, int, double);
34  void line(const position &, const position *, int n,
35	    const line_type &);
36  void polygon(const position *, int n,
37	       const line_type &, double);
38  void spline(const position &, const position *, int n,
39	      const line_type &);
40  void arc(const position &, const position &, const position &,
41	   const line_type &);
42  void circle(const position &, double rad, const line_type &, double);
43  void ellipse(const position &, const distance &, const line_type &, double);
44  void command(const char *, const char *, int);
45  void set_color(char *, char *);
46  void reset_color();
47  char *get_last_filled();
48  char *get_outline_color();
49  int supports_filled_polygons();
50private:
51  position upper_left;
52  double height;
53  double width;
54  double scale;
55  double pen_size;
56
57  void point(const position &);
58  void dot(const position &, const line_type &);
59  void solid_arc(const position &cent, double rad, double start_angle,
60		 double end_angle, const line_type &lt);
61  position transform(const position &);
62protected:
63  virtual void set_pen_size(double ps);
64};
65
66// convert inches to milliinches
67
68inline int milliinches(double x)
69{
70  return int(x*1000.0 + .5);
71}
72
73inline position tex_output::transform(const position &pos)
74{
75  return position((pos.x - upper_left.x)/scale,
76		  (upper_left.y - pos.y)/scale);
77}
78
79output *make_tex_output()
80{
81  return new tex_output;
82}
83
84tex_output::tex_output()
85{
86}
87
88tex_output::~tex_output()
89{
90}
91
92const int DEFAULT_PEN_SIZE = 8;
93
94void tex_output::set_pen_size(double ps)
95{
96  if (ps < 0.0)
97    ps = -1.0;
98  if (ps != pen_size) {
99    pen_size = ps;
100    printf("    \\special{pn %d}%%\n",
101	   ps < 0.0 ? DEFAULT_PEN_SIZE : int(ps*(1000.0/72.0) + .5));
102  }
103}
104
105void tex_output::start_picture(double sc, const position &ll,
106			       const position &ur)
107{
108  upper_left.x = ll.x;
109  upper_left.y = ur.y;
110  scale = compute_scale(sc, ll, ur);
111  height = (ur.y - ll.y)/scale;
112  width = (ur.x - ll.x)/scale;
113  /* The point of \vskip 0pt is to ensure that the vtop gets
114     a height of 0 rather than the height of the hbox; this
115     might be non-zero if text from text attributes lies outside pic's
116     idea of the bounding box of the picture. */
117  /* \newbox and \newdimen are defined with \outer in plain.tex and can't
118     be used directly in an \if clause. */
119  printf("\\expandafter\\ifx\\csname %s\\endcsname\\relax\n"
120	 "   \\csname newbox\\expandafter\\endcsname\\csname %s\\endcsname\n"
121	 "\\fi\n"
122	 "\\ifx\\graphtemp\\undefined\n"
123	 "  \\csname newdimen\\endcsname\\graphtemp\n"
124	 "\\fi\n"
125	 "\\expandafter\\setbox\\csname %s\\endcsname\n"
126	 " =\\vtop{\\vskip 0pt\\hbox{%%\n",
127	 graphname, graphname, graphname);
128  pen_size = -2.0;
129}
130
131void tex_output::finish_picture()
132{
133  printf("    \\hbox{\\vrule depth%.3fin width0pt height 0pt}%%\n"
134	 "    \\kern %.3fin\n"
135	 "  }%%\n"
136	 "}%%\n",
137	 height, width);
138}
139
140void tex_output::text(const position &center, text_piece *v, int n, double)
141{
142  position c = transform(center);
143  for (int i = 0; i < n; i++)
144    if (v[i].text != 0 && *v[i].text != '\0') {
145      int j = 2*i - n + 1;
146      if (v[i].adj.v == ABOVE_ADJUST)
147	j--;
148      else if (v[i].adj.v == BELOW_ADJUST)
149	j++;
150      if (j == 0) {
151	printf("    \\graphtemp=.5ex\n"
152	       "    \\advance\\graphtemp by %.3fin\n", c.y);
153      }
154      else {
155	printf("    \\graphtemp=\\baselineskip\n"
156	       "    \\multiply\\graphtemp by %d\n"
157	       "    \\divide\\graphtemp by 2\n"
158	       "    \\advance\\graphtemp by .5ex\n"
159	       "    \\advance\\graphtemp by %.3fin\n",
160	       j, c.y);
161      }
162      printf("    \\rlap{\\kern %.3fin\\lower\\graphtemp", c.x);
163      fputs("\\hbox to 0pt{", stdout);
164      if (v[i].adj.h != LEFT_ADJUST)
165	fputs("\\hss ", stdout);
166      fputs(v[i].text, stdout);
167      if (v[i].adj.h != RIGHT_ADJUST)
168	fputs("\\hss", stdout);
169      fputs("}}%\n", stdout);
170    }
171}
172
173void tex_output::point(const position &pos)
174{
175  position p = transform(pos);
176  printf("    \\special{pa %d %d}%%\n", milliinches(p.x), milliinches(p.y));
177}
178
179void tex_output::line(const position &start, const position *v, int n,
180		      const line_type &lt)
181{
182  set_pen_size(lt.thickness);
183  point(start);
184  for (int i = 0; i < n; i++)
185    point(v[i]);
186  fputs("    \\special{", stdout);
187  switch(lt.type) {
188  case line_type::invisible:
189    fputs("ip", stdout);
190    break;
191  case line_type::solid:
192    fputs("fp", stdout);
193    break;
194  case line_type::dotted:
195    printf("dt %.3f", lt.dash_width/scale);
196    break;
197  case line_type::dashed:
198    printf("da %.3f", lt.dash_width/scale);
199    break;
200  }
201  fputs("}%\n", stdout);
202}
203
204void tex_output::polygon(const position *v, int n,
205			 const line_type &lt, double fill)
206{
207  if (fill >= 0.0) {
208    if (fill > 1.0)
209      fill = 1.0;
210    printf("    \\special{sh %.3f}%%\n", fill);
211  }
212  line(v[n-1], v, n, lt);
213}
214
215void tex_output::spline(const position &start, const position *v, int n,
216			const line_type &lt)
217{
218  if (lt.type == line_type::invisible)
219    return;
220  set_pen_size(lt.thickness);
221  point(start);
222  for (int i = 0; i < n; i++)
223    point(v[i]);
224  fputs("    \\special{sp", stdout);
225  switch(lt.type) {
226  case line_type::solid:
227    break;
228  case line_type::dotted:
229    printf(" %.3f", -lt.dash_width/scale);
230    break;
231  case line_type::dashed:
232    printf(" %.3f", lt.dash_width/scale);
233    break;
234  case line_type::invisible:
235    assert(0);
236  }
237  fputs("}%\n", stdout);
238}
239
240void tex_output::solid_arc(const position &cent, double rad,
241			   double start_angle, double end_angle,
242			   const line_type &lt)
243{
244  set_pen_size(lt.thickness);
245  position c = transform(cent);
246  printf("    \\special{ar %d %d %d %d %f %f}%%\n",
247	 milliinches(c.x),
248	 milliinches(c.y),
249	 milliinches(rad/scale),
250	 milliinches(rad/scale),
251	 -end_angle,
252	 (-end_angle > -start_angle) ? (double)M_PI * 2 - start_angle
253	 			     : -start_angle);
254}
255
256void tex_output::arc(const position &start, const position &cent,
257		     const position &end, const line_type &lt)
258{
259  switch (lt.type) {
260  case line_type::invisible:
261    break;
262  case line_type::dashed:
263    dashed_arc(start, cent, end, lt);
264    break;
265  case line_type::dotted:
266    dotted_arc(start, cent, end, lt);
267    break;
268  case line_type::solid:
269    {
270      position c;
271      if (!compute_arc_center(start, cent, end, &c)) {
272	line(start, &end, 1, lt);
273	break;
274      }
275      solid_arc(c,
276		hypot(cent - start),
277		atan2(start.y - c.y, start.x - c.x),
278		atan2(end.y - c.y, end.x - c.x),
279		lt);
280      break;
281    }
282  }
283}
284
285void tex_output::circle(const position &cent, double rad,
286			const line_type &lt, double fill)
287{
288  if (fill >= 0.0 && lt.type != line_type::solid) {
289    if (fill > 1.0)
290      fill = 1.0;
291    line_type ilt;
292    ilt.type = line_type::invisible;
293    ellipse(cent, position(rad*2.0, rad*2.0), ilt, fill);
294  }
295  switch (lt.type) {
296  case line_type::dashed:
297    dashed_circle(cent, rad, lt);
298    break;
299  case line_type::invisible:
300    break;
301  case line_type::solid:
302    ellipse(cent, position(rad*2.0,rad*2.0), lt, fill);
303    break;
304  case line_type::dotted:
305    dotted_circle(cent, rad, lt);
306    break;
307  default:
308    assert(0);
309  }
310}
311
312void tex_output::ellipse(const position &cent, const distance &dim,
313			 const line_type &lt, double fill)
314{
315  if (lt.type == line_type::invisible) {
316    if (fill < 0.0)
317      return;
318  }
319  else
320    set_pen_size(lt.thickness);
321  if (fill >= 0.0) {
322    if (fill > 1.0)
323      fill = 1.0;
324    printf("    \\special{sh %.3f}%%\n", fill);
325  }
326  position c = transform(cent);
327  switch (lt.type) {
328  case line_type::solid:
329  case line_type::invisible:
330    printf("    \\special{%s %d %d %d %d 0 6.28319}%%\n",
331	   (lt.type == line_type::invisible ? "ia" : "ar"),
332	   milliinches(c.x),
333	   milliinches(c.y),
334	   milliinches(dim.x/(2.0*scale)),
335	   milliinches(dim.y/(2.0*scale)));
336    break;
337  case line_type::dashed:
338    dashed_ellipse(cent, dim / scale, lt);
339    break;
340  case line_type::dotted:
341    dotted_ellipse(cent, dim / scale, lt);
342    break;
343  default:
344    assert(0);
345  }
346}
347
348void tex_output::command(const char *s, const char *, int)
349{
350  fputs(s, stdout);
351  putchar('%');			// avoid unwanted spaces
352  putchar('\n');
353}
354
355int tex_output::supports_filled_polygons()
356{
357  return 1;
358}
359
360void tex_output::dot(const position &pos, const line_type &lt)
361{
362  if (zero_length_line_flag) {
363    line_type slt = lt;
364    slt.type = line_type::solid;
365    line(pos, &pos, 1, slt);
366  }
367  else {
368    int dot_rad = int(lt.thickness*(1000.0/(72.0*2)) + .5);
369    if (dot_rad == 0)
370      dot_rad = 1;
371    position p = transform(pos);
372    printf("    \\special{sh 1}%%\n"
373	   "    \\special{ia %d %d %d %d 0 6.28319}%%\n",
374	   milliinches(p.x), milliinches(p.y), dot_rad, dot_rad);
375  }
376}
377
378void tex_output::set_color(char *, char *)
379{
380  /* not implemented yet */
381}
382
383void tex_output::reset_color()
384{
385  /* not implemented yet */
386}
387
388char *tex_output::get_last_filled()
389{
390  /* not implemented yet */
391  return NULL;
392}
393
394char *tex_output::get_outline_color()
395{
396  /* not implemented yet */
397  return NULL;
398}
399
400class tpic_output : public tex_output {
401public:
402  tpic_output();
403  void command(const char *, const char *, int);
404private:
405  void set_pen_size(double ps);
406  int default_pen_size;
407  int prev_default_pen_size;
408};
409
410tpic_output::tpic_output()
411: default_pen_size(DEFAULT_PEN_SIZE), prev_default_pen_size(DEFAULT_PEN_SIZE)
412{
413}
414
415void tpic_output::command(const char *s, const char *filename, int lineno)
416{
417  assert(s[0] == '.');
418  if (s[1] == 'p' && s[2] == 's' && (s[3] == '\0' || !csalpha(s[3]))) {
419    const char *p = s + 3;
420    while (csspace(*p))
421      p++;
422    if (*p == '\0') {
423      int temp = default_pen_size;
424      default_pen_size = prev_default_pen_size;
425      prev_default_pen_size = temp;
426    }
427    else {
428      char *ptr;
429      int temp = (int)strtol(p, &ptr, 10);
430      if (temp == 0 && ptr == p)
431	error_with_file_and_line(filename, lineno,
432				 "argument to `.ps' not an integer");
433      else if (temp < 0)
434	error_with_file_and_line(filename, lineno,
435				 "negative pen size");
436      else {
437	prev_default_pen_size = default_pen_size;
438	default_pen_size = temp;
439      }
440    }
441  }
442  else
443    printf("\\%s%%\n", s + 1);
444}
445
446void tpic_output::set_pen_size(double ps)
447{
448  if (ps < 0.0)
449    printf("    \\special{pn %d}%%\n", default_pen_size);
450  else
451    tex_output::set_pen_size(ps);
452}
453
454output *make_tpic_output()
455{
456  return new tpic_output;
457}
458
459#endif
460