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 ¢, double rad, double start_angle,
59 double end_angle, const line_type <);
60 position transform(const position &);
61 protected:
62 virtual void set_pen_size(double ps);
63 };
64
65 // convert inches to milliinches
66
milliinches(double x)67 inline int milliinches(double x)
68 {
69 return int(x*1000.0 + .5);
70 }
71
transform(const position & pos)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
make_tex_output()78 output *make_tex_output()
79 {
80 return new tex_output;
81 }
82
tex_output()83 tex_output::tex_output()
84 {
85 }
86
~tex_output()87 tex_output::~tex_output()
88 {
89 }
90
91 const int DEFAULT_PEN_SIZE = 8;
92
set_pen_size(double ps)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
start_picture(double sc,const position & ll,const position & ur)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
finish_picture()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
text(const position & center,text_piece * v,int n,double)139 void tex_output::text(const position ¢er, 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
point(const position & pos)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
line(const position & start,const position * v,int n,const line_type & lt)178 void tex_output::line(const position &start, const position *v, int n,
179 const line_type <)
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
polygon(const position * v,int n,const line_type & lt,double fill)203 void tex_output::polygon(const position *v, int n,
204 const line_type <, 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
spline(const position & start,const position * v,int n,const line_type & lt)214 void tex_output::spline(const position &start, const position *v, int n,
215 const line_type <)
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
solid_arc(const position & cent,double rad,double start_angle,double end_angle,const line_type & lt)239 void tex_output::solid_arc(const position ¢, double rad,
240 double start_angle, double end_angle,
241 const line_type <)
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
arc(const position & start,const position & cent,const position & end,const line_type & lt)255 void tex_output::arc(const position &start, const position ¢,
256 const position &end, const line_type <)
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
circle(const position & cent,double rad,const line_type & lt,double fill)284 void tex_output::circle(const position ¢, double rad,
285 const line_type <, 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
ellipse(const position & cent,const distance & dim,const line_type & lt,double fill)311 void tex_output::ellipse(const position ¢, const distance &dim,
312 const line_type <, 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
command(const char * s,const char *,int)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
supports_filled_polygons()354 int tex_output::supports_filled_polygons()
355 {
356 return 1;
357 }
358
dot(const position & pos,const line_type & lt)359 void tex_output::dot(const position &pos, const line_type <)
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
set_color(char *,char *)377 void tex_output::set_color(char *, char *)
378 {
379 /* not implemented yet */
380 }
381
reset_color()382 void tex_output::reset_color()
383 {
384 /* not implemented yet */
385 }
386
get_last_filled()387 char *tex_output::get_last_filled()
388 {
389 /* not implemented yet */
390 return NULL;
391 }
392
get_outline_color()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
tpic_output()409 tpic_output::tpic_output()
410 : default_pen_size(DEFAULT_PEN_SIZE), prev_default_pen_size(DEFAULT_PEN_SIZE)
411 {
412 }
413
command(const char * s,const char * filename,int lineno)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
set_pen_size(double ps)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
make_tpic_output()453 output *make_tpic_output()
454 {
455 return new tpic_output;
456 }
457
458 #endif
459