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