1114402Sru// -*- C++ -*-
2151497Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2005
3114402Sru   Free Software Foundation, Inc.
4114402Sru     Written by James Clark (jjc@jclark.com)
5114402Sru
6114402SruThis file is part of groff.
7114402Sru
8114402Srugroff is free software; you can redistribute it and/or modify it under
9114402Sruthe terms of the GNU General Public License as published by the Free
10114402SruSoftware Foundation; either version 2, or (at your option) any later
11114402Sruversion.
12114402Sru
13114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY
14114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or
15114402SruFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16114402Srufor more details.
17114402Sru
18114402SruYou should have received a copy of the GNU General Public License along
19114402Sruwith groff; see the file COPYING.  If not, write to the Free Software
20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21114402Sru
22114402Sru#include "pic.h"
23114402Sru#include "common.h"
24114402Sru
25114402Sru
26114402Sruconst double RELATIVE_THICKNESS = -1.0;
27114402Sruconst double BAD_THICKNESS = -2.0;
28114402Sru
29114402Sruclass simple_output : public common_output {
30114402Sru  virtual void simple_line(const position &, const position &) = 0;
31114402Sru  virtual void simple_spline(const position &, const position *, int n) = 0;
32114402Sru  virtual void simple_arc(const position &, const position &,
33114402Sru			  const position &) = 0;
34114402Sru  virtual void simple_circle(int, const position &, double rad) = 0;
35114402Sru  virtual void simple_ellipse(int, const position &, const distance &) = 0;
36114402Sru  virtual void simple_polygon(int, const position *, int) = 0;
37114402Sru  virtual void line_thickness(double) = 0;
38114402Sru  virtual void set_fill(double) = 0;
39114402Sru  virtual void set_color(char *, char *) = 0;
40114402Sru  virtual void reset_color() = 0;
41114402Sru  virtual char *get_last_filled() = 0;
42114402Sru  void dot(const position &, const line_type &) = 0;
43114402Srupublic:
44114402Sru  void start_picture(double sc, const position &ll, const position &ur) = 0;
45114402Sru  void finish_picture() = 0;
46114402Sru  void text(const position &, text_piece *, int, double) = 0;
47114402Sru  void line(const position &, const position *, int n,
48114402Sru	    const line_type &);
49114402Sru  void polygon(const position *, int n,
50114402Sru	       const line_type &, double);
51114402Sru  void spline(const position &, const position *, int n,
52114402Sru	      const line_type &);
53114402Sru  void arc(const position &, const position &, const position &,
54114402Sru	   const line_type &);
55114402Sru  void circle(const position &, double rad, const line_type &, double);
56114402Sru  void ellipse(const position &, const distance &, const line_type &, double);
57114402Sru  int supports_filled_polygons();
58114402Sru};
59114402Sru
60114402Sruint simple_output::supports_filled_polygons()
61114402Sru{
62114402Sru  return driver_extension_flag != 0;
63114402Sru}
64114402Sru
65114402Sruvoid simple_output::arc(const position &start, const position &cent,
66114402Sru			const position &end, const line_type &lt)
67114402Sru{
68114402Sru  switch (lt.type) {
69114402Sru  case line_type::solid:
70114402Sru    line_thickness(lt.thickness);
71114402Sru    simple_arc(start, cent, end);
72114402Sru    break;
73114402Sru  case line_type::invisible:
74114402Sru    break;
75114402Sru  case line_type::dashed:
76114402Sru    dashed_arc(start, cent, end, lt);
77114402Sru    break;
78114402Sru  case line_type::dotted:
79114402Sru    dotted_arc(start, cent, end, lt);
80114402Sru    break;
81114402Sru  }
82114402Sru}
83114402Sru
84114402Sruvoid simple_output::line(const position &start, const position *v, int n,
85114402Sru			 const line_type &lt)
86114402Sru{
87114402Sru  position pos = start;
88114402Sru  line_thickness(lt.thickness);
89114402Sru  for (int i = 0; i < n; i++) {
90114402Sru    switch (lt.type) {
91114402Sru    case line_type::solid:
92114402Sru      simple_line(pos, v[i]);
93114402Sru      break;
94114402Sru    case line_type::dotted:
95114402Sru      {
96114402Sru	distance vec(v[i] - pos);
97114402Sru	double dist = hypot(vec);
98114402Sru	int ndots = int(dist/lt.dash_width + .5);
99114402Sru	if (ndots == 0)
100114402Sru	  dot(pos, lt);
101114402Sru	else {
102114402Sru	  vec /= double(ndots);
103114402Sru	  for (int j = 0; j <= ndots; j++)
104114402Sru	    dot(pos + vec*j, lt);
105114402Sru	}
106114402Sru      }
107114402Sru      break;
108114402Sru    case line_type::dashed:
109114402Sru      {
110114402Sru	distance vec(v[i] - pos);
111114402Sru	double dist = hypot(vec);
112114402Sru	if (dist <= lt.dash_width*2.0)
113114402Sru	  simple_line(pos, v[i]);
114114402Sru	else {
115114402Sru	  int ndashes = int((dist - lt.dash_width)/(lt.dash_width*2.0) + .5);
116114402Sru	  distance dash_vec = vec*(lt.dash_width/dist);
117114402Sru	  double dash_gap = (dist - lt.dash_width)/ndashes;
118114402Sru	  distance dash_gap_vec = vec*(dash_gap/dist);
119114402Sru	  for (int j = 0; j <= ndashes; j++) {
120114402Sru	    position s(pos + dash_gap_vec*j);
121114402Sru	    simple_line(s, s + dash_vec);
122114402Sru	  }
123114402Sru	}
124114402Sru      }
125114402Sru      break;
126114402Sru    case line_type::invisible:
127114402Sru      break;
128114402Sru    default:
129114402Sru      assert(0);
130114402Sru    }
131114402Sru    pos = v[i];
132114402Sru  }
133114402Sru}
134114402Sru
135114402Sruvoid simple_output::spline(const position &start, const position *v, int n,
136114402Sru			   const line_type &lt)
137114402Sru{
138114402Sru  line_thickness(lt.thickness);
139114402Sru  simple_spline(start, v, n);
140114402Sru}
141114402Sru
142114402Sruvoid simple_output::polygon(const position *v, int n,
143114402Sru			    const line_type &lt, double fill)
144114402Sru{
145114402Sru  if (driver_extension_flag && ((fill >= 0.0) || (get_last_filled() != 0))) {
146114402Sru    if (get_last_filled() == 0)
147114402Sru      set_fill(fill);
148114402Sru    simple_polygon(1, v, n);
149114402Sru  }
150114402Sru  if (lt.type == line_type::solid && driver_extension_flag) {
151114402Sru    line_thickness(lt.thickness);
152114402Sru    simple_polygon(0, v, n);
153114402Sru  }
154114402Sru  else if (lt.type != line_type::invisible) {
155114402Sru    line_thickness(lt.thickness);
156114402Sru    line(v[n - 1], v, n, lt);
157114402Sru  }
158114402Sru}
159114402Sru
160114402Sruvoid simple_output::circle(const position &cent, double rad,
161114402Sru			   const line_type &lt, double fill)
162114402Sru{
163114402Sru  if (driver_extension_flag && ((fill >= 0.0) || (get_last_filled() != 0))) {
164114402Sru    if (get_last_filled() == 0)
165114402Sru      set_fill(fill);
166114402Sru    simple_circle(1, cent, rad);
167114402Sru  }
168114402Sru  line_thickness(lt.thickness);
169114402Sru  switch (lt.type) {
170114402Sru  case line_type::invisible:
171114402Sru    break;
172114402Sru  case line_type::dashed:
173114402Sru    dashed_circle(cent, rad, lt);
174114402Sru    break;
175114402Sru  case line_type::dotted:
176114402Sru    dotted_circle(cent, rad, lt);
177114402Sru    break;
178114402Sru  case line_type::solid:
179114402Sru    simple_circle(0, cent, rad);
180114402Sru    break;
181114402Sru  default:
182114402Sru    assert(0);
183114402Sru  }
184114402Sru}
185114402Sru
186114402Sruvoid simple_output::ellipse(const position &cent, const distance &dim,
187114402Sru			    const line_type &lt, double fill)
188114402Sru{
189114402Sru  if (driver_extension_flag && ((fill >= 0.0) || (get_last_filled() != 0))) {
190114402Sru    if (get_last_filled() == 0)
191114402Sru      set_fill(fill);
192114402Sru    simple_ellipse(1, cent, dim);
193114402Sru  }
194114402Sru  if (lt.type != line_type::invisible)
195114402Sru    line_thickness(lt.thickness);
196114402Sru  switch (lt.type) {
197114402Sru  case line_type::invisible:
198114402Sru    break;
199114402Sru  case line_type::dotted:
200151497Sru    dotted_ellipse(cent, dim, lt);
201151497Sru    break;
202114402Sru  case line_type::dashed:
203151497Sru    dashed_ellipse(cent, dim, lt);
204151497Sru    break;
205114402Sru  case line_type::solid:
206114402Sru    simple_ellipse(0, cent, dim);
207114402Sru    break;
208114402Sru  default:
209114402Sru    assert(0);
210114402Sru  }
211114402Sru}
212114402Sru
213114402Sruclass troff_output : public simple_output {
214114402Sru  const char *last_filename;
215114402Sru  position upper_left;
216114402Sru  double height;
217114402Sru  double scale;
218114402Sru  double last_line_thickness;
219114402Sru  double last_fill;
220114402Sru  char *last_filled;		// color
221114402Sru  char *last_outlined;		// color
222114402Srupublic:
223114402Sru  troff_output();
224114402Sru  ~troff_output();
225114402Sru  void start_picture(double, const position &ll, const position &ur);
226114402Sru  void finish_picture();
227114402Sru  void text(const position &, text_piece *, int, double);
228114402Sru  void dot(const position &, const line_type &);
229114402Sru  void command(const char *, const char *, int);
230114402Sru  void set_location(const char *, int);
231114402Sru  void simple_line(const position &, const position &);
232114402Sru  void simple_spline(const position &, const position *, int n);
233114402Sru  void simple_arc(const position &, const position &, const position &);
234114402Sru  void simple_circle(int, const position &, double rad);
235114402Sru  void simple_ellipse(int, const position &, const distance &);
236114402Sru  void simple_polygon(int, const position *, int);
237114402Sru  void line_thickness(double p);
238114402Sru  void set_fill(double);
239114402Sru  void set_color(char *, char *);
240114402Sru  void reset_color();
241114402Sru  char *get_last_filled();
242114402Sru  char *get_outline_color();
243114402Sru  position transform(const position &);
244114402Sru};
245114402Sru
246114402Sruoutput *make_troff_output()
247114402Sru{
248114402Sru  return new troff_output;
249114402Sru}
250114402Sru
251114402Srutroff_output::troff_output()
252114402Sru: last_filename(0), last_line_thickness(BAD_THICKNESS),
253114402Sru  last_fill(-1.0), last_filled(0), last_outlined(0)
254114402Sru{
255114402Sru}
256114402Sru
257114402Srutroff_output::~troff_output()
258114402Sru{
259114402Sru}
260114402Sru
261114402Sruinline position troff_output::transform(const position &pos)
262114402Sru{
263114402Sru  return position((pos.x - upper_left.x)/scale,
264114402Sru		  (upper_left.y - pos.y)/scale);
265114402Sru}
266114402Sru
267114402Sru#define FILL_REG "00"
268114402Sru
269114402Sru// If this register > 0, then pic will generate \X'ps: ...' commands
270114402Sru// if the aligned attribute is used.
271114402Sru#define GROPS_REG "0p"
272114402Sru
273114402Sru// If this register is defined, geqn won't produce `\x's.
274114402Sru#define EQN_NO_EXTRA_SPACE_REG "0x"
275114402Sru
276114402Sruvoid troff_output::start_picture(double sc,
277114402Sru				 const position &ll, const position &ur)
278114402Sru{
279114402Sru  upper_left.x = ll.x;
280114402Sru  upper_left.y = ur.y;
281114402Sru  scale = compute_scale(sc, ll, ur);
282114402Sru  height = (ur.y - ll.y)/scale;
283114402Sru  double width = (ur.x - ll.x)/scale;
284114402Sru  printf(".PS %.3fi %.3fi", height, width);
285114402Sru  if (args)
286114402Sru    printf(" %s\n", args);
287114402Sru  else
288114402Sru    putchar('\n');
289114402Sru  printf(".\\\" %g %g %g %g\n", ll.x, ll.y, ur.x, ur.y);
290114402Sru  printf(".\\\" %.3fi %.3fi %.3fi %.3fi\n", 0.0, height, width, 0.0);
291114402Sru  printf(".nr " FILL_REG " \\n(.u\n.nf\n");
292114402Sru  printf(".nr " EQN_NO_EXTRA_SPACE_REG " 1\n");
293114402Sru  // This guarantees that if the picture is used in a diversion it will
294114402Sru  // have the right width.
295114402Sru  printf("\\h'%.3fi'\n.sp -1\n", width);
296114402Sru}
297114402Sru
298114402Sruvoid troff_output::finish_picture()
299114402Sru{
300114402Sru  line_thickness(BAD_THICKNESS);
301114402Sru  last_fill = -1.0;		// force it to be reset for each picture
302114402Sru  reset_color();
303114402Sru  if (!flyback_flag)
304114402Sru    printf(".sp %.3fi+1\n", height);
305114402Sru  printf(".if \\n(" FILL_REG " .fi\n");
306114402Sru  printf(".br\n");
307114402Sru  printf(".nr " EQN_NO_EXTRA_SPACE_REG " 0\n");
308114402Sru  // this is a little gross
309114402Sru  set_location(current_filename, current_lineno);
310114402Sru  fputs(flyback_flag ? ".PF\n" : ".PE\n", stdout);
311114402Sru}
312114402Sru
313114402Sruvoid troff_output::command(const char *s,
314114402Sru			   const char *filename, int lineno)
315114402Sru{
316114402Sru  if (filename != 0)
317114402Sru    set_location(filename, lineno);
318114402Sru  fputs(s, stdout);
319114402Sru  putchar('\n');
320114402Sru}
321114402Sru
322114402Sruvoid troff_output::simple_circle(int filled, const position &cent, double rad)
323114402Sru{
324114402Sru  position c = transform(cent);
325114402Sru  printf("\\h'%.3fi'"
326114402Sru	 "\\v'%.3fi'"
327151497Sru	 "\\D'%c %.3fi'"
328114402Sru	 "\n.sp -1\n",
329114402Sru	 c.x - rad/scale,
330114402Sru	 c.y,
331114402Sru	 (filled ? 'C' : 'c'),
332114402Sru	 rad*2.0/scale);
333114402Sru}
334114402Sru
335114402Sruvoid troff_output::simple_ellipse(int filled, const position &cent,
336114402Sru				  const distance &dim)
337114402Sru{
338114402Sru  position c = transform(cent);
339114402Sru  printf("\\h'%.3fi'"
340114402Sru	 "\\v'%.3fi'"
341151497Sru	 "\\D'%c %.3fi %.3fi'"
342114402Sru	 "\n.sp -1\n",
343114402Sru	 c.x - dim.x/(2.0*scale),
344114402Sru	 c.y,
345114402Sru	 (filled ? 'E' : 'e'),
346114402Sru	 dim.x/scale, dim.y/scale);
347114402Sru}
348114402Sru
349114402Sruvoid troff_output::simple_arc(const position &start, const distance &cent,
350114402Sru			      const distance &end)
351114402Sru{
352114402Sru  position s = transform(start);
353114402Sru  position c = transform(cent);
354114402Sru  distance cv = c - s;
355114402Sru  distance ev = transform(end) - c;
356114402Sru  printf("\\h'%.3fi'"
357114402Sru	 "\\v'%.3fi'"
358151497Sru	 "\\D'a %.3fi %.3fi %.3fi %.3fi'"
359114402Sru	 "\n.sp -1\n",
360114402Sru	 s.x, s.y, cv.x, cv.y, ev.x, ev.y);
361114402Sru}
362114402Sru
363114402Sruvoid troff_output::simple_line(const position &start, const position &end)
364114402Sru{
365114402Sru  position s = transform(start);
366114402Sru  distance ev = transform(end) - s;
367114402Sru  printf("\\h'%.3fi'"
368114402Sru	 "\\v'%.3fi'"
369151497Sru	 "\\D'l %.3fi %.3fi'"
370114402Sru	 "\n.sp -1\n",
371114402Sru	 s.x, s.y, ev.x, ev.y);
372114402Sru}
373114402Sru
374114402Sruvoid troff_output::simple_spline(const position &start,
375114402Sru				 const position *v, int n)
376114402Sru{
377114402Sru  position pos = transform(start);
378114402Sru  printf("\\h'%.3fi'"
379114402Sru	 "\\v'%.3fi'",
380114402Sru	 pos.x, pos.y);
381151497Sru  fputs("\\D'~ ", stdout);
382114402Sru  for (int i = 0; i < n; i++) {
383114402Sru    position temp = transform(v[i]);
384114402Sru    distance d = temp - pos;
385114402Sru    pos = temp;
386114402Sru    if (i != 0)
387114402Sru      putchar(' ');
388114402Sru    printf("%.3fi %.3fi", d.x, d.y);
389114402Sru  }
390114402Sru  printf("'\n.sp -1\n");
391114402Sru}
392114402Sru
393114402Sru// a solid polygon
394114402Sru
395114402Sruvoid troff_output::simple_polygon(int filled, const position *v, int n)
396114402Sru{
397114402Sru  position pos = transform(v[0]);
398114402Sru  printf("\\h'%.3fi'"
399114402Sru	 "\\v'%.3fi'",
400114402Sru	 pos.x, pos.y);
401151497Sru  printf("\\D'%c ", (filled ? 'P' : 'p'));
402114402Sru  for (int i = 1; i < n; i++) {
403114402Sru    position temp = transform(v[i]);
404114402Sru    distance d = temp - pos;
405114402Sru    pos = temp;
406114402Sru    if (i != 1)
407114402Sru      putchar(' ');
408114402Sru    printf("%.3fi %.3fi", d.x, d.y);
409114402Sru  }
410114402Sru  printf("'\n.sp -1\n");
411114402Sru}
412114402Sru
413114402Sruconst double TEXT_AXIS = 0.22;	// in ems
414114402Sru
415114402Srustatic const char *choose_delimiter(const char *text)
416114402Sru{
417114402Sru  if (strchr(text, '\'') == 0)
418114402Sru    return "'";
419114402Sru  else
420114402Sru    return "\\(ts";
421114402Sru}
422114402Sru
423114402Sruvoid troff_output::text(const position &center, text_piece *v, int n,
424114402Sru			double ang)
425114402Sru{
426114402Sru  line_thickness(BAD_THICKNESS); // the text might use lines (eg in equations)
427114402Sru  int rotate_flag = 0;
428114402Sru  if (driver_extension_flag && ang != 0.0) {
429114402Sru    rotate_flag = 1;
430114402Sru    position c = transform(center);
431114402Sru    printf(".if \\n(" GROPS_REG " \\{\\\n"
432114402Sru	   "\\h'%.3fi'"
433114402Sru	   "\\v'%.3fi'"
434114402Sru	   "\\X'ps: exec gsave currentpoint 2 copy translate %.4f rotate neg exch neg exch translate'"
435114402Sru	   "\n.sp -1\n"
436114402Sru	   ".\\}\n",
437114402Sru	   c.x, c.y, -ang*180.0/M_PI);
438114402Sru  }
439114402Sru  for (int i = 0; i < n; i++)
440114402Sru    if (v[i].text != 0 && *v[i].text != '\0') {
441114402Sru      position c = transform(center);
442114402Sru      if (v[i].filename != 0)
443114402Sru	set_location(v[i].filename, v[i].lineno);
444114402Sru      printf("\\h'%.3fi", c.x);
445114402Sru      const char *delim = choose_delimiter(v[i].text);
446114402Sru      if (v[i].adj.h == RIGHT_ADJUST)
447114402Sru	printf("-\\w%s%s%su", delim, v[i].text, delim);
448114402Sru      else if (v[i].adj.h != LEFT_ADJUST)
449114402Sru	printf("-(\\w%s%s%su/2u)", delim, v[i].text, delim);
450114402Sru      putchar('\'');
451114402Sru      printf("\\v'%.3fi-(%dv/2u)+%dv+%.2fm",
452114402Sru	     c.y,
453114402Sru	     n - 1,
454114402Sru	     i,
455114402Sru	     TEXT_AXIS);
456114402Sru      if (v[i].adj.v == ABOVE_ADJUST)
457114402Sru	printf("-.5v");
458114402Sru      else if (v[i].adj.v == BELOW_ADJUST)
459114402Sru	printf("+.5v");
460114402Sru      putchar('\'');
461114402Sru      fputs(v[i].text, stdout);
462114402Sru      fputs("\n.sp -1\n", stdout);
463114402Sru    }
464114402Sru  if (rotate_flag)
465114402Sru    printf(".if '\\*(.T'ps' \\{\\\n"
466114402Sru	   "\\X'ps: exec grestore'\n.sp -1\n"
467114402Sru	   ".\\}\n");
468114402Sru}
469114402Sru
470114402Sruvoid troff_output::line_thickness(double p)
471114402Sru{
472114402Sru  if (p < 0.0)
473114402Sru    p = RELATIVE_THICKNESS;
474114402Sru  if (driver_extension_flag && p != last_line_thickness) {
475114402Sru    printf("\\D't %.3fp'\\h'%.3fp'\n.sp -1\n", p, -p);
476114402Sru    last_line_thickness = p;
477114402Sru  }
478114402Sru}
479114402Sru
480114402Sruvoid troff_output::set_fill(double f)
481114402Sru{
482114402Sru  if (driver_extension_flag && f != last_fill) {
483151497Sru    // \D'Fg ...' emits a node only in compatibility mode,
484151497Sru    // thus we add a dummy node
485151497Sru    printf("\\&\\D'Fg %.3f'\n.sp -1\n", 1.0 - f);
486114402Sru    last_fill = f;
487114402Sru  }
488114402Sru  if (last_filled) {
489114402Sru    free(last_filled);
490114402Sru    last_filled = 0;
491151497Sru    printf(".fcolor\n");
492114402Sru  }
493114402Sru}
494114402Sru
495114402Sruvoid troff_output::set_color(char *color_fill, char *color_outlined)
496114402Sru{
497114402Sru  if (driver_extension_flag) {
498114402Sru    if (last_filled || last_outlined) {
499114402Sru      reset_color();
500114402Sru    }
501151497Sru    // .gcolor and .fcolor emit a node in compatibility mode only,
502151497Sru    // but that won't work anyway
503114402Sru    if (color_fill) {
504151497Sru      printf(".fcolor %s\n", color_fill);
505114402Sru      last_filled = strsave(color_fill);
506114402Sru    }
507114402Sru    if (color_outlined) {
508151497Sru      printf(".gcolor %s\n", color_outlined);
509114402Sru      last_outlined = strsave(color_outlined);
510114402Sru    }
511114402Sru  }
512114402Sru}
513114402Sru
514114402Sruvoid troff_output::reset_color()
515114402Sru{
516114402Sru  if (driver_extension_flag) {
517114402Sru    if (last_filled) {
518151497Sru      printf(".fcolor\n");
519114402Sru      a_delete last_filled;
520114402Sru      last_filled = 0;
521114402Sru    }
522114402Sru    if (last_outlined) {
523151497Sru      printf(".gcolor\n");
524114402Sru      a_delete last_outlined;
525114402Sru      last_outlined = 0;
526114402Sru    }
527114402Sru  }
528114402Sru}
529114402Sru
530114402Sruchar *troff_output::get_last_filled()
531114402Sru{
532114402Sru  return last_filled;
533114402Sru}
534114402Sru
535114402Sruchar *troff_output::get_outline_color()
536114402Sru{
537114402Sru  return last_outlined;
538114402Sru}
539114402Sru
540114402Sruconst double DOT_AXIS = .044;
541114402Sru
542114402Sruvoid troff_output::dot(const position &cent, const line_type &lt)
543114402Sru{
544114402Sru  if (driver_extension_flag) {
545114402Sru    line_thickness(lt.thickness);
546114402Sru    simple_line(cent, cent);
547114402Sru  }
548114402Sru  else {
549114402Sru    position c = transform(cent);
550114402Sru    printf("\\h'%.3fi-(\\w'.'u/2u)'"
551114402Sru	   "\\v'%.3fi+%.2fm'"
552114402Sru	   ".\n.sp -1\n",
553114402Sru	   c.x,
554114402Sru	   c.y,
555114402Sru	   DOT_AXIS);
556114402Sru  }
557114402Sru}
558114402Sru
559114402Sruvoid troff_output::set_location(const char *s, int n)
560114402Sru{
561114402Sru  if (last_filename != 0 && strcmp(s, last_filename) == 0)
562114402Sru    printf(".lf %d\n", n);
563114402Sru  else {
564114402Sru    printf(".lf %d %s\n", n, s);
565114402Sru    last_filename = s;
566114402Sru  }
567114402Sru}
568