diff options
author | Werner LEMBERG <wl@gnu.org> | 2001-01-17 14:17:26 +0000 |
---|---|---|
committer | Werner LEMBERG <wl@gnu.org> | 2001-01-17 14:17:26 +0000 |
commit | 037ff7dfcffa192298911d26e422ba694cb17be4 (patch) | |
tree | 4565ad40fb36d2457ddb9bb9035b488b1f885156 | |
parent | a785ca6c278dfb6b1cf2ca99cd8f50b24b3bca25 (diff) | |
download | groff-git-037ff7dfcffa192298911d26e422ba694cb17be4.tar.gz |
First cut of the new html device driver. Changes to pre-html and
the new grohtml are too numerous to be documented here.
Stuff related to `html' has been renamed to `html-old' and `html2'
stuff has been renamed to `html' (including directories). The new
html device driver is therefore invoked as `-Thtml'.
Added new `O' escape to suppress output (needed by html driver).
Added functions and code to pass info about input-level commands
(`.in', `.fl', etc.) to html driver.
Three new functions (.html-begin, .html-end, and .html-image) for
better html handling: `html-begin' will execute the remaining line
if at the outermost nesting level, increasing an internal counter.
`html-end' does the same but decreases the internal counter.
`html_image' puts its arguments into a special node (suppress_node)
to define an image region.
The `output' request has been removed.
* tmac/html-tags.tmac: Removed.
* tmac/arkup.tmac: Updated and renamed to ...
* tmac/www.tmac: New file.
* tmac/markup.tmac Updated and renamed to ...
* tmac/mwww.tmac: New file.
* tmac/Makefile.sub: Updated.
* tmac/an-old.tmac: Updated.
* tmac/eqnrc: Updated.
* tmac/groff_man.man
* tmac/groff_markup.man: Updated and renamed to ...
* tmac/groff_mwww.man: New file.
* tmac/groff_tmac.man: Updated.
* tmac/html-old.tmac: Updated and Renamed from html.tmac.
* tmac/html.tmac: Updated and renamed from html2.tmac.
* tmac/pspic.tmac: Updated html support.
* tmac/s.tmac: Added html output support.
* tmac/troffrc, tmac/troffrc-end: Updated.
* Makefile.in, doc/Makefile: Updated.
* doc/groff.texinfo: Added info about new `O' escape.
* doc/homepage.ms: Use `MAILTO' macro.
* font/devhtml/DESC.proto: Add `C' font.
* font/devhtml/Makefile.sub: Updated.
* font/devhtml/R.proto: Minor fixes.
* font/devhtml-old/Makefile.sub: Updated.
* src/devices/grohtml-old/Makefile.sub: Updated.
* src/libs/libdriver/printer.cc (printer::get_font_from_index): New
method.
* src/libs/libgroff/htmlindicate.cc (html_begin_suppress,
graphic_start): Add `inline' parameter. Update.
(html_end_suppress, graphic_end): Update.
* src/include/html-strings.h: New file.
* src/include/htmlindicate.h: Comments updated.
* src/include/printer.h: Updated.
* src/preproc/eqn/main.cc (do_file, main): Updated.
* src/preproc/pic/troff.cc (troff_output::start_picture,
troff_output::finish_picture): Updated.
* src/preproc/tbl/main.cc (process_input_file): Updated.
* src/roff/groff/groff.cc (main): Updated.
Pass device arguments to predrivers also.
Use `ps' device for `eqn' preprocessor if `-Thtml' is given.
* src/roff/troff/env.h (environment): Updated.
New elements `need_eol' and `ignore_next_eol' (for html output).
* src/roff/troff/env.cc (environment::environment): Add initializers
for `need_eol' and `ignore_next_eol'.
(environment::add_html_tag_eol, environment::add_html_tag_tabs): New
functions.
(point_size, fill, no_fill, center, right_justify, line_length,
indent, temporary_indent, break_request, handle_tab): Use
`add_html_tag()'.
(set_tabs): Use `add_html_tag_tabs()'.
(environment::add_html_tag): Updated.
(environment::do_break): Updated.
* src/roff/troff/div.cc (space_request, flush_output): Use
`environment::add_html_tag()'.
* src/roff/troff/input.cc: Updated.
New variable `html_level' to indicate nested `html-begin' requests.
(file_iterator::fill): Use `environment::add_html_tag_eol()'.
(non_interpreted_char_node, token_node, non_interpreted_node): Add
`force_tprint()' method.
(token::next): Handle `O'.
(do_suppress): Implement it.
(html_begin, html_end, html_image): New functions.
(init_output_requests): Renamed to ...
(init_html_requests): this.
(main): Use it.
(macro::append_str, macro::append_unsigned, macro::append_int): New
methods.
New variable `output_low_mark_miny' to limit minimal value of y.
(reset_output_registers): Use it.
(output_request): Removed.
(get_output_registers): New function.
* src/roff/troff/node.h (node): Make `force_tprint()' virtual.
(*_node): Added `force_tprint()' if necessary.
(special_node): New elements `tf' and `get_tfont()'.
(suppress_node): New class.
* src/roff/troff/node.cc:
New global variables `image_no' and `suppress_start_page'.
(real_output_file): New method `is_on()'.
(troff_output_file): New method `start_special(tfont)'.
(troff_output_file::really_print_line): Use `tprint' conditionally.
(real_output_file::print_line): Updated.
(real_output_file::on): Updated.
(*_node): Added `force_tprint()'.
(special_node::special_node): Initializer updated.
(special_node::same, special_node::copy, special_node::tprint_start):
Updated.
(get_reg_int, get_reg_str): New functions.
(suppress_node::*): New methods.
New global variables last_position, last_image_filename;
(min): New inline function.
* src/roff/troff/reg.h, src/roff/troff/request.h,
src/roff/troff/troff.h: Updated.
64 files changed, 5022 insertions, 10216 deletions
@@ -1,3 +1,126 @@ +2000-01-15 Gaius Mulley <gaius@glam.ac.uk> + + First cut of the new html device driver. Changes to pre-html and + the new grohtml are too numerous to be documented here. + + Stuff related to `html' has been renamed to `html-old' and `html2' + stuff has been renamed to `html' (including directories). The new + html device driver is therefore invoked as `-Thtml'. + + Added new `\O' escape to suppress output (needed by html driver). + + Added functions and code to pass info about input-level commands + (`.in', `.fl', etc.) to html driver. + + Three new functions (.html-begin, .html-end, and .html-image) for + better html handling: `html-begin' will execute the remaining line + if at the outermost nesting level, increasing an internal counter. + `html-end' does the same but decreases the internal counter. + `html_image' puts its arguments into a special node (suppress_node) + to define an image region. + + The `output' request has been removed. + + * tmac/html-tags.tmac: Removed. + * tmac/arkup.tmac: Updated and renamed to ... + * tmac/www.tmac: New file. + * tmac/markup.tmac Updated and renamed to ... + * tmac/mwww.tmac: New file. + * tmac/Makefile.sub: Updated. + * tmac/an-old.tmac: Updated. + * tmac/eqnrc: Updated. + * tmac/groff_man.man + * tmac/groff_markup.man: Updated and renamed to ... + * tmac/groff_mwww.man: New file. + * tmac/groff_tmac.man: Updated. + * tmac/html-old.tmac: Updated and Renamed from html.tmac. + * tmac/html.tmac: Updated and renamed from html2.tmac. + * tmac/pspic.tmac: Updated html support. + * tmac/s.tmac: Added html output support. + * tmac/troffrc, tmac/troffrc-end: Updated. + + * Makefile.in, doc/Makefile: Updated. + * doc/groff.texinfo: Added info about new `\O' escape. + * doc/homepage.ms: Use `MAILTO' macro. + + * font/devhtml/DESC.proto: Add `C' font. + * font/devhtml/Makefile.sub: Updated. + * font/devhtml/R.proto: Minor fixes. + * font/devhtml-old/Makefile.sub: Updated. + + * src/devices/grohtml-old/Makefile.sub: Updated. + + * src/libs/libdriver/printer.cc (printer::get_font_from_index): New + method. + * src/libs/libgroff/htmlindicate.cc (html_begin_suppress, + graphic_start): Add `inline' parameter. Update. + (html_end_suppress, graphic_end): Update. + + * src/include/html-strings.h: New file. + * src/include/htmlindicate.h: Comments updated. + * src/include/printer.h: Updated. + + * src/preproc/eqn/main.cc (do_file, main): Updated. + * src/preproc/pic/troff.cc (troff_output::start_picture, + troff_output::finish_picture): Updated. + * src/preproc/tbl/main.cc (process_input_file): Updated. + + * src/roff/groff/groff.cc (main): Updated. + Pass device arguments to predrivers also. + Use `ps' device for `eqn' preprocessor if `-Thtml' is given. + * src/roff/troff/env.h (environment): Updated. + New elements `need_eol' and `ignore_next_eol' (for html output). + * src/roff/troff/env.cc (environment::environment): Add initializers + for `need_eol' and `ignore_next_eol'. + (environment::add_html_tag_eol, environment::add_html_tag_tabs): New + functions. + (point_size, fill, no_fill, center, right_justify, line_length, + indent, temporary_indent, break_request, handle_tab): Use + `add_html_tag()'. + (set_tabs): Use `add_html_tag_tabs()'. + (environment::add_html_tag): Updated. + (environment::do_break): Updated. + * src/roff/troff/div.cc (space_request, flush_output): Use + `environment::add_html_tag()'. + * src/roff/troff/input.cc: Updated. + New variable `html_level' to indicate nested `html-begin' requests. + (file_iterator::fill): Use `environment::add_html_tag_eol()'. + (non_interpreted_char_node, token_node, non_interpreted_node): Add + `force_tprint()' method. + (token::next): Handle `\O'. + (do_suppress): Implement it. + (html_begin, html_end, html_image): New functions. + (init_output_requests): Renamed to ... + (init_html_requests): this. + (main): Use it. + (macro::append_str, macro::append_unsigned, macro::append_int): New + methods. + New variable `output_low_mark_miny' to limit minimal value of y. + (reset_output_registers): Use it. + (output_request): Removed. + (get_output_registers): New function. + * src/roff/troff/node.h (node): Make `force_tprint()' virtual. + (*_node): Added `force_tprint()' if necessary. + (special_node): New elements `tf' and `get_tfont()'. + (suppress_node): New class. + * src/roff/troff/node.cc: + New global variables `image_no' and `suppress_start_page'. + (real_output_file): New method `is_on()'. + (troff_output_file): New method `start_special(tfont)'. + (troff_output_file::really_print_line): Use `tprint' conditionally. + (real_output_file::print_line): Updated. + (real_output_file::on): Updated. + (*_node): Added `force_tprint()'. + (special_node::special_node): Initializer updated. + (special_node::same, special_node::copy, special_node::tprint_start): + Updated. + (get_reg_int, get_reg_str): New functions. + (suppress_node::*): New methods. + New global variables last_position, last_image_filename; + (min): New inline function. + * src/roff/troff/reg.h, src/roff/troff/request.h, + src/roff/troff/troff.h: Updated. + 2001-01-13 Werner LEMBERG <wl@gnu.org> * NEWS, src/roff/troff/troff.man, doc/groff.texinfo: Fix diff --git a/Makefile.in b/Makefile.in index 4d8870e79..602a812ea 100644 --- a/Makefile.in +++ b/Makefile.in @@ -341,13 +341,13 @@ CCPROGDIRS=\ src/preproc/grn \ src/preproc/refer \ src/preproc/soelim \ - src/preproc/html2 \ + src/preproc/html \ src/devices/grops \ src/devices/grotty \ src/devices/grodvi \ src/devices/grolj4 \ src/devices/grohtml \ - src/devices/grohtml2 \ + src/devices/grohtml-old \ src/devices/grolbp \ src/utils/tfmtodit \ src/utils/hpftodit \ @@ -366,7 +366,7 @@ DEVDIRS=\ font/devX100-12 \ font/devlj4 \ font/devhtml \ - font/devhtml2 \ + font/devhtml-old \ font/devlbp ALLTTYDEVDIRS=\ font/devascii \ diff --git a/doc/Makefile b/doc/Makefile index 57abf327b..408e6e12c 100755 --- a/doc/Makefile +++ b/doc/Makefile @@ -54,13 +54,13 @@ all: $(DOCS) GROFF_TMAC_PATH=../tmac; \ export GROFF_TMAC_PATH; \ sed -e "s;@VERSION@;$(version)$(revision);" $< \ - | $(GROFF) -Tascii $(FFLAG) -U -ms -markup >$@ + | $(GROFF) -Tascii $(FFLAG) -U -ms -mwww >$@ .ms.ps: GROFF_TMAC_PATH=../tmac; \ export GROFF_TMAC_PATH; \ sed -e "s;@VERSION@;$(version)$(revision);" $< \ - | $(GROFF) -Tps $(FFLAG) -U -ms -markup >$@ + | $(GROFF) -Tps $(FFLAG) -U -ms -mwww >$@ .texinfo.dvi: texi2dvi -e $< diff --git a/doc/groff.texinfo b/doc/groff.texinfo index 39b95dedc..9c55cb20f 100644 --- a/doc/groff.texinfo +++ b/doc/groff.texinfo @@ -1081,7 +1081,7 @@ accessible via @code{groff}. This option prevents the loading of the Make programs run by @code{groff} print out their version number. @item -V -Print the pipeline on stdout instead of executing it. +Print the pipeline on @code{stdout} instead of executing it. @item -z Suppress output from @code{gtroff}. Only error messages will be @@ -1352,7 +1352,7 @@ groff file @noindent This command processes @file{file} without a macro package or a preprocessor. The output device is the default, @samp{ps}, and the -output is sent to stdout. +output is sent to @code{stdout}. @example groff -t -mandoc -Tascii file | less @@ -2174,7 +2174,7 @@ in italic. @cindex @file{man}, default indentation @cindex default indentation, @file{man} The default indentation is 7.2@dmn{n} for all output devices except for -@code{grohtml} which uses 1.2@dmn{i} instead. +@code{grohtml} which ignores indentation. @maindex DT @maindex TH @@ -2329,6 +2329,7 @@ Users of macro packages may skip it if not interested in details. * Traps:: * Diversions:: * Environments:: +* Suppressing output:: * I/O:: * Postprocessor Access:: * Miscellaneous:: @@ -7063,7 +7064,7 @@ hacks; for example, the following will set register @code{n} to@w{ }1. @c ===================================================================== -@node Environments, I/O, Diversions, Programming Tutorial +@node Environments, Suppressing output, Diversions, Programming Tutorial @section Environments @cindex environments @@ -7150,7 +7151,46 @@ which takes the name of the environment to copy from as an argument. @c ===================================================================== -@node I/O, Postprocessor Access, Environments, Programming Tutorial +@node Suppressing output, I/O, Environments, Programming Tutorial +@section Suppressing output +@cindex suppressing output + +@findex \O +@code{gtroff} allows the programmer to disable resp.@: enable output +through the use of the @code{\O} escape: + +@table @samp +@item \O0 +Disable any ditroff glyphs from being emitted to the device driver. + +@item \O1 +Enable output of glyphs. +@end table + +The previous commands also reset the four registers @samp{opminx}, +@samp{opminy}, @samp{opmaxx}, and @samp{opmaxy} to -1. @xref{Register +Index}. These four registers mark the top left and bottom right hand +corners of a box which encompasses all written glyphs. + +The following two parameter of @code{\O} are @code{grohtml} device +specific. + +@table @samp +@item \O2 +Disable any ditroff glyphs from being emitted to the device driver. Also +write out to @code{stderr} the page number and four registers encompassing +the glyphs previously written since the last call to @code{\O}. + +@item \O3 +Enable output of glyphs (the default). Also write out to @code{stderr} +the page number and four registers encompassing the glyphs previously +written since the last call to @code{\O}. +@end table + + +@c ===================================================================== + +@node I/O, Postprocessor Access, Suppressing output, Programming Tutorial @section I/O @cindex i/o @cindex input and output requests @@ -7501,16 +7541,16 @@ The @code{pm} request will dump out the entire symbol table. @cindex dumping number registers @cindex number registers, dumping The @code{pnr} request will print the names and contents of all -currently defined number registers on stderr. +currently defined number registers on @code{stderr}. @item @findex ptr @cindex dumping traps @cindex traps, dumping The @code{ptr} request will print the names and positions of all traps -(not including input line traps and diversion traps) on stderr. Empty -slots in the page trap list are printed as well, because they can affect -the priority of subsequently planted traps. +(not including input line traps and diversion traps) on @code{stderr}. +Empty slots in the page trap list are printed as well, because they can +affect the priority of subsequently planted traps. @item @findex fl diff --git a/doc/homepage.ms b/doc/homepage.ms index ccf81319a..00da216f2 100755 --- a/doc/homepage.ms +++ b/doc/homepage.ms @@ -139,16 +139,18 @@ bug-groff@gnu.org for reporting bugs groff@gnu.org for general discussion of groff groff-commit@ffii.org a read-only list showing logs of commitments to the CVS repository -\fR +\fP .fi .RE .sp .LP -Note that groff@gnu.org is an alias for groff@ffii.org; you must be -subscribed to the `groff' and `groff-commit' lists to send mails. +Note that groff@gnu.org is an alias for +.MAILTO groff@ffii.org groff@ffii.org ; +you must be subscribed to the `groff' and `groff-commit' lists to send mails. .LP To subscribe, send e-mail to [list]-request@[domain] (example: -groff-request@ffii.org) with the word `subscribe' in either the +.MAILTO groff-request@ffii.org groff-request@ffii.org ) +with the word `subscribe' in either the subject or body of the e-mail (don't include the quotes). .LP GNU groff was written by @@ -157,6 +159,6 @@ It is now maintained by .MAILTO Ted.Harding@nessie.mcc.ac.uk "Ted Harding" and .MAILTO wl@gnu.org "Werner Lemberg" . -.LINE +.br . .\" EOF diff --git a/font/devhtml2/DESC.proto b/font/devhtml2/DESC.proto deleted file mode 100755 index ba27fba0a..000000000 --- a/font/devhtml2/DESC.proto +++ /dev/null @@ -1,12 +0,0 @@ -res 240 -hor 24 -vert 40 -unitwidth 10 -sizes 10 0 -fonts 5 R I B BI S -tcommand -html -postpro post-grohtml -prepro pre-grohtml -use_charnames_in_special -pass_filenames diff --git a/font/devhtml2/Makefile.sub b/font/devhtml2/Makefile.sub deleted file mode 100755 index f973e9d27..000000000 --- a/font/devhtml2/Makefile.sub +++ /dev/null @@ -1,33 +0,0 @@ -DEV=html2 -PROTOFONTS=R I B BI -FONTS=$(PROTOFONTS) S -DEVFILES=$(FONTS) DESC -CLEANADD=$(FONTS) DESC - -RES=240 -CPI=10 -LPI=6 - -$(FONTS): R.proto - @echo Making $@ - @-rm -f $@ - @(charwidth=`expr $(RES) / $(CPI)` ; \ - sed -e "s/^name [A-Z]*$$/name $@/" \ - -e "s/^\\([^ ]*\\) [0-9]+ /\\1 $$charwidth /" \ - -e "s/^spacewidth [0-9]+$$/spacewidth $$charwidth/" \ - -e "s/^internalname .*$$/internalname $@/" \ - -e "/^internalname/s/BI/3/" \ - -e "/^internalname/s/B/2/" \ - -e "/^internalname/s/I/1/" \ - -e "/^internalname .*[^ 0-9]/d" \ - $(srcdir)/R.proto >$@) - -DESC: DESC.proto - @echo Making $@ - @-rm -f $@ - @sed -e "s/^res .*$$/res $(RES)/" \ - -e "s/^hor .*$$/hor `expr $(RES) / $(CPI)`/" \ - -e "s/^vert .*$$/vert `expr $(RES) / $(LPI)`/" \ - -e "s/^fonts .*$$/fonts `set $(FONTS); echo $$#` $(FONTS)/" \ - $(srcdir)/DESC.proto >$@ - diff --git a/font/devhtml2/R.proto b/font/devhtml2/R.proto deleted file mode 100755 index cb0e0799c..000000000 --- a/font/devhtml2/R.proto +++ /dev/null @@ -1,308 +0,0 @@ -name R -internalname 0 -spacewidth 24 -charset -! 24 0 0x0021 -" 24 0 0x0022 " -dq " -# 24 0 0x0023 -sh " -$ 24 0 0x0024 -Do " -% 24 0 0x0025 -& 24 0 0x0026 & -aq 24 0 0x0027 -( 24 0 0x0028 -) 24 0 0x0029 -* 24 0 0x002A -+ 24 0 0x002B -pl " -, 24 0 0x002C -\- 24 0 00002D -hy " -- " -. 24 0 0x002E -/ 24 0 0x002F -sl " -0 24 0 0x0030 -1 24 0 0x0031 -2 24 0 0x0032 -3 24 0 0x0033 -4 24 0 0x0034 -5 24 0 0x0035 -6 24 0 0x0036 -7 24 0 0x0037 -8 24 0 0x0038 -9 24 0 0x0039 -: 24 0 0x003A -; 24 0 0x003B -< 24 0 0x003C < -= 24 0 0x003D -eq " -> 24 0 0x003E > -? 24 0 0x003F -@ 24 0 0x0040 -at " -A 24 0 0x0041 -B 24 0 0x0042 -C 24 0 0x0043 -D 24 0 0x0044 -E 24 0 0x0045 -F 24 0 0x0046 -G 24 0 0x0047 -H 24 0 0x0048 -I 24 0 0x0049 -J 24 0 0x004A -K 24 0 0x004B -L 24 0 0x004C -M 24 0 0x004D -N 24 0 0x004E -O 24 0 0x004F -P 24 0 0x0050 -Q 24 0 0x0051 -R 24 0 0x0052 -S 24 0 0x0053 -T 24 0 0x0054 -U 24 0 0x0055 -V 24 0 0x0056 -W 24 0 0x0057 -X 24 0 0x0058 -Y 24 0 0x0059 -Z 24 0 0x005A -[ 24 0 0x005B -lB " -\ 24 0 0x005C -rs " -] 24 0 0x005D -rB " -a^ 24 0 0x005E -^ " -ha " -_ 24 0 0x005F -ru " -ul " -\` 24 0 0x0060 -ga " -a 24 0 0x0061 -b 24 0 0x0062 -c 24 0 0x0063 -d 24 0 0x0064 -e 24 0 0x0065 -f 24 0 0x0066 -g 24 0 0x0067 -h 24 0 0x0068 -i 24 0 0x0069 -j 24 0 0x006A -k 24 0 0x006B -l 24 0 0x006C -m 24 0 0x006D -n 24 0 0x006E -o 24 0 0x006F -p 24 0 0x0070 -q 24 0 0x0071 -r 24 0 0x0072 -s 24 0 0x0073 -t 24 0 0x0074 -u 24 0 0x0075 -v 24 0 0x0076 -w 24 0 0x0077 -x 24 0 0x0078 -y 24 0 0x0079 -z 24 0 0x007A -lC 24 0 0x007B -{ " -ba 24 0 0x007C -or " -| " -rC 24 0 0x007D -} " -a~ 24 0 0x007E -~ " -ti " -r! 24 0 0x00A1 ¡ -char161 " -ct 24 0 0x00A2 ¢ -char162 " -Po 24 0 0x00A3 £ -char163 " -Cs 24 0 0x00A4 ¤ -char164 " -Ye 24 0 0x00A5 ¥ -char165 " -bb 24 0 0x00A6 ¦ -char166 " -sc 24 0 0x00A7 § -char167 " -ad 24 0 0x00A8 ¨ -char168 " -co 24 0 0x00A9 © -char169 " -Of 24 0 0x00AA ª -char170 " -Fo 24 0 0x00AB « -char171 " -no 24 0 0x00AC ¬ -char172 " -rg 24 0 0x00AE ® -char174 " -a- 24 0 0x00AF ¯ -char175 " -de 24 0 0x00B0 ° -char176 " -+- 24 0 0x00B1 ± -char177 " -S2 24 0 0x00B2 ² -char178 " -S3 24 0 0x00B3 ³ -char179 " -aa 24 0 0x00B4 ´ -char180 " -char181 24 0 0x00B5 µ -ps 24 0 0x00B6 ¶ -char182 " -pc 24 0 0x00B7 · -char183 " -ac 24 0 0x00B8 ¸ -char184 " -S1 24 0 0x00B9 ¹ -char185 " -Om 24 0 0x00BA º -char186 " -Fc 24 0 0x00BB » -char187 " -14 24 0 0x00BC ¼ -char188 " -12 24 0 0x00BD ½ -char189 " -34 24 0 0x00BE ¾ -char190 " -r? 24 0 0x00BF ¿ -char191 " -`A 24 0 0x00C0 À -char192 " -'A 24 0 0x00C1 Á -char193 " -^A 24 0 0x00C2 Â -char194 " -~A 24 0 0x00C3 Ã -char195 " -:A 24 0 0x00C4 Ä -char196 " -oA 24 0 0x00C5 Å -char197 " -AE 24 0 0x00C6 Æ -char198 " -,C 24 0 0x00C7 Ç -char199 " -`E 24 0 0x00C8 È -char200 " -'E 24 0 0x00C9 É -char201 " -^E 24 0 0x00CA Ê -char202 " -:E 24 0 0x00CB Ë -char203 " -`I 24 0 0x00CC Ì -char204 " -'I 24 0 0x00CD Í -char205 " -^I 24 0 0x00CE Î -char206 " -:I 24 0 0x00CF Ï -char207 " --D 24 0 0x00D0 Ð -char208 " -~N 24 0 0x00D1 Ñ -char209 " -`O 24 0 0x00D2 Ò -char210 " -'O 24 0 0x00D3 Ó -char211 " -^O 24 0 0x00D4 Ô -char212 " -~O 24 0 0x00D5 Õ -char213 " -:O 24 0 0x00D6 Ö -char214 " -mu 24 0 0x00D7 × -char215 " -/O 24 0 0x00D8 Ø -char216 " -`U 24 0 0x00D9 Ù -char217 " -'U 24 0 0x00DA Ú -char218 " -^U 24 0 0x00DB Û -char219 " -:U 24 0 0x00DC Ü -char220 " -'Y 24 0 0x00DD Ý -char221 " -TP 24 0 0x00DE Þ -char222 " -ss 24 0 0x00DF ß -char223 " -`a 24 0 0x00E0 à -char224 " -'a 24 0 0x00E1 á -char225 " -^a 24 0 0x00E2 â -char226 " -~a 24 0 0x00E3 ã -char227 " -:a 24 0 0x00E4 ä -char228 " -oa 24 0 0x00E5 å -char229 " -ae 24 0 0x00E6 æ -char230 " -,c 24 0 0x00E7 ç -char231 " -`e 24 0 0x00E8 è -char232 " -'e 24 0 0x00E9 é -char233 " -^e 24 0 0x00EA ê -char234 " -:e 24 0 0x00EB ë -char235 " -`i 24 0 0x00EC ì -char236 " -'i 24 0 0x00ED í -char237 " -^i 24 0 0x00EE î -char238 " -:i 24 0 0x00EF ï -char239 " -Sd 24 0 0x00F0 ð -char240 " -~n 24 0 0x00F1 ñ -char241 " -`o 24 0 0x00F2 ò -char242 " -'o 24 0 0x00F3 ó -char243 " -^o 24 0 0x00F4 ô -char244 " -~o 24 0 0x00F5 õ -char245 " -:o 24 0 0x00F6 ö -char246 " -di 24 0 0x00F7 ÷ -char247 " -/o 24 0 0x00F8 ø -char248 " -`u 24 0 0x00F9 ù -char249 " -'u 24 0 0x00FA ú -char250 " -^u 24 0 0x00FB û -char251 " -:u 24 0 0x00FC ü -char252 " -'y 24 0 0x00FD ý -char253 " -Tp 24 0 0x00FE þ -char254 " -:y 24 0 0x00FF ÿ -char255 " diff --git a/man/groff_font.man b/man/groff_font.man index 8b9e3b407..c2f7461ee 100644 --- a/man/groff_font.man +++ b/man/groff_font.man @@ -1,5 +1,5 @@ .ig \"-*- nroff -*- -Copyright (C) 1989-1995 Free Software Foundation, Inc. +Copyright (C) 1989-1995, 2001 Free Software Foundation, Inc. Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice @@ -250,7 +250,7 @@ otherwise it corresponds to the groff input character (if it is exactly two characters .I xx it can be entered as -.BI \e( xx\fR.) +.BI \e( xx\fR). Groff supports eight bit characters; however some utilities has difficulties with eight bit characters. For this reason, there is a convention that the name diff --git a/src/devices/grohtml/ChangeLog b/src/devices/grohtml/ChangeLog deleted file mode 100755 index 4b574384f..000000000 --- a/src/devices/grohtml/ChangeLog +++ /dev/null @@ -1,263 +0,0 @@ -2000-11-16 Werner LEMBERG <wl@gnu.org> - - * html.cc (main): Use stdout for -v. - - Fixing copyright dates. - -2000-11-15 Werner LEMBERG <wl@gnu.org> - - * html.cc (main): Make -v exit immediately to be compliant with - the GNU standard. - -2000-08-23 Gaius Mulley <gaius@glam.ac.uk> - - * html.cc (char_translate_to_html): New function. - (str_translate_to_html): Use it. - -2000-06-17 Eli Zaretskii <eliz@is.elta.co.il> - - * html.cc [!_POSIX_VERSION]: Include limits.h and dirent.h or - sys/dir.h. Define NAME_MAX using MAXNAMLEN. Include nonposix.h. - (file_name_max): New function. - (html_printer::make_new_image_name): If the filesystem doesn't - support file names longer than 14 characters, use a shorter - image_name string. - (html_printer::convert_to_image): Enlarge the size of buffer[] to - accomodate 2 temp file names plus some slack. Don't put \n at the - end of commands passed to system(). Redirect stderr to the null - device programmatically, not via the shell. Use NULL_DEV, not - literal "/dev/null". Print diagnostics if any calls to system() - failed. - -2000-05-31 Keith Thompson <kst@SDSC.EDU> - - * html.cc: Added declaration of mktemp() as needed for SunOS 4.1.3. - -2000-05-11 Werner LEMBERG <wl@gnu.org> - - * output.cc (simple_output::simple_output): Reordering of - initializers to remove compiler warning. - -2000-04-28 Gaius Mulley <gaius@glam.ac.uk> - - * html.cc (calculate_margin): Calculate the left and right margin - irrespective of the boolean `margin_on'. Fixes a divide by zero bug - and a column bug as reported by Steve Blinkhorn <steve@prd.co.uk>. - Improved the behaviour of the -m (margin on) option. - - * html.cc (make_html_indent): More checking. - - * html.cc (right_indentation): Fixed substitution slip-up. - -2000-03-30 Werner LEMBERG <wl@gnu.org> - - * grohtml.man: Document use of whitespace between command line - arguments and its parameters. - -2000-03-17 Werner LEMBERG <wl@gnu.org> - - * grohtml.man: Some formatting. - -2000-03-11 Werner LEMBERG <wl@gnu.org> - - * ordered_list.h (list_element): Added `<T>' twice to satisfy picky - compilers. - -2000-03-01 Gaius Mulley <gaius@glam.ac.uk> - - * html.cc (handle_unknown_font_command): Removed dead code as - spotted by Werner. - -2000-02-11 Gaius Mulley <gaius@glam.ac.uk> - - * html.cc (create_tmp_file, create_temp_name): Removed. It has been - replaced with calls to xtmpfile() and xtmptemplate(). - -2000-02-07 Gaius Mulley <gaius@glam.ac.uk> - - * html.cc (html_printer::make_new_image_name): Tidied up file and - fixed name of image if the source file is in a different directory. - - * html.cc (create_file): Renamed to create_tmp_file. - -2000-02-07 Colin Phipps <crp22@cam.ac.uk> - - * html.cc (create_file): Identified & fixed security bug when - creating files in /tmp. - -2000-02-06 Werner LEMBERG <wl@gnu.org> - - * Makefile.sub: Adapted to new directory structure. - -2000-01-28 Gaius Mulley <gaius@glam.ac.uk> - - * html.cc: Minor fixes. - -2000-01-27 Gaius Mulley <gaius@glam.ac.uk> - - * html.cc: Added support for the new tcommand `F'. - * TODO: Updated. - -2000-01-24 Gaius Mulley <gaius@glam.ac.uk> - - * design.ms: Revised. Removed TODO stuff. - - * TODO: New file. - -2000-01-21 Gaius Mulley <gaius@glam.ac.uk> - - * html.cc: Add support for char names in special requests (to - support e.g. accented characters in HTML specials). - -2000-01-14 Gaius Mulley <gaius@glam.ac.uk> - - * html.cc, html.h: Many fixes to table code. - - Fixes to manual page handling, font changes, spaces, and - diacritical characters. All *standard* html character encodings - are handled. - - Added -T option which turns off all image generation for tables. - One day grohtml should be able to determine this for itself. - - Altered image name to: <groff_input_file>-<index>.png as per - Werners suggestion. - - * grohtml.man: Document -T option. - - * html_chars.h: New file, providing diacritical table support. - - * output.cc: New file, providing basic output routines for grohtml. - - * Makefile.sub: Added output.cc. - - * Makefile.dep: Updated. - -2000-01-13 Bruno Haible <haible@clisp.cons.org> - - * html.cc: Avoid most "g++ -Wall -Wno-sign-compare" warnings. - -2000-01-10 Werner Lemberg <wl@gnu.org> - - * html.cc: Use Version_string instead of version_string. - -1999-12-30 Gaius Mulley <gaius@glam.ac.uk> - - * html.cc (is_appropriate_to_start_table): Added a missing - declaration. - -1999-12-28 Gaius Mulley <gaius@glam.ac.uk> - - * html.cc: Revisited the table handling code with a vengeance, - aiming to make manual pages generate sensible html. - Superscripting/subscripting revisited. Fixed wierd table lengths. - Table widths are now specified in percentages. Fixed the man.n - test example which Werner reported. - -Version 1.15 released -===================== - -1999-12-21 Werner LEMBERG <wl@gnu.org> - - * grohtml.man: Fixed copyright year. - -1999-12-15 Gaius Mulley <gaius@glam.ac.uk> - - * html.cc: Some other fixes. - -1999-12-13 Gaius Mulley <gaius@glam.ac.uk> - - * html.cc (main): Added new option `-x' to help debugging tables. - -1999-12-11 Gaius Mulley <gaius@glam.ac.uk> - - * html.cc: Fixed image position bugs. However, three major bugs - remain: Firstly, grohtml sometimes miscalculates the end of an - html table resulting in text which appears twice. Secondly, - equation numbers are not handled correctly. Thirdly, equation - macros and pic macros can confuse grohtml; this can be seen by - nested `graphic-start's -- I believe the best method to solve this - is to detect .EQ, .EN, .TS, .TE, .PS, .PE sequences in troff and - add the graphic-start special character at this point. - - * grohtml.man: Minor fixes. - -1999-11-29 Gaius Mulley <gaius@glam.ac.uk> - - * design.ms: More updates; added some basic introductional - information. - - * html.cc: Fixed more bugs mainly in the table handling code. - Making the code terminate a table at the correct position. - Indented .IPs appear to work now. Region ends also correctly - terminate tables. - -1999-11-16 Gaius Mulley <gaius@glam.ac.uk> - - * design.ms, grohtml.man: Updated. - - * html.cc, ordered_list.h: Fixed many bugs in the table handling - code. Reverted the -t switch so that table handling code is used - by default and users must turn it off with -t. - - Manual page generation using `groff -Thtml -man' is much better - due in large part to the table code and minor alterations in - tmac.an. - -1999-10-30 Gaius Mulley <gaius@glam.ac.uk> - - * implemented auto formatting and introduced html table - code. Fixed several text handling bugs and grohtml will - detect centered lines - an offshoot of the html table code. - - * reverted meaning of grohtml's `-a' switch: using -a means that - output will be preformatted. - -1999-10-05 Gaius Mulley <gaius@glam.ac.uk> - - * Introduced command line options -r to determine the resolution - of generated images, -I to determine the format of images - generated. - - * Fixed many bugs to do with superscripts, subscripts, - indentation, font changes, and extraneous spaces. - - * Fixed bug in determining the range of polygons and splines. - - * Updated the manual page to reflect the new options. - - * The default image type is png format, however this will only - work if you have a gs with a png output device. If you don't have - a gs with this ability you can either reconfigure html to generate - gif images by default (alter a #define in html.cc). Or - alternatively you can use the -Igif option. - -1999-09-27 Werner LEMBERG <wl@gnu.org> - - * html.cc (move_horizontal): Fonts have changed one character too - late. - -1999-09-26 Werner LEMBERG <wl@gnu.org> - - * grohtml.man: Minor cosmetic fixes. - -1999-09-25 Gaius Mulley <gaius@glam.ac.uk> - - * grohtml.man, html.cc: Rewrite of the html text component. Basic - font faces supported together with font types. Superscript and - subscript have also been implemented. Temporarily removed the - -P-a switch on grohtml as it is not working (never worked). This - is the next `to do'. Added a simple macro tmac.arkup which - contains simple html features. This macro needs further work. - Arc, spline, polygon fill have all been added and arc max/min xy - limits are calculated, the same needs to be done for spline. Many - bugs have been fixed regarding basic html text. - - * design.ms: New file describing how html.cc works. - -Aug 1999 - - Initial release, very basic html text generated, quite ugly text - is generated according to many reports :-) Equations, tables, - pictures generate gif files via gs and ppmquant, ppmtogif, grops. - diff --git a/src/devices/grohtml/Makefile.sub b/src/devices/grohtml/Makefile.sub index a1e301f01..2c3a55a5d 100644 --- a/src/devices/grohtml/Makefile.sub +++ b/src/devices/grohtml/Makefile.sub @@ -1,10 +1,16 @@ -PROG=grohtml +PROG=post-grohtml MAN1=grohtml.n XLIBS=$(LIBDRIVER) $(LIBGROFF) MLIB=$(LIBM) OBJS=\ - html.o \ + post-html.o \ + html-text.o \ output.o CCSRCS=\ - $(srcdir)/html.cc \ + $(srcdir)/post-html.cc \ + $(srcdir)/html-text.cc \ $(srcdir)/output.cc +HDRS=\ + $(srcdir)/html.h \ + $(srcdir)/html-chars.h \ + $(srcdir)/html-text.h diff --git a/src/devices/grohtml/TODO b/src/devices/grohtml/TODO deleted file mode 100755 index 4924bd1c5..000000000 --- a/src/devices/grohtml/TODO +++ /dev/null @@ -1,294 +0,0 @@ - ------------------------------------------------------------------- - T O D O L I S T ------------------------------------------------------------------- -finish working out the max and min x, y, extents for splines. ------------------------------------------------------------------- -check and test thoroughly all the character descriptions in devhtml -(originally taken from devX100) ------------------------------------------------------------------- -improve tmac.arkup ------------------------------------------------------------------- -also improve documentation. ------------------------------------------------------------------- -fix the bugs which are exposed by Eric Raymonds pic guide, -"Making Pictures With GNU PIC". It appears that grohtml becomes confused -about which sections of the document are text and which sections need -to be rendered as an image. ------------------------------------------------------------------- -it would be nice to modularise the source. A natural division might be -to extract the table handling code from html.cc into table.cc. -The table.cc could be expanded to recognise output from tbl and try -and generate html tables with lines/rules/boxes. The code as it stands -should cope with very simple plain text tables. But of course at present -it does not get a chance to do this because the output of gtbl is -bracketed by \fCgraphic-start\fR and \fCgraphic-end\fR. ------------------------------------------------------------------- -introduce anti aliasing for the images as mentioned by Werner. ------------------------------------------------------------------- -improve generation of html. Perhaps by using a stack of current -html commands and using a kind of peephole optimizer on the stack? -Certainly the html should be buffered and optimized. ------------------------------------------------------------------- - - -Informal to do bug list and done list -===================================== - -This very informal and I've included some comments. Mainly consists -of a emailed bugs and wish lists. All very useful and welcome. - ------------------------------------------------------------------- -Dean writes: (provinsd@enf403-2.ensu.ucalgary.ca) - -I noticed also that the TOC appears immediately after the title, splitting -it from the author and abstract. Any chance it can be moved down? - -gaius> this should be straight forward. (Not done yet though) ------------------------------------------------------------------- - -.) The command `\(->', translates to the `registered' sign (or rather - the character `0xAE') instead of a right arrow. - ---nearly fixed-- 4/01/2000 - -gaius> if we know the standard html character encoding for farrow which -gaius> will work on *all* browsers then this can be fixed inside devhtml/TR -gaius> etc. Otherwise I guess we could translate this character into -> -gaius> in tmac.html ? - ------------------------------------------------------------------- - -Werner writes: - -Nevertheless, still some bugs in it. As usual, I'm refering to man.1 -of the mandb package; my command to create man.html was - - groff -U -t -man -Thtml -P-r -P200 man.1 > man.html - -.) The `-w , --where, --location' node at the beginning of man.html - shouldn't be there at all. - -> .) Some paragraphs still contain hyphenated words (e.g. first -> paragraph of the `DESCRIPTION' section). - -Oops! Please ignore this. I forgot to include `-mhtml' :-) - -.) Is it possible to have anti-aliased PNG images? - -.) The item `man --help' in the `EXAMPLES' section doesn't start a new - paragraph. - -.) In the description of the -r switch (in the `OPTIONS' section), - there is a new paragraph in the middle of a sentence. - -.) What about centering the images? Or does it depend on the table - itself? - -gaius> yes, grohtml places images at their relative position on the page. - -.) In the `OPTIONS' section, `-c, --catman' and `-d, --debug' are - glued together which shouldn't happen. ---fixed-- - -.) Sometimes, an empty line is missing between items, e.g. between the - description of the -e and the -f options. - -.) After the `-w, --where, --location' line, there is a superfluous - empty line. - -.) The indentation in the `FILES' section is inconsistent. The same - is true for `-V, --version' a few lines above. - -.) The formatting of the paragraph after the first table is completely - wrong. It appears that the first few words are set in two columns; - additionally, the indentation is incorrect. - -.) Similarly, the description of `-l' in the OPTIONS section is - idented incorrectly. Wrong indentations happen still quite - frequently. - -.) In the description of the `-D' option, there is a blank line in the - middle of a paragraph. - - - Werner - ------------------------------------------------------------------- -Werner writes: - -Gaius, - -checking a weird man page written by myself in German (using German -hyphenation patterns also :-), I found some more bugs: - -.) Look at the following: - -[\c -...\^\c -] -[\c -.BI -P \ \%Plattform-ID\^\c -] - - This translates to - -[<font size=3><B>-E</B> <font size=3><I>Kodierungs-ID</I> <font size=3>] - ^ - (groff breaks the line after the final `]'.) - - There are two errors in it: First of all, the `\ ' command should - be translated to ` '. Secondly, a blank has crept in (marked - with `^'. Apparently, this is related to whether it is the last - item of a line or not. - ---fixed-- 4 01 2000 ------------------------------------------------------------------- - -from Steve Blinkhorn <steve@prd.co.uk> - -One thought that came immediately to mind after our first trials. -If grohtml depends on grops, should there not be an easy interface to -allow PostScript code to be interpreted into the output? For -instance, we generate our letterhead, including a logo, on the fly in -groff. The logo is pure PostScript. We use PostScript for colour -manipulation, and recently for generating a lot of graphics for -printing. - -gaius> should be interesting - if we can generate PS then GS it -gaius> we should be in business - ------------------------------------------------------------------- - D O N E L I S T ------------------------------------------------------------------- -the logical place and name for a file describing tmac.arkup is -groff_markup.man placed into the `tmac' subdirectory, and your html.ms -looks like being this kind of file. - -So I won't check it in currently -- may I ask you to convert this file -to a man page? - --- fixed -- - -Another related problem: I can imagine that a lot of people start to -write man pages with HTML output in mind also. Nevertheless, it -should be still possible to display such pages correctly with a plain -text man pager. As a consequence, such man pages should contain at -the beginning something like - - .do mso tmac.arkup - -What do you think? - - Werner - --- fixed -- -gaius> fixed by using troffrc-end I believe --------------------------------------------------------------------- -Gaius, - -in troffrc, it appears to me that tmac.html is loaded if the output -device is HTML. So why must I load it again (using -mhtml) to -suppress hyphenation for HTML output? Can you provide a fix for this? - - Werner - -gaius> fixed as above --------------------------------------------------------------------- - -from (daeschler@shuttle.de) Rainer Daeschler - -I recognized s problem limiting the usage for -"none-english aliens". The generation of PNG of GIF, -skips all special characters like - - äöü ÄÖÜ ß - -French, Spanish, and Scandinavian national letters, too. - ---fixed-- 14/01/2000 - -An option which forces tables into HTML-code instead of building -an image would be most valuable. Of course it would not preserve -the original layout in many cases, but ease modifications of -the HTML-output to the users demand afterwards. - ---fixed-- 14/01/2000 - -gaius> use the new -T option to grohtml (-P-T to groff) - ------------------------------------------------------------------ -from Werner - - but `pre-defined' appears as `pre­ line' (note the space - character after the soft hyphen). Something in the code makes - problems here... - - (IIRC, I've sent you this man.1 file a few weeks ago). - -gaius> Werner fixed this by adding .cflags 0 -\(hy\(em\(en to tmac.html - ------------------------------------------------------------------ -from Werner and Eddie -> > > .LP -> > > .URL Germany "ftp://groff.ffii.org/pub/groff/" -> > > | -> > > .URL USA "ftp://ftp.gnu.org/gnu/groff/" -> > -> > Problem: the first "|" of each line is missing a leading white space -> > space. -> > -> > How to ensure the spaces get put there? -> -> This is a feature grohtml (unfortunately -- AFAIK, Gaius hasn't found -> a good workaround yet). HTML stuff gets written as specials which -> don't consume space for troff, causing some miscalculation if placed -> at the beginning of a paragraph. A workaround is to write -> -> .LP -> \& -> .URL ... -> | -> .URL ... - -gaius> fixed by adding \& to HTML as per Werner's suggestion - - -Werner writes: - -PNGs created by grohtml have apparently a white background -- isn't it -possible to make the background transparent optionally? - -Another suggestion: What do you think about calling the PNG files -<groff_input_file>-<index>.png or something like this? I can't see an -advantage in the current naming scheme except for debugging purposes -where it may be necessary to stay with the old files. - ---fixed-- 04 01 2000 - -gaius> however I've had to retain a default grohtml-pid-index.png for all -gaius> stdin as we don't know the filename.. sadly looks like everything.. -gaius> Nearly done by including a new tcommand 'F filename' - ---fixed-- 26 01 2000 ------------------------------------------------------------------- - -.) The following code produces ugly results -- is it possible to make - the HTML result similar to the ascii output? - -.in +4m -.ta 3iC -.I "Plattform Plattform-ID (pid)" -\&.sp -.ta 3iR -Apple Unicode 0 -.br -Macintosh 1 -.br -ISO 2 -.br -Microsoft 3 -.PP - ---fixed-- 14/01/2000 ------------------------------------------------------------------- diff --git a/src/devices/grohtml/design.ms b/src/devices/grohtml/design.ms deleted file mode 100755 index 6216d4eb5..000000000 --- a/src/devices/grohtml/design.ms +++ /dev/null @@ -1,129 +0,0 @@ -.nr PS 12 -.nr VS 14 -.LP -.TL -Design of grohtml -.sp 1i -.SH -What is grohtml -.LP -Grohtml is a back end for groff which generates html. -The aim of grohtml is to produce respectible html given -fairly typical groff input. -.SH -Limitations of grohtml -.LP -Although basic text can be translated -in a straightforward fashion there are some areas where grohtml -has to try and guess text relationship. In particular whenever -grohtml encounters text tables and indented paragraphs or -two column mode it will try and utilize the html table construct -to preserve columns. Grohtml also attempts to work out which -lines should be automatically formatted by the browser. -Ultimately in trying to make reasonable guesses most of the time -it will make mistakes occasionally. -.PP -Tbl, pic, eqn's are also generated using images which may be -considered a limitation. -.SH -Overview of html.cc -.LP -This file briefly provides an overview of how html.cc operates. -The html device driver works as follows: -.IP (i) .5i -firstly it creates a linked list of all words on a page. -.IP (ii) .5i -it runs through the page and finds the left most margin. Later -on when generating the page it removes the margin. -.IP (iii) .5i -scans a page and builds two kinds of regions ascii text and graphical. -The graphical regions consist of tbl's, eqn's, pic's -(basically anything that cannot be textually displayed). -It will scan through a page to find lines (such as footer etc) -and places these into tiny graphical regions. Certain fonts -also are treated as a graphical region - as html has no easy -equivalent. For example Greek math symbols. -.LP -Finally all graphical regions are translated into png files and -all text regions into html text. -.PP -To give grohtml a sporting chance of accuratly deciding which -is a graphical region and which is text, the front end programs -tbl, eqn, pic have all been tweeked to encapsulate pictures, tables -and equations with the following lines: -.sp -.nf -\f[CR]\&.if '\\*(.T'html' \\X(graphic-start(\c - -\&.if '\\*(.T'html' \\X(graphic-end(\c -\fP -.fi -.sp -these appear to grohtml as: -.sp -.nf -\f[CR]\&x X graphic-start - -\&... - -\&x X graphic-end\fP -.fi -.sp -.LP -In addition to graphic-start and graphic-end there are two -other "special characters" which are used. -.sp -\f[CR]\&x X index:N\fP -.sp -where N is a number. The purpose of this sequence is to stop -devhtml from automatically producing links to headings which -have a header level >N. -The line: -.sp -\f[CR]\&x X html:STRING\fR -.sp -.LP -allows a STRING to be passed through to the output file with -no processing whatsoever. Ie it allows users to include html -commands, via macro, such as: -.sp -\f[CR]\&.URL "Latest Emacs" "ftp://somewonderful.gnu.software"\fP -.sp -.LP -Where the URL macro bundles the info into STRING above. -For more info consult: \f[CR]tmac/tmac.arkup\fP. -.PP -While scanning through a page the html device copies headings and titles -into a list of links which are later written to the beginning -of the html document. -.SH -Table handling code -.LP -Provided that the -t option is not present when grohtml is run the grohtml -driver will attempt to find textual tables and generate html tables. -This allows .RS and .RE commands to operate with auto formatting. It also -should grohtml to process .2C correctly. However, the table handling code -has to examine the troff output and \fIguess\fR when a table starts and -finishes. It is well to know the limitations of this approach as it -sometimes makes the wrong decision. -.LP -Here are some of the rules that grohtml uses for terminating a html table: -.LP -.IP "(i)" .5i -A table will be terminated when grohtml finds line which is all in bold -font (it believes that this is a header which is outside of a table). -This might be considered incorrect behaviour especially if you use .2C -which generates a heading on the left column when the corresponding -right row is blank. -.IP "(ii)" .5i -A table is terminated when grohtml sees that the complete line is -has been spanned by words. Ie no gaps exist. -.IP "(nb)" .5i -the documentation about these rules is particularly incomplete and needs finishing -when time prevails. -.SH -Dependencies -.LP -Grohtml is dependent upon grops, gs which are invoked to -generate all png files. Png files are generated whenever a table, picture, -equation or line is encountered. diff --git a/src/devices/grohtml/grohtml.man b/src/devices/grohtml/grohtml.man index 95488f0e5..1e93e0067 100644 --- a/src/devices/grohtml/grohtml.man +++ b/src/devices/grohtml/grohtml.man @@ -36,24 +36,21 @@ grohtml \- html driver for groff .ie \\n(.$-1 .RI "[\ \fB\\$1\fP" "\\$2" "\ ]" .el .RB "[\ " "\\$1" "\ ]" .. -.OP \-atTvdgm? +.OP \-v?lrn .OP \-F dir -.OP \-I imagetype -.OP \-r resolution +.OP \-i resolution +.OP \-o image vertical offset .RI "[\ " files\|.\|.\|. "\ ]" .br .ad \na -.PP -It is possible to have whitespace between a command line option and its -parameter. .SH DESCRIPTION .B grohtml translates the output of GNU .B troff to html. -Normally +Users should always invoke .B grohtml -should be invoked by using the groff command with a +via the groff command with a .B \-Thtml option. If no files are given, @@ -77,49 +74,22 @@ using the option. .SH OPTIONS .TP -.B \-a -force -.B grohtml -to generate html line breaks in the same position as troff dictates. -Without this option -.B grohtml -generates text in paragraphs which is formatted by the html browser. +.B \-v +Displays the version. .TP -.B \-d -turn on internal debugging. +.B \-? +Emits a usage synopsis. .TP -.B \-g -tell -.B grohtml -not to try and guess titles and headings. -By using this flag together with the -m and -a flag -.B grohtml -will treat the html browser as a printer, not as a formatter. +.B -l +Turns off the production of automatic section links at the top of the document. .TP -.B \-m -leave margins alone. -.B grohtml -will not remove left margins. +.B -r +Turns off the automatic header and footer line (html rule). .TP -.B \-t -forbids -.B grohtml -from generating html tables when implementing indentation and tabular text. -.B grohtml -can implement .IP by tables or html indents. -However if .2C is used it can only be sensibly converted to html using a -table structure. -As a few known bugs still exist with the html table code this option is -present to supress execution of this development code. -The default in -.B grohtml -is that html tables are generated when appropriate. -.TP -.B \-T -forbids -.B grohtml -from generating images when processing output from tbl. -This is useful when simple textual tables are being produced. +.B -n +Generate simple heading anchors whenever a section/number heading is found. +Without the option the anchor value is the textual heading. This can cause problems +when a heading contains a `?' on some brousers (netscape). .TP .BI \-F dir Prepend directory @@ -129,17 +99,10 @@ to the search path for font and device description files; is the name of the device, usually .BR html . .TP -.BI \-I imagetype -select the type of image generated when grohtml encounters an equation, -table, or picture. -By default this is png256. -Legal image types are: gif and any of the png formats which are supported by -ghostscript gs(1). -.TP -.BI \-r resolution +.BI \-i resolution select the resolution for all images. By default this is 80 pixels per inch. -Example: -r100 indicates 100 pixels per inch. +Example: -i100 indicates 100 pixels per inch. .TP .B \-v Print the version number. @@ -154,29 +117,15 @@ There are styles called and .B BI mounted at font positions 1 to 4. -It is advisable to invoke groff with the -mhtml macro set, which turns off -headers, footers, and hyphenation; additionally, it will right justify text. .SH DEPENDENCIES .B grohtml -is dependent upon grops and gs. -If -.B grohtml -has been configured to generate gif files then it is further dependent upon, -ppmtogif, and ppmquant. -However if it has been configured to generate png files (the default) then -it is dependent upon gs having a png output device. +is dependent upon the png utilities and gs. Images are generated whenever a table, picture, equation or line is encountered. .SH BUGS -This is still very alpha. -At least three major bugs remain: -Firstly, -.B grohtml -sometimes miscalculates the end of an html table resulting in text which -appears twice. -Secondly equation numbers are not handled correctly. -Thirdly equation macros and pic macros can confuse -.BR grohtml . +.B Grohtml +has been completely redesigned and rewriten. +It is still alpha code. .SH "SEE ALSO" .BR afmtodit (@MAN1EXT@), .BR groff (@MAN1EXT@), diff --git a/src/devices/grohtml/html_chars.h b/src/devices/grohtml/html-chars.h index 76f094c83..f58f8dcc2 100755 --- a/src/devices/grohtml/html_chars.h +++ b/src/devices/grohtml/html-chars.h @@ -1,11 +1,11 @@ // -*- C++ -*- -/* Copyright (C) 2000 Free Software Foundation, Inc. +/* Copyright (C) 2000, 2001 Free Software Foundation, Inc. * * Gaius Mulley (gaius@glam.ac.uk) wrote output.cc * but it owes a huge amount of ideas and raw code from * James Clark (jjc@jclark.com) grops/ps.cc. * - * html_chars.h + * html-chars.h * * provides a diacritical character combination table for html */ diff --git a/src/devices/grohtml/html-text.cc b/src/devices/grohtml/html-text.cc new file mode 100644 index 000000000..1621085b2 --- /dev/null +++ b/src/devices/grohtml/html-text.cc @@ -0,0 +1,649 @@ +// -*- C++ -*- +/* Copyright (C) 2000, 2001 Free Software Foundation, Inc. + * + * Gaius Mulley (gaius@glam.ac.uk) wrote html-text.cc + * + * html-text.cc + * + * provide a troff like state machine interface which + * generates html text. + */ + +/* +This file is part of groff. + +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. */ + +#include "driver.h" +#include "stringclass.h" +#include "cset.h" + +#if !defined(TRUE) +# define TRUE (1==1) +#endif +#if !defined(FALSE) +# define FALSE (1==0) +#endif + + +#include "html-text.h" + + +html_text::html_text (simple_output *op) : + stackptr(NULL), lastptr(NULL), out(op), space_emitted(TRUE) +{ +} + +html_text::~html_text () +{ + flush_text(); +} + +/* + * end_tag - shuts down the tag. + */ + +void html_text::end_tag (tag_definition *t) +{ + switch (t->type) { + + case I_TAG: out->put_string("</i>"); break; + case B_TAG: out->put_string("</b>"); break; + case P_TAG: out->put_string("</p>"); out->write_newline(); out->enable_newlines(FALSE); break; + case SUB_TAG: out->put_string("</sub>"); break; + case SUP_TAG: out->put_string("</sup>"); break; + case TT_TAG: out->put_string("</tt>"); break; + case PRE_TAG: out->put_string("</pre>\n"); out->enable_newlines(TRUE); break; + case SMALL_TAG: out->put_string("</small>"); break; + case BIG_TAG: out->put_string("</big>"); break; + + default: + error("unrecognised tag"); + } +} + +/* + * issue_tag - writes out an html tag with argument. + */ + +void html_text::issue_tag (char *tagname, char *arg) +{ + if ((arg == 0) || (strlen(arg) == 0)) { + out->put_string(tagname); + out->put_string(">"); + } else { + out->put_string(tagname); + out->put_string(" "); + out->put_string(arg); + out->put_string(">"); + } +} + +/* + * start_tag - starts a tag. + */ + +void html_text::start_tag (tag_definition *t) +{ + switch (t->type) { + + case I_TAG: issue_tag("<i", t->arg1); break; + case B_TAG: issue_tag("<b", t->arg1); break; + case P_TAG: issue_tag("\n<p", t->arg1); out->enable_newlines(TRUE); break; + case SUB_TAG: issue_tag("<sub", t->arg1); break; + case SUP_TAG: issue_tag("<sup", t->arg1); break; + case TT_TAG: issue_tag("<tt", t->arg1); break; + case PRE_TAG: issue_tag("\n<pre", t->arg1); out->enable_newlines(FALSE); break; + case SMALL_TAG: issue_tag("<small", t->arg1); break; + case BIG_TAG: issue_tag("<big", t->arg1); break; + case BREAK_TAG: break; + + default: + error("unrecognised tag"); + } +} + +/* + * flush_text - flushes html tags which are outstanding on the html stack. + */ + +void html_text::flush_text (void) +{ + int notext=TRUE; + tag_definition *p=stackptr; + + while (stackptr != 0) { + notext = (notext && (! stackptr->text_emitted)); + if (! notext) { + end_tag(stackptr); + } + p = stackptr; + stackptr = stackptr->next; + free(p); + } + lastptr = NULL; +} + +/* + * is_present - returns TRUE if tag is already present on the stack. + */ + +int html_text::is_present (HTML_TAG t) +{ + tag_definition *p=stackptr; + + while (p != NULL) { + if (t == p->type) { + return( TRUE ); + } + p = p->next; + } + return( FALSE ); +} + +/* + * push_para - adds a new entry onto the html paragraph stack. + */ + +void html_text::push_para (HTML_TAG t, char *arg) +{ + tag_definition *p=(tag_definition *)malloc(sizeof(tag_definition)); + + p->type = t; + p->arg1 = arg; + p->text_emitted = FALSE; + + /* + * if t is a P_TAG or PRE_TAG make sure it goes on the end of the stack. + */ + + if (((t == P_TAG) || (t == PRE_TAG)) && (lastptr != NULL)) { + lastptr->next = p; + lastptr = p; + p->next = NULL; + } else { + p->next = stackptr; + if (stackptr == NULL) + lastptr = p; + stackptr = p; + } +} + +/* + * do_italic - changes to italic + */ + +void html_text::do_italic (void) +{ + done_bold(); + done_tt(); + if (! is_present(I_TAG)) { + push_para(I_TAG, ""); + } +} + +/* + * do_bold - changes to bold. + */ + +void html_text::do_bold (void) +{ + done_italic(); + done_tt(); + if (! is_present(B_TAG)) { + push_para(B_TAG, ""); + } +} + +/* + * do_tt - changes to teletype. + */ + +void html_text::do_tt (void) +{ + done_bold(); + done_italic(); + if (! is_present(TT_TAG)) { + push_para(TT_TAG, ""); + } +} + +/* + * do_pre - changes to preformated text. + */ + +void html_text::do_pre (void) +{ + done_bold(); + done_italic(); + done_tt(); + char *type = done_para(); + if (! is_present(PRE_TAG)) { + push_para(PRE_TAG, ""); + } +} + +/* + * is_in_pre - returns TRUE if we are currently within a preformatted + * <pre> block. + */ + +int html_text::is_in_pre (void) +{ + return( is_present(PRE_TAG) ); +} + +/* + * shutdown - shuts down an html tag. + */ + +char *html_text::shutdown (HTML_TAG t) +{ + char *arg=NULL; + + if (is_present(t)) { + tag_definition *p =stackptr; + tag_definition *temp =NULL; + int notext =TRUE; + + while ((stackptr != NULL) && (stackptr->type != t)) { + notext = (notext && (! stackptr->text_emitted)); + if (! notext) { + end_tag(stackptr); + } + + /* + * pop tag + */ + p = stackptr; + stackptr = stackptr->next; + if (stackptr == NULL) + lastptr = NULL; + + /* + * push tag onto temp stack + */ + p->next = temp; + temp = p; + } + + /* + * and examine stackptr + */ + if ((stackptr != NULL) && (stackptr->type == t)) { + if (stackptr->text_emitted) { + end_tag(stackptr); + } + if (t == P_TAG) { + arg = stackptr->arg1; + } + p = stackptr; + stackptr = stackptr->next; + if (stackptr == NULL) + lastptr = NULL; + free(p); + } + + /* + * and restore unaffected tags + */ + while (temp != NULL) { + push_para(temp->type, temp->arg1); + p = temp; + temp = temp->next; + free(p); + } + } + return( arg ); +} + +/* + * done_bold - shuts downs a bold tag. + */ + +void html_text::done_bold (void) +{ + shutdown(B_TAG); +} + +/* + * done_italic - shuts downs an italic tag. + */ + +void html_text::done_italic (void) +{ + shutdown(I_TAG); +} + +/* + * done_sup - shuts downs a sup tag. + */ + +void html_text::done_sup (void) +{ + shutdown(SUP_TAG); +} + +/* + * done_sub - shuts downs a sub tag. + */ + +void html_text::done_sub (void) +{ + shutdown(SUB_TAG); +} + +/* + * done_tt - shuts downs a tt tag. + */ + +void html_text::done_tt (void) +{ + shutdown(TT_TAG); +} + +/* + * done_pre - shuts downs a pre tag. + */ + +void html_text::done_pre (void) +{ + shutdown(PRE_TAG); +} + +/* + * done_small - shuts downs a small tag. + */ + +void html_text::done_small (void) +{ + shutdown(SMALL_TAG); +} + +/* + * done_big - shuts downs a big tag. + */ + +void html_text::done_big (void) +{ + shutdown(BIG_TAG); +} + +/* + * check_emit_text - ensures that all previous tags have been emitted (in order) + * before the text is written. + */ + +void html_text::check_emit_text (tag_definition *t) +{ + if ((t != NULL) && (! t->text_emitted)) { + check_emit_text(t->next); + t->text_emitted = TRUE; + start_tag(t); + } +} + +/* + * do_emittext - tells the class that text was written during the current tag. + */ + +void html_text::do_emittext (char *s, int length) +{ + if ((! is_present(P_TAG)) && (! is_present(PRE_TAG))) + do_para(""); + + if (is_present(BREAK_TAG)) { + int text = remove_break(); + check_emit_text(stackptr); + if (text) { + if (is_present(PRE_TAG)) { + out->put_string("\n"); + } else { + out->put_string("<br>\n"); + } + } + } else { + check_emit_text(stackptr); + } + out->put_string(s, length); + space_emitted = FALSE; +} + +/* + * do_para- starts a new paragraph + */ + +void html_text::do_para (char *arg) +{ + done_pre(); + if (! is_present(P_TAG)) { + remove_sub_sup(); + push_para(P_TAG, arg); + space_emitted = TRUE; + } +} + +/* + * done_para - shuts down a paragraph tag. + */ + +char *html_text::done_para (void) +{ + space_emitted = TRUE; + return( shutdown(P_TAG) ); +} + +/* + * do_space - issues an end of paragraph + */ + +void html_text::do_space (void) +{ + do_para(done_para()); + space_emitted = TRUE; +} + +/* + * do_break - issue a break tag. + */ + +void html_text::do_break (void) +{ + if (! is_present(PRE_TAG)) { + if (emitted_text()) { + if (! is_present(BREAK_TAG)) { + push_para(BREAK_TAG, ""); + } + } + } + space_emitted = TRUE; +} + +/* + * do_newline - issue a newline providing that we are inside a <pre> tag. + */ + +void html_text::do_newline (void) +{ + if (is_present(PRE_TAG)) { + do_emittext("\n", 1); + space_emitted = TRUE; + } +} + +/* + * emitted_text - returns FALSE if white space has just been written. + */ + +int html_text::emitted_text (void) +{ + return( ! space_emitted); +} + +/* + * emit_space - writes a space providing that text was written beforehand. + */ + +int html_text::emit_space (void) +{ + if (space_emitted) { + if (is_present(PRE_TAG)) { + do_emittext(" ", 1); + } + } else { + out->space_or_newline(); + space_emitted = TRUE; + } +} + +/* + * remove_tag - removes a tag from the stack. + */ + +void html_text::remove_tag (HTML_TAG tag) +{ + tag_definition *p = stackptr; + tag_definition *l = 0; + tag_definition *q = 0; + + while ((p != 0) && (p->type != tag)) { + l = p; + p = p->next; + } + if ((p != 0) && (p->type == tag)) { + if (p == stackptr) { + stackptr = stackptr->next; + if (stackptr == NULL) + lastptr = NULL; + q = stackptr; + } else if (l == 0) { + error("stack list pointers are wrong"); + } else { + l->next = p->next; + q = p->next; + if (l->next == NULL) + lastptr = l; + } + free(p); + } +} + +/* + * remove_sub_sup - removes a sub or sup tag, should either exist on the stack. + */ + +void html_text::remove_sub_sup (void) +{ + if (is_present(SUB_TAG)) { + remove_tag(SUB_TAG); + } + if (is_present(SUP_TAG)) { + remove_tag(SUP_TAG); + } + if (is_present(PRE_TAG)) { + remove_tag(PRE_TAG); + } +} + +/* + * remove_break - break tags are not balanced thus remove it once it has been emitted. + * It returns TRUE if text was emitted before the <br> was issued. + */ + +int html_text::remove_break (void) +{ + tag_definition *p = stackptr; + tag_definition *l = 0; + tag_definition *q = 0; + + while ((p != 0) && (p->type != BREAK_TAG)) { + l = p; + p = p->next; + } + if ((p != 0) && (p->type == BREAK_TAG)) { + if (p == stackptr) { + stackptr = stackptr->next; + if (stackptr == NULL) + lastptr = NULL; + q = stackptr; + } else if (l == 0) { + error("stack list pointers are wrong"); + } else { + l->next = p->next; + q = p->next; + if (l->next == NULL) + lastptr = l; + } + free(p); + } + /* + * now determine whether text was issued before <br> + */ + while (q != 0) { + if (q->text_emitted) { + return( TRUE ); + } else { + q = q->next; + } + } + return( FALSE ); +} + +/* + * do_small - potentially inserts a <small> tag into the html stream. + * However we check for a <big> tag, if present then we terminate it. + * Otherwise a <small> tag is inserted. + */ + +void html_text::do_small (void) +{ + if (is_present(BIG_TAG)) { + done_big(); + } else { + push_para(SMALL_TAG, ""); + } +} + +/* + * do_big - is the mirror image of do_small. + */ + +void html_text::do_big (void) +{ + if (is_present(SMALL_TAG)) { + done_small(); + } else { + push_para(BIG_TAG, ""); + } +} + +/* + * do_sup - save a superscript tag on the stack of tags. + */ + +void html_text::do_sup (void) +{ + push_para(SUP_TAG, ""); +} + +/* + * do_sub - save a subscript tag on the stack of tags. + */ + +void html_text::do_sub (void) +{ + push_para(SUB_TAG, ""); +} + diff --git a/src/devices/grohtml/html-text.h b/src/devices/grohtml/html-text.h new file mode 100644 index 000000000..b9164e6ed --- /dev/null +++ b/src/devices/grohtml/html-text.h @@ -0,0 +1,97 @@ +// -*- C++ -*- +/* Copyright (C) 2000, 2001 Free Software Foundation, Inc. + * + * Gaius Mulley (gaius@glam.ac.uk) wrote html-text.cc + * + * html-text.h + * + * provides a state machine interface which generates html text. + */ + +/* +This file is part of groff. + +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. */ + +#include "html.h" + +/* + * html tags + */ + +typedef enum {I_TAG, B_TAG, P_TAG, SUB_TAG, SUP_TAG, TT_TAG, PRE_TAG, SMALL_TAG, BIG_TAG, BREAK_TAG} HTML_TAG; + +typedef struct tag_definition { + HTML_TAG type; + char *arg1; + int text_emitted; + tag_definition *next; +} tag_definition ; + +/* + * the state of the current paragraph. + * It allows post-html.cc to request font changes, paragraph start/end + * and emits balanced tags with a small amount of peephole optimization. + */ + +class html_text { +public: + html_text (simple_output *op); + ~html_text (void); + void flush_text (void); + void do_emittext (char *s, int length); + void do_italic (void); + void do_bold (void); + void do_roman (void); + void do_tt (void); + void do_pre (void); + void do_small (void); + void do_big (void); + void do_para (char *arg1); + void do_sup (void); + void do_sub (void); + void do_space (void); + void do_break (void); + void do_newline (void); + void done_bold (void); + void done_italic (void); + char *done_para (void); + void done_sup (void); + void done_sub (void); + void done_tt (void); + void done_pre (void); + void done_small (void); + void done_big (void); + int emitted_text (void); + int emit_space (void); + int is_in_pre (void); + void remove_tag (HTML_TAG tag); + void remove_sub_sup (void); + +private: + tag_definition *stackptr; /* the current paragraph state */ + tag_definition *lastptr; /* the end of the stack */ + simple_output *out; + int space_emitted; + + int is_present (HTML_TAG t); + void end_tag (tag_definition *t); + void start_tag (tag_definition *t); + void push_para (HTML_TAG t, char *arg); + char *shutdown (HTML_TAG t); + void check_emit_text (tag_definition *t); + int remove_break (void); + void issue_tag (char *tagname, char *arg); +}; diff --git a/src/devices/grohtml/html.cc b/src/devices/grohtml/html.cc deleted file mode 100755 index b7acb6b78..000000000 --- a/src/devices/grohtml/html.cc +++ /dev/null @@ -1,6607 +0,0 @@ -// -*- C++ -*- -/* Copyright (C) 1999 Free Software Foundation, Inc. - * - * Gaius Mulley (gaius@glam.ac.uk) wrote grohtml - * but it owes a huge amount of ideas and raw code from - * James Clark (jjc@jclark.com) grops/ps.cc. - */ - -/* -This file is part of groff. - -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. */ - -#include "driver.h" -#include "stringclass.h" -#include "cset.h" - -#include "html.h" -#include "html_chars.h" -#include <time.h> - -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif - -extern "C" { - // SunOS 4.1.3 fails to declare this in stdlib.h - char *mktemp(char *); -} - -#include <stdio.h> -#include <fcntl.h> - -#ifndef _POSIX_VERSION - -#ifdef HAVE_LIMITS_H -#include <limits.h> -#endif /* HAVE_LIMITS_H */ - -#ifdef HAVE_DIRENT_H -#include <dirent.h> -#else /* not HAVE_DIRENT_H */ -#ifdef HAVE_SYS_DIR_H -#include <sys/dir.h> -#endif /* HAVE_SYS_DIR_H */ -#endif /* not HAVE_DIRENT_H */ - -#ifndef NAME_MAX -#ifdef MAXNAMLEN -#define NAME_MAX MAXNAMLEN -#else /* !MAXNAMLEN */ -#ifdef MAXNAMELEN -#define NAME_MAX MAXNAMELEN -#else /* !MAXNAMELEN */ -#define NAME_MAX 14 -#endif /* !MAXNAMELEN */ -#endif /* !MAXNAMLEN */ -#endif /* !NAME_MAX */ - -#endif /* not _POSIX_VERSION */ - -#include "nonposix.h" - -#include "ordered_list.h" - -#if !defined(TRUE) -# define TRUE (1==1) -#endif -#if !defined(FALSE) -# define FALSE (1==0) -#endif - -#define MAX_TEMP_NAME 1024 -#define MAX_STRING_LENGTH 4096 -#define MAX_CHAR_SIZE 50 // maximum length of character name - -#define Y_FUDGE_MARGIN +0.83 -#define A4_PAGE_LENGTH (11.6944-Y_FUDGE_MARGIN) -#define DEFAULT_IMAGE_RES 80 -#define IMAGE_BOARDER_PIXELS 10 -#define MAX_WORDS_PER_LINE 1000 // only used for table indentation -#define GAP_SPACES 3 // how many spaces needed to guess a gap? -#define GAP_WIDTH_ONE_LINE 2 // 1/GAP_WIDTH_ONE_LINE inches required for one line table -#define CENTER_TOLERANCE 2 // how many pixels off center will we think a line or region is centered -#define MIN_COLUMN 7 // minimum column size pixels for multiple lines -#define MIN_COLUMN_FOR_TWO_LINES 20 // minimum column size pixels for a 2 line table -#define MIN_TEXT_PERCENT 5 // try and round to this percentage value for used columns -#define PERCENT_THRESHOLD 20 // don't bother trying to increase and width greater than this - - -/* - * Only uncomment one of the following to determine default image type. - */ - -#define IMAGE_DEFAULT_PNG -/* #define IMAGE_DEFAULT_GIF */ - - -#if defined(IMAGE_DEFAULT_GIF) -static enum { gif, png } image_type = gif; -static char *image_device = "gif"; -#elif defined(IMAGE_DEFAULT_PNG) -static enum { gif, png } image_type = png; -static char *image_device = "png256"; -#else -# error "you must define either IMAGE_DEFAULT_GIF or IMAGE_DEFAULT_PNG" -#endif - -static int debug_on = FALSE; -static int guess_on = TRUE; -static int margin_on = FALSE; -static int auto_on = TRUE; -static int table_on = TRUE; -static int image_res = DEFAULT_IMAGE_RES; -static int debug_table_on = FALSE; -static int table_image_on = TRUE; // default is to create images for tbl - -static int linewidth = -1; - -#define DEFAULT_LINEWIDTH 40 /* in ems/1000 */ -#define MAX_LINE_LENGTH 72 -#define FILL_MAX 1000 - -void stop () {} - - -/* - * start with a few favorites - */ - -static int min (int a, int b) -{ - if (a < b) { - return( a ); - } else { - return( b ); - } -} - -static int max (int a, int b) -{ - if (a > b) { - return( a ); - } else { - return( b ); - } -} - -/* - * is_subsection - returns TRUE if a1..a2 is within b1..b2 - */ - -static int is_subsection (int a1, int a2, int b1, int b2) -{ - // easier to see whether this is not the case - return( !((a1 < b1) || (a1 > b2) || (a2 < b1) || (a2 > b2)) ); -} - -/* - * is_intersection - returns TRUE if range a1..a2 intersects with b1..b2 - */ - -static int is_intersection (int a1, int a2, int b1, int b2) -{ - // again easier to prove NOT outside limits - return( ! ((a1 > b2) || (a2 < b1)) ); -} - -/* - * is_digit - returns TRUE if character, ch, is a digit. - */ - -static int is_digit (char ch) -{ - return( (ch >= '0') && (ch <= '9') ); -} - -/* - * more_than_line_break - returns TRUE should v1 and v2 differ by more than - * a simple line break. - */ - -static int more_than_line_break (int v1, int v2, int size) -{ - return( abs(v1-v2)>size ); -} - -/* - * the class and methods for styles - */ - -struct style { - font *f; - int point_size; - int font_no; - int height; - int slant; - style (); - style (font *, int, int, int, int); - int operator == (const style &) const; - int operator != (const style &) const; -}; - -style::style() - : f(0) -{ -} - -style::style(font *p, int sz, int h, int sl, int no) - : f(p), point_size(sz), font_no(no), height(h), slant(sl) -{ -} - -int style::operator==(const style &s) const -{ - return (f == s.f && point_size == s.point_size - && height == s.height && slant == s.slant); -} - -int style::operator!=(const style &s) const -{ - return !(*this == s); -} - - -/* - * the class and methods for retaining ascii text - */ - -struct char_block { - enum { SIZE = 256 }; - char buffer[SIZE]; - int used; - char_block *next; - - char_block(); -}; - -char_block::char_block() -: used(0), next(0) -{ -} - -class char_buffer { -public: - char_buffer(); - ~char_buffer(); - char *add_string(char *, unsigned int); -private: - char_block *head; - char_block *tail; -}; - -char_buffer::char_buffer() -: head(0), tail(0) -{ -} - -char_buffer::~char_buffer() -{ - while (head != 0) { - char_block *temp = head; - head = head->next; - delete temp; - } -} - -char *char_buffer::add_string (char *s, unsigned int length) -{ - int i=0; - unsigned int old_used; - - if (tail == 0) { - tail = new char_block; - head = tail; - } else { - if (tail->used + length+1 > char_block::SIZE) { - tail->next = new char_block; - tail = tail->next; - } - } - // at this point we have a tail which is ready for the string. - if (tail->used + length+1 > char_block::SIZE) { - fatal("need to increase char_block::SIZE"); - } - - old_used = tail->used; - do { - tail->buffer[tail->used] = s[i]; - tail->used++; - i++; - length--; - } while (length>0); - - // add terminating nul character - - tail->buffer[tail->used] = '\0'; - tail->used++; - - // and return start of new string - - return( &tail->buffer[old_used] ); -} - -/* - * the classes and methods for maintaining pages and text positions and graphic regions - */ - -class text_glob { -public: - int is_less (text_glob *a, text_glob *b); - text_glob (style *s, char *string, unsigned int length, - int min_vertical, int min_horizontal, - int max_vertical, int max_horizontal, int is_command, int is_html); - text_glob (void); - ~text_glob (void); - - style text_style; - char *text_string; - unsigned int text_length; - int minv, maxv, minh, maxh; - int is_raw_command; // should the text be sent directly to the device? - int is_html_command; // is the raw command definitely for the html device ie not an eqn? -}; - -text_glob::text_glob (style *s, char *string, unsigned int length, - int min_vertical, int min_horizontal, - int max_vertical, int max_horizontal, int is_command, int is_html) - : text_style(*s), text_string(string), text_length(length), - minv(min_vertical), maxv(max_vertical), minh(min_horizontal), maxh(max_horizontal), - is_raw_command(is_command), is_html_command(is_html) -{ -} - -text_glob::text_glob () - : text_string(0), text_length(0), minv(-1), maxv(-1), minh(-1), maxh(-1), - is_raw_command(FALSE), is_html_command(FALSE) -{ -} - -text_glob::~text_glob () -{ -} - -int text_glob::is_less (text_glob *a, text_glob *b) -{ - if (is_intersection(a->minv+1, a->maxv-1, b->minv+1, b->maxv-1)) { - return( a->minh < b->minh ); - } else { - return( a->maxv < b->maxv ); - } -} - -struct xycoord { - int x; - int y; -}; - -class graphic_glob { -public: - int is_less (graphic_glob *a, graphic_glob *b); - graphic_glob (int troff_code); - graphic_glob (void); - ~graphic_glob (void); - - int minv, maxv, minh, maxh; - int xc, yc; - int nopoints; // number of points allocated in array below - struct xycoord *point; - int size; - int fill; - int code; -}; - -graphic_glob::graphic_glob () - : minv(-1), maxv(-1), minh(-1), maxh(-1), nopoints(0), point(0), size(0), code(0) -{ -} - -graphic_glob::~graphic_glob () -{ - if (point != 0) { - free(point); - } -} - -graphic_glob::graphic_glob (int troff_code) - : minv(-1), maxv(-1), minh(-1), maxh(-1), nopoints(0), point(0), size(0), code(troff_code) -{ -} - -int graphic_glob::is_less (graphic_glob *a, graphic_glob *b) -{ - return( (a->minv < b->minv) || ((a->minv == b->minv) && (a->minh < b->minh)) ); -} - -class region_glob { -public: - region_glob (void); - ~region_glob (void); - int is_less (region_glob *a, region_glob *b); - - int minv, maxv, minh, maxh; -}; - -int region_glob::is_less (region_glob *a, region_glob *b) -{ - return( (a->minv < b->minv) || ((a->minv == b->minv) && (a->minh < b->minh)) ); -} - -region_glob::region_glob (void) - : minv(-1), maxv(-1), minh(-1), maxh(-1) -{ -} - -region_glob::~region_glob (void) -{ -} - -class page { -public: - page (void); - void add (style *s, char *string, unsigned int length, - int min_vertical, int min_horizontal, - int max_vertical, int max_horizontal); - void add_html_command (style *s, char *string, unsigned int length, - int min_vertical, int min_horizontal, - int max_vertical, int max_horizontal); - void add_special_char (style *s, char *string, unsigned int length, - int min_vertical, int min_horizontal, - int max_vertical, int max_horizontal); - void add_line (int code, int x1, int y1, int x2, int y2, int size, int fill); - void add_arc (int code, int xc, int yc, int *p, double *c, int size, int fill); - void add_polygon (int code, int np, int *p, int oh, int ov, int size, int fill); - void add_spline (int code, int xc, int yc, int np, int *p, int size, int fill); - void calculate_region (void); - int is_in_region (graphic_glob *g); - int can_grow_region (graphic_glob *g); - void make_new_region (graphic_glob *g); - int has_line (region_glob *r); - int has_word (region_glob *r); - int no_raw_commands (int minv, int maxv); - - // and the data - - ordered_list <region_glob> regions; // squares of bitmapped pics,eqn,tbl's - ordered_list <text_glob> words; // position of words on page - ordered_list <graphic_glob> lines; // position of lines on page - char_buffer buffer; // all characters for this page - int is_in_graphic; // should graphics and words go below or above - ordered_list <text_glob> region_words; // temporary accumulation of words in a region - ordered_list <graphic_glob> region_lines; // (as above) and used so that we can determine - // the regions vertical limits -}; - -page::page() - : is_in_graphic(FALSE) -{ -} - -void page::add (style *s, char *string, unsigned int length, - int min_vertical, int min_horizontal, - int max_vertical, int max_horizontal) -{ - if (length > 0) { - text_glob *g=new text_glob(s, buffer.add_string(string, length), length, - min_vertical, min_horizontal, max_vertical, max_horizontal, FALSE, FALSE); - if (is_in_graphic) { - region_words.add(g); - } else { - words.add(g); - } - } -} - -/* - * add_html_command - it only makes sense to add html commands when we are not inside - * a graphical entity. - */ - -void page::add_html_command (style *s, char *string, unsigned int length, - int min_vertical, int min_horizontal, - int max_vertical, int max_horizontal) -{ - if ((length > 0) && (! is_in_graphic)) { - text_glob *g=new text_glob(s, buffer.add_string(string, length), length, - min_vertical, min_horizontal, max_vertical, max_horizontal, TRUE, TRUE); - words.add(g); - } -} - -/* - * add_special_char - it only makes sense to add special characters when we are inside - * a graphical entity. - */ - -void page::add_special_char (style *s, char *string, unsigned int length, - int min_vertical, int min_horizontal, - int max_vertical, int max_horizontal) -{ - if ((length > 0) && (is_in_graphic)) { - text_glob *g=new text_glob(s, buffer.add_string(string, length), length, - min_vertical, min_horizontal, max_vertical, max_horizontal, TRUE, FALSE); - region_words.add(g); - } -} - -void page::add_line (int code, int x1, int y1, int x2, int y2, int size, int fill) -{ - graphic_glob *g = new graphic_glob(code); - - g->minh = min(x1, x2); - g->maxh = max(x1, x2); - g->minv = min(y1, y2); - g->maxv = max(y1, y2); - g->point = (struct xycoord *)malloc(sizeof(xycoord)*2); - g->nopoints = 2; - g->point[0].x = x1 ; - g->point[0].y = y1 ; - g->point[1].x = x2 ; - g->point[1].y = y2 ; - g->xc = 0; - g->yc = 0; - g->size = size; - g->fill = fill; - - if (is_in_graphic) { - region_lines.add(g); - } else { - lines.add(g); - } -} - -/* - * assign_min_max_for_arc - works out the smallest box that will encompass an - * arc defined by: origin: g->xc, g->xc - * and vector (p[0], p[1]) and (p[2], p[3]) - */ - -void assign_min_max_for_arc (graphic_glob *g, int *p, double *c) -{ - int radius = (int) sqrt(c[0]*c[0]+c[1]*c[1]); - int xv1 = p[0]; - int yv1 = p[1]; - int xv2 = p[2]; - int yv2 = p[3]; - int x1 = g->xc+xv1; - int y1 = g->yc+yv1; - int x2 = g->xc+xv1+xv2; - int y2 = g->yc+yv1+yv2; - - // firstly lets use the 'circle' limitation - g->minh = x1-radius; - g->maxh = x1+radius; - g->minv = y1-radius; - g->maxv = y1+radius; - - // incidentally I'm sure there is a better way to do this, but I don't know it - // please can someone let me know or "improve" this function - - // 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)) { - // first vector in Q3 - if ((xv2>=0) && (yv2>=0)) { - // second in Q1 - g->maxh = x2; - g->minv = y1; - } else if ((xv2<0) && (yv2>=0)) { - // second in Q2 - g->maxh = x2; - g->minv = y1; - } else if ((xv2>=0) && (yv2<0)) { - // second in Q4 - g->minv = min(y1, y2); - } else if ((xv2<0) && (yv2<0)) { - // second in Q3 - if (x1>=x2) { - g->minh = x2; - g->maxh = x1; - g->minv = min(y1, y2); - g->maxv = max(y1, y2); - } else { - // xv2, yv2 could all be zero? - } - } - } else if ((xv1>=0) && (yv1<0)) { - // first vector in Q2 - if ((xv2>=0) && (yv2>=0)) { - // second in Q1 - g->maxh = max(x1, x2); - g->minh = min(x1, x2); - g->minv = y1; - } else if ((xv2<0) && (yv2>=0)) { - // second in Q2 - if (x1<x2) { - g->maxh = x2; - g->minh = x1; - g->minv = min(y1, y2); - g->maxv = max(y1, y2); - } else { - // otherwise almost full circle anyway - } - } else if ((xv2>=0) && (yv2<0)) { - // second in Q4 - g->minv = y2; - g->minh = x1; - } else if ((xv2<0) && (yv2<0)) { - // second in Q3 - g->minh = min(x1, x2); - } - } else if ((xv1<0) && (yv1<0)) { - // first vector in Q1 - if ((xv2>=0) && (yv2>=0)) { - // second in Q1 - if (x1<x2) { - g->minh = x1; - g->maxh = x2; - g->minv = min(y1, y2); - g->maxv = max(y1, y2); - } else { - // nearly full circle - } - } else if ((xv2<0) && (yv2>=0)) { - // second in Q2 - g->maxv = max(y1, y2); - } else if ((xv2>=0) && (yv2<0)) { - // second in Q4 - g->minv = min(y1, y2); - g->maxv = max(y1, y2); - g->minh = min(x1, x2); - } else if ((xv2<0) && (yv2<0)) { - // second in Q3 - g->minh = x2; - g->maxv = y1; - } - } else if ((xv1<0) && (yv1>=0)) { - // first vector in Q4 - if ((xv2>=0) && (yv2>=0)) { - // second in Q1 - g->maxh = max(x1, x2); - } else if ((xv2<0) && (yv2>=0)) { - // second in Q2 - g->maxv = max(y1, y2); - g->maxh = max(x1, x2); - } else if ((xv2>=0) && (yv2<0)) { - // second in Q4 - if (x1>=x2) { - g->minv = min(y1, y2); - g->maxv = max(y1, y2); - g->minh = min(x1, x2); - g->maxh = max(x2, x2); - } else { - // nearly full circle - } - } else if ((xv2<0) && (yv2<0)) { - // second in Q3 - g->maxv = max(y1, y2); - g->minh = min(x1, x2); - g->maxh = max(x1, x2); - } - } - // this should *never* happen but if it does it means a case above is wrong.. - - // this code is only present for safety sake - if (g->maxh < g->minh) { - if (debug_on) { - fprintf(stderr, "assert failed minh > maxh\n"); fflush(stderr); - // stop(); - } - g->maxh = g->minh; - } - if (g->maxv < g->minv) { - if (debug_on) { - fprintf(stderr, "assert failed minv > maxv\n"); fflush(stderr); - // stop(); - } - g->maxv = g->minv; - } -} - -void page::add_arc (int code, int xc, int yc, int *p, double *c, int size, int fill) -{ - graphic_glob *g = new graphic_glob(code); - - g->point = (struct xycoord *)malloc(sizeof(xycoord)*2); - g->nopoints = 2; - g->point[0].x = p[0] ; - g->point[0].y = p[1] ; - g->point[1].x = p[2] ; - g->point[1].y = p[3] ; - g->xc = xc; - g->yc = yc; - g->size = size; - g->fill = fill; - - assign_min_max_for_arc(g, p, c); - - if (is_in_graphic) { - region_lines.add(g); - } else { - lines.add(g); - } -} - - -void page::add_polygon (int code, int np, int *p, int oh, int ov, int size, int fill) -{ - graphic_glob *g = new graphic_glob(code); - int j = 0; - int i; - - g->point = (struct xycoord *)malloc(sizeof(xycoord)*np/2); - g->nopoints = np/2; - - for (i=0; i<g->nopoints; i++) { - g->point[i].x = p[j]; - j++; - g->point[i].y = p[j]; - j++; - } - // now calculate min/max - g->minh = g->point[0].x; - g->minv = g->point[0].y; - g->maxh = g->point[0].x; - g->maxv = g->point[0].y; - for (i=1; i<g->nopoints; i++) { - g->minh = min(g->minh, g->point[i].x); - g->minv = min(g->minv, g->point[i].y); - g->maxh = max(g->maxh, g->point[i].x); - g->maxv = max(g->maxv, g->point[i].y); - } - g->size = size; - g->xc = oh; - g->yc = ov; - g->fill = fill; - - if (is_in_graphic) { - region_lines.add(g); - } else { - lines.add(g); - } -} - -void page::add_spline (int code, int xc, int yc, int np, int *p, int size, int fill) -{ - graphic_glob *g = new graphic_glob(code); - int j = 0; - int i; - - g->point = (struct xycoord *)malloc(sizeof(xycoord)*np/2); - g->nopoints = np/2; - - for (i=0; i<g->nopoints; i++) { - g->point[i].x = p[j]; - j++; - g->point[i].y = p[j]; - j++; - } - // now calculate min/max - g->minh = min(g->point[0].x, g->point[0].x/2); - g->minv = min(g->point[0].y, g->point[0].y/2); - g->maxh = max(g->point[0].x, g->point[0].x/2); - g->maxv = max(g->point[0].y, g->point[0].y/2); - - /* tnum/tden should be between 0 and 1; the closer it is to 1 - the tighter the curve will be to the guiding lines; 2/3 - is the standard value */ - const int tnum = 2; - const int tden = 3; - - for (i=1; i<g->nopoints-1; i++) { - g->minh = min(g->minh, g->point[i].x*tnum/(2*tden)); - g->minv = min(g->minv, g->point[i].y*tnum/(2*tden)); - g->maxh = max(g->maxh, g->point[i].x*tnum/(2*tden)); - g->maxv = max(g->maxv, g->point[i].y*tnum/(2*tden)); - - g->minh = min(g->minh, g->point[i].x/2+(g->point[i+1].x*(tden-tden))/(2*tden)); - g->minv = min(g->minv, g->point[i].y/2+(g->point[i+1].y*(tden-tden))/(2*tden)); - g->maxh = max(g->maxh, g->point[i].x/2+(g->point[i+1].x*(tden-tden))/(2*tden)); - g->maxv = max(g->maxv, g->point[i].y/2+(g->point[i+1].y*(tden-tden))/(2*tden)); - - g->minh = min(g->minh, (g->point[i].x-g->point[i].x/2) + g->point[i+1].x/2); - g->minv = min(g->minv, (g->point[i].y-g->point[i].y/2) + g->point[i+1].y/2); - g->maxh = max(g->maxh, (g->point[i].x-g->point[i].x/2) + g->point[i+1].x/2); - g->maxv = max(g->maxv, (g->point[i].y-g->point[i].y/2) + g->point[i+1].y/2); - } - i = g->nopoints-1; - - g->minh = min(g->minh, (g->point[i].x-g->point[i].x/2)) + xc; - g->minv = min(g->minv, (g->point[i].y-g->point[i].y/2)) + yc; - g->maxh = max(g->maxh, (g->point[i].x-g->point[i].x/2)) + xc; - g->maxv = max(g->maxv, (g->point[i].y-g->point[i].y/2)) + yc; - - g->size = size; - g->xc = xc; - g->yc = yc; - g->fill = fill; - - if (is_in_graphic) { - region_lines.add(g); - } else { - lines.add(g); - } -} - -class html_font : public font { - html_font(const char *); -public: - int encoding_index; - char *encoding; - char *reencoded_name; - ~html_font(); - static html_font *load_html_font(const char *); -}; - -html_font *html_font::load_html_font(const char *s) -{ - html_font *f = new html_font(s); - if (!f->load()) { - delete f; - return 0; - } - return f; -} - -html_font::html_font(const char *nm) -: font(nm) -{ -} - -html_font::~html_font() -{ -} - -/* - * a simple class to contain the header to this document - */ - -class title_desc { -public: - title_desc (); - ~title_desc (); - - int has_been_written; - int has_been_found; - char text[MAX_STRING_LENGTH]; -}; - - -title_desc::title_desc () - : has_been_written(FALSE), has_been_found(FALSE) -{ -} - -title_desc::~title_desc () -{ -} - -class header_desc { -public: - header_desc (); - ~header_desc (); - - int no_of_headings; // how many headings have we found? - char_buffer headings; // all the headings used in the document - ordered_list <text_glob> headers; - int header_level; // current header level - int written_header; // have we written the header yet? - char header_buffer[MAX_STRING_LENGTH]; // current header text - - void write_headings (FILE *f); -}; - -header_desc::header_desc () - : no_of_headings(0), header_level(2), written_header(0) -{ -} - -header_desc::~header_desc () -{ -} - -/* - * paragraph_type - alignment for a new paragraph - */ - -typedef enum { left_alignment, center_alignment } paragraph_type; - -/* - * text_defn - defines the limit of text, initially these are stored in the - * column array as words. Later we examine the white space between - * the words in successive lines to find out whether we can detect - * distinct columns. The columns are generated via html tables. - */ - -struct text_defn { - int left; // the start of a word or text - int right; // the end of the text and beginning of white space - int is_used; // will this this column be used for words or space - int right_hits; // count of the number of words touching right position - int percent; // what percentage width should we use for this cell? -}; - -/* - * introduce a paragraph class so that we can nest paragraphs - * from plain html text and html tables. - */ - -class html_paragraph { -public: - html_paragraph (int in, int need, paragraph_type type, html_paragraph *prev); - ~html_paragraph (); - - int in_paragraph; - int need_paragraph; - paragraph_type para_type; - html_paragraph *previous; -}; - -/* - * html_paragraph - constructor, fill in the public fields. - */ - -html_paragraph::html_paragraph (int in, int need, paragraph_type type, html_paragraph *prev) - : in_paragraph(in), need_paragraph(need), - para_type(type), previous(prev) -{ -} - -/* - * html_paragraph - deconstructor - */ - -html_paragraph::~html_paragraph () -{ -} - -/* - * note that html_tables are currently only used to provide a better - * indentation mechanism for html text (in particular it allows grohtml - * to render .IP and .2C together with autoformatting). - */ - -class html_table { -public: - html_table (); - ~html_table (); - - int no_of_columns; // how many columns are we using? - struct text_defn *columns; // left and right margins for each column - int vertical_limit; // the limit of the table - int wrap_margin; // is the current rightmost margin able to wrap words? -}; - -html_table::html_table () - : no_of_columns(0), columns(0), vertical_limit(0), wrap_margin(0) -{ -} - -html_table::~html_table () -{ -} - -class html_printer : public printer { - FILE *tempfp; - simple_output html; - simple_output troff; - int res; - int postscript_res; - int space_char_index; - int no_of_printed_pages; - int paper_length; - enum { SBUF_SIZE = 8192 }; - char sbuf[SBUF_SIZE]; - int sbuf_len; - int sbuf_start_hpos; - int sbuf_vpos; - int sbuf_end_hpos; - int sbuf_kern; - style sbuf_style; - int sbuf_dmark_hpos; - style output_style; - int output_hpos; - int output_vpos; - int output_draw_point_size; - int line_thickness; - int output_line_thickness; - int fill; - unsigned char output_space_code; - string defs; - char *inside_font_style; - int page_number; - title_desc title; - header_desc header; - int header_indent; - page *page_contents; - html_table indentation; - int left_margin_indent; - int right_margin_indent; - int need_one_newline; - int issued_newline; - html_paragraph *current_paragraph; - char image_name[MAX_STRING_LENGTH]; - int image_number; - int graphic_level; - int supress_sub_sup; - - int start_region_vpos; - int start_region_hpos; - int end_region_vpos; - int end_region_hpos; - int cutoff_heading; - - struct graphic_glob *start_graphic; - struct text_glob *start_text; - - void flush_sbuf (); - void set_style (const style &); - void set_space_code (unsigned char c); - void do_exec (char *, const environment *); - void do_import (char *, const environment *); - void do_def (char *, const environment *); - void do_mdef (char *, const environment *); - void do_file (char *, const environment *); - void set_line_thickness (const environment *); - void change_font (text_glob *g, int is_to_html); - void terminate_current_font (void); - void flush_font (void); - void flush_page (void); - void add_char_to_sbuf (unsigned char code); - void add_to_sbuf (char code, const char *name); - void display_word (text_glob *g, int is_to_html); - void html_display_word (text_glob *g); - void troff_display_word (text_glob *g); - void display_line (graphic_glob *g, int is_to_html); - void display_fill (graphic_glob *g); - void calculate_margin (void); - void traverse_page_regions (void); - void dump_page (void); - int is_within_region (graphic_glob *g); - int is_within_region (text_glob *t); - int is_less (graphic_glob *g, text_glob *t); - void display_globs (int is_to_html); - void move_horizontal (text_glob *g, int left_margin); - void move_vertical (text_glob *g, paragraph_type p); - void write_html_font_face (const char *fontname, const char *left, const char *right); - void write_html_font_type (const char *fontname, const char *left, const char *right); - void html_change_font (text_glob *g, const char *fontname, int size); - char *html_position_text (text_glob *g, int left_margin, int right_margin); - int html_position_region (void); - void troff_change_font (const char *fontname, int size, int font_no); - void troff_position_text (text_glob *g); - int pretend_is_on_same_line (text_glob *g, int left_margin, int right_margin); - int is_on_same_line (text_glob *g, int vpos); - int looks_like_subscript (text_glob *g); - int looks_like_superscript (text_glob *g); - int looks_like_smaller_font (text_glob *g); - int looks_like_larger_font (text_glob *g); - void begin_paragraph (paragraph_type p); - void begin_paragraph_no_height (paragraph_type p); - void force_begin_paragraph (void); - void end_paragraph (void); - void save_paragraph (void); - void restore_paragraph (void); - void html_newline (void); - void convert_to_image (char *troff_src, char *image_name); - void write_title (int in_head); - void find_title (void); - int is_bold (text_glob *g); - void write_header (text_glob *g); - void determine_header_level (void); - void build_header (text_glob *g); - void make_html_indent (int indent); - int is_whole_line_bold (text_glob *g); - int is_a_header (text_glob *g); - int processed_header (text_glob *g); - void make_new_image_name (void); - void calculate_region_margins (region_glob *r); - void remove_redundant_regions (void); - void remove_duplicate_regions (void); - void move_region_to_page (void); - void calculate_region_range (graphic_glob *r); - void flush_graphic (void); - void write_string (graphic_glob *g, int is_to_html); - void prologue (void); - int gs_x (int x); - int gs_y (int y); - void display_regions (void); - int check_able_to_use_table (text_glob *g); - int using_table_for_indent (void); - int collect_columns (struct text_defn *next_words, struct text_defn *next_cols, - struct text_defn *last_words, struct text_defn *last_cols, - int max_words); - void include_into_list (struct text_defn *line, struct text_defn *item); - int is_in_column (struct text_defn *line, struct text_defn *item, int max_words); - int is_column_match (struct text_defn *match, struct text_defn *line1, - struct text_defn *line2, int max_words); - int count_columns (struct text_defn *line); - void rewind_text_to (text_glob *g); - int found_use_for_table (text_glob *start); - void column_display_word (int cell, int vert, int left, int right, int next); - void start_table (void); - void end_table (void); - void foreach_column_include_text (text_glob *start); - void define_cell (int i); - int column_calculate_left_margin (int left, int right); - int column_calculate_right_margin (int left, int right); - void display_columns (const char *word, const char *name, text_defn *line); - void calculate_right (struct text_defn *line, int max_words); - void determine_right_most_column (struct text_defn *line, int max_words); - int remove_white_using_words (struct text_defn *next_guess, struct text_defn *last_guess, struct text_defn *next_line); - void copy_line (struct text_defn *dest, struct text_defn *src); - void combine_line (struct text_defn *dest, struct text_defn *src); - int conflict_with_words (struct text_defn *column_guess, struct text_defn *words); - void remove_entry_in_line (struct text_defn *line, int j); - void remove_redundant_columns (struct text_defn *line); - void add_column_gaps (struct text_defn *line); - int continue_searching_column (text_defn *next_col, text_defn *last_col, text_defn *all_words); - void add_right_full_width (struct text_defn *line, int mingap); - int is_continueous_column (text_defn *last_col, text_defn *next_line); - int is_exact_left (text_defn *last_col, text_defn *next_line); - int find_column_index_in_line (text_glob *t, text_defn *line); - void emit_space (text_glob *g, int force_space); - int is_in_middle (int left, int right); - int check_able_to_use_center (text_glob *g); - void write_centered_line (text_glob *g); - int single_centered_line (text_defn *first, text_defn *second, text_glob *g); - int determine_row_limit (text_glob *start, int v); - void assign_used_columns (text_glob *start); - int find_column_index (text_glob *t); - int large_enough_gap (text_defn *last_col); - int is_worth_column (int left, int right); - int is_subset_of_columns (text_defn *a, text_defn *b); - void count_hits (text_defn *col, int no_of_columns, int limit); - void count_right_hits (text_defn *col, int no_of_columns); - int calculate_min_gap (text_glob *g); - int right_indentation (struct text_defn *last_guess); - void calculate_percentage_width (text_glob *start); - int able_to_steal_width (void); - int need_to_steal_width (void); - int can_distribute_fairly (void); - void utilize_round_off (void); - int will_wrap_text (int i, text_glob *start); - int next_line_on_left_column (int i, text_glob *start); - void remove_table_column (int i); - void remove_unnecessary_unused (text_glob *start); - int is_small_table (int lines, struct text_defn *last_guess, - struct text_defn *words_1, struct text_defn *cols_1, - struct text_defn *words_2, struct text_defn *cols_2, - int *limit, int *limit_1); - int is_column_subset (struct text_defn *cols_1, struct text_defn *cols_2); - int is_appropriate_to_start_table (struct text_defn *cols_1, struct text_defn *cols_2, - struct text_defn *last_guess); - int is_a_full_width_column (void); - int right_most_column (struct text_defn *col); - int large_enough_gap_for_two (struct text_defn *col); - void remove_zero_percentage_column (void); - void translate_to_html (text_glob *g); - int html_knows_about (char *troff); - void determine_diacritical_mark (const char *name, const environment *env); - int sbuf_continuation (unsigned char code, const char *name, const environment *env, int w); - char *remove_last_char_from_sbuf (); - const char *check_diacritical_combination (unsigned char code, const char *name); - int seen_backwards_escape (char *s, int l); - int should_defer_table (int lines, struct text_glob *start, struct text_defn *cols_1); - int is_new_exact_right (struct text_defn *last_guess, struct text_defn *last_cols, struct text_defn *next_cols); - void issue_left_paragraph (void); - void adjust_margin_percentages (void); - int total_percentages (void); - int get_left (void); - void can_loose_column (text_glob *start, struct text_defn *last_guess, int limit); - int check_lack_of_hits (struct text_defn *next_guess, struct text_defn *last_guess, text_glob *start, int limit); - int is_in_table (void); - - // ADD HERE - -public: - html_printer(); - ~html_printer(); - void set_char(int i, font *f, const environment *env, int w, const char *name); - void draw(int code, int *p, int np, const environment *env); - void begin_page(int); - void end_page(int); - void special(char *arg, const environment *env); - font *make_font(const char *); - void end_of_line(); -}; - -html_printer::html_printer() -: html(0, MAX_LINE_LENGTH), - troff(0, MAX_LINE_LENGTH), - no_of_printed_pages(0), - sbuf_len(0), - sbuf_dmark_hpos(-1), - output_hpos(-1), - output_vpos(-1), - line_thickness(-1), - fill(FILL_MAX + 1), - inside_font_style(0), - page_number(0), - header_indent(-1), - left_margin_indent(0), - right_margin_indent(0), - need_one_newline(0), - issued_newline(0), - image_number(0), - graphic_level(0), - supress_sub_sup(TRUE), - start_region_vpos(0), - start_region_hpos(0), - end_region_vpos(0), - end_region_hpos(0), - cutoff_heading(100) -{ - tempfp = xtmpfile(); - html.set_file(tempfp); - if (linewidth < 0) - linewidth = DEFAULT_LINEWIDTH; - if (font::hor != 1) - fatal("horizontal resolution must be 1"); - if (font::vert != 1) - fatal("vertical resolution must be 1"); -#if 0 - // should be sorted html.. - if (font::res % (font::sizescale*72) != 0) - fatal("res must be a multiple of 72*sizescale"); -#endif - int r = font::res; - int point = 0; - while (r % 10 == 0) { - r /= 10; - point++; - } - res = r; - html.set_fixed_point(point); - space_char_index = font::name_to_index("space"); - paper_length = font::paperlength; - if (paper_length == 0) - paper_length = 11*font::res; - page_contents = new page; - - postscript_res = 72000; - current_paragraph = new html_paragraph(FALSE, FALSE, left_alignment, 0); -} - -/* - * add_char_to_sbuf - adds a single character to the sbuf. - */ - -void html_printer::add_char_to_sbuf (unsigned char code) -{ - if (sbuf_len < SBUF_SIZE) { - sbuf[sbuf_len] = code; - sbuf_len++; - } else { - fatal("need to increase SBUF_SIZE"); - } -} - -/* - * add_to_sbuf - adds character code or name to the sbuf. - * It escapes \ with \\ - * We need to preserve the name of characters if they exist - * because we may need to send this character to two different - * devices: html and postscript. - */ - -void html_printer::add_to_sbuf (char code, const char *name) -{ - if (name == 0) { - if (code == '\\') { - add_char_to_sbuf('\\'); - } - add_char_to_sbuf(code); - } else { - int l=strlen(name); - int i=0; - - add_char_to_sbuf('\\'); - add_char_to_sbuf('('); - while (i<l) { - if (name[i] == '\\') { - add_char_to_sbuf('\\'); - } - add_char_to_sbuf(name[i]); - i++; - } - add_char_to_sbuf('\\'); - add_char_to_sbuf(')'); - } -} - -int html_printer::sbuf_continuation (unsigned char code, const char *name, - const environment *env, int w) -{ - if ((sbuf_end_hpos == env->hpos) || (sbuf_dmark_hpos == env->hpos)) { - name = check_diacritical_combination(code, name); - add_to_sbuf(code, name); - determine_diacritical_mark(name, env); - sbuf_end_hpos += w + sbuf_kern; - return( TRUE ); - } else { - if ((sbuf_len < SBUF_SIZE-1) && (env->hpos >= sbuf_end_hpos) && - ((sbuf_kern == 0) || (sbuf_end_hpos - sbuf_kern != env->hpos))) { - /* - * lets see whether a space is needed or not - */ - int space_width = sbuf_style.f->get_space_width(sbuf_style.point_size); - - if (env->hpos-sbuf_end_hpos < space_width) { - name = check_diacritical_combination(code, name); - add_to_sbuf(code, name); - determine_diacritical_mark(name, env); - sbuf_end_hpos = env->hpos + w; - return( TRUE ); - } - } else if ((sbuf_len > 0) && (sbuf_dmark_hpos)) { - /* - * check whether the diacritical mark is on the same character - */ - int space_width = sbuf_style.f->get_space_width(sbuf_style.point_size); - - if (abs(sbuf_dmark_hpos-env->hpos) < space_width) { - name = check_diacritical_combination(code, name); - add_to_sbuf(code, name); - determine_diacritical_mark(name, env); - sbuf_end_hpos = env->hpos + w; - return( TRUE ); - } - } - } - return( FALSE ); -} - -/* - * seen_backwards_escape - returns TRUE if we can see a escape at position i..l in s - */ - -int html_printer::seen_backwards_escape (char *s, int l) -{ - /* - * this is tricky so it is broken into components for clarity - * (we let the compiler put in all back into a complex expression) - */ - if ((l>0) && (sbuf[l] == '(') && (sbuf[l-1] == '\\')) { - /* - * ok seen '\(' but we must now check for '\\(' - */ - if ((l>1) && (sbuf[l-2] == '\\')) { - /* - * escaped the escape - */ - return( FALSE ); - } else { - return( TRUE ); - } - } else { - return( FALSE ); - } -} - -/* - * reverse - return reversed string. - */ - -char *reverse (char *s) -{ - int i=0; - int j=strlen(s)-1; - char t; - - while (i<j) { - t = s[i]; - s[i] = s[j]; - s[j] = t; - i++; - j--; - } - return( s ); -} - -/* - * remove_last_char_from_sbuf - removes the last character from sbuf. - */ - -char *html_printer::remove_last_char_from_sbuf () -{ - int l=sbuf_len; - static char last[MAX_STRING_LENGTH]; - - if (l>0) { - l--; - if ((sbuf[l] == ')') && (l>0) && (sbuf[l-1] == '\\')) { - /* - * found terminating escape - */ - int i=0; - - l -= 2; - while ((l>0) && (! seen_backwards_escape(sbuf, l))) { - if (sbuf[l] == '\\') { - if (sbuf[l-1] == '\\') { - last[i] = sbuf[l]; - i++; - l--; - } - } else { - last[i] = sbuf[l]; - i++; - } - l--; - } - last[i] = (char)0; - sbuf_len = l; - if (seen_backwards_escape(sbuf, l)) { - sbuf_len--; - } - return( reverse(last) ); - } else { - if ((sbuf[l] == '\\') && (l>0) && (sbuf[l-1] == '\\')) { - l -= 2; - sbuf_len = l; - return( "\\" ); - } else { - sbuf_len--; - last[0] = sbuf[sbuf_len]; - last[1] = (char)0; - return( last ); - } - } - } else { - return( NULL ); - } -} - -/* - * check_diacriticial_combination - checks to see whether the character code - * if combined with the previous diacriticial mark - * forms a new character. - */ - -const char *html_printer::check_diacritical_combination (unsigned char code, const char *name) -{ - static char troff_char[2]; - - if ((name == 0) && (sbuf_dmark_hpos >= 0)) { - // last character was a diacritical mark - char *last = remove_last_char_from_sbuf(); - - int i=0; - int j; - - while (diacritical_table[i].mark != NULL) { - if (strcmp(diacritical_table[i].mark, last) == 0) { - j=0; - while ((diacritical_table[i].second_troff_char[j] != (char)0) && - (diacritical_table[i].second_troff_char[j] != code)) { - j++; - } - if (diacritical_table[i].second_troff_char[j] == code) { - troff_char[0] = diacritical_table[i].translation; - troff_char[1] = code; - troff_char[2] = (char)0; - return( troff_char ); - } - } - i++; - } - add_to_sbuf(last[0], last); - } - return( name ); -} - -/* - * determine_diacritical_mark - if name is a diacriticial mark the record the position. - * --fixme-- is there a better way of doing this - * this must be done in troff somewhere. - */ - -void html_printer::determine_diacritical_mark (const char *name, const environment *env) -{ - if (name != 0) { - int i=0; - - while (diacritical_table[i].mark != NULL) { - if (strcmp(name, diacritical_table[i].mark) == 0) { - sbuf_dmark_hpos = env->hpos; - return; - } - i++; - } - } - sbuf_dmark_hpos = -1; -} - -/* - * set_char - adds a character into the sbuf if it is a continuation with the previous - * word otherwise flush the current sbuf and add character anew. - */ - -void html_printer::set_char(int i, font *f, const environment *env, int w, const char *name) -{ - unsigned char code = f->get_code(i); - -#if 0 - if (code == ' ') { - stop(); - } -#endif - style sty(f, env->size, env->height, env->slant, env->fontno); - if (sty.slant != 0) { - if (sty.slant > 80 || sty.slant < -80) { - error("silly slant `%1' degrees", sty.slant); - sty.slant = 0; - } - } - if ((name != 0) && (page_contents->is_in_graphic)) { - flush_sbuf(); - int r=font::res; // resolution of the device - page_contents->add_special_char(&sty, (char *)name, strlen(name), - env->vpos-sty.point_size*r/72, env->hpos, - env->vpos , env->hpos+w); - sbuf_end_hpos = env->hpos + w; - sbuf_start_hpos = env->hpos; - sbuf_vpos = env->vpos; - sbuf_style = sty; - sbuf_kern = 0; - } else { - if ((sbuf_len > 0) && (sbuf_len < SBUF_SIZE) && (sty == sbuf_style) && - (sbuf_vpos == env->vpos) && (sbuf_continuation(code, name, env, w))) { - return; - } else { - flush_sbuf(); - sbuf_len = 0; - add_to_sbuf(code, name); - determine_diacritical_mark(name, env); - sbuf_end_hpos = env->hpos + w; - sbuf_start_hpos = env->hpos; - sbuf_vpos = env->vpos; - sbuf_style = sty; - sbuf_kern = 0; - } - } -} - -/* - * file_name_max - return the maximum file-name length permitted - * by the underlying filesystem. - * - * (Code shamelessly stolen from indxbib/dirnamemax.c.) - */ - -static size_t -file_name_max (const char *fname) -{ -#ifdef _POSIX_VERSION - return pathconf (fname, _PC_NAME_MAX); -#else - return NAME_MAX; -#endif -} - - -/* - * make_new_image_name - creates a new file name ready for a image file. - */ - -void html_printer::make_new_image_name (void) -{ - image_number++; - - if ((current_filename == 0) || - (strcmp(current_filename, "<standard input>") == 0) || - (strcmp(current_filename, "-") == 0) || - (strcspn(current_filename, DIR_SEPS) < strlen(current_filename))) { - if (file_name_max(".") > 14) - sprintf(image_name, "groff-html-%d-%ld", image_number, (long)getpid()); - else - // The "-gh" part might be truncated on MS-DOS, but there's enough - // space for the PID and up to 99 image numbers. That's why "-gh" - // comes last. - sprintf(image_name, "%d-%ld-gh", image_number, (long)getpid()); - } else if (file_name_max(".") > 14) { - sprintf(image_name, "%s-%d-%ld", current_filename, image_number, (long)getpid()); - } else { // see the commentary above - sprintf(image_name, "%d-%ld-%s", - image_number, (long)getpid(), current_filename); - // Make sure image_name does not have a dot in its trunk, since - // convert_to_image will append .gif or .png to it, and DOS doesn't - // allow more than a single dot in a file name. - int i = strlen(image_name); - for ( ; i > 0; i--) { - if (strchr(DIR_SEPS, image_name[i - 1])) - break; - if (image_name[i - 1] == '.') { - image_name[i - 1] = '\0'; - break; - } - } - } -} - -/* - * write_title - writes the title to this document - */ - -void html_printer::write_title (int in_head) -{ - if (title.has_been_found) { - if (in_head) { - html.put_string("<title>"); - html.put_string(title.text); - html.put_string("</title>\n"); - } else { - title.has_been_written = TRUE; - html.put_string("<h1 align=center>"); - html.put_string(title.text); - html.put_string("</h1>\n"); - } - } -} - -/* - * get_html_translation - given the position of the character and its name - * return the device encoding for such character. - */ - -char *get_html_translation (font *f, char *name) -{ - int index; - - if ((f == 0) || (name == 0) || (strcmp(name, "") == 0)) { - return( NULL ); - } else { - index = f->name_to_index(name); - if (index == 0) { - error("character `%s' not found", name); - return( NULL ); - } else { - return( (char *)f->get_special_device_encoding(index) ); - } - } -} - -/* - * char_translate_to_html - convert a single non escaped character - * into the appropriate html character. - */ - -int char_translate_to_html (font *f, char *buf, int buflen, char ch, int b, int and_single) -{ - if (and_single) { - int t, l; - char *translation; - char name[2]; - - name[0] = ch; - name[1] = (char)0; - translation = get_html_translation(f, name); - if (translation) { - l = strlen(translation); - t = max(0, min(l, buflen-b)); - strncpy(&buf[b], translation, t); - b += t; - } else { - if (b<buflen) { - buf[b] = ch; - b++; - } - } - } else { - /* - * do not attempt to encode single characters - */ - if (b<buflen) { - buf[b] = ch; - b++; - } - } - return( b ); -} - -/* - * str_translate_to_html - converts a string, str, into html text. It places - * the output input buffer, buf. It truncates string, str, if - * there is not enough space in buf. - * It looks up the html character encoding of single characters - * if, and_single, is TRUE. Characters such as < > & etc. - */ - -void str_translate_to_html (font *f, char *buf, int buflen, char *str, int len, int and_single) -{ - char *translation; - int e; - char escaped_char[MAX_STRING_LENGTH]; - int l; - int i=0; - int b=0; - int t=0; - -#if 0 - if (strcmp(str, "``@,;:\\\\()[]''") == 0) { - stop(); - } -#endif - while (str[i] != (char)0) { - if ((str[i]=='\\') && (i+1<len)) { - i++; // skip the leading backslash - if (str[i] == '(') { - // start of escape - i++; - e = 0; - while ((str[i] != (char)0) && - (! ((str[i] == '\\') && (i+1<len) && (str[i+1] == ')')))) { - if (str[i] == '\\') { - i++; - } - escaped_char[e] = str[i]; - e++; - i++; - } - if ((str[i] == '\\') && (i+1<len) && (str[i+1] == ')')) { - i += 2; - } - escaped_char[e] = (char)0; - if (e > 0) { - translation = get_html_translation(f, escaped_char); - if (translation) { - l = strlen(translation); - t = max(0, min(l, buflen-b)); - strncpy(&buf[b], translation, t); - b += t; - } else { - int index=f->name_to_index(escaped_char); - - if (index != 0) { - buf[b] = f->get_code(index); - b++; - } - } - } - } else { - b = char_translate_to_html(f, buf, buflen, str[i], b, and_single); - i++; - } - } else { - b = char_translate_to_html(f, buf, buflen, str[i], b, and_single); - i++; - } - } - buf[min(b, buflen)] = (char)0; -} - -/* - * find_title - finds a title to this document, if it exists. - */ - -void html_printer::find_title (void) -{ - text_glob *t; - int r=font::res; - int removed_from_head; - char buf[MAX_STRING_LENGTH]; - - if ((page_number == 1) && (guess_on)) { - if (! page_contents->words.is_empty()) { - - int end_title_hpos = 0; - int start_title_vpos = 0; - int found_title_start = FALSE; - int height = 0; - int start_region =-1; - - if (! page_contents->regions.is_empty()) { - region_glob *r; - - page_contents->regions.start_from_head(); - r = page_contents->regions.get_data(); - if (r->minv > 0) { - start_region = r->minv; - } - } - - page_contents->words.start_from_head(); - do { - t = page_contents->words.get_data(); - removed_from_head = FALSE; - if ((found_title_start) && (start_region != -1) && (t->maxv >= start_region)) { - /* - * we have just encountered the first graphic region so - * we stop looking for a title. - */ - title.has_been_found = TRUE; - return; - } else if (t->is_raw_command) { - // skip raw commands - page_contents->words.move_right(); // move onto next word - } else if ((!found_title_start) && (t->minh > left_margin_indent) && - ((start_region == -1) || (t->maxv < start_region))) { - start_title_vpos = t->minv; - end_title_hpos = t->minh; - str_translate_to_html(t->text_style.f, buf, MAX_STRING_LENGTH, t->text_string, t->text_length, TRUE); - strcpy((char *)title.text, buf); - height = t->text_style.point_size*r/72; - found_title_start = TRUE; - page_contents->words.sub_move_right(); - removed_from_head = ((!page_contents->words.is_empty()) && - (page_contents->words.is_equal_to_head())); - } else if (found_title_start) { - if ((t->minv == start_title_vpos) || - ((!more_than_line_break(start_title_vpos, t->minv, (height*3)/2)) && - (t->minh > left_margin_indent)) || - (is_bold(t) && (t->minh > left_margin_indent))) { - start_title_vpos = min(t->minv, start_title_vpos); - end_title_hpos = max(t->maxh, end_title_hpos); - strcat(title.text, " "); - str_translate_to_html(t->text_style.f, buf, MAX_STRING_LENGTH, t->text_string, t->text_length, TRUE); - strcat(title.text, buf); - page_contents->words.sub_move_right(); - removed_from_head = ((!page_contents->words.is_empty()) && - (page_contents->words.is_equal_to_head())); - } else { - // end of title - title.has_been_found = TRUE; - return; - } - } else if (t->minh <= left_margin_indent) { - // no margin exists - return; - } else { - // move onto next word - page_contents->words.move_right(); - } - } while ((! page_contents->words.is_equal_to_head()) || (removed_from_head)); - } - } -} - -/* - * html_newline - generates a newline <br> - */ - -void html_printer::html_newline (void) -{ - int r = font::res; - int height = output_style.point_size*r/72; - - if (current_paragraph->in_paragraph) { - // safe to generate a pretty newline - html.put_string("<br>\n"); - } else { - html.put_string("<br>"); - } - output_vpos += height; - issued_newline = TRUE; -} - -/* - * issue_left_paragraph - emits a left paragraph together with appropriate - * margin if header_indent is < left_margin_indent. - */ - -void html_printer::issue_left_paragraph (void) -{ - if ((header_indent < left_margin_indent) && (! using_table_for_indent())) { - html.put_string("<p style=\"margin-left: "); - html.put_number(((left_margin_indent-header_indent)*100)/(right_margin_indent-header_indent)); - html.put_string("%\">"); - } else { - html.put_string("<p>"); - } -} - -/* - * force_begin_paragraph - force the begin_paragraph to be emitted. - */ - -void html_printer::force_begin_paragraph (void) -{ - if (current_paragraph->in_paragraph && current_paragraph->need_paragraph) { - switch (current_paragraph->para_type) { - - case left_alignment: issue_left_paragraph(); - break; - case center_alignment: html.put_string("<p align=center>"); - break; - default: fatal("unknown paragraph alignment type"); - } - current_paragraph->need_paragraph = FALSE; - } -} - -/* - * begin_paragraph - starts a new paragraph. It does nothing if a paragraph - * has already been started. - */ - -void html_printer::begin_paragraph (paragraph_type p) -{ - if (! current_paragraph->in_paragraph) { - int r = font::res; - int height = output_style.point_size*r/72; - - if (output_vpos >=0) { - // we leave it alone if it is set to the top of page - output_vpos += height; - } - current_paragraph->need_paragraph = TRUE; // delay the <p> just in case we don't actually emit text - current_paragraph->in_paragraph = TRUE; - current_paragraph->para_type = p; - issued_newline = TRUE; - } -} - - -/* - * begin_paragraph_no_height - starts a new paragraph. It does nothing if a paragraph - * has already been started. Note it does not alter output_vpos. - */ - -void html_printer::begin_paragraph_no_height (paragraph_type p) -{ - if (! current_paragraph->in_paragraph) { - current_paragraph->need_paragraph = TRUE; // delay the <p> just in case we don't actually emit text - current_paragraph->in_paragraph = TRUE; - current_paragraph->para_type = p; - issued_newline = TRUE; - } -} - -/* - * end_paragraph - end the current paragraph. It does nothing if a paragraph - * has not been started. - */ - -void html_printer::end_paragraph (void) -{ - if (current_paragraph->in_paragraph) { - // check whether we have generated any text inbetween the potential paragraph begin end - if (! current_paragraph->need_paragraph) { - int r = font::res; - int height = output_style.point_size*r/72; - - output_vpos += height; - terminate_current_font(); - html.put_string("</p>\n"); - } else { - terminate_current_font(); - } - current_paragraph->para_type = left_alignment; - current_paragraph->in_paragraph = FALSE; - } -} - -/* - * save_paragraph - saves the current paragraph state and - * creates new paragraph state. - */ - -void html_printer::save_paragraph (void) -{ - if (current_paragraph == 0) { - fatal("current_paragraph is NULL"); - } - current_paragraph = new html_paragraph(current_paragraph->in_paragraph, - current_paragraph->need_paragraph, - current_paragraph->para_type, - current_paragraph); - terminate_current_font(); -} - -/* - * restore_paragraph - restores the previous paragraph state. - */ - -void html_printer::restore_paragraph (void) -{ - html_paragraph *old = current_paragraph; - - current_paragraph = current_paragraph->previous; - free(old); -} - -/* - * calculate_margin - runs through the words and graphics globs - * and finds the start of the left most margin. - */ - -void html_printer::calculate_margin (void) -{ - text_glob *w; - graphic_glob *g; - - // remove margin - - right_margin_indent = 0; - - if (! page_contents->words.is_empty()) { - - // firstly check the words to determine the right margin - - page_contents->words.start_from_head(); - do { - w = page_contents->words.get_data(); - if ((w->maxh >= 0) && (w->maxh > right_margin_indent)) { - right_margin_indent = w->maxh; -#if 0 - if (right_margin_indent == 758) stop(); -#endif - } - page_contents->words.move_right(); - } while (! page_contents->words.is_equal_to_head()); - - /* - * only examine graphics if no words present - */ - if (! page_contents->lines.is_empty()) { - // now check for diagrams for right margin - page_contents->lines.start_from_head(); - do { - g = page_contents->lines.get_data(); - if ((g->maxh >= 0) && (g->maxh > right_margin_indent)) { - right_margin_indent = g->maxh; -#if 0 - if (right_margin_indent == 950) stop(); -#endif - } - page_contents->lines.move_right(); - } while (! page_contents->lines.is_equal_to_head()); - } - - - /* - * now we know the right margin lets do the same to find left margin - */ - - if (header_indent == -1) { - header_indent = right_margin_indent; - } - left_margin_indent = right_margin_indent; - - if (! page_contents->words.is_empty()) { - do { - w = page_contents->words.get_data(); - if ((w->minh >= 0) && (w->minh < left_margin_indent)) { - if (! is_a_header(w) && (! w->is_raw_command)) { - left_margin_indent = w->minh; - } - } - page_contents->words.move_right(); - } while (! page_contents->words.is_equal_to_head()); - } - - /* - * only examine graphic for margins if text yields nothing - */ - - if (! page_contents->lines.is_empty()) { - // now check for diagrams - page_contents->lines.start_from_head(); - do { - g = page_contents->lines.get_data(); - if ((g->minh >= 0) && (g->minh < left_margin_indent)) { - left_margin_indent = g->minh; - } - page_contents->lines.move_right(); - } while (! page_contents->lines.is_equal_to_head()); - } - } -} - -/* - * calculate_region - runs through the graphics globs and text globs - * and ensures that all graphic routines - * are defined by the region lists. - * This then allows us to easily - * determine the range of vertical and - * horizontal boundaries for pictures, - * tbl's and eqn's. - * - */ - -void page::calculate_region (void) -{ - graphic_glob *g; - - if (! lines.is_empty()) { - lines.start_from_head(); - do { - g = lines.get_data(); - if (! is_in_region(g)) { - if (! can_grow_region(g)) { - make_new_region(g); - } - } - lines.move_right(); - } while (! lines.is_equal_to_head()); - } -} - -/* - * remove_redundant_regions - runs through the regions and ensures that - * all are needed. This is required as - * a picture may be empty, or EQ EN pair - * maybe empty. - */ - -void html_printer::remove_redundant_regions (void) -{ - region_glob *r; - - // firstly run through the region making sure that all are needed - // ie all contain a line or word - if (! page_contents->regions.is_empty()) { - page_contents->regions.start_from_tail(); - do { - r = page_contents->regions.get_data(); - calculate_region_margins(r); - if (page_contents->has_line(r) || page_contents->has_word(r)) { - page_contents->regions.move_right(); - } else { - page_contents->regions.sub_move_right(); - } - } while ((! page_contents->regions.is_empty()) && - (! page_contents->regions.is_equal_to_tail())); - } -} - -void html_printer::display_regions (void) -{ - if (debug_table_on) { - region_glob *r; - - fprintf(stderr, "==========s t a r t===========\n"); - if (! page_contents->regions.is_empty()) { - page_contents->regions.start_from_head(); - do { - r = page_contents->regions.get_data(); - fprintf(stderr, "region minv %d maxv %d\n", r->minv, r->maxv); - page_contents->regions.move_right(); - } while (! page_contents->regions.is_equal_to_head()); - } - fprintf(stderr, "============e n d=============\n"); - fflush(stderr); - } -} - -/* - * remove_duplicate_regions - runs through the regions and ensures that - * no duplicates exist. - */ - -void html_printer::remove_duplicate_regions (void) -{ - region_glob *r; - region_glob *l=0; - - if (! page_contents->regions.is_empty()) { - page_contents->regions.start_from_head(); - l = page_contents->regions.get_data(); - page_contents->regions.move_right(); - r = page_contents->regions.get_data(); - if (l != r) { - do { - r = page_contents->regions.get_data(); - // we have a legit region so we check for an intersection - if (is_intersection(r->minv, r->minv, l->minv, l->maxv) && - is_intersection(r->minh, r->maxh, l->minh, l->maxh)) { - l->minv = min(r->minv, l->minv); - l->maxv = max(r->maxv, l->maxv); - l->minh = min(r->minh, l->minh); - l->maxh = max(r->maxh, l->maxh); - calculate_region_margins(l); - page_contents->regions.sub_move_right(); - } else { - l = r; - page_contents->regions.move_right(); - } - } while ((! page_contents->regions.is_empty()) && - (! page_contents->regions.is_equal_to_head())); - } - } -} - -int page::has_line (region_glob *r) -{ - graphic_glob *g; - - if (! lines.is_empty()) { - lines.start_from_head(); - do { - g = lines.get_data(); - if (is_subsection(g->minv, g->maxv, r->minv, r->maxv) && - is_subsection(g->minh, g->maxh, r->minh, r->maxh)) { - return( TRUE ); - } - lines.move_right(); - } while (! lines.is_equal_to_head()); - } - return( FALSE ); -} - - -int page::has_word (region_glob *r) -{ - text_glob *g; - - if (! words.is_empty()) { - words.start_from_head(); - do { - g = words.get_data(); - if (is_subsection(g->minv, g->maxv, r->minv, r->maxv) && - is_subsection(g->minh, g->maxh, r->minh, r->maxh)) { - return( TRUE ); - } - words.move_right(); - } while (! words.is_equal_to_head()); - } - return( FALSE ); -} - - -void html_printer::calculate_region_margins (region_glob *r) -{ - text_glob *w; - graphic_glob *g; - - r->minh = right_margin_indent; - r->maxh = left_margin_indent; - - if (! page_contents->lines.is_empty()) { - page_contents->lines.start_from_head(); - do { - g = page_contents->lines.get_data(); - if (is_subsection(g->minv, g->maxv, r->minv, r->maxv)) { - r->minh = min(r->minh, g->minh); - r->maxh = max(r->maxh, g->maxh); - } - page_contents->lines.move_right(); - } while (! page_contents->lines.is_equal_to_head()); - } - if (! page_contents->words.is_empty()) { - page_contents->words.start_from_head(); - do { - w = page_contents->words.get_data(); - if (is_subsection(w->minv, w->maxv, r->minv, r->maxv)) { - r->minh = min(r->minh, w->minh); - r->maxh = max(r->maxh, w->maxh); - } - page_contents->words.move_right(); - } while (! page_contents->words.is_equal_to_head()); - } -} - - -int page::is_in_region (graphic_glob *g) -{ - region_glob *r; - - if (! regions.is_empty()) { - regions.start_from_head(); - do { - r = regions.get_data(); - if (is_subsection(g->minv, g->maxv, r->minv, r->maxv) && - is_subsection(g->minh, g->maxh, r->minh, r->maxh)) { - return( TRUE ); - } - regions.move_right(); - } while (! regions.is_equal_to_head()); - } - return( FALSE ); -} - - -/* - * no_raw_commands - returns TRUE if no html raw commands exist between - * minv and maxv. - */ - -int page::no_raw_commands (int minv, int maxv) -{ - text_glob *g; - - if (! words.is_empty()) { - words.start_from_head(); - do { - g = words.get_data(); - if ((g->is_raw_command) && (g->is_html_command) && - (is_intersection(g->minv, g->maxv, minv, maxv))) { - return( FALSE ); - } - words.move_right(); - } while (! words.is_equal_to_head()); - } - return( TRUE ); -} - -/* - * can_grow_region - returns TRUE if a region exists which can be extended - * to include graphic_glob *g. The region is extended. - */ - -int page::can_grow_region (graphic_glob *g) -{ - region_glob *r; - int quarter_inch=font::res/4; - - if (! regions.is_empty()) { - regions.start_from_head(); - do { - r = regions.get_data(); - // must prevent grohtml from growing a region through a html raw command - if (is_intersection(g->minv, g->maxv, r->minv, r->maxv+quarter_inch) && - (no_raw_commands(r->minv, r->maxv+quarter_inch))) { -#if defined(DEBUGGING) - stop(); - printf("r minh=%d minv=%d maxh=%d maxv=%d\n", - r->minh, r->minv, r->maxh, r->maxv); - printf("g minh=%d minv=%d maxh=%d maxv=%d\n", - g->minh, g->minv, g->maxh, g->maxv); -#endif - r->minv = min(r->minv, g->minv); - r->maxv = max(r->maxv, g->maxv); - r->minh = min(r->minh, g->minh); - r->maxh = max(r->maxh, g->maxh); -#if defined(DEBUGGING) - printf(" r minh=%d minv=%d maxh=%d maxv=%d\n", - r->minh, r->minv, r->maxh, r->maxv); -#endif - return( TRUE ); - } - regions.move_right(); - } while (! regions.is_equal_to_head()); - } - return( FALSE ); -} - - -/* - * make_new_region - creates a new region to contain, g. - */ - -void page::make_new_region (graphic_glob *g) -{ - region_glob *r=new region_glob; - - r->minv = g->minv; - r->maxv = g->maxv; - r->minh = g->minh; - r->maxv = g->maxv; - regions.add(r); -} - - -void html_printer::dump_page(void) -{ - text_glob *g; - - printf("\n\ndebugging start\n"); - page_contents->words.start_from_head(); - do { - g = page_contents->words.get_data(); - printf("%s ", g->text_string); - page_contents->words.move_right(); - } while (! page_contents->words.is_equal_to_head()); - printf("\ndebugging end\n\n"); -} - - -/* - * traverse_page_regions - runs through the regions in current_page - * and generate html for text, and troff output - * for all graphics. - */ - -void html_printer::traverse_page_regions (void) -{ - region_glob *r; - - start_region_vpos = 0; - start_region_hpos = 0; - end_region_vpos = -1; - end_region_hpos = -1; - - if (! page_contents->regions.is_empty()) { - page_contents->regions.start_from_head(); - do { - r = page_contents->regions.get_data(); - if (r->minv > 0) { - end_region_vpos = r->minv-1; - } else { - end_region_vpos = 0; - } - end_region_hpos = -1; - display_globs(TRUE); - calculate_region_margins(r); - start_region_vpos = end_region_vpos; - end_region_vpos = r->maxv; - start_region_hpos = r->minh; - end_region_hpos = r->maxh; - display_globs(FALSE); - start_region_vpos = end_region_vpos+1; - start_region_hpos = 0; - page_contents->regions.move_right(); - } while (! page_contents->regions.is_equal_to_head()); - start_region_vpos = end_region_vpos+1; - start_region_hpos = 0; - end_region_vpos = -1; - end_region_hpos = -1; - } - display_globs(TRUE); -} - -int html_printer::is_within_region (text_glob *t) -{ - int he, ve, hs; - - if (start_region_hpos == -1) { - hs = t->minh; - } else { - hs = start_region_hpos; - } - if (end_region_vpos == -1) { - ve = t->maxv; - } else { - ve = end_region_vpos; - } - if (end_region_hpos == -1) { - he = t->maxh; - } else { - he = end_region_hpos; - } - return( is_subsection(t->minv, t->maxv, start_region_vpos, ve) && - is_subsection(t->minh, t->maxh, hs, he) ); -} - -int html_printer::is_within_region (graphic_glob *g) -{ - int he, ve, hs; - - if (start_region_hpos == -1) { - hs = g->minh; - } else { - hs = start_region_hpos; - } - if (end_region_vpos == -1) { - ve = g->maxv; - } else { - ve = end_region_vpos; - } - if (end_region_hpos == -1) { - he = g->maxh; - } else { - he = end_region_hpos; - } - return( is_subsection(g->minv, g->maxv, start_region_vpos, ve) && - is_subsection(g->minh, g->maxh, hs, he) ); -} - -int html_printer::is_less (graphic_glob *g, text_glob *t) -{ - return( (g->minv < t->minv) || ((g->minv == t->minv) && (g->minh < t->minh)) ); -} - -void html_printer::convert_to_image (char *troff_src, char *image_name) -{ - char buffer[MAX_STRING_LENGTH*2 + 200]; - char *ps_src = mktemp(xtmptemplate("-ps-")); - - sprintf(buffer, "grops%s %s > %s", EXE_EXT, troff_src, ps_src); - if (debug_on) { - fprintf(stderr, "%s\n", buffer); - } - int status = system(buffer); - if (status == -1) { - fprintf(stderr, "\"%s\" failed (no grops on PATH?)\n", buffer); - return; - } - else if (status) { - fprintf(stderr, "\"%s\" returned status %d\n", buffer, status); - } - - if (image_type == gif) { - sprintf(buffer, - "echo showpage | gs%s -q -dSAFER -sDEVICE=ppmraw -r%d -g%dx%d -sOutputFile=- %s - | ppmquant%s 256 | ppmtogif%s > %s.gif", - EXE_EXT, image_res, - (end_region_hpos-start_region_hpos)*image_res/font::res+IMAGE_BOARDER_PIXELS, - (end_region_vpos-start_region_vpos)*image_res/font::res+IMAGE_BOARDER_PIXELS, - ps_src, EXE_EXT, EXE_EXT, image_name); - } else { - sprintf(buffer, - "echo showpage | gs%s -q -dSAFER -sDEVICE=%s -r%d -g%dx%d -sOutputFile=- %s - > %s.png", - EXE_EXT, image_device, - image_res, - (end_region_hpos-start_region_hpos)*image_res/font::res+IMAGE_BOARDER_PIXELS, - (end_region_vpos-start_region_vpos)*image_res/font::res+IMAGE_BOARDER_PIXELS, - ps_src, image_name); -#if 0 - sprintf(buffer, - "echo showpage | gs -q -dSAFER -sDEVICE=ppmraw -r%d -g%dx%d -sOutputFile=- %s.ps - > %s.pnm ; pnmtopng -transparent white %s.pnm > %s.png \n", - /* image_device, */ - image_res, - (end_region_hpos-start_region_hpos)*image_res/font::res+IMAGE_BOARDER_PIXELS, - (end_region_vpos-start_region_vpos)*image_res/font::res+IMAGE_BOARDER_PIXELS, - name, name, name, image_name); -#endif - } - if (debug_on) { - fprintf(stderr, "%s\n", buffer); - } - // Redirect standard error to the null device. This is more - // portable than using "2> /dev/null" inside the commands above, - // since it doesn't require a Unixy shell. - int save_stderr = dup(2); - if (save_stderr > 2) { - int fdnull = open(NULL_DEV, O_WRONLY|O_BINARY, 0666); - if (fdnull > 2) - dup2(fdnull, 2); - if (fdnull >= 0) - close(fdnull); - } - status = system(buffer); - dup2(save_stderr, 2); - if (status == -1) { - fprintf(stderr, - "Conversion to image failed (no gs/ppmquant/ppmtogif on PATH?)\n"); - } - else if (status) { - fprintf(stderr, - "Conversion to image returned status %d\n", status); - } - unlink(ps_src); - unlink(troff_src); -} - -void html_printer::prologue (void) -{ - troff.put_string("x T ps\nx res "); - troff.put_number(postscript_res); - troff.put_string(" 1 1\nx init\np1\n"); -} - -void html_printer::display_globs (int is_to_html) -{ - text_glob *t=0; - graphic_glob *g=0; - FILE *f=0; - char *troff_src; - int something=FALSE; - int is_center=FALSE; - - end_paragraph(); - - if (! is_to_html) { - is_center = html_position_region(); - make_new_image_name(); - f = xtmpfile(&troff_src, "-troff-", FALSE); - troff.set_file(f); - prologue(); - output_style.f = 0; - } - if (! page_contents->words.is_empty()) { - page_contents->words.start_from_head(); - t = page_contents->words.get_data(); - } - - if (! page_contents->lines.is_empty()) { - page_contents->lines.start_from_head(); - g = page_contents->lines.get_data(); - } - - do { -#if 0 - if ((t != 0) && (strcmp(t->text_string, "(1.a)") == 0)) { - stop(); - } -#endif - if ((t == 0) && (g != 0)) { - if (is_within_region(g)) { - something = TRUE; - display_line(g, is_to_html); - } - if (page_contents->lines.is_empty() || page_contents->lines.is_equal_to_tail()) { - g = 0; - } else { - g = page_contents->lines.move_right_get_data(); - } - } else if ((g == 0) && (t != 0)) { - if (is_within_region(t)) { - display_word(t, is_to_html); - something = TRUE; - } - if (page_contents->words.is_empty() || page_contents->words.is_equal_to_tail()) { - t = 0; - } else { - t = page_contents->words.move_right_get_data(); - } - } else { - if ((g == 0) || (t == 0)) { - // hmm nothing to print out... - } else if (is_less(g, t)) { - if (is_within_region(g)) { - display_line(g, is_to_html); - something = TRUE; - } - if (page_contents->lines.is_empty() || page_contents->lines.is_equal_to_tail()) { - g = 0; - } else { - g = page_contents->lines.move_right_get_data(); - } - } else { - if (is_within_region(t)) { - display_word(t, is_to_html); - something = TRUE; - } - if (page_contents->words.is_empty() || page_contents->words.is_equal_to_tail()) { - t = 0; - } else { - t = page_contents->words.move_right_get_data(); - } - } - } - } while ((t != 0) || (g != 0)); - - if ((! is_to_html) && (f != 0)) { - fclose(troff.get_file()); - if (something) { - convert_to_image(troff_src, image_name); - - if (is_center) { - end_paragraph(); - begin_paragraph(center_alignment); - force_begin_paragraph(); - } - html.put_string("<img src=\""); - html.put_string(image_name); - if (image_type == gif) { - html.put_string(".gif\""); - } else { - html.put_string(".png\""); - } - html.put_string(">\n"); - html_newline(); - if (is_center) { - end_paragraph(); - } - - output_vpos = end_region_vpos; - output_hpos = 0; - need_one_newline = FALSE; - output_style.f = 0; - end_paragraph(); - } - } -} - -void html_printer::flush_page (void) -{ - calculate_margin(); - output_vpos = -1; - output_hpos = get_left(); - supress_sub_sup = TRUE; -#if 0 - dump_page(); -#endif - html.begin_comment("left margin: ").comment_arg(i_to_a(left_margin_indent)).end_comment();; - html.begin_comment("right margin: ").comment_arg(i_to_a(right_margin_indent)).end_comment();; - remove_redundant_regions(); - page_contents->calculate_region(); - remove_duplicate_regions(); - find_title(); - supress_sub_sup = TRUE; - traverse_page_regions(); - terminate_current_font(); - if (need_one_newline) { - html_newline(); - } - end_paragraph(); - - // move onto a new page - delete page_contents; - page_contents = new page; -} - -static int convertSizeToHTML (int size) -{ - if (size < 6) { - return( 0 ); - } else if (size < 8) { - return( 1 ); - } else if (size < 10) { - return( 2 ); - } else if (size < 12) { - return( 3 ); - } else if (size < 14) { - return( 4 ); - } else if (size < 16) { - return( 5 ); - } else if (size < 18) { - return( 6 ); - } else { - return( 7 ); - } -} - - -void html_printer::write_html_font_face (const char *fontname, const char *left, const char *right) -{ - switch (fontname[0]) { - - case 'C': html.put_string(left) ; html.put_string("tt"); html.put_string(right); - break; - case 'H': break; - case 'T': break; - default: break; - } -} - - -void html_printer::write_html_font_type (const char *fontname, const char *left, const char *right) -{ - if (strcmp(&fontname[1], "B") == 0) { - html.put_string(left) ; html.put_string("B"); html.put_string(right); - } else if (strcmp(&fontname[1], "I") == 0) { - html.put_string(left) ; html.put_string("I"); html.put_string(right); - } else if (strcmp(&fontname[1], "BI") == 0) { - html.put_string(left) ; html.put_string("EM"); html.put_string(right); - } -} - - -void html_printer::html_change_font (text_glob *g, const char *fontname, int size) -{ - char buffer[1024]; - - if (output_style.f != 0) { - const char *oldfontname = output_style.f->get_name(); - - // firstly terminate the current font face and type - if ((oldfontname != 0) && (oldfontname != fontname)) { - write_html_font_face(oldfontname, "</", ">"); - write_html_font_type(oldfontname, "</", ">"); - } - } - - if ((output_style.point_size != size) && (output_style.point_size != 0)) { - // shutdown the previous font size - html.put_string("</font>"); - } - - if ((output_style.point_size != size) && (size != 0)) { - // now emit the size if it has changed - sprintf(buffer, "<font size=%d>", convertSizeToHTML(size)); - html.put_string(buffer); - output_style.point_size = size; // and remember the size - } - output_style.f = 0; // no style at present - output_style.point_size = size; // remember current font size - - if (fontname != 0) { - if (! g->is_raw_command) { - // now emit the new font - write_html_font_face(fontname, "<", ">"); - - // now emit the new font type - write_html_font_type(fontname, "<", ">"); - - output_style = g->text_style; // remember style for next time - } - } -} - - -void html_printer::change_font (text_glob *g, int is_to_html) -{ - if (is_to_html) { - if (output_style != g->text_style) { - const char *fontname=0; - int size=0; - - if (g->text_style.f != 0) { - fontname = g->text_style.f->get_name(); - size = (font::res/(72*font::sizescale))*g->text_style.point_size; - } - html_change_font(g, fontname, size); - } - } else { - // is to troff - if (output_style != g->text_style) { - if (g->text_style.f != 0) { - const char *fontname = g->text_style.f->get_name(); - int size = (font::res/(72*font::sizescale))*g->text_style.point_size; - - if (fontname == 0) { - fatal("no internalname specified for font"); - } - - troff_change_font(fontname, size, g->text_style.font_no); - output_style = g->text_style; // remember style for next time - } - } - } -} - -/* - * is_bold - returns TRUE if the text inside, g, is using a bold face. - * It returns FALSE is g contains a raw html command, even if this uses - * a bold font. - */ - -int html_printer::is_bold (text_glob *g) -{ - if (g->text_style.f == 0) { - // unknown font - return( FALSE ); - } else if (g->is_raw_command) { - return( FALSE ); - } else { - const char *fontname = g->text_style.f->get_name(); - - if (strlen(fontname) >= 2) { - return( fontname[1] == 'B' ); - } else { - return( FALSE ); - } - } -} - -void html_printer::terminate_current_font (void) -{ - text_glob g; - - // we create a dummy glob just so we can tell html_change_font not to start up - // a new font - g.is_raw_command = TRUE; - html_change_font(&g, 0, 0); -} - -void html_printer::write_header (text_glob *g) -{ - if (strlen(header.header_buffer) > 0) { - if (header.header_level > 7) { - header.header_level = 7; - } - - if (cutoff_heading+2 > header.header_level) { - // firstly we must terminate any font and type faces - terminate_current_font(); - end_paragraph(); - - // secondly we generate a tag - html.put_string("<a name=\""); - html.put_string(header.header_buffer); - html.put_string("\"></a>"); - // now we save the header so we can issue a list of link - style st; - - header.no_of_headings++; - - text_glob *h=new text_glob(&st, - header.headings.add_string(header.header_buffer, strlen(header.header_buffer)), - strlen(header.header_buffer), - header.no_of_headings, header.header_level, - header.no_of_headings, header.header_level, - FALSE, FALSE); - header.headers.add(h); // and add this header to the header list - } else { - terminate_current_font(); - end_paragraph(); - } - - // we adjust the margin if necessary - - if (g->minh < left_margin_indent) { - header_indent = g->minh; - } - - // and now we issue the real header - html.put_string("<h"); - html.put_number(header.header_level); - html.put_string(">"); - html.put_string(header.header_buffer); - html.put_string("</h"); - html.put_number(header.header_level); - html.put_string(">"); - - need_one_newline = FALSE; - begin_paragraph(left_alignment); - header.written_header = TRUE; - } -} - -/* - * translate_str_to_html - translates a string, str, into html representation. - * len indicates the string length. - */ - -void translate_str_to_html (font *f, char *str, int len) -{ - char buf[MAX_STRING_LENGTH]; - - str_translate_to_html(f, buf, MAX_STRING_LENGTH, str, len, TRUE); - strncpy(str, buf, max(len, strlen(buf)+1)); -} - -/* - * write_headings - emits a list of links for the headings in this document - */ - -void header_desc::write_headings (FILE *f) -{ - text_glob *g; - - if (! headers.is_empty()) { - headers.start_from_head(); - do { - g = headers.get_data(); - fprintf(f, "<a href=\"#%s\">%s</a><br>\n", g->text_string, g->text_string); - headers.move_right(); - } while (! headers.is_equal_to_head()); - } -} - -void html_printer::determine_header_level (void) -{ - int i; - int l=strlen(header.header_buffer); - int stops=0; - - for (i=0; ((i<l) && ((header.header_buffer[i] == '.') || is_digit(header.header_buffer[i]))) ; i++) { - if (header.header_buffer[i] == '.') { - stops++; - } - } - if (stops > 0) { - header.header_level = stops; - } -} - - -void html_printer::build_header (text_glob *g) -{ - text_glob *l; - int current_vpos; - char buf[MAX_STRING_LENGTH]; - - strcpy(header.header_buffer, ""); - do { - l = g; - current_vpos = g->minv; - str_translate_to_html(g->text_style.f, buf, MAX_STRING_LENGTH, g->text_string, g->text_length, TRUE); - strcat(header.header_buffer, (char *)buf); - page_contents->words.move_right(); - g = page_contents->words.get_data(); - if (g->minv == current_vpos) { - strcat(header.header_buffer, " "); - } - } while ((! page_contents->words.is_equal_to_head()) && - ((g->minv == current_vpos) || (l->maxh == right_margin_indent))); - - determine_header_level(); - // finally set the output to neutral for after the header - - g = page_contents->words.get_data(); - output_vpos = g->minv; // set output_vpos to the next line since - output_hpos = left_margin_indent; // html header forces a newline anyway - page_contents->words.move_left(); // so that next time we use old g - - need_one_newline = FALSE; -} - - -/* - * is_whole_line_bold - returns TRUE if the whole line is bold. - */ - -int html_printer::is_whole_line_bold (text_glob *g) -{ - text_glob *n=g; - int current_vpos=g->minv; - - do { - if (is_bold(n)) { - page_contents->words.move_right(); - n = page_contents->words.get_data(); - } else { - while (page_contents->words.get_data() != g) { - page_contents->words.move_left(); - } - return( FALSE ); - } - } while ((! page_contents->words.is_equal_to_head()) && (is_on_same_line(n, current_vpos))); - // was (n->minv == current_vpos) - while (page_contents->words.get_data() != g) { - page_contents->words.move_left(); - } - return( TRUE ); -} - - -/* - * is_a_header - returns TRUE if the whole sequence of contineous lines are bold. - * It checks to see whether a line is likely to be contineous and - * then checks that all words are bold. - */ - -int html_printer::is_a_header (text_glob *g) -{ - text_glob *l; - text_glob *n=g; - int current_vpos; - - do { - l = n; - current_vpos = n->minv; - if (is_bold(n)) { - page_contents->words.move_right(); - n = page_contents->words.get_data(); - } else { - while (page_contents->words.get_data() != g) { - page_contents->words.move_left(); - } - return( FALSE ); - } - } while ((! page_contents->words.is_equal_to_head()) && - ((n->minv == current_vpos) || (l->maxh == right_margin_indent))); - while (page_contents->words.get_data() != g) { - page_contents->words.move_left(); - } - return( TRUE ); -} - - -int html_printer::processed_header (text_glob *g) -{ - if ((guess_on) && (g->minh <= left_margin_indent) && (! using_table_for_indent()) && - (is_a_header(g))) { - build_header(g); - write_header(g); - return( TRUE ); - } else { - return( FALSE ); - } -} - -int is_punctuation (char *s, int length) -{ - return( (length == 1) && - ((s[0] == '(') || (s[0] == ')') || (s[0] == '!') || (s[0] == '.') || (s[0] == '[') || - (s[0] == ']') || (s[0] == '?') || (s[0] == ',') || (s[0] == ';') || (s[0] == ':') || - (s[0] == '@') || (s[0] == '#') || (s[0] == '$') || (s[0] == '%') || (s[0] == '^') || - (s[0] == '&') || (s[0] == '*') || (s[0] == '+') || (s[0] == '-') || (s[0] == '=') || - (s[0] == '{') || (s[0] == '}') || (s[0] == '|') || (s[0] == '\"') || (s[0] == '\'')) - ); -} - -/* - * move_horizontal - moves right into the position, g->minh. - */ - -void html_printer::move_horizontal (text_glob *g, int left_margin) -{ - if (g->text_style.f != 0) { - int w = g->text_style.f->get_space_width(g->text_style.point_size); - - if (w == 0) { - fatal("space width is zero"); - } - if ((output_hpos == left_margin) && (g->minh > output_hpos)) { - make_html_indent(g->minh-output_hpos); - } else { - emit_space(g, FALSE); - } - output_hpos = g->maxh; - output_vpos = g->minv; - - change_font(g, TRUE); - } -} - -/* - * looks_like_subscript - returns TRUE if, g, looks like a subscript. - */ - -int html_printer::looks_like_subscript (text_glob *g) -{ - int r = font::res; - int height = output_style.point_size*r/72; - - /* was return( ((output_vpos < g->minv) && (output_style.point_size != 0) && - * (output_style.point_size > g->text_style.point_size)) ); - */ - - return( (output_style.point_size != 0) && (! supress_sub_sup) && (output_vpos+height < g->maxv) ); -} - -/* - * looks_like_superscript - returns TRUE if, g, looks like a superscript. - */ - -int html_printer::looks_like_superscript (text_glob *g) -{ - int r = font::res; - int height = output_style.point_size*r/72; - -/* was - * return(((output_vpos > g->minv) && (output_style.point_size != 0) && - * (output_style.point_size > g->text_style.point_size))); - */ - - return( (output_style.point_size != 0) && (! supress_sub_sup) && (output_vpos+height > g->maxv) ); -} - -/* - * looks_like_larger_font - returns TRUE if, g, can be treated as a larger font. - * g needs to be on the same line - */ - -int html_printer::looks_like_larger_font (text_glob *g) -{ - int r = font::res; - int height = output_style.point_size*r/72; - - return( (output_vpos+height == g->maxv) && (output_style.point_size != 0) && - (convertSizeToHTML(g->text_style.point_size)+1 == convertSizeToHTML(output_style.point_size)) ); -} - -/* - * looks_like_smaller_font - returns TRUE if, g, can be treated as a smaller font. - * g needs to be on the same line - */ - -int html_printer::looks_like_smaller_font (text_glob *g) -{ - int r = font::res; - int height = output_style.point_size*r/72; - - return( (output_vpos+height == g->maxv) && (output_style.point_size != 0) && - (convertSizeToHTML(g->text_style.point_size) == convertSizeToHTML(output_style.point_size)+1) ); -} - -/* - * pretend_is_on_same_line - returns TRUE if we think, g, is on the same line as the previous glob. - * Note that it believes a single word spanning the left..right as being - * on a different line. - */ - -int html_printer::pretend_is_on_same_line (text_glob *g, int left_margin, int right_margin) -{ - return( auto_on && (right_margin == output_hpos) && (left_margin == g->minh) && - (right_margin != g->maxh) && ((! is_whole_line_bold(g)) || (g->text_style.f == output_style.f)) && - (! (using_table_for_indent()) || (indentation.wrap_margin)) ); -} - -int html_printer::is_on_same_line (text_glob *g, int vpos) -{ -#if 0 - if (g->is_html_command) { - stop(); - } -#endif - return( - (vpos >= 0) && - (is_intersection(vpos, vpos+g->text_style.point_size*font::res/72-1, g->minv, g->maxv)) - ); -} - - -/* - * make_html_indent - creates a relative indentation. - */ - -void html_printer::make_html_indent (int indent) -{ - if ((indent > 0) && ((right_margin_indent-get_left()) > 0) && - ((indent*100)/(right_margin_indent-get_left()))) { - html.put_string("<span style=\" text-indent: "); - html.put_number((indent*100)/(right_margin_indent-get_left())); - html.put_string("%;\"></span>"); - } -} - -/* - * using_table_for_indent - returns TRUE if we currently using a table for indentation - * purposes. - */ - -int html_printer::using_table_for_indent (void) -{ - return( indentation.no_of_columns != 0 ); -} - -/* - * calculate_min_gap - returns the minimum gap by which we deduce columns. - * This is a rough heuristic. - */ - -int html_printer::calculate_min_gap (text_glob *g) -{ - text_glob *t = g; - - while ((t->is_raw_command) && (! page_contents->words.is_equal_to_tail()) && - ((t->minv < end_region_vpos) || (end_region_vpos < 0))) { - page_contents->words.move_right(); - t=page_contents->words.get_data(); - } - rewind_text_to(g); - if (t->is_raw_command) { - return( font::res * 10 ); // impossibly large gap width - } else { - return( t->text_style.f->get_space_width(t->text_style.point_size)*GAP_SPACES ); - } -} - -/* - * collect_columns - place html text in a column and return the vertical limit reached. - */ - -int html_printer::collect_columns (struct text_defn *next_words, - struct text_defn *next_cols, - struct text_defn *last_words, - struct text_defn *last_cols, - int max_words) -{ - text_glob *start = page_contents->words.get_data(); - text_glob *t = start; - int upper_limit = 0; - - /* - * initialize cols and words - */ - next_words[0].left = 0; - next_words[0].right = 0; - next_cols [0].left = 0; - next_cols [0].right = 0; - - /* - * if we have not reached the end collect the words on the current line - */ - if (start != 0) { - int graphic_limit = end_region_vpos; - - if (is_whole_line_bold(t) && (t->minh <= left_margin_indent) && (guess_on)) { - /* - * found header therefore terminate indentation table. - * Return a negative number so we know a header has - * stopped the column - */ - upper_limit = -t->minv; - } else { - int i =0; // is the index into next_cols - int j =0; // is the column index for last_cols - int k =0; // is the index into next_words - int l =0; // is the index into next_words - int prevh =0; - int mingap =calculate_min_gap(start); - - /* - * while words on the same line record them and any significant gaps - */ - while ((t != 0) && (is_on_same_line(t, start->minv) && (i<max_words)) && - ((graphic_limit == -1) || (graphic_limit > t->minv))) { - - /* - * now find column index from the last line which corresponds to, t. - */ - j = find_column_index_in_line(t, last_cols); - - /* - * now find word index from the last line which corresponds to, t. - */ - l = find_column_index_in_line(t, last_words); - - /* - * Note t->minh might equal t->maxh when we are passing a special device character via \X - * we currently ignore this when considering tables - * - * if we have found a significant gap then record it - */ - if (((t->minh - prevh >= mingap) || - ((last_cols != 0) && (last_cols [j].right != 0) && (t->minh == last_cols [j].left))) && - (t->minh != t->maxh)) { - next_cols[i].left = t->minh; - next_cols[i].right = t->maxh; - i++; - /* - * terminate the array - */ - if (i<max_words) { - next_cols[i].left = 0; - next_cols[i].right = 0; - } - } else if (i>0) { - /* - * move previous right hand column to align with, t. - */ - - if (t->minh > next_cols[i-1].left) { - /* - * a simple precaution in case we get globs which are technically on the same line - * (sadly this does occur sometimes - maybe we should be stricter with is_on_same_line) - * --fixme-- - */ - next_cols[i-1].right = max(next_cols[i-1].right, t->maxh); - } - } - /* - * remember to record the individual words - */ - next_words[k].left = t->minh; - next_words[k].right = t->maxh; - k++; - - /* - * and record the vertical upper limit - */ - upper_limit = max(t->minv, upper_limit); - - /* - * and update prevh - used to detect a when a different line is seen - */ - prevh = t->maxh; - - /* - * get next word into, t, which equals 0, if no word is found - */ - page_contents->words.move_right(); - t = page_contents->words.get_data(); - if (page_contents->words.is_equal_to_head()) { - t = 0; - } - } - - /* - * and terminate the next_words array - */ - - if (k<max_words) { - next_words[k].left = 0; - next_words[k].right = 0; - } - - /* - * consistency check, next_cols, after removing redundant colums. - */ - - remove_redundant_columns(next_cols); - -#if 0 - for (k=0; k<count_columns(next_cols); k++) { - if (next_cols[k].left > next_cols[k].right) { - fprintf(stderr, "left > right\n"); fflush(stderr); - stop(); - fatal("next_cols has messed up columns"); - } - if ((k>0) && (k+1<count_columns(next_cols)) && (next_cols[k].right > next_cols[k+1].left)) { - fprintf(stderr, "next_cols[k].right > next_cols[k+1].left\n"); fflush(stderr); - stop(); - fatal("next_cols has messed up columns"); - } - } -#endif - } - } - return( upper_limit ); -} - -/* - * conflict_with_words - returns TRUE if a word sequence crosses a column. - */ - -int html_printer::conflict_with_words (struct text_defn *column_guess, struct text_defn *words) -{ - int i=0; - int j; - - while ((column_guess[i].right != 0) && (i<MAX_WORDS_PER_LINE)) { - j=0; - while ((words[j].right != 0) && (j<MAX_WORDS_PER_LINE)) { - if ((words[j].left <= column_guess[i].right) && (i+1<MAX_WORDS_PER_LINE) && - (column_guess[i+1].right != 0) && (words[j].right >= column_guess[i+1].left)) { - if (debug_table_on) { - fprintf(stderr, "is a conflict with words\n"); - fflush(stderr); - } - return( TRUE ); - } - j++; - } - i++; - } - if (debug_table_on) { - fprintf(stderr, "is NOT a conflict with words\n"); - fflush(stderr); - } - return( FALSE ); -} - -/* - * combine_line - combines dest and src. - */ - -void html_printer::combine_line (struct text_defn *dest, struct text_defn *src) -{ - int i; - - for (i=0; (i<MAX_WORDS_PER_LINE) && (src[i].right != 0); i++) { - include_into_list(dest, &src[i]); - } - remove_redundant_columns(dest); -} - -/* - * remove_entry_in_line - removes an entry, j, in, line. - */ - -void html_printer::remove_entry_in_line (struct text_defn *line, int j) -{ - while (line[j].right != 0) { - line[j].left = line[j+1].left; - line[j].right = line[j+1].right; - j++; - } -} - -/* - * remove_redundant_columns - searches through the array columns and removes any redundant entries. - */ - -void html_printer::remove_redundant_columns (struct text_defn *line) -{ - int i=0; - int j=0; - - while (line[i].right != 0) { - if ((i<MAX_WORDS_PER_LINE) && (line[i+1].right != 0)) { - j = 0; - while ((j<MAX_WORDS_PER_LINE) && (line[j].right != 0)) { - if ((j != i) && (is_intersection(line[i].left, line[i].right, line[j].left, line[j].right))) { - line[i].left = min(line[i].left , line[j].left); - line[i].right = max(line[i].right, line[j].right); - remove_entry_in_line(line, j); - } else { - j++; - } - } - } - i++; - } -} - -/* - * include_into_list - performs an order set inclusion - */ - -void html_printer::include_into_list (struct text_defn *line, struct text_defn *item) -{ - int i=0; - - while ((i<MAX_WORDS_PER_LINE) && (line[i].right != 0) && (line[i].left<item->left)) { - i++; - } - - if (line[i].right == 0) { - // add to the end - if (i<MAX_WORDS_PER_LINE) { - if ((i>0) && (line[i-1].left > item->left)) { - fatal("insertion error"); - } - line[i].left = item->left; - line[i].right = item->right; - i++; - line[i].left = 0; - line[i].right = 0; - } - } else { - if (line[i].left == item->left) { - line[i].right = max(item->right, line[i].right); - } else { - // insert - int left = item->left; - int right = item->right; - int l = line[i].left; - int r = line[i].right; - - while ((i+1<MAX_WORDS_PER_LINE) && (line[i].right != 0)) { - line[i].left = left; - line[i].right = right; - i++; - left = l; - right = r; - l = line[i].left; - r = line[i].right; - } - if (i+1<MAX_WORDS_PER_LINE) { - line[i].left = left; - line[i].right = right; - line[i+1].left = 0; - line[i+1].right = 0; - } - } - } -} - -/* - * is_in_column - return TRUE if value is present in line. - */ - -int html_printer::is_in_column (struct text_defn *line, struct text_defn *item, int max_words) -{ - int i=0; - - while ((i<max_words) && (line[i].right != 0)) { - if (line[i].left == item->left) { - return( TRUE ); - } else { - i++; - } - } - return( FALSE ); -} - -/* - * calculate_right - calculate the right most margin for each column in line. - */ - -void html_printer::calculate_right (struct text_defn *line, int max_words) -{ - int i=0; - - while ((i<max_words) && (line[i].right != 0)) { - if (i>0) { - line[i-1].right = line[i].left; - } - i++; - } -} - -/* - * add_right_full_width - adds an extra column to the right to bring the table up to - * full width. - */ - -void html_printer::add_right_full_width (struct text_defn *line, int mingap) -{ - int i=0; - - while ((i<MAX_WORDS_PER_LINE) && (line[i].right != 0)) { - i++; - } - - if ((i>0) && (line[i-1].right != right_margin_indent) && (i+1<MAX_WORDS_PER_LINE)) { - line[i].left = min(line[i-1].right+mingap, right_margin_indent); - line[i].right = right_margin_indent; - i++; - if (i<MAX_WORDS_PER_LINE) { - line[i].left = 0; - line[i].right = 0; - } - } -} - -/* - * determine_right_most_column - works out the right most limit of the right most column. - * Required as we might be performing a .2C and only - * have enough text to fill the left column. - */ - -void html_printer::determine_right_most_column (struct text_defn *line, int max_words) -{ - int i=0; - - while ((i<max_words) && (line[i].right != 0)) { - i++; - } - if (i>0) { - // remember right_margin_indent is the right most position for this page - line[i-1].right = column_calculate_right_margin(line[i-1].left, right_margin_indent); - } -} - -/* - * is_column_match - returns TRUE if a word is aligned in the same horizontal alignment - * between two lines, line1 and line2. If so then this horizontal - * position is saved in match. - */ - -int html_printer::is_column_match (struct text_defn *match, - struct text_defn *line1, struct text_defn *line2, int max_words) -{ - int i=0; - int j=0; - int found=FALSE; - int first=(match[0].left==0); - - if (first) { - struct text_defn t; - - t.left = left_margin_indent; - t.right = 0; - - include_into_list(match, &t); - } - while ((line1[i].right != 0) && (line2[i].right != 0)) { - if (line1[i].left == line2[j].left) { - // same horizontal alignment found - include_into_list(match, &line1[i]); - i++; - j++; - found = TRUE; - } else if (line1[i].left < line2[j].left) { - i++; - } else { - j++; - } - } - calculate_right(match, max_words); - return( found ); -} - -/* - * check_lack_of_hits - returns TRUE if a column has been moved to a position - * of only one hit from a position of more than one hit. - */ - -int html_printer::check_lack_of_hits (struct text_defn *next_guess, - struct text_defn *last_guess, - text_glob *start, int limit) -{ - text_glob *current=page_contents->words.get_data(); - int n=count_columns(last_guess); - int m=count_columns(next_guess); - int i, j; - - if (limit > 0) { - rewind_text_to(start); - count_hits(last_guess, n, limit); - rewind_text_to(current); - i=0; - j=0; - while ((i<n) && (j<m) && - (last_guess[i].right != 0) && (next_guess[j].right != 0)) { - if ((is_intersection(last_guess[i].left, last_guess[i].right, - next_guess[j].left, next_guess[j].right)) && - (next_guess[j].left < last_guess[i].left) && - (last_guess[i].is_used >= 2)) { - /* - * next_guess has to be = 1 as this position is new - */ - return( TRUE ); - } - if (last_guess[i].left < next_guess[j].left) { - i++; - } else { - j++; - } - } - } - return( FALSE ); -} - -/* - * remove_white_using_words - remove white space in, last_guess, by examining, next_line - * placing results into next_guess. - * It returns TRUE if the same columns exist in next_guess and last_guess - * we do allow columns to shrink but if a column disappears then we return FALSE. - */ - -int html_printer::remove_white_using_words (struct text_defn *next_guess, - struct text_defn *last_guess, struct text_defn *next_line) -{ - int i=0; - int j=0; - int k=0; - int removed=FALSE; - - while ((last_guess[j].right != 0) && (next_line[k].right != 0)) { - if (last_guess[j].left == next_line[k].left) { - // same horizontal alignment found - next_guess[i].left = last_guess[j].left; - next_guess[i].right = max(last_guess[j].right, next_line[k].right); - i++; - j++; - k++; - if ((next_guess[i-1].right > last_guess[j].left) && (last_guess[j].right != 0)) { - removed = TRUE; - } - } else if (last_guess[j].right < next_line[k].left) { - next_guess[i].left = last_guess[j].left; - next_guess[i].right = last_guess[j].right; - i++; - j++; - } else if (last_guess[j].left > next_line[k].right) { - // insert a word sequence from next_line[k] - next_guess[i].left = next_line[k].left; - next_guess[i].right = next_line[k].right; - i++; - k++; - } else if (is_intersection(last_guess[j].left, last_guess[j].right, next_line[k].left, next_line[k].right)) { - // potential for a column disappearing - next_guess[i].left = min(last_guess[j].left , next_line[k].left); - next_guess[i].right = max(last_guess[j].right, next_line[k].right); - i++; - j++; - k++; - if ((next_guess[i-1].right > last_guess[j].left) && (last_guess[j].right != 0)) { - removed = TRUE; - } - } - } - while (next_line[k].right != 0) { - next_guess[i].left = next_line[k].left; - next_guess[i].right = next_line[k].right; - i++; - k++; - } - if (i<MAX_WORDS_PER_LINE) { - next_guess[i].left = 0; - next_guess[i].right = 0; - } - if (debug_table_on) { - if (removed) { - fprintf(stderr, "have removed column\n"); - } else { - fprintf(stderr, "have NOT removed column\n"); - } - fflush(stderr); - } - remove_redundant_columns(next_guess); - return( removed ); -} - -/* - * count_columns - returns the number of elements inside, line. - */ - -int html_printer::count_columns (struct text_defn *line) -{ - int i=0; - - while (line[i].right != 0) { - i++; - } - return( i ); -} - -/* - * rewind_text_to - moves backwards until page_contents is looking at, g. - */ - -void html_printer::rewind_text_to (text_glob *g) -{ - while (page_contents->words.get_data() != g) { - if (page_contents->words.is_equal_to_head()) { - page_contents->words.start_from_tail(); - } else { - page_contents->words.move_left(); - } - } -} - -/* - * can_loose_column - checks to see whether we should combine two columns. - * This is allowed if there are is only one hit on the - * left hand edge and the previous column is very close. - */ - -void html_printer::can_loose_column (text_glob *start, struct text_defn *last_guess, int limit) -{ - text_glob *current=page_contents->words.get_data(); - int n=count_columns(last_guess); - int i; - - rewind_text_to(start); - count_hits(last_guess, n, limit); - i=0; - while (i<n-1) { - if ((last_guess[i+1].is_used == 1) && - (calculate_min_gap(start) > (last_guess[i+1].left-last_guess[i].right))) { - last_guess[i].right = last_guess[i+1].right; - remove_entry_in_line(last_guess, i+1); - n = count_columns(last_guess); - i = 0; - } else { - i++; - } - } - rewind_text_to(current); -} - -/* - * display_columns - a long overdue debugging function, as this column code is causing me grief :-( - */ - -void html_printer::display_columns (const char *word, const char *name, text_defn *line) -{ - int i=0; - - fprintf(stderr, "[%s:%s]", name, word); - while (line[i].right != 0) { - fprintf(stderr, " <left=%d right=%d %d%%> ", line[i].left, line[i].right, line[i].percent); - i++; - } - fprintf(stderr, "\n"); - fflush(stderr); -} - -/* - * copy_line - dest = src - */ - -void html_printer::copy_line (struct text_defn *dest, struct text_defn *src) -{ - int k; - - for (k=0; ((src[k].right != 0) && (k<MAX_WORDS_PER_LINE)); k++) { - dest[k].left = src[k].left; - dest[k].right = src[k].right; - } - if (k<MAX_WORDS_PER_LINE) { - dest[k].left = 0; - dest[k].right = 0; - } -} - -/* - * add_column_gaps - adds empty columns between columns which don't exactly align - */ - -void html_printer::add_column_gaps (struct text_defn *line) -{ - int i=0; - struct text_defn t; - - // firstly lets see whether we need an initial column on the left hand side - if ((line[0].left != get_left()) && (line[0].right != 0) && - (get_left() < line[0].left) && (is_worth_column(get_left(), line[0].left))) { - t.left = get_left(); - t.right = line[0].left; - include_into_list(line, &t); - } - - while ((i<MAX_WORDS_PER_LINE) && (line[i].right != 0)) { - if ((i+1<MAX_WORDS_PER_LINE) && (line[i+1].right != 0) && (line[i].right != line[i+1].left) && - (is_worth_column(line[i].right, line[i+1].left))) { - t.left = line[i].right; - t.right = line[i+1].left; - include_into_list(line, &t); - i=0; - } else { - i++; - } - } - // now let us see whether we need a final column on the right hand side - if ((i>0) && (line[i-1].right != right_margin_indent) && - (is_worth_column(line[i-1].right, right_margin_indent))) { - t.left = line[i-1].right; - t.right = right_margin_indent; - include_into_list(line, &t); - } -} - -/* - * is_continueous_column - returns TRUE if a line has a word on one - * of the last_col right most boundaries. - */ - -int html_printer::is_continueous_column (text_defn *last_col, text_defn *next_line) -{ - int w = count_columns(next_line); - int c = count_columns(last_col); - int i, j; - - for (i=0; i<c; i++) { - for (j=0; j<w; j++) { - if (last_col[i].right == next_line[j].right) { - return( TRUE ); - } - } - } - return( FALSE ); -} - -/* - * is_exact_left - returns TRUE if a line has a word on one - * of the last_col left most boundaries. - */ - -int html_printer::is_exact_left (text_defn *last_col, text_defn *next_line) -{ - int w = count_columns(next_line); - int c = count_columns(last_col); - int i, j; - - for (i=0; i<c; i++) { - for (j=0; j<w; j++) { - if ((last_col[i].left == next_line[j].left) || - (last_col[i].left != left_margin_indent)) { - return( TRUE ); - } - } - } - return( FALSE ); -} - -/* - * continue_searching_column - decides whether we should carry on searching text for a column. - */ - -int html_printer::continue_searching_column (text_defn *next_col, - text_defn *last_col, - text_defn *all_words) -{ - int count = count_columns(next_col); - int words = count_columns(all_words); - - if ((words == 0) || ((words == 1) && - (all_words[0].left == left_margin_indent) && - (all_words[0].right == right_margin_indent))) { - // no point as we have now seen a full line of contineous text with no gap - return( FALSE ); - } - return( (count == count_columns(last_col)) && - (last_col[0].left != left_margin_indent) || (last_col[0].right != right_margin_indent) ); -} - -/* - * is_worth_column - returns TRUE if the size of this column is worth defining. - */ - -int html_printer::is_worth_column (int left, int right) -{ -#if 0 - return( abs(right-left) >= MIN_COLUMN ); -#endif - return( TRUE ); -} - -/* - * large_enough_gap - returns TRUE if a large enough gap for one line was seen. - * We need to make sure that a single line definitely warrents - * a table. - * It also removes other smaller gaps. - */ - -int html_printer::large_enough_gap (text_defn *last_col) -{ - int i=0; - int found=FALSE; - int r=font::res; - int gap=r/GAP_WIDTH_ONE_LINE; - - if (abs(last_col[i].left - left_margin_indent) >= gap) { - found = TRUE; - } - while ((last_col[i].right != 0) && (last_col[i+1].right != 0)) { - if (abs(last_col[i+1].left-last_col[i].right) >= gap) { - found = TRUE; - i++; - } else { - // not good enough for a single line, remove it - last_col[i].right = last_col[i+1].right; - remove_entry_in_line(last_col, i+1); - } - } - return( found ); -} - -/* - * is_subset_of_columns - returns TRUE if line, a, is a subset of line, b. - */ - -int html_printer::is_subset_of_columns (text_defn *a, text_defn *b) -{ - int i; - int j; - - i=0; - while ((i<MAX_WORDS_PER_LINE) && (a[i].right != 0)) { - j=0; - while ((j<MAX_WORDS_PER_LINE) && (b[j].right != 0) && - ((b[j].left != a[i].left) || (b[j].right != a[i].right))) { - j++; - } - if ((j==MAX_WORDS_PER_LINE) || (b[j].right == 0)) { - // found a different column - not a subset - return( FALSE ); - } - i++; - } - return( TRUE ); -} - -/* - * count_hits - counts the number of hits per column. A left hit - * is when the left hand position of a glob hits - * the left hand column. - */ - -void html_printer::count_hits (text_defn *col, int no_of_columns, int limit) -{ - int i; - text_glob *start = page_contents->words.get_data(); - text_glob *g = start; - - // firstly reset the used field - for (i=0; i<no_of_columns; i++) { - col[i].is_used = 0; - } - // now calculate the left hand hits - while ((g != 0) && (g->minv <= limit)) { - i=0; - while ((i<no_of_columns) && (col[i].right < g->minh)) { - i++; - } - if ((col[i].left == g->minh) && (col[i].right != 0)) { - col[i].is_used++; - } - page_contents->words.move_right(); - if (page_contents->words.is_equal_to_head()) { - g = 0; - page_contents->words.start_from_tail(); - } else { - g=page_contents->words.get_data(); - } - } -} - -/* - * count_right_hits - counts the number of right hits per column. - * A right hit is when the left hand position - * of a glob hits the right hand column. - */ - -void html_printer::count_right_hits (text_defn *col, int no_of_columns) -{ - int i; - text_glob *start = page_contents->words.get_data(); - text_glob *g = start; - - // firstly reset the used field - for (i=0; i<no_of_columns; i++) { - col[i].right_hits = 0; - } - // now calculate the left hand hits - while ((g != 0) && (g->minv <= indentation.vertical_limit)) { - i=0; - while ((i<no_of_columns) && (col[i].right < g->minh)) { - i++; - } - if ((i<no_of_columns) && (col[i].right == g->maxh)) { - if (debug_table_on) { - fprintf(stderr, "found right hit [%s] at %d in %d\n", - g->text_string, g->maxh, i); - fflush(stderr); - } - col[i].right_hits++; - } - page_contents->words.move_right(); - if (page_contents->words.is_equal_to_head()) { - g = 0; - page_contents->words.start_from_tail(); - } else { - g=page_contents->words.get_data(); - } - } -} - -/* - * right_indentation - returns TRUE if a single column has been found and - * it resembles an indentation. Ie .RS/.RE or ABSTACT - */ - -int html_printer::right_indentation (struct text_defn *last_guess) -{ - // it assumes that last_guess contains a single column - return( (last_guess[0].left > left_margin_indent) ); -} - -/* - * able_to_steal_width - returns TRUE if we have an unused column which we can steal from. - * It must have more than MIN_TEXT_PERCENT to do this. - */ - -int html_printer::able_to_steal_width (void) -{ - int i; - - for (i=0; i<indentation.no_of_columns; i++) { - if ((! indentation.columns[i].is_used) && - (indentation.columns[i].percent > MIN_TEXT_PERCENT)) { - return( TRUE ); - } - } - return( FALSE ); -} - -/* - * is_divisible_by - returns TRUE if n is divisible by d leaving no remainder. - */ - -static int is_divisible_by (int n, int d) -{ - return( (n % d) == 0 ); -} - -/* - * need_to_steal_width - returns TRUE if a used column need to be - * given a little extra width for safty sake. - */ - -int html_printer::need_to_steal_width (void) -{ - int i; - - for (i=0; i<indentation.no_of_columns; i++) { - if ((indentation.columns[i].is_used) && - (indentation.columns[i].percent == (((indentation.columns[i].right - indentation.columns[i].left) * 100) / - (right_margin_indent-left_margin_indent))) && - (indentation.columns[i].percent < PERCENT_THRESHOLD)) { - return( TRUE ); - } - } - return( FALSE ); -} - -/* - * utilize_round_off - utilize the remaining percent width in text columns - */ - -void html_printer::utilize_round_off (void) -{ - int total = total_percentages(); - int excess, i; - - // use up the spare excess - - excess = 100-total; - - for (i=0; (i<indentation.no_of_columns) && (excess>0); i++) { - if ((indentation.columns[i].is_used) && - (indentation.columns[i].percent < PERCENT_THRESHOLD)) { - indentation.columns[i].percent++; - excess--; - } - } - // we might as well try and keep any numbers simple if possible - for (i=0; (i<indentation.no_of_columns) && (excess>0); i++) { - if ((indentation.columns[i].is_used) && - (! is_divisible_by(indentation.columns[i].percent, MIN_TEXT_PERCENT))) { - indentation.columns[i].percent++; - excess--; - } - } - // forget the niceties lets just use excess up now! - for (i=0; (i<indentation.no_of_columns) && (excess>0); i++) { - if (indentation.columns[i].is_used) { - indentation.columns[i].percent++; - excess--; - } - } -} - -/* - * can_distribute_fairly - returns TRUE if we can redistribute some of the unused width into - * columns that are used. - */ - -int html_printer::can_distribute_fairly (void) -{ - int i; - int total=0; - int used =0; - int excess; - - // firstly total up all percentages - so we can use round offs - for (i=0; i<indentation.no_of_columns; i++) { - total += indentation.columns[i].percent; - if ((indentation.columns[i].is_used) && - (indentation.columns[i].percent < PERCENT_THRESHOLD)) { - used++; - } - } - // - excess = 100-total; - if (excess < used) { - for (i=0; i<indentation.no_of_columns; i++) { - if (! indentation.columns[i].is_used) { - if (indentation.columns[i].percent > MIN_TEXT_PERCENT) { - indentation.columns[i].percent--; - excess++; - } - } - } - } - if (excess >= used) { - for (i=0; i<indentation.no_of_columns; i++) { - if ((indentation.columns[i].is_used) && - (indentation.columns[i].percent < PERCENT_THRESHOLD) && - (indentation.columns[i].percent == (((indentation.columns[i].right - indentation.columns[i].left) * 100) / - (right_margin_indent-left_margin_indent)))) { - indentation.columns[i].percent++; - excess--; - } - } - return( TRUE ); - } - return( FALSE ); -} - -/* - * remove_table_column - removes column, i, from the indentation. - */ - -void html_printer::remove_table_column (int i) -{ - while (i<indentation.no_of_columns) { - indentation.columns[i].left = indentation.columns[i+1].left; - indentation.columns[i].right = indentation.columns[i+1].right; - indentation.columns[i].is_used = indentation.columns[i+1].is_used; - indentation.columns[i].percent = indentation.columns[i+1].percent; - i++; - } - indentation.no_of_columns--; -} - -/* - * next_line_on_left_column - returns TRUE if the next line in - * column, i, has a word on the left margin. - */ - -int html_printer::next_line_on_left_column (int i, text_glob *start) -{ - int current_vpos=start->minv; - - while ((start != 0) && (start->minv < indentation.vertical_limit) && - (is_on_same_line(start, current_vpos))) { - if (page_contents->words.is_equal_to_tail()) { - start = 0; - } else { - page_contents->words.move_right(); - start = page_contents->words.get_data(); - } - } - if ((start != 0) && (start->minv < indentation.vertical_limit)) { - // onto next line now - current_vpos=start->minv; - while ((start != 0) && (start->minv < indentation.vertical_limit) && - (is_on_same_line(start, current_vpos))) { - if (start->minh == indentation.columns[i].left) { - return( TRUE ); - } - if (page_contents->words.is_equal_to_tail()) { - start = 0; - } else { - page_contents->words.move_right(); - start = page_contents->words.get_data(); - } - } - } - return( FALSE ); -} - -/* - * will_wrap_text - returns TRUE if text is wrapped in column, i. - */ - -int html_printer::will_wrap_text (int i, text_glob *start) -{ - text_glob *current=page_contents->words.get_data(); - - if (auto_on) { - rewind_text_to(start); - while ((start != 0) && (start->minv < indentation.vertical_limit)) { - if (indentation.columns[i].right == start->maxh) { - // ok right word is on column boarder - check next line - if (next_line_on_left_column(i, start)) { - rewind_text_to(current); - return( TRUE ); - } - } - if (page_contents->words.is_equal_to_tail()) { - start = 0; - } else { - page_contents->words.move_right(); - start = page_contents->words.get_data(); - } - } - } - rewind_text_to(current); - return( FALSE ); -} - -/* - * remove_unnecessary_unused - runs through a table and decides whether an unused - * column can be removed. This is only true if the - * column to the left does not wrap text. - */ - -void html_printer::remove_unnecessary_unused (text_glob *start) -{ - int i=0; - int left=get_left(); - int right; - - while (i<indentation.no_of_columns) { - if ((indentation.columns[i].is_used) && - (i+1<indentation.no_of_columns) && (! indentation.columns[i+1].is_used)) { - /* - * so i+1 is unused and there is a used column to the left. - * Now we check whether we can add the unused column to the column, i. - * This can only be done if column, i, is not wrapping text. - */ - if (! will_wrap_text(i, start)) { -#if 1 - if (i+1 < indentation.no_of_columns) { - right = indentation.columns[i+1].right; - } else { - right = right_margin_indent; - } - indentation.columns[i].percent = (((right - indentation.columns[i].left) * 100) / - (right_margin_indent-left)); -#else - indentation.columns[i].percent = (((indentation.columns[i+1].right - indentation.columns[i].left) * 100) / - (right_margin_indent-left)); -#endif - remove_table_column(i+1); - i=-1; - } - } - i++; - } -} - -/* - * remove_zero_percentage_column - removes all zero percentage width columns - */ - -void html_printer::remove_zero_percentage_column (void) -{ - int i=0; - - while (i<indentation.no_of_columns) { - if (indentation.columns[i].percent == 0) { - remove_table_column(i); - i=0; - } else { - i++; - } - } -} - -/* - * get_left - returns the actual left most margin. - */ - -int html_printer::get_left (void) -{ - if ((header_indent < left_margin_indent) && (header_indent != -1)) { - return( header_indent ); - } else { - if (margin_on) { - return( 0 ); - } else { - return( left_margin_indent ); - } - } -} - -/* - * calculate_percentage_width - calculates the percentage widths, - * this function will be generous to - * columns which have words as some browsers - * produce messy output if the percentage is exactly - * that required for text.. - * We try and round up to MIN_TEXT_PERCENT - * of course we can only do this if we can steal from - * an unused column. - */ - -void html_printer::calculate_percentage_width (text_glob *start) -{ - int i; - int left=get_left(); - int right; - - // firstly calculate raw percentages - for (i=0; i<indentation.no_of_columns; i++) { -#if 0 - indentation.columns[i].percent = (((indentation.columns[i].right - indentation.columns[i].left) * 100) / - (right_margin_indent-left)); -#else - if (i+1 < indentation.no_of_columns) { - right = indentation.columns[i+1].left; - } else { - right = right_margin_indent; - } - indentation.columns[i].percent = (((right - indentation.columns[i].left) * 100) / - (right_margin_indent-left)); -#endif - } - if (debug_table_on) { - display_columns(start->text_string, "[b4 steal] indentation.columns", indentation.columns); - } - - // now steal from the unused columns.. - remove_unnecessary_unused(start); - - if (debug_table_on) { - display_columns(start->text_string, "[after steal] indentation.columns", indentation.columns); - } - -#if 0 - utilize_round_off(); -#endif - remove_zero_percentage_column(); -} - - -/* - * is_column_subset - returns TRUE if the columns described by small can be contained in - * the columns in large. - */ - -int html_printer::is_column_subset (struct text_defn *small, struct text_defn *large) -{ - int ns=count_columns(small); - int nl=count_columns(large); - int found; - int i=0; - int j; - - while (i<ns) { - j=0; - found = FALSE; - while (j<nl) { - if (is_intersection(small[i].left, small[i].right, large[j].left, large[j].right)) { - found = TRUE; - if (! is_subsection(small[i].left, small[i].right, large[j].left, large[j].right)) { - // found column which is not a subset - return( FALSE ); - } - } - j++; - } - if (! found) { - return( FALSE ); - } - i++; - } - // small cannot be an empty set - return( ns>0 ); -} - -/* - * right_most_column - returns the right most column position. - */ - -int html_printer::right_most_column (struct text_defn *col) -{ - int i = count_columns(col); - - if (i>0) { - return( col[i-1].right ); - } else { - return( 0 ); - } -} - -/* - * large_enough_gap_for_two - returns TRUE if there exists a large enough gap - * for two lines. - */ - -int html_printer::large_enough_gap_for_two (struct text_defn *col) -{ - int i=0; - int found=FALSE; - int gap=MIN_COLUMN_FOR_TWO_LINES; - - if (abs(col[i].left - left_margin_indent) >= gap) { - found = TRUE; - } - while ((col[i].right != 0) && (col[i+1].right != 0)) { - if (abs(col[i+1].left-col[i].right) >= gap) { - found = TRUE; - i++; - } else { - // not good enough for this table, remove it - col[i].right = col[i+1].right; - remove_entry_in_line(col, i+1); - } - } - return( found ); -} - -/* - * is_small_table - applies some rigorous rules to test whether we should start this - * table at this point. - */ - -int html_printer::is_small_table (int lines, struct text_defn *last_guess, - struct text_defn *words_1, struct text_defn *cols_1, - struct text_defn *words_2, struct text_defn *cols_2, - int *limit, int *limit_1) -{ - /* - * firstly we check for an indented paragraph - */ - - if ((lines >= 2) && - (count_columns(cols_1) == count_columns(cols_2)) && (count_columns(cols_1) == 1) && - right_indentation(cols_1) && (! right_indentation(cols_2)) && - (cols_1[0].right == right_margin_indent)) { - return( FALSE ); - } - - if (lines == 2) { - /* - * as we only have two lines in our table we need to examine in detail whether - * we should construct a table from these two lines. - * For example if the text is the start of an indented paragraph and - * line1 and line2 are contineous then they should form one row in our table but - * if line1 and line2 are not contineous it is safer to treat them separately. - * - * We are prepared to reduce the table to one line - */ - if (((count_columns(cols_1) != count_columns(cols_2)) && (cols_1[0].left > cols_2[0].left)) || - (! ((is_column_subset(cols_1, cols_2)) || - (is_column_subset(cols_2, cols_1))))) { - /* - * now we must check to see whether line1 and line2 join - */ - if ((right_most_column(cols_1) == right_margin_indent) && - (cols_2[0].left == left_margin_indent)) { - /* - * looks like they join, we don't want a table at all. - */ - return( FALSE ); - } - /* - * use single line table - */ - lines--; - *limit = *limit_1; - copy_line(last_guess, cols_1); - } - } - - if ((count_columns(last_guess)==1) && (right_indentation(last_guess))) { - if (lines == 1) { - *limit = *limit_1; - } - return( TRUE ); - } - - /* - * check for large gap with single line or if multiple lines with more than one column - */ - - if (lines == 1) { - if (large_enough_gap(last_guess)) { - *limit = *limit_1; - return( TRUE ); - } - } else if (count_columns(last_guess)>1) { - if (lines == 2) { - return( large_enough_gap_for_two(last_guess) ); - } - return( TRUE ); - } - return( FALSE ); -} - - -/* - * is_appropriate_to_start_table - returns TRUE if it is appropriate to start the table - * at this point. - */ - -int html_printer::is_appropriate_to_start_table (struct text_defn *cols_1, - struct text_defn *cols_2, - struct text_defn *last_guess) -{ - if (count_columns(last_guess) == 1) { - if (debug_table_on) { - display_columns("", "[is] cols_1" , cols_1); - display_columns("", "[is] cols_2" , cols_2); - display_columns("", "[is] last_guess", last_guess); - } - - if (! ((is_column_subset(cols_1, cols_2)) || - (is_column_subset(cols_2, cols_1)))) { - return( FALSE ); - } - if ((count_columns(cols_1) == 1) && - (cols_1[0].left > left_margin_indent) && (cols_1[0].right < right_margin_indent) && - (cols_1[0].right != cols_2[0].right) && - (count_columns(last_guess) == 1)) { - return( FALSE ); - } - } - return( TRUE ); -} - -/* - * is_a_full_width_column - returns TRUE if there exists a full width column. - */ - -int html_printer::is_a_full_width_column (void) -{ - int i=0; - - while (i<indentation.no_of_columns) { - if (((indentation.columns[i].left == get_left()) || - (indentation.columns[i].left == left_margin_indent)) && - (indentation.columns[i].right == right_margin_indent)) { - return( TRUE ); - } - i++; - } - return( FALSE ); -} - -/* - * should_defer_table - returns TRUE if we should defer this table. - * This can occur if the first line seen indent - * is < than future lines. In which case it - * will cause future lines in this table - * to be indented. The lesser of the evils - * is to treat the first line by itself. - */ - -int html_printer::should_defer_table (int lines, struct text_glob *start, struct text_defn *cols_1) -{ - if (lines > 2) { - int i=0; - int c=count_columns(cols_1); - - count_hits(cols_1, count_columns(cols_1), indentation.vertical_limit); - rewind_text_to(start); - count_right_hits(cols_1, count_columns(cols_1)); - rewind_text_to(start); - while (i<c) { - if ((cols_1[i].is_used > 1) || (cols_1[i].right_hits > 1)) { - return( FALSE ); - } - i++; - } - /* - * first line (cols_1) is not aligned on any future column, we defer. - */ - return( TRUE ); - } - return( FALSE ); -} - -/* - * is_new_exact_right - returns TRUE if the, next_cols, has a word sitting - * on the right hand margin of last_guess. But only - * if no exact right word was found in last_cols. - */ - -int html_printer::is_new_exact_right (struct text_defn *last_guess, - struct text_defn *last_cols, - struct text_defn *next_cols) -{ - int n=count_columns(last_guess)-1; - return( FALSE ); - - if ((n>=0) && (last_guess[n].right != 0) && (last_cols[n].right != 0) && (next_cols[n].right != 0)) { - if ((last_cols[n].right != last_guess[n].right) && - ((next_cols[n].right == last_guess[n].right) || (next_cols[n].right == right_margin_indent))) { - return( TRUE ); - } - } - return( FALSE ); -} - -/* - * found_use_for_table - checks whether the some words on one line directly match - * the horizontal alignment of the line below. - * This is rather complex as we need to detect text tables - * such as .2C .IP Abstracts and indentations - * - * Algorithm is: - * - * read first line of text and calculate the significant - * gaps between words - * next next line of text and do the same - * if a conflict between these lines exists and - * first line is centered - * then - * return centered line - * elsif start of a table is found - * then - * repeat - * read next line of text and calculate significant gaps - * until conflict between the gaps is found - * record table - * return table found - * else - * return no table found - * fi - */ - -int html_printer::found_use_for_table (text_glob *start) -{ - text_glob *t; - struct text_defn all_words [MAX_WORDS_PER_LINE]; // logical OR of words on each line - struct text_defn words_1 [MAX_WORDS_PER_LINE]; // actual words found on first line - struct text_defn words_2 [MAX_WORDS_PER_LINE]; // actual words found on second line - struct text_defn cols_1 [MAX_WORDS_PER_LINE]; // columns found on line 1 - struct text_defn cols_2 [MAX_WORDS_PER_LINE]; // columns found on line 2 - struct text_defn last_words [MAX_WORDS_PER_LINE]; // actual words found on last line - struct text_defn last_cols [MAX_WORDS_PER_LINE]; // columns found so far - struct text_defn next_words [MAX_WORDS_PER_LINE]; // actual words found on last line (new) - struct text_defn next_cols [MAX_WORDS_PER_LINE]; // columns found on next line - struct text_defn last_guess [MAX_WORDS_PER_LINE]; // columns found on last line - // (logical AND of gaps (treat gaps = true)) - struct text_defn next_guess [MAX_WORDS_PER_LINE]; // columns found on next line - // (logical AND of gaps (treat gaps = true)) - struct text_defn prev_guess [MAX_WORDS_PER_LINE]; // temporary copy of last_guess - int i =0; - int lines =1; // number of lines read - int limit; // vertical limit reached in our table - int limit_1; // vertical position after line 1 - -#if 0 - if (strcmp(start->text_string, "<hr>") == 0) { - stop(); - } -#endif - - /* - * get first set of potential columns into last_line, call this last_guess - */ - limit = collect_columns(words_1, cols_1, 0, 0, MAX_WORDS_PER_LINE); - limit_1 = limit; - copy_line(last_guess, cols_1); - - /* - * initialize the all_words columns - if this should ever equal a complete line - * with no gaps then we terminate the table. - */ - copy_line(all_words, cols_1); - - /* - * and set the current limit found - */ - indentation.vertical_limit = limit; - - /* - * have we reached the end of page? - */ - if (page_contents->words.is_equal_to_head() || (limit == 0)) { - cols_2[0].left = 0; - cols_2[0].right = 0; - } else { - /* - * the answer to the previous question was no. - * So we need to examine the next line - */ - limit = collect_columns(words_2, cols_2, words_1, cols_1, MAX_WORDS_PER_LINE); - if (limit >= 0) { - lines++; - } - } - - /* - * now check to see whether the first line looks like a single centered line - */ - if (single_centered_line(cols_1, cols_2, start)) { - rewind_text_to(start); - write_centered_line(start); - /* - * indicate to caller than we have centered text, not found a table. - */ - indentation.no_of_columns = 0; - return( TRUE ); - } else if (! table_on) { - /* - * user does not allow us to find a table (we are allowed to find centered lines (above)) - */ - rewind_text_to(start); - return( FALSE ); - } - - /* - * remove any gaps from all_words - */ - combine_line(all_words, cols_2); - if (debug_table_on) { - display_columns(start->text_string, "[1] all_words" , all_words); - display_columns(start->text_string, "[1] cols_1" , cols_1); - display_columns(start->text_string, "[1] words_1" , words_1); - display_columns(start->text_string, "[1] cols_2" , cols_2); - display_columns(start->text_string, "[1] words_2" , words_2); - display_columns(start->text_string, "[1] last_guess", last_guess); - } - - /* - * next_guess = last_guess AND next_cols (where gap = true) - */ - - if (remove_white_using_words(prev_guess, last_guess, cols_2)) { - } - if (remove_white_using_words(next_guess, prev_guess, all_words)) { - } - - if (debug_table_on) { - display_columns(start->text_string, "[2] next_guess", next_guess); - } - - copy_line(prev_guess, cols_1); - combine_line(prev_guess, cols_2); - - /* - * if no sequence of words crosses a column and - * both the last column and all_words are not a full solid line of text - */ - if ((! conflict_with_words(next_guess, all_words)) && - (continue_searching_column(next_guess, next_guess, all_words)) && - (is_appropriate_to_start_table(cols_1, cols_2, prev_guess)) && - (! page_contents->words.is_equal_to_head()) && - ((end_region_vpos < 0) || (limit < end_region_vpos)) && - (limit > 0)) { - - /* - * subtract any columns which are bridged by a sequence of words - */ - - copy_line(next_cols , cols_2); - copy_line(next_words, words_2); - - do { - copy_line(prev_guess, next_guess); // copy next_guess away so we can compare it later - combine_line(last_guess, next_guess); - - if (debug_table_on) { - t = page_contents->words.get_data(); - display_columns(t->text_string, "[l] last_guess", last_guess); - } - indentation.vertical_limit = limit; - - copy_line(last_cols, next_cols); - copy_line(last_words, next_words); - if (page_contents->words.is_equal_to_head()) { - /* - * terminate the search - */ - next_cols[0].left = 0; - next_cols[0].right = 0; - } else { - limit = collect_columns(next_words, next_cols, last_words, last_cols, MAX_WORDS_PER_LINE); - lines++; - } - - combine_line(all_words, next_cols); - if (debug_table_on) { - display_columns(t->text_string, "[l] all_words" , all_words); - display_columns(t->text_string, "[l] last_cols" , last_cols); - display_columns(t->text_string, "[l] next_words", next_words); - display_columns(t->text_string, "[l] next_cols" , next_cols); - } - - if (limit >= 0) { - /* - * (if limit is < 0 then the table ends anyway.) - * we check to see whether we should combine close columns. - */ - can_loose_column(start, last_guess, limit); - } - t = page_contents->words.get_data(); -#if 0 - if (strcmp(t->text_string, "heT") == 0) { - stop(); - } -#endif - - } while ((! remove_white_using_words(next_guess, last_guess, next_cols)) && - (! conflict_with_words(next_guess, all_words)) && - (continue_searching_column(next_guess, last_guess, all_words)) && - ((is_continueous_column(prev_guess, last_cols)) || (is_exact_left(last_guess, next_cols))) && - (! is_new_exact_right(last_guess, last_cols, next_cols)) && - (! page_contents->words.is_equal_to_head()) && - (! check_lack_of_hits(next_guess, last_guess, start, limit)) && - ((end_region_vpos <= 0) || (t->minv < end_region_vpos)) && - (limit >= 0)); - lines--; - } - - if (limit < 0) { - indentation.vertical_limit = limit; - } - - if (page_contents->words.is_equal_to_head()) { - // end of page check whether we should include everything - if ((! conflict_with_words(next_guess, all_words)) && - (continue_searching_column(next_guess, last_guess, all_words)) && - ((is_continueous_column(prev_guess, last_cols)) || (is_exact_left(last_guess, next_cols)))) { - // end of page reached - therefore include everything - page_contents->words.start_from_tail(); - t = page_contents->words.get_data(); - combine_line(last_guess, next_guess); - indentation.vertical_limit = t->minv; - } - } else { - t = page_contents->words.get_data(); - if (((! conflict_with_words(last_guess, all_words))) && - (t->minv > end_region_vpos) && (end_region_vpos > 0)) { - indentation.vertical_limit = limit; - } - if ((end_region_vpos > 0) && (t->minv > end_region_vpos)) { - indentation.vertical_limit = min(indentation.vertical_limit, end_region_vpos+1); - } else if (indentation.vertical_limit < 0) { - // -1 as we don't want to include section heading itself - indentation.vertical_limit = -indentation.vertical_limit-1; - } - } - - if (debug_table_on) { - display_columns(start->text_string, "[1] all_words" , all_words); - display_columns(start->text_string, "[1] cols_1" , cols_1); - display_columns(start->text_string, "[1] words_1" , words_1); - display_columns(start->text_string, "[1] cols_2" , cols_2); - display_columns(start->text_string, "[1] words_2" , words_2); - display_columns(start->text_string, "[1] last_guess", last_guess); - display_columns(start->text_string, "[1] next_guess", next_guess); - } - rewind_text_to(start); - - i = count_columns(last_guess); - if ((i>1) || (right_indentation(last_guess))) { - - // was (continue_searching_column(last_guess, last_guess, all_words)))) { - if (should_defer_table(lines, start, cols_1)) { - /* - * yes, but let us check for a single line table - */ - lines = 1; - copy_line(last_guess, cols_1); - } - - if (is_small_table(lines, last_guess, words_1, cols_1, words_2, cols_2, - &indentation.vertical_limit, &limit_1)) { - - // copy match into permenant html_table - - if (indentation.columns != 0) { - free(indentation.columns); - } - if (debug_table_on) { - display_columns(start->text_string, "[x] last_guess", last_guess); - } - add_column_gaps(last_guess); - if (debug_table_on) { - display_columns(start->text_string, "[g] last_guess", last_guess); - } - - /* - * +1 for the potential header_margin - * +1 for null - */ - - indentation.no_of_columns = count_columns(last_guess); - indentation.columns = (struct text_defn *)malloc((indentation.no_of_columns+2)*sizeof(struct text_defn)); - - i=0; - while (i<=indentation.no_of_columns) { - indentation.columns[i].left = last_guess[i].left; - indentation.columns[i].right = last_guess[i].right; - i++; - } - - if (indentation.no_of_columns>0) { - assign_used_columns(start); - rewind_text_to(start); - calculate_percentage_width(start); - - if (debug_table_on) { - display_columns(start->text_string, "[g] indentation.columns", indentation.columns); - } - - /* - * clearly a single column 100% is not worth using a table. - * Also we check to see whether the first line is sensibly - * part of this table. - */ - if (is_a_full_width_column()) { - indentation.no_of_columns = 0; - free( indentation.columns ); - indentation.columns = 0; - } else { - return( TRUE ); - } - } - } - } - return( FALSE ); -} - -/* - * define_cell - creates a table cell using the percentage width. - */ - -void html_printer::define_cell (int i) -{ - html.put_string("<td valign=\"top\" align=\"left\" width=\""); - html.put_number(indentation.columns[i].percent); - html.put_string("%\">\n"); -} - -/* - * column_display_word - given a left, right pair and the indentation.vertical_limit - * write out html text within this region. - */ - -void html_printer::column_display_word (int cell, int vert, int left, int right, int next) -{ - text_glob *g=page_contents->words.get_data(); - - supress_sub_sup = TRUE; - if (left != next) { - define_cell(cell); - begin_paragraph_no_height(left_alignment); - while ((g != 0) && (g->minv <= vert)) { - if ((left <= g->minh) && (g->minh<right)) { - char *postword=html_position_text(g, left, right); - - if (header.written_header) { - fatal("should never generate a header inside a table"); - } else { - if (g->is_raw_command) { - html.put_string((char *)g->text_string); - } else { - translate_to_html(g); - } - if (postword != 0) { - html.put_string(postword); - } - issued_newline = FALSE; - } - } - if (page_contents->words.is_equal_to_tail()) { - g = 0; - } else { - page_contents->words.move_right(); - g=page_contents->words.get_data(); - } - } - end_paragraph(); - html.put_string("</td>\n"); - if (g != 0) { - page_contents->words.move_left(); - // and correct output_vpos - g=page_contents->words.get_data(); - output_vpos = g->minv; - } - } -} - -/* - * total_percentages - returns the total of all the percentages in the table. - */ - -int html_printer::total_percentages () -{ - int i; - int sum=0; - - for (i=0; i<indentation.no_of_columns; i++) { - sum += indentation.columns[i].percent; - } - return( sum ); -} - -/* - * start_table - creates a table according with parameters contained within class html_table. - */ - -void html_printer::start_table (void) -{ - save_paragraph(); - html.put_string("\n<table width=\""); - html.put_number(total_percentages()); - html.put_string("%\" rules=\"none\" frame=\"none\" cols=\""); - html.put_number(indentation.no_of_columns); - html.put_string("\" cellspacing=\"0\" cellpadding=\"0\">\n"); -} - -/* - * end_table - finishes off a table. - */ - -void html_printer::end_table (void) -{ - html.put_string("</table>\n"); - indentation.no_of_columns = 0; - restore_paragraph(); - supress_sub_sup = TRUE; -} - - -/* - * is_in_table - returns TRUE if we are inside an html table. - */ - -int html_printer::is_in_table (void) -{ - return( indentation.no_of_columns != 0 ); -} - - -/* - * column_calculate_right_margin - scan through the column and find the right most margin - */ - -int html_printer::column_calculate_right_margin (int left, int right) -{ - if (left == right) { - return( right ); - } else { - int rightmost =-1; - int count = 0; - text_glob *start = page_contents->words.get_data(); - text_glob *g = start; - - while ((g != 0) && (g->minv <= indentation.vertical_limit)) { - if ((left <= g->minh) && (g->minh<right)) { - if (debug_on) { - fprintf(stderr, "right word = %s %d\n", g->text_string, g->maxh); fflush(stderr); - } - if (g->maxh == rightmost) { - count++; - } else if (g->maxh > rightmost) { - count = 1; - rightmost = g->maxh; - } - if (g->maxh > right) { - if (debug_on) { - fprintf(stderr, "problem as right word = %s %d [%d..%d]\n", - g->text_string, right, g->minh, g->maxh); fflush(stderr); - // stop(); - } - } - } - page_contents->words.move_right(); - if (page_contents->words.is_equal_to_head()) { - g = 0; - page_contents->words.start_from_tail(); - } else { - g=page_contents->words.get_data(); - } - } - rewind_text_to(start); - if (rightmost == -1) { - return( right ); // no words in this column - } else { - return( rightmost ); - } - } -} - -/* - * column_calculate_left_margin - scan through the column and find the left most margin - */ - -int html_printer::column_calculate_left_margin (int left, int right) -{ - if (left == right) { - return( left ); - } else { - int leftmost=right; - text_glob *start = page_contents->words.get_data(); - text_glob *g = start; - - while ((g != 0) && (g->minv <= indentation.vertical_limit)) { - if ((left <= g->minh) && (g->minh<right)) { - leftmost = min(g->minh, leftmost); - } - page_contents->words.move_right(); - if (page_contents->words.is_equal_to_head()) { - g = 0; - page_contents->words.start_from_tail(); - } else { - g=page_contents->words.get_data(); - } - } - rewind_text_to(start); - if (leftmost == right) { - return( left ); // no words in this column - } else { - return( leftmost ); - } - } -} - -/* - * find_column_index - returns the index to the column in which glob, t, exists. - */ - -int html_printer::find_column_index_in_line (text_glob *t, text_defn *line) -{ - int i=0; - - while ((line != 0) && ((line[i].right != 0) || (line[i].right != 0)) && - (! ((line[i].left<=t->minh) && (line[i].right>t->minh)))) { - i++; - } - return( i ); -} - -/* - * find_column_index - returns the index to the column in which glob, t, exists. - */ - -int html_printer::find_column_index (text_glob *t) -{ - int i=0; - - while ((i<indentation.no_of_columns) && - (! ((indentation.columns[i].left<=t->minh) && - (indentation.columns[i].right>t->minh)))) { - i++; - } - return( i ); -} - -/* - * determine_row_limit - checks each row to see if there is a gap in a cell. - * We return the vertical position after the empty cell - * at the start of the next line. - */ - -int html_printer::determine_row_limit (text_glob *start, int v) -{ - text_glob *t; - int i; - int vpos, last, prev; - text_glob *is_gap[MAX_WORDS_PER_LINE]; - text_glob zero(&start->text_style, 0, 0, 0, 0, 0, 0, 0, 0); - -#if 1 - if ((v == -1) && (strcmp(start->text_string, "CASE") == 0)) { - stop(); - } -#endif - - if (v >= indentation.vertical_limit) { - return( v+1 ); - } else { - /* - * initially we start with all gaps in our table - * after a gap we start a new row - * here we set the gap array to the previous line - */ - - if (v>=0) { - t = page_contents->words.get_data(); - if (t->minv < v) { - do { - page_contents->words.move_right(); - t = page_contents->words.get_data(); - } while ((! page_contents->words.is_equal_to_head()) && - (t->minv <= v)); - } - } - if (page_contents->words.is_equal_to_head()) { - t = &zero; - } else { - page_contents->words.move_left(); - t = page_contents->words.get_data(); - } - - prev = t->minv; - for (i=0; i<indentation.no_of_columns; i++) { - is_gap[i] = t; - } - - if (page_contents->words.is_equal_to_tail()) { - rewind_text_to(start); - return( indentation.vertical_limit ); - } else { - page_contents->words.move_right(); - } - t = page_contents->words.get_data(); - vpos = t->minv; - - // now check each row for a gap - do { - last = vpos; - vpos = t->minv; - if (vpos > indentation.vertical_limit) { - // we have reached the end of the table, quit - rewind_text_to(start); - return( indentation.vertical_limit ); - } - - i = find_column_index(t); - if (i>=indentation.no_of_columns) { - error("find_column_index has failed"); - stop(); - } else { - if (! is_on_same_line(t, last)) { - prev = last; - } - - if ((! is_on_same_line(is_gap[i], vpos)) && (! is_on_same_line(is_gap[i], prev)) && - (indentation.columns[i].is_used)) { - // no word on previous line - must be a gap - force alignment of row - rewind_text_to(start); - return( prev ); - } - is_gap[i] = t; - } - page_contents->words.move_right(); - t = page_contents->words.get_data(); - } while ((! page_contents->words.is_equal_to_head()) && - (vpos < indentation.vertical_limit) && (vpos >= last)); - page_contents->words.move_left(); - t = page_contents->words.get_data(); - rewind_text_to(start); - return( indentation.vertical_limit ); - } -} - -/* - * assign_used_columns - sets the is_used field of the column array of records. - */ - -void html_printer::assign_used_columns (text_glob *start) -{ - text_glob *t = start; - int i; - - for (i=0; i<indentation.no_of_columns; i++) { - indentation.columns[i].is_used = FALSE; - } - - rewind_text_to(start); - if (! page_contents->words.is_empty()) { - do { - i = find_column_index(t); - if (indentation.columns[i].right != 0) { - if (debug_table_on) { - fprintf(stderr, "[%s] in column %d at %d..%d limit %d\n", t->text_string, - i, t->minv, t->maxv, indentation.vertical_limit); fflush(stderr); - } - indentation.columns[i].is_used = TRUE; - } - page_contents->words.move_right(); - t = page_contents->words.get_data(); - } while ((t->minv<indentation.vertical_limit) && - (! page_contents->words.is_equal_to_head())); - } - if (debug_table_on) { - for (i=0; i<indentation.no_of_columns; i++) { - fprintf(stderr, " <left=%d right=%d is_used=%d> ", - indentation.columns[i].left, - indentation.columns[i].right, - indentation.columns[i].is_used); - } - fprintf(stderr, "\n"); - fflush(stderr); - } -} - -/* - * adjust_margin_percentages - so far we have ignored the header_indent - * and just considered left_margin_indent..right_margin_indent. - * (We do this since we can assume 100% is total width for main text). - * However as header_indent can be < left_margin_indent we need to - * recalculate the real percentages in the light of the extended width. - */ - -void html_printer::adjust_margin_percentages (void) -{ - if ((header_indent < left_margin_indent) && (header_indent != -1)) { - /* - * recalculation necessary - */ - int i=0; - - while (i<indentation.no_of_columns) { - indentation.columns[i].percent = (indentation.columns[i].percent * - (right_margin_indent - left_margin_indent)) / - (right_margin_indent - header_indent); - i++; - } - // remove_zero_percentage_column(); - } -} - -/* - * foreach_column_include_text - foreach column in a table place the - * appropriate html text. - */ - -void html_printer::foreach_column_include_text (text_glob *start) -{ - if (indentation.no_of_columns>0) { - int i; - int left, right; - int limit=-1; - - start_table(); - rewind_text_to(start); - count_right_hits(indentation.columns, indentation.no_of_columns); - rewind_text_to(start); - - do { - limit = determine_row_limit(start, limit); // find the bottom of the next row - html.put_string("<tr valign=\"top\" align=\"left\">\n"); - i=0; - start = page_contents->words.get_data(); - while (i<indentation.no_of_columns) { - // reset the output position to the start of column - rewind_text_to(start); - output_vpos = start->minv; - output_hpos = indentation.columns[i].left; - // and display each column until limit - right = column_calculate_right_margin(indentation.columns[i].left, - indentation.columns[i].right); - left = column_calculate_left_margin(indentation.columns[i].left, - indentation.columns[i].right); - - if (right>indentation.columns[i].right) { - if (debug_on) { - fprintf(stderr, "assert calculated right column edge is greater than column\n"); fflush(stderr); - // stop(); - } - } - - if (left<indentation.columns[i].left) { - if (debug_on) { - fprintf(stderr, "assert calculated left column edge is less than column\n"); fflush(stderr); - // stop(); - } - } - - if ((indentation.columns[i].right_hits == 1) && - (indentation.columns[i].right != right_margin_indent)) { - indentation.wrap_margin = FALSE; - if (debug_on) { - fprintf(stderr, "turning auto wrap off during column %d for start word %s\n", - i, start->text_string); - fflush(stderr); - // stop(); - } - } else { - indentation.wrap_margin = TRUE; - } - - column_display_word(i, limit, left, right, indentation.columns[i].right); - i++; - } - - if (page_contents->words.is_equal_to_tail()) { - start = 0; - } else { - page_contents->words.sub_move_right(); - if (page_contents->words.is_empty()) { - start = 0; - } else { - start = page_contents->words.get_data(); - } - } - - html.put_string("</tr>\n"); - } while (((limit < indentation.vertical_limit) && (start != 0) && - (! page_contents->words.is_empty())) || (limit == -1)); - end_table(); - - if (start == 0) { - // finished page remove all words - page_contents->words.start_from_head(); - while (! page_contents->words.is_empty()) { - page_contents->words.sub_move_right(); - } - } else if (! page_contents->words.is_empty()) { - page_contents->words.move_left(); - } - } -} - -/* - * write_centered_line - generates a line of centered text. - */ - -void html_printer::write_centered_line (text_glob *g) -{ - int current_vpos=g->minv; - - move_vertical(g, center_alignment); - - header.written_header = FALSE; - supress_sub_sup = TRUE; - output_vpos = g->minv; - output_hpos = g->minh; - do { - char *postword=html_position_text(g, left_margin_indent, right_margin_indent); - - if (! header.written_header) { - if (g->is_raw_command) { - html.put_string((char *)g->text_string); - } else { - translate_to_html(g); - } - if (postword != 0) { - html.put_string(postword); - } - need_one_newline = TRUE; - issued_newline = FALSE; - } - page_contents->words.move_right(); - g = page_contents->words.get_data(); - } while ((! page_contents->words.is_equal_to_head()) && (is_on_same_line(g, current_vpos))); - page_contents->words.move_left(); // so when we move right we land on the word following this centered line - need_one_newline = TRUE; -} - -/* - * is_in_middle - returns TRUE if the text defn, t, is in the middle of the page. - */ - -int html_printer::is_in_middle (int left, int right) -{ - return( abs(abs(left-left_margin_indent) - abs(right_margin_indent-right)) <= CENTER_TOLERANCE ); -} - -/* - * single_centered_line - returns TRUE if first is a centered line with a different - * margin to second. - */ - -int html_printer::single_centered_line (text_defn *first, text_defn *second, text_glob *g) -{ - return( - ((count_columns(first) == 1) && (first[0].left != left_margin_indent) && - (first[0].left != second[0].left) && is_in_middle(first->left, first->right)) - ); -} - -/* - * check_able_to_use_center - returns TRUE if we can see a centered line. - */ - -int html_printer::check_able_to_use_center (text_glob *g) -{ - if (auto_on && table_on && ((! is_on_same_line(g, output_vpos)) || issued_newline) && (! using_table_for_indent())) { - // we are allowed to check for centered line - // first check to see whether we might be looking at a set of columns - struct text_defn last_guess[MAX_WORDS_PER_LINE]; - struct text_defn last_words[MAX_WORDS_PER_LINE]; - - collect_columns(last_words, last_guess, 0, 0, MAX_WORDS_PER_LINE); - - rewind_text_to(g); - if ((count_columns(last_guess) == 1) && (is_in_middle(last_guess[0].left, last_guess[0].right))) { - write_centered_line(g); - return( TRUE ); - } - } - return( FALSE ); -} - -/* - * check_able_to_use_table - examines forthcoming text to see whether we can - * better format it by using an html transparent table. - */ - -int html_printer::check_able_to_use_table (text_glob *g) -{ - if (auto_on && ((! is_on_same_line(g, output_vpos)) || issued_newline) && (! using_table_for_indent())) { - // we are allowed to check for table - - if ((output_hpos != right_margin_indent) && (found_use_for_table(g))) { - foreach_column_include_text(g); - return( TRUE ); - } - } - return( FALSE ); -} - -/* - * move_vertical - if we are using html auto formatting then decide whether to - * break the line via a <br> or a </p><p> sequence. - */ - -void html_printer::move_vertical (text_glob *g, paragraph_type p) -{ - int r = font::res; - int height = (g->text_style.point_size+2)*r/72; // --fixme-- we always assume VS is PS+2 (could do better) - int temp_vpos; - - if (auto_on) { - if ((more_than_line_break(output_vpos, g->minv, height)) || (p != current_paragraph->para_type)) { - end_paragraph(); - begin_paragraph(p); - } else { - html_newline(); - } - } else { - if (output_vpos == -1) { - temp_vpos = g->minv; - } else { - temp_vpos = output_vpos; - } - - force_begin_paragraph(); - if (need_one_newline) { - html_newline(); - temp_vpos += height; - } else { - need_one_newline = TRUE; - } - - while ((temp_vpos < g->minv) && (more_than_line_break(temp_vpos, g->minv, height))) { - html_newline(); - temp_vpos += height; - } - } -} - -/* - * emit_space - emits a space within html, it checks for the font type and - * will change font depending upon, g. Courier spaces are larger - * than roman so we need consistancy when changing between them. - */ - -void html_printer::emit_space (text_glob *g, int force_space) -{ - if (! current_paragraph->need_paragraph) { - // only generate a space if we have written a word - as html will ignore it otherwise - if ((output_style != g->text_style) && (g->text_style.f != 0)) { - terminate_current_font(); - } - if (force_space || (g->minh > output_hpos)) { - html.put_string(" "); - } - change_font(g, TRUE); - } -} - -/* - * html_position_text - determine whether the text is subscript/superscript/normal - * or a header. - */ - -char *html_printer::html_position_text (text_glob *g, int left_margin, int right_margin) -{ - char *postword=0; - - begin_paragraph(left_alignment); - - if ((! header.written_header) && - (is_on_same_line(g, output_vpos) || - pretend_is_on_same_line(g, left_margin, right_margin))) { - - /* - * check whether we should supress superscripts and subscripts. - * I guess we might be able to do better by examining text on this line - * --fixme-- - */ - - if ((! is_on_same_line(g, output_vpos)) && (pretend_is_on_same_line(g, left_margin, right_margin))) { - supress_sub_sup = TRUE; - } - header.written_header = FALSE; - force_begin_paragraph(); - - // check whether we need to insert white space between words on 'same' line - if (pretend_is_on_same_line(g, left_margin, right_margin)) { - emit_space(g, TRUE); - } - - // check whether the font was reset after generating an image - if (output_style.f == 0) { - change_font(g, TRUE); - } - - if (looks_like_subscript(g)) { - - g->text_style.point_size = output_style.point_size; - g->minv = output_vpos; // this ensures that output_vpos doesn't alter - // which allows multiple subscripted words - move_horizontal(g, left_margin); - html.put_string("<sub>"); - postword = "</sub>"; - } else if (looks_like_superscript(g)) { - - g->text_style.point_size = output_style.point_size; - g->minv = output_vpos; - - move_horizontal(g, left_margin); - html.put_string("<sup>"); - postword = "</sup>"; - } else { - move_horizontal(g, left_margin); - } - supress_sub_sup = FALSE; - } else { - // we have found a new line - if (! header.written_header) { - move_vertical(g, left_alignment); - } - header.written_header = FALSE; - - if (processed_header(g)) { - // we must not alter output_vpos as we have peeped at the next word - // and set vpos to this - to ensure we do not generate a <br> after - // a heading. (The html heading automatically generates a line break) - output_hpos = left_margin; - return( postword ); - } else { - force_begin_paragraph(); - if ((! is_in_table()) && (margin_on)) { - make_html_indent(left_margin); - } - if (g->minh-left_margin != 0) { - make_html_indent(g->minh-left_margin); - } - change_font(g, TRUE); - supress_sub_sup = FALSE; - } - } - output_vpos = g->minv; - output_hpos = g->maxh; - return( postword ); -} - - -int html_printer::html_position_region (void) -{ - int r = font::res; - int height = output_style.point_size*r/72; - int temp_vpos; - int is_center = FALSE; - - if (output_style.point_size != 0) { - if (output_vpos != start_region_vpos) { - - // graphic starts on a different line - if (output_vpos == -1) { - temp_vpos = start_region_vpos; - } else { - temp_vpos = output_vpos; - } - supress_sub_sup = TRUE; - if (need_one_newline) { - html_newline(); - temp_vpos += height; - } else { - need_one_newline = TRUE; - } - - while ((temp_vpos < start_region_vpos) && - (more_than_line_break(temp_vpos, start_region_vpos, height))) { - html_newline(); - temp_vpos += height; - } - } - } - if (auto_on && (is_in_middle(start_region_hpos, end_region_hpos))) { - is_center = TRUE; - } else { - if (start_region_hpos > get_left()) { - make_html_indent(start_region_hpos-get_left()); - } - } - output_vpos = start_region_vpos; - output_hpos = start_region_hpos; - return( is_center ); -} - -/* - * gs_x - translate and scale the x axis - */ - -int html_printer::gs_x (int x) -{ - x += IMAGE_BOARDER_PIXELS/2; - return((x-start_region_hpos)*postscript_res/font::res); -} - - -/* - * gs_y - translate and scale the y axis - */ - -int html_printer::gs_y (int y) -{ - int yoffset=((int)(A4_PAGE_LENGTH*(double)font::res))-end_region_vpos; - - y += IMAGE_BOARDER_PIXELS/2; - return( (y+yoffset)*postscript_res/font::res ); -} - - -void html_printer::troff_position_text (text_glob *g) -{ - change_font(g, FALSE); - - troff.put_string("V"); - troff.put_number(gs_y(g->maxv)); - troff.put_string("\n"); - - troff.put_string("H"); - troff.put_number(gs_x(g->minh)); - troff.put_string("\n"); -} - -void html_printer::troff_change_font (const char *fontname, int size, int font_no) -{ - troff.put_string("x font "); - troff.put_number(font_no); - troff.put_string(" "); - troff.put_string(fontname); - troff.put_string("\nf"); - troff.put_number(font_no); - troff.put_string("\ns"); - troff.put_number(size*1000); - troff.put_string("\n"); -} - - -void html_printer::set_style(const style &sty) -{ -#if 0 - const char *fontname = sty.f->get_name(); - if (fontname == 0) - fatal("no internalname specified for font"); - - change_font(fontname, (font::res/(72*font::sizescale))*sty.point_size); -#endif -} - -void html_printer::end_of_line() -{ - flush_sbuf(); - output_hpos = -1; -} - -void html_printer::html_display_word (text_glob *g) -{ -#if 0 - if (strcmp(g->text_string, "ot") == 0) { - stop(); - } -#endif - if (! check_able_to_use_table(g)) { - char *postword=html_position_text(g, left_margin_indent, right_margin_indent); - - if (! header.written_header) { - if (g->is_raw_command) { - html.put_string((char *)g->text_string); - } else { - translate_to_html(g); - } - if (postword != 0) { - html.put_string(postword); - } - need_one_newline = TRUE; - issued_newline = FALSE; - } - } -} - -void html_printer::troff_display_word (text_glob *g) -{ - troff_position_text(g); - if (g->is_raw_command) { - int l=strlen((char *)g->text_string); - if (l == 1) { - troff.put_string("c"); - troff.put_string((char *)g->text_string); - troff.put_string("\n"); - } else if (l > 1) { - troff.put_string("C"); - troff.put_troffps_char((char *)g->text_string); - troff.put_string("\n"); - } - } else { - troff_position_text(g); - troff.put_string("t"); - troff.put_translated_string((const char *)g->text_string); - troff.put_string("\n"); - } -} - -void html_printer::display_word (text_glob *g, int is_to_html) -{ - if (is_to_html) { - html_display_word(g); - } else if ((g->is_raw_command) && (g->is_html_command)) { - // found a raw html command inside a graphic glob. - // We should emit the command to the html device, but of course we - // cannot place it correctly as we are dealing with troff words. - // Remember output_vpos will refer to troff and not html. - html.put_string((char *)g->text_string); - } else { - troff_display_word(g); - } -} - -/* - * translate_to_html - translates a textual string into html text - */ - -void html_printer::translate_to_html (text_glob *g) -{ - char buf[MAX_STRING_LENGTH]; - - str_translate_to_html(g->text_style.f, buf, MAX_STRING_LENGTH, - g->text_string, g->text_length, TRUE); - html.put_string(buf); -} - -/* - * html_knows_about - given a character name, troff, return TRUE - * if we know how to display this character using - * html unicode. - */ - -int html_printer::html_knows_about (char *troff) -{ - // --fixme-- needs to have similar code as above - return( FALSE ); -} - -/* - * display_fill - generates a troff format fill command - */ - -void html_printer::display_fill (graphic_glob *g) -{ - troff.put_string("Df ") ; - troff.put_number(g->fill); - troff.put_string(" 0\n"); -} - -/* - * display_line - displays a line using troff format - */ - -void html_printer::display_line (graphic_glob *g, int is_to_html) -{ - if (is_to_html) { - fatal("cannot emit lines in html"); - } - if (g->code == 'l') { - // straight line - - troff.put_string("V"); - troff.put_number(gs_y(g->point[0].y)); - troff.put_string("\n"); - - troff.put_string("H"); - troff.put_number(gs_x(g->point[0].x)); - troff.put_string("\n"); - - display_fill(g); - - troff.put_string("Dl "); - troff.put_number((g->point[1].x-g->point[0].x)*postscript_res/font::res); - troff.put_string(" "); - troff.put_number((g->point[1].y-g->point[0].y)*postscript_res/font::res); - troff.put_string("\n"); - // printf("line %c %d %d %d %d size %d\n", (char)g->code, g->point[0].x, g->point[0].y, - // g->point[1].x, g->point[1].y, g->size); - } else if ((g->code == 'c') || (g->code == 'C')) { - // circle - - int xradius = (g->maxh - g->minh) / 2; - int yradius = (g->maxv - g->minv) / 2; - // center of circle or elipse - - troff.put_string("V"); - troff.put_number(gs_y(g->minv+yradius)); - troff.put_string("\n"); - - troff.put_string("H"); - troff.put_number(gs_x(g->minh)); - troff.put_string("\n"); - - display_fill(g); - - if (g->code == 'c') { - troff.put_string("Dc "); - } else { - troff.put_string("DC "); - } - - troff.put_number(xradius*2*postscript_res/font::res); - troff.put_string("\n"); - - } else if ((g->code == 'e') || (g->code == 'E')) { - // ellipse - - int xradius = (g->maxh - g->minh) / 2; - int yradius = (g->maxv - g->minv) / 2; - // center of elipse - this is untested - - troff.put_string("V"); - troff.put_number(gs_y(g->minv+yradius)); - troff.put_string("\n"); - - troff.put_string("H"); - troff.put_number(gs_x(g->minh)); - troff.put_string("\n"); - - display_fill(g); - - if (g->code == 'e') { - troff.put_string("De "); - } else { - troff.put_string("DE "); - } - - troff.put_number(xradius*2*postscript_res/font::res); - troff.put_string(" "); - troff.put_number(yradius*2*postscript_res/font::res); - troff.put_string("\n"); - } else if ((g->code == 'p') || (g->code == 'P')) { - // polygon - troff.put_string("V"); - troff.put_number(gs_y(g->yc)); - troff.put_string("\n"); - - troff.put_string("H"); - troff.put_number(gs_x(g->xc)); - troff.put_string("\n"); - - display_fill(g); - - if (g->code == 'p') { - troff.put_string("Dp"); - } else { - troff.put_string("DP"); - } - - int i; - int xc=g->xc; - int yc=g->yc; - for (i=0; i<g->nopoints; i++) { - troff.put_string(" "); - troff.put_number((g->point[i].x-xc)*postscript_res/font::res); - troff.put_string(" "); - troff.put_number((g->point[i].y-yc)*postscript_res/font::res); - xc = g->point[i].x; - yc = g->point[i].y; - } - troff.put_string("\n"); - } else if (g->code == 'a') { - // arc - troff.put_string("V"); - troff.put_number(gs_y(g->yc)); - troff.put_string("\n"); - - troff.put_string("H"); - troff.put_number(gs_x(g->xc)); - troff.put_string("\n"); - - display_fill(g); - - troff.put_string("Da"); - - int i; - - for (i=0; i<g->nopoints; i++) { - troff.put_string(" "); - troff.put_number(g->point[i].x*postscript_res/font::res); - troff.put_string(" "); - troff.put_number(g->point[i].y*postscript_res/font::res); - } - troff.put_string("\n"); - } else if (g->code == '~') { - // spline - troff.put_string("V"); - troff.put_number(gs_y(g->yc)); - troff.put_string("\n"); - - troff.put_string("H"); - troff.put_number(gs_x(g->xc)); - troff.put_string("\n"); - - display_fill(g); - - troff.put_string("D~"); - - int i; - int xc=g->xc; - int yc=g->yc; - for (i=0; i<g->nopoints; i++) { - troff.put_string(" "); - troff.put_number((g->point[i].x-xc)*postscript_res/font::res); - troff.put_string(" "); - troff.put_number((g->point[i].y-yc)*postscript_res/font::res); - xc = g->point[i].x; - yc = g->point[i].y; - } - troff.put_string("\n"); - } -} - - -/* - * flush_sbuf - flushes the current sbuf into the list of glyphs. - */ - -void html_printer::flush_sbuf() -{ - if (sbuf_len > 0) { - int r=font::res; // resolution of the device - set_style(sbuf_style); - - page_contents->add(&sbuf_style, sbuf, sbuf_len, - sbuf_vpos-sbuf_style.point_size*r/72, sbuf_start_hpos, - sbuf_vpos , sbuf_end_hpos); - - output_hpos = sbuf_end_hpos; - output_vpos = sbuf_vpos; - sbuf_len = 0; - sbuf_dmark_hpos = -1; - } -} - - -void html_printer::set_line_thickness(const environment *env) -{ - line_thickness = env->size; - printf("line thickness = %d\n", line_thickness); -} - -void html_printer::draw(int code, int *p, int np, const environment *env) -{ - switch (code) { - - case 'l': - if (np == 2) { - page_contents->add_line(code, - env->hpos, env->vpos, env->hpos+p[0], env->vpos+p[1], - env->size, fill); - } else { - error("2 arguments required for line"); - } - break; - case 't': - { - if (np == 0) { - line_thickness = -1; - } else { - // troff gratuitously adds an extra 0 - if (np != 1 && np != 2) { - error("0 or 1 argument required for thickness"); - break; - } - line_thickness = p[0]; - } - break; - } - - case 'P': - // fall through - case 'p': - { - if (np & 1) { - error("even number of arguments required for polygon"); - break; - } - if (np == 0) { - error("no arguments for polygon"); - break; - } - // firstly lets add our current position to polygon - int oh=env->hpos; - int ov=env->vpos; - int i=0; - - while (i<np) { - p[i+0] += oh; - p[i+1] += ov; - oh = p[i+0]; - ov = p[i+1]; - i += 2; - } - // now store polygon in page - page_contents->add_polygon(code, np, p, env->hpos, env->vpos, env->size, fill); - } - break; - case 'E': - // fall through - case 'e': - if (np != 2) { - error("2 arguments required for ellipse"); - break; - } - page_contents->add_line(code, - env->hpos, env->vpos-p[1]/2, env->hpos+p[0], env->vpos+p[1]/2, - env->size, fill); - - break; - case 'C': - // fill circle - - case 'c': - { - // troff adds an extra argument to C - if (np != 1 && !(code == 'C' && np == 2)) { - error("1 argument required for circle"); - break; - } - page_contents->add_line(code, - env->hpos, env->vpos-p[0]/2, env->hpos+p[0], env->vpos+p[0]/2, - env->size, fill); - } - break; - case 'a': - { - if (np == 4) { - double c[2]; - - if (adjust_arc_center(p, c)) { - page_contents->add_arc('a', env->hpos, env->vpos, p, c, env->size, fill); - } else { - // a straignt line - page_contents->add_line('l', env->hpos, env->vpos, p[0]+p[2], p[1]+p[3], env->size, fill); - } - } else { - error("4 arguments required for arc"); - } - } - break; - case '~': - { - if (np & 1) { - error("even number of arguments required for spline"); - break; - } - if (np == 0) { - error("no arguments for spline"); - break; - } - // firstly lets add our current position to spline - int oh=env->hpos; - int ov=env->vpos; - int i=0; - - while (i<np) { - p[i+0] += oh; - p[i+1] += ov; - oh = p[i+0]; - ov = p[i+1]; - i += 2; - } - page_contents->add_spline('~', env->hpos, env->vpos, np, p, env->size, fill); - } - break; - case 'f': - { - if (np != 1 && np != 2) { - error("1 argument required for fill"); - break; - } - fill = p[0]; - if (fill < 0 || fill > FILL_MAX) { - // This means fill with the current color. - fill = FILL_MAX + 1; - } - break; - } - - default: - error("unrecognised drawing command `%1'", char(code)); - break; - } -} - - -void html_printer::begin_page(int n) -{ - page_number = n; - html.begin_comment("Page: ").comment_arg(i_to_a(page_number)).end_comment();; - no_of_printed_pages++; - - output_style.f = 0; - output_space_code = 32; - output_draw_point_size = -1; - output_line_thickness = -1; - output_hpos = -1; - output_vpos = -1; -} - -void testing (text_glob *g) {} - -void html_printer::flush_graphic (void) -{ - graphic_glob g; - - graphic_level = 0; - page_contents->is_in_graphic = FALSE; - - g.minv = -1; - g.maxv = -1; - calculate_region_range(&g); - if (g.minv != -1) { - page_contents->make_new_region(&g); - } - move_region_to_page(); -} - -void html_printer::end_page(int) -{ - flush_sbuf(); - flush_graphic(); - flush_page(); -} - -font *html_printer::make_font(const char *nm) -{ - return html_font::load_html_font(nm); -} - -html_printer::~html_printer() -{ - if (fseek(tempfp, 0L, 0) < 0) - fatal("fseek on temporary file failed"); - html.set_file(stdout); - fputs("<html>\n", stdout); - fputs("<head>\n", stdout); - fputs("<meta name=\"Content-Style\" content=\"text/css\">\n", stdout); - write_title(TRUE); - fputs("</head>\n", stdout); - fputs("<body>\n", stdout); - write_title(FALSE); - header.write_headings(stdout); - { - extern const char *Version_string; - html.begin_comment("Creator : ") - .comment_arg("groff ") - .comment_arg("version ") - .comment_arg(Version_string) - .end_comment(); - } - { -#ifdef LONG_FOR_TIME_T - long -#else - time_t -#endif - t = time(0); - html.begin_comment("CreationDate: ") - .comment_arg(ctime(&t)) - .end_comment(); - } - html.begin_comment("Total number of pages: ").comment_arg(i_to_a(no_of_printed_pages)).end_comment(); - html.end_line(); - html.copy_file(tempfp); - fputs("</body>\n", stdout); - fputs("</html>\n", stdout); - fclose(tempfp); -} - - -/* - * calculate_region_range - calculates the vertical range for words and lines - * within the region lists. - */ - -void html_printer::calculate_region_range (graphic_glob *r) -{ - text_glob *w; - graphic_glob *g; - - if (! page_contents->region_lines.is_empty()) { - page_contents->region_lines.start_from_head(); - do { - g = page_contents->region_lines.get_data(); - if ((r->minv == -1) || (g->minv < r->minv)) { - r->minv = g->minv; - } - if ((r->maxv == -1) || (g->maxv > r->maxv)) { - r->maxv = g->maxv; - } - page_contents->region_lines.move_right(); - } while (! page_contents->region_lines.is_equal_to_head()); - } - if (! page_contents->region_words.is_empty()) { - page_contents->region_words.start_from_head(); - do { - w = page_contents->region_words.get_data(); - - if ((r->minv == -1) || (w->minv < r->minv)) { - r->minv = w->minv; - } - if ((r->maxv == -1) || (w->maxv > r->maxv)) { - r->maxv = w->maxv; - } - page_contents->region_words.move_right(); - } while (! page_contents->region_words.is_equal_to_head()); - } -} - - -/* - * move_region_to_page - moves lines and words held in the temporary region - * list to the page list. - */ - -void html_printer::move_region_to_page (void) -{ - text_glob *w; - graphic_glob *g; - - page_contents->region_lines.start_from_head(); - while (! page_contents->region_lines.is_empty()) { - g = page_contents->region_lines.get_data(); // remove from our temporary region list - page_contents->lines.add(g); // and add to the page list - page_contents->region_lines.sub_move_right(); - } - page_contents->region_words.start_from_head(); - while (! page_contents->region_words.is_empty()) { - w = page_contents->region_words.get_data(); // remove from our temporary region list - page_contents->words.add(w); // and add to the page list - page_contents->region_words.sub_move_right(); - } -} - -/* - * is_graphic_start - returns TRUE if the start of table, pic, eqn was seen. - */ - -int is_graphic_start (char *s) -{ - return( (strcmp(s, "graphic-start") == 0) || - ((strcmp(s, "table-start") == 0) && (table_image_on)) ); -} - -/* - * is_graphic_end - return TRUE if the end of a table, pic, eqn was seen. - */ - -int is_graphic_end (char *s) -{ - return( (strcmp(s, "graphic-end") == 0) || - ((strcmp(s, "table-end") == 0) && (table_image_on)) ); -} - -/* - * special - handle all x X requests from troff. For grohtml they allow users - * to pass raw html commands, turn auto linked headings off/on and - * also allow tbl, eqn & pic say what commands they have generated. - */ - -void html_printer::special(char *s, const environment *env) -{ - if (s != 0) { - if (is_graphic_start(s)) { - graphic_level++; - if (graphic_level == 1) { - page_contents->is_in_graphic = TRUE; // add words and lines to temporary region lists - } - } else if (is_graphic_end(s) && (graphic_level > 0)) { - graphic_level--; - if (graphic_level == 0) { - flush_graphic(); - } - } else if (strncmp(s, "html:", 5) == 0) { - int r=font::res; // resolution of the device - char buf[MAX_STRING_LENGTH]; - font *f=sbuf_style.f; - - if (f == NULL) { - int found=FALSE; - - f = font::load_font("TR", &found); - } - str_translate_to_html(f, buf, MAX_STRING_LENGTH, - &s[5], strlen(s)-5, FALSE); - page_contents->add_html_command(&sbuf_style, buf, strlen(buf), - - // need to pass rest of string through to html output during flush - - env->vpos-env->size*r/72, env->hpos, - env->vpos , env->hpos); - // assume that the html command has no width, if it does then we hopefully troff - // will have fudged this in a macro and requested that the formatting move right by - // the appropriate width - } else if (strncmp(s, "index:", 6) == 0) { - cutoff_heading = atoi(&s[6]); - } - } -} - -void set_image_type (char *type) -{ - if (strcmp(type, "gif") == 0) { - image_type = gif; - } else if (strcmp(type, "png") == 0) { - image_type = png; - image_device = "png256"; - } else if (strncmp(type, "png", 3) == 0) { - image_type = png; - image_device = type; - } -} - -printer *make_printer() -{ - return new html_printer; -} - -static void usage(); - -int main(int argc, char **argv) -{ - program_name = argv[0]; - static char stderr_buf[BUFSIZ]; - setbuf(stderr, stderr_buf); - int c; - while ((c = getopt(argc, argv, "F:atTvdgmx?I:r:")) != EOF) - switch(c) { - case 'v': - { - extern const char *Version_string; - printf("GNU grohtml (groff) version %s\n", Version_string); - exit(0); - break; - } - case 'a': - auto_on = FALSE; - break; - case 't': - table_on = FALSE; - break; - case 'T': - table_image_on = FALSE; - break; - case 'F': - font::command_line_font_dir(optarg); - break; - case 'I': - // user specifying the type of images we should generate - set_image_type(optarg); - break; - case 'r': - // resolution (dots per inch for an image) - image_res = atoi(optarg); - break; - case 'd': - // debugging on - debug_on = TRUE; - break; - case 'x': - debug_table_on = TRUE; - break; - case 'g': - // do not guess title and headings - guess_on = FALSE; - break; - case 'm': - // leave margins alone - margin_on = TRUE; - break; - case '?': - usage(); - break; - default: - assert(0); - } - if (optind >= argc) { - do_file("-"); - } else { - for (int i = optind; i < argc; i++) - do_file(argv[i]); - } - delete pr; - return 0; -} - -static void usage() -{ - fprintf(stderr, "usage: %s [-avdgmt?] [-r resolution] [-F dir] [-I imagetype] [files ...]\n", - program_name); - exit(1); -} diff --git a/src/devices/grohtml/html.h b/src/devices/grohtml/html.h index d2c675302..3e442b8f7 100644 --- a/src/devices/grohtml/html.h +++ b/src/devices/grohtml/html.h @@ -1,5 +1,5 @@ // -*- C++ -*- -/* Copyright (C) 2000 Free Software Foundation, Inc. +/* Copyright (C) 2000, 2001 Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) This file is part of groff. @@ -18,6 +18,11 @@ 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. */ +#if !defined(HTML_H) +# define HTML_H +# undef DEBUGGING +// #define DEBUGGING + class simple_output { public: simple_output(FILE *, int max_line_length); @@ -40,7 +45,11 @@ public: simple_output &end_line(); simple_output &put_raw_char(char); simple_output &special(const char *); - simple_output &put_html_char (char); + simple_output &enable_newlines(int); + simple_output &check_newline(int n); + simple_output &write_newline(void); + simple_output &space_or_newline (void); + simple_output &check_space (int n); FILE *get_file(); private: FILE *fp; @@ -48,6 +57,7 @@ private: int col; int need_space; int fixed_point; + int newlines; // can we issue newlines automatically? }; inline FILE *simple_output::get_file() @@ -55,3 +65,4 @@ inline FILE *simple_output::get_file() return fp; } +#endif diff --git a/src/devices/grohtml/ordered_list.h b/src/devices/grohtml/ordered_list.h deleted file mode 100755 index b86ad9f04..000000000 --- a/src/devices/grohtml/ordered_list.h +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (C) 1999, 2000 Free Software Foundation, Inc. - * - * Ordered list, a template module for simple ordered list manipulation. - * - * Gaius Mulley (gaius@glam.ac.uk) - */ - -template <class T> class list_element -{ - public: - list_element<T> *right; - list_element<T> *left; - - list_element (T *in); - T *data; -}; - -template <class T> class ordered_list -{ - private: - list_element<T> *head; - list_element<T> *tail; - list_element<T> *ptr; - public: - ordered_list (void); - ~ ordered_list (void); - void add (T* in); - void sub_move_right (void); - void move_right (void); - void move_left (void); - int is_empty (void); - int is_equal_to_tail (void); - int is_equal_to_head (void); - void start_from_head (void); - void start_from_tail (void); - T *move_right_get_data (void); - T *move_left_get_data (void); - T *get_data (void); -}; - - -template <class T> ordered_list<T>::ordered_list() - : head(0), tail(0), ptr(0) -{ -} - -template <class T> ordered_list<T>::~ordered_list() -{ - list_element<T> *temp=head; - - do { - temp = head; - if (temp != 0) { - head = head->right; - delete temp; - } - } while ((head != 0) && (head != tail)); -} - -template <class T> list_element<T>::list_element(T *in) - : right(0), left(0) -{ - data = in; -} - -template <class T> void ordered_list<T>::add(T *in) -{ - list_element<T> *t = new list_element<T>(in); // create a new list element with data field initialized - list_element<T> *last; - - if (in == 0) { - fatal("cannot add NULL to ordered list"); - } - - if (head == 0) { - head = t; - tail = t; - t->left = t; - t->right = t; - } else { - last = tail; - - while ((last != head) && (in->is_less(in, last->data))) { - last = last->left; - } - - if (in->is_less(in, last->data)) { - t->right = last; - last->left->right = t; - t->left = last->left; - last->left = t; - // now check for a new head - if (last == head) { - head = t; - } - } else { - // add t onto beyond last - t->right = last->right; - t->left = last; - last->right->left = t; - last->right = t; - // now check for a new tail - if (last == tail) { - tail = t; - } - } - } -} - -template <class T> void ordered_list<T>::sub_move_right (void) -{ - list_element<T> *t=ptr->right; - - if (head == tail) { - head = 0; - if (tail != 0) { - delete tail; - } - tail = 0; - ptr = 0; - } else { - if (head == ptr) { - head = head->right; - } - if (tail == ptr) { - tail = tail->left; - } - ptr->left->right = ptr->right; - ptr->right->left = ptr->left; - ptr=t; - } -} - -template <class T> void ordered_list<T>::start_from_head (void) -{ - ptr = head; -} - -template <class T> void ordered_list<T>::start_from_tail (void) -{ - ptr = tail; -} - -template <class T> int ordered_list<T>::is_empty (void) -{ - return( head == 0 ); -} - -template <class T> int ordered_list<T>::is_equal_to_tail (void) -{ - return( ptr == tail ); -} - -template <class T> int ordered_list<T>::is_equal_to_head (void) -{ - return( ptr == head ); -} - -template <class T> void ordered_list<T>::move_left (void) -{ - ptr = ptr->left; -} - -template <class T> void ordered_list<T>::move_right (void) -{ - ptr = ptr->right; -} - -template <class T> T* ordered_list<T>::get_data (void) -{ - return( ptr->data ); -} - -template <class T> T* ordered_list<T>::move_right_get_data (void) -{ - ptr = ptr->right; - if (ptr == head) { - return( 0 ); - } else { - return( ptr->data ); - } -} - -template <class T> T* ordered_list<T>::move_left_get_data (void) -{ - ptr = ptr->left; - if (ptr == tail) { - return( 0 ); - } else { - return( ptr->data ); - } -} diff --git a/src/devices/grohtml/output.cc b/src/devices/grohtml/output.cc index d6dc18849..15b26f2c2 100644 --- a/src/devices/grohtml/output.cc +++ b/src/devices/grohtml/output.cc @@ -1,5 +1,5 @@ // -*- C++ -*- -/* Copyright (C) 2000 Free Software Foundation, Inc. +/* Copyright (C) 2000, 2001 Free Software Foundation, Inc. * * Gaius Mulley (gaius@glam.ac.uk) wrote output.cc * but it owes a huge amount of ideas and raw code from @@ -45,20 +45,20 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ # define FALSE (1==0) #endif - /* * the classes and methods for simple_output manipulation */ simple_output::simple_output(FILE *f, int n) -: fp(f), max_line_length(n), col(0), need_space(0), fixed_point(0) +: fp(f), max_line_length(n), col(0), need_space(0), fixed_point(0), newlines(0) { } simple_output &simple_output::set_file(FILE *f) { + if (fp) + fflush(fp); fp = f; - col = 0; return *this; } @@ -103,6 +103,7 @@ simple_output &simple_output::begin_comment(const char *s) putc('\n', fp); fputs("<!-- ", fp); fputs(s, fp); + need_space = 0; col = 5 + strlen(s); return *this; } @@ -112,7 +113,7 @@ simple_output &simple_output::end_comment() if (need_space) { putc(' ', fp); } - fputs(" -->\n", fp); + fputs("-->\n", fp); col = 0; need_space = 0; return *this; @@ -121,16 +122,67 @@ simple_output &simple_output::end_comment() simple_output &simple_output::comment_arg(const char *s) { int len = strlen(s); + int i = 0; if (col + len + 1 > max_line_length) { fputs("\n ", fp); col = 1; } - fputs(s, fp); - col += len + 1; + while (i < len) { + if (s[i] != '\n') { + putc(s[i], fp); + col++; + } + i++; + } + need_space = 1; return *this; } +/* + * check_newline - checks to see whether we are able to issue + * a newline and that one is needed. + */ + +simple_output &simple_output::check_newline(int n) +{ + if ((col + n > max_line_length) && (newlines)) { + fputc('\n', fp); + need_space = 0; + col = 0; + } +} + +/* + * space_or_newline - will emit a newline or a space later on + * depending upon the current column. + */ + +simple_output &simple_output::space_or_newline (void) +{ + if ((col + 1 > max_line_length) && (newlines)) { + fputc('\n', fp); + need_space = 0; + col = 0; + } else { + need_space = 1; + } +} + +/* + * write_newline - writes a newline providing that we + * are not in the first column. + */ + +simple_output &simple_output::write_newline (void) +{ + if (col != 0) { + fputc('\n', fp); + need_space = 0; + col = 0; + } +} + simple_output &simple_output::set_fixed_point(int n) { assert(n >= 0 && n <= 10); @@ -146,14 +198,33 @@ simple_output &simple_output::put_raw_char(char c) return *this; } +/* + * check_space - writes a space if required. + */ + +simple_output &simple_output::check_space (int n) +{ + check_newline(n); + if (need_space) { + fputc(' ', fp); + need_space = 0; + col++; + } +} + simple_output &simple_output::put_string(const char *s, int n) { int i=0; + check_space(n); + while (i<n) { fputc(s[i], fp); i++; } +#if defined(DEBUGGING) + fflush(fp); // just for testing +#endif col += n; return *this; } @@ -162,12 +233,17 @@ simple_output &simple_output::put_translated_string(const char *s) { int i=0; + check_space(strlen(s)); + while (s[i] != (char)0) { if ((s[i] & 0x7f) == s[i]) { fputc(s[i], fp); } i++; } +#if defined(DEBUGGING) + fflush(fp); // just for testing +#endif col += i; return *this; } @@ -175,48 +251,24 @@ simple_output &simple_output::put_translated_string(const char *s) simple_output &simple_output::put_string(const char *s) { int i=0; + int j=0; + + check_space(strlen(s)); while (s[i] != '\0') { fputc(s[i], fp); - i++; - } - col += i; - return *this; -} - -struct html_2_postscript { - char *html_char; - char *postscript_char; -}; - -static struct html_2_postscript ps_char_conversions[] = { - { "+-", "char177", }, - { "eq", "=" , }, - { "mu", "char215", }, - { NULL, NULL , }, -}; - - -/* - * this is an aweful hack which attempts to translate html characters onto - * postscript characters. Can this be done inside the devhtml files? - * - * or should we read the devps files and find out the translations? - */ - -simple_output &simple_output::put_troffps_char (const char *s) -{ - int i=0; - - while (ps_char_conversions[i].html_char != NULL) { - if (strcmp(s, ps_char_conversions[i].html_char) == 0) { - put_string(ps_char_conversions[i].postscript_char); - return *this; + if (s[i] == '\n') { + col = 0; + j = 0; } else { - i++; + j++; } + i++; } - put_string(s); + col += j; +#if defined(DEBUGGING) + fflush(fp); // just for testing +#endif return *this; } @@ -226,7 +278,6 @@ simple_output &simple_output::put_number(int n) sprintf(buf, "%d", n); int len = strlen(buf); put_string(buf, len); - need_space = 1; return *this; } @@ -241,7 +292,6 @@ simple_output &simple_output::put_float(double d) return *this; } - simple_output &simple_output::put_symbol(const char *s) { int len = strlen(s); @@ -255,3 +305,9 @@ simple_output &simple_output::put_symbol(const char *s) need_space = 1; return *this; } + +simple_output &simple_output::enable_newlines (int auto_newlines) +{ + newlines = auto_newlines; + check_newline(0); +} diff --git a/src/devices/grohtml/post-html.cc b/src/devices/grohtml/post-html.cc new file mode 100644 index 000000000..7f6490ca2 --- /dev/null +++ b/src/devices/grohtml/post-html.cc @@ -0,0 +1,2667 @@ +// -*- C++ -*- +/* Copyright (C) 2000, 2001 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 + * James Clark (jjc@jclark.com) grops/ps.cc. + */ + +/* +This file is part of groff. + +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. */ + +#include "driver.h" +#include "stringclass.h" +#include "cset.h" +#include "html.h" +#include "html-chars.h" +#include "html-text.h" + +#include <time.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <stdio.h> +#include <fcntl.h> + +#if !defined(TRUE) +# define TRUE (1==1) +#endif +#if !defined(FALSE) +# define FALSE (1==0) +#endif + +#define MAX_STRING_LENGTH 4096 +#define MAX_LINE_LENGTH 60 /* maximum characters we want in a line */ +#define SIZE_INCREMENT 2 /* font size increment <big> = +2 */ +#define BASE_POINT_SIZE 10 /* 10 points is the base size ie html size 3 */ +#define CENTER_TOLERANCE 2 /* how many pixels off center will we still */ +#define ANCHOR_TEMPLATE "heading%d" /* if simple anchor is set we use this */ + +typedef enum {CENTERED, LEFT, RIGHT, INLINE} TAG_ALIGNMENT; + +/* + * prototypes + */ + +void str_translate_to_html (font *f, char *buf, int buflen, char *str, int len, int and_single); +char *get_html_translation (font *f, const char *name); + + +static int auto_links = TRUE; /* by default we enable automatic links at */ + /* top of the document. */ +static int auto_rule = TRUE; /* by default we enable an automatic rule */ + /* at the top and bottom of the document */ +static int simple_anchors = FALSE; /* default to anchors with heading text */ + + +/* + * start with a few favorites + */ + +void stop () {} + +static int min (int a, int b) +{ + if (a < b) { + return( a ); + } else { + return( b ); + } +} + +static int max (int a, int b) +{ + if (a > b) { + return( a ); + } else { + return( b ); + } +} + +/* + * is_subsection - returns TRUE if a1..a2 is within b1..b2 + */ + +static int is_subsection (int a1, int a2, int b1, int b2) +{ + // easier to see whether this is not the case + return( !((a1 < b1) || (a1 > b2) || (a2 < b1) || (a2 > b2)) ); +} + +/* + * is_intersection - returns TRUE if range a1..a2 intersects with b1..b2 + */ + +static int is_intersection (int a1, int a2, int b1, int b2) +{ + // again easier to prove NOT outside limits + return( ! ((a1 > b2) || (a2 < b1)) ); +} + +/* + * is_digit - returns TRUE if character, ch, is a digit. + */ + +static int is_digit (char ch) +{ + return( (ch >= '0') && (ch <= '9') ); +} + +/* + * the classes and methods for maintaining a list of files. + */ + +struct file { + FILE *fp; + file *next; + + file (FILE *f); +}; + +/* + * file - initialize all fields to NULL + */ + +file::file (FILE *f) + : fp(f), next(0) +{ +} + +class files { +public: + files (); + FILE *get_file (void); + void start_of_list (void); + void move_next (void); + void add_new_file (FILE *f); +private: + file *head; + file *tail; + file *ptr; +}; + +/* + * files - create an empty list of files. + */ + +files::files () + : head(0), tail(0), ptr(0) +{ +} + +/* + * get_file - returns the FILE associated with ptr. + */ + +FILE *files::get_file (void) +{ + if (ptr) { + return( ptr->fp ); + } else { + return( 0 ); + } +} + +/* + * start_of_list - reset the ptr to the start of the list. + */ + +void files::start_of_list (void) +{ + ptr = head; +} + +/* + * move_next - moves the ptr to the next element on the list. + */ + +void files::move_next (void) +{ + if (ptr != 0) + ptr = ptr->next; +} + +/* + * add_new_file - adds a new file, f, to the list. + */ + +void files::add_new_file (FILE *f) +{ + if (head == 0) { + head = new file(f); + tail = head; + } else { + tail->next = new file(f); + tail = tail->next; + } + ptr = tail; +} + +/* + * the class and methods for styles + */ + +struct style { + font *f; + int point_size; + int font_no; + int height; + int slant; + style (); + style (font *, int, int, int, int); + int operator == (const style &) const; + int operator != (const style &) const; +}; + +style::style() + : f(0) +{ +} + +style::style(font *p, int sz, int h, int sl, int no) + : f(p), point_size(sz), font_no(no), height(h), slant(sl) +{ +} + +int style::operator==(const style &s) const +{ + return (f == s.f && point_size == s.point_size + && height == s.height && slant == s.slant); +} + +int style::operator!=(const style &s) const +{ + return !(*this == s); +} + +/* + * the class and methods for retaining ascii text + */ + +struct char_block { + enum { SIZE = 256 }; + char buffer[SIZE]; + int used; + char_block *next; + + char_block(); +}; + +char_block::char_block() +: used(0), next(0) +{ +} + +class char_buffer { +public: + char_buffer(); + ~char_buffer(); + char *add_string(char *, unsigned int); +private: + char_block *head; + char_block *tail; +}; + +char_buffer::char_buffer() +: head(0), tail(0) +{ +} + +char_buffer::~char_buffer() +{ + while (head != 0) { + char_block *temp = head; + head = head->next; + delete temp; + } +} + +char *char_buffer::add_string (char *s, unsigned int length) +{ + int i=0; + unsigned int old_used; + + if (tail == 0) { + tail = new char_block; + head = tail; + } else { + if (tail->used + length+1 > char_block::SIZE) { + tail->next = new char_block; + tail = tail->next; + } + } + // at this point we have a tail which is ready for the string. + if (tail->used + length+1 > char_block::SIZE) { + fatal("need to increase char_block::SIZE"); + } + + old_used = tail->used; + do { + tail->buffer[tail->used] = s[i]; + tail->used++; + i++; + length--; + } while (length>0); + + // add terminating nul character + + tail->buffer[tail->used] = '\0'; + tail->used++; + + // and return start of new string + + return( &tail->buffer[old_used] ); +} + +/* + * the classes and methods for maintaining glyph positions. + */ + +class text_glob { +public: + text_glob (style *s, char *string, unsigned int length, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal, + int is_html , int is_troff_command, + int is_a_line , int thickness); + text_glob (void); + ~text_glob (void); + int is_a_line (void); + int is_a_tag (void); + int is_raw (void); + int is_eol (void); + + style text_style; + char *text_string; + unsigned int text_length; + int minv, maxv, minh, maxh; + int is_raw_command; // should the text be sent directly to the device? + int is_tag; // is this a .br, .sp, .tl etc + int is_line; // is the command a <line>? + int thickness; // the thickness of a line +}; + +text_glob::text_glob (style *s, char *string, unsigned int length, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal, + int is_html, int is_troff_command, + int is_a_line, int line_thickness) + : text_style(*s), text_string(string), text_length(length), + minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal), + is_raw_command(is_html), is_tag(is_troff_command), is_line(is_a_line), + thickness(line_thickness) +{ +} + +text_glob::text_glob () + : text_string(0), text_length(0), minv(-1), maxv(-1), minh(-1), maxh(-1), + is_raw_command(FALSE), is_tag(FALSE), is_line(FALSE), thickness(0) +{ +} + +text_glob::~text_glob () +{ +} + +/* + * is_a_line - returns TRUE if glob should be converted into an <hr> + */ + +int text_glob::is_a_line (void) +{ + return( is_line ); +} + +/* + * is_a_tag - returns TRUE if glob contains a troff directive. + */ + +int text_glob::is_a_tag (void) +{ + return( is_tag ); +} + +/* + * is_eol - returns TRUE if glob contains the tag eol + */ + +int text_glob::is_eol (void) +{ + return( is_tag && (strcmp(text_string, "html-tag:eol") == 0) ); +} + +/* + * is_raw - returns TRUE if glob contains raw html. + */ + +int text_glob::is_raw (void) +{ + return( is_raw_command ); +} + +/* + * the class and methods used to construct ordered double linked lists. + * In a previous implementation we used templates via #include "ordered-list.h", + * but this does assume that all C++ compilers can handle this feature. Pragmatically + * it is safer to assume this is not the case. + */ + +struct element_list { + element_list *right; + element_list *left; + text_glob *datum; + int lineno; + int minv, maxv, minh, maxh; + + element_list (text_glob *d, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal); + element_list (); +}; + +element_list::element_list () + : right(0), left(0), datum(0), lineno(0), minv(-1), maxv(-1), minh(-1), maxh(-1) +{ +} + +/* + * element_list - create a list element assigning the datum and region parameters. + */ + +element_list::element_list (text_glob *in, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal) + : right(0), left(0), datum(in), lineno(line_number), + minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal) +{ +} + +class list { +public: + list (); + ~list (); + int is_less (element_list *a, element_list *b); + void add (text_glob *in, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal); + void sub_move_right (void); + void move_right (void); + void move_left (void); + int is_empty (void); + int is_equal_to_tail (void); + int is_equal_to_head (void); + void start_from_head (void); + void start_from_tail (void); + text_glob *move_right_get_data (void); + text_glob *move_left_get_data (void); + text_glob *get_data (void); +private: + element_list *head; + element_list *tail; + element_list *ptr; +}; + +/* + * list - construct an empty list. + */ + +list::list () + : head(0), tail(0), ptr(0) +{ +} + +/* + * ~list - destroy a complete list. + */ + +list::~list() +{ + element_list *temp=head; + + do { + temp = head; + if (temp != 0) { + head = head->right; + delete temp; + } + } while ((head != 0) && (head != tail)); +} + +/* + * is_less - returns TRUE if a is left of b if on the same line or + * if a is higher up the page than b. + */ + +int list::is_less (element_list *a, element_list *b) +{ + // was if (is_intersection(a->minv+1, a->maxv-1, b->minv+1, b->maxv-1)) { + if (a->lineno < b->lineno) { + return( TRUE ); + } else if (a->lineno > b->lineno) { + return( FALSE ); + } else if (is_intersection(a->minv, a->maxv, b->minv, b->maxv)) { + return( a->minh < b->minh ); + } else { + return( a->maxv < b->maxv ); + } +} + +/* + * add - adds a datum to the list in the order specified by the region position. + */ + +void list::add (text_glob *in, int line_number, int min_vertical, int min_horizontal, int max_vertical, int max_horizontal) +{ + // create a new list element with datum and position fields initialized + element_list *t = new element_list(in, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal); + element_list *last; + + if (head == 0) { + head = t; + tail = t; + t->left = t; + t->right = t; + } else { + last = tail; + + while ((last != head) && (is_less(t, last))) { + last = last->left; + } + + if (is_less(t, last)) { + t->right = last; + last->left->right = t; + t->left = last->left; + last->left = t; + // now check for a new head + if (last == head) { + head = t; + } + } else { + // add t beyond last + t->right = last->right; + t->left = last; + last->right->left = t; + last->right = t; + // now check for a new tail + if (last == tail) { + tail = t; + } + } + } +} + +/* + * sub_move_right - removes the element which is currently pointed to by ptr + * from the list and moves ptr to the right. + */ + +void list::sub_move_right (void) +{ + element_list *t=ptr->right; + + if (head == tail) { + head = 0; + if (tail != 0) { + delete tail; + } + tail = 0; + ptr = 0; + } else { + if (head == ptr) { + head = head->right; + } + if (tail == ptr) { + tail = tail->left; + } + ptr->left->right = ptr->right; + ptr->right->left = ptr->left; + ptr=t; + } +} + +/* + * start_from_head - assigns ptr to the head. + */ + +void list::start_from_head (void) +{ + ptr = head; +} + +/* + * start_from_tail - assigns ptr to the tail. + */ + +void list::start_from_tail (void) +{ + ptr = tail; +} + +/* + * is_empty - returns TRUE if the list has no elements. + */ + +int list::is_empty (void) +{ + return( head == 0 ); +} + +/* + * is_equal_to_tail - returns TRUE if the ptr equals the tail. + */ + +int list::is_equal_to_tail (void) +{ + return( ptr == tail ); +} + +/* + * is_equal_to_head - returns TRUE if the ptr equals the head. + */ + +int list::is_equal_to_head (void) +{ + return( ptr == head ); +} + +/* + * move_left - moves the ptr left. + */ + +void list::move_left (void) +{ + ptr = ptr->left; +} + +/* + * move_right - moves the ptr right. + */ + +void list::move_right (void) +{ + ptr = ptr->right; +} + +/* + * get_datum - returns the datum referenced via ptr. + */ + +text_glob* list::get_data (void) +{ + return( ptr->datum ); +} + +/* + * move_right_get_data - returns the datum referenced via ptr and moves + * ptr right. + */ + +text_glob* list::move_right_get_data (void) +{ + ptr = ptr->right; + if (ptr == head) { + return( 0 ); + } else { + return( ptr->datum ); + } +} + +/* + * move_left_get_data - returns the datum referenced via ptr and moves + * ptr right. + */ + +text_glob* list::move_left_get_data (void) +{ + ptr = ptr->left; + if (ptr == tail) { + return( 0 ); + } else { + return( ptr->datum ); + } +} + +/* + * page class and methods + */ + +class page { +public: + page (void); + void add (style *s, char *string, unsigned int length, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal); + void add_html (style *s, char *string, unsigned int length, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal); + void add_tag (style *s, char *string, unsigned int length, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal); + void add_line (style *s, + int line_number, + int x1, int y1, int x2, int y2, + int thickness); + void dump_page (void); // debugging method + + // and the data + + list glyphs; // position of glyphs and specials on page + char_buffer buffer; // all characters for this page +}; + +page::page() +{ +} + +void page::add (style *s, char *string, unsigned int length, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal) +{ + if (length > 0) { + text_glob *g=new text_glob(s, buffer.add_string(string, length), length, + min_vertical, min_horizontal, max_vertical, max_horizontal, + FALSE, FALSE, FALSE, 0); + glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal); + } +} + +/* + * add_html - add a raw html command, for example mailto, line, background, image etc. + */ + +void page::add_html (style *s, char *string, unsigned int length, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal) +{ + if (length > 0) { + text_glob *g=new text_glob(s, buffer.add_string(string, length), length, + min_vertical, min_horizontal, max_vertical, max_horizontal, + TRUE, FALSE, FALSE, 0); + glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal); + } +} + +/* + * add_tag - adds a troff tag, for example: .tl .sp .br + */ + +void page::add_tag (style *s, char *string, unsigned int length, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal) +{ + if (length > 0) { + text_glob *g=new text_glob(s, buffer.add_string(string, length), length, + min_vertical, min_horizontal, max_vertical, max_horizontal, + FALSE, TRUE, FALSE, 0); + glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal); + } +} + +/* + * add_line - adds the <line> primitive providing that y1==y2 + */ + +void page::add_line (style *s, + int line_number, + int x1, int y1, int x2, int y2, + int thickness) +{ + if (y1 == y2) { + text_glob *g = new text_glob(s, "", 0, + min(y1, y2), min(x1, y2), max(y1, y2), max(x1, x2), + FALSE, TRUE, FALSE, thickness); + glyphs.add(g, line_number, min(y1, y2), min(x1, y2), max(y1, y2), max(x1, x2)); + } +} + +/* + * dump_page - dump the page contents for debugging purposes. + */ + +void page::dump_page(void) +{ + text_glob *g; + + printf("\n\ndebugging start\n"); + glyphs.start_from_head(); + do { + g = glyphs.get_data(); + printf("%s ", g->text_string); + glyphs.move_right(); + } while (! glyphs.is_equal_to_head()); + printf("\ndebugging end\n\n"); +} + +/* + * font classes and methods + */ + +class html_font : public font { + html_font(const char *); +public: + int encoding_index; + char *encoding; + char *reencoded_name; + ~html_font(); + static html_font *load_html_font(const char *); +}; + +html_font *html_font::load_html_font(const char *s) +{ + html_font *f = new html_font(s); + if (!f->load()) { + delete f; + return 0; + } + return f; +} + +html_font::html_font(const char *nm) +: font(nm) +{ +} + +html_font::~html_font() +{ +} + +/* + * a simple class to contain the header to this document + */ + +class title_desc { +public: + title_desc (); + ~title_desc (); + + int has_been_written; + int has_been_found; + char text[MAX_STRING_LENGTH]; +}; + + +title_desc::title_desc () + : has_been_written(FALSE), has_been_found(FALSE) +{ +} + +title_desc::~title_desc () +{ +} + +class header_desc { +public: + header_desc (); + ~header_desc (); + + int no_of_headings; // how many headings have we found? + char_buffer headings; // all the headings used in the document + list headers; // list of headers built from .NH and .SH + int header_level; // current header level + int written_header; // have we written the header yet? + char header_buffer[MAX_STRING_LENGTH]; // current header text + + void write_headings (FILE *f, int force); +}; + +header_desc::header_desc () + : no_of_headings(0), header_level(2), written_header(0) +{ +} + +header_desc::~header_desc () +{ +} + +/* + * write_headings - emits a list of links for the headings in this document + */ + +void header_desc::write_headings (FILE *f, int force) +{ + text_glob *g; + + if (auto_links || force) { + if (! headers.is_empty()) { + int h=1; + + headers.start_from_head(); + do { + g = headers.get_data(); + fputs("<a href=\"#", f); + if (simple_anchors) + fprintf(f, ANCHOR_TEMPLATE, h); + else + fputs(g->text_string, f); + h++; + fputs("\">", f); + fputs(g->text_string, f); + fputs("</a><br>\n", f); + headers.move_right(); + } while (! headers.is_equal_to_head()); + fputs("\n", f); + } + } +} + +class html_printer : public printer { + files file_list; + simple_output html; + int res; + int space_char_index; + int no_of_printed_pages; + int paper_length; + enum { SBUF_SIZE = 8192 }; + char sbuf[SBUF_SIZE]; + int sbuf_len; + int sbuf_start_hpos; + int sbuf_vpos; + int sbuf_end_hpos; + int sbuf_kern; + style sbuf_style; + style output_style; + int output_hpos; + int output_vpos; + int output_vpos_max; + int output_draw_point_size; + int line_thickness; + int output_line_thickness; + unsigned char output_space_code; + string defs; + char *inside_font_style; + int page_number; + title_desc title; + header_desc header; + int header_indent; + int supress_sub_sup; + int cutoff_heading; + page *page_contents; + html_text *current_paragraph; + int end_center; + TAG_ALIGNMENT next_tag; + int fill_on; + int linelength; + int pageoffset; + int indentation; + int pointsize; + int vertical_spacing; + int line_number; + + void flush_sbuf (); + void set_style (const style &); + void set_space_code (unsigned char c); + void do_exec (char *, const environment *); + void do_import (char *, const environment *); + void do_def (char *, const environment *); + void do_mdef (char *, const environment *); + void do_file (char *, const environment *); + void set_line_thickness (const environment *); + void terminate_current_font (void); + void flush_font (void); + void add_char_to_sbuf (unsigned char code); + void add_to_sbuf (char code, const char *name); + void write_title (int in_head); + void determine_diacritical_mark (const char *name, const environment *env); + int sbuf_continuation (unsigned char code, const char *name, const environment *env, int w); + char *remove_last_char_from_sbuf (); + int seen_backwards_escape (char *s, int l); + void flush_page (void); + void troff_tag (text_glob *g); + void flush_globs (void); + void emit_line (text_glob *g); + void emit_raw (text_glob *g); + void translate_to_html (text_glob *g); + void determine_space (text_glob *g); + void start_font (const char *name); + void end_font (const char *name); + int is_font_courier (font *f); + int is_courier_until_eol (void); + void start_size (int from, int to); + void do_font (text_glob *g); + void do_space (void); + void do_break (void); + void do_center (char *arg); + void do_eol (void); + void do_title (void); + void do_fill (int on); + void do_heading (char *arg); + void write_header (void); + void determine_header_level (int level); + void do_linelength (char *arg); + void do_pageoffset (char *arg); + void do_indentation (char *arg); + void do_verticalspacing (char *arg); + void do_pointsize (char *arg); + void do_centered_image (void); + void do_left_image (void); + void do_right_image (void); + void do_auto_image (text_glob *g, const char *filename); + void do_indent (char *arg); + void do_links (void); + void do_flush (void); + int is_in_middle (int left, int right); + void do_sup_or_sub (text_glob *g); + int start_subscript (text_glob *g); + int end_subscript (text_glob *g); + int start_superscript (text_glob *g); + int end_superscript (text_glob *g); + + // ADD HERE + +public: + html_printer (); + ~html_printer (); + void set_char (int i, font *f, const environment *env, int w, const char *name); + void draw (int code, int *p, int np, const environment *env); + void begin_page (int); + void end_page (int); + void special (char *arg, const environment *env); + font *make_font (const char *); + void end_of_line (); +}; + +printer *make_printer() +{ + return new html_printer; +} + +static void usage(); + +void html_printer::set_style(const style &sty) +{ + const char *fontname = sty.f->get_name(); + if (fontname == 0) + fatal("no internalname specified for font"); + +#if 0 + change_font(fontname, (font::res/(72*font::sizescale))*sty.point_size); +#endif +} + +void html_printer::end_of_line() +{ + flush_sbuf(); + line_number++; +} + +/* + * emit_line - writes out a horizontal rule. + */ + +void html_printer::emit_line (text_glob *g) +{ + // --fixme-- needs to know the length in percentage + html.put_string("<hr>"); +} + +/* + * emit_raw - writes the raw html information directly to the device. + */ + +void html_printer::emit_raw (text_glob *g) +{ + do_font(g); + if (next_tag == INLINE) { + determine_space(g); + current_paragraph->do_emittext(g->text_string, g->text_length); + } else { + current_paragraph->done_para(); + switch (next_tag) { + + case CENTERED: + current_paragraph->do_para("align=center"); + break; + case LEFT: + current_paragraph->do_para("align=left"); + break; + case RIGHT: + current_paragraph->do_para("align=right"); + break; + default: + fatal("unknown enumeration"); + } + current_paragraph->do_emittext(g->text_string, g->text_length); + current_paragraph->done_para(); + next_tag = INLINE; + supress_sub_sup = TRUE; + } +} + +/* + * do_center - handle the .ce commands from troff. + */ + +void html_printer::do_center (char *arg) +{ + int n = atoi(arg); + + current_paragraph->do_break(); + current_paragraph->done_para(); + supress_sub_sup = TRUE; + + if (n > 0) { + current_paragraph->do_para("align=center"); + end_center += n; + } else { + end_center = 0; + } +} + +/* + * do_centered_image - set a flag such that the next html-tag is + * placed inside a centered paragraph. + */ + +void html_printer::do_centered_image (void) +{ + next_tag = CENTERED; +} + +/* + * do_right_image - set a flag such that the next html-tag is + * placed inside a right aligned paragraph. + */ + +void html_printer::do_right_image (void) +{ + next_tag = RIGHT; +} + +/* + * do_left_image - set a flag such that the next html-tag is + * placed inside a left aligned paragraph. + */ + +void html_printer::do_left_image (void) +{ + next_tag = LEFT; +} + +/* + * exists - returns TRUE if filename exists. + */ + +static int exists (const char *filename) +{ + FILE *fp = fopen(filename, "r"); + + if (fp == 0) { + return( FALSE ); + } else { + fclose(fp); + return( TRUE ); + } +} + +/* + * do_auto_image - tests whether the image, indicated by filename, + * is present, if so then it emits an html image tag. + * An image tag may be passed through from pic, eqn + * but the corresponding image might not be created. + * Consider .EQ delim $$ .EN or an empty .PS .PE. + */ + +void html_printer::do_auto_image (text_glob *g, const char *filename) +{ + while (filename && (filename[0] == ' ')) { + filename++; + } + if (exists(filename)) { + /* + * utilize emit_raw by creating a new text_glob. + */ + text_glob h = *g; + char buffer[MAX_STRING_LENGTH]; + + strcpy(buffer, "<img src=\""); + strncat(buffer, filename, MAX_STRING_LENGTH-strlen("<img src=\"")-1); + if (strlen(buffer) < MAX_STRING_LENGTH-3) { + strncat(buffer, "\">", 3); + h.text_string = (char *)&buffer; + h.text_length = strlen(buffer); + emit_raw(&h); + } + } else { + next_tag = INLINE; + } +} + +/* + * do_title - handle the .tl commands from troff. + */ + +void html_printer::do_title (void) +{ + text_glob *t; + int removed_from_head; + char buf[MAX_STRING_LENGTH]; + + if (page_number == 1) { + int found_title_start = FALSE; + if (! page_contents->glyphs.is_empty()) { + page_contents->glyphs.sub_move_right(); /* move onto next word */ + do { + t = page_contents->glyphs.get_data(); + removed_from_head = FALSE; + if (t->is_raw_command) { + /* skip raw commands + */ + page_contents->glyphs.sub_move_right(); /* move onto next word */ + } else if (t->is_eol()) { + /* end of title found + */ + title.has_been_found = TRUE; + return; + } else if (t->is_a_tag()) { + /* end of title found, but move back so that we read this tag and process it + */ + page_contents->glyphs.move_left(); /* move backwards to last word */ + title.has_been_found = TRUE; + return; + } else if (found_title_start) { + strcat(title.text, " "); + str_translate_to_html(t->text_style.f, buf, MAX_STRING_LENGTH, t->text_string, t->text_length, TRUE); + strcat(title.text, buf); + page_contents->glyphs.sub_move_right(); /* move onto next word */ + removed_from_head = ((!page_contents->glyphs.is_empty()) && + (page_contents->glyphs.is_equal_to_head())); + } else { + str_translate_to_html(t->text_style.f, buf, MAX_STRING_LENGTH, t->text_string, t->text_length, TRUE); + strcpy((char *)title.text, buf); + found_title_start = TRUE; + title.has_been_found = TRUE; + page_contents->glyphs.sub_move_right(); /* move onto next word */ + removed_from_head = ((!page_contents->glyphs.is_empty()) && + (page_contents->glyphs.is_equal_to_head())); + } + } while ((! page_contents->glyphs.is_equal_to_head()) || (removed_from_head)); + } + // page_contents->glyphs.move_left(); /* move backwards to last word */ + } +} + +void html_printer::write_header (void) +{ + if (strlen(header.header_buffer) > 0) { + if (header.header_level > 7) { + header.header_level = 7; + } + + if (cutoff_heading+2 > header.header_level) { + // firstly we must terminate any font and type faces + current_paragraph->done_para(); + supress_sub_sup = TRUE; + + // now we save the header so we can issue a list of links + header.no_of_headings++; + style st; + + text_glob *h=new text_glob(&st, + header.headings.add_string(header.header_buffer, strlen(header.header_buffer)), + strlen(header.header_buffer), + header.no_of_headings, header.header_level, + header.no_of_headings, header.header_level, + FALSE, FALSE, FALSE, FALSE); + header.headers.add(h, + header.no_of_headings, + header.no_of_headings, header.no_of_headings, + header.no_of_headings, header.no_of_headings); // and add this header to the header list + + // lastly we generate a tag + + html.put_string("\n<a name=\""); + if (simple_anchors) { + char buffer[MAX_LINE_LENGTH]; + + sprintf(buffer, ANCHOR_TEMPLATE, header.no_of_headings); + html.put_string(buffer); + } else { + html.put_string(header.header_buffer); + } + html.put_string("\"></a>\n"); + } else { + current_paragraph->done_para(); + supress_sub_sup = TRUE; + } + + // and now we issue the real header + html.put_string("<h"); + html.put_number(header.header_level); + html.put_string(">"); + html.put_string(header.header_buffer); + html.put_string("</h"); + html.put_number(header.header_level); + html.put_string(">\n"); + + current_paragraph->do_para(""); + } +} + +void html_printer::determine_header_level (int level) +{ + if (level == 0) { + int i; + int l=strlen(header.header_buffer); + + for (i=0; ((i<l) && ((header.header_buffer[i] == '.') || is_digit(header.header_buffer[i]))) ; i++) { + if (header.header_buffer[i] == '.') { + level++; + } + } + } + header.header_level = level+1; +} + +/* + * do_heading - handle the .SH and .NH and equivalent commands from troff. + */ + +void html_printer::do_heading (char *arg) +{ + text_glob *g; + text_glob *l = 0; + char buf[MAX_STRING_LENGTH]; + int level=atoi(arg); + + strcpy(header.header_buffer, ""); + page_contents->glyphs.move_right(); + if (! page_contents->glyphs.is_equal_to_head()) { + g = page_contents->glyphs.get_data(); + do { + if (! (g->is_a_line() || g->is_a_tag() || g->is_raw())) { + /* + * we ignore raw commands when constructing a heading + */ + if (l != 0) { + strcat(header.header_buffer, " "); + } + l = g; + str_translate_to_html(g->text_style.f, buf, MAX_STRING_LENGTH, g->text_string, g->text_length, TRUE); + strcat(header.header_buffer, (char *)buf); + } + page_contents->glyphs.move_right(); + g = page_contents->glyphs.get_data(); + } while ((! page_contents->glyphs.is_equal_to_head()) && + (! g->is_a_tag())); + } + + determine_header_level(level); + write_header(); + + // finally set the output to neutral for after the header + g = page_contents->glyphs.get_data(); + page_contents->glyphs.move_left(); // so that next time we use old g +} + +/* + * is_courier_until_eol - returns TRUE if we can see a whole line which is courier + */ + +int html_printer::is_courier_until_eol (void) +{ + text_glob *orig = page_contents->glyphs.get_data(); + int result = TRUE; + text_glob *g; + + if (! page_contents->glyphs.is_equal_to_tail()) { + page_contents->glyphs.move_right(); + do { + g = page_contents->glyphs.get_data(); + if (! is_font_courier(g->text_style.f)) { + result = FALSE; + } + page_contents->glyphs.move_right(); + } while ((result) && + (! page_contents->glyphs.is_equal_to_head()) && + (! g->is_eol())); + + /* + * now restore our previous position. + */ + while (page_contents->glyphs.get_data() != orig) { + page_contents->glyphs.move_left(); + } + } + return( result ); +} + +/* + * do_linelength - handle the .ll command from troff. + */ + +void html_printer::do_linelength (char *arg) +{ + linelength = atoi(arg); +} + +/* + * do_pageoffset - handle the .po command from troff. + */ + +void html_printer::do_pageoffset (char *arg) +{ + pageoffset = atoi(arg); +} + +/* + * do_indentation - handle the .in command from troff. + */ + +void html_printer::do_indentation (char *arg) +{ + indentation = atoi(arg); +} + +/* + * do_verticalspacing - handle the .vs command from troff. + */ + +void html_printer::do_verticalspacing (char *arg) +{ + vertical_spacing = atoi(arg); +} + +/* + * do_pointsize - handle the .ps command from troff. + */ + +void html_printer::do_pointsize (char *arg) +{ + pointsize = atoi(arg); +} + +/* + * do_fill - records whether troff has requested that text be filled. + */ + +void html_printer::do_fill (int on) +{ + if (fill_on != on) { + if (is_font_courier(output_style.f) && (is_courier_until_eol())) { + if (on) { + current_paragraph->do_pre(); + } else { + current_paragraph->done_pre(); + } + } + } + fill_on = on; +} + +/* + * do_eol - handle the end of line + */ + +void html_printer::do_eol (void) +{ + if (! fill_on) { + current_paragraph->do_newline(); + current_paragraph->do_break(); + } + output_hpos = pageoffset; + if (end_center > 0) { + if (end_center > 1) { + current_paragraph->do_break(); + } + end_center--; + if (end_center == 0) { + current_paragraph->done_para(); + supress_sub_sup = TRUE; + } + } +} + +/* + * do_flush - flushes all output and tags. + */ + +void html_printer::do_flush (void) +{ + current_paragraph->done_para(); +} + +/* + * do_links - moves onto a new temporary file and sets auto_links to FALSE. + */ + +void html_printer::do_links (void) +{ + current_paragraph->done_para(); + auto_links = FALSE; /* from now on only emit under user request */ +#if !defined(DEBUGGING) + file_list.add_new_file(xtmpfile()); + html.set_file(file_list.get_file()); +#endif +} + +/* + * troff_tag - processes the troff tag and manipulates the troff state machine. + */ + +void html_printer::troff_tag (text_glob *g) +{ + /* + * firstly skip over html-tag: + */ + char *t=(char *)g->text_string+9; + + if (g->is_eol()) { + do_eol(); + } else if (strncmp(t, ".sp", 3) == 0) { + current_paragraph->do_space(); + supress_sub_sup = TRUE; + } else if (strncmp(t, ".br", 3) == 0) { + current_paragraph->do_break(); + output_hpos = pageoffset; + supress_sub_sup = TRUE; + } else if (strcmp(t, ".centered-image") == 0) { + do_centered_image(); + } else if (strcmp(t, ".right-image") == 0) { + do_right_image(); + } else if (strcmp(t, ".left-image") == 0) { + do_left_image(); + } else if (strncmp(t, ".auto-image", 11) == 0) { + char *a = (char *)t+11; + do_auto_image(g, a); + } else if (strncmp(t, ".ce", 3) == 0) { + char *a = (char *)t+3; + supress_sub_sup = TRUE; + do_center(a); + } else if (strncmp(t, ".tl", 3) == 0) { + supress_sub_sup = TRUE; + do_title(); + } else if (strncmp(t, ".fi", 3) == 0) { + do_fill(TRUE); + } else if (strncmp(t, ".nf", 3) == 0) { + do_fill(FALSE); + } else if ((strncmp(t, ".SH", 3) == 0) || (strncmp(t, ".NH", 3) == 0)) { + char *a = (char *)t+3; + do_heading(a); + } else if (strncmp(t, ".ll", 3) == 0) { + char *a = (char *)t+3; + do_linelength(a); + } else if (strncmp(t, ".po", 3) == 0) { + char *a = (char *)t+3; + do_pageoffset(a); + } else if (strncmp(t, ".in", 3) == 0) { + char *a = (char *)t+3; + do_indentation(a); + } else if (strncmp(t, ".vs", 3) == 0) { + char *a = (char *)t+3; + do_verticalspacing(a); + } else if (strcmp(t, ".links") == 0) { + do_links(); + } +} + +/* + * is_in_middle - returns TRUE if the positions left..right are in the center of the page. + */ + +int html_printer::is_in_middle (int left, int right) +{ + return( abs(abs(left-pageoffset) - abs(pageoffset+linelength-right)) <= CENTER_TOLERANCE ); +} + +/* + * flush_globs - runs through the text glob list and emits html. + */ + +void html_printer::flush_globs (void) +{ + text_glob *g; + + if (! page_contents->glyphs.is_empty()) { + page_contents->glyphs.start_from_head(); + do { + g = page_contents->glyphs.get_data(); + + if (strcmp(g->text_string, "ZZZZ") == 0) { + stop(); + } + + if (g->is_raw()) { + emit_raw(g); + } else if (g->is_a_tag()) { + troff_tag(g); + } else if (g->is_a_line()) { + emit_line(g); + } else { + translate_to_html(g); + } + /* + * after processing the title (and removing it) the glyph list might be empty + */ + if (! page_contents->glyphs.is_empty()) { + page_contents->glyphs.move_right(); + } + } while (! page_contents->glyphs.is_equal_to_head()); + } +} + +void html_printer::flush_page (void) +{ + supress_sub_sup = TRUE; + flush_sbuf(); + // page_contents->dump_page(); + flush_globs(); + current_paragraph->done_para(); + + // move onto a new page + delete page_contents; + page_contents = new page; +} + +/* + * determine_space - works out whether we need to write a space. + * If last glyth is ajoining then no space emitted. + */ + +void html_printer::determine_space (text_glob *g) +{ + if (current_paragraph->is_in_pre()) { + int space_width = sbuf_style.f->get_space_width(sbuf_style.point_size); + /* + * .nf has been specified + */ + while (output_hpos < g->minh) { + output_hpos += space_width; + current_paragraph->emit_space(); + } + } else { + if ((output_vpos != g->minv) || (output_hpos < g->minh)) { + current_paragraph->emit_space(); + } + } +} + +/* + * is_font_courier - returns TRUE if the font, f, is courier. + */ + +int html_printer::is_font_courier (font *f) +{ + if (f != 0) { + const char *fontname = f->get_name(); + + return( (fontname != 0) && (fontname[0] == 'C') ); + } + return( FALSE ); +} + +/* + * end_font - shuts down the font corresponding to fontname. + */ + +void html_printer::end_font (const char *fontname) +{ + if (strcmp(fontname, "B") == 0) { + current_paragraph->done_bold(); + } else if (strcmp(fontname, "I") == 0) { + current_paragraph->done_italic(); + } else if (strcmp(fontname, "BI") == 0) { + current_paragraph->done_bold(); + current_paragraph->done_italic(); + } else if (strcmp(fontname, "CR") == 0) { + current_paragraph->done_tt(); + current_paragraph->done_pre(); + } +} + +/* + * start_font - starts the font corresponding to name. + */ + +void html_printer::start_font (const char *fontname) +{ + if (strcmp(fontname, "R") == 0) { + current_paragraph->done_bold(); + current_paragraph->done_italic(); + current_paragraph->done_tt(); + } else if (strcmp(fontname, "B") == 0) { + current_paragraph->do_bold(); + } else if (strcmp(fontname, "I") == 0) { + current_paragraph->do_italic(); + } else if (strcmp(fontname, "BI") == 0) { + current_paragraph->do_bold(); + current_paragraph->do_italic(); + } else if (strcmp(fontname, "CR") == 0) { + if ((! fill_on) && (is_courier_until_eol())) { + current_paragraph->do_pre(); + } + current_paragraph->do_tt(); + } +} + +/* + * start_size - from is old font size, to is the new font size. + * The html increase <big> and <small> decrease alters the + * font size by 20%. We try and map these onto glyph sizes. + */ + +void html_printer::start_size (int from, int to) +{ + if (from < to) { + while (from < to) { + current_paragraph->do_big(); + from += SIZE_INCREMENT; + } + } else if (from > to) { + while (from > to) { + current_paragraph->do_small(); + from -= SIZE_INCREMENT; + } + } +} + +/* + * do_font - checks to see whether we need to alter the html font. + */ + +void html_printer::do_font (text_glob *g) +{ + /* + * check if the output_style.point_size has not been set yet + * this allow users to place .ps at the top of their troff files + * and grohtml can then treat the .ps value as the base font size (3) + */ + if (output_style.point_size == -1) { + output_style.point_size = pointsize; + } + + if (g->text_style.f != output_style.f) { + if (output_style.f != 0) { + end_font(output_style.f->get_name()); + } + output_style.f = g->text_style.f; + if (output_style.f != 0) { + start_font(output_style.f->get_name()); + } + } + if (output_style.point_size != g->text_style.point_size) { + do_sup_or_sub(g); + if ((output_style.point_size > 0) && + (g->text_style.point_size > 0)) { + start_size(output_style.point_size, g->text_style.point_size); + } + if (g->text_style.point_size > 0) { + output_style.point_size = g->text_style.point_size; + } + } +} + +/* + * start_subscript - returns TRUE if, g, looks like a subscript start. + */ + +int html_printer::start_subscript (text_glob *g) +{ + int r = font::res; + int height = output_style.point_size*r/72; + + return( (output_style.point_size != 0) && + (output_vpos < g->minv) && + (output_vpos-height > g->maxv) && + (output_style.point_size > g->text_style.point_size) ); +} + +/* + * start_superscript - returns TRUE if, g, looks like a superscript start. + */ + +int html_printer::start_superscript (text_glob *g) +{ + int r = font::res; + int height = output_style.point_size*r/72; + + return( (output_style.point_size != 0) && + (output_vpos > g->minv) && + (output_vpos-height < g->maxv) && + (output_style.point_size > g->text_style.point_size) ); +} + +/* + * end_subscript - returns TRUE if, g, looks like the end of a subscript. + */ + +int html_printer::end_subscript (text_glob *g) +{ + int r = font::res; + int height = output_style.point_size*r/72; + + return( (output_style.point_size != 0) && + (g->minv < output_vpos) && + (output_vpos-height > g->maxv) && + (output_style.point_size < g->text_style.point_size) ); +} + +/* + * end_superscript - returns TRUE if, g, looks like the end of a superscript. + */ + +int html_printer::end_superscript (text_glob *g) +{ + int r = font::res; + int height = output_style.point_size*r/72; + + return( (output_style.point_size != 0) && + (g->minv > output_vpos) && + (output_vpos-height < g->maxv) && + (output_style.point_size < g->text_style.point_size) ); +} + +/* + * do_sup_or_sub - checks to see whether the next glyph is a subscript/superscript + * start/end and it calls the services of html-text to issue the + * appropriate tags. + */ + +void html_printer::do_sup_or_sub (text_glob *g) +{ + if (! supress_sub_sup) { + if (start_subscript(g)) { + current_paragraph->do_sub(); + } else if (start_superscript(g)) { + current_paragraph->do_sup(); + } else if (end_subscript(g)) { + current_paragraph->done_sub(); + } else if (end_superscript(g)) { + current_paragraph->done_sup(); + } + } +} + +/* + * translate_to_html - translates a textual string into html text + */ + +void html_printer::translate_to_html (text_glob *g) +{ + char buf[MAX_STRING_LENGTH]; + + do_font(g); + determine_space(g); + str_translate_to_html(g->text_style.f, buf, MAX_STRING_LENGTH, + g->text_string, g->text_length, TRUE); + current_paragraph->do_emittext(buf, strlen(buf)); + output_vpos = g->minv; + output_hpos = g->maxh; + output_vpos_max = g->maxv; + supress_sub_sup = FALSE; +} + +/* + * flush_sbuf - flushes the current sbuf into the list of glyphs. + */ + +void html_printer::flush_sbuf() +{ + if (sbuf_len > 0) { + int r=font::res; // resolution of the device + set_style(sbuf_style); + + page_contents->add(&sbuf_style, sbuf, sbuf_len, + line_number, + sbuf_vpos-sbuf_style.point_size*r/72, sbuf_start_hpos, + sbuf_vpos , sbuf_end_hpos); + + output_hpos = sbuf_end_hpos; + output_vpos = sbuf_vpos; + sbuf_len = 0; + } +} + +void html_printer::set_line_thickness(const environment *env) +{ + line_thickness = env->size; +} + +void html_printer::draw(int code, int *p, int np, const environment *env) +{ + switch (code) { + + case 'l': + if (np == 2) { + page_contents->add_line(&sbuf_style, + line_number, + env->hpos, env->vpos, env->hpos+p[0], env->vpos+p[1], line_thickness); + } else { + error("2 arguments required for line"); + } + break; + case 't': + { + if (np == 0) { + line_thickness = -1; + } else { + // troff gratuitously adds an extra 0 + if (np != 1 && np != 2) { + error("0 or 1 argument required for thickness"); + break; + } + line_thickness = p[0]; + } + break; + } + + case 'P': + // fall through + case 'p': + { +#if 0 + if (np & 1) { + error("even number of arguments required for polygon"); + break; + } + if (np == 0) { + error("no arguments for polygon"); + break; + } + // firstly lets add our current position to polygon + int oh=env->hpos; + int ov=env->vpos; + int i=0; + + while (i<np) { + p[i+0] += oh; + p[i+1] += ov; + oh = p[i+0]; + ov = p[i+1]; + i += 2; + } + // now store polygon in page + page_contents->add_polygon(code, np, p, env->hpos, env->vpos, env->size, fill); +#endif + } + break; + case 'E': + // fall through + case 'e': +#if 0 + if (np != 2) { + error("2 arguments required for ellipse"); + break; + } + page_contents->add_line(code, + env->hpos, env->vpos-p[1]/2, env->hpos+p[0], env->vpos+p[1]/2, + env->size, fill); +#endif + break; + case 'C': + // fill circle + + case 'c': + { +#if 0 + // troff adds an extra argument to C + if (np != 1 && !(code == 'C' && np == 2)) { + error("1 argument required for circle"); + break; + } + page_contents->add_line(code, + env->hpos, env->vpos-p[0]/2, env->hpos+p[0], env->vpos+p[0]/2, + env->size, fill); +#endif + } + break; + case 'a': + { +#if 0 + if (np == 4) { + double c[2]; + + if (adjust_arc_center(p, c)) { + page_contents->add_arc('a', env->hpos, env->vpos, p, c, env->size, fill); + } else { + // a straignt line + page_contents->add_line('l', env->hpos, env->vpos, p[0]+p[2], p[1]+p[3], env->size, fill); + } + } else { + error("4 arguments required for arc"); + } +#endif + } + break; + case '~': + { +#if 0 + if (np & 1) { + error("even number of arguments required for spline"); + break; + } + if (np == 0) { + error("no arguments for spline"); + break; + } + // firstly lets add our current position to spline + int oh=env->hpos; + int ov=env->vpos; + int i=0; + + while (i<np) { + p[i+0] += oh; + p[i+1] += ov; + oh = p[i+0]; + ov = p[i+1]; + i += 2; + } + page_contents->add_spline('~', env->hpos, env->vpos, np, p, env->size, fill); +#endif + } + break; + case 'f': + { +#if 0 + if (np != 1 && np != 2) { + error("1 argument required for fill"); + break; + } + fill = p[0]; + if (fill < 0 || fill > FILL_MAX) { + // This means fill with the current color. + fill = FILL_MAX + 1; + } +#endif + break; + } + + default: + error("unrecognised drawing command `%1'", char(code)); + break; + } +} + +html_printer::html_printer() +: html(0, MAX_LINE_LENGTH), + no_of_printed_pages(0), + sbuf_len(0), + output_hpos(-1), + output_vpos(-1), + output_vpos_max(-1), + line_thickness(-1), + inside_font_style(0), + page_number(0), + header_indent(-1), + supress_sub_sup(TRUE), + cutoff_heading(100), + end_center(0), + next_tag(INLINE), + fill_on(TRUE), + linelength(0), + pageoffset(0), + indentation(0), + line_number(0) +{ +#if defined(DEBUGGING) + file_list.add_new_file(stdout); +#else + file_list.add_new_file(xtmpfile()); +#endif + html.set_file(file_list.get_file()); + if (font::hor != 24) + fatal("horizontal resolution must be 1"); + if (font::vert != 40) + fatal("vertical resolution must be 1"); +#if 0 + // should be sorted html.. + if (font::res % (font::sizescale*72) != 0) + fatal("res must be a multiple of 72*sizescale"); +#endif + int r = font::res; + int point = 0; + while (r % 10 == 0) { + r /= 10; + point++; + } + res = r; + html.set_fixed_point(point); + space_char_index = font::name_to_index("space"); + paper_length = font::paperlength; + if (paper_length == 0) + paper_length = 11*font::res; + + page_contents = new page(); +} + +/* + * add_char_to_sbuf - adds a single character to the sbuf. + */ + +void html_printer::add_char_to_sbuf (unsigned char code) +{ + if (sbuf_len < SBUF_SIZE) { + sbuf[sbuf_len] = code; + sbuf_len++; + } else { + fatal("need to increase SBUF_SIZE"); + } +} + +/* + * add_to_sbuf - adds character code or name to the sbuf. + */ + +void html_printer::add_to_sbuf (char code, const char *name) +{ + if (name == 0) { + add_char_to_sbuf(code); + } else { + if (sbuf_style.f != NULL) { + char *html_glyph = get_html_translation(sbuf_style.f, name); + + if (html_glyph != NULL) { + int l = strlen(html_glyph); + int i; + + for (i=0; i<l; i++) { + add_char_to_sbuf(html_glyph[i]); + } + } + } + } +} + +int html_printer::sbuf_continuation (unsigned char code, const char *name, + const environment *env, int w) +{ + if (sbuf_end_hpos == env->hpos) { + add_to_sbuf(code, name); + sbuf_end_hpos += w + sbuf_kern; + return( TRUE ); + } else { + if ((sbuf_len < SBUF_SIZE-1) && (env->hpos >= sbuf_end_hpos) && + ((sbuf_kern == 0) || (sbuf_end_hpos - sbuf_kern != env->hpos))) { + /* + * lets see whether a space is needed or not + */ + int space_width = sbuf_style.f->get_space_width(sbuf_style.point_size); + + if (env->hpos-sbuf_end_hpos < space_width/2) { + add_to_sbuf(code, name); + sbuf_end_hpos = env->hpos + w; + return( TRUE ); + } + } + } + return( FALSE ); +} + +/* + * seen_backwards_escape - returns TRUE if we can see a escape at position i..l in s + */ + +int html_printer::seen_backwards_escape (char *s, int l) +{ + /* + * this is tricky so it is broken into components for clarity + * (we let the compiler put in all back into a complex expression) + */ + if ((l>0) && (sbuf[l] == '(') && (sbuf[l-1] == '\\')) { + /* + * ok seen '\(' but we must now check for '\\(' + */ + if ((l>1) && (sbuf[l-2] == '\\')) { + /* + * escaped the escape + */ + return( FALSE ); + } else { + return( TRUE ); + } + } else { + return( FALSE ); + } +} + +/* + * reverse - return reversed string. + */ + +char *reverse (char *s) +{ + int i=0; + int j=strlen(s)-1; + char t; + + while (i<j) { + t = s[i]; + s[i] = s[j]; + s[j] = t; + i++; + j--; + } + return( s ); +} + +/* + * remove_last_char_from_sbuf - removes the last character from sbuf. + */ + +char *html_printer::remove_last_char_from_sbuf () +{ + int l=sbuf_len; + static char last[MAX_STRING_LENGTH]; + + if (l>0) { + l--; + if ((sbuf[l] == ')') && (l>0) && (sbuf[l-1] == '\\')) { + /* + * found terminating escape + */ + int i=0; + + l -= 2; + while ((l>0) && (! seen_backwards_escape(sbuf, l))) { + if (sbuf[l] == '\\') { + if (sbuf[l-1] == '\\') { + last[i] = sbuf[l]; + i++; + l--; + } + } else { + last[i] = sbuf[l]; + i++; + } + l--; + } + last[i] = (char)0; + sbuf_len = l; + if (seen_backwards_escape(sbuf, l)) { + sbuf_len--; + } + return( reverse(last) ); + } else { + if ((sbuf[l] == '\\') && (l>0) && (sbuf[l-1] == '\\')) { + l -= 2; + sbuf_len = l; + return( "\\" ); + } else { + sbuf_len--; + last[0] = sbuf[sbuf_len]; + last[1] = (char)0; + return( last ); + } + } + } else { + return( NULL ); + } +} + +/* + * get_html_translation - given the position of the character and its name + * return the device encoding for such character. + */ + +char *get_html_translation (font *f, const char *name) +{ + int index; + + if ((f == 0) || (name == 0) || (strcmp(name, "") == 0)) { + return( NULL ); + } else { + index = f->name_to_index((char *)name); + if (index == 0) { + error("character `%s' not found", name); + return( NULL ); + } else { + if (f->contains(index)) { + return( (char *)f->get_special_device_encoding(index) ); + } else { + return( NULL ); + } + } + } +} + +/* + * char_translate_to_html - convert a single non escaped character + * into the appropriate html character. + */ + +int char_translate_to_html (font *f, char *buf, int buflen, char ch, int b, int and_single) +{ + if (and_single) { + int t, l; + char *translation; + char name[2]; + + name[0] = ch; + name[1] = (char)0; + translation = get_html_translation(f, name); + if (translation) { + l = strlen(translation); + t = max(0, min(l, buflen-b)); + strncpy(&buf[b], translation, t); + b += t; + } else { + if (b<buflen) { + buf[b] = ch; + b++; + } + } + } else { + /* + * do not attempt to encode single characters + */ + if (b<buflen) { + buf[b] = ch; + b++; + } + } + return( b ); +} + +/* + * str_translate_to_html - converts a string, str, into html text. It places + * the output input buffer, buf. It truncates string, str, if + * there is not enough space in buf. + * It looks up the html character encoding of single characters + * if, and_single, is TRUE. Characters such as < > & etc. + */ + +void str_translate_to_html (font *f, char *buf, int buflen, char *str, int len, int and_single) +{ + char *translation; + int e; + char escaped_char[MAX_STRING_LENGTH]; + int l; + int i=0; + int b=0; + int t=0; + +#if 0 + if (strcmp(str, "``@,;:\\\\()[]''") == 0) { + stop(); + } +#endif + while (str[i] != (char)0) { + if ((str[i]=='\\') && (i+1<len)) { + i++; // skip the leading backslash + if (str[i] == '(') { + // start of escape + i++; + e = 0; + while ((str[i] != (char)0) && + (! ((str[i] == '\\') && (i+1<len) && (str[i+1] == ')')))) { + if (str[i] == '\\') { + i++; + } + escaped_char[e] = str[i]; + e++; + i++; + } + if ((str[i] == '\\') && (i+1<len) && (str[i+1] == ')')) { + i += 2; + } + escaped_char[e] = (char)0; + if (e > 0) { + translation = get_html_translation(f, escaped_char); + if (translation) { + l = strlen(translation); + t = max(0, min(l, buflen-b)); + strncpy(&buf[b], translation, t); + b += t; + } else { + int index=f->name_to_index(escaped_char); + + if (f->contains(index) && (index != 0)) { + buf[b] = f->get_code(index); + b++; + } + } + } + } else { + b = char_translate_to_html(f, buf, buflen, str[i], b, and_single); + i++; + } + } else { + b = char_translate_to_html(f, buf, buflen, str[i], b, and_single); + i++; + } + } + buf[min(b, buflen)] = (char)0; +} + +/* + * set_char - adds a character into the sbuf if it is a continuation with the previous + * word otherwise flush the current sbuf and add character anew. + */ + +void html_printer::set_char(int i, font *f, const environment *env, int w, const char *name) +{ + unsigned char code = f->get_code(i); + +#if 0 + if (code == ' ') { + stop(); + } +#endif + style sty(f, env->size, env->height, env->slant, env->fontno); + if (sty.slant != 0) { + if (sty.slant > 80 || sty.slant < -80) { + error("silly slant `%1' degrees", sty.slant); + sty.slant = 0; + } + } + if ((sbuf_len > 0) && (sbuf_len < SBUF_SIZE) && (sty == sbuf_style) && + (sbuf_vpos == env->vpos) && (sbuf_continuation(code, name, env, w))) { + return; + } else { + flush_sbuf(); + sbuf_len = 0; + add_to_sbuf(code, name); + sbuf_end_hpos = env->hpos + w; + sbuf_start_hpos = env->hpos; + sbuf_vpos = env->vpos; + sbuf_style = sty; + sbuf_kern = 0; + } +} + +/* + * write_title - writes the title to this document + */ + +void html_printer::write_title (int in_head) +{ + if (title.has_been_found) { + if (in_head) { + html.put_string("<title>"); + html.put_string(title.text); + html.put_string("</title>\n\n"); + } else { + title.has_been_written = TRUE; + html.put_string("<h1 align=center>"); + html.put_string(title.text); + html.put_string("</h1>\n\n"); + } + } +} + +/* + * write_rule - emits a html rule tag, if the auto_rule boolean is true. + */ + +static void write_rule (void) +{ + if (auto_rule) + fputs("<hr>\n", stdout); +} + +void html_printer::begin_page(int n) +{ + page_number = n; + html.begin_comment("Page: ").comment_arg(i_to_a(page_number)).end_comment();; + no_of_printed_pages++; + + output_style.f = 0; + output_style.point_size= -1; + output_space_code = 32; + output_draw_point_size = -1; + output_line_thickness = -1; + output_hpos = -1; + output_vpos = -1; + output_vpos_max = -1; + current_paragraph = new html_text(&html); + current_paragraph->do_para(""); +} + +void html_printer::end_page(int) +{ + flush_sbuf(); + flush_page(); +} + +font *html_printer::make_font(const char *nm) +{ + return html_font::load_html_font(nm); +} + +html_printer::~html_printer() +{ + current_paragraph->done_para(); + html.set_file(stdout); + fputs("<html>\n", stdout); + fputs("<head>\n", stdout); + fputs("<meta name=\"generator\" content=\"groff -Thtml, see www.gnu.org\">\n", stdout); + fputs("<meta name=\"Content-Style\" content=\"text/css\">\n", stdout); + write_title(TRUE); + fputs("</head>\n", stdout); + fputs("<body>\n\n", stdout); + write_title(FALSE); + header.write_headings(stdout, FALSE); + write_rule(); + { + extern const char *Version_string; + html.begin_comment("Creator : ") + .comment_arg("groff ") + .comment_arg("version ") + .comment_arg(Version_string) + .end_comment(); + } + { +#ifdef LONG_FOR_TIME_T + long +#else + time_t +#endif + t = time(0); + html.begin_comment("CreationDate: ") + .comment_arg(ctime(&t)) + .end_comment(); + } + html.begin_comment("Total number of pages: ").comment_arg(i_to_a(no_of_printed_pages)).end_comment(); + html.end_line(); + /* + * now run through the file list copying each temporary file in turn and emitting the links. + */ + file_list.start_of_list(); + while (file_list.get_file() != 0) { + if (fseek(file_list.get_file(), 0L, 0) < 0) + fatal("fseek on temporary file failed"); + html.copy_file(file_list.get_file()); + fclose(file_list.get_file()); + file_list.move_next(); + if (file_list.get_file() != 0) + header.write_headings(stdout, TRUE); + } + write_rule(); + fputs("</body>\n", stdout); + fputs("</html>\n", stdout); +} + +/* + * special - handle all x X requests from troff. For post-html they allow users + * to pass raw html commands, turn auto linked headings off/on and + * also allow troff to emit tags to indicate when a: .br, .sp etc occurs. + */ + +void html_printer::special(char *s, const environment *env) +{ + if (s != 0) { + flush_sbuf(); + if (env->fontno >= 0) { + style sty(get_font_from_index(env->fontno), env->size, env->height, env->slant, env->fontno); + sbuf_style = sty; + } + + if (strncmp(s, "html:", 5) == 0) { + int r=font::res; /* resolution of the device */ + char buf[MAX_STRING_LENGTH]; + font *f=sbuf_style.f; + + if (f == NULL) { + int found=FALSE; + + f = font::load_font("TR", &found); + } + str_translate_to_html(f, buf, MAX_STRING_LENGTH, + &s[5], strlen(s)-5, FALSE); + + /* + * need to pass rest of string through to html output during flush + */ + page_contents->add_html(&sbuf_style, buf, strlen(buf), + line_number, + env->vpos-env->size*r/72, env->hpos, + env->vpos , env->hpos); + + /* + * assume that the html command has no width, if it does then hopefully troff + * will have fudged this in a macro by requesting that the formatting move right by + * the appropriate width. + */ + } else if (strncmp(s, "index:", 6) == 0) { + cutoff_heading = atoi(&s[6]); + } else if (strncmp(s, "html-tag:", 9) == 0) { + int r=font::res; /* resolution of the device */ + + page_contents->add_tag(&sbuf_style, s, strlen(s), + line_number, + env->vpos-env->size*r/72, env->hpos, + env->vpos , env->hpos); + } + } +} + +int main(int argc, char **argv) +{ + program_name = argv[0]; + static char stderr_buf[BUFSIZ]; + setbuf(stderr, stderr_buf); + int c; + while ((c = getopt(argc, argv, "o:i:F:vd?lrn")) != EOF) + switch(c) { + case 'v': + { + extern const char *Version_string; + fprintf(stderr, "post-grohtml version %s\n", Version_string); + fflush(stderr); + break; + } + case 'F': + font::command_line_font_dir(optarg); + break; + case 'l': + auto_links = FALSE; + break; + case 'r': + auto_rule = FALSE; + break; + case '?': + usage(); + break; + case 'o': + /* handled by pre-html */ + break; + case 'i': + /* handled by pre-html */ + break; + case 'n': + simple_anchors = TRUE; + break; + default: + assert(0); + } + if (optind >= argc) { + do_file("-"); + } else { + for (int i = optind; i < argc; i++) + do_file(argv[i]); + } + delete pr; + return 0; +} + +static void usage() +{ + fprintf(stderr, "usage: %s [-vld?n] [-F dir] [files ...]\n", + program_name); + exit(1); +} diff --git a/src/devices/grohtml2/Makefile.in b/src/devices/grohtml2/Makefile.in deleted file mode 100755 index bca59ad7f..000000000 --- a/src/devices/grohtml2/Makefile.in +++ /dev/null @@ -1,9 +0,0 @@ -PROG=post-grohtml -MAN1= -XLIBS=$(LIBDRIVER) $(LIBGROFF) -MLIB=$(LIBM) -OBJS=\ - post-html.o -CCSRCS=\ - $(srcdir)/post-html.cc - diff --git a/src/devices/grohtml2/Makefile.sub b/src/devices/grohtml2/Makefile.sub deleted file mode 100755 index f43a276da..000000000 --- a/src/devices/grohtml2/Makefile.sub +++ /dev/null @@ -1,9 +0,0 @@ -PROG=post-grohtml -MAN1= -# MAN1=grohtml.n -XLIBS=$(LIBDRIVER) $(LIBGROFF) -MLIB=$(LIBM) -OBJS=\ - post-html.o -CCSRCS=\ - $(srcdir)/post-html.cc diff --git a/src/devices/grohtml2/post-html.cc b/src/devices/grohtml2/post-html.cc deleted file mode 100755 index 3ef4daa80..000000000 --- a/src/devices/grohtml2/post-html.cc +++ /dev/null @@ -1,93 +0,0 @@ -// -*- C++ -*- -/* Copyright (C) 1999, 2000 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 - * James Clark (jjc@jclark.com) grops/ps.cc. - */ - -/* -This file is part of groff. - -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. */ - -#include "driver.h" -#include "stringclass.h" -#include "cset.h" - -#include <time.h> - -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif - -#include <stdio.h> -#include <fcntl.h> - -#if !defined(TRUE) -# define TRUE (1==1) -#endif -#if !defined(FALSE) -# define FALSE (1==0) -#endif - -printer *make_printer() -{ - // return new html_printer; - return( 0 ); -} - -static void usage(); - -int main(int argc, char **argv) -{ - program_name = argv[0]; - static char stderr_buf[BUFSIZ]; - setbuf(stderr, stderr_buf); - int c; - while ((c = getopt(argc, argv, "F:atTvdgmx?I:r:")) != EOF) - switch(c) { - case 'v': - { - extern const char *Version_string; - printf("GNU post-grohtml (groff) version %s\n", Version_string); - exit(0); - break; - } - case 'F': - font::command_line_font_dir(optarg); - break; - case '?': - usage(); - break; - default: - assert(0); - } - if (optind >= argc) { - do_file("-"); - } else { - for (int i = optind; i < argc; i++) - do_file(argv[i]); - } - delete pr; - return 0; -} - -static void usage() -{ - fprintf(stderr, "usage: %s [-avdgmt?] [-r resolution] [-F dir] [-I imagetype] [files ...]\n", - program_name); - exit(1); -} diff --git a/src/preproc/html2/pre-html.h b/src/include/html-strings.h index 7f56600da..375f5fba6 100755..100644 --- a/src/preproc/html2/pre-html.h +++ b/src/include/html-strings.h @@ -1,5 +1,5 @@ // -*- C++ -*- -/* Copyright (C) 2000 Free Software Foundation, Inc. +/* Copyright (C) 2001 Free Software Foundation, Inc. Written by Gaius Mulley (gaius@glam.ac.uk). This file is part of groff. @@ -19,19 +19,12 @@ with groff; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* - * defines functions implemented within pre-html.c + * defines the image tags issued by the pre-processors (tbl, pic, eqn) + * and later detected by pre-html.cc */ -#if !defined(PREHTMLH) -# define PREHTMLH -# if defined(PREHTMLC) -# define EXTERN -# else -# define EXTERN extern -# endif - - -extern void sys_fatal (const char *s); - -#undef EXTERN -#endif +#define HTML_IMAGE_INLINE ".HTML-IMAGE-INLINE" +#define HTML_IMAGE_CENTERED ".HTML-IMAGE" +#define HTML_IMAGE_RIGHT ".HTML-IMAGE-RIGHT" +#define HTML_IMAGE_LEFT ".HTML-IMAGE-LEFT" +#define HTML_IMAGE_END ".HTML-IMAGE-END" diff --git a/src/include/htmlindicate.h b/src/include/htmlindicate.h index a375fb221..488fdbdcf 100755 --- a/src/include/htmlindicate.h +++ b/src/include/htmlindicate.h @@ -1,5 +1,5 @@ // -*- C++ -*- -/* Copyright (C) 2000 Free Software Foundation, Inc. +/* Copyright (C) 2000, 2001 Free Software Foundation, Inc. Written by Gaius Mulley <gaius@glam.ac.uk> This file is part of groff. @@ -24,35 +24,41 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * graphic_start - emit a html graphic start indicator, but only * if one has not already been issued. + * + * The boolean, is_inline, should be: + * + * FALSE if this is called via EQ, TS, PS, and + * TRUE if issued via delim $$ $ x over y $ etc. */ -extern void graphic_start (void); +extern void graphic_start (int is_inline); /* * graphic_end - emit a html graphic end indicator, but only * if a corresponding matching graphic-start has * been issued. + * */ extern void graphic_end (void); /* - * html_begin_suppress - if the 'htmlflip' variable is set to 1 then - * all text following this line will be suppressed by troff - * and if the -Thtml2 device is specified a generic IMAGE tag - * is emitted which is later filled in by the pre-html preprocessor. + * html_begin_suppress - suppresses output for the html device + * and resets the min/max registers for -Tps + * + * The boolean, is_inline, should be: + * + * FALSE if this is called via EQ, TS, PS, and + * TRUE if issued via delim $$ $ x over y $ etc. */ -extern void html_begin_suppress (void); +extern void html_begin_suppress (int is_inline); + /* - * html_end_suppress - if the 'htmlflip' variable has been set then - * enable generation of text after this line of troff. - * If 'htmlflip' and -Thtml2 is set then issue the - * upper x,y and lower x,y coordinates to stderr via - * a troff '.tm' command. + * html_end_suppress - end the suppression of output. */ extern void html_end_suppress (void); diff --git a/src/include/printer.h b/src/include/printer.h index 974c9d5b0..d7253d03b 100644 --- a/src/include/printer.h +++ b/src/include/printer.h @@ -1,5 +1,5 @@ // -*- C++ -*- -/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc. +/* Copyright (C) 1989, 1990, 1991, 1992, 2001 Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) This file is part of groff. @@ -48,6 +48,7 @@ public: void set_numbered_char(int n, const environment *env, int *widthp = 0); int set_char_and_width(const char *nm, const environment *env, int *widthp, font **f); + font *get_font_from_index(int fontno); virtual void draw(int code, int *p, int np, const environment *env); virtual void begin_page(int) = 0; virtual void end_page(int page_length) = 0; diff --git a/src/libs/libdriver/printer.cc b/src/libs/libdriver/printer.cc index 770aa5071..f7c20384f 100644 --- a/src/libs/libdriver/printer.cc +++ b/src/libs/libdriver/printer.cc @@ -1,5 +1,5 @@ // -*- C++ -*- -/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc. +/* Copyright (C) 1989, 1990, 1991, 1992, 2001 Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) This file is part of groff. @@ -192,6 +192,14 @@ void printer::set_numbered_char(int num, const environment *env, int *widthp) set_char(i, f, env, w, 0); } +font *printer::get_font_from_index(int fontno) +{ + if ((fontno >= 0) && (fontno < nfonts)) + return(font_table[fontno]); + else + return(0); +} + // This utility function adjusts the specified center of the // arc so that it is equidistant between the specified start // and end points. (p[0], p[1]) is a vector from the current diff --git a/src/libs/libgroff/htmlindicate.cc b/src/libs/libgroff/htmlindicate.cc index 6b5913f33..0eb2c1c00 100755 --- a/src/libs/libgroff/htmlindicate.cc +++ b/src/libs/libgroff/htmlindicate.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 Free Software Foundation, Inc. +/* Copyright (C) 2000, 2001 Free Software Foundation, Inc. Written by Gaius Mulley (gaius@glam.ac.uk) This file is part of groff. @@ -23,83 +23,72 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "nonposix.h" #include "stringclass.h" +#include "html-strings.h" /* * this file contains a very simple set of routines shared by * tbl, pic, eqn which help the html device driver to make * sensible formatting choices. Currently it simply indicates - * when a region of gyphs should be rendered as an image rather - * than html. In the future it should be expanded so to enable: - * - * tbl to inform grohtml about table widths. - * troff to inform grohtml about tab positions and whether - * we are entering two/three column mode. + * to pre-html when an image is about to be created this is then + * passes to pre-html. + * Pre-html runs troff twice once with -Thtml and once with -Tps. + * troff -Thtml device driver emits a <src='image'.png> tag + * and the postscript device driver works out the min/max limits + * of the graphic region. These region limits are read by pre-html + * and an image is generated via troff -Tps -> gs -> png */ - static int is_in_graphic_start = 0; /* - * html_begin_suppress - if the 'htmlflip' variable is set to 1 then - * all text following this line will be suppressed by troff - * and if the -Thtml2 device is specified a generic IMAGE tag - * is emitted which is later filled in by the pre-html preprocessor. + * html_begin_suppress - */ -void html_begin_suppress (void) +void html_begin_suppress (int is_inline) { - /* - * the pre-html processor looks for <pre-html-image> and replaces it - * with a sensible name - */ - put_string(".if r html2enable .if '\\*(.T'html2' .IMAGE <pre-html-image>\n", stdout); -#if 1 - // debugging information - put_string(".if r html2enable .if !r htmlflip .tm \"htmlflip was not set?\"\n", stdout); -#endif - put_string(".if r html2enable .if r htmlflip .output 1-\\n[htmlflip]\n", stdout); + if (is_inline) { + put_string(HTML_IMAGE_INLINE, stdout); + } else { + put_string(HTML_IMAGE_CENTERED, stdout); + } + put_string("\n", stdout); } /* - * html_end_suppress - if the 'htmlflip' variable is 1 then - * enable generation of text after this line of troff. - * If 'htmlflip' and -Thtml2 is set then issue the - * upper x,y and lower x,y coordinates to stderr via - * a troff '.tm' command. + * html_end_suppress - */ void html_end_suppress (void) { - put_string(".if r html2enable .if r htmlflip .if !'\\*(.T'html2' .tm grohtml-info:page \\n% \\n[opminx] \\n[opminy] \\n[opmaxx] \\n[opmaxy] \\n[.H] \\n[.V] \\n[.F]\n", - stdout); - put_string(".if r html2enable .if r htmlflip .output \\n[htmlflip]\n", stdout); + put_string(HTML_IMAGE_END, stdout); + put_string("\n", stdout); } /* - * graphic_start - emit a html graphic start indicator, but only - * if one has not already been issued. + * graphic_start - The boolean, is_inline, should be: + * + * FALSE if this is called via EQ, TS, PS, and + * TRUE if issued via delim $$ $ x over y $ etc. */ -void graphic_start (void) +void graphic_start (int is_inline) { if (! is_in_graphic_start) { - put_string(".if '\\*(.T'html' \\X(graphic-start(\\c\n", stdout); - html_begin_suppress(); + put_string(".if '\\*(.T'html-old' \\X(graphic-start(\\c\n", stdout); + html_begin_suppress(is_inline); is_in_graphic_start = 1; } } /* - * graphic_end - emit a html graphic end indicator, but only - * if a corresponding matching graphic-start has - * been issued. + * graphic_end - tell troff that the image region is ending. */ void graphic_end (void) { if (is_in_graphic_start) { - put_string(".if '\\*(.T'html' \\X(graphic-end(\\c\n", stdout); html_end_suppress(); + put_string(".if '\\*(.T'html-old' \\X(graphic-end(\\c\n", stdout); is_in_graphic_start = 0; } } diff --git a/src/preproc/eqn/main.cc b/src/preproc/eqn/main.cc index 1cc03303e..354b66eb8 100644 --- a/src/preproc/eqn/main.cc +++ b/src/preproc/eqn/main.cc @@ -1,5 +1,6 @@ // -*- C++ -*- -/* Copyright (C) 1989, 1990, 1991, 1992, 2000 Free Software Foundation, Inc. +/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001 + Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) This file is part of groff. @@ -79,8 +80,8 @@ void do_file(FILE *fp, const char *filename) && linebuf[1] == 'E' && linebuf[2] == 'Q' && (linebuf[3] == ' ' || linebuf[3] == '\n' || compatible_flag)) { + graphic_start(0); put_string(linebuf, stdout); - graphic_start(); int start_lineno = current_lineno + 1; str.clear(); for (;;) { @@ -167,7 +168,7 @@ static int inline_equation(FILE *fp, string &linebuf, string &str) ptr = &linebuf[0]; } str += '\0'; - graphic_start(); + graphic_start(1); init_lex(str.contents(), current_filename, start_lineno); yyparse(); start = delim_search(ptr, start_delim); @@ -325,8 +326,15 @@ int main(int argc, char **argv) init_table(device); init_char_table(); printf(".if !'\\*(.T'%s' " + ".if !'\\*(.T'html' " /* the html device uses `-Tps' to render + equations as images */ ".tm warning: %s should have been given a `-T\\*(.T' option\n", device, program_name); + printf(".if '\\*(.T'html' " + ".if !'%s'ps' " + ".tm1 \"warning: %s should have been given a `-Tps' option\n" + ".tm1 \" (it is advisable to invoke groff via: groff -Thtml -e)\n", + device, program_name); if (load_startup_file) { char *path; FILE *fp = config_macro_path.open_file(STARTUP_FILE, &path); diff --git a/src/preproc/html2/Makefile.sub b/src/preproc/html2/Makefile.sub deleted file mode 100755 index 9d5045a97..000000000 --- a/src/preproc/html2/Makefile.sub +++ /dev/null @@ -1,7 +0,0 @@ -PROG=pre-grohtml -# MAN1=pre-grohtml.n -MAN1= -XLIBS=$(LIBGROFF) -OBJS=pre-html.o pushbackbuffer.o -CCSRCS=$(srcdir)/pre-html.cc $(srcdir)/pushbackbuffer.cc -NAMEPREFIX=$(g) diff --git a/src/preproc/html2/image.cc b/src/preproc/html2/image.cc deleted file mode 100755 index e05ee3dc5..000000000 --- a/src/preproc/html2/image.cc +++ /dev/null @@ -1,448 +0,0 @@ -// -*- C++ -*- -/* Copyright (C) 2000 Free Software Foundation, Inc. - Written by Gaius Mulley (gaius@glam.ac.uk) but owes much - of the code from James Clark (jjc@jclark.com). - -This file is part of groff. - -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. */ - -#include <stdio.h> -#include <signal.h> -#include <ctype.h> -#include <string.h> -#include <assert.h> -#include <stdlib.h> -#include <errno.h> -#include "lib.h" -#include "errarg.h" -#include "error.h" -#include "stringclass.h" -#include "posix.h" - -#include <errno.h> -#include <sys/types.h> -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif - -#ifdef _POSIX_VERSION -#include <sys/wait.h> -#define PID_T pid_t -#else /* not _POSIX_VERSION */ -#define PID_T int -#endif /* not _POSIX_VERSION */ - -extern char *strerror(); - -static char *file_contents; -static int stdoutfd =1; // output file descriptor - normally 1 but might move - // -1 means closed - -#define DEBUGGING - -/* - * eventually it would be nice to have the choice of image devices. - * This could be implemented once we have a -Tpng or -Tpdf device driver. - * For now we use what is available.. postscript, ghostscript + png utils. - */ - -#define IMAGEDEVICE "-Tps" - - -static int do_file(const char *filename); - -/* - * sys_fatal - writes a fatal error message. Taken from src/roff/groff/pipeline.c - */ - -static void sys_fatal(const char *s) -{ - fprintf(stderr, "%s: %s: %s", program_name, s, strerror(errno)); -} - - sprintf(buffer, - "echo showpage | gs -q -dSAFER -sDEVICE=%s -r%d -g%dx%d -sOutputFile=- %s - 2> /dev/null > %s.png \n", - image_device, - image_res, - (end_region_hpos-start_region_hpos)*image_res/font::res+IMAGE_BOARDER_PIXELS, - (end_region_vpos-start_region_vpos)*image_res/font::res+IMAGE_BOARDER_PIXELS, - ps_src, image_name); - - -/* - * the class and methods for retaining ascii text - */ - -struct char_block { - enum { SIZE = 256 }; - char buffer[SIZE]; - int used; - char_block *next; - - char_block(); -}; - -char_block::char_block() -: used(0), next(0) -{ -} - -class char_buffer { -public: - char_buffer(); - ~char_buffer(); - int read_file(FILE *fp); - int do_html(int argc, char *argv[]); - int do_image(int argc, char *argv[]); - int write_file(int is_html); -private: - char_block *head; - char_block *tail; -}; - -char_buffer::char_buffer() -: head(0), tail(0) -{ -} - -char_buffer::~char_buffer() -{ - while (head != 0) { - char_block *temp = head; - head = head->next; - delete temp; - } -} - -int char_buffer::read_file (FILE *fp) -{ - int i=0; - unsigned int old_used; - int n; - - while (! feof(fp)) { - if (tail == 0) { - tail = new char_block; - head = tail; - } else { - if (tail->used == char_block::SIZE) { - tail->next = new char_block; - tail = tail->next; - } - } - // at this point we have a tail which is ready for the the next SIZE bytes of the file - - n = fread(tail->buffer, sizeof(char), char_block::SIZE-tail->used, fp); - if (n <= 0) { - // error - return( 0 ); - } else { - tail->used += n*sizeof(char); - } - } - return( 1 ); -} - -/* - * writeNbytes - writes n bytes to stdout. - */ - -static void writeNbytes (char *s, int l) -{ - int n=0; - int r; - - while (n<l) { - r = write(stdoutfd, s, l-n); - if (r<0) { - sys_fatal("write"); - } - n += r; - s += r; - } -} - -/* - * writeString - writes a string to stdout. - */ - -static void writeString (char *s) -{ - writeNbytes(s, strlen(s)); -} - -/* - * write_file - writes the buffer to stdout (troff). - * It prepends the number register set to 1 if stdout is - * connected to troff -Thtml and 0 if connected to troff -Tps - */ - -int char_buffer::write_file (int is_html) -{ - char_block *t=head; - int r; - - fprintf(stderr, "output to pipeline\n"); - if (is_html) { - writeString(".nr htmlflip 1\n"); - } else { - writeString(".nr htmlflip 0\n"); - fprintf(stderr, ".nr htmlflip 0\n"); - } - if (t != 0) { - do { - writeNbytes(t->buffer, t->used); - fprintf(stderr, "hunk..\n"); - t = t->next; - } while ((t != head) && (t != 0)); - } - if (close(stdoutfd) < 0) - sys_fatal("close"); - - // now we grab fd=1 so that the next pipe cannot use fd=1 - if (stdoutfd == 1) { - if (dup(2) != stdoutfd) { - sys_fatal("dup failed to use fd=1"); - } - } - - return( 1 ); -} - -/* - * replaceFd - replace a file descriptor, was, with, willbe. - */ - -static void replaceFd (int was, int willbe) -{ - int dupres; - - if (was != willbe) { - if (close(was)<0) { - sys_fatal("close"); - } - dupres = dup(willbe); - if (dupres != was) { - sys_fatal("dup"); - fprintf(stderr, "trying to replace fd=%d with %d dup used %d\n", was, willbe, dupres); - if (willbe == 1) { - fprintf(stderr, "likely that stdout should be opened before %d\n", was); - } - exit(1); - } - if (close(willbe) < 0) { - sys_fatal("close"); - } - } -} - -/* - * waitForChild - waits for child, pid, to exit. - */ - -static void waitForChild (PID_T pid) -{ - PID_T waitpd; - int status; - - waitpd = wait(&status); - if (waitpd != pid) - sys_fatal("wait"); -} - -/* - * do_html - sets the troff number htmlflip and - * writes out the buffer to troff -Thtml - */ - -int char_buffer::do_html(int argc, char *argv[]) -{ - int pdes[2]; - PID_T pid; - - if (pipe(pdes) < 0) - sys_fatal("pipe"); - - argv++; // skip pre-grohtml argv[0] - pid = fork(); - if (pid < 0) - sys_fatal("fork"); - - if (pid == 0) { - // child - replaceFd(0, pdes[0]); - // close end we are not using - if (close(pdes[1])<0) - sys_fatal("close"); - - execvp(argv[0], argv); - error("couldn't exec %1: %2", argv[0], strerror(errno), (char *)0); - fflush(stderr); /* just in case error() doesn't */ - exit(1); - } else { - // parent - - replaceFd(1, pdes[1]); - // close end we are not using - if (close(pdes[0])<0) - sys_fatal("close"); - - write_file(1); - waitForChild(pid); - } - return( 0 ); -} - -/* - * alterToDeviceImage - manipulates the argv to include IMAGEDEVICE rather than -Thtml2 - */ - -static void alterToDeviceImage (int argc, char *argv[]) -{ - int i=0; - - while (i < argc) { - if (strcmp(argv[i], "-Thtml2") == 0) { - argv[i] = IMAGEDEVICE; - } - i++; - } - argv[1] = "groff"; /* rather than troff */ -} - -/* - * do_image - sets the troff number htmlflip and - * writes out the buffer to troff -Tps - */ - -int char_buffer::do_image(int argc, char *argv[]) -{ - PID_T pid; - int pdes[2]; - - if (pipe(pdes) < 0) - sys_fatal("pipe"); - - alterToDeviceImage(argc, argv); - argv++; // skip pre-grohtml argv[0] - - pid = fork(); - if (pid == 0) { - // child - -#if defined(DEBUGGING) - int psFd = creat("/tmp/prehtml-ps", S_IWUSR|S_IRUSR); - int regionFd = creat("/tmp/prehtml-region", S_IWUSR|S_IRUSR); -#else - int psFd = mkstemp(xtmptemplate("-ps-")); - int regionFd = mkstemp(xtmptemplate("-regions-")); -#endif - - fprintf(stderr, "about to exec %s\n", argv[0]); - replaceFd(1, psFd); - replaceFd(0, pdes[0]); - replaceFd(2, regionFd); - - // close end we are not using - if (close(pdes[1])<0) - sys_fatal("close"); - - execvp(argv[0], argv); - error("couldn't exec %1: %2", argv[0], strerror(errno), (char *)0); - fflush(stderr); /* just in case error() doesn't */ - exit(1); - } else { - // parent - - replaceFd(1, pdes[1]); - write_file(0); - waitForChild(pid); - } - return( 0 ); -} - -static char_buffer inputFile; - - -/* - * usage - emit usage arguments and exit. - */ - -void usage() -{ - fprintf(stderr, "usage: %s troffname [ troff flags ] [ files ]\n", program_name); - exit(1); -} - -int main(int argc, char **argv) -{ - program_name = argv[0]; - int i; // skip over troff name - int found=0; - int ok=1; - - for (i = 2; i < argc; i++) { - if (argv[i][0] == '-') { - if (argv[i][1] == 'v') { - extern const char *Version_string; - fprintf(stderr, "GNU pre-grohtml version %s\n", Version_string); - fflush(stderr); - } - } else { - ok = do_file(argv[i]); - if (! ok) { - return( 0 ); - } - found = 1; - } - } - - if (! found) { - do_file("-"); - } - ok = inputFile.do_html(argc, argv); - if (ok == 0) { - ok = inputFile.do_image(argc, argv); - if (ok == 0) { - // generateImages(); - } - } - return ok; -} - -static int do_file(const char *filename) -{ - FILE *fp; - - current_filename = filename; - if (strcmp(filename, "-") == 0) { - fp = stdin; - } else { - fp = fopen(filename, "r"); - if (fp == 0) { - error("can't open `%1': %2", filename, strerror(errno)); - return 0; - } - } - - if (inputFile.read_file(fp)) { - } - - if (fp != stdin) - fclose(fp); - current_filename = 0; - return 1; -} diff --git a/src/preproc/html2/pre-html.cc b/src/preproc/html2/pre-html.cc deleted file mode 100755 index b282a8b7b..000000000 --- a/src/preproc/html2/pre-html.cc +++ /dev/null @@ -1,773 +0,0 @@ -// -*- C++ -*- -/* Copyright (C) 2000 Free Software Foundation, Inc. - Written by Gaius Mulley (gaius@glam.ac.uk). - -This file is part of groff. - -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. */ - -#define PREHTMLC - -#include <stdio.h> -#include <signal.h> -#include <ctype.h> -#include <string.h> -#include <assert.h> -#include <stdlib.h> -#include <errno.h> -#include "lib.h" -#include "errarg.h" -#include "error.h" -#include "stringclass.h" -#include "posix.h" - -#include <errno.h> -#include <sys/types.h> -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif - -#ifdef _POSIX_VERSION -#include <sys/wait.h> -#define PID_T pid_t -#else /* not _POSIX_VERSION */ -#define PID_T int -#endif /* not _POSIX_VERSION */ - -extern char *strerror(); - -#include "pre-html.h" -#include "pushbackbuffer.h" - -#define POSTSCRIPTRES 72000 // maybe there is a better way to find this? --fixme-- -#define DEFAULT_IMAGE_RES 80 -#define IMAGE_BOARDER_PIXELS 10 - -// #define TRANSPARENT "-background \"#FFF\" -transparent \"#FFF\"" -#define TRANSPARENT "" - -#define DEBUGGING - -#if !defined(TRUE) -# define TRUE (1==1) -#endif -#if !defined(FALSE) -# define FALSE (1==0) -#endif - -void stop() {} - - -static int stdoutfd =1; // output file descriptor - normally 1 but might move - // -1 means closed -static char *psFileName =0; // name of postscript file -static char *regionFileName=0; // name of file containing all image regions -static char *image_device = "pnmraw"; -static int image_res = DEFAULT_IMAGE_RES; - - -/* - * Images are generated via postscript, gs and the pnm utilities. - */ - -#define IMAGEDEVICE "-Tps" - - -static int do_file(const char *filename); - -/* - * sys_fatal - writes a fatal error message. Taken from src/roff/groff/pipeline.c - */ - -void sys_fatal (const char *s) -{ - fprintf(stderr, "%s: %s: %s", program_name, s, strerror(errno)); -} - -/* - * the class and methods for retaining ascii text - */ - -struct char_block { - enum { SIZE = 256 }; - char buffer[SIZE]; - int used; - char_block *next; - - char_block(); -}; - -char_block::char_block() -: used(0), next(0) -{ -} - -class char_buffer { -public: - char_buffer(); - ~char_buffer(); - int read_file(FILE *fp); - int do_html(int argc, char *argv[]); - int do_image(int argc, char *argv[]); - void write_file_html(void); - void write_file_troff(void); - void write_upto_newline (char_block **t, int *i); - int can_see(char_block **t, int *i, char *string); -private: - char_block *head; - char_block *tail; -}; - -char_buffer::char_buffer() -: head(0), tail(0) -{ -} - -char_buffer::~char_buffer() -{ - while (head != 0) { - char_block *temp = head; - head = head->next; - delete temp; - } -} - -int char_buffer::read_file (FILE *fp) -{ - int i=0; - unsigned int old_used; - int n; - - while (! feof(fp)) { - if (tail == 0) { - tail = new char_block; - head = tail; - } else { - if (tail->used == char_block::SIZE) { - tail->next = new char_block; - tail = tail->next; - } - } - // at this point we have a tail which is ready for the next SIZE bytes of the file - - n = fread(tail->buffer, sizeof(char), char_block::SIZE-tail->used, fp); - if (n <= 0) { - // error - return( 0 ); - } else { - tail->used += n*sizeof(char); - } - } - return( 1 ); -} - -/* - * writeNbytes - writes n bytes to stdout. - */ - -static void writeNbytes (char *s, int l) -{ - int n=0; - int r; - - while (n<l) { - r = write(stdoutfd, s, l-n); - if (r<0) { - sys_fatal("write"); - } - n += r; - s += r; - } -} - -/* - * writeString - writes a string to stdout. - */ - -static void writeString (char *s) -{ - writeNbytes(s, strlen(s)); -} - -/* - * write_upto_newline - writes the contents of the buffer until a newline is seen. - */ - -void char_buffer::write_upto_newline (char_block **t, int *i) -{ - int j=*i; - - if (*t) { - while ((j < (*t)->used) && ((*t)->buffer[j] != '\n')) { - j++; - } - if ((j < (*t)->used) && ((*t)->buffer[j] == '\n')) { - j++; - } - writeNbytes((*t)->buffer+(*i), j-(*i)); - if (j == (*t)->used) { - *i = 0; - *t = (*t)->next; - write_upto_newline(t, i); - } else { - // newline was seen - *i = j; - } - } -} - -/* - * can_see - returns TRUE if we can see string in t->buffer[i] onwards - */ - -int char_buffer::can_see(char_block **t, int *i, char *string) -{ - int j = 0; - int l = strlen(string); - int k = *i; - char_block *s = *t; - - while (s) { - while ((k<s->used) && (j<l) && (s->buffer[k] == string[j])) { - j++; - k++; - } - if (j == l) { - *i = k; - *t = s; - return( TRUE ); - } else if ((k<s->used) && (s->buffer[k] != string[j])) { - return( FALSE ); - } - s = s->next; - k = 0; - } - return( FALSE ); -} - -/* - * write_file_troff - writes the buffer to stdout (troff). - * It prepends the number register set to 0. - */ - -void char_buffer::write_file_troff (void) -{ - char_block *t=head; - int r; - - writeString(".nr html2enable 0\n"); - writeString(".nr htmlflip 0\n"); - if (t != 0) { - do { - writeNbytes(t->buffer, t->used); - t = t->next; - } while ((t != head) && (t != 0)); - } - if (close(stdoutfd) < 0) - sys_fatal("close"); - - // now we grab fd=1 so that the next pipe cannot use fd=1 - if (stdoutfd == 1) { - if (dup(2) != stdoutfd) { - sys_fatal("dup failed to use fd=1"); - } - } -} - -/* - * the image class remembers the position of all images in the postscript file - * and assigns names for each image. - */ - -struct imageItem { - imageItem *next; - int X1; - int Y1; - int X2; - int Y2; - char *imageName; - int resolution; - int pageNo; - - imageItem (int x1, int y1, int x2, int y2, int page, int res, char *name); - ~imageItem (); -}; - -/* - * imageItem - constructor - */ - -imageItem::imageItem (int x1, int y1, int x2, int y2, int page, int res, char *name) -{ - X1 = x1; - Y1 = y1; - X2 = x2; - Y2 = y2; - pageNo = page; - resolution = res; - imageName = name; - next = 0; -} - -/* - * imageItem - deconstructor - */ - -imageItem::~imageItem () -{ -} - -/* - * imageList - class containing a list of imageItems. - */ - -class imageList { -private: - imageItem *head; - imageItem *tail; - int count; -public: - imageList(); - ~imageList(); - void add(int x1, int y1, int x2, int y2, int page, int res); - char *get(int i); -}; - -/* - * imageList - constructor. - */ - -imageList::imageList () - : head(0), tail(0), count(0) -{ -} - -/* - * imageList - deconstructor. - */ - -imageList::~imageList () -{ - while (head != 0) { - imageItem *i = head; - head = head->next; - delete i; - } -} - -/* - * createImage - generates a png file from the information held in, i, and - * the postscript file. - */ - -static void createImage (imageItem *i) -{ - if (i->X1 != -1) { - char buffer[4096]; - - sprintf(buffer, - "echo showpage | gs -q -dFirstPage=%d -dLastPage=%d -dSAFER -sDEVICE=%s -r%d -sOutputFile=- %s - 2> /dev/null | pnmcut %d %d %d %d | pnmtopng %s > %s.png \n", - i->pageNo, i->pageNo, - image_device, - image_res, - psFileName, - i->X1*image_res/POSTSCRIPTRES-IMAGE_BOARDER_PIXELS, - i->Y1*image_res/POSTSCRIPTRES-IMAGE_BOARDER_PIXELS, - (i->X2-i->X1)*image_res/POSTSCRIPTRES+2*IMAGE_BOARDER_PIXELS, - (i->Y2-i->Y1)*image_res/POSTSCRIPTRES+2*IMAGE_BOARDER_PIXELS, - TRANSPARENT, - i->imageName); - // fprintf(stderr, buffer); - system(buffer); - } else { - fprintf(stderr, "ignoring image as x1 coord is -1\n"); - fflush(stderr); - } -} - -/* - * add - an image description to the imageList. - */ - -void imageList::add (int x1, int y1, int x2, int y2, int page, int res) -{ - char *name = (char *)malloc(50); - - if (name == 0) - sys_fatal("malloc"); - - if (x1 == -1) { - name[0] = (char)0; - } else { - count++; - sprintf(name, "grohtml-%d", count); - } - imageItem *i = new imageItem(x1, y1, x2, y2, page, res, name); - - if (head == 0) { - head = i; - tail = i; - } else { - tail->next = i; - tail = i; - } - createImage(i); -} - -/* - * get - returns the name for image number, i. - */ - -char *imageList::get(int i) -{ - imageItem *t=head; - - while (i>0) { - if (i == 1) { - if (t->X1 == -1) { - return( NULL ); - } else { - return( t->imageName ); - } - } - t = t->next; - i--; - } -} - -static imageList listOfImages; // list of images defined by the region file. - -/* - * write_file_html - writes the buffer to stdout (troff). - * It prepends the number register set to 1 and writes - * out the file replacing template image names with - * actual image names. - */ - -void char_buffer::write_file_html (void) -{ - char_block *t =head; - int imageNo=0; - char *name; - int i=0; - - writeString(".nr html2enable 1\n"); - writeString(".nr htmlflip 1\n"); - if (t != 0) { - stop(); - do { - if (can_see(&t, &i, ".if '\\*(.T'html2' .IMAGE <pre-html-image>\n")) { - imageNo++; - name = listOfImages.get(imageNo); - if (name != 0) { - writeString(".if '\\*(.T'html2' .IMAGE \""); - writeString(name); - writeString(".png\"\n"); - } - } else { - write_upto_newline(&t, &i); - } - } while (t != 0); - } - if (close(stdoutfd) < 0) - sys_fatal("close"); - - // now we grab fd=1 so that the next pipe cannot use fd=1 - if (stdoutfd == 1) { - if (dup(2) != stdoutfd) { - sys_fatal("dup failed to use fd=1"); - } - } -} - -/* - * generateImages - parses the region file and generates images - * from the postscript file. The region file - * contains the x1,y1 x2,y2 extents of each - * image. - */ - -static void generateImages (char *regionFileName) -{ - pushBackBuffer *f=new pushBackBuffer(regionFileName); - char ch; - - if (f->putPB('\n') == '\n') { - } - while (f->putPB(f->getPB()) != eof) { - if (f->isString("\ngrohtml-info:page")) { - int page= f->readInt(); - int x1 = f->readInt(); - int y1 = f->readInt(); - int x2 = f->readInt(); - int y2 = f->readInt(); - int res = POSTSCRIPTRES; // --fixme-- prefer (f->readInt()) providing that troff can discover the value - listOfImages.add(x1, y1, x2, y2, page, res); - } - ch = f->getPB(); - } -} - -/* - * replaceFd - replace a file descriptor, was, with, willbe. - */ - -static void replaceFd (int was, int willbe) -{ - int dupres; - - if (was != willbe) { - if (close(was)<0) { - sys_fatal("close"); - } - dupres = dup(willbe); - if (dupres != was) { - sys_fatal("dup"); - fprintf(stderr, "trying to replace fd=%d with %d dup used %d\n", was, willbe, dupres); - if (willbe == 1) { - fprintf(stderr, "likely that stdout should be opened before %d\n", was); - } - exit(1); - } - if (close(willbe) < 0) { - sys_fatal("close"); - } - } -} - -/* - * waitForChild - waits for child, pid, to exit. - */ - -static void waitForChild (PID_T pid) -{ - PID_T waitpd; - int status; - - waitpd = wait(&status); - if (waitpd != pid) - sys_fatal("wait"); -} - -/* - * alterDeviceTo - if toImage is set then the arg list is altered to include - * IMAGEDEVICE and we invoke groff rather than troff. - * else - * set -Thtml2 and troff - */ - -static void alterDeviceTo (int argc, char *argv[], int toImage) -{ - int i=0; - - if (toImage) { - while (i < argc) { - if (strcmp(argv[i], "-Thtml2") == 0) { - argv[i] = IMAGEDEVICE; - } - i++; - } - argv[1] = "groff"; /* rather than troff */ - } else { - while (i < argc) { - if (strcmp(argv[i], IMAGEDEVICE) == 0) { - argv[i] = "-Thtml2"; - } - i++; - } - argv[1] = "troff"; /* use troff */ - } -} - -/* - * do_html - sets the troff number htmlflip and - * writes out the buffer to troff -Thtml - */ - -int char_buffer::do_html(int argc, char *argv[]) -{ - int pdes[2]; - PID_T pid; - - if (pipe(pdes) < 0) - sys_fatal("pipe"); - - alterDeviceTo(argc, argv, 0); - argv++; // skip pre-grohtml argv[0] - pid = fork(); - if (pid < 0) - sys_fatal("fork"); - - if (pid == 0) { - // child - replaceFd(0, pdes[0]); - // close end we are not using - if (close(pdes[1])<0) - sys_fatal("close"); - - execvp(argv[0], argv); - error("couldn't exec %1: %2", argv[0], strerror(errno), (char *)0); - fflush(stderr); /* just in case error() doesn't */ - exit(1); - } else { - // parent - - replaceFd(1, pdes[1]); - // close end we are not using - if (close(pdes[0])<0) - sys_fatal("close"); - - write_file_html(); - waitForChild(pid); - } - return( 0 ); -} - -/* - * do_image - sets the troff number htmlflip and - * writes out the buffer to troff -Tps - */ - -int char_buffer::do_image(int argc, char *argv[]) -{ - PID_T pid; - int pdes[2]; - - if (pipe(pdes) < 0) - sys_fatal("pipe"); - - alterDeviceTo(argc, argv, 1); - argv++; // skip pre-grohtml argv[0] - - pid = fork(); - if (pid == 0) { - // child - -#if defined(DEBUGGING) - int psFd = creat(psFileName, S_IWUSR|S_IRUSR); - int regionFd = creat(regionFileName, S_IWUSR|S_IRUSR); -#else - int psFd = mkstemp(psFileName); - int regionFd = mkstemp(regionFileName); -#endif - - replaceFd(1, psFd); - replaceFd(0, pdes[0]); - replaceFd(2, regionFd); - - // close end we are not using - if (close(pdes[1])<0) - sys_fatal("close"); - - execvp(argv[0], argv); - error("couldn't exec %1: %2", argv[0], strerror(errno), (char *)0); - fflush(stderr); /* just in case error() doesn't */ - exit(1); - } else { - // parent - - replaceFd(1, pdes[1]); - write_file_troff(); - waitForChild(pid); - } - return( 0 ); -} - -static char_buffer inputFile; - - -/* - * usage - emit usage arguments and exit. - */ - -void usage() -{ - fprintf(stderr, "usage: %s troffname [ troff flags ] [ files ]\n", program_name); - exit(1); -} - -/* - * makeTempFiles - name the temporary files - */ - -static void makeTempFiles (void) -{ -#if defined(DEBUGGING) - psFileName = "/tmp/prehtml-ps"; - regionFileName = "/tmp/prehtml-region"; -#else - psFileName = xtmptemplate("-ps-"); - regionFileName = xtmptemplate("-regions-"); -#endif -} - -int main(int argc, char **argv) -{ - program_name = argv[0]; - int i; // skip over troff name - int found=0; - int ok=1; - - for (i = 2; i < argc; i++) { - if (argv[i][0] == '-') { - if (argv[i][1] == 'v') { - extern const char *Version_string; - printf("GNU pre-grohtml (groff) version %s\n", Version_string); - exit(0); - } - } else { - ok = do_file(argv[i]); - if (! ok) { - return( 0 ); - } - found = 1; - } - } - - if (! found) { - do_file("-"); - } - makeTempFiles(); - ok = inputFile.do_image(argc, argv); - if (ok == 0) { - generateImages(regionFileName); - ok = inputFile.do_html(argc, argv); - } - return ok; -} - -static int do_file(const char *filename) -{ - FILE *fp; - - current_filename = filename; - if (strcmp(filename, "-") == 0) { - fp = stdin; - } else { - fp = fopen(filename, "r"); - if (fp == 0) { - error("can't open `%1': %2", filename, strerror(errno)); - return 0; - } - } - - if (inputFile.read_file(fp)) { - } - - if (fp != stdin) - fclose(fp); - current_filename = 0; - return 1; -} diff --git a/src/preproc/html2/pushbackbuffer.cc b/src/preproc/html2/pushbackbuffer.cc deleted file mode 100755 index edd31c979..000000000 --- a/src/preproc/html2/pushbackbuffer.cc +++ /dev/null @@ -1,308 +0,0 @@ -// -*- C++ -*- -/* Copyright (C) 2000 Free Software Foundation, Inc. - Written by Gaius Mulley (gaius@glam.ac.uk). - -This file is part of groff. - -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. */ - -#include <stdio.h> -#include <signal.h> -#include <ctype.h> -#include <string.h> -#include <assert.h> -#include <stdlib.h> -#include <errno.h> -#include "lib.h" -#include "errarg.h" -#include "error.h" -#include "stringclass.h" -#include "posix.h" - -#include <errno.h> -#include <sys/types.h> -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif - -#include "pushbackbuffer.h" -#include "pre-html.h" - -#if !defined(TRUE) -# define TRUE (1==1) -#endif - -#if !defined(FALSE) -# define FALSE (1==0) -#endif - -# define ERROR(X) (fprintf(stderr, "%s:%d error %s\n", __FILE__, __LINE__, X) && \ - (fflush(stderr)) && localexit(1)) - - -#define MAXPUSHBACKSTACK 4096 /* maximum number of character that can be pushed back */ - - -/* - * constructor for pushBackBuffer - */ - -pushBackBuffer::pushBackBuffer (char *filename) -{ - charStack = (char *)malloc(MAXPUSHBACKSTACK); - if (charStack == 0) { - sys_fatal("malloc"); - } - stackPtr = 0; /* index to push back stack */ - debug = 0; - verbose = 0; - eofFound = FALSE; - lineNo = 1; - if (strcmp(filename, "") != 0) { - stdIn = dup(0); - close(0); - if (open(filename, O_RDONLY) != 0) { - sys_fatal("when tring to read graph file"); - } else { - fileName = filename; - } - } -} - -pushBackBuffer::~pushBackBuffer () -{ - int old; - - if (charStack != 0) { - free(charStack); - } - close(0); - /* restore stdin in file descriptor 0 */ - old = dup(stdIn); - close(stdIn); -} - -/* - * localexit - wraps exit with a return code to aid the ERROR macro. - */ - -int localexit (int i) -{ - exit(i); - return( 1 ); -} - -/* - * getPB - returns a character, possibly a pushed back character. - */ - -char pushBackBuffer::getPB (void) -{ - if (stackPtr>0) { - stackPtr--; - return( charStack[stackPtr] ); - } else { - char ch; - - if (read(0, &ch, 1) == 1) { - if (verbose) { - printf("%c", ch); - } - if (ch == '\n') { - lineNo++; - } - return( ch ); - } else { - eofFound = TRUE; - return( eof ); - } - } -} - -/* - * putPB - pushes a character onto the push back stack. - * The same character is returned. - */ - -char pushBackBuffer::putPB (char ch) -{ - if (stackPtr<MAXPUSHBACKSTACK) { - charStack[stackPtr] = ch ; - stackPtr++; - } else { - ERROR("max push back stack exceeded, increase MAXPUSHBACKSTACK constant"); - } - return( ch ); -} - -/* - * isWhite - returns TRUE if a white character is found. This character is NOT consumed. - */ - -static int isWhite (char ch) -{ - return( (ch==' ') || (ch == '\t') || (ch == '\n') ); -} - -/* - * skipToNewline - skips characters until a newline is seen. - */ - -void pushBackBuffer::skipToNewline (void) -{ - char ch; - - while ((putPB(getPB()) != '\n') && (! eofFound)) { - ch = getPB(); - } -} - -/* - * skipUntilToken - skips until a token is seen - */ - -void pushBackBuffer::skipUntilToken (void) -{ - char ch; - - while ((isWhite(putPB(getPB())) || (putPB(getPB()) == '#')) && (! eofFound)) { - ch = getPB(); - if (ch == '#') { - skipToNewline(); - } - } -} - -/* - * isString - returns TRUE if the string, s, matches the pushed back string. - * if TRUE is returned then this string is consumed, otherwise it is - * left alone. - */ - -int pushBackBuffer::isString (char *s) -{ - int length=strlen(s); - int i=0; - int j; - - while ((i<length) && (putPB(getPB())==s[i])) { - if (getPB() != s[i]) { - ERROR("assert failed"); - } - i++; - } - if (i==length) { - return( TRUE ); - } else { - i--; - while (i>=0) { - if (putPB(s[i]) != s[i]) { - ERROR("assert failed"); - } - i--; - } - } - return( FALSE ); -} - -/* - * isDigit - returns TRUE if the character, ch, is a digit. - */ - -static int isDigit (char ch) -{ - return( ((ch>='0') && (ch<='9')) ); -} - -/* - * isHexDigit - returns TRUE if the character, ch, is a hex digit. - */ - -static int isHexDigit (char ch) -{ - return( (isDigit(ch)) || ((ch>='a') && (ch<='f')) ); -} - -/* - * readInt - returns an integer from the input stream. - */ - -int pushBackBuffer::readInt (void) -{ - int c =0; - int i =0; - int s =1; - char ch=getPB(); - - while (isWhite(ch)) { - ch=getPB(); - } - // now read integer - - if (ch == '-') { - s = -1; - ch = getPB(); - } - while (isDigit(ch)) { - i *= 10; - if ((ch>='0') && (ch<='9')) { - i += (int)(ch-'0'); - } - ch = getPB(); - c++; - } - if (ch != putPB(ch)) { - ERROR("assert failed"); - } - return( i*s ); -} - -/* - * convertToFloat - converts integers, a and b into a.b - */ - -static float convertToFloat (int a, int b) -{ - int c=10; - float f; - - while (b>c) { - c *= 10; - } - f = ((float)a) + (((float)b)/((float)c)); - return( f ); -} - -/* - * readNumber - returns a float representing the word just read. - */ - -float pushBackBuffer::readNumber (void) -{ - int integer; - int fraction; - char ch; - float f; - - integer = readInt(); - if (putPB(getPB()) == '.') { - ch = getPB(); - fraction = readInt(); - f = convertToFloat(integer, fraction); - return( f ); - } else { - return( (float)integer ); - } -} diff --git a/src/preproc/html2/pushbackbuffer.h b/src/preproc/html2/pushbackbuffer.h deleted file mode 100755 index 8c3ff9b7d..000000000 --- a/src/preproc/html2/pushbackbuffer.h +++ /dev/null @@ -1,53 +0,0 @@ -// -*- C -*- -/* Copyright (C) 2000 Free Software Foundation, Inc. - Written by Gaius Mulley (gaius@glam.ac.uk). - -This file is part of groff. - -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. */ - - -#define eof (char)-1 - - -/* - * defines the class and methods implemented within pbbuffer.cc - */ - -class pushBackBuffer -{ - private: - char *charStack; - int stackPtr; /* index to push back stack */ - int debug; - int verbose; - int eofFound; - char *fileName; - int lineNo; - int stdIn; - - public: - pushBackBuffer (char *); - ~ pushBackBuffer (); - char getPB (void); - char putPB (char ch); - void skipUntilToken (void); - void skipToNewline (void); - float readNumber (void); - int readInt (void); - int isString (char *string); -}; - - diff --git a/src/preproc/pic/troff.cc b/src/preproc/pic/troff.cc index 2f0496bf1..62fe540ae 100644 --- a/src/preproc/pic/troff.cc +++ b/src/preproc/pic/troff.cc @@ -1,5 +1,6 @@ // -*- C++ -*- -/* Copyright (C) 1989, 1990, 1991, 1992, 2000 Free Software Foundation, Inc. +/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001 + Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) This file is part of groff. @@ -268,12 +269,12 @@ void troff_output::start_picture(double sc, scale = compute_scale(sc, ll, ur); height = (ur.y - ll.y)/scale; double width = (ur.x - ll.x)/scale; + graphic_start(0); printf(".PS %.3fi %.3fi", height, width); if (args) printf(" %s\n", args); else putchar('\n'); - graphic_start(); printf(".\\\" %g %g %g %g\n", ll.x, ll.y, ur.x, ur.y); printf(".\\\" %.3fi %.3fi %.3fi %.3fi\n", 0.0, height, width, 0.0); printf(".nr " FILL_REG " \\n(.u\n.nf\n"); @@ -292,10 +293,10 @@ void troff_output::finish_picture() printf(".if \\n(" FILL_REG " .fi\n"); printf(".br\n"); printf(".nr " EQN_NO_EXTRA_SPACE_REG " 0\n"); - graphic_end(); // this is a little gross set_location(current_filename, current_lineno); fputs(flyback_flag ? ".PF\n" : ".PE\n", stdout); + graphic_end(); } void troff_output::command(const char *s, diff --git a/src/preproc/tbl/main.cc b/src/preproc/tbl/main.cc index 53ce75774..d13f026c1 100644 --- a/src/preproc/tbl/main.cc +++ b/src/preproc/tbl/main.cc @@ -1,5 +1,6 @@ // -*- C++ -*- -/* Copyright (C) 1989, 1990, 1991, 1992, 2000 Free Software Foundation, Inc. +/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001 + Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) This file is part of groff. @@ -218,6 +219,8 @@ void process_input_file(FILE *fp) break; case HAD_TS: if (c == ' ' || c == '\n' || compatible_flag) { + printf(".if '\\*(.T'html' \\X(table-start(\n"); + html_begin_suppress(0); putchar('.'); putchar('T'); putchar('S'); @@ -230,25 +233,25 @@ void process_input_file(FILE *fp) c = getc(fp); } putchar('\n'); - printf(".if '\\*(.T'html' \\X(table-start(\n"); - html_begin_suppress(); current_lineno++; { table_input input(fp); process_table(input); set_troff_location(current_filename, current_lineno); if (input.ended()) { - printf(".if '\\*(.T'html' \\X(table-end(\n"); - html_end_suppress(); fputs(".TE", stdout); while ((c = getc(fp)) != '\n') { if (c == EOF) { + printf(".if '\\*(.T'html' \\X(table-end(\n"); + html_end_suppress(); putchar('\n'); return; } putchar(c); } putchar('\n'); + printf(".if '\\*(.T'html' \\X(table-end(\n"); + html_end_suppress(); current_lineno++; } } diff --git a/src/roff/groff/groff.cc b/src/roff/groff/groff.cc index 0dd67b68c..ff0b9d678 100644 --- a/src/roff/groff/groff.cc +++ b/src/roff/groff/groff.cc @@ -1,5 +1,5 @@ // -*- C++ -*- -/* Copyright (C) 1989-2000 Free Software Foundation, Inc. +/* Copyright (C) 1989-2000, 2001 Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) This file is part of groff. @@ -208,7 +208,7 @@ int main(int argc, char **argv) safer_flag = 0; break; case 'T': - if (strcmp(optarg, "html2") == 0) { + if (strcmp(optarg, "html") == 0) { // force soelim to aid the html preprocessor commands[SOELIM_INDEX].set_name(command_prefix, "soelim"); } @@ -275,6 +275,13 @@ int main(int argc, char **argv) if (predriver) { commands[TROFF_INDEX].insert_arg(commands[TROFF_INDEX].get_name()); + const char *p = Pargs.contents(); + const char *end = p + Pargs.length(); + while (p < end) { + // pass the device arguments to the predrivers as well + commands[TROFF_INDEX].insert_arg(p); + p = strchr(p, '\0') + 1; + } commands[TROFF_INDEX].set_name(predriver); } @@ -332,7 +339,12 @@ int main(int argc, char **argv) commands[SPOOL_INDEX].set_name(0); } commands[TROFF_INDEX].append_arg("-T", device); - commands[EQN_INDEX].append_arg("-T", device); + // html renders equations as images via ps + if (strcmp(device, "html") == 0) + commands[EQN_INDEX].append_arg("-Tps"); + else + commands[EQN_INDEX].append_arg("-T", device); + commands[GRN_INDEX].append_arg("-T", device); int first_index; diff --git a/src/roff/troff/div.cc b/src/roff/troff/div.cc index 6de99c455..46661941e 100644 --- a/src/roff/troff/div.cc +++ b/src/roff/troff/div.cc @@ -753,6 +753,7 @@ void space_request() else // The line might have had line spacing that was truncated. truncated_space += n; + curenv->add_html_tag(".sp", n.to_units()); tok.next(); } @@ -819,6 +820,7 @@ void flush_output() curenv->do_break(); if (the_output) the_output->flush(); + curenv->add_html_tag(".fl"); tok.next(); } diff --git a/src/roff/troff/env.cc b/src/roff/troff/env.cc index 6ae7f22ae..eee3f1083 100644 --- a/src/roff/troff/env.cc +++ b/src/roff/troff/env.cc @@ -577,6 +577,8 @@ environment::environment(symbol nm) #ifdef WIDOW_CONTROL widow_control(0), #endif /* WIDOW_CONTROL */ + need_eol(0), + ignore_next_eol(0), name(nm), control_char('.'), no_break_control_char('\''), @@ -658,6 +660,8 @@ environment::environment(const environment *e) #ifdef WIDOW_CONTROL widow_control(e->widow_control), #endif /* WIDOW_CONTROL */ + need_eol(0), + ignore_next_eol(0), name(e->name), // so that eg `.if "\n[.ev]"0"' works control_char(e->control_char), no_break_control_char(e->no_break_control_char), @@ -1079,6 +1083,7 @@ void point_size() if (n <= 0) n = 1; curenv->set_size(n); + curenv->add_html_tag(".ps", n); } else curenv->set_size(0); @@ -1105,6 +1110,7 @@ void fill() if (break_flag) curenv->do_break(); curenv->fill = 1; + curenv->add_html_tag(".fi"); tok.next(); } @@ -1112,9 +1118,14 @@ void no_fill() { while (!tok.newline() && !tok.eof()) tok.next(); - if (break_flag) + if (break_flag) { curenv->do_break(); + curenv->add_html_tag(".br"); + } curenv->fill = 0; + curenv->add_html_tag(".nf"); + curenv->ignore_next_eol = 1; + curenv->add_html_tag(".po", topdiv->get_page_offset().to_units()); tok.next(); } @@ -1131,6 +1142,7 @@ void center() curenv->do_break(); curenv->right_justify_lines = 0; curenv->center_lines = n; + curenv->add_html_tag(".ce", n); tok.next(); } @@ -1147,6 +1159,7 @@ void right_justify() curenv->do_break(); curenv->center_lines = 0; curenv->right_justify_lines = n; + curenv->add_html_tag(".rj", n); tok.next(); } @@ -1163,6 +1176,7 @@ void line_length() temp = curenv->prev_line_length; curenv->prev_line_length = curenv->line_length; curenv->line_length = temp; + curenv->add_html_tag(".ll", temp.to_units()); skip_line(); } @@ -1249,6 +1263,7 @@ void indent() curenv->have_temporary_indent = 0; curenv->prev_indent = curenv->indent; curenv->indent = temp; + curenv->add_html_tag(".in", temp.to_units()); tok.next(); } @@ -1269,6 +1284,7 @@ void temporary_indent() if (!err) { curenv->temporary_indent = temp; curenv->have_temporary_indent = 1; + curenv->add_html_tag(".ti", temp.to_units()); } tok.next(); } @@ -1940,15 +1956,121 @@ void environment::final_break() do_break(); } -void environment::add_html_tag (const char *name) +/* + * add_html_tag_eol - add an end of line tag if appropriate. + */ + +void environment::add_html_tag_eol(void) { - if (is_html2) { - // need to emit tag for post-grohtml + if (is_html) { + if (ignore_next_eol > 0) + ignore_next_eol--; + else if (need_eol > 0) { + need_eol--; + add_html_tag("eol"); + } + else if (!fill) { + add_html_tag("eol"); + } + } +} + +/* + * add_html_tag - emits a special html-tag: to help post-grohtml understand + * the key troff commands + */ + +void environment::add_html_tag(const char *name) +{ + if (is_html) { + /* + * need to emit tag for post-grohtml + * but we check to see whether we can emit specials + */ + if (curdiv == topdiv && topdiv->before_first_page) + topdiv->begin_page(); macro *m = new macro; + m->append_str("html-tag:"); + for (const char *p = name; *p; p++) + if (!illegal_input_char((unsigned char)*p)) + m->append(*p); + add_node(new special_node(*m)); + } +} + +/* + * add_html_tag - emits a special html-tag: to help post-grohtml understand + * the key troff commands, it appends a string representation + * of i. + */ +void environment::add_html_tag(const char *name, int i) +{ + if (is_html) { + if (strcmp(name, ".ce") == 0) { + if (i == 0) + need_eol = 0; + else { + need_eol = i; + ignore_next_eol = 1; // since the .ce creates an eol + } + } + /* + * need to emit tag for post-grohtml + * but we check to see whether we can emit specials + */ + if (curdiv == topdiv && topdiv->before_first_page) + topdiv->begin_page(); + macro *m = new macro; + m->append_str("html-tag:"); for (const char *p = name; *p; p++) if (!illegal_input_char((unsigned char)*p)) m->append(*p); + m->append(' '); + m->append_int(i); + // output_pending_lines(); + output(new special_node(*m), !fill, 0, 0, 0); + // output_pending_lines(); + } +} + +/* + * add_html_tag_tabs - emits the tab settings for post-grohtml + */ + +void environment::add_html_tag_tabs(void) +{ + if (is_html) { + /* + * need to emit tag for post-grohtml + * but we check to see whether we can emit specials + */ + if (curdiv == topdiv && topdiv->before_first_page) + topdiv->begin_page(); + macro *m = new macro; + hunits d, l; + enum tab_type t; + m->append_str("html-tag:.ta "); + do { + t = curenv->tabs.distance_to_next_tab(l, &d); + l += d; + switch (t) { + case TAB_LEFT: + m->append_str(" L "); + m->append_int(d.to_units()); + break; + case TAB_CENTER: + m->append_str(" C "); + m->append_int(d.to_units()); + break; + case TAB_RIGHT: + m->append_str(" R "); + m->append_int(d.to_units()); + break; + case TAB_NONE: + break; + } + } while ((t != TAB_NONE) && (l<get_line_length())) ; output_pending_lines(); output(new special_node(*m), !fill, 0, 0, 0); output_pending_lines(); @@ -1958,7 +2080,6 @@ void environment::add_html_tag (const char *name) void environment::do_break() { if (curdiv == topdiv && topdiv->before_first_page) { - add_html_tag("html-tag:eol"); topdiv->begin_page(); return; } @@ -1989,7 +2110,6 @@ void environment::do_break() break; } } - add_html_tag("html-tag:eol"); node *tem = line; line = 0; output_line(tem, width_total); @@ -2011,8 +2131,10 @@ void break_request() { while (!tok.newline() && !tok.eof()) tok.next(); - if (break_flag) + if (break_flag) { curenv->do_break(); + curenv->add_html_tag(".br"); + } tok.next(); } @@ -2383,6 +2505,7 @@ void set_tabs() } } curenv->tabs = tabs; + curenv->add_html_tag_tabs(); skip_line(); } @@ -2494,10 +2617,12 @@ void environment::handle_tab(int is_leader) case TAB_NONE: return; case TAB_LEFT: + add_html_tag("tab left"); add_node(make_tab_node(d)); return; case TAB_RIGHT: case TAB_CENTER: + add_html_tag("tab center"); tab_width = 0; tab_distance = d; tab_contents = 0; diff --git a/src/roff/troff/env.h b/src/roff/troff/env.h index a5bee15df..521bccf71 100644 --- a/src/roff/troff/env.h +++ b/src/roff/troff/env.h @@ -179,6 +179,8 @@ class environment { #ifdef WIDOW_CONTROL int widow_control; #endif /* WIDOW_CONTROL */ + int need_eol; + int ignore_next_eol; tab_type distance_to_next_tab(hunits *); void start_line(); @@ -271,7 +273,10 @@ public: void possibly_break_line(int forced = 0); void do_break(); // .br void final_break(); - void add_html_tag (const char *name); + void add_html_tag_eol(void); + void add_html_tag(const char *); + void add_html_tag(const char *, int); + void add_html_tag_tabs(void); void newline(); void handle_tab(int is_leader = 0); // do a tab or leader void add_node(node *); diff --git a/src/roff/troff/input.cc b/src/roff/troff/input.cc index e79f17d6b..aeca49c12 100644 --- a/src/roff/troff/input.cc +++ b/src/roff/troff/input.cc @@ -110,7 +110,8 @@ static symbol blank_line_macro_name; int compatible_flag = 0; int ascii_output_flag = 0; int suppress_output_flag = 0; -int is_html2 = 0; +int is_html = 0; +int html_level = 0; // number of nested .html-begin requests int tcommand_flag = 0; int safer_flag = 1; // safer by default @@ -310,8 +311,10 @@ int file_iterator::next_file(FILE *f, const char *s) int file_iterator::fill(node **) { - if (newline_flag) + if (newline_flag) { + curenv->add_html_tag_eol(); lineno++; + } newline_flag = 0; unsigned char *p = buf; ptr = p; @@ -912,6 +915,7 @@ public: int interpret(macro *); int same(node *); const char *type(); + int force_tprint(); }; int non_interpreted_char_node::same(node *nd) @@ -924,6 +928,11 @@ const char *non_interpreted_char_node::type() return "non_interpreted_char_node"; } +int non_interpreted_char_node::force_tprint() +{ + return 0; +} + non_interpreted_char_node::non_interpreted_char_node(unsigned char n) : c(n) { assert(n != 0); @@ -943,6 +952,7 @@ int non_interpreted_char_node::interpret(macro *mac) static void do_width(); static node *do_non_interpreted(); static node *do_special(); +static node *do_suppress(); static void do_register(); static node *do_overstrike() @@ -1129,6 +1139,7 @@ public: token_node *get_token_node(); int same(node *); const char *type(); + int force_tprint(); }; token_node::token_node(const token &t) : tk(t) @@ -1155,6 +1166,11 @@ const char *token_node::type() return "token_node"; } +int token_node::force_tprint() +{ + return 0; +} + token::token() : nd(0), type(TOKEN_EMPTY) { } @@ -1552,6 +1568,12 @@ void token::next() nd = do_overstrike(); type = TOKEN_NODE; return; + case 'O': + nd = do_suppress(); + if (!nd) + break; + type = TOKEN_NODE; + return; case 'p': type = TOKEN_SPREAD; return; @@ -2051,7 +2073,6 @@ int_stack::~int_stack() top = top->next; delete temp; } - } int int_stack::is_empty() @@ -2508,6 +2529,18 @@ void macro::append(unsigned char c) ++length; } +void macro::append_str(const char *s) +{ + int i = 0; + + if (s) { + while (s[i] != (char)0) { + append(s[i]); + i++; + } + } +} + void macro::append(node *n) { assert(n != 0); @@ -2524,6 +2557,23 @@ void macro::append(node *n) ++length; } +void macro::append_unsigned(unsigned int i) +{ + unsigned int j = i / 10; + if (j != 0) + append_unsigned(j); + append(((unsigned char)(((int)'0') + i % 10))); +} + +void macro::append_int(int i) +{ + if (i < 0) { + append('-'); + i = -i; + } + append_unsigned((unsigned int)i); +} + void macro::print_size() { errprint("%1", length); @@ -3991,6 +4041,7 @@ public: node *copy(); int same(node *); const char *type(); + int force_tprint(); }; non_interpreted_node::non_interpreted_node(const macro &m) : mac(m) @@ -4007,6 +4058,11 @@ const char *non_interpreted_node::type() return "non_interpreted_node"; } +int non_interpreted_node::force_tprint() +{ + return 0; +} + node *non_interpreted_node::copy() { return new non_interpreted_node(mac); @@ -4111,6 +4167,22 @@ node *do_special() return new special_node(mac); } +node *do_suppress() +{ + tok.next(); + int c = tok.ch(); + + if (c == '0') + return new suppress_node(0, 0); + else if (c == '1') + return new suppress_node(1, 0); + else if (c == '2') + return new suppress_node(1, 1); + else + error("invalid argument to \\O"); + return 0; +} + void special_node::tprint(troff_output_file *out) { tprint_start(out); @@ -4342,6 +4414,67 @@ void else_request() } } +/* + * html_begin - if this is the outermost html_begin request then execute the + * rest of the line, else skip line + */ + +void html_begin() +{ + html_level++; + if (html_level == 1) + begin_alternative(); + else + skip_alternative(); +} + +/* + * html_end - if this is the outermost html_end request then execute the + * rest of the line, else skip line + */ + +void html_end() +{ + html_level--; + if (html_level == 0) + begin_alternative(); + else + skip_alternative(); + if (html_level < 0) + html_level = 0; +} + +/* + * html_image - implements the directive `.html_image {l|r|c|i} filename' + * which places the filename into a node which is later + * written out + * + * . either as a special in the form of an image tag for -Thtml + * . or as an image region definition for all other devices + * + */ + +void html_image() +{ + if (has_arg()) { + char position = tok.ch(); + if (!(position == 'l' + || position == 'r' + || position == 'c' + || position == 'i')) { + error("l, r, c, or i expected (got %1)", tok.description()); + position = 'c'; + } + else { + tok.next(); + symbol filename = get_long_name(1); + if (!filename.is_null()) + curenv->add_node(new suppress_node(filename, position)); + } + } + skip_line(); +} + static int while_depth = 0; static int while_break_flag = 0; @@ -5832,7 +5965,7 @@ int main(int argc, char **argv) case 'T': device = optarg; tflag = 1; - is_html2 = (strcmp(device, "html2") == 0); + is_html = (strcmp(device, "html") == 0); break; case 'C': compatible_flag = 1; @@ -5946,7 +6079,7 @@ int main(int argc, char **argv) init_column_requests(); #endif /* COLUMN */ init_node_requests(); - init_output_requests(); + init_html_requests(); number_reg_dictionary.define(".T", new constant_reg(tflag ? "1" : "0")); init_registers(); init_reg_requests(); @@ -6020,69 +6153,51 @@ static void init_registers() } /* - * .output request and associated registers + * registers associated with \O */ static int output_reg_minx_contents = -1; static int output_reg_miny_contents = -1; static int output_reg_maxx_contents = -1; static int output_reg_maxy_contents = -1; +static int output_low_mark_miny = -1; // internal only (limits miny) -void check_output_limits (int x, int y) +void check_output_limits(int x, int y) { - if ((output_reg_minx_contents == -1) || (x < output_reg_minx_contents)) { + if ((output_reg_minx_contents == -1) || (x < output_reg_minx_contents)) output_reg_minx_contents = x; - } - if (x > output_reg_maxx_contents) { + if (x > output_reg_maxx_contents) output_reg_maxx_contents = x; - } - if ((output_reg_miny_contents == -1) || (y < output_reg_miny_contents)) { + if (((output_reg_miny_contents == -1) || (y < output_reg_miny_contents)) + && (y >= output_low_mark_miny)) output_reg_miny_contents = y; - } - if (y > output_reg_maxy_contents) { + if (y > output_reg_maxy_contents) output_reg_maxy_contents = y; - } - // fprintf(stderr, "x = %d y=%d miny=%d maxy=%d\n", x, y, output_reg_miny_contents, output_reg_maxy_contents); } -void reset_output_registers() +void reset_output_registers(int miny) { // fprintf(stderr, "reset_output_registers\n"); + output_low_mark_miny = miny; output_reg_minx_contents = -1; output_reg_miny_contents = -1; output_reg_maxx_contents = -1; output_reg_maxy_contents = -1; } -void output_request() +void get_output_registers(int *minx, int *miny, int *maxx, int *maxy) { - if (has_arg()) { - int n; - - if (! get_integer(&n)) { - error("missing integer argument for output request"); - n = 1; - } - - if (break_flag) - curenv->do_break(); - - if (!the_output) - init_output(); - if (n == 0) { - the_output->off(); - } else { - the_output->on(); - } - } else { - error("missing argument for output request"); - } - skip_line(); + *minx = output_reg_minx_contents; + *miny = output_reg_miny_contents; + *maxx = output_reg_maxx_contents; + *maxy = output_reg_maxy_contents; } -void init_output_requests() +void init_html_requests() { - init_request("output", output_request); + init_request("html-begin", html_begin); + init_request("html-end", html_end); + init_request("html-image", html_image); } void init_input_requests() diff --git a/src/roff/troff/node.cc b/src/roff/troff/node.cc index 44ccda7bb..3770aab9f 100644 --- a/src/roff/troff/node.cc +++ b/src/roff/troff/node.cc @@ -55,6 +55,14 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #endif /* not _POSIX_VERSION */ +/* + * how many boundaries of images have been written? Useful for + * debugging grohtml + */ + +static int image_no=0; +static int suppress_start_page=0; + #define STORE_WIDTH 1 symbol HYPHEN_SYMBOL("hy"); @@ -322,7 +330,7 @@ void font_info::set_conditional_bold(int fontno, hunits offset) } conditional_bold::conditional_bold(int f, hunits h, conditional_bold *x) - : next(x), fontno(f), offset(h) +: next(x), fontno(f), offset(h) { } @@ -405,9 +413,9 @@ hunits font_info::get_half_narrow_space_width(font_size fs) tfont_spec::tfont_spec(symbol nm, int n, font *f, font_size s, int h, int sl) - : name(nm), input_position(n), fm(f), size(s), - is_bold(0), is_constant_spaced(0), ligature_mode(1), kern_mode(1), - height(h), slant(sl) +: name(nm), input_position(n), fm(f), size(s), + is_bold(0), is_constant_spaced(0), ligature_mode(1), kern_mode(1), + height(h), slant(sl) { if (height == size.to_scaled_points()) height = 0; @@ -671,6 +679,7 @@ public: void put_filename(const char *filename); void on(); void off(); + int is_on(); int is_printing(); void copy_file(hunits x, vunits y, const char *filename); }; @@ -742,6 +751,7 @@ public: void right(hunits); void down(vunits); void moveto(hunits, vunits); + void start_special(tfont *tf); void start_special(); void special_char(unsigned char c); void end_special(); @@ -785,6 +795,22 @@ inline void troff_output_file::put(int i) put_string(i_to_a(i), fp); } +void troff_output_file::start_special(tfont *tf) +{ + flush_tbuf(); + + /* + * although this is extremely unlikely to have an effect on other devices + * this way is safer. Currently this is only needed for html. + */ + if (is_html && tf) { + if (tf != current_tfont) + set_font(tf); + } + do_motion(); + put("x X "); +} + void troff_output_file::start_special() { flush_tbuf(); @@ -815,7 +841,8 @@ void troff_output_file::really_print_line(hunits x, vunits y, node *n, { moveto(x, y); while (n != 0) { - n->tprint(this); + if (is_on() || (n->force_tprint())) + n->tprint(this); n = n->next; } flush_tbuf(); @@ -1367,7 +1394,7 @@ void real_output_file::transparent_char(unsigned char c) void real_output_file::print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width) { - if (printing && output_on) + if (printing) really_print_line(x, y, n, before, after, width); delete_node_list(n); } @@ -1392,10 +1419,6 @@ void real_output_file::on() if (output_on == 0) { output_on = 1; } - /* - * lastly we reset the output registers - */ - reset_output_registers(); } void real_output_file::off() @@ -1404,6 +1427,11 @@ void real_output_file::off() output_on = 0; } +int real_output_file::is_on() +{ + return( output_on ); +} + void real_output_file::really_on() { } @@ -1527,6 +1555,7 @@ public: int character_type(); int same(node *); const char *type(); + int force_tprint(); }; glyph_node *glyph_node::free_list = 0; @@ -1549,6 +1578,7 @@ public: void asciify(macro *); int same(node *); const char *type(); + int force_tprint(); }; class kern_pair_node : public node { @@ -1574,6 +1604,7 @@ public: void asciify(macro *); int same(node *); const char *type(); + int force_tprint(); void vertical_extent(vunits *, vunits *); }; @@ -1602,6 +1633,7 @@ public: void asciify(macro *); int same(node *); const char *type(); + int force_tprint(); }; void *glyph_node::operator new(size_t n) @@ -1793,14 +1825,14 @@ void glyph_node::ascii_print(ascii_output_file *ascii) ligature_node::ligature_node(charinfo *c, tfont *t, node *gn1, node *gn2, node *x) - : glyph_node(c, t, x), n1(gn1), n2(gn2) +: glyph_node(c, t, x), n1(gn1), n2(gn2) { } #ifdef STORE_WIDTH ligature_node::ligature_node(charinfo *c, tfont *t, hunits w, node *gn1, node *gn2, node *x) - : glyph_node(c, t, w, x), n1(gn1), n2(gn2) +: glyph_node(c, t, w, x), n1(gn1), n2(gn2) { } #endif @@ -1841,12 +1873,12 @@ node *ligature_node::add_self(node *n, hyphen_list **p) } kern_pair_node::kern_pair_node(hunits n, node *first, node *second, node *x) - : node(x), amount(n), n1(first), n2(second) +: node(x), amount(n), n1(first), n2(second) { } dbreak_node::dbreak_node(node *n, node *p, node *x) - : node(x), none(n), pre(p), post(0) +: node(x), none(n), pre(p), post(0) { } @@ -2005,6 +2037,7 @@ public: node *copy(); int same(node *); const char *type(); + int force_tprint(); hyphenation_type get_hyphenation_type(); }; @@ -2027,6 +2060,11 @@ const char *hyphen_inhibitor_node::type() return "hyphen_inhibitor_node"; } +int hyphen_inhibitor_node::force_tprint() +{ + return 0; +} + hyphenation_type hyphen_inhibitor_node::get_hyphenation_type() { return HYPHEN_INHIBIT; @@ -2096,6 +2134,11 @@ node *node::last_char_node() return 0; } +int node::force_tprint() +{ + return 0; +} + hunits hmotion_node::width() { return n; @@ -2172,6 +2215,7 @@ public: hunits skew(); node *add_self(node *, hyphen_list **); const char *type(); + int force_tprint(); }; node *node::add_italic_correction(hunits *width) @@ -2309,6 +2353,7 @@ public: tfont *get_tfont(); int same(node *); const char *type(); + int force_tprint(); }; break_char_node::break_char_node(node *n, int c, node *x) @@ -2657,6 +2702,11 @@ node *space_node::copy() return new space_node(n, set); } +int space_node::force_tprint() +{ + return 0; +} + int space_node::nspaces() { return set ? 0 : 1; @@ -3216,11 +3266,25 @@ int node::interpret(macro *) special_node::special_node(const macro &m) : mac(m) { + font_size fs = curenv->get_font_size(); + int char_height = curenv->get_char_height(); + int char_slant = curenv->get_char_slant(); + int fontno = curenv->get_font(); + tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, + fontno); + if (curenv->is_composite()) + tf = tf->get_plain(); +} + +special_node::special_node(const macro &m, tfont *t) +: mac(m), tf(t) +{ } int special_node::same(node *n) { - return mac == ((special_node *)n)->mac; + return ((mac == ((special_node *)n)->mac) && + (tf == ((special_node *)n)->tf)); } const char *special_node::type() @@ -3228,14 +3292,19 @@ const char *special_node::type() return "special_node"; } +int special_node::force_tprint() +{ + return 0; +} + node *special_node::copy() { - return new special_node(mac); + return new special_node(mac, tf); } void special_node::tprint_start(troff_output_file *out) { - out->start_special(); + out->start_special(get_tfont()); } void special_node::tprint_char(troff_output_file *out, unsigned char c) @@ -3248,6 +3317,200 @@ void special_node::tprint_end(troff_output_file *out) out->end_special(); } +tfont *special_node::get_tfont() +{ + return tf; +} + +/* suppress_node */ + +suppress_node::suppress_node(int on_or_off, int issue_limits) +: is_on(on_or_off), emit_limits(issue_limits), filename(0), position(0) +{ +} + +suppress_node::suppress_node(symbol f, char p) +: is_on(2), emit_limits(0), filename(f), position(p) +{ +} + +suppress_node::suppress_node(int issue_limits, int on_or_off, symbol f, char p) +: is_on(on_or_off), emit_limits(issue_limits), filename(f), position(p) +{ +} + +int suppress_node::same(node *n) +{ + return ((is_on == ((suppress_node *)n)->is_on) + && (emit_limits == ((suppress_node *)n)->emit_limits) + && (filename == ((suppress_node *)n)->filename) + && (position == ((suppress_node *)n)->position)); +; +} + +const char *suppress_node::type() +{ + return "suppress_node"; +} + +node *suppress_node::copy() +{ + return new suppress_node(emit_limits, is_on, filename, position); +} + +int get_reg_int (const char *p) +{ + reg *r = (reg *)number_reg_dictionary.lookup(p); + units prev_value; + if (r && (r->get_value(&prev_value))) + return( (int)prev_value ); + else + warning(WARN_REG, "number register `%1' not defined", p); + return( 0 ); +} + +const char *get_reg_str (const char *p) +{ + reg *r = (reg *)number_reg_dictionary.lookup(p); + if (r) + return r->get_string(); + else + warning(WARN_REG, "register `%1' not defined", p); + return( 0 ); +} + +void suppress_node::put(troff_output_file *out, const char *s) +{ + int i=0; + + while (s[i] != (char)0) { + out->special_char(s[i]); + i++; + } +} + +/* + * We prefer to remember the last position, rather than have a .html-start + * followed by html-end-center, html-end-left as it is more natural to + * express an image by: + * + * .html-image-left + * + * .html-image-end + * + * similar to other troff commands, although this method is slightly more + * messy to implement. + */ + +static char last_position = 0; +static const char *last_image_filename = 0; + +inline int min(int a, int b) +{ + return a < b ? a : b; +} + +/* + * tprint - if (is_on == 2) + * remember current position (l, r, c, i) and filename + * else + * if (emit_limits) + * if (html) + * emit image tag + * else + * emit postscript bounds for image + * else + * if (suppress boolean differs from current state) + * alter state + * reset registers + * record current page + * set low water mark. + */ + +void suppress_node::tprint(troff_output_file *out) +{ + int current_page = get_reg_int("%"); + // firstly check to see whether this suppress node contains + // an image filename & position. + if (is_on == 2) { + // remember position and filename + last_position = position; + last_image_filename = filename.contents(); + } + else { + // now check whether the suppress node requires us to issue limits. + if (emit_limits) { + char name[8192]; + image_no++; + // remember that the filename will contain a %d in which the + // image_no is placed + sprintf(name, last_image_filename, image_no); + if (is_html) { + switch (last_position) { + case 'c': + out->start_special(); + put(out, "html-tag:.centered-image"); + break; + case 'r': + out->start_special(); + put(out, "html-tag:.right-image"); + break; + case 'l': + out->start_special(); + put(out, "html-tag:.left-image"); + break; + case 'i': + ; + default: + ; + } + out->end_special(); + out->start_special(); + put(out, "html-tag:.auto-image "); + put(out, name); + out->end_special(); + } + else { + // postscript (or other device) + if (current_page != suppress_start_page) + error("suppression limit registers span more than one page;\n" + "image description %1 will be wrong", image_no); + // remember that the filename will contain a %d in which the + // image_no is placed */ + fprintf(stderr, + "grohtml-info:page %d %d %d %d %d %d %s %d %d %s\n", + current_page, + get_reg_int("opminx"), get_reg_int("opminy"), + get_reg_int("opmaxx"), min(get_reg_int("opmaxy"), + out->get_vpos()), + // page offset + line length + get_reg_int(".o") + get_reg_int(".l"), + name, hresolution, vresolution, get_reg_str(".F")); + fflush(stderr); + } + } + else { + if (is_on) + out->on(); + else + out->off(); + // lastly we reset the output registers + reset_output_registers(out->get_vpos()); + suppress_start_page = current_page; + } + } +} + +int suppress_node::force_tprint() +{ + return is_on; +} + +hunits suppress_node::width() +{ + return H0; +} + /* composite_node */ class composite_node : public charinfo_node { @@ -3269,6 +3532,7 @@ public: tfont *get_tfont(); int same(node *); const char *type(); + int force_tprint(); void vertical_extent(vunits *, vunits *); vunits vertical_width(); }; @@ -3433,6 +3697,11 @@ node *unbreakable_space_node::copy() return new unbreakable_space_node(n, set, num_spaces); } +int unbreakable_space_node::force_tprint() +{ + return 0; +} + breakpoint *unbreakable_space_node::get_breakpoints(hunits, int, breakpoint *rest, int) { @@ -3459,7 +3728,7 @@ hvpair::hvpair() } draw_node::draw_node(char c, hvpair *p, int np, font_size s) - : npoints(np), sz(s), code(c) +: npoints(np), sz(s), code(c) { point = new hvpair[npoints]; for (int i = 0; i < npoints; i++) @@ -3482,6 +3751,11 @@ const char *draw_node::type() return "draw_node"; } +int draw_node::force_tprint() +{ + return 0; +} + draw_node::~draw_node() { if (point) @@ -4035,6 +4309,11 @@ const char *extra_size_node::type() return "extra_size_node"; } +int extra_size_node::force_tprint() +{ + return 0; +} + int vertical_size_node::same(node *nd) { return n == ((vertical_size_node *)nd)->n; @@ -4045,6 +4324,11 @@ const char *vertical_size_node::type() return "vertical_size_node"; } +int vertical_size_node::force_tprint() +{ + return 0; +} + int hmotion_node::same(node *nd) { return n == ((hmotion_node *)nd)->n; @@ -4055,6 +4339,11 @@ const char *hmotion_node::type() return "hmotion_node"; } +int hmotion_node::force_tprint() +{ + return 0; +} + int space_char_hmotion_node::same(node *nd) { return n == ((space_char_hmotion_node *)nd)->n; @@ -4065,6 +4354,11 @@ const char *space_char_hmotion_node::type() return "space_char_hmotion_node"; } +int space_char_hmotion_node::force_tprint() +{ + return 0; +} + int vmotion_node::same(node *nd) { return n == ((vmotion_node *)nd)->n; @@ -4075,6 +4369,11 @@ const char *vmotion_node::type() return "vmotion_node"; } +int vmotion_node::force_tprint() +{ + return 0; +} + int hline_node::same(node *nd) { return x == ((hline_node *)nd)->x && same_node(n, ((hline_node *)nd)->n); @@ -4085,6 +4384,11 @@ const char *hline_node::type() return "hline_node"; } +int hline_node::force_tprint() +{ + return 0; +} + int vline_node::same(node *nd) { return x == ((vline_node *)nd)->x && same_node(n, ((vline_node *)nd)->n); @@ -4095,6 +4399,11 @@ const char *vline_node::type() return "vline_node"; } +int vline_node::force_tprint() +{ + return 0; +} + int dummy_node::same(node * /*nd*/) { return 1; @@ -4105,6 +4414,11 @@ const char *dummy_node::type() return "dummy_node"; } +int dummy_node::force_tprint() +{ + return 0; +} + int transparent_dummy_node::same(node * /*nd*/) { return 1; @@ -4115,6 +4429,11 @@ const char *transparent_dummy_node::type() return "transparent_dummy_node"; } +int transparent_dummy_node::force_tprint() +{ + return 0; +} + int transparent_dummy_node::ends_sentence() { return 2; @@ -4130,6 +4449,11 @@ const char *zero_width_node::type() return "zero_width_node"; } +int zero_width_node::force_tprint() +{ + return 0; +} + int italic_corrected_node::same(node *nd) { return (x == ((italic_corrected_node *)nd)->x @@ -4141,6 +4465,10 @@ const char *italic_corrected_node::type() return "italic_corrected_node"; } +int italic_corrected_node::force_tprint() +{ + return 0; +} left_italic_corrected_node::left_italic_corrected_node(node *x) : node(x), n(0) @@ -4196,6 +4524,11 @@ const char *left_italic_corrected_node::type() return "left_italic_corrected_node"; } +int left_italic_corrected_node::force_tprint() +{ + return 0; +} + int left_italic_corrected_node::same(node *nd) { return (x == ((left_italic_corrected_node *)nd)->x @@ -4300,6 +4633,11 @@ const char *overstrike_node::type() return "overstrike_node"; } +int overstrike_node::force_tprint() +{ + return 0; +} + int bracket_node::same(node *nd) { return same_node_list(list, ((bracket_node *)nd)->list); @@ -4310,6 +4648,11 @@ const char *bracket_node::type() return "bracket_node"; } +int bracket_node::force_tprint() +{ + return 0; +} + int composite_node::same(node *nd) { return ci == ((composite_node *)nd)->ci @@ -4321,6 +4664,11 @@ const char *composite_node::type() return "composite_node"; } +int composite_node::force_tprint() +{ + return 0; +} + int glyph_node::same(node *nd) { return ci == ((glyph_node *)nd)->ci && tf == ((glyph_node *)nd)->tf; @@ -4331,6 +4679,11 @@ const char *glyph_node::type() return "glyph_node"; } +int glyph_node::force_tprint() +{ + return 0; +} + int ligature_node::same(node *nd) { return (same_node(n1, ((ligature_node *)nd)->n1) @@ -4343,6 +4696,11 @@ const char *ligature_node::type() return "ligature_node"; } +int ligature_node::force_tprint() +{ + return 0; +} + int kern_pair_node::same(node *nd) { return (amount == ((kern_pair_node *)nd)->amount @@ -4355,6 +4713,11 @@ const char *kern_pair_node::type() return "kern_pair_node"; } +int kern_pair_node::force_tprint() +{ + return 0; +} + int dbreak_node::same(node *nd) { return (same_node_list(none, ((dbreak_node *)nd)->none) @@ -4367,6 +4730,11 @@ const char *dbreak_node::type() return "dbreak_node"; } +int dbreak_node::force_tprint() +{ + return 0; +} + int break_char_node::same(node *nd) { return (break_code == ((break_char_node *)nd)->break_code @@ -4378,6 +4746,11 @@ const char *break_char_node::type() return "break_char_node"; } +int break_char_node::force_tprint() +{ + return 0; +} + int line_start_node::same(node * /*nd*/) { return 1; @@ -4388,6 +4761,11 @@ const char *line_start_node::type() return "line_start_node"; } +int line_start_node::force_tprint() +{ + return 0; +} + int space_node::same(node *nd) { return n == ((space_node *)nd)->n && set == ((space_node *)nd)->set; @@ -4409,6 +4787,11 @@ const char *word_space_node::type() return "word_space_node"; } +int word_space_node::force_tprint() +{ + return 0; +} + int unbreakable_space_node::same(node *nd) { return (n == ((unbreakable_space_node *)nd)->n @@ -4430,6 +4813,11 @@ const char *diverted_space_node::type() return "diverted_space_node"; } +int diverted_space_node::force_tprint() +{ + return 0; +} + int diverted_copy_file_node::same(node *nd) { return filename == ((diverted_copy_file_node *)nd)->filename; @@ -4440,6 +4828,11 @@ const char *diverted_copy_file_node::type() return "diverted_copy_file_node"; } +int diverted_copy_file_node::force_tprint() +{ + return 0; +} + // Grow the font_table so that its size is > n. static void grow_font_table(int n) @@ -4872,9 +5265,8 @@ track_kerning_function::track_kerning_function() : non_zero(0) track_kerning_function::track_kerning_function(int min_s, hunits min_a, int max_s, hunits max_a) - : non_zero(1), - min_size(min_s), min_amount(min_a), - max_size(max_s), max_amount(max_a) +: non_zero(1), min_size(min_s), min_amount(min_a), max_size(max_s), + max_amount(max_a) { } diff --git a/src/roff/troff/node.h b/src/roff/troff/node.h index 8366b01fe..c861262e2 100644 --- a/src/roff/troff/node.h +++ b/src/roff/troff/node.h @@ -57,6 +57,7 @@ struct node { virtual ~node(); virtual node *copy() = 0; + virtual int force_tprint () = 0; virtual hunits width(); virtual hunits subscript_correction(); virtual hunits italic_correction(); @@ -133,6 +134,7 @@ public: line_start_node() {} node *copy() { return new line_start_node; } int same(node *); + int force_tprint(); const char *type(); void asciify(macro *); }; @@ -169,6 +171,7 @@ public: void ascii_print(ascii_output_file *); int same(node *); const char *type(); + int force_tprint(); }; class word_space_node : public space_node { @@ -183,6 +186,7 @@ public: void asciify(macro *); const char *type(); int merge_space(hunits); + int force_tprint(); }; class unbreakable_space_node : public word_space_node { @@ -193,6 +197,7 @@ public: int same(node *); void asciify(macro *); const char *type(); + int force_tprint(); breakpoint *get_breakpoints(hunits width, int nspaces, breakpoint *rest = 0, int is_inner = 0); int nbreaks(); @@ -208,6 +213,7 @@ public: int reread(int *); int same(node *); const char *type(); + int force_tprint(); }; class diverted_copy_file_node : public node { @@ -219,6 +225,7 @@ public: int reread(int *); int same(node *); const char *type(); + int force_tprint(); }; class extra_size_node : public node { @@ -229,17 +236,19 @@ public: node *copy(); int same(node *); const char *type(); + int force_tprint(); }; class vertical_size_node : public node { vunits n; - public: +public: vertical_size_node(vunits i) : n(i) {} void set_vertical_size(vertical_size *); void asciify(macro *); node *copy(); int same(node *); const char *type(); + int force_tprint(); }; class hmotion_node : public node { @@ -253,6 +262,7 @@ public: void ascii_print(ascii_output_file *); int same(node *); const char *type(); + int force_tprint(); }; class space_char_hmotion_node : public hmotion_node { @@ -263,23 +273,25 @@ public: void asciify(macro *); int same(node *); const char *type(); + int force_tprint(); }; class vmotion_node : public node { vunits n; - public: +public: vmotion_node(vunits i) : n(i) {} void tprint(troff_output_file *); node *copy(); vunits vertical_width(); int same(node *); const char *type(); + int force_tprint(); }; class hline_node : public node { hunits x; node *n; - public: +public: hline_node(hunits i, node *c, node *next = 0) : node(next), x(i), n(c) {} ~hline_node(); node *copy(); @@ -287,12 +299,13 @@ class hline_node : public node { void tprint(troff_output_file *); int same(node *); const char *type(); + int force_tprint(); }; class vline_node : public node { vunits x; node *n; - public: +public: vline_node(vunits i, node *c, node *next= 0) : node(next), x(i), n(c) {} ~vline_node(); node *copy(); @@ -302,15 +315,17 @@ class vline_node : public node { void vertical_extent(vunits *, vunits *); int same(node *); const char *type(); + int force_tprint(); }; class dummy_node : public node { - public: +public: dummy_node(node *nd = 0) : node(nd) {} node *copy(); int same(node *); const char *type(); + int force_tprint(); hyphenation_type get_hyphenation_type(); }; @@ -320,19 +335,21 @@ public: node *copy(); int same(node *); const char *type(); + int force_tprint(); int ends_sentence(); hyphenation_type get_hyphenation_type(); }; class zero_width_node : public node { node *n; - public: +public: zero_width_node(node *gn); ~zero_width_node(); node *copy(); void tprint(troff_output_file *); int same(node *); const char *type(); + int force_tprint(); void append(node *); int character_type(); void vertical_extent(vunits *min, vunits *max); @@ -350,6 +367,7 @@ public: node *copy(); int same(node *); const char *type(); + int force_tprint(); hunits width(); node *last_char_node(); void vertical_extent(vunits *, vunits *); @@ -379,6 +397,7 @@ public: hunits width(); int same(node *); const char *type(); + int force_tprint(); }; class bracket_node : public node { @@ -393,19 +412,43 @@ public: hunits width(); int same(node *); const char *type(); + int force_tprint(); }; class special_node : public node { macro mac; + tfont *tf; void tprint_start(troff_output_file *); void tprint_char(troff_output_file *, unsigned char); void tprint_end(troff_output_file *); public: special_node(const macro &); + special_node(const macro &, tfont *t); + node *copy(); + void tprint(troff_output_file *); + int same(node *); + const char *type(); + int force_tprint(); + tfont *get_tfont(); +}; + +class suppress_node : public node { + int is_on; + int emit_limits; // must we issue the extent of the area written out? + symbol filename; + char position; +public: + suppress_node(int, int); + suppress_node(symbol f, char p); + suppress_node(int, int, symbol f, char p); node *copy(); void tprint(troff_output_file *); + hunits width(); int same(node *); const char *type(); + int force_tprint(); +private: + void put(troff_output_file *out, const char *s); }; struct hvpair { @@ -429,6 +472,7 @@ public: void tprint(troff_output_file *); int same(node *); const char *type(); + int force_tprint(); }; class charinfo; diff --git a/src/roff/troff/reg.h b/src/roff/troff/reg.h index fe04f2ab1..8d403d442 100644 --- a/src/roff/troff/reg.h +++ b/src/roff/troff/reg.h @@ -1,5 +1,6 @@ // -*- C++ -*- -/* Copyright (C) 1989, 1990, 1991, 1992, 2000 Free Software Foundation, Inc. +/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001 + Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) This file is part of groff. @@ -67,7 +68,7 @@ public: extern object_dictionary number_reg_dictionary; extern void set_number_reg(symbol nm, units n); extern void check_output_limits(int x, int y); -extern void reset_output_registers (void); +extern void reset_output_registers (int miny); reg *lookup_number_reg(symbol); #if 0 diff --git a/src/roff/troff/request.h b/src/roff/troff/request.h index a26ebf483..7e361766c 100644 --- a/src/roff/troff/request.h +++ b/src/roff/troff/request.h @@ -1,5 +1,6 @@ // -*- C++ -*- -/* Copyright (C) 1989, 1990, 1991, 1992, 2000 Free Software Foundation, Inc. +/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001 + Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) This file is part of groff. @@ -55,6 +56,9 @@ public: macro &operator=(const macro &); void append(unsigned char); void append(node *); + void append_unsigned(unsigned int i); + void append_int(int i); + void append_str(const char *); void invoke(symbol); macro *to_macro(); void print_size(); @@ -66,7 +70,7 @@ public: }; extern void init_input_requests(); -extern void init_output_requests(); +extern void init_html_requests(); extern void init_div_requests(); extern void init_node_requests(); extern void init_reg_requests(); diff --git a/src/roff/troff/troff.h b/src/roff/troff/troff.h index 5702d24e4..254c62647 100644 --- a/src/roff/troff/troff.h +++ b/src/roff/troff/troff.h @@ -1,5 +1,6 @@ // -*- C++ -*- -/* Copyright (C) 1989, 1990, 1991, 1992, 2000 Free Software Foundation, Inc. +/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001 + Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) This file is part of groff. @@ -42,7 +43,7 @@ extern units units_per_inch; extern int ascii_output_flag; extern int suppress_output_flag; -extern int is_html2; +extern int is_html; extern int tcommand_flag; extern int vresolution; diff --git a/tmac/Makefile.sub b/tmac/Makefile.sub index 3d4a7239f..98d4683a8 100644 --- a/tmac/Makefile.sub +++ b/tmac/Makefile.sub @@ -6,7 +6,7 @@ MAN7=\ groff_me.n \ groff_mdoc.n \ groff_mdoc.samples.n \ - groff_markup.n + groff_mwww.n NORMALFILES=\ mandoc.tmac andoc.tmac an-old.tmac \ @@ -21,7 +21,7 @@ NORMALFILES=\ tty.tmac tty-char.tmac latin1.tmac \ X.tmac Xps.tmac \ lj4.tmac \ - html.tmac html2.tmac html-tags.tmac markup.tmac arkup.tmac \ + html.tmac html-old.tmac mwww.tmac www.tmac \ eqnrc \ troffrc troffrc-end \ hyphen.us diff --git a/tmac/an-old.tmac b/tmac/an-old.tmac index 7d8ca6660..5b406a070 100644 --- a/tmac/an-old.tmac +++ b/tmac/an-old.tmac @@ -41,12 +41,25 @@ .if !r D .nr D 0 .if !r C .nr C 0 .if !r S .nr S 10 -.if r P .pn 0\n[P] +.\" we must use consecutive page numbers when using postscript to generate +.\" html images, and we must not reset the page number at the beginning +.\" (the `ps4html' register is automatically added to the command line by +.\" the pre-html preprocessor) +.if !r ps4html \ +. if r P .pn 0\n[P] .if !r cR \{\ . ie n .nr cR 1 . el .nr cR 0 .\} . +.\" compute the value of '\*(.T'html':'\*(.T'html-old' +.\" XXX: it would be nice to allow logical or with strings +. +.nr html-or-html-old 0 +.if '\*(.T'html-old' .nr html-or-html-old 1 +.if '\*(.T'html' .nr html-or-html-old 1 +.if \\n[html-or-html-old] .nr C 1 +.if r ps4html .nr C 1 . .de set-an-margin . nr an-margin \\n[IN] @@ -54,6 +67,10 @@ . .\" .TH title section extra1 extra2 extra3 .de TH +. if '\*(.T'html' \{\ +. HTML-TAG ".tl" +\\$1 +. \} . cp 0 . . de an-init \" We have to do it like this to get multiple man pages right. @@ -95,8 +112,8 @@ . ps \\n[PS]u . vs \\n[VS]u . -. ie '\*[.T]'html' .nr IN 1.2i -. el .nr IN 7.2n +. ie '\*[.T]'html-old' .nr IN 1.2i +. el .nr IN 7.2n . PD . nr SN 3n \" the indentation of sub-sub-headings relative to sub-headings . nr an-level 1 @@ -136,7 +153,7 @@ . ev 1 . ps \\n[PS]u . vs \\n[VS]u -. ie '\*[.T]'html' \ +. ie \\n[html-or-html-old] \ . tl '''' . el \{\ . if !\\n[cR] \ @@ -161,7 +178,7 @@ . ev 1 . ps \\n[PS]u . vs \\n[VS]u -. ie '\*[.T]'html' \{\ +. ie \\n[html-or-html-old] \{\ . ds an-page-string . ds an-extra1 . ds an-extra2 @@ -174,7 +191,7 @@ . ds an-page-string \\n[X]\\n[an-page-letter] . \} . \} -. ie '\*[.T]'html' \ +. ie \\n[html-or-html-old] \ . tl '''' . el \{\ . ie \\n[D] \{\ @@ -206,6 +223,7 @@ . fi . in \\n[an-margin]u . ti 0 +. HTML-TAG ".NH \\n[an-level]" . it 1 an-trap . nr an-no-space-flag 1 . nr an-break-flag 1 @@ -448,7 +466,7 @@ .ds lq \(lq .ds rq \(rq . -.hy 14 +.if !\\n[html-or-html-old] .hy 14 . .\" Load local modifications. .mso man.local diff --git a/tmac/arkup.tmac b/tmac/arkup.tmac deleted file mode 100755 index 8c52d121c..000000000 --- a/tmac/arkup.tmac +++ /dev/null @@ -1,162 +0,0 @@ -.\" arkup.tmac -.\" -.\" A simple set of macros to provide HTML documents with basic -.\" www functionality. It will work with any macro set. -.\" -.de HTML -. if '\*(.T'html' \{\ -.\" the following line makes the vertical mode leave, so to say -\& -\X^html:\\$*^ -. \} -.. -.de HTMLINDEX -. if '\*(.T'html' \X^index:\\$*^ -.. -.\" -.\" BODYCOLOR - $1 is foreground color -.\" $2 is background color -.\" $3 is the color of an active hypertext link -.\" $4 is the color of a hypertext link not yet visited -.\" $5 is the color of a visited hypertext link -.\" -.de BODYCOLOR -. HTML <body text=\\$1 bgcolor=\\$2 link=\\$3 alink=\\$4 vlink=\\$5> -.. -.\" -.\" BACKGROUND - $1 is the background image file -.\" -.de BACKGROUND -. HTML <body background=\\$1> -.. -.\" -.\" URL - $1 is the classical underlined blue text -.\" $2 is the url -.\" $3 is optional stuff printed immediately after $3 -.\" -.de URL -. ie '\*(.T'html' \{\ -. HTML <a href="\\$2">\\$1</a>\\$3 -. \} -. el \{\ -\\$1 \(la\fC\\$2\fP\(ra\\$3 -. \} -.. -.\" -.\" FTP - $1 is the classical underlined blue text -.\" $2 is the ftp url -.\" $3 is optional stuff printed immediately after $2 -.de FTP -. ie '\*(.T'html' \{\ -. HTML <a href=\\$2>\\$1</a>\\$3 -. \} -. el \{\ -\\$1 \(la\fC\\$2\fP\(ra\\$3 -. \} -.. -.\" -.\" MAILTO - generate html email reference -.\" $1 is the email address (without the `mailto:' prefix) -.\" $2 is the optional name -.\" $3 is optional stuff printed immediately after $2 (resp. $1) -.\" -.\" example: -.\" -.\" Foobar has been written by -.\" .MAILTO fred@foo.bar "Fredrick Bloggs" . -.\" -.de MAILTO -.\" -.\" force reset after a potential heading by performing some motion.. -.\" how do we do this --fixme-- -.\" \h'\w' ''\h'-\w' '' doesn't work.. -. ie '\*(.T'html' \{\ -. ie '\\$2'' \{\ -. HTML "<a href=mailto:\\$1>\\$1</a>\\$3" -. \} -. el \{\ -. HTML "<a href=mailto:\\$1>\\$2</a>\\$3" -. \} -. \} -. el \{\ -. ie '\\$2'' \{\ -\fC\\$1\fP\\$3 -. \} -. el \{\ -\\$2 \(la\fC\\$1\fP\(ra\\$3 -. \} -. \} -.. -.\" -.\" TAG - generate an html name $1 -.\" -.de TAG -. HTML <a name="\\$1"></a> -.. -.\" -.\" IMAGE - reference an image -.\" $1 is the image file -.\" $2 is the x width (default if absent 400 pixels) -.\" $3 is the y width (default if absent is the x value) -.\" -.de IMAGE -. ie '\*(.T'html' \{\ -. nr HTMLWIDTH 400 -. if '\\$2'' \{\ -. nr HTMLWIDTH \\$2 -. \} -. nr HTMLHEIGHT \\n[HTMLWIDTH] -. if '\\$3'' \{\ -. nr HTMLHEIGHT \\$3 -. \} -. HTML <img src="\\$1" width=\\n[HTMLWIDTH height=\\n[HTMLHEIGHT]> -. \} -. el \{\ -. B1 -\(la\fC\\$1\fP\(ra -. B2 -. \} -.. -.\" -.\" CDFTP - if we are processing this on machine \\$1 then we create a -.\" FTP reference using \\$2 --> \\$3 -.\" -.\" otherwise we create a URL from \\$2 --> \\$4 -.\" -.\" example: -.\" -.\" .CDFTP "foobar" "somegnusoftware.tar.gz" \ -.\" "ftp://ftp.gnu.org/gnu/somegnusoftware.tar.gz" \ -.\" "../../../TARGZ/somegnusoftware.tar.gz" -.\" -.\" meaning if we are on machine foobar then generate an ftp url -.\" to the GNU anonymous ftp server otherwise generate a file url -.\" to a local copy (cdrom maybe) -.\" -.\" Useful when one machine is designated as a cdrom burner and another -.\" designated as an appache server. -.\" The same source for web pages can be burnt onto a CD and also -.\" served across the network. It doesn't solve the problem of one -.\" machine doing both though :-( -.\" -.\" -.\".de CDFTP -.\". sy /bin/rm -f /tmp/n.tmac -.\". sy /bin/echo ".ds HOSTNAME `hostname --short`" > /tmp/n.tmac -.\". so /tmp/n.tmac -.\". sy /bin/rm -f /tmp/n.tmac -.\". ie '\\*[HOSTNAME]'\\$1' \{\ -.\". FTP "\\$2" "\\$3" -.\". \} -.\". el \{\ -.\". URL "\\$2" "\\$4" -.\". \} -.\".. -.de LINE -. HTML <hr> -.. -.\" -.\" it doesn't make sense to use hyphenation with html, so we turn it off. -.\" -.hy 0 -.nr HY 0 diff --git a/tmac/eqnrc b/tmac/eqnrc index 4c6a5a158..5b9598fa0 100644 --- a/tmac/eqnrc +++ b/tmac/eqnrc @@ -15,12 +15,12 @@ ifdef X100 ! define X %1% ! ifdef X75-12 ! define X %1% ! ifdef X100-12 ! define X %1% ! -ifdef ps ! define ps|X|html|html2 %1% ! -ifdef X ! define ps|X|html|html2 %1% ! -ifdef html ! define ps|X|html|html2 %1% ! -ifdef html2 ! define ps|X|html|html2 %1% ! +ifdef ps ! define ps|X|html|html-old %1% ! +ifdef X ! define ps|X|html|html-old %1% ! +ifdef html ! define ps|X|html|html-old %1% ! +ifdef html-old ! define ps|X|html|html-old %1% ! -ifdef ps|X|html|html2 ! sdefine inf %"\s[\En[.s]*13u/10u]\v'12M'\(if\v'-12M'\s0"% ! +ifdef ps|X|html|html-old ! sdefine inf %"\s[\En[.s]*13u/10u]\v'12M'\(if\v'-12M'\s0"% ! ifdef dvi ! sdefine int %{type "operator" vcenter \(is}% @@ -48,7 +48,7 @@ set big_op_spacing5 10 ifdef X ! set axis_height 32 ! -ifdef ps|X|html|html2 ! set draw_lines 1 ! +ifdef ps|X|html|html-old ! set draw_lines 1 ! ifdef ascii ! define n %1% ! ifdef latin1 ! define n %1% ! @@ -59,6 +59,6 @@ set nroff 1 ! undef X -undef ps|X|html|html2 +undef ps|X|html|html-old undef n .EN diff --git a/tmac/groff_man.man b/tmac/groff_man.man index 5f2f15546..420e2840f 100644 --- a/tmac/groff_man.man +++ b/tmac/groff_man.man @@ -362,7 +362,7 @@ of the next line appears in italic. . The default indentation is 7.2n for all output devices except for .B grohtml -which uses 1.2i instead. +which ignores indentation. .TP .B .DT Sets tabs every 0.5 inches. diff --git a/tmac/groff_markup.man b/tmac/groff_mwww.man index 74cc40f05..d57eaddbf 100755 --- a/tmac/groff_markup.man +++ b/tmac/groff_mwww.man @@ -1,4 +1,4 @@ -.TH GROFF_MARKUP @MAN7EXT@ "@MDATE@" "Groff Version @VERSION@" +.TH GROFF_MWWW @MAN7EXT@ "@MDATE@" "Groff Version @VERSION@" .\" Copyright (C) 2000 Free Software Foundation, Inc. .\" Written by Gaius Mulley (gaius@glam.ac.uk) .\" @@ -18,23 +18,26 @@ .\" with groff; see the file COPYING. If not, write to the Free Software .\" Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. .\" -.\" user level guide to using the -markup macroset +.\" user level guide to using the -mwww macroset .\" -.do mso arkup.tmac -.LINE +.do mso www.tmac +.\" we need the .LINKS here as we use it in the middle as an example +.\" once the user requests .LINKS then the automatic generation of links +.\" at the top of the document is suppresed. +.LINKS .SH NAME -groff_markup \- groff macros for authoring web pages +groff_mwww \- groff macros for authoring web pages .LINE .SH SYNOPSIS -.B "groff \-markup" +.B "groff \-mwww" [ options ] file ... .SH DESCRIPTION -This manual page describes the GNU \-markup macros, which is part of the +This manual page describes the GNU \-mwww macros, which is part of the groff document formatting system. The manual page is very a basic guide, and the html device driver .RB ( grohtml ) -is still very alpha. +has been completely rewritten but still remains as in an alpha state. It has been included into the distribution so that a lot of people have a chance to test it. Note that this macro file will be automatically called (via the @@ -106,6 +109,8 @@ An \fCHTMLINDEX\fP of 2 will mean that a heading .sp will not be included in the links either as it is said to have a heading level of three. +Another method for switching automatic headings is via the command line +switch \fC-P-l\fP. .TP .B BODYCOLOR takes five parameters: foreground, background, active hypertext link, @@ -152,18 +157,15 @@ and This is achieved by the following macros: .sp .nf -\s-2\fC\&.MAILTO wl@gnu.org "Werner Lemberg"\fP -.sp -\fC\&.MAILTO Ted.Harding@nessie.mcc.ac.uk \\ -.br -"Ted Harding" .\s+2\fP +\s-2\fC\&.MAILTO wl@gnu.org "Werner Lemberg" +\&.MAILTO Ted.Harding@nessie.mcc.ac.uk "Ted Harding" .\s+2\fP .fi .sp Note that all the urls actually are treated as consuming no textual space in groff. This could be considered as a bug since it causes some problems. -To circumvent this, \fCarkup.tmac\fP inserts a zero-width character which -expands to a harmless space (only if in HTML mode) +To circumvent this, \fCwww.tmac\fP inserts a zero-width character which +expands to a harmless space (only if run with -Thtml). .TP .B FTP indicates that data can be obtained via ftp. @@ -176,9 +178,10 @@ As an example, here the location of the ftp://ftp.ffii.org/pub/groff/devel/groff-current.tar.gz . The macro example above was specified by: .sp +.nf \s-2\fC\&.FTP "current groff development distribution" \\ -.br ftp://ftp.ffii.org/pub/groff/devel/groff-current.tar.gz .\fP\s+2 +.fi .sp .TP .B IMAGE @@ -214,95 +217,48 @@ a local reference. This link was achieved via placing a TAG in the URL description above; the source looks like this: .sp +.nf \s-2\fC\&.TP -.br \&.B URL -.br generates -.br \&.TAG URL -.br a URL using either two or three -.br arguments. -.br -$1 is the name of the link, $2 is the actual URL -.br -\fP\s+2etc. +$1 is the name of the link, $2 is the actual URL.\fP\s+2 +.fi .sp -.\".TP -.\".B CDFTP -.\"takes four arguments. -.\"Basically it is the FTP macro with optional local reference. -.\"It was designed to allow the same groff source to be built on two different -.\"machines and access the ftp data differently. -.\"For example, on a web server you might wish for the web page to reference -.\"a web site. -.\"However, if you were producing a CDROM of your information you might wish -.\"for the ftp data to be also stored on your CDROM and referenced as a file. -.\"An example to get the current groff development distribution -.\".CDFTP merlin "click here." \ -.\"ftp://ftp.ffii.org/pub/groff/devel/groff-current.tar.gz \ -.\"../../groff.tar.gz -.\"The source for this CDFTP invocation is -.\".sp -.\"\fC\s-2\&.CDFTP merlin "click here." \ -.\"ftp://ftp.ffii.org/pub/groff/devel/groff-current.tar.gz \ -.\"../../groff.tar.gz\fP\s+2 -.\".sp -.\"which means if the html is generated on machine \fCmerlin\fP -.\"then generate a URL to \fC../../groff.tar.gz\fP. -.\"Otherwise construct an FTP URL to -.\"\fCftp://ftp.ffii.org/pub/groff/devel/groff-current.tar.gz\fP. .TP .B LINE generates a full width horizontal rule. Example: .sp +.nf \fC\s-2\&.LINE\fP\s+2 +.fi .sp +.TP +.B LINKS +.TAG LINK +requests that grohtml place the automatically generated links at this position. +For example: +.LINKS .SH SECTION HEADING LINKS .LP By default .B grohtml generates links to all section headings and places these at the top of the -html document. -.B Grohtml -has to guess what a section heading looks like \(em remember that all -.B grohtml -actually sees is a device independent language telling it where to place -text, draw lines, change font sizes and faces etc. -It believes a section heading to be a line of bold text which starts at the -left most margin. -Consequently it may misinterpret. -Users can turn off all heading and title guessing by giving invoking groff -with \fCgroff -P-g\fP. +html document. (See +.URL LINKS #LINK +for details of how to switch this off or alter the position. .SH LIMITATIONS OF GROHTML .LP -Although basic text can be translated in a straightforward fashion there are -some areas where -.B grohtml -has to try and guess text relationship. -In particular, whenever -.B grohtml -encounters text tables and indented paragraphs or two column mode it will -try and utilize the html table construct to preserve columns. -.B Grohtml -also attempts to work out which lines should be automatically formatted by -the browser. -Ultimately in trying to make reasonable guesses most of the time it will -make mistakes. -Hopefully these mistakes will happen less and less as we get bug reports -and patches :-). -.PP -Tbl, pic, eqn's are also generated using images which may be -considered a limitation. +Tbl information is currently rendered as a png image. .SH FILES -@MACRODIR@/markup.tmac (a wrapper for arkup.tmac) +@MACRODIR@/mwww.tmac (a wrapper for www.tmac) .br -@MACRODIR@/arkup.tmac +@MACRODIR@/www.tmac .SH "SEE ALSO" .BR groff (@MAN1EXT@), .BR @g@troff (@MAN1EXT@) @@ -310,7 +266,7 @@ considered a limitation. .LP .SH AUTHOR .B Grohtml -is written by +was written by .MAILTO gaius@glam.ac.uk "Gaius Mulley" .LINE .SH BUGS @@ -318,4 +274,3 @@ Report bugs to the .MAILTO bug-groff@gnu.org "Groff Bug Mailing List" . Include a complete, self-contained example that will allow the bug to be reproduced, and say which version of groff you are using. -.LINE diff --git a/tmac/groff_tmac.man b/tmac/groff_tmac.man index bd8f4d862..331fe8079 100755 --- a/tmac/groff_tmac.man +++ b/tmac/groff_tmac.man @@ -431,7 +431,7 @@ in the groff source package. .LP The groff tmac macro packages are .BR groff_man (@MAN7EXT@), -.BR groff_markup (@MAN7EXT@), +.BR groff_mwww (@MAN7EXT@), .BR groff_mdoc (@MAN7EXT@), .BR groff_mdoc.samples (@MAN7EXT@), .BR groff_me (@MAN7EXT@), diff --git a/tmac/html2.tmac b/tmac/html-old.tmac index e98aa4ae4..662cf58fc 100755 --- a/tmac/html2.tmac +++ b/tmac/html-old.tmac @@ -1,4 +1,4 @@ -.\" html2.tmac +.\" html-old.tmac .\" .nr _C \n(.C .cp 0 @@ -43,7 +43,7 @@ .if !c\(rh .char \(rh -> .if !c\(bq .tr \(bq, .if !c\(aq .tr \(aq' -.if '\*(.T'html' .char \[radicalex] \h'-\w'\(sr'u'\[radicalex]\h'\w'\(sr'u' +.if '\*(.T'html-old' .char \[radicalex] \h'-\w'\(sr'u'\[radicalex]\h'\w'\(sr'u' .if !\n(_C .mso pspic.tmac .cp \n(_C .\" now turn off all headers and footers for ms, me and mm macro sets @@ -55,10 +55,8 @@ .if d of .of ''' .if d oh .oh ''' .if d eh .eh ''' -.\" it doesn't make sense to use hyphenation with html, so we turn it off. +.\" it doesn't make sense to use hyphenation with html-old, so we turn it off. .hy 0 .nr HY 0 .\" avoid line breaks after hyphen-like characters. .cflags 0 -\(hy\(em\(en\[shc] -.\" now load the tag macros -.mso html-tags.tmac diff --git a/tmac/html-tags.tmac b/tmac/html-tags.tmac deleted file mode 100755 index e1715afa0..000000000 --- a/tmac/html-tags.tmac +++ /dev/null @@ -1,49 +0,0 @@ -.\" html-tags.tmac - issues tags for post-html2 -.\" -.als html-sp-old sp -.de sp -\X^html-tag:.sp \\$*^ -. html-sp-old \\$* -.. -.als html-br-old br -.de br -\X^html-tag:.br \\$*^ -. html-br-old \\$* -.. -.als html-ce-old ce -.de ce -\X^html-tag:.ce \\$*^ -. html-ce-old \\$* -.. -.als html-tl-old tl -.de tl -\X^html-tag:.tl \\$*^ -. html-tl-old \\$* -.. -.als html-in-old in -.de in -\X^html-tag:.in \\$*^ -. html-in-old \\$* -.. -.als html-ti-old ti -.de ti -\X^html-tag:.ti \\$*^ -. html-ti-old \\$* -.. -.als html-ta-old ta -.de ta -\X^html-tag:.ta \\$*^ -. html-ta-old \\$* -.. -.\" these definitions should be removed once grohtml2 has been finished -.\" and arkup.html is working with html2 (actually expect html2 to replace html). -.de HTML -. if '\*(.T'html2' \{\ -.\" the following line makes the vertical mode leave, so to say -\& -\X^html:\\$*^ -. \} -.. -.de IMAGE -. HTML <img src="\\$1"> -.. diff --git a/tmac/html.tmac b/tmac/html.tmac index 05457807e..199245b1d 100644 --- a/tmac/html.tmac +++ b/tmac/html.tmac @@ -10,13 +10,6 @@ .ftr HO HI .ftr HX HBI .ftr NX NBI -.char \(ru \D'l .5m 0' -.char \(ul \v'.25m'\D'l .5m 0'\v'-.25m' -.char \(br \v'.25m'\D'l 0 -1m'\v'.75m' -.char \(rn \v'-.75m'\D'l .5m 0'\v'.75m' -.\" .char ~ \v'-.55m'\\s[\\n(.s/2u]\v'.2m'\(ti\v'-.2m'\s0\v'.55m' -.\" .char ^ \v'-.55m'\\s[\\n(.s/2u]\v'.3m'\(ha\v'-.3m'\s0\v'.55m' -.if !c\(va .char \(va \o'\(ua\(da' .if !c\(em .char \(em -- .if !c\(en .char \(en \- .if !c\(fi .char \(fi fi @@ -24,39 +17,32 @@ .if !c\(ff .char \(ff ff .if !c\(Fi .char \(Fi ffi .if !c\(Fl .char \(Fl ffl -.if !c\(ci .char \(ci \v'-.25m'\h'.05m'\D'c .5m'\h'.05m'\v'.25m' -.if !c\(sq .char \(sq \h'.05m'\D'l .5m 0'\D'l 0 -.5m'\D'l -.5m 0'\D'l 0 .5m'\h'.55m' -.if !c\(ga .char \(ga \Z'\v'-.7m'\D'l .22m .18m''\h'.33m' -.if !c\(dg .char \(dg \Z'\h'.25m'\v'.15m'\D'l 0 -.8m'\v'.2m'\h'-.195m'\ -\D'l .39m 0''\h'.5m' -.if !c\(dd .char \(dd \Z'\h'.25m'\v'.15m'\D'l 0 -.8m'\v'.2m'\h'-.195m'\ -\D'l .39m 0'\v'.4m'\D'l -.39m 0''\h'.5m' .if !c\(lq .char \(lq `` .if !c\(rq .char \(rq '' .if !c\(Bq .char \(bq ,, -.if !c\(OE .char \(OE O\h'-.25m'E -.if !c\(oe .char \(oe o\h'-.14m'e -.if !c\(ah .char \(ah \v'-.55m'\s[\En[.s]/2u]v\s0\v'.55m' -.if !c\(ao .char \(ao \v'-.55m'\s[\En[.s]*6u/10u]\D'c .25m'\s0\v'.55m' -.if !c\(ho .char \(ho \s[\En[.s]/2u]\v'.4m'c\v'-.4m'\s0 +.if !c\(OE .char \(OE OE +.if !c\(oe .char \(oe oe .if !c\(lh .char \(lh <- .if !c\(rh .char \(rh -> .if !c\(bq .tr \(bq, .if !c\(aq .tr \(aq' -.if '\*(.T'html' .char \[radicalex] \h'-\w'\(sr'u'\[radicalex]\h'\w'\(sr'u' .if !\n(_C .mso pspic.tmac .cp \n(_C .\" now turn off all headers and footers for ms, me and mm macro sets -.if d EF .EF ''' -.if d EH .EH ''' -.if d OF .OF ''' -.if d OH .OH ''' -.if d ef .ef ''' -.if d of .of ''' -.if d oh .oh ''' -.if d eh .eh ''' +.if d EF .EF '''' +.if d EH .EH '''' +.if d OF .OF '''' +.if d OH .OH '''' +.if d ef .ef '''' +.if d of .of '''' +.if d oh .oh '''' +.if d eh .eh '''' +.tl '''' .\" it doesn't make sense to use hyphenation with html, so we turn it off. +.\" avoid line breaks after hyphen-like characters. .hy 0 .nr HY 0 .\" avoid line breaks after hyphen-like characters. .cflags 0 -\(hy\(em\(en\[shc] +.pl 99999 +.\" eof of file, make sure this is the last time diff --git a/tmac/markup.tmac b/tmac/mwww.tmac index 787b6db60..787b6db60 100755 --- a/tmac/markup.tmac +++ b/tmac/mwww.tmac diff --git a/tmac/pspic.tmac b/tmac/pspic.tmac index 77b52ec22..ab0666860 100644 --- a/tmac/pspic.tmac +++ b/tmac/pspic.tmac @@ -5,18 +5,25 @@ .\" the picture would go. .de PSPIC .nr ps-offset-mode 0 -.if '\\$1'-L' \{\ +.ie '\\$1'-L' \{\ . nr ps-offset-mode 1 . shift +. HTML-DO-IMAGE \\$1 l .\} -.if '\\$1'-R' \{\ -. nr ps-offset-mode 2 -. shift -.\} -.if '\\$1'-I' \{\ -. nr ps-offset-mode 3 -. nr ps-offset (m;\\$2) -. shift 2 +.el \{\ +. ie '\\$1'-R' \{\ +. nr ps-offset-mode 2 +. shift +. HTML-DO-IMAGE \\$1 r +. \} +. el \{\ +. if '\\$1'-I' \{\ +. nr ps-offset-mode 3 +. nr ps-offset (m;\\$2) +. shift 2 +. \} +. HTML-DO-IMAGE \\$1 i +. \} .\} .br .psbb \\$1 @@ -49,4 +56,5 @@ . br . sp \\n[ps-desht]u .\} +.HTML-IMAGE-END .. diff --git a/tmac/s.tmac b/tmac/s.tmac index 507e5547d..9a8d65e94 100644 --- a/tmac/s.tmac +++ b/tmac/s.tmac @@ -144,6 +144,7 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. . als FE @FE .\} .wh 0 pg@top +.CHECK-FOOTER-AND-KEEP .. .wh 0 cov*first-page-init .\" This handles the case where FS occurs before TL or LP. @@ -165,13 +166,14 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. .rn @AB AB .rn @AU AU .rn @AI AI -.di cov*tl-div +.if !'\*(.T'html' .di cov*tl-div .par@reset .ft B .ps +2 .vs +3p .ll (u;\\n[LL]*5/6) .nr cov*n-au 0 +.HTML-TAG ".tl" .. .de @AU .par@reset @@ -186,7 +188,7 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. .ps \\n[PS] .. .de @AI -.par@reset +.if !'\*(.T'html' .par@reset .if !'\\n(.z'' \{\ . br . di @@ -249,7 +251,7 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. . di .\} .cov*ab-init -.di cov*ab-div +.if !'\*(.T'html' .di cov*ab-div .par@ab-indent .par@reset .if !'\\$1'no' \{\ @@ -261,9 +263,20 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. .\} .ns .@PP +.if '\*(.T'html' \{\ +. cov*tl-au-print +. als cov*tl-au-print @nop +. par@reset-env +. par@reset +. cov*print +.\} .. .de AE -.ie '\\n(.z'cov*ab-div' \{\ +.ie '\*(.T'html' \{\ +. als AE cov*err-not-again +.\} +.el \{\ +. ie '\\n(.z'cov*ab-div' \{\ . als AE cov*err-not-again . br . di @@ -271,8 +284,9 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. . par@reset-env . par@reset . cov*print +. \} +. el .@error AE without AB .\} -.el .@error AE without AB .. .de @div-end!cov*ab-div .AE @@ -289,6 +303,7 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. . bp 1 . als FS @FS . als FE @FE +. CHECK-FOOTER-AND-KEEP . \} . br .\} @@ -299,7 +314,7 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. .cov*tl-au-print .sp 3 .if d cov*ab-div \{\ -. nf +. if !'\*(.T'html' . nf . cov*ab-div .\} .sp 3 @@ -321,6 +336,7 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. .\} .als FS @FS .als FE @FE +.CHECK-FOOTER-AND-KEEP .\" If anything was printed below where the footer line is normally printed, .\" then that's an overflow. .if -\\n[FM]/2+1v+\\n[cov*page-length]<\\n[nl] .@error cover sheet overflow @@ -345,7 +361,7 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. .rs .sp 3 .ce 9999 -.cov*tl-div +.if d cov*tl-div .cov*tl-div .nr cov*i 1 .nr cov*sp 1v .while \\n[cov*i]<=\\n[cov*n-au] \{\ @@ -461,6 +477,7 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. . nr pg*gutw \\n[LL]-(\\n[pg@ncols]*\\n[pg@colw])/(\\n[pg@ncols]-1) . el .nr pg*gutw 0 .\} +.HTML-TAG ".mc \\n[pg@ncols] \\n[pg@colw] \\n[pg*gutw]" .mk pg*col-top .ns .nr pg*col-num 0 @@ -985,6 +1002,7 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. .if !rHY .nr HY 14 .hy \\n[HY] .TA +.CHECK-FOOTER-AND-KEEP .. .de par*vs .\" If it's too big to be in points, treat it as units. @@ -1206,6 +1224,7 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. .\" start boxed text .de B1 .br +.HTML-IMAGE .di par*box-div .nr \\n[.ev]:li +1n .nr \\n[.ev]:ri +1n @@ -1242,6 +1261,7 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. . par*box-draw \\n[.i]u \\n[.l]u-(\\n[.H]u==1n*1n) .\} .el .@error B2 without B1 +.HTML-IMAGE-END .. .de par*box-mark-top .ie '\\n[.z]'' \{\ @@ -1280,6 +1300,7 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. .ne 3v+\\n[\\n[.ev]:PD]u+\\n(.Vu .sp 1 .ft B +.HTML-TAG ".SH 1" .. .\" TL, AU, and AI are aliased to these in cov*ab-init. .de par@TL @@ -1289,6 +1310,7 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. .ps +2 .vs +3p .ce 9999 +.HTML-TAG ".tl" .. .de par@AU .par@finish @@ -1401,6 +1423,7 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. .nr nh*hl 0 .\" numbered heading .de @NH +.HTML-TAG ".NH \\$1" .ie '\\$1'S' \{\ . shift . nr nh*hl 0 @@ -1868,5 +1891,36 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. .ds ? \(r?\" upside down ? .ds ! \(r!\" upside down ! .. +.de CHECK-FOOTER-AND-KEEP +.\" it might be better to als FS -> B1 and FE -> B2 +.\" however this produced wierd results, so I've moved back to a more reliable +.\" but less interesting solution --fixme-- +. if '\*(.T'html' \{\ +. rm KF +. als KF KS +. rm FS +. de FS +. br +. HTML-IMAGE +\\.. +. rm FE +. de FE +. br +. HTML-IMAGE-END +\\.. +. \} +. if r ps4html \{\ +. rm FS +. de FS +. br +. HTML-IMAGE +\\.. +. rm FE +. de FE +. br +. HTML-IMAGE-END +\\.. +. \} +.. .par@load-init .\" Make sure that no blank lines creep in at the end of this file. diff --git a/tmac/troffrc b/tmac/troffrc index 9f2a212a5..9e71e7740 100644 --- a/tmac/troffrc +++ b/tmac/troffrc @@ -16,11 +16,12 @@ .do ds troffrc!cp1047 tty.tmac .do ds troffrc!lj4 lj4.tmac .do ds troffrc!lbp lbp.tmac -.do ds troffrc!html arkup.tmac +.do ds troffrc!html www.tmac +.do ds troffrc!html-old www.tmac .do if d troffrc!\*[.T] \ . do mso \*[troffrc!\*[.T]] .do rm troffrc!ps troffrc!Xps troffrc!dvi troffrc!X75 troffrc!X75-12 \ -troffrc!X100 troffrc!X100-12 troffrc!lj4 troff!lbp troffrc!html +troffrc!X100 troffrc!X100-12 troffrc!lj4 troff!lbp troffrc!html troffrc!html-old .ie '\*(.T'cp1047' .do tr \[char65] .el .do tr \[char160] .\" Set the hyphenation language to `us'. diff --git a/tmac/troffrc-end b/tmac/troffrc-end index 8f97605cc..930e6afd2 100644 --- a/tmac/troffrc-end +++ b/tmac/troffrc-end @@ -2,6 +2,19 @@ .\" final startup file for troff .\" this file is parsed after all macro sets have been read .\" -.if '\*(.T'html' .mso html.tmac -.if '\*(.T'html2' .mso html2.tmac +.if '\*(.T'html' .mso html.tmac +.if '\*(.T'html-old' .mso html-old.tmac +.\" if we are running the postscript device for html images then load -mwww +.\" +.if r ps4html .mso www.tmac +.\" +.\" for all other devices blank out these macros +.\" +.if !d HTML-IMAGE-INLINE .ds HTML-IMAGE-INLINE +.if !d HTML-IMAGE .ds HTML-IMAGE +.if !d HTML-IMAGE-RIGHT .ds HTML-IMAGE-RIGHT +.if !d HTML-IMAGE-LEFT .ds HTML-IMAGE-LEFT +.if !d HTML-IMAGE-END .ds HTML-IMAGE-END +.if !d HTML-TAG .ds HTML-TAG +.if !d HTML-DO-IMAGE .ds HTML-DO-IMAGE .\" Don't let blank lines creep in here. diff --git a/tmac/www.tmac b/tmac/www.tmac new file mode 100644 index 000000000..0297a265c --- /dev/null +++ b/tmac/www.tmac @@ -0,0 +1,213 @@ +.\" www.tmac +.\" +.\" A simple set of macros to provide HTML documents with basic +.\" www functionality. It will work with any macro set. +.\" +.\" compute the value of '\*(.T'html-old':'\*(.T'html' +.\" --fixme-- it would be nice to allow logical or with strings +.nr html-or-html-old 0 +.if '\*(.T'html-old' .nr html-or-html-old 1 +.if '\*(.T'html' .nr html-or-html-old 1 +.\" +.\" +.\" +.de HTML +. if \\n[html-or-html-old] \{\ +.\" the following line makes the vertical mode leave, so to say +\& +\X^html:\\$*^ +. \} +.. +.de HTMLINDEX +. if \\n[html-or-html-old] \X^index:\\$*^ +.. +.\" +.\" BODYCOLOR - $1 is foreground color +.\" $2 is background color +.\" $3 is the color of an active hypertext link +.\" $4 is the color of a hypertext link not yet visited +.\" $5 is the color of a visited hypertext link +.\" +.de BODYCOLOR +. HTML <body text=\\$1 bgcolor=\\$2 link=\\$3 alink=\\$4 vlink=\\$5> +.. +.\" +.\" BACKGROUND - $1 is the background image file +.\" +.de BACKGROUND +. HTML <body background=\\$1> +.. +.\" +.\" URL - $1 is the classical underlined blue text +.\" $2 is the url +.\" $3 is optional stuff printed immediately after $3 +.\" +.de URL +. ie \\n[html-or-html-old] \{\ +. HTML <a href="\\$2">\\$1</a>\\$3 +. \} +. el \{\ +\\$1 \(la\fC\\$2\fP\(ra\\$3 +. \} +.. +.\" +.\" FTP - $1 is the classical underlined blue text +.\" $2 is the ftp url +.\" $3 is optional stuff printed immediately after $2 +.de FTP +. ie \\n[html-or-html-old] \{\ +. HTML <a href=\\$2>\\$1</a>\\$3 +. \} +. el \{\ +\\$1 \(la\fC\\$2\fP\(ra\\$3 +. \} +.. +.\" +.\" MAILTO - generate html email reference +.\" $1 is the email address (without the `mailto:' prefix) +.\" $2 is the optional name +.\" $3 is optional stuff printed immediately after $2 (resp. $1) +.\" +.\" example: +.\" +.\" Foobar has been written by +.\" .MAILTO fred@foo.bar "Fredrick Bloggs" . +.\" +.de MAILTO +.\" +.\" force reset after a potential heading by performing some motion.. +.\" how do we do this --fixme-- +.\" \h'\w' ''\h'-\w' '' doesn't work.. +. ie \\n[html-or-html-old] \{\ +. ie '\\$2'' \{\ +. HTML "<a href=mailto:\\$1>\\$1</a>\\$3" +. \} +. el \{\ +. HTML "<a href=mailto:\\$1>\\$2</a>\\$3" +. \} +. \} +. el \{\ +. ie '\\$2'' \{\ +\fC\\$1\fP\\$3 +. \} +. el \{\ +\\$2 \(la\fC\\$1\fP\(ra\\$3 +. \} +. \} +.. +.\" +.\" TAG - generate an html name $1 +.\" +.de TAG +. HTML <a name="\\$1"></a> +.. +.\" +.\" IMAGE - reference an image +.\" $1 is the image file +.\" $2 is the x width (default if absent 400 pixels) +.\" $3 is the y width (default if absent is the x value) +.\" +.de IMAGE +. ie \\n[html-or-html-old] \{\ +. nr HTMLWIDTH 400 +. if !'\\$2'' \{\ +. nr HTMLWIDTH \\$2 +. \} +. nr HTMLHEIGHT \\n[HTMLWIDTH] +. if !'\\$3'' \{\ +. nr HTMLHEIGHT \\$3 +. \} +. HTML-TAG ".centered-image" +. HTML <img src="\\$1" width=\\n[HTMLWIDTH] height=\\n[HTMLHEIGHT]> +. \} +. el \{\ +. B1 +\(la\fC\\$1\fP\(ra +. B2 +. \} +.. +.\" HTML-TAG - emit a tag for the new grohtml +.de HTML-TAG +. if '\*(.T'html' \{\ +.\" the following line makes the vertical mode leave, so to say +\& +\X^html-tag:\\$*^ +. \} +.. +.\" LINKS - emit the automatically collected links derived from section/numbered +.\" headings at this position. +.de LINKS +. HTML-TAG ".links" +.. +.\" +.\" LINE - produce a horizontal line +.\" +.de LINE +. HTML <hr> +.. +.\" +.\" supplimentary macros used by other macro sets +.\" +.\" here are some tags specially for -Tps or -Thtml when invoked by pre-html to +.\" generate png images from postscript. +.\" +.\" HTML-DO-IMAGE - tells troff to issue an image marker which can be read back by pre-html +.\" +.de HTML-DO-IMAGE +. if r ps4html .html-begin \{\ +. html-image \\$2 \\$1.png +. bp +. tl ''' +\O0\O1 +. \} +. if '\*(.T'html' .html-begin \{ +. html-image \\$2 \\$1.png +\O0 +. \} +.. +.\" +.\" HTML-IMAGE-END - terminates an image for html +.\" +.de HTML-IMAGE-END +. if r ps4html .html-end \{\ +\O2\O1 +. \} +. if '\*(.T'html' .html-end \{ +\O2\O1 +. \} +.. +.nr png-no 0 +.\" +.\" MAKE-UNIQUE-NAME - generates another unique name +.\" +.de MAKE-UNIQUE-NAME +. nr png-no \\n[png-no]+1 +. ds HTML-UNIQUE-NAME \\n(.F-auto-\\n[png-no] +.. +.\" +.\" HTML-IMAGE - is the same name as a tag generated from eqn/tbl/pic. +.\" Although the tags generated via the preprocessor +.\" are given image names by pre-html and troff. +.\" The macros below can only be invoked by *other macro sets* +.\" not user troff input since the contents of macro sets are +.\" not seen by pre-html. +.\" +.de HTML-IMAGE +.\" generates a centered image +. MAKE-UNIQUE-NAME +. HTML-DO-IMAGE \\*[HTML-UNIQUE-NAME] c +.. +.de HTML-IMAGE-RIGHT +. MAKE-UNIQUE-NAME +. HTML-DO-IMAGE \\*[HTML-UNIQUE-NAME] r +.. +.de HTML-IMAGE-LEFT +. MAKE-UNIQUE-NAME +. HTML-DO-IMAGE \\*[HTML-UNIQUE-NAME] l +.. +.de HTML-IMAGE-INLINE +. MAKE-UNIQUE-NAME +. HTML-DO-IMAGE \\*[HTML-UNIQUE-NAME] i +.. +.nr HY 0 +.nh |