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