diff options
-rw-r--r-- | ChangeLog | 99 | ||||
-rw-r--r-- | NEWS | 17 | ||||
-rw-r--r-- | doc/groff.texinfo | 11 | ||||
-rw-r--r-- | doc/pic.ms | 8 | ||||
-rw-r--r-- | font/devps/prologue.ps | 25 | ||||
-rw-r--r-- | man/groff.man | 3 | ||||
-rw-r--r-- | man/groff_diff.man | 11 | ||||
-rw-r--r-- | man/groff_out.man | 8 | ||||
-rw-r--r-- | src/devices/grohtml/html-text.cc | 16 | ||||
-rw-r--r-- | src/devices/grohtml/post-html.cc | 12 | ||||
-rw-r--r-- | src/devices/grops/ps.cc | 153 | ||||
-rw-r--r-- | src/devices/grops/ps.h | 3 | ||||
-rw-r--r-- | src/include/color.h | 65 | ||||
-rw-r--r-- | src/include/lib.h | 3 | ||||
-rw-r--r-- | src/libs/libdriver/input.cc | 1961 | ||||
-rw-r--r-- | src/libs/libgroff/color.cc | 434 | ||||
-rw-r--r-- | src/libs/libgroff/geometry.cc | 32 | ||||
-rw-r--r-- | src/libs/libgroff/itoa.c | 17 | ||||
-rw-r--r-- | src/preproc/pic/object.cc | 22 | ||||
-rw-r--r-- | src/preproc/pic/pic.man | 13 | ||||
-rw-r--r-- | src/roff/troff/env.cc | 10 | ||||
-rw-r--r-- | src/roff/troff/input.cc | 73 | ||||
-rw-r--r-- | src/roff/troff/node.cc | 112 | ||||
-rw-r--r-- | tmac/andoc.tmac | 5 | ||||
-rw-r--r-- | tmac/groff_www.man | 2 |
25 files changed, 2192 insertions, 923 deletions
@@ -1,6 +1,105 @@ +2002-01-24 Werner LEMBERG <wl@gnu.org> + + * tmac/groff_www.man, NEWS: Fix typos. + +2002-01-21 Werner LEMBERG <wl@gnu.org> + + Complete revision of color support: + + Adapt programs to the new libdriver/input.cc. + + Color spaces are no longer converted to RGB but transferred as-is + in the troff intermediate output format. + + Handle default color gracefully. troff now supports a `default' + color (which can't be changed). + + grops will now use the proper color space functions if available. + + Update pic. + + Note that currently grohtml doesn't handle colors properly. This + has to be fixed. + + * src/libgroff/itoa.c (UINT_DIGITS): New macro. + (ui_to_a): New function. + * src/include/lib.h: Updated. + + * src/include/color.h (color_scheme): Replace `NONE' with `DEFAULT'. + (color): Simplified; removed all `double' members and methods. + A new array `components' now holds the color parameters. + (color::is_default, color::get_components): New methods. + (color::operator==, color:operator!=): New. + (Red, Green, Blue, Cyan, Magenta, Yellow, Black, Gray): New macros + to make access to the `components' array more comprehensible. + * src/libgroff/color.cc: Implement new color support. + (atoh): Small fixes. + (color::read_encoding): Simplified for new troff intermediate color + output format. + (default_color): New global variable. + + * src/roff/troff/input.cc (default_symbol): New global variable. + (lookup_color): Use it. + (default_black): Removed. + (do_glyph_color, do_fill_color): Simplified. + (define_color): Handle default color. + Improve warnings. + (do_if_request): Handle default color. + * src/roff/troff/env.cc (environment::environment): Initialize + colors with `default_color'. + * src/roff/troff/node.cc (troff_output_file::put): Add method + for `unsigned int'. + (troff_output_file::hex): Removed. + (troff_output_file::fill_color, troff_output_file::glyph_color): + Updated to include/color.h and libdriver/input.cc. + + * src/preproc/pic/object.cc (draw_arrow): New parameter to set + fill color properly (identically to the outline color). \D'f...' + doesn't work any more. + All function calls to it updated. + + * src/devices/grohtml/post-html.cc (html_printer::do_body, main): + Updated. + * src/devices/grohtml/html-text.cc (html_text::issue_color_begin): + Updated. + + * src/devices/grops/ps.cc (ps_output::put_color): New method. + (ps_printer::sbuf_color): Make a real member instead of pointer. + (ps_printer::fill_color, ps_printer::output_color): Removed. + (ps_printer::ps_printer): Updated. + (ps_printer::set_char): Ditto. + (ps_printer::set_color): Use various color schemes. + Use `put_color' method. + (ps_printer::flush_sbuf): Don't set color. + (ps_printer::fill_path): Take `environment' as parameter. + Simplify color handling. + (ps_printer::set_line_thickness): Renamed to ... + (ps_printer::set_line_thickness_and_color): This (and updated). + (ps_printer::set_color): Change second parameter from `complete' + to `fill' which better describes what it does. + (ps_printer::draw): Call `flush_sbuf' to output graphic commands + and text in the right order. + Updated. + Remove branches for `f' and `F'; this is handled by + libdriver/input.cc. + * src/devices/grops/ps.h: Updated. + * font/devps/prologue (FL): Redefined + ({F,C}r,k,g: New color functions (with and without filling). + + * doc/pic.ms, src/preproc/pic/pic.man: Small fixes. + * man/groff_diff.man, man/groff.man, man/groff_out.man, + doc/groff.texinfo, NEWS: Updated. + +2002-01-20 Bernd Warken <bwarken@mayn.de> + + * src/libs/libdriver/input.cc: Completely rewritten. See comments + in this file for what has been changed. + 2002-01-19 Werner LEMBERG <wl@gnu.org> * test-groff: Fix GROFF_FONT_PATH. + * tmac/andoc.tmac: Add dummy macros for equation support -- eqnrc + is read before .TH or .Dd is parsed. 2002-01-18 Gaius Mulley <gaius@glam.ac.uk> @@ -13,7 +13,8 @@ o Color support has been added to troff and pic (and to two device drivers, drawing color, the escape sequence `\M' specifies the background color for closed objects created with \D'...' commands. Similar to fonts, `\mP' and `\MP' switch back to the previous color. `\m' and `\M' correspond to the - new troff output commands `m' and `DF'. + new troff output commands `m' and `DF'. The device-specific default color + is called `default' and can't be redefined. Outputting color can be disabled in troff and groff with the option -c (it is always disabled in compatibility mode). @@ -23,7 +24,8 @@ o Color support has been added to troff and pic (and to two device drivers, is defined (with .if and .ie), a new conditional operator `m' is available. - More details can be found in the troff manual page. + More details can be found in the groff_diff.7 manual page and in + groff.texinfo. o Two new glyph symbols are available: `eu' is the official Euro symbol; `Eu' is a font-specific glyph variant. @@ -56,7 +58,7 @@ o The new request `itc' is a variant of `.it' for which a line interrupted with \c counts as one input line. o A new escape sequence `\O' is available (mainly for internal use with - grohtml). Please see groff_diff.man and groff.texinfo for more details. + grohtml). Please see groff_diff.7 and groff.texinfo for more details. o Two macros `AT' (AT&T) and `UC' (Univ. of California) have been added to the man macros for compatibility with older BSD releases. @@ -68,12 +70,15 @@ o The `-xwidth' specifier in the mdoc macro package has been removed. Its o A new macro `Ex' has been added to the mdoc macro package to document an exit status. -o `troff.man' has been split. Differences to UNIX troff are now documented - in the new man page `groff_diff.man'. +o `troff.1' has been split. Differences to UNIX troff are now documented + in the new man page `groff_diff.7'. -o `groff_mwww.man' has been renamed to `groff_www.man'. The file mwww.tmac +o `groff_mwww.1' has been renamed to `groff_www.1'. The file mwww.tmac has been removed. +o `groff_ms.7' has been completely rewritten. It now contains a complete + reference to the ms macros. + o The macro `NO-AUTO-RULE' has been added to www.tmac; it suppresses the generation of top and bottom rules which grohtml emits by default. diff --git a/doc/groff.texinfo b/doc/groff.texinfo index 91369cff..07f38aef 100644 --- a/doc/groff.texinfo +++ b/doc/groff.texinfo @@ -8696,7 +8696,7 @@ The @code{als} request can make a macro have more than one name. This would be called as @Example -.vl $Id: groff.texinfo,v 1.90 2002/01/13 08:22:10 wlemb Exp $ +.vl $Id: groff.texinfo,v 1.91 2002/01/24 22:37:30 wlemb Exp $ @endExample @endDefesc @@ -9655,17 +9655,20 @@ following values: @code{rgb} (three components), @code{cym} (three components), @code{cmyk} (four components), and @code{gray} or @code{grey} (one component). +@cindex default color +@cindex color, default Color components can be given either as a hexadecimal string or as positive decimal integers in the range 0--65535. A hexadecimal string contains all color components concatenated. It must start with either @code{#} or @code{##}; the former specifies hex values in the range 0--255 (which are internally multiplied by@w{ }257), the latter in the range 0--65535. Examples: @code{#FFC0CB} (pink), @code{##ffff0000ffff} -(magenta). +(magenta). The default color name @c{default} can't be redefined; its +value is device-specific (usually black). It is possible that the +default color for @code{\m} and @code{\M} is not identical. @cindex @code{f} unit, and colors @cindex unit, @code{f}, and colors - A new scaling indicator @code{f} has been introduced which multiplies its value by 65536; this makes it convenient to specify color components as fractions in the range 0 to@w{ }1 (1f equals 65536u). Example: @@ -9674,7 +9677,7 @@ as fractions in the range 0 to@w{ }1 (1f equals 65536u). Example: .defcolor darkgreen rgb 0.1f 0.5f 0.2f @endExample -Note that @code{f} is the default scaling indicator for the +Note that @code{f} is the default scaling indicator for the @code{defcolor} request, thus the above statement is equivalent to @Example @@ -10,7 +10,7 @@ .\" This document was written for free use and redistribution by .\" Eric S. Raymond <esr@thyrsus.com> in August 1995. .\" -.\" $Id: pic.ms,v 1.10 2002/01/13 08:22:10 wlemb Exp $ +.\" $Id: pic.ms,v 1.11 2002/01/24 22:37:30 wlemb Exp $ .\" .\" Set a proper TeX .ie t .ds tx T\h'-.1667m'\v'.224m'E\v'-.224m'\h'-.125m'X @@ -670,9 +670,9 @@ Alternative spellings are \fBcolour\fP, \fBcolored\fP, \fBcoloured\fP, and \fBoutlined\fP. .PP Currently, color support is not available in \*(tx mode. Predefined color -names for \fIgroff\fP(1) are in the file \f(CWcolor.tmac\fP; additional -colors can be defined with the \f(CW.defcolor\fP request (see the manual -page of GNU \fItroff\fP(1) for more details). +names for \fIgroff\fP(1) are in the device macro files, for example +\f(CWps.tmac\fP; additional colors can be defined with the \f(CW.defcolor\fP +request (see the manual page of GNU \fItroff\fP(1) for more details). .NH 1 More About Text Placement .PP diff --git a/font/devps/prologue.ps b/font/devps/prologue.ps index d95b8fd2..41adab26 100644 --- a/font/devps/prologue.ps +++ b/font/devps/prologue.ps @@ -148,24 +148,33 @@ % fill the last path -% amount FL - +% r g b Fr - -/FL { - currentgray exch setgray fill setgray +/Fr { + setrgbcolor fill } bind def -% r g b FC - +% c m y k Fk - -/FC { - setrgbcolor fill +/Fk { + setcmykcolor fill +} bind def + +% g Fg - + +/Fg { + setgray fill } bind def % fill with the ``current color'' -/BL /fill load def +/FL /fill load def /LW /setlinewidth load def -/CO /setrgbcolor load def + +/Cr /setrgbcolor load def +/Ck /setcmykcolor load def +/Cg /setgray load def % new_font_name encoding_vector old_font_name RE - diff --git a/man/groff.man b/man/groff.man index abd1c423..7bc08750 100644 --- a/man/groff.man +++ b/man/groff.man @@ -1371,6 +1371,9 @@ as a string of two-digit hexadecimal color components with a leading .BR # , or as a string of four-digit hexadecimal components with two leading .BR # . +The color +.B default +can't be redefined. . .REQ .dei macro Define or redefine a macro whose name is contained in the string register diff --git a/man/groff_diff.man b/man/groff_diff.man index c4f687ce..6049c423 100644 --- a/man/groff_diff.man +++ b/man/groff_diff.man @@ -1127,6 +1127,17 @@ request, thus the above statement is equivalent to .ft .RE . +.IP +The color named +.B default +(which is device-specific) can't be redefined. +. +It is possible that the default color for +.esc M +and +.esc m +is not the same. +. .TP .BI .dei\ xx\ yy Define macro indirectly. diff --git a/man/groff_out.man b/man/groff_out.man index 99d77d8b..cc0d9c91 100644 --- a/man/groff_out.man +++ b/man/groff_out.man @@ -628,8 +628,8 @@ where all arguments are integers between 0 and \n[@maxcolor] with black having all arguments\~0. . .command md -Set color for glyphs and line drawing to the default color value -(black). +Set color for glyphs and line drawing to the default drawing color value +(black in most cases). . .command mg "gray" Set color for glyphs and line drawing to the shade of gray given by @@ -893,8 +893,8 @@ having all arguments\~0. No position changing. . .D-command Fd -Set fill color for solid drawing objects to the default color value -(black). +Set fill color for solid drawing objects to the default fill color value +(black in most cases). . No position changing. . diff --git a/src/devices/grohtml/html-text.cc b/src/devices/grohtml/html-text.cc index e78bddad..0d550230 100644 --- a/src/devices/grohtml/html-text.cc +++ b/src/devices/grohtml/html-text.cc @@ -1,5 +1,5 @@ // -*- C++ -*- -/* Copyright (C) 2000, 2001 Free Software Foundation, Inc. +/* Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc. * * Gaius Mulley (gaius@glam.ac.uk) wrote html-text.cc * @@ -105,15 +105,17 @@ void html_text::issue_tag (char *tagname, char *arg) void html_text::issue_color_begin (color *c) { - double r, g, b; + unsigned int r, g, b; char buf[6+1]; out->put_string("<font color=\"#"); - c->get_rgb(&r, &g, &b); - sprintf(buf, "%.2X%.2X%.2X", - (unsigned int)(((double) 0xff)*r), - (unsigned int)(((double) 0xff)*g), - (unsigned int)(((double) 0xff)*b)); + if (c->is_default()) + sprintf(buf, "000000"); + else { + c->get_rgb(&r, &g, &b); + // we have to scale 0..0xFFFF to 0..0xFF + sprintf(buf, "%.2X%.2X%.2X", r/0x101, g/0x101, b/0x101); + } out->put_string(buf); out->put_string("\">"); } diff --git a/src/devices/grohtml/post-html.cc b/src/devices/grohtml/post-html.cc index 8d8b77fe..9d734ace 100644 --- a/src/devices/grohtml/post-html.cc +++ b/src/devices/grohtml/post-html.cc @@ -1,5 +1,5 @@ // -*- C++ -*- -/* Copyright (C) 2000, 2001 Free Software Foundation, Inc. +/* Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc. * * Gaius Mulley (gaius@glam.ac.uk) wrote post-html.cc * but it owes a huge amount of ideas and raw code from @@ -2687,14 +2687,12 @@ void html_printer::do_body (void) if (background == NULL) fputs("<body>\n\n", stdout); else { - double r, g, b; + unsigned int r, g, b; char buf[6+1]; background->get_rgb(&r, &g, &b); - sprintf(buf, "%.2X%.2X%.2X", - (unsigned int)(((double) 0xff)*r), - (unsigned int)(((double) 0xff)*g), - (unsigned int)(((double) 0xff)*b)); + // we have to scale 0..0xFFFF to 0..0xFF + sprintf(buf, "%.2X%.2X%.2X", r/0x101, g/0x101, b/0x101); fputs("<body bgcolor=\"#", stdout); fputs(buf, stdout); @@ -2842,7 +2840,7 @@ int main(int argc, char **argv) case 'b': // set background color to white default_background = new color; - default_background->set_rgb(1.0, 1.0, 1.0); + default_background->set_gray(color::MAX_COLOR_VAL); break; case 'F': font::command_line_font_dir(optarg); diff --git a/src/devices/grops/ps.cc b/src/devices/grops/ps.cc index 8f893197..2d6abf93 100644 --- a/src/devices/grops/ps.cc +++ b/src/devices/grops/ps.cc @@ -354,6 +354,26 @@ ps_output &ps_output::put_symbol(const char *s) return *this; } +ps_output &ps_output::put_color(unsigned int c) +{ + char buf[128]; + sprintf(buf, "%.3g", double(c) / color::MAX_COLOR_VAL); + int len = strlen(buf); + if (col > 0 && col + len + need_space > max_line_length) { + putc('\n', fp); + col = 0; + need_space = 0; + } + if (need_space) { + putc(' ', fp); + col++; + } + fputs(buf, fp); + col += len; + need_space = 1; + return *this; +} + ps_output &ps_output::put_literal_symbol(const char *s) { int len = strlen(s); @@ -476,15 +496,13 @@ class ps_printer : public printer { int sbuf_space_code; int sbuf_kern; style sbuf_style; - color *sbuf_color; + color sbuf_color; // the current PS color style output_style; int output_hpos; int output_vpos; int output_draw_point_size; int line_thickness; int output_line_thickness; - color *fill_color; - color *output_color; unsigned char output_space_code; enum { MAX_DEFINED_STYLES = 50 }; style defined_styles[MAX_DEFINED_STYLES]; @@ -506,12 +524,12 @@ class ps_printer : public printer { void do_file(char *, const environment *); void do_invis(char *, const environment *); void do_endinvis(char *, const environment *); - void set_line_thickness(const environment *); - void fill_path(); + void set_line_thickness_and_color(const environment *); + void fill_path(const environment *); void encode_fonts(); void define_encoding(const char *, int); void reencode_font(ps_font *); - void set_color(color *c, int complete = 1); + void set_color(color *c, int fill = 0); public: ps_printer(); @@ -560,11 +578,6 @@ ps_printer::ps_printer() if (paper_length == 0) paper_length = 11*font::res; equalise_spaces = font::res >= 72000; - fill_color = new color; - fill_color->set_gray(1.0); // black - output_color = new color; - output_color->set_gray(1.0); - sbuf_color = output_color; } int ps_printer::set_encoding_index(ps_font *f) @@ -599,7 +612,7 @@ void ps_printer::set_char(int i, font *f, const environment *env, int w, const c if (sbuf_len < SBUF_SIZE && sty == sbuf_style && sbuf_vpos == env->vpos - && sbuf_color->is_equal(env->col)) { + && sbuf_color == *env->col) { if (sbuf_end_hpos == env->hpos) { sbuf[sbuf_len++] = code; sbuf_end_hpos += w + sbuf_kern; @@ -654,7 +667,8 @@ void ps_printer::set_char(int i, font *f, const environment *env, int w, const c sbuf_space_width = 0; sbuf_space_count = sbuf_space_diff_count = 0; sbuf_kern = 0; - sbuf_color = env->col; + if (sbuf_color != *env->col) + set_color(env->col); } static char *make_encoding_name(int encoding_index) @@ -782,18 +796,41 @@ void ps_printer::set_style(const style &sty) defined_styles[ndefined_styles++] = sty; } -void ps_printer::set_color(color *col, int complete) +void ps_printer::set_color(color *col, int fill) { - output_color = col; - if (col) { - double r, g, b; - col->get_rgb(&r, &g, &b); - out.put_float(r) - .put_float(g) - .put_float(b); - if (complete) - out.put_symbol("CO"); + sbuf_color = *col; + unsigned int components[4]; + char s[3]; + color_scheme cs = col->get_components(components); + s[0] = fill ? 'F' : 'C'; + s[2] = 0; + switch (cs) { + case DEFAULT: // black + out.put_symbol("0"); + s[1] = 'g'; + break; + case RGB: + out.put_color(Red) + .put_color(Green) + .put_color(Blue); + s[1] = 'r'; + break; + case CMY: + col->get_cmyk(&Cyan, &Magenta, &Yellow, &Black); + // fall through + case CMYK: + out.put_color(Cyan) + .put_color(Magenta) + .put_color(Yellow) + .put_color(Black); + s[1] = 'k'; + break; + case GRAY: + out.put_color(Gray); + s[1] = 'g'; + break; } + out.put_symbol(s); } void ps_printer::set_space_code(unsigned char c) @@ -826,8 +863,6 @@ void ps_printer::flush_sbuf() set_style(sbuf_style); output_style = sbuf_style; } - if (!output_color->is_equal(sbuf_color)) - set_color(sbuf_color); int extra_space = 0; if (output_hpos < 0 || output_vpos < 0) motion = ABSOLUTE; @@ -895,7 +930,7 @@ void ps_printer::flush_sbuf() sbuf_len = 0; } -void ps_printer::set_line_thickness(const environment *env) +void ps_printer::set_line_thickness_and_color(const environment *env) { if (line_thickness < 0) { if (output_draw_point_size != env->size) { @@ -915,32 +950,23 @@ void ps_printer::set_line_thickness(const environment *env) output_draw_point_size = -1; } } - if (!env->col->is_equal(output_color)) + if (sbuf_color != *env->col) set_color(env->col); } -void ps_printer::fill_path() +void ps_printer::fill_path(const environment *env) { - double k; - if (fill_color->is_gray()) { - // gray shade is a special case - fill_color->get_gray(&k); - output_color = fill_color; - out.put_float(1.0-k) - .put_symbol("FL"); - } - else if (fill_color->is_equal(output_color)) - out.put_symbol("BL"); - else { - set_color(fill_color, 0); - out.put_symbol("FC"); - } + if (sbuf_color == *env->fill) + out.put_symbol("FL"); + else + set_color(env->fill, 1); } void ps_printer::draw(int code, int *p, int np, const environment *env) { if (invis_count > 0) return; + flush_sbuf(); int fill_flag = 0; switch (code) { case 'C': @@ -956,11 +982,10 @@ void ps_printer::draw(int code, int *p, int np, const environment *env) .put_fix_number(env->vpos) .put_fix_number(p[0]/2) .put_symbol("DC"); - if (fill_flag) { - fill_path(); - } + if (fill_flag) + fill_path(env); else { - set_line_thickness(env); + set_line_thickness_and_color(env); out.put_symbol("ST"); } break; @@ -969,7 +994,7 @@ void ps_printer::draw(int code, int *p, int np, const environment *env) error("2 arguments required for line"); break; } - set_line_thickness(env); + set_line_thickness_and_color(env); out.put_fix_number(p[0] + env->hpos) .put_fix_number(p[1] + env->vpos) .put_fix_number(env->hpos) @@ -989,11 +1014,10 @@ void ps_printer::draw(int code, int *p, int np, const environment *env) .put_fix_number(env->hpos + p[0]/2) .put_fix_number(env->vpos) .put_symbol("DE"); - if (fill_flag) { - fill_path(); - } + if (fill_flag) + fill_path(env); else { - set_line_thickness(env); + set_line_thickness_and_color(env); out.put_symbol("ST"); } break; @@ -1018,11 +1042,10 @@ void ps_printer::draw(int code, int *p, int np, const environment *env) .put_fix_number(p[i+1]) .put_symbol("RL"); out.put_symbol("CL"); - if (fill_flag) { - fill_path(); - } + if (fill_flag) + fill_path(env); else { - set_line_thickness(env); + set_line_thickness_and_color(env); out.put_symbol("ST"); } break; @@ -1060,7 +1083,7 @@ void ps_printer::draw(int code, int *p, int np, const environment *env) out.put_fix_number(p[np - 2] - p[np - 2]/2) .put_fix_number(p[np - 1] - p[np - 1]/2) .put_symbol("RL"); - set_line_thickness(env); + set_line_thickness_and_color(env); out.put_symbol("ST"); } break; @@ -1070,7 +1093,7 @@ void ps_printer::draw(int code, int *p, int np, const environment *env) error("4 arguments required for arc"); break; } - set_line_thickness(env); + set_line_thickness_and_color(env); double c[2]; if (adjust_arc_center(p, c)) out.put_fix_number(env->hpos + int(c[0])) @@ -1099,22 +1122,6 @@ void ps_printer::draw(int code, int *p, int np, const environment *env) line_thickness = p[0]; } break; - case 'f': - if (np != 1 && np != 2) { - error("1 argument required for fill"); - break; - } - if (p[0] < 0 || p[0] > FILL_MAX) { - // This means fill with the current color. - fill_color->set_gray(1.0); - } - else - fill_color->set_gray(double(p[0]) / FILL_MAX); - break; - case 'F': - // fill with color env->fill - fill_color = env->fill; - break; default: error("unrecognised drawing command `%1'", char(code)); break; diff --git a/src/devices/grops/ps.h b/src/devices/grops/ps.h index 6e78597d..0e149fc1 100644 --- a/src/devices/grops/ps.h +++ b/src/devices/grops/ps.h @@ -1,5 +1,5 @@ // -*- C++ -*- -/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc. +/* Copyright (C) 1989, 1990, 1991, 1992, 2002 Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) This file is part of groff. @@ -26,6 +26,7 @@ public: ps_output &put_fix_number(int); ps_output &put_float(double); ps_output &put_symbol(const char *); + ps_output &put_color(unsigned int); ps_output &put_literal_symbol(const char *); ps_output &set_fixed_point(int); ps_output &simple_comment(const char *); diff --git a/src/include/color.h b/src/include/color.h index bd54be4d..9da07be5 100644 --- a/src/include/color.h +++ b/src/include/color.h @@ -1,5 +1,5 @@ // -*- C++ -*- -/* Copyright (C) 2001 Free Software Foundation, Inc. +/* Copyright (C) 2001, 2002 Free Software Foundation, Inc. Written by Gaius Mulley <gaius@glam.ac.uk> This file is part of groff. @@ -19,57 +19,54 @@ with groff; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -enum color_scheme {NONE, CMY, CMYK, RGB, GRAY}; - -// colors are internally held as CMYK values. +enum color_scheme {DEFAULT, CMY, CMYK, RGB, GRAY}; class color { private: - unsigned int cyan; - unsigned int magenta; - unsigned int yellow; - unsigned int black; - color_scheme scheme; // how was the color originally defined? - // and now the data structures necessary for the state machine - // inside the read routines - unsigned int color_space_num; // how many numbers have been read? - enum {REAL, HEX, UNDEFINED} encoding; - unsigned int hex_length; - unsigned int c[4]; - double d[4]; - - int read_encoding(const char *s, unsigned int n); - - public: + color_scheme scheme; + unsigned int components[4]; + + int read_encoding(color_scheme cs, const char *s, unsigned int n); + +public: enum {MAX_COLOR_VAL = 0xffff}; color(); - int is_equal(color *c); - int is_gray (void); + int operator==(const color & c) const; + int operator!=(const color & c) const; + + int is_default() { return scheme == DEFAULT; } + + void set_default(); void set_rgb(unsigned int r, unsigned int g, unsigned int b); void set_cmy(unsigned int c, unsigned int m, unsigned int y); void set_cmyk(unsigned int c, unsigned int m, unsigned int y, unsigned int k); - void set_gray(unsigned int l); - - void set_rgb(double r, double g, double b); - void set_cmy(double c, double m, double y); - void set_cmyk(double c, double m, double y, double k); - void set_gray(double l); + void set_gray(unsigned int g); int read_rgb(const char *s); int read_cmy(const char *s); int read_cmyk(const char *s); int read_gray(const char *s); + color_scheme get_components(unsigned int *c); + void get_rgb(unsigned int *r, unsigned int *g, unsigned int *b); void get_cmy(unsigned int *c, unsigned int *m, unsigned int *y); void get_cmyk(unsigned int *c, unsigned int *m, unsigned int *y, unsigned int *k); - void get_gray(unsigned int *l); - - void get_rgb(double *r, double *g, double *b); - void get_cmy(double *c, double *m, double *y); - void get_cmyk(double *c, double *m, double *y, double *k); - void get_gray(double *l); + void get_gray(unsigned int *g); }; + +#define Cyan components[0] +#define Magenta components[1] +#define Yellow components[2] +#define Black components[3] + +#define Red components[0] +#define Green components[1] +#define Blue components[2] + +#define Gray components[0] + +extern color default_color; diff --git a/src/include/lib.h b/src/include/lib.h index e507ee84..f785d481 100644 --- a/src/include/lib.h +++ b/src/include/lib.h @@ -1,5 +1,5 @@ // -*- C++ -*- -/* Copyright (C) 1989-2000, 2001 Free Software Foundation, Inc. +/* Copyright (C) 1989-2000, 2001, 2002 Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) This file is part of groff. @@ -27,6 +27,7 @@ extern "C" { char *strerror(int); #endif const char *i_to_a(int); + const char *ui_to_a(unsigned int); const char *if_to_a(int, int); } diff --git a/src/libs/libdriver/input.cc b/src/libs/libdriver/input.cc index efe63c38..629453df 100644 --- a/src/libs/libdriver/input.cc +++ b/src/libs/libdriver/input.cc @@ -1,190 +1,1446 @@ // -*- C++ -*- -/* Copyright (C) 1989, 1990, 1991, 1992, 2001 Free Software Foundation, Inc. - Written by James Clark (jjc@jclark.com) -This file is part of groff. +// <groff_src_dir>/src/libs/libdriver/input.cc -groff is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free -Software Foundation; either version 2, or (at your option) any later -version. +/* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002 + Free Software Foundation, Inc. -groff is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or -FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -for more details. + Written by James Clark (jjc@jclark.com) + Major rewrite 2001 by Bernd Warken (bwarken@mayn.de) -You should have received a copy of the GNU General Public License along -with groff; see the file COPYING. If not, write to the Free Software -Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + Last update: 14 Jan 2002 + + This file is part of groff, the GNU roff text processing system. + + groff is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + groff is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with groff; see the file COPYING. If not, write to the Free + Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/* Description + + This file implements the parser for the intermediate groff output, + see groff_out(5), and does the printout for the given device. + + All parsed information is processed within the function do_file() by + using the global object `pr' of class `printer'. So a device + postprocessor just needs to fill in the methods for the class + `printer' without having to worry about the syntax of the + intermediate output format. Consequently, the programming of groff + postprocessors is similar to the development of device-drivers. + + The prototyping for this file is done in driver.h (and error.h). + + Postprocessor programs must deallocate the global variables `pr' and + `device' using `delete', and `current_filename' using + `free((char *))'. +*/ + +/* Changes of the 2001 rewrite of this file. + + The interface to the outside and the handling of the global + variables was not changed, but internally many necessary changes + were performed. + + The main aim for this rewrite is to provide a first step towards + making groff fully compatible with classical troff without pain. + + Bugs fixed + - Unknown subcommands of `D' and `x' are now ignored like in the + classical case, but a warning is issued. This was also + implemented for the other commands. + - `DC' and `DE' commands didn't position to the right end after + drawing (now they do), see discussion below. + - So far, `x stop' was ignored. Now it terminates the processing + of the current intermediate output file like the classical troff. + - The command `c' didn't check correctly on white-space. + - The environment stack wasn't suitable for the color extensions + (replaced by a class). + - The old groff parser could only handle a prologue with the first + 3 lines having a fixed structure, while classical troff specified + the sequence of the first 3 commands without further + restrictions. Now the parser is smart about additional + white space, comments, and empty lines in the prologue. + - The old parser allowed space characters only as syntactical + separators, while classical troff had tab characters as well. + Now any sequence of tabs and/or spaces is a syntactical + separator between commands and/or arguments. + - Range checks for numbers implemented. + + New and improved features + - The color commands `m' and `DF' are added. + - The old color command `Df' is now converted and delegated to `DFg'. + - The command `F' is implemented as `use intended file name'. It + checks whether its argument agrees with the file name used so far, + otherwise a warning is issued. Then the new name is remembered + and used for the following error messages. + - Positioning of drawing commands is corrected according to the + classical rule, see the discussion below. + - Setting commands (`Dt', `Df', `DF') and polygons (`Dp' and `DP') + do not change position now. + - Filled circles and ellipses (`DC' and `DE') position at their + most right point (outlined ones `Dc' and `De' did this anyway). + - As before, all open graphical objects position to their final + drawing point (alternate sum of the command arguments). + There is a macro STUPID_DRAWING_POSITIONING that implements the + old behavior for testing purposes. + - For the `D' commands that only set the environment, the calling of + pr->send_draw() was removed because this doesn't make sense for + the `DF' commands, and the (changed) environment is sent with the + next command anyway. + - Error handling was clearly separated into warnings and fatal. + - The error behavior on additional arguments for `D' and `x' + commands with a fixed number of arguments was changed from being + ignored (former groff) to issue a warning and ignore (now), see + skip_line_x(). No fatal was chosen because both string and + integer arguments can occur. + - All D commands with a variable number of args expect an even + number of trailing integer arguments, so fatal on error was + implemented. + + Cosmetics + - Nested `switch' commands are avoided by using more functions. + Dangerous fall-thrus avoided. + - Commands and functions are sorted alphabetically (where possible). + - Dynamic arrays/buffers are now implemented as container classes. + - Some functions had an ugly return structure; this has been + streamlined by using classes. + - Use standard C math functions for number handling, so getting rid + of differences to '0'. + - The macro `IntArg' has been created for an easier transition + to guaranteed 32 bits integers (`int' is enough for GNU, while + ANSI only guarantees `long int' to have a length of 32 bits). + - The many usages of type `int' are differentiated by using `Char', + `bool', and `IntArg' where appropriate. + - To ease the calls of the local utility functions, the parser + variables `current_file', `npages', and `current_env' + (formerly env) were made global to the file (formerly they were + local to the do_file() function) + - Various comments were added. + + TODO + - Should a missing `x stop' be warned? + - Get rid of the stupid drawing positioning. + - Can the `Dt' command be completely handled by setting environment + within do_file() instead of sending to pr? + - Integer arguments must be >= 32 bits, use conditional #define. + - Classical troff output had a quasi device independence by scaling + the intermediate output to the resolution of the postprocessor + device if different from the one specified with `x T', groff does + not. So implement full quasi device indepedence, including + the mapping of the strange classical devices to the postprocessor + device (seems to be reasonably easy). + - The external, global pointer variables are not optimally handled. + - `pr' isn't used outside besides initialization and deletion. + So it could be replaced by a static local variable. For + example, a wrapper class `Postprocessor' for class `printer' with + internal make_printer() and automatic clean-up would make sense. + - The global variables `current_filename' and `current_lineno' are + only used for error reporting. So implement a static class + `Error' (`::' calls). + - Can the global `device' be made independent of libgroff? + - Implement the B-spline drawing `D~' for all graphical devices. + - The class definitions of this document could go into a new file. + - Once things will have been settled the comments in this document + could be strongly reduced. +*/ + +/* + Discussion of the positioning by drawing commands + + There was some confusion about the positioning of the graphical + pointer at the printout after having executed a `D' command. + The classical troff manual of Osanna & Kernighan specified, + + `The position after a graphical object has been drawn is + at its end; for circles and ellipses, the "end" is at the + right side.' + + From this, it follows that + - all open figures (args, splines, and lines) should position at their + final point. + - all circles and ellipses should position at their right-most point + (as if 2 halves had been drawn). + - all closed figures apart from circles and ellipses shouldn't change + the position because they return to their origin. + - all setting commands should not change position because they do not + draw any graphical object. + + In the case of the open figures, this means that the horizontal + displacement is the sum of all odd arguments and the vertical offset + the sum of all even arguments, called the alternate arguments sum + displacement in the following. + + Unfortunately, groff did not implement this simple rule. The former + documentation in groff_out(5) differed from the source code, and + neither of them is compatible with the classical rule. + + The former groff_out(5) specified to use the alternative arguments + sum displacement for calculating the drawing positioning of + non-classical commands, including the `Dt' command (setting-only) + and closed polygons. Applying this to the new groff color commands + will lead to disaster. For their arguments can take large values (> + 65000). On low resolution devices, the displacement of such large + values will corrupt the display or kill the printer. So the + nonsense specification has come to a natural end anyway. + + The groff source code, however, had no positioning for the + setting-only commands (esp. `Dt'), the right-end positioning for + outlined circles and ellipses, and the alternative argument sum + displacement for all other commands (including filled circles and + ellipses). + + The reason why no one seems to have suffered from this mayhem so + far is that the graphical objects are usually generated by + preprocessors like pic that do not depend on the automatic + positioning. When using the low level `\D' escape sequences or `D' + output commands, the strange positionings can be circumvented by + absolute positionings or by tricks like `\Z'. + + So doing an exorcism on the strange, incompatible displacements might + not harm any existing documents, but will make the usage of the + graphical escape sequences and commands natural. + + That's why the rewrite of this file returned to the reasonable, + classical specification with its clear end-of-drawing rule that is + suitable for all cases. But a macro STUPID_DRAWING_POSITIONING is + provided for testing the funny former behavior. +*/ + +#ifndef STUPID_DRAWING_POSITIONING +// uncomment next line if all non-classical D commands shall position +// to the strange alternate sum of args displacement +#define STUPID_DRAWING_POSITIONING +#endif #include "driver.h" #include "device.h" -#include "cset.h" -const char *current_filename=0; -int current_lineno; -const char *device = 0; -FILE *current_file; +#include <stdlib.h> +#include <errno.h> +#include <ctype.h> +#include <math.h> + +/********************************************************************** + local types + **********************************************************************/ + +// integer arguments of groff_out commands, must be >= 32 bits +typedef int IntArg; + +// color components of groff_out color commands, must be >= 32 bits +typedef unsigned int ColorArg; -int get_integer(); // don't read the newline -int possibly_get_integer(int *); -char *get_string(int is_long = 0); -void skip_line(); +class EnvStack { + environment **data; + size_t num_allocated; + size_t num_stored; +public: + EnvStack(void); + ~EnvStack(void); + environment *pop(void); + void push(environment *e); +}; + +// Array for IntArg values. +class IntArray { + size_t num_allocated; +public: + IntArg *data; + size_t num_stored; + IntArray(void); + IntArray(const int); + ~IntArray(void); + void append(IntArg); +}; -struct environment_list { - environment env; - environment_list *next; +// Characters read from the input queue. +class Char { + int data; +public: + Char(void) : data('\0') {} + Char(const int c) : data(c) {} + bool operator==(int c) { return (data == c) ? true : false; } + bool operator==(Char c) { return (data == c.data) ? true : false; } + operator int() const { return (int) data; } + operator unsigned char() const { return (unsigned char) data; } + operator char() const { return (char) data; } +}; - environment_list(const environment &, environment_list *); +// Buffer for string arguments (Char, not char). +class StringBuf { + size_t num_allocated; + size_t num_stored; + Char *data; // not terminated by '\0' +public: + StringBuf(void); // allocate without storing + ~StringBuf(void); + void append(const Char); // append character to `data' + char *make_string(void); // return new copy of `data' with '\0' + bool is_empty(void) { // true if none stored + return (num_stored > 0) ? false : true; + } + void reset(void); // set `num_stored' to 0 }; -environment_list::environment_list(const environment &e, environment_list *p) -: env(e), next(p) +/********************************************************************** + external variables + **********************************************************************/ + +// exported as extern by error.h (called from driver.h) +// needed for error messages (see ../libgroff/error.cc) +const char *current_filename = 0; // printable name of the current file +int current_lineno = 0; // current line number of printout + +// exported as extern by device.h; +const char *device = 0; // cancel former init with literal + +// from driver.h; pr is kept between several runs of do_file() +// extern printer *pr; + +// Note: +// +// We rely on an implementation of the `new' operator which aborts +// gracefully if it can't allocate memory (e.g. from libgroff/new.cc). + + +/********************************************************************** + static local variables + **********************************************************************/ + +FILE *current_file = 0; // current input stream for parser + +// parser environment, created and deleted by each run of do_file() +environment *current_env = 0; + +// npages: number of pages processed so far (including current page), +// _not_ the page number in the printout (can be set with `p'). +int npages = 0; + +const ColorArg +COLORARG_MAX = (ColorArg) 65536U; // == 0xFFFF + 1 == 0x10000 + +const IntArg +INTARG_MAX = (IntArg) 0x7FFFFFFF; // maximal signed 32 bits number + +const size_t +envp_size = sizeof(environment *); + + +/********************************************************************** + function declarations + **********************************************************************/ + +// utility functions +ColorArg color_from_Df_command(IntArg); // transform old color into new +void fatal_command(char); // abort for illegal command +inline Char get_char(void); // read next character from input stream +ColorArg get_color_arg(void); // read in argument for new color cmds +IntArray *get_D_fixed_args(const int, const bool = false); // read in fixed no. of int args +IntArray *get_D_variable_args(void); // variable, even no. of int args +char *get_extended_arg(void); // argument for `x X' (several lines) +IntArg get_integer_arg(void); // read in next integer argument +char *get_string_arg(void); // read in next string arg, ended by WS +inline bool is_space_or_tab(const Char); // test on space/tab char +Char next_arg_begin(void); // skip white space on current line +Char next_command(void); // go to next command, evt. diff. line +inline bool odd(const int); // test if integer is odd +void position_to_end_of_args(const IntArray *); // after drawing +void remember_filename(const char *); // set global current_filename +void send_draw(const Char, const IntArray *); // call pr->draw +void skip_line(void); // unconditionally skip to next line +bool skip_line_checked(void); // skip line, false if args are left +void skip_line_fatal(void); // skip line, fatal if args are left +void skip_line_warn(void); // skip line, warn if args are left +void skip_line_x(); // skip line in x and D commands +inline void unget_char(const Char); // restore character onto input + +// parser subcommands +void parse_color_command(color *); // color sub(sub)commands m and DF +void parse_D_command(void); // graphical subcommands +bool parse_x_command(void); // device controller subcommands + +/********************************************************************** + class methods + **********************************************************************/ + +EnvStack::EnvStack(void) { + num_allocated = 4; + // allocate pointer to array of num_allocated pointers to environment + data = (environment **) malloc(envp_size * num_allocated); + if (data == 0) + fatal("could not allocate environment data"); + num_stored = 0; +} + +EnvStack::~EnvStack(void) { + for (size_t i = 0; i < num_stored; i++) + delete data[i]; + free(data); +} + +// return top element from stack and decrease stack pointer +// +// the calling function must take care of properly deleting the result +environment * +EnvStack::pop(void) { + num_stored--; + environment *result = data[num_stored]; + data[num_stored] = 0; + return result; +} + +// copy argument and push this onto the stack +void +EnvStack::push(environment *e) { + environment *e_copy = new environment; + if (num_stored >= num_allocated) { + environment **old_data = data; + num_allocated *= 2; + data = (environment **) malloc(envp_size * num_allocated); + if (data == 0) + fatal("could not allocate data"); + for (size_t i = 0; i < num_stored; i++) + data[i] = old_data[i]; + free(old_data); + } + e_copy->col = new color; + e_copy->fill = new color; + *e_copy->col = *e->col; + *e_copy->fill = *e->fill; + e_copy->fontno = e->fontno; + e_copy->height = e->height; + e_copy->hpos = e->hpos; + e_copy->size = e->size; + e_copy->slant = e->slant; + e_copy->vpos = e->vpos; + data[num_stored] = e_copy; + num_stored++; +} + +IntArray::IntArray(void) { + num_allocated = 4; + data = new IntArg[num_allocated]; + num_stored = 0; +} + +IntArray::IntArray(const int n) { + if (n <= 0) + fatal("number of integers to be allocated must be > 0"); + num_allocated = n; + data = new IntArg[num_allocated]; + num_stored = 0; +} + +IntArray::~IntArray(void) { + a_delete data; +} + +void +IntArray::append(IntArg x) { + if (num_stored >= num_allocated) { + IntArg *old_data = data; + num_allocated *= 2; + data = new IntArg[num_allocated]; + for (size_t i = 0; i < num_stored; i++) + data[i] = old_data[i]; + a_delete old_data; + } + data[num_stored] = x; + num_stored++; +} + +StringBuf::StringBuf(void) { + num_stored = 0; + num_allocated = 128; + data = new Char[num_allocated]; +} + +StringBuf::~StringBuf(void) { + a_delete data; +} + +void +StringBuf::append(const Char c) { + if (num_stored >= num_allocated) { + Char *old_data = data; + num_allocated *= 2; + data = new Char[num_allocated]; + for (size_t i = 0; i < num_stored; i++) + data[i] = old_data[i]; + a_delete old_data; + } + data[num_stored] = c; + num_stored++; +} + +char * +StringBuf::make_string(void) { + char *result = new char[num_stored + 1]; + for (size_t i = 0; i < num_stored; i++) + result[i] = (char) data[i]; + result[num_stored] = '\0'; + return result; +} + +void +StringBuf::reset(void) { + num_stored = 0; +} + +/********************************************************************** + utility functions + **********************************************************************/ + +////////////////////////////////////////////////////////////////////// +/* color_from_Df_command: + Process the gray shade setting command Df. + + Transform Df style color into DF style color. + Df color: 0-1000, 0 is white + DF color: 0-65536, 0 is black + + The Df command is obsoleted by command DFg, but kept for + compatibility. + + XXX: Add proper handling for values < 0 or > 1000 as documented in + groff_out(5). +*/ +ColorArg +color_from_Df_command(IntArg Df_gray) { + if (Df_gray <= 0) + return COLORARG_MAX; + if (Df_gray >= 1000) + return 0; + return ColorArg((1000-Df_gray) * COLORARG_MAX / 1000); // scaling } -inline int get_char() +////////////////////////////////////////////////////////////////////// +/* fatal_command(): + Emit error message about illegal command and abort. +*/ +void +fatal_command(char command) { - return getc(current_file); + fatal("`%1' command illegal before first `p' command", command); } -/* - * remember_filename - is needed as get_string might overwrite the - * filename eventually. - */ +////////////////////////////////////////////////////////////////////// +/* get_char(): + Retrieve the next character from the input queue. -void remember_filename(const char *filename) + Return: The retrieved character (incl. EOF), converted to Char. +*/ +inline Char +get_char(void) { - if (current_filename != 0) { - free((char *)current_filename); + return (Char) getc(current_file); +} + +////////////////////////////////////////////////////////////////////// +/* get_color_arg(): + Retrieve an argument suitable for the color commands m and DF. + + Return: The retrieved color argument. +*/ +ColorArg +get_color_arg(void) { + IntArg x = get_integer_arg(); + if (x < 0 || x > (IntArg)COLORARG_MAX) { + error("color component argument out of range"); + x = 0; } - if (strcmp(filename, "-") == 0) { - filename = "<standard input>"; + return (ColorArg) x; +} + +////////////////////////////////////////////////////////////////////// +/* get_D_fixed_args(): + Get a fixed number of integer args for D commands. + + Fatal if wrong number of arguments. + Too many arguments on the line raise a warning. + A line skip is done. + + number: In-parameter, the number of args to be retrieved. + ignore: In-parameter, ignore next argument -- GNU troff always emits + pairs of parameters for `D' extensions. Default is `false'. + + Return: New IntArray containing the arguments. +*/ +IntArray * +get_D_fixed_args(const int number, const bool ignore) { + if (number <= 0) + fatal("requested number of arguments must be > 0"); + IntArray *args = new IntArray(number); + for (int i = 0; i < number; i++) + args->append(get_integer_arg()); + if (ignore) + (void) get_integer_arg(); + skip_line_x(); + return args; +} + +////////////////////////////////////////////////////////////////////// +/* get_D_variable_args(): + Get a variable even number of integer args for D commands. + + Get as many integer arguments as possible from the rest of the + current line. + - The arguments are separated by an arbitrary sequence of space or + tab characters. + - A comment, a newline, or EOF indicates the end of processing. + - Error on non-digit characters different from these. + - A final line skip is performed (except for EOF). + + Return: New IntArray of the retrieved arguments. +*/ +IntArray * +get_D_variable_args() +{ + bool done = false; + StringBuf buf = StringBuf(); + Char c = get_char(); + IntArray *args = new IntArray(); + while (!done) { + buf.reset(); + while (is_space_or_tab(c)) + c = get_char(); + if (c == '-') { + Char c1 = get_char(); + if (isdigit((int) c1)) { + buf.append(c); + c = c1; + } + else + unget_char(c1); + } + while (isdigit((int) c)) { + buf.append(c); + c = get_char(); + } + if (!buf.is_empty()) { + char *s = buf.make_string(); + errno = 0; + long int x = strtol(s, 0, 10); + if (errno + || x > INTARG_MAX || x < -INTARG_MAX) { + error("invalid integer argument, set to 0"); + x = 0; + } + args->append((IntArg) x); + delete s; + } + // Here, c is not a digit. + // Terminate on comment, end of line, or end of file, while + // space or tab indicate continuation; otherwise error. + switch((int) c) { + case '#': + skip_line(); + done = true; + break; + case '\n': + current_lineno++; + done = true; + break; + case EOF: + done = true; + break; + case ' ': + case '\t': + break; + default: + error("integer argument expected"); + break; + } } - current_filename = (const char *)malloc(strlen(filename) + 1); - if (current_filename == 0) { + if (args->num_stored <= 0) + error("no arguments found"); + if (odd(args->num_stored)) + error("even number of arguments expected"); + return args; +} + +////////////////////////////////////////////////////////////////////// +/* get_extended_arg(): + Retrieve extended arg for `x X' command. + + - Skip leading spaces and tabs, error on EOL or newline. + - Return everything before the next NL or EOF ('#' is not a comment); + as long as the following line starts with '+' this is returned + as well, with the '+' replaced by a newline. + - Final line skip is always performed. + + Return: Allocated (new) string of retrieved text argument. +*/ +char * +get_extended_arg(void) +{ + StringBuf buf = StringBuf(); + Char c = next_arg_begin(); + while ((int) c != EOF) { + if ((int) c == '\n') { + current_lineno++; + c = get_char(); + if ((int) c == '+') + buf.append((Char) '\n'); + else { + unget_char(c); // first character of next line + break; + } + } + buf.append(c); + c = get_char(); + } + return buf.make_string(); +} + +////////////////////////////////////////////////////////////////////// +/* get_integer_arg(): Retrieve integer argument. + + Skip leading spaces and tabs, collect an optional '-' and all + following decimal digits (at least one) up to the next non-digit, + which is restored onto the input queue. + + Fatal error on all other situations. + + Return: Retrieved integer. +*/ +IntArg +get_integer_arg(void) +{ + StringBuf buf = StringBuf(); + Char c = next_arg_begin(); + if ((int) c == '-') { + buf.append(c); + c = get_char(); + } + if (!isdigit((int) c)) + error("integer argument expected"); + while (isdigit((int) c)) { + buf.append(c); + c = get_char(); + } + // c is not a digit + unget_char(c); + char *s = buf.make_string(); + errno = 0; + long int number = strtol(s, 0, 10); + if (errno != 0 + || number > INTARG_MAX || number < -INTARG_MAX) { + error("integer argument too large"); + number = 0; + } + delete s; + return (IntArg) number; +} + +////////////////////////////////////////////////////////////////////// +/* get_string_arg(): + Retrieve string arg. + + - Skip leading spaces and tabs; error on EOL or newline. + - Return all following characters before the next space, tab, + newline, or EOF character (in-word '#' is not a comment character). + - The terminating space, tab, newline, or EOF character is restored + onto the input queue, so no line skip. + + Return: Retrieved string as char *, allocated by 'new'. +*/ +char * +get_string_arg(void) +{ + StringBuf buf = StringBuf(); + Char c = next_arg_begin(); + while (!is_space_or_tab(c) + && c != Char('\n') && c != Char(EOF)) { + buf.append(c); + c = get_char(); + } + unget_char(c); // restore white space + return buf.make_string(); +} + +////////////////////////////////////////////////////////////////////// +/* is_space_or_tab(): + Test a character if it is a space or tab. + + c: In-parameter, character to be tested. + + Return: True, if c is a space or tab character, false otherwise. +*/ +inline bool +is_space_or_tab(const Char c) +{ + return (c == (Char) ' ' || c == (Char) '\t') ? true : false; +} + +////////////////////////////////////////////////////////////////////// +/* next_arg_begin(): + Return first character of next argument. + + Skip space and tab characters; error on newline or EOF. + + Return: The first character different from these (including '#'). +*/ +Char +next_arg_begin(void) +{ + Char c; + while (1) { + c = get_char(); + switch ((int) c) { + case ' ': + case '\t': + break; + case '\n': + case EOF: + error("missing argument"); + break; + default: // first essential character + return c; + } + } +} + +////////////////////////////////////////////////////////////////////// +/* next_command(): + Find the first character of the next command. + + Skip spaces, tabs, comments (introduced by #), and newlines. + + Return: The first character different from these (including EOF). +*/ +Char +next_command(void) +{ + Char c; + while (1) { + c = get_char(); + switch ((int) c) { + case ' ': + case '\t': + break; + case '\n': + current_lineno++; + break; + case '#': // comment + skip_line(); + break; + default: // EOF or first essential character + return c; + } + } +} + +////////////////////////////////////////////////////////////////////// +/* odd(): + Test whether argument is an odd number. + + n: In-parameter, the integer to be tested. + + Return: True if odd, false otherwise. +*/ +inline bool +odd(const int n) +{ + return (n & 1 == 1) ? true : false; +} + +////////////////////////////////////////////////////////////////////// +/* position_to_end_of_args(): + Move graphical pointer to end of drawn figure. + + This is used by the D commands that draw open geometrical figures. + The algorithm simply sums up all horizontal displacements (arguments + with even number) for the horizontal component. Similarly, the + vertical component is the sum of the odd arguments. + + args: In-parameter, the arguments of a former drawing command. +*/ +void +position_to_end_of_args(const IntArray *args) +{ + int i; + int nr = args->num_stored; + for (i = 0; i < nr; i += 2) + current_env->hpos += args->data[i]; + for (i = 1; i < nr; i += 2) + current_env->vpos += args->data[i]; +} + +////////////////////////////////////////////////////////////////////// +/* remember_filename(): + Set global variable current_filename. + + The actual filename is stored in current_filename. This is used by + the postprocessors, expecting the name "<standard input>" for stdin. + + filename: In-out-parameter; is changed to the new value also. +*/ +void +remember_filename(const char *filename) +{ + char *fname; + if (strcmp(filename, "-") == 0) + fname = "<standard input>"; + else + fname = (char *) filename; + size_t len = strlen(fname) + 1; + if (current_filename != 0) + free((char *)current_filename); + current_filename = (const char *) malloc(len); + if (current_filename == 0) fatal("can't malloc space for filename"); + strncpy((char *)current_filename, (char *)fname, len); +} + +////////////////////////////////////////////////////////////////////// +/* send_draw(): + Call draw method of printer class. + + subcmd: Letter of actual D subcommand. + args: Array of integer arguments of actual D subcommand. +*/ +void +send_draw(const Char subcmd, const IntArray *args) { + pr->draw((int) subcmd, args->data, args->num_stored, + current_env); +} + +////////////////////////////////////////////////////////////////////// +/* skip_line(): + Go to next line within the input queue. + + Skip the rest of the current line, including the newline character. + The global variable current_lineno is adjusted. + No errors are raised. +*/ +void +skip_line(void) +{ + Char c = get_char(); + while (1) { + if (c == '\n') { + current_lineno++; + break; + } + if (c == EOF) + break; + c = get_char(); } - strcpy((char *)current_filename, (char *)filename); } -void do_file(const char *filename) +////////////////////////////////////////////////////////////////////// +/* skip_line_checked (): + Check that the rest of the line has no args left, then skip line. + + Spaces, tabs, and a comment are allowed before newline or EOF. + All other characters raise an error. +*/ +bool +skip_line_checked() { - int npages = 0; - if (filename[0] == '-' && filename[1] == '\0') { - remember_filename(filename); - current_file = stdin; + bool ok = true; + Char c = get_char(); + while (is_space_or_tab(c)) + c = get_char(); + switch((int) c) { + case '#': // comment + skip_line(); + break; + case '\n': + current_lineno++; + break; + case EOF: + break; + default: + ok = false; + skip_line(); + break; + } + return ok; +} + +////////////////////////////////////////////////////////////////////// +/* skip_line_fatal (): + Fatal error if args left, otherwise skip line. + + Spaces, tabs, and a comment are allowed before newline or EOF. + All other characters trigger the error. +*/ +void +skip_line_fatal() +{ + bool ok = skip_line_checked(); + if (!ok) { + current_lineno--; + error("too many arguments"); + current_lineno++; + } +} + +////////////////////////////////////////////////////////////////////// +/* skip_line_warn (): + Skip line, but warn if args are left on actual line. + + Spaces, tabs, and a comment are allowed before newline or EOF. + All other characters raise a warning +*/ +void +skip_line_warn() +{ + bool ok = skip_line_checked(); + if (!ok) { + current_lineno--; + warning("too many arguments on current line"); + current_lineno++; + } +} + +////////////////////////////////////////////////////////////////////// +/* skip_line_x (): + Skip line in `x' or `D' commands. + + Decide whether in case of an additional argument a fatal error is + raised (the documented classical behavior), only a warning is + issued, or the line is just skipped (former groff behavior). + Actually decided for the warning. +*/ +void +skip_line_x() +{ + skip_line_warn(); + // or: skip_line_fatal(); + // or: skip_line(); +} + +////////////////////////////////////////////////////////////////////// +/* unget_char(c): + Restore character c onto input queue. + + Write a character back onto the input stream. + EOF is gracefully handled. + + c: In-parameter; character to be pushed onto the input queue. +*/ +inline void +unget_char(const Char c) +{ + if (c != EOF) { + int ch = (int) c; + if (ungetc(ch, current_file) == EOF) + fatal("could not unget character"); + } +} + + +/********************************************************************** + parser subcommands + **********************************************************************/ + +////////////////////////////////////////////////////////////////////// +/* parse_color_command: + Process the commands m and DF, but not Df. + + col: In-out-parameter; the color object to be set, must have + been initialized before. +*/ +void +parse_color_command(color *col) +{ + ColorArg gray = 0; + ColorArg red = 0, green = 0, blue = 0; + ColorArg cyan = 0, magenta = 0, yellow = 0, black = 0; + Char subcmd = next_arg_begin(); + switch((int) subcmd) { + case 'c': // DFc or mc: CMY + cyan = get_color_arg(); + magenta = get_color_arg(); + yellow = get_color_arg(); + col->set_cmy(cyan, magenta, yellow); + break; + case 'd': // DFd or md: set default color + col->set_default(); + break; + case 'g': // DFg or mg: gray + gray = get_color_arg(); + col->set_gray(gray); + break; + case 'k': // DFk or mk: CMYK + cyan = get_color_arg(); + magenta = get_color_arg(); + yellow = get_color_arg(); + black = get_color_arg(); + col->set_cmyk(cyan, magenta, yellow, black); + break; + case 'r': // DFr or mr: RGB + red = get_color_arg(); + green = get_color_arg(); + blue = get_color_arg(); + col->set_rgb(red, green, blue); + break; + default: + error("illegal color scheme `%1'", (int) subcmd); + break; + } // end of color subcommands +} + +////////////////////////////////////////////////////////////////////// +/* parse_D_command(): + Parse the subcommands of graphical command D. + + This is the part of the do_file() parser that scans the graphical + subcommands. + - Error on lacking or wrong arguments. + - Warning on too many arguments. + - Line is always skipped. +*/ +void +parse_D_command() +{ + Char subcmd = next_arg_begin(); + switch((int) subcmd) { + case '~': // D~: draw B-spline + // actually, this isn't available for some postprocessors + { + IntArray *args = get_D_variable_args(); + send_draw(subcmd, args); + position_to_end_of_args(args); + delete args; + break; + } + case 'a': // Da: draw arc + { + IntArray *args = get_D_fixed_args(4); + send_draw(subcmd, args); + position_to_end_of_args(args); + delete args; + break; + } + case 'c': // Dc: draw circle line + case 'C': // DC: draw solid circle + { + IntArray *args = get_D_fixed_args(1, subcmd == 'C' ? true : false); + send_draw(subcmd, args); + // move to right end + current_env->hpos += args->data[0]; + delete args; + break; + } + case 'e': // De: draw ellipse line + case 'E': // DE: draw solid ellipse + { + IntArray *args = get_D_fixed_args(2); + send_draw(subcmd, args); + // move to right end + current_env->hpos += args->data[0]; + delete args; + break; + } + case 'f': // Df: set fill gray; obsoleted by DFg + { + // convert arg and treat it like DFg + ColorArg gray = color_from_Df_command(get_integer_arg()); + // skip the unused `vertical' component -- \D'...' always emits pairs + (void) get_integer_arg(); + current_env->fill->set_gray(gray); + // no positioning + skip_line_x(); + break; + } + case 'F': // DF: set fill color, several formats + parse_color_command(current_env->fill); + // no positioning (setting-only command) + skip_line_x(); + break; + case 'l': // DFl: draw line + { + IntArray *args = get_D_fixed_args(2); + send_draw(subcmd, args); + position_to_end_of_args(args); + delete args; + break; + } + case 'p': // Dp: draw closed polygon line + case 'P': // DP: draw solid closed polygon + { + IntArray *args = get_D_variable_args(); + send_draw(subcmd, args); +# ifdef STUPID_DRAWING_POSITIONING + // final args positioning + position_to_end_of_args(args); +# endif + delete args; + break; + } + case 't': // Dt: set line thickness + { + IntArray *args = get_D_fixed_args(1, true); + send_draw(subcmd, args); +# ifdef STUPID_DRAWING_POSITIONING + // final args positioning + position_to_end_of_args(args); +# endif + // no positioning? + delete args; + break; + } + default: // ignore unknown D commands, but warn + warning("unknown command `D%1'", (char) subcmd); + skip_line(); + // no positioning + break; + } // end of D subcommands +} + +////////////////////////////////////////////////////////////////////// +/* parse_x_command(): + Parse subcommands of the device control command x. + + This is the part of the do_file() parser that scans the device + controlling commands. + - Error on duplicate prologue commands. + - Error on wrong or lacking arguments. + - Warning on too many arguments. + - Line is always skipped. + + Globals: + - current_env: is set by many subcommands. + - npages: page counting variable + + Return: boolean in the meaning of `stopped' + - true if parsing should be stopped (`x stop'). + - false if parsing should continue. +*/ +bool +parse_x_command(void) +{ + bool stopped = false; + char *subcmd_str = get_string_arg(); + char subcmd = subcmd_str[0]; + switch (subcmd) { + case 'f': // x font: mount font + { + IntArg n = get_integer_arg(); + char *name = get_string_arg(); + pr->load_font(n, name); + delete name; + skip_line_x(); + break; + } + case 'F': // x Filename: set filename for errors + { + char *str_arg = get_string_arg(); + if (str_arg == 0) + warning("empty argument for `x F' command"); + else { + remember_filename(str_arg); + delete str_arg; + } + break; + } + case 'H': // x Height: set character height + current_env->height = get_integer_arg(); + if (current_env->height == current_env->size) + current_env->height = 0; + skip_line_x(); + break; + case 'i': // x init: initialize device + error("duplicate `x init' command"); + skip_line_x(); + break; + case 'p': // x pause: pause device + skip_line_x(); + break; + case 'r': // x res: set resolution + error("duplicate `x res' command"); + skip_line_x(); + break; + case 's': // x stop: stop device + stopped = true; + skip_line_x(); + break; + case 'S': // x Slant: set slant + current_env->slant = get_integer_arg(); + skip_line_x(); + break; + case 't': // x trailer: generate trailer info + skip_line_x(); + break; + case 'T': // x Typesetter: set typesetter + error("duplicate `x T' command"); + skip_line(); + break; + case 'u': // x underline: from .cu + { + char *str_arg = get_string_arg(); + pr->special(str_arg, current_env, 'u'); + delete str_arg; + skip_line_x(); + break; + } + case 'X': // x X: send uninterpretedly to device + { + char *str_arg = get_extended_arg(); // includes line skip + if (npages <= 0) + error("`x X' command illegal before first `p' command"); + else + pr->special(str_arg, current_env); + delete str_arg; + break; + } + default: // ignore unknown x commands, but warn + warning("unknown command `x %1'", subcmd); + skip_line(); } + delete subcmd_str; + return stopped; +} + + +/********************************************************************** + exported part (by driver.h) + **********************************************************************/ + +//////////////////////////////////////////////////////////////////////// +/* do_file(): + Parse and postprocess groff intermediate output. + + filename: "-" for standard input, normal file name otherwise +*/ +void +do_file(const char *filename) +{ + Char command; + EnvStack env_stack = EnvStack(); + bool stopped = false; // terminating condition + + // setup of global variables + npages = 0; + current_lineno = 1; + // `pr' is initialized after the prologue. + // `device' is set by the 1st prologue command. + + if (filename[0] == '-' && filename[1] == '\0') + current_file = stdin; else { errno = 0; current_file = fopen(filename, "r"); - if (current_file == 0) { - error("can't open `%1'", filename); + if (errno != 0 || current_file == 0) { + error("can't open file `%1'", filename); return; } - remember_filename(filename); } - environment env; - env.vpos = -1; - env.hpos = -1; - env.fontno = -1; - env.height = 0; - env.slant = 0; - env.col = new color; - env.col->set_gray(1.0); - env.fill = new color; - env.fill->set_gray(1.0); - environment_list *env_list = 0; - current_lineno = 1; - int command; - char *s; - command = get_char(); - if (command == EOF) - return; - if (command != 'x') - fatal("the first command must be `x T'"); - s = get_string(); - if (s[0] != 'T') - fatal("the first command must be `x T'"); - char *dev = get_string(); - if (pr == 0) { - device = strsave(dev); - if (!font::load_desc()) - fatal("sorry, I can't continue"); + remember_filename(filename); + + if (current_env != 0) { + delete current_env->col; + delete current_env->fill; + delete current_env; } - else { - if (device == 0 || strcmp(device, dev) != 0) - fatal("all files must use the same device"); + current_env = new environment; + current_env->col = new color; + current_env->fill = new color; + current_env->fontno = -1; + current_env->height = 0; + current_env->hpos = -1; + current_env->slant = 0; + current_env->size = 0; + current_env->vpos = -1; + + // parsing of prologue (first 3 commands) + { + char *str_arg; + IntArg int_arg; + + // 1st command `x T' + command = next_command(); + if ((int) command == EOF) + return; + if ((int) command != 'x') + fatal("the first command must be `x T'"); + str_arg = get_string_arg(); + if (str_arg[0] != 'T') + fatal("the first command must be `x T'"); + delete str_arg; + char *tmp_dev = get_string_arg(); + if (pr == 0) { // note: `pr' initialized after prologue + device = tmp_dev; + if (!font::load_desc()) + fatal("couldn't load DESC file, can't continue"); + } + else { + if (device == 0 || strcmp(device, tmp_dev) != 0) + fatal("all files must use the same device"); + delete tmp_dev; + } + skip_line_x(); // ignore further args + current_env->size = 10 * font::sizescale; + + // 2nd command `x res' + command = next_command(); + if ((int) command != 'x') + fatal("the second command must be `x res'"); + str_arg = get_string_arg(); + if (str_arg[0] != 'r') + fatal("the second command must be `x res'"); + delete str_arg; + int_arg = get_integer_arg(); + if (int_arg != font::res) + fatal("resolution does not match"); + int_arg = get_integer_arg(); + if (int_arg != font::hor) + fatal("minimum horizontal motion does not match"); + int_arg = get_integer_arg(); + if (int_arg != font::vert) + fatal("minimum vertical motion does not match"); + skip_line_x(); // ignore further args + + // 3rd command `x init' + command = next_command(); + if (command != 'x') + fatal("the third command must be `x init'"); + str_arg = get_string_arg(); + if (str_arg[0] != 'i') + fatal("the third command must be `x init'"); + delete str_arg; + skip_line_x(); } - skip_line(); - env.size = 10*font::sizescale; - command = get_char(); - if (command != 'x') - fatal("the second command must be `x res'"); - s = get_string(); - if (s[0] != 'r') - fatal("the second command must be `x res'"); - int n = get_integer(); - if (n != font::res) - fatal("resolution does not match"); - n = get_integer(); - if (n != font::hor) - fatal("horizontal resolution does not match"); - n = get_integer(); - if (n != font::vert) - fatal("vertical resolution does not match"); - skip_line(); - command = get_char(); - if (command != 'x') - fatal("the third command must be `x init'"); - s = get_string(); - if (s[0] != 'i') - fatal("the third command must be `x init'"); - skip_line(); + + // parsing of body if (pr == 0) pr = make_printer(); - while ((command = get_char()) != EOF) { - switch (command) { - case 's': - env.size = get_integer(); - if (env.height == env.size) - env.height = 0; - break; - case 'f': - env.fontno = get_integer(); - break; - case 'F': - remember_filename(get_string()); - break; - case 'C': - { - if (npages == 0) - fatal("`C' command illegal before first `p' command"); - char *s = get_string(); - pr->set_special_char(s, &env); - } - break; - case 'N': - { - if (npages == 0) - fatal("`N' command illegal before first `p' command"); - pr->set_numbered_char(get_integer(), &env); - } - break; - case 'H': - env.hpos = get_integer(); + while (!stopped) { + command = next_command(); + if (command == EOF) break; - case 'h': - env.hpos += get_integer(); + // spaces, tabs, comments, and newlines are skipped here + switch ((int) command) { + case '#': // #: comment, ignore up to end of line + skip_line(); break; - case 'V': - env.vpos = get_integer(); + case '{': // {: start a new environment (a copy) + env_stack.push(current_env); break; - case 'v': - env.vpos += get_integer(); + case '}': // }: pop previous env from stack + delete current_env->fill; + delete current_env->col; + delete current_env; + current_env = env_stack.pop(); break; - case '0': + case '0': // ddc: obsolete jump and print command case '1': case '2': case '3': @@ -194,328 +1450,155 @@ void do_file(const char *filename) case '7': case '8': case '9': + { // expect 2 digits and a character + char s[3]; + Char c = next_arg_begin(); + if (npages <= 0) + fatal_command(command); + if (!isdigit((int) c)) { + error("digit expected"); + c = 0; + } + s[0] = (char) command; + s[1] = (char) c; + s[2] = '\0'; + errno = 0; + long int hor_pos = strtol(s, 0, 10); + if (errno != 0) + error("couldn't convert 2 digits"); + current_env->hpos += (IntArg) hor_pos; + c = next_arg_begin(); + if ((int) c == '\n' || (int) c == EOF) + error("character argument expected"); + else + pr->set_ascii_char((unsigned char) c, current_env); + break; + } + case 'c': // c: print ascii char without moving { - int c = get_char(); - if (!csdigit(c)) - fatal("digit expected"); - env.hpos += (command - '0')*10 + (c - '0'); + if (npages <= 0) + fatal_command(command); + Char c = next_arg_begin(); + if (c == '\n' || c == EOF) + error("missing argument to `c' command"); + else + pr->set_ascii_char((unsigned char) c, current_env); + break; } - // fall through - case 'c': + case 'C': // C: print named special character { - if (npages == 0) - fatal("`c' command illegal before first `p' command"); - int c = get_char(); - if (c == EOF) - fatal("missing argument to `c' command"); - pr->set_ascii_char(c, &env); + if (npages <= 0) + fatal_command(command); + char *str_arg = get_string_arg(); + pr->set_special_char(str_arg, current_env); + delete str_arg; + break; } + case 'D': // drawing commands + if (npages <= 0) + fatal_command(command); + parse_D_command(); break; - case 'm': - // glyph color - env.col = new color; - env.col->read_rgb(get_string()); + case 'f': // f: set font to number + current_env->fontno = get_integer_arg(); break; - case 'n': - if (npages == 0) - fatal("`n' command illegal before first `p' command"); - pr->end_of_line(); - (void)get_integer(); - (void)get_integer(); + case 'F': // F: obsolete, replaced by `x F' + { + char *str_arg = get_string_arg(); + remember_filename(str_arg); + delete str_arg; + break; + } + case 'h': // h: relative horizontal move + current_env->hpos += get_integer_arg(); break; - case 'w': - case ' ': + case 'H': // H: absolute horizontal positioning + current_env->hpos = get_integer_arg(); break; - case '\n': - current_lineno++; + case 'm': // m: glyph color + parse_color_command(current_env->col); break; - case 'p': - if (npages) - pr->end_page(env.vpos); - npages++; - pr->begin_page(get_integer()); - env.vpos = 0; + case 'n': // n: print end of line, ignore 2 args + if (npages <= 0) + fatal_command(command); + pr->end_of_line(); + (void) get_integer_arg(); + (void) get_integer_arg(); break; - case '{': - env_list = new environment_list(env, env_list); + case 'N': // N: print char with given int code + if (npages <= 0) + fatal_command(command); + pr->set_numbered_char(get_integer_arg(), current_env); break; - case '}': - if (!env_list) { - fatal("can't pop"); - } - else { - env = env_list->env; - environment_list *tem = env_list; - env_list = env_list->next; - delete tem; - } + case 'p': // p: start new page with given number + if (npages > 0) + pr->end_page(current_env->vpos); + npages++; // increment # of processed pages + pr->begin_page(get_integer_arg()); + current_env->vpos = 0; + break; + case 's': // s: set point size + current_env->size = get_integer_arg(); + if (current_env->height == current_env->size) + current_env->height = 0; break; - case 'u': + case 't': // t: print a text word { - if (npages == 0) - fatal("`u' command illegal before first `p' command"); - int kern = get_integer(); - int c = get_char(); - while (c == ' ') - c = get_char(); - while (c != EOF) { - if (c == '\n') { - current_lineno++; - break; - } + char c; + if (npages <= 0) + fatal_command(command); + char *str_arg = get_string_arg(); + int i = 0; + while ((c = str_arg[i++]) != '\0') { int w; - pr->set_ascii_char(c, &env, &w); - env.hpos += w + kern; - c = get_char(); - if (c == ' ') - break; + pr->set_ascii_char((unsigned char) c, current_env, &w); + current_env->hpos += w; } + delete str_arg; + break; } - break; - case 't': + case 'u': // u: print spaced word { - if (npages == 0) - fatal("`t' command illegal before first `p' command"); - int c; - while ((c = get_char()) != EOF && c != ' ') { - if (c == '\n') { - current_lineno++; - break; - } + char c; + if (npages <= 0) + fatal_command(command); + int kern = get_integer_arg(); + char *str_arg = get_string_arg(); + int i = 0; + while ((c = str_arg[i++]) != '\0') { int w; - pr->set_ascii_char(c, &env, &w); - env.hpos += w; + pr->set_ascii_char((unsigned char) c, current_env, &w); + current_env->hpos += w + kern; } + delete str_arg; + break; } + case 'v': // v: relative vertical move + current_env->vpos += get_integer_arg(); break; - case '#': - skip_line(); + case 'V': // V: absolute vertical positioning + current_env->vpos = get_integer_arg(); break; - case 'D': - { - if (npages == 0) - fatal("`D' command illegal before first `p' command"); - int c; - while ((c = get_char()) == ' ') - ; - int n = 0; - int *p = 0; - int szp = 0; - int np = 0; - - if (c == 'F') { - // fill color - env.fill = new color; - env.fill->read_rgb(get_string()); - } - else { - for (np = 0; possibly_get_integer(&n); np++) { - if (np >= szp) { - if (szp == 0) { - szp = 16; - p = new int[szp]; - } - else { - int *oldp = p; - p = new int[szp*2]; - memcpy(p, oldp, szp*sizeof(int)); - szp *= 2; - a_delete oldp; - } - } - p[np] = n; - } - } - pr->draw(c, p, np, &env); - if (c == 'e') { - if (np > 0) - env.hpos += p[0]; - } - else if (c == 'f' || c == 'F' || c == 't') - ; - else { - int i; - for (i = 0; i < np/2; i++) { - env.hpos += p[i*2]; - env.vpos += p[i*2 + 1]; - } - // there might be an odd number of characters - if (i*2 < np) - env.hpos += p[i*2]; - } - a_delete p; - skip_line(); - } + case 'w': // w: inform about paddable space break; - case 'x': - { - char *s = get_string(); - int suppress_skip = 0; - switch (s[0]) { - case 'i': - error("duplicate `x init' command"); - break; - case 'T': - error("duplicate `x T' command"); - break; - case 'r': - error("duplicate `x res' command"); - break; - case 'p': - break; - case 's': - break; - case 't': - break; - case 'f': - { - int n = get_integer(); - char *name = get_string(); - pr->load_font(n, name); - } - break; - case 'H': - env.height = get_integer(); - if (env.height == env.size) - env.height = 0; - break; - case 'S': - env.slant = get_integer(); - break; - case 'X': - if (npages == 0) - fatal("`x X' command illegal before first `p' command"); - pr->special(get_string(1), &env); - suppress_skip = 1; - break; - case 'u': - // .cu - pr->special(get_string(), &env, 'u'); - break; - default: - error("unrecognised x command `%1'", s); - } - if (!suppress_skip) - skip_line(); - } + case 'x': // device controlling commands + stopped = parse_x_command(); break; default: - error("unrecognised command code %1", int(command)); + warning("unrecognized command `%1'", (unsigned char) command); skip_line(); break; - } - } - if (npages) - pr->end_page(env.vpos); -} - -int get_integer() -{ - int c = get_char(); - while (c == ' ') - c = get_char(); - int neg = 0; - if (c == '-') { - neg = 1; - c = get_char(); - } - if (!csdigit(c)) - fatal("integer expected"); - int total = 0; - do { - total = total*10; - if (neg) - total -= c - '0'; - else - total += c - '0'; - c = get_char(); - } while (csdigit(c)); - if (c != EOF) - ungetc(c, current_file); - return total; -} - -int possibly_get_integer(int *res) -{ - int c = get_char(); - while (c == ' ') - c = get_char(); - int neg = 0; - if (c == '-') { - neg = 1; - c = get_char(); - } - if (!csdigit(c)) { - if (c != EOF) - ungetc(c, current_file); - return 0; - } - int total = 0; - do { - total = total*10; - if (neg) - total -= c - '0'; - else - total += c - '0'; - c = get_char(); - } while (csdigit(c)); - if (c != EOF) - ungetc(c, current_file); - *res = total; - return 1; -} - - -char *get_string(int is_long) -{ - static char *buf; - static int buf_size; - int c = get_char(); - while (c == ' ') - c = get_char(); - for (int i = 0;; i++) { - if (i >= buf_size) { - if (buf_size == 0) { - buf_size = 16; - buf = new char[buf_size]; - } - else { - char *old_buf = buf; - int old_size = buf_size; - buf_size *= 2; - buf = new char[buf_size]; - memcpy(buf, old_buf, old_size); - a_delete old_buf; - } - } - if ((!is_long && (c == ' ' || c == '\n')) || c == EOF) { - buf[i] = '\0'; - break; - } - if (is_long && c == '\n') { - current_lineno++; - c = get_char(); - if (c == '+') - c = '\n'; - else { - buf[i] = '\0'; - break; - } - } - buf[i] = c; - c = get_char(); - } - if (c != EOF) - ungetc(c, current_file); - return buf; -} + } // end of switch + } // end of while -void skip_line() -{ - int c; - while ((c = get_char()) != EOF) - if (c == '\n') { - current_lineno++; - break; - } + // end of file reached + if (npages > 0) + pr->end_page(current_env->vpos); + fclose(current_file); + // If `stopped' is not `true' here then there wasn't any `x stop'. + if (!stopped) + warning("no final `x stop' command"); + delete current_env->col; + delete current_env->fill; + delete current_env; } diff --git a/src/libs/libgroff/color.cc b/src/libs/libgroff/color.cc index 46f80b55..bfd79dcb 100644 --- a/src/libs/libgroff/color.cc +++ b/src/libs/libgroff/color.cc @@ -1,5 +1,5 @@ // -*- C++ -*- -/* Copyright (C) 2001 Free Software Foundation, Inc. +/* Copyright (C) 2001, 2002 Free Software Foundation, Inc. Written by Gaius Mulley <gaius@glam.ac.uk> This file is part of groff. @@ -40,115 +40,100 @@ static inline unsigned int min(unsigned int a, unsigned int b) } color::color() -: cyan(0), magenta(0), yellow(0), black(0), scheme(NONE), - color_space_num(0), encoding(UNDEFINED), hex_length(0) +: scheme(DEFAULT) { } -int color::is_equal(color *c) +int color::operator==(const color & c) const { - return (c->cyan == cyan) - && (c->magenta == magenta) - && (c->yellow == yellow) - && (c->black == black); + if (scheme != c.scheme) + return 0; + switch (scheme) { + case DEFAULT: + break; + case RGB: + if (Red != c.Red || Green != c.Green || Blue != c.Blue) + return 0; + break; + case CMYK: + if (Cyan != c.Cyan || Magenta != c.Magenta + || Yellow != c.Yellow || Black != c.Black) + return 0; + break; + case GRAY: + if (Gray != c.Gray) + return 0; + break; + case CMY: + if (Cyan != c.Cyan || Magenta != c.Magenta || Yellow != c.Yellow) + return 0; + break; + } + return 1; } -int color::is_gray(void) +int color::operator!=(const color & c) const { - return (scheme == GRAY) - || ((magenta == cyan) && (cyan == yellow)); + return !(*this == c); } -void color::set_rgb(unsigned int r, unsigned int g, unsigned int b) +color_scheme color::get_components(unsigned int *c) { - scheme = RGB; - r = min(MAX_COLOR_VAL, r); - g = min(MAX_COLOR_VAL, g); - b = min(MAX_COLOR_VAL, b); - black = min(MAX_COLOR_VAL - r, min(MAX_COLOR_VAL - g, MAX_COLOR_VAL - b)); - if (MAX_COLOR_VAL - black == 0) { - cyan = MAX_COLOR_VAL; - magenta = MAX_COLOR_VAL; - yellow = MAX_COLOR_VAL; - } - else { - cyan = (MAX_COLOR_VAL * (MAX_COLOR_VAL - r - black)) - / (MAX_COLOR_VAL - black); - magenta = (MAX_COLOR_VAL * (MAX_COLOR_VAL - g - black)) - / (MAX_COLOR_VAL - black); - yellow = (MAX_COLOR_VAL * (MAX_COLOR_VAL - b - black)) - / (MAX_COLOR_VAL - black); - } + c[0] = components[0]; + c[1] = components[1]; + c[2] = components[2]; + c[3] = components[3]; + return scheme; } -void color::set_cmy(unsigned int c, unsigned int m, unsigned int y) +void color::set_default() { - scheme = CMY; - c = min(MAX_COLOR_VAL, c); - m = min(MAX_COLOR_VAL, m); - y = min(MAX_COLOR_VAL, y); - black = min(c, min(m, y)); - if (MAX_COLOR_VAL - black == 0) { - cyan = MAX_COLOR_VAL; - magenta = MAX_COLOR_VAL; - yellow = MAX_COLOR_VAL; - } - else { - cyan = (MAX_COLOR_VAL * (c - black)) / (MAX_COLOR_VAL - black); - magenta = (MAX_COLOR_VAL * (m - black)) / (MAX_COLOR_VAL - black); - yellow = (MAX_COLOR_VAL * (y - black)) / (MAX_COLOR_VAL - black); - } + scheme = DEFAULT; } -void color::set_cmyk(unsigned int c, unsigned int m, - unsigned int y, unsigned int k) -{ - scheme = CMYK; - cyan = c; - magenta = m; - yellow = y; - black = k; -} +// (0, 0, 0) is black -void color::set_gray(unsigned int b) +void color::set_rgb(unsigned int r, unsigned int g, unsigned int b) { - scheme = GRAY; - cyan = 0; - magenta = 0; - yellow = 0; - black = min(MAX_COLOR_VAL, b); + scheme = RGB; + Red = min(MAX_COLOR_VAL, r); + Green = min(MAX_COLOR_VAL, g); + Blue = min(MAX_COLOR_VAL, b); } -void color::set_rgb(double r, double g, double b) -{ - set_rgb((unsigned int)(r * MAX_COLOR_VAL), - (unsigned int)(g * MAX_COLOR_VAL), - (unsigned int)(b * MAX_COLOR_VAL)); -} +// (0, 0, 0) is white -void color::set_cmy(double c, double m, double y) +void color::set_cmy(unsigned int c, unsigned int m, unsigned int y) { - set_cmy((unsigned int)(c * MAX_COLOR_VAL), - (unsigned int)(m * MAX_COLOR_VAL), - (unsigned int)(y * MAX_COLOR_VAL)); + scheme = CMY; + Cyan = min(MAX_COLOR_VAL, c); + Magenta = min(MAX_COLOR_VAL, m); + Yellow = min(MAX_COLOR_VAL, y); } -void color::set_cmyk(double c, double m, double y, double k) +// (0, 0, 0, 0) is white + +void color::set_cmyk(unsigned int c, unsigned int m, + unsigned int y, unsigned int k) { - set_cmyk((unsigned int)(c * MAX_COLOR_VAL), - (unsigned int)(m * MAX_COLOR_VAL), - (unsigned int)(y * MAX_COLOR_VAL), - (unsigned int)(k * MAX_COLOR_VAL)); + scheme = CMYK; + Cyan = min(MAX_COLOR_VAL, c); + Magenta = min(MAX_COLOR_VAL, m); + Yellow = min(MAX_COLOR_VAL, y); + Black = min(MAX_COLOR_VAL, k); } -void color::set_gray(double l) +// (0) is black + +void color::set_gray(unsigned int g) { - set_gray((unsigned int)(l * MAX_COLOR_VAL)); + scheme = GRAY; + Gray = min(MAX_COLOR_VAL, g); } /* - * atoh - computes the value of the hex number S in N. Returns 1 if - * successful. + * atoh - computes the value of the hex number S in N. LENGTH characters + * are read. Returns 1 if successful. */ static int atoh(unsigned int *n, const char *s, unsigned int length) @@ -156,8 +141,6 @@ static int atoh(unsigned int *n, const char *s, unsigned int length) unsigned int i = 0; unsigned int val = 0; while ((i < length) && csxdigit(s[i])) { - if (!s[i]) - return 0; if (csdigit(s[i])) val = val*0x10 + (s[i]-'0'); else if (csupper(s[i])) @@ -166,191 +149,192 @@ static int atoh(unsigned int *n, const char *s, unsigned int length) val = val*0x10 + (s[i]-'a') + 10; i++; } + if (i != length) + return 0; *n = val; return 1; } /* - * read_encoding - read the next component of the color encoding from S. - * Returns 1 when finished. + * read_encoding - reads a hexadecimal color string in S, using color + * scheme CS with N components. Returns 1 if successful. */ -int color::read_encoding(const char *s, unsigned int n) +int color::read_encoding(color_scheme cs, const char *s, unsigned int n) { - if ((scheme == NONE) && (color_space_num == 0)) { - if (*s == '#') { - s++; - encoding = HEX; - if (*s == '#') { - hex_length = 4; - s++; - } - else - hex_length = 2; - } - else - encoding = REAL; + int hex_length = 2; + scheme = cs; + s++; + if (*s == '#') { + hex_length = 4; + s++; } - if (encoding == REAL) { - d[color_space_num] = atof(s); - color_space_num++; - return (color_space_num == n); - } - else { - for (unsigned int i = 0; i < n; i++) { - if (!atoh(&c[i], s, hex_length)) - return 0; - if (hex_length == 2) - c[i] *= 0x101; // scale up -- 0xff should become 0xffff - s += hex_length; - } - return 1; + for (unsigned int i = 0; i < n; i++) { + if (!atoh(&components[i], s, hex_length)) + return 0; + if (hex_length == 2) + components[i] *= 0x101; // scale up -- 0xff should become 0xffff + s += hex_length; } + return 1; } -/* - * read_rgb - read an rgb color description from S. It returns 1 - * when the complete color has been read. - */ - int color::read_rgb(const char *s) { - assert(scheme == NONE); - int finished = read_encoding(s, 3); - if (finished) { - if (encoding == HEX) - set_rgb(c[0], c[1], c[2]); - else - set_rgb(d[0], d[1], d[2]); - } - return finished; + return read_encoding(RGB, s, 3); } -/* - * read_cmy - read a cmy color description from S. It returns 1 - * when the complete color has been read. - */ - int color::read_cmy(const char *s) { - assert(scheme == NONE); - int finished = read_encoding(s, 3); - if (finished) { - if (encoding == HEX) - set_cmy(c[0], c[1], c[2]); - else - set_cmy(d[0], d[1], d[2]); - } - return finished; + return read_encoding(CMY, s, 3); } -/* - * read_cmyk - read a cmyk color description from S. It returns 1 - * when the complete color has been read. - */ - int color::read_cmyk(const char *s) { - assert(scheme == NONE); - int finished = read_encoding(s, 4); - if (finished) { - if (encoding == HEX) - set_cmyk(c[0], c[1], c[2], c[3]); - else - set_cmyk(d[0], d[1], d[2], d[3]); - } - return finished; + return read_encoding(CMYK, s, 4); } -/* - * read_gray - read a gray scale from S. It returns 1. - */ - int color::read_gray(const char *s) { - assert(scheme == NONE); - int finished = read_encoding(s, 1); - if (finished) { - if (encoding == HEX) - set_gray(c[0]); - else - set_gray(d[0]); - } - return finished; + return read_encoding(GRAY, s, 1); } void color::get_rgb(unsigned int *r, unsigned int *g, unsigned int *b) { - assert(scheme != NONE); - *r = MAX_COLOR_VAL - - min(MAX_COLOR_VAL, - cyan * (MAX_COLOR_VAL - black) / MAX_COLOR_VAL + black); - *g = MAX_COLOR_VAL - - min(MAX_COLOR_VAL, - magenta * (MAX_COLOR_VAL - black) / MAX_COLOR_VAL + black); - *b = MAX_COLOR_VAL - - min(MAX_COLOR_VAL, - yellow * (MAX_COLOR_VAL - black) / MAX_COLOR_VAL + black); + switch (scheme) { + case RGB: + *r = Red; + *g = Green; + *b = Blue; + break; + case CMY: + *r = MAX_COLOR_VAL - Cyan; + *g = MAX_COLOR_VAL - Magenta; + *b = MAX_COLOR_VAL - Yellow; + break; + case CMYK: + *r = MAX_COLOR_VAL + - min(MAX_COLOR_VAL, + Cyan * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black); + *g = MAX_COLOR_VAL + - min(MAX_COLOR_VAL, + Magenta * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black); + *b = MAX_COLOR_VAL + - min(MAX_COLOR_VAL, + Yellow * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black); + break; + case GRAY: + *r = *g = *b = Gray; + break; + default: + assert(0); + break; + } } void color::get_cmy(unsigned int *c, unsigned int *m, unsigned int *y) { - assert(scheme != NONE); - *c = min(MAX_COLOR_VAL, - cyan * (MAX_COLOR_VAL - black) / MAX_COLOR_VAL + black); - *m = min(MAX_COLOR_VAL, - magenta * (MAX_COLOR_VAL - black) / MAX_COLOR_VAL + black); - *y = min(MAX_COLOR_VAL, - yellow * (MAX_COLOR_VAL - black) / MAX_COLOR_VAL + black); + switch (scheme) { + case RGB: + *c = MAX_COLOR_VAL - Red; + *m = MAX_COLOR_VAL - Green; + *y = MAX_COLOR_VAL - Blue; + break; + case CMY: + *c = Cyan; + *m = Magenta; + *y = Yellow; + break; + case CMYK: + *c = min(MAX_COLOR_VAL, + Cyan * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black); + *m = min(MAX_COLOR_VAL, + Magenta * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black); + *y = min(MAX_COLOR_VAL, + Yellow * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black); + break; + case GRAY: + *c = *m = *y = MAX_COLOR_VAL - Gray; + break; + default: + assert(0); + break; + } } void color::get_cmyk(unsigned int *c, unsigned int *m, unsigned int *y, unsigned int *k) { - assert(scheme != NONE); - *c = cyan; - *m = magenta; - *y = yellow; - *k = black; -} - -void color::get_gray(unsigned int *l) -{ - assert(scheme != NONE); - *l = black; -} - -void color::get_rgb(double *r, double *g, double *b) -{ - assert(scheme != NONE); - unsigned int ir, ig, ib; - get_rgb(&ir, &ig, &ib); - *r = (double)ir / MAX_COLOR_VAL; - *g = (double)ig / MAX_COLOR_VAL; - *b = (double)ib / MAX_COLOR_VAL; + switch (scheme) { + case RGB: + *k = min(MAX_COLOR_VAL - Red, + min(MAX_COLOR_VAL - Green, MAX_COLOR_VAL - Blue)); + if (MAX_COLOR_VAL == *k) { + *c = MAX_COLOR_VAL; + *m = MAX_COLOR_VAL; + *y = MAX_COLOR_VAL; + } + else { + *c = (MAX_COLOR_VAL * (MAX_COLOR_VAL - Red - *k)) + / (MAX_COLOR_VAL - *k); + *m = (MAX_COLOR_VAL * (MAX_COLOR_VAL - Green - *k)) + / (MAX_COLOR_VAL - *k); + *y = (MAX_COLOR_VAL * (MAX_COLOR_VAL - Blue - *k)) + / (MAX_COLOR_VAL - *k); + } + break; + case CMY: + *k = min(Cyan, min(Magenta, Yellow)); + if (MAX_COLOR_VAL == *k) { + *c = MAX_COLOR_VAL; + *m = MAX_COLOR_VAL; + *y = MAX_COLOR_VAL; + } + else { + *c = (MAX_COLOR_VAL * (Cyan - *k)) / (MAX_COLOR_VAL - *k); + *m = (MAX_COLOR_VAL * (Magenta - *k)) / (MAX_COLOR_VAL - *k); + *y = (MAX_COLOR_VAL * (Yellow - *k)) / (MAX_COLOR_VAL - *k); + } + break; + case CMYK: + *c = Cyan; + *m = Magenta; + *y = Yellow; + *k = Black; + break; + case GRAY: + *c = *m = *y = 0; + *k = MAX_COLOR_VAL - Gray; + break; + default: + assert(0); + break; + } } -void color::get_cmy(double *c, double *m, double *y) -{ - assert(scheme != NONE); - unsigned int ic, im, iy; - get_cmy(&ic, &im, &iy); - *c = (double)ic / MAX_COLOR_VAL; - *m = (double)im / MAX_COLOR_VAL; - *y = (double)iy / MAX_COLOR_VAL; -} +// we use `0.222r + 0.707g + 0.071b' (this is the ITU standard) +// as an approximation for gray -void color::get_cmyk(double *c, double *m, double *y, double *k) +void color::get_gray(unsigned int *g) { - assert(scheme != NONE); - *c = (double)cyan / MAX_COLOR_VAL; - *m = (double)magenta / MAX_COLOR_VAL; - *y = (double)yellow / MAX_COLOR_VAL; - *k = (double)black / MAX_COLOR_VAL; + switch (scheme) { + case RGB: + *g = (222*Red + 707*Green + 71*Blue) / 1000; + break; + case CMY: + *g = MAX_COLOR_VAL - (222*Cyan + 707*Magenta + 71*Yellow) / 1000; + break; + case CMYK: + *g = (MAX_COLOR_VAL - (222*Cyan + 707*Magenta + 71*Yellow) / 1000) + * (MAX_COLOR_VAL - Black); + break; + case GRAY: + *g = Gray; + break; + default: + assert(0); + break; + } } -void color::get_gray(double *l) -{ - assert(scheme != NONE); - *l = (double)black / MAX_COLOR_VAL; -} +color default_color; diff --git a/src/libs/libgroff/geometry.cc b/src/libs/libgroff/geometry.cc index 7aa0d2b8..58a94a4a 100644 --- a/src/libs/libgroff/geometry.cc +++ b/src/libs/libgroff/geometry.cc @@ -1,5 +1,5 @@ // -*- C++ -*- -/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001 +/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002 Free Software Foundation, Inc. Written by Gaius Mulley <gaius@glam.ac.uk> using adjust_arc_center() from printer.cc, written by James Clark. @@ -136,14 +136,22 @@ void check_output_arc_limits(int x1, int y1, *miny = y1 + yv1 - radius; *maxy = y1 + yv1 + radius; - // now see which min/max can be reduced and increased for the limits of - // the arc - // - // Q2 | Q1 - // -----+----- - // Q3 | Q4 - // - if (xv1 >= 0 && yv1 >= 0) { + /* now to see which min/max can be reduced and increased for the limits of + * the arc + * + * Q2 | Q1 + * -----+----- + * Q3 | Q4 + * + * + * NB. (x1+xv1, y1+yv1) is at the origin + * + * below we ask a nested question + * (i) from which quadrant does the first vector start? + * (ii) into which quadrant does the second vector go? + * from the 16 possible answers we determine the limits of the arc + */ + if (xv1 > 0 && yv1 > 0) { // first vector in Q3 if (xv2 >= 0 && yv2 >= 0 ) { // second in Q1 @@ -172,7 +180,7 @@ void check_output_arc_limits(int x1, int y1, } } } - else if (xv1 >= 0 && yv1 < 0) { + else if (xv1 > 0 && yv1 < 0) { // first vector in Q2 if (xv2 >= 0 && yv2 >= 0) { // second in Q1 @@ -202,7 +210,7 @@ void check_output_arc_limits(int x1, int y1, *minx = MIN(x1, x2); } } - else if (xv1 < 0 && yv1 < 0) { + else if (xv1 <= 0 && yv1 <= 0) { // first vector in Q1 if (xv2 >= 0 && yv2 >= 0) { // second in Q1 @@ -232,7 +240,7 @@ void check_output_arc_limits(int x1, int y1, *maxy = y1; } } - else if (xv1 < 0 && yv1 >= 0) { + else if (xv1 <= 0 && yv1 > 0) { // first vector in Q4 if (xv2 >= 0 && yv2 >= 0) { // second in Q1 diff --git a/src/libs/libgroff/itoa.c b/src/libs/libgroff/itoa.c index 72826b74..245c7dfe 100644 --- a/src/libs/libgroff/itoa.c +++ b/src/libs/libgroff/itoa.c @@ -1,4 +1,5 @@ -/* Copyright (C) 1989, 1990, 1991, 1992, 2000 Free Software Foundation, Inc. +/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2002 + Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) This file is part of groff. @@ -18,6 +19,7 @@ with groff; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define INT_DIGITS 19 /* enough for 64 bit integer */ +#define UINT_DIGITS 20 char *i_to_a(i) int i; @@ -41,3 +43,16 @@ char *i_to_a(i) } return p; } + +char *ui_to_a(i) + unsigned int i; +{ + /* Room for UINT_DIGITS digits and '\0' */ + static char buf[UINT_DIGITS + 1]; + char *p = buf + UINT_DIGITS; /* points to terminating '\0' */ + do { + *--p = '0' + (i % 10); + i /= 10; + } while (i != 0); + return p; +} diff --git a/src/preproc/pic/object.cc b/src/preproc/pic/object.cc index 38b0963b..4eae9564 100644 --- a/src/preproc/pic/object.cc +++ b/src/preproc/pic/object.cc @@ -211,7 +211,8 @@ struct arrow_head_type { }; void draw_arrow(const position &pos, const distance &dir, - const arrow_head_type &aht, const line_type <) + const arrow_head_type &aht, const line_type <, + char *outline_color_for_fill) { double hyp = hypot(dir); if (hyp == 0.0) { @@ -229,8 +230,9 @@ void draw_arrow(const position &pos, const distance &dir, v[0] = pos; v[1] = pos + base + n; v[2] = pos + base - n; - // A value > 1 means fill with the current color. - out->polygon(v, 3, slt, 2.0); + // fill with outline color + out->set_color(outline_color_for_fill, outline_color_for_fill); + out->polygon(v, 3, slt, 1); } else { position v[2]; @@ -1253,9 +1255,10 @@ void line_object::print() out->set_color(0, graphic_object::get_outline_color()); out->line(strt, v, n, lt); if (arrow_at_start) - draw_arrow(strt, strt-v[0], aht, lt); + draw_arrow(strt, strt-v[0], aht, lt, graphic_object::get_outline_color()); if (arrow_at_end) - draw_arrow(en, v[n-1] - (n > 1 ? v[n - 2] : strt), aht, lt); + draw_arrow(en, v[n-1] - (n > 1 ? v[n - 2] : strt), aht, lt, + graphic_object::get_outline_color()); out->reset_color(); } @@ -1322,9 +1325,10 @@ void spline_object::print() out->set_color(0, graphic_object::get_outline_color()); out->spline(strt, v, n, lt); if (arrow_at_start) - draw_arrow(strt, strt-v[0], aht, lt); + draw_arrow(strt, strt-v[0], aht, lt, graphic_object::get_outline_color()); if (arrow_at_end) - draw_arrow(en, v[n-1] - (n > 1 ? v[n - 2] : strt), aht, lt); + draw_arrow(en, v[n-1] - (n > 1 ? v[n - 2] : strt), aht, lt, + graphic_object::get_outline_color()); out->reset_color(); } @@ -1541,13 +1545,13 @@ void arc_object::print() position c = cent - strt; draw_arrow(strt, (clockwise ? position(c.y, -c.x) : position(-c.y, c.x)), - aht, lt); + aht, lt, graphic_object::get_outline_color()); } if (arrow_at_end) { position e = en - cent; draw_arrow(en, (clockwise ? position(e.y, -e.x) : position(-e.y, e.x)), - aht, lt); + aht, lt, graphic_object::get_outline_color()); } out->reset_color(); } diff --git a/src/preproc/pic/pic.man b/src/preproc/pic/pic.man index e7ca94dd..1804e660 100644 --- a/src/preproc/pic/pic.man +++ b/src/preproc/pic/pic.man @@ -1,5 +1,5 @@ .ig -Copyright (C) 1989-2000, 2001 Free Software Foundation, Inc. +Copyright (C) 1989-2000, 2001, 2002 Free Software Foundation, Inc. Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice @@ -693,8 +693,8 @@ All three keywords expect a suffix specifying the color, for example Currently, color support isn't available in \*(tx mode. Predefined color names for .B groff -are in the file -.BR color.tmac ; +are in the device macro files, for example +.BR ps.tmac ; additional colors can be defined with the .B .defcolor request (see the manual page of @@ -703,10 +703,9 @@ for more details). .LP Arrow heads will be drawn as solid triangles if the variable .B arrowhead -is non-zero and either \*(tx mode is enabled or -the -.B \-x -option has been given. +is non-zero and either \*(tx mode is enabled or the +.B \-n +option has not been given. Initially .B arrowhead has a value of 1. diff --git a/src/roff/troff/env.cc b/src/roff/troff/env.cc index 1bd12cb6..69cfa205 100644 --- a/src/roff/troff/env.cc +++ b/src/roff/troff/env.cc @@ -1,5 +1,5 @@ // -*- C++ -*- -/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001 +/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002 Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) @@ -636,10 +636,10 @@ environment::environment(symbol nm) #endif /* WIDOW_CONTROL */ ignore_next_eol(0), emitted_node(0), - cur_glyph_color(0), - prev_glyph_color(0), - cur_fill_color(0), - prev_fill_color(0), + cur_glyph_color(&default_color), + prev_glyph_color(&default_color), + cur_fill_color(&default_color), + prev_fill_color(&default_color), name(nm), control_char('.'), no_break_control_char('\''), diff --git a/src/roff/troff/input.cc b/src/roff/troff/input.cc index c2e7f6e2..0758a02f 100644 --- a/src/roff/troff/input.cc +++ b/src/roff/troff/input.cc @@ -1,5 +1,5 @@ // -*- C++ -*- -/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001 +/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002 Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) @@ -1014,54 +1014,33 @@ static node *do_suppress(symbol nm); static void do_register(); dictionary color_dictionary(501); +static symbol default_symbol("default"); static color *lookup_color(symbol nm) { assert(!nm.is_null()); + if (nm == default_symbol) + return &default_color; color *c = (color *)color_dictionary.lookup(nm); if (c == 0) warning(WARN_COLOR, "`%1' not defined", nm.contents()); return c; } -static color *default_black(color *c) -{ - symbol b = symbol("black"); - if (c == 0) { - color *black = (color *)color_dictionary.lookup(b); - if (black == 0) { - warning(WARN_COLOR, "default color `black' not defined"); - color *tem = new color; - tem->set_cmyk((unsigned int)0, (unsigned int)0, - (unsigned int)0, (unsigned int)0); - (void)color_dictionary.lookup(b, tem); - return tem; - } - else - return black; - } - else - return c; -} - static node *do_glyph_color(symbol nm) { if (nm.is_null()) return 0; if (nm == symbol("P")) - curenv->set_glyph_color(default_black(curenv->get_prev_glyph_color())); + curenv->set_glyph_color(curenv->get_prev_glyph_color()); else { color *tem = lookup_color(nm); if (tem) curenv->set_glyph_color(tem); - else { - tem = new color; - tem->set_cmyk((unsigned int)0, (unsigned int)0, - (unsigned int)0, (unsigned int)0); - (void)color_dictionary.lookup(nm, tem); - } + else + (void)color_dictionary.lookup(nm, new color); } - return new glyph_color_node(default_black(curenv->get_glyph_color())); + return new glyph_color_node(curenv->get_glyph_color()); } static node *do_fill_color(symbol nm) @@ -1069,35 +1048,31 @@ static node *do_fill_color(symbol nm) if (nm.is_null()) return 0; if (nm == symbol("P")) - curenv->set_fill_color(default_black(curenv->get_prev_fill_color())); + curenv->set_fill_color(curenv->get_prev_fill_color()); else { color *tem = lookup_color(nm); if (tem) curenv->set_fill_color(tem); - else { - tem = new color; - tem->set_cmyk((unsigned int)0, (unsigned int)0, - (unsigned int)0, (unsigned int)0); - (void)color_dictionary.lookup(nm, tem); - } + else + (void)color_dictionary.lookup(nm, new color); } - return new fill_color_node(default_black(curenv->get_fill_color())); + return new fill_color_node(curenv->get_fill_color()); } -static unsigned int get_color_element(const char *scheme, const char *color) +static unsigned int get_color_element(const char *scheme, const char *col) { units val; if (!get_number(&val, 'f')) { - warning(WARN_COLOR, "%1 in %2 definition set to 0", color, scheme); + warning(WARN_COLOR, "%1 in %2 definition set to 0", col, scheme); tok.next(); return 0; } if (val < 0) { - warning(WARN_RANGE, "%1 cannot be negative: set to 0", color); + warning(WARN_RANGE, "%1 cannot be negative: set to 0", col); return 0; } if (val > color::MAX_COLOR_VAL+1) { - warning(WARN_RANGE, "%1 cannot be greater than 1", color); + warning(WARN_RANGE, "%1 cannot be greater than 1", col); // we change 0x10000 to 0xffff return color::MAX_COLOR_VAL; } @@ -1218,6 +1193,11 @@ static void define_color() skip_line(); return; } + if (color_name == default_symbol) { + warning(WARN_COLOR, "default color can't be redefined"); + skip_line(); + return; + } symbol style = get_long_name(1); if (style.is_null()) { skip_line(); @@ -1236,8 +1216,8 @@ static void define_color() col = read_cmy(); else { warning(WARN_COLOR, - "unknown color space definition `%1', " - "use rgb, cmyk or gray", style.contents()); + "unknown color space `%1'; use rgb, cmyk, gray or cmy", + style.contents()); skip_line(); return; } @@ -2505,7 +2485,7 @@ void process_input_stack() else interpolate_macro(nm); suppress_next = 1; - have_input = 0; + have_input = 0; } else { if (possibly_handle_first_page_transition()) @@ -2582,7 +2562,7 @@ void process_input_stack() break; } suppress_next = 1; - have_input = 0; + have_input = 0; break; } case token::TOKEN_SPACE: @@ -4846,7 +4826,8 @@ int do_if_request() skip_alternative(); return 0; } - result = (color_dictionary.lookup(nm) != 0); + result = (nm == default_symbol + || color_dictionary.lookup(nm) != 0); } else if (c == 'c') { tok.next(); diff --git a/src/roff/troff/node.cc b/src/roff/troff/node.cc index 14dce56a..a0a48e09 100644 --- a/src/roff/troff/node.cc +++ b/src/roff/troff/node.cc @@ -1,5 +1,5 @@ // -*- C++ -*- -/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001 +/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002 Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) @@ -753,8 +753,8 @@ class troff_output_file : public real_output_file { void put(char c); void put(unsigned char c); void put(int i); + void put(unsigned int i); void put(const char *s); - void put_hex(int i, int length); void set_font(tfont *tf); void flush_tbuf(); public: @@ -813,21 +813,9 @@ inline void troff_output_file::put(int i) put_string(i_to_a(i), fp); } -inline void troff_output_file::put_hex(int i, int length) +inline void troff_output_file::put(unsigned int i) { - char *a = new char[length+1]; - a[length] = '\0'; - while (length > 0) { - length--; - int j = i % 0x10; - if (j <= 9) - a[length] = '0' + j; - else - a[length] = 'a' + (j - 10); - i /= 0x10; - } - put_string(a, fp); - a_delete a; + put_string(ui_to_a(i), fp); } void troff_output_file::start_special(tfont *tf, int no_init_string) @@ -1148,30 +1136,96 @@ void troff_output_file::set_font(tfont *tf) void troff_output_file::fill_color(color *col) { - unsigned int r, g, b; if ((current_pagecolor == col) || disable_color_flag) return; - col->get_rgb(&r, &g, &b); flush_tbuf(); - put("DF ##"); - put_hex(r, 4); - put_hex(g, 4); - put_hex(b, 4); + put("DF"); + unsigned int components[4]; + color_scheme cs; + cs = col->get_components(components); + switch (cs) { + case DEFAULT: + put('d'); + break; + case RGB: + put("r "); + put(Red); + put(' '); + put(Green); + put(' '); + put(Blue); + break; + case CMY: + put("c "); + put(Cyan); + put(' '); + put(Magenta); + put(' '); + put(Yellow); + break; + case CMYK: + put("k "); + put(Cyan); + put(' '); + put(Magenta); + put(' '); + put(Yellow); + put(' '); + put(Black); + break; + case GRAY: + put("g "); + put(Gray); + break; + } put('\n'); current_pagecolor = col; } void troff_output_file::glyph_color(color *col) { - unsigned int r, g, b; if ((current_glyphcolor == col) || disable_color_flag) return; - col->get_rgb(&r, &g, &b); - flush_tbuf(); - put("m ##"); - put_hex(r, 4); - put_hex(g, 4); - put_hex(b, 4); + flush_tbuf(); + put("m"); + unsigned int components[4]; + color_scheme cs; + cs = col->get_components(components); + switch (cs) { + case DEFAULT: + put('d'); + break; + case RGB: + put("r "); + put(Red); + put(' '); + put(Green); + put(' '); + put(Blue); + break; + case CMY: + put("c "); + put(Cyan); + put(' '); + put(Magenta); + put(' '); + put(Yellow); + break; + case CMYK: + put("k "); + put(Cyan); + put(' '); + put(Magenta); + put(' '); + put(Yellow); + put(' '); + put(Black); + break; + case GRAY: + put("g "); + put(Gray); + break; + } put('\n'); current_glyphcolor = col; } diff --git a/tmac/andoc.tmac b/tmac/andoc.tmac index f6a16db6..bfb869ab 100644 --- a/tmac/andoc.tmac +++ b/tmac/andoc.tmac @@ -12,3 +12,8 @@ .do mso an-old.tmac \\*(TH\\ .. +.\" dummy equation macros -- eqnrc is read before .TH or .Dd is parsed. +.de EQ +.. +.de EN +.. diff --git a/tmac/groff_www.man b/tmac/groff_www.man index 5ce157de..f531b981 100644 --- a/tmac/groff_www.man +++ b/tmac/groff_www.man @@ -1,4 +1,4 @@ -.TH GROFF_MWWW @MAN7EXT@ "@MDATE@" "Groff Version @VERSION@" +.TH GROFF_WWW @MAN7EXT@ "@MDATE@" "Groff Version @VERSION@" .\" Copyright (C) 2000, 2001 Free Software Foundation, Inc. .\" Written by Gaius Mulley (gaius@glam.ac.uk) .\" |