home *** CD-ROM | disk | FTP | other *** search
/ Aminet 10 / aminetcdnumber101996.iso / Aminet / util / gnu / groff_src.lha / groff-1.10src / grolj4 / lj4.cc < prev    next >
C/C++ Source or Header  |  1995-06-22  |  16KB  |  676 lines

  1. // -*- C++ -*-
  2. /* Copyright (C) 1994 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 2, or (at your option) any later
  10. 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 along
  18. with groff; see the file COPYING.  If not, write to the Free Software
  19. Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
  20.  
  21. /*
  22. TODO
  23.  
  24. option to use beziers for circle/ellipse/arc
  25. option to use lines for spline (for LJ3)
  26. duplex option
  27. duplex short/long edge options
  28. left/top offset registration
  29. output bin selection option
  30. paper source option
  31. output non-integer parameters using fixed point numbers
  32. X command to insert contents of file
  33. X command to specify inline escape sequence (how to specify unprintable chars?)
  34. X command to include bitmap graphics
  35. */
  36.  
  37. #include "driver.h"
  38.  
  39. static struct {
  40.   const char *name;
  41.   int code;
  42.   // at 300dpi
  43.   int x_offset_portrait;
  44.   int x_offset_landscape;
  45. } paper_table[] = {
  46.   { "letter", 2, 75, 60 },
  47.   { "legal", 3, 75, 60 },
  48.   { "executive", 1, 75, 60 },
  49.   { "a4", 26, 71, 59 },
  50.   { "com10", 81, 75, 60 },
  51.   { "monarch", 80, 75, 60 },
  52.   { "c5", 91, 71, 59 },
  53.   { "b5", 100, 71, 59 },
  54.   { "dl", 90, 71, 59 },
  55. };
  56.  
  57. static int paper_size = -1;
  58. static int landscape_flag = 0;
  59.  
  60. // An upper limit on the paper size in centipoints,
  61. // used for setting HPGL picture frame.
  62. #define MAX_PAPER_WIDTH (12*720)
  63. #define MAX_PAPER_HEIGHT (17*720)
  64.  
  65. // Dotted lines that are thinner than this don't work right.
  66. #define MIN_DOT_PEN_WIDTH .351
  67.  
  68. #ifndef DEFAULT_LINE_WIDTH_FACTOR
  69. // in ems/1000
  70. #define DEFAULT_LINE_WIDTH_FACTOR 40
  71. #endif
  72.  
  73. const int DEFAULT_HPGL_UNITS = 1016;
  74. int line_width_factor = DEFAULT_LINE_WIDTH_FACTOR;
  75. unsigned ncopies = 0;        // 0 means don't send ncopies command
  76.  
  77. class lj4_font : public font {
  78. public:
  79.   ~lj4_font();
  80.   void handle_unknown_font_command(const char *command, const char *arg,
  81.                    const char *filename, int lineno);
  82.   static lj4_font *load_lj4_font(const char *);
  83.   int weight;
  84.   int style;
  85.   int proportional;
  86.   int typeface;
  87. private:
  88.   lj4_font(const char *);
  89. };
  90.  
  91. lj4_font::lj4_font(const char *nm)
  92. : font(nm), weight(0), style(0), proportional(0), typeface(0)
  93. {
  94. }
  95.  
  96. lj4_font::~lj4_font()
  97. {
  98. }
  99.  
  100. lj4_font *lj4_font::load_lj4_font(const char *s)
  101. {
  102.   lj4_font *f = new lj4_font(s);
  103.   if (!f->load()) {
  104.     delete f;
  105.     return 0;
  106.   }
  107.   return f;
  108. }
  109.  
  110. static struct {
  111.   const char *s;
  112.   int lj4_font::*ptr;
  113.   int min;
  114.   int max;
  115. } command_table[] = {
  116.   { "pclweight", &lj4_font::weight, -7, 7 },
  117.   { "pclstyle", &lj4_font::style, 0, 32767 },
  118.   { "pclproportional", &lj4_font::proportional, 0, 1 },
  119.   { "pcltypeface", &lj4_font::typeface, 0, 65535 },
  120. };
  121.  
  122. void lj4_font::handle_unknown_font_command(const char *command,
  123.                        const char *arg,
  124.                        const char *filename, int lineno)
  125. {
  126.   for (int i = 0; i < sizeof(command_table)/sizeof(command_table[0]); i++) {
  127.     if (strcmp(command, command_table[i].s) == 0) {
  128.       if (arg == 0)
  129.     fatal_with_file_and_line(filename, lineno,
  130.                  "`%1' command requires an argument",
  131.                  command);
  132.       char *ptr;
  133.       long n = strtol(arg, &ptr, 10);
  134.       if (n == 0 && ptr == arg)
  135.     fatal_with_file_and_line(filename, lineno,
  136.                  "`%1' command requires numeric argument",
  137.                  command);
  138.       if (n < command_table[i].min) {
  139.     error_with_file_and_line(filename, lineno,
  140.                  "argument for `%1' command must not be less than %2",
  141.                  command, command_table[i].min);
  142.     n = command_table[i].min;
  143.       }
  144.       else if (n > command_table[i].max) {
  145.     error_with_file_and_line(filename, lineno,
  146.                  "argument for `%1' command must not be greater than %2",
  147.                  command, command_table[i].max);
  148.     n = command_table[i].max;
  149.       }
  150.       this->*command_table[i].ptr = int(n);
  151.       break;
  152.     }
  153.   }
  154. }
  155.  
  156. class lj4_printer : public printer {
  157. public:
  158.   lj4_printer();
  159.   ~lj4_printer();
  160.   void set_char(int, font *, const environment *, int);
  161.   void draw(int code, int *p, int np, const environment *env);
  162.   void begin_page(int);
  163.   void end_page(int page_length);
  164.   font *make_font(const char *);
  165.   void end_of_line();
  166. private:
  167.   void set_line_thickness(int size, int dot = 0);
  168.   void hpgl_init();
  169.   void hpgl_start();
  170.   void hpgl_end();
  171.   int moveto(int hpos, int vpos);
  172.   int moveto1(int hpos, int vpos);
  173.  
  174.   int cur_hpos;
  175.   int cur_vpos;
  176.   lj4_font *cur_font;
  177.   int cur_size;
  178.   unsigned short cur_symbol_set;
  179.   int x_offset;
  180.   int line_thickness;
  181.   double pen_width;
  182.   double hpgl_scale;
  183.   int hpgl_inited;
  184. };
  185.  
  186. inline
  187. int lj4_printer::moveto(int hpos, int vpos)
  188. {
  189.   if (cur_hpos != hpos || cur_vpos != vpos || cur_hpos < 0)
  190.     return moveto1(hpos, vpos);
  191.   else
  192.     return 1;
  193. }
  194.  
  195. inline
  196. void lj4_printer::hpgl_start()
  197. {
  198.   fputs("\033%1B", stdout);
  199. }
  200.  
  201. inline
  202. void lj4_printer::hpgl_end()
  203. {
  204.   fputs(";\033%0A", stdout);
  205. }
  206.  
  207. lj4_printer::lj4_printer()
  208. : cur_hpos(-1),
  209.   cur_font(0),
  210.   cur_size(0),
  211.   cur_symbol_set(0),
  212.   line_thickness(-1),
  213.   pen_width(-1.0),
  214.   hpgl_inited(0)
  215. {
  216.   if (7200 % font::res != 0)
  217.     fatal("invalid resolution %1: resolution must be a factor of 7200",
  218.       font::res);
  219.   fputs("\033E", stdout);        // reset
  220.   if (font::res != 300)
  221.     printf("\033&u%dD", font::res); // unit of measure
  222.   if (ncopies > 0)
  223.     printf("\033&l%uX", ncopies);
  224.   if (paper_size < 0)
  225.     paper_size = 0;        // default to letter
  226.   printf("\033&l%dA"        // paper size
  227.      "\033&l%dO"        // orientation
  228.      "\033&l0E",        // no top margin
  229.      paper_table[paper_size].code,
  230.      landscape_flag != 0);
  231.   if (landscape_flag)
  232.     x_offset = paper_table[paper_size].x_offset_landscape;
  233.   else
  234.     x_offset = paper_table[paper_size].x_offset_portrait;
  235.   x_offset = (x_offset * font::res) / 300;
  236. }
  237.  
  238. lj4_printer::~lj4_printer()
  239. {
  240.   fputs("\033E", stdout);
  241. }
  242.  
  243. void lj4_printer::begin_page(int)
  244. {
  245. }
  246.  
  247. void lj4_printer::end_page(int)
  248. {
  249.   putchar('\f');
  250.   cur_hpos = -1;
  251. }
  252.  
  253. void lj4_printer::end_of_line()
  254. {
  255.   cur_hpos = -1;        // force absolute motion
  256. }
  257.  
  258. inline
  259. int is_unprintable(unsigned char c)
  260. {
  261.   return c < 32 && (c == 0 || (7 <= c && c <= 15) || c == 27);
  262. }
  263.  
  264. void lj4_printer::set_char(int index, font *f, const environment *env, int w)
  265. {
  266.   int code = f->get_code(index);
  267.  
  268.   unsigned char ch = code & 0xff;
  269.   unsigned short symbol_set = code >> 8;
  270.   if (symbol_set != cur_symbol_set) {
  271.     printf("\033(%d%c", symbol_set/32, (symbol_set & 31) + 64);
  272.     cur_symbol_set = symbol_set;
  273.   }
  274.   if (f != cur_font) {
  275.     lj4_font *psf = (lj4_font *)f;
  276.     // FIXME only output those that are needed
  277.     printf("\033(s%dp%ds%db%dT",
  278.        psf->proportional,
  279.        psf->style,
  280.        psf->weight,
  281.        psf->typeface);
  282.     if (!psf->proportional || !cur_font || !cur_font->proportional)
  283.       cur_size = 0;
  284.     cur_font = psf;
  285.   }
  286.   if (env->size != cur_size) {
  287.     if (cur_font->proportional) {
  288.       static const char *quarters[] = { "", ".25", ".5", ".75" };
  289.       printf("\033(s%d%sV", env->size/4, quarters[env->size & 3]);
  290.     }
  291.     else {
  292.       double pitch = double(font::res)/w;
  293.       // PCL uses the next largest pitch, so round it down.
  294.       pitch = floor(pitch*100.0)/100.0;
  295.       printf("\033(s%.2fH", pitch);
  296.     }
  297.     cur_size = env->size;
  298.   }
  299.   if (!moveto(env->hpos, env->vpos))
  300.     return;
  301.   if (is_unprintable(ch))
  302.     fputs("\033&p1X", stdout);
  303.   putchar(ch);
  304.   cur_hpos += w;
  305. }
  306.  
  307. int lj4_printer::moveto1(int hpos, int vpos)
  308. {
  309.   if (hpos < x_offset || vpos < 0)
  310.     return 0;
  311.   fputs("\033*p", stdout);
  312.   if (cur_hpos < 0)
  313.     printf("%dx%dY", hpos - x_offset, vpos);
  314.   else {
  315.     if (cur_hpos != hpos)
  316.       printf("%s%d%c", hpos > cur_hpos ? "+" : "",
  317.          hpos - cur_hpos, vpos == cur_vpos ? 'X' : 'x');
  318.     if (cur_vpos != vpos)
  319.       printf("%s%dY", vpos > cur_vpos ? "+" : "", vpos - cur_vpos);
  320.   }
  321.   cur_hpos = hpos;
  322.   cur_vpos = vpos;
  323.   return 1;
  324. }
  325.  
  326. void lj4_printer::draw(int code, int *p, int np, const environment *env)
  327. {
  328.   switch (code) {
  329.   case 'R':
  330.     {
  331.       if (np != 2) {
  332.     error("2 arguments required for rule");
  333.     break;
  334.       }
  335.       int hpos = env->hpos;
  336.       int vpos = env->vpos;
  337.       int hsize = p[0];
  338.       int vsize = p[1];
  339.       if (hsize < 0) {
  340.     hpos += hsize;
  341.     hsize = -hsize;
  342.       }
  343.       if (vsize < 0) {
  344.     vpos += vsize;
  345.     vsize = -vsize;
  346.       }
  347.       if (!moveto(hpos, vpos))
  348.     return;
  349.       printf("\033*c%da%db0P", hsize, vsize);
  350.       break;
  351.     }
  352.   case 'l':
  353.     if (np != 2) {
  354.       error("2 arguments required for line");
  355.       break;
  356.     }
  357.     hpgl_init();
  358.     if (!moveto(env->hpos, env->vpos))
  359.       return;
  360.     hpgl_start();
  361.     set_line_thickness(env->size, p[0] == 0 && p[1] == 0);
  362.     printf("PD%d,%d", p[0], p[1]);
  363.     hpgl_end();
  364.     break;
  365.   case 'p':
  366.   case 'P':
  367.     {
  368.       if (np & 1) {
  369.     error("even number of arguments required for polygon");
  370.     break;
  371.       }
  372.       if (np == 0) {
  373.     error("no arguments for polygon");
  374.     break;
  375.       }
  376.       hpgl_init();
  377.       if (!moveto(env->hpos, env->vpos))
  378.     return;
  379.       hpgl_start();
  380.       if (code == 'p')
  381.     set_line_thickness(env->size);
  382.       printf("PMPD%d", p[0]);
  383.       for (int i = 1; i < np; i++)
  384.     printf(",%d", p[i]);
  385.       printf("PM2%cP", code == 'p' ? 'E' : 'F');
  386.       hpgl_end();
  387.       break;
  388.     }
  389.   case '~':
  390.     {
  391.       if (np & 1) {
  392.     error("even number of arguments required for spline");
  393.     break;
  394.       }
  395.       if (np == 0) {
  396.     error("no arguments for spline");
  397.     break;
  398.       }
  399.       hpgl_init();
  400.       if (!moveto(env->hpos, env->vpos))
  401.     return;
  402.       hpgl_start();
  403.       set_line_thickness(env->size);
  404.       printf("PD%d,%d", p[0]/2, p[1]/2);
  405.       const int tnum = 2;
  406.       const int tden = 3;
  407.       if (np > 2) {
  408.     fputs("BR", stdout);
  409.     for (int i = 0; i < np - 2; i += 2) {
  410.       if (i != 0)
  411.         putchar(',');
  412.       printf("%d,%d,%d,%d,%d,%d",
  413.          (p[i]*tnum)/(2*tden),
  414.          (p[i + 1]*tnum)/(2*tden),
  415.          p[i]/2 + (p[i + 2]*(tden - tnum))/(2*tden),
  416.          p[i + 1]/2 + (p[i + 3]*(tden - tnum))/(2*tden),
  417.          (p[i] - p[i]/2) + p[i + 2]/2,
  418.          (p[i + 1] - p[i + 1]/2) + p[i + 3]/2);
  419.     }
  420.       }
  421.       printf("PR%d,%d", p[np - 2] - p[np - 2]/2, p[np - 1] - p[np - 1]/2);
  422.       hpgl_end();
  423.       break;
  424.     }
  425.   case 'c':
  426.   case 'C':
  427.     // troff adds an extra argument to C
  428.     if (np != 1 && !(code == 'C' && np == 2)) {
  429.       error("1 argument required for circle");
  430.       break;
  431.     }
  432.     hpgl_init();
  433.     if (!moveto(env->hpos + p[0]/2, env->vpos))
  434.       return;
  435.     hpgl_start();
  436.     if (code == 'c') {
  437.       set_line_thickness(env->size);
  438.       printf("CI%d", p[0]/2);
  439.     }
  440.     else
  441.       printf("WG%d,0,360", p[0]/2);
  442.     hpgl_end();
  443.     break;
  444.   case 'e':
  445.   case 'E':
  446.     if (np != 2) {
  447.       error("2 arguments required for ellipse");
  448.       break;
  449.     }
  450.     hpgl_init();
  451.     if (!moveto(env->hpos + p[0]/2, env->vpos))
  452.       return;
  453.     hpgl_start();
  454.     printf("SC0,%.4f,0,-%.4f,2", hpgl_scale * double(p[0])/p[1], hpgl_scale);
  455.     if (code == 'e') {
  456.       set_line_thickness(env->size);
  457.       printf("CI%d", p[1]/2);
  458.     }
  459.     else
  460.       printf("WG%d,0,360", p[1]/2);
  461.     printf("SC0,%.4f,0,-%.4f,2", hpgl_scale, hpgl_scale);
  462.     hpgl_end();
  463.     break;
  464.   case 'a':
  465.     {
  466.       if (np != 4) {
  467.     error("4 arguments required for arc");
  468.     break;
  469.       }
  470.       hpgl_init();
  471.       if (!moveto(env->hpos, env->vpos))
  472.     return;
  473.       hpgl_start();
  474.       set_line_thickness(env->size);
  475.       double c[2];
  476.       if (adjust_arc_center(p, c)) {
  477.     double sweep = ((atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0])
  478.              - atan2(-c[1], -c[0]))
  479.             * 180.0/PI);
  480.     if (sweep > 0.0)
  481.       sweep -= 360.0;
  482.     printf("PDAR%d,%d,%f", int(c[0]), int(c[1]), sweep);
  483.       }
  484.       else
  485.     printf("PD%d,%d", p[0] + p[2], p[1] + p[3]);
  486.       hpgl_end();
  487.     }
  488.     break;
  489.   case 'f':
  490.     if (np != 1 && np != 2) {
  491.       error("1 argument required for fill");
  492.       break;
  493.     }
  494.     hpgl_init();
  495.     hpgl_start();
  496.     if (p[0] >= 0 && p[0] <= 1000)
  497.       printf("FT10,%d", p[0]/10);
  498.     hpgl_end();
  499.     break;
  500.   case 't':
  501.     {
  502.       if (np == 0) {
  503.     line_thickness = -1;
  504.       }
  505.       else {
  506.     // troff gratuitously adds an extra 0
  507.     if (np != 1 && np != 2) {
  508.       error("0 or 1 argument required for thickness");
  509.       break;
  510.     }
  511.     line_thickness = p[0];
  512.       }
  513.       break;
  514.     }
  515.   default:
  516.     error("unrecognised drawing command `%1'", char(code));
  517.     break;
  518.   }
  519. }
  520.  
  521. void lj4_printer::hpgl_init()
  522. {
  523.   if (hpgl_inited)
  524.     return;
  525.   hpgl_inited = 1;
  526.   hpgl_scale = double(DEFAULT_HPGL_UNITS)/font::res;
  527.   printf("\033&f0S"        // push position
  528.      "\033*p0x0Y"        // move to 0,0
  529.      "\033*c%dx%dy0T" // establish picture frame
  530.      "\033%%1B"          // switch to HPGL
  531.      "SP1SC0,%.4f,0,-%.4f,2IR0,100,0,100" // set up scaling
  532.      "LA1,4,2,4"        // round line ends and joins
  533.      "PR"            // relative plotting
  534.      "TR0"            // opaque
  535.      ";\033%%1A"        // back to PCL
  536.      "\033&f1S",        // pop position
  537.      MAX_PAPER_WIDTH, MAX_PAPER_HEIGHT,
  538.      hpgl_scale, hpgl_scale);
  539. }
  540.  
  541. void lj4_printer::set_line_thickness(int size, int dot)
  542. {
  543.   double pw;
  544.   if (line_thickness < 0)
  545.     pw = (size * (line_width_factor * 25.4))/(font::sizescale * 72000.0);
  546.   else
  547.     pw = line_thickness*25.4/font::res;
  548.   if (dot && pw < MIN_DOT_PEN_WIDTH)
  549.     pw = MIN_DOT_PEN_WIDTH;
  550.   if (pw != pen_width) {
  551.     printf("PW%f", pw);
  552.     pen_width = pw;
  553.   }
  554. }
  555.  
  556. font *lj4_printer::make_font(const char *nm)
  557. {
  558.   return lj4_font::load_lj4_font(nm);
  559. }
  560.  
  561. printer *make_printer()
  562. {
  563.   return new lj4_printer;
  564. }
  565.  
  566. static
  567. int lookup_paper_size(const char *s)
  568. {
  569.   for (int i = 0; i < sizeof(paper_table)/sizeof(paper_table[0]); i++)
  570.     // FIXME Do this case-insensitively.
  571.     // Perhaps allow unique prefix.
  572.     if (strcmp(s, paper_table[i].name) == 0)
  573.       return i;
  574.   return -1;
  575. }
  576.  
  577. static
  578. void handle_unknown_desc_command(const char *command, const char *arg,
  579.                  const char *filename, int lineno)
  580. {
  581.   if (strcmp(command, "papersize") == 0) {
  582.     if (arg == 0)
  583.       error_with_file_and_line(filename, lineno,
  584.                    "`papersize' command requires an argument");
  585.     else if (paper_size < 0) {
  586.       int n = lookup_paper_size(arg);
  587.       if (n < 0)
  588.     error_with_file_and_line(filename, lineno,
  589.                  "unknown paper size `%1'", arg);
  590.       else
  591.     paper_size = n;
  592.     }
  593.   }
  594. }
  595.  
  596. static void usage();
  597.  
  598. int main(int argc, char **argv)
  599. {
  600.   program_name = argv[0];
  601.   static char stderr_buf[BUFSIZ];
  602.   setbuf(stderr, stderr_buf);
  603.   font::set_unknown_desc_command_handler(handle_unknown_desc_command);
  604.   int c;
  605.   while ((c = getopt(argc, argv, "F:p:lvw:c:")) != EOF)
  606.     switch(c) {
  607.     case 'l':
  608.       landscape_flag = 1;
  609.       break;
  610.     case 'p':
  611.       {
  612.     int n = lookup_paper_size(optarg);
  613.     if (n < 0)
  614.       error("unknown paper size `%1'", optarg);
  615.     else
  616.       paper_size = n;
  617.     break;
  618.       }
  619.     case 'v':
  620.       {
  621.     extern const char *version_string;
  622.     fprintf(stderr, "grolj4 version %s\n", version_string);
  623.     fflush(stderr);
  624.     break;
  625.       }
  626.     case 'F':
  627.       font::command_line_font_dir(optarg);
  628.       break;
  629.     case 'c':
  630.       {
  631.     char *ptr;
  632.     long n = strtol(optarg, &ptr, 10);
  633.     if (n == 0 && ptr == optarg)
  634.       error("argument for -c must be a positive integer");
  635.     else if (n <= 0 || n > 32767)
  636.       error("out of range argument for -c");
  637.     else
  638.       ncopies = unsigned(n);
  639.     break;
  640.       }
  641.     case 'w':
  642.       {
  643.     char *ptr;
  644.     long n = strtol(optarg, &ptr, 10);
  645.     if (n == 0 && ptr == optarg)
  646.       error("argument for -w must be a non-negative integer");
  647.     else if (n < 0 || n > INT_MAX)
  648.       error("out of range argument for -w");
  649.     else
  650.       line_width_factor = int(n);
  651.     break;
  652.       }
  653.     case '?':
  654.       usage();
  655.       break;
  656.     default:
  657.       assert(0);
  658.     }
  659.   if (optind >= argc)
  660.     do_file("-");
  661.   else {
  662.     for (int i = optind; i < argc; i++)
  663.       do_file(argv[i]);
  664.   }
  665.   delete pr;
  666.   return 0;
  667. }
  668.  
  669. static void usage()
  670. {
  671.   fprintf(stderr,
  672.       "usage: %s [-lv] [-c n] [-p paper_size] [-w n] [-F dir] [files ...]\n",
  673.       program_name);
  674.   exit(1);
  675. }
  676.