PageRenderTime 175ms CodeModel.GetById 10ms app.highlight 145ms RepoModel.GetById 1ms app.codeStats 2ms

/contrib/groff/src/roff/troff/node.cpp

https://bitbucket.org/freebsd/freebsd-head/
C++ | 6482 lines | 5629 code | 741 blank | 112 comment | 944 complexity | e517a17900a0b49bc72513e34d6fdf9d MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1// -*- C++ -*-
   2/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005
   3   Free Software Foundation, Inc.
   4     Written by James Clark (jjc@jclark.com)
   5
   6This file is part of groff.
   7
   8groff is free software; you can redistribute it and/or modify it under
   9the terms of the GNU General Public License as published by the Free
  10Software Foundation; either version 2, or (at your option) any later
  11version.
  12
  13groff is distributed in the hope that it will be useful, but WITHOUT ANY
  14WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  16for more details.
  17
  18You should have received a copy of the GNU General Public License along
  19with groff; see the file COPYING.  If not, write to the Free Software
  20Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
  21
  22extern int debug_state;
  23
  24#include "troff.h"
  25
  26#ifdef HAVE_UNISTD_H
  27#include <unistd.h>
  28#endif
  29
  30#include "dictionary.h"
  31#include "hvunits.h"
  32#include "stringclass.h"
  33#include "mtsm.h"
  34#include "env.h"
  35#include "request.h"
  36#include "node.h"
  37#include "token.h"
  38#include "div.h"
  39#include "reg.h"
  40#include "charinfo.h"
  41#include "font.h"
  42#include "input.h"
  43#include "geometry.h"
  44
  45#include "nonposix.h"
  46
  47#ifdef _POSIX_VERSION
  48
  49#include <sys/wait.h>
  50
  51#else /* not _POSIX_VERSION */
  52
  53/* traditional Unix */
  54
  55#define WIFEXITED(s) (((s) & 0377) == 0)
  56#define WEXITSTATUS(s) (((s) >> 8) & 0377)
  57#define WTERMSIG(s) ((s) & 0177)
  58#define WIFSTOPPED(s) (((s) & 0377) == 0177)
  59#define WSTOPSIG(s) (((s) >> 8) & 0377)
  60#define WIFSIGNALED(s) (((s) & 0377) != 0 && (((s) & 0377) != 0177))
  61
  62#endif /* not _POSIX_VERSION */
  63
  64// declarations to avoid friend name injections
  65class tfont;
  66class tfont_spec;
  67tfont *make_tfont(tfont_spec &);
  68
  69
  70/*
  71 *  how many boundaries of images have been written? Useful for
  72 *  debugging grohtml
  73 */
  74
  75int image_no = 0;
  76static int suppress_start_page = 0;
  77
  78#define STORE_WIDTH 1
  79
  80symbol HYPHEN_SYMBOL("hy");
  81
  82// Character used when a hyphen is inserted at a line break.
  83static charinfo *soft_hyphen_char;
  84
  85enum constant_space_type {
  86  CONSTANT_SPACE_NONE,
  87  CONSTANT_SPACE_RELATIVE,
  88  CONSTANT_SPACE_ABSOLUTE
  89  };
  90
  91struct special_font_list {
  92  int n;
  93  special_font_list *next;
  94};
  95
  96special_font_list *global_special_fonts;
  97static int global_ligature_mode = 1;
  98static int global_kern_mode = 1;
  99
 100class track_kerning_function {
 101  int non_zero;
 102  units min_size;
 103  hunits min_amount;
 104  units max_size;
 105  hunits max_amount;
 106public:
 107  track_kerning_function();
 108  track_kerning_function(units, hunits, units, hunits);
 109  int operator==(const track_kerning_function &);
 110  int operator!=(const track_kerning_function &);
 111  hunits compute(int point_size);
 112};
 113
 114// embolden fontno when this is the current font
 115
 116struct conditional_bold {
 117  conditional_bold *next;
 118  int fontno;
 119  hunits offset;
 120  conditional_bold(int, hunits, conditional_bold * = 0);
 121};
 122
 123class font_info {
 124  tfont *last_tfont;
 125  int number;
 126  font_size last_size;
 127  int last_height;
 128  int last_slant;
 129  symbol internal_name;
 130  symbol external_name;
 131  font *fm;
 132  char is_bold;
 133  hunits bold_offset;
 134  track_kerning_function track_kern;
 135  constant_space_type is_constant_spaced;
 136  units constant_space;
 137  int last_ligature_mode;
 138  int last_kern_mode;
 139  conditional_bold *cond_bold_list;
 140  void flush();
 141public:
 142  special_font_list *sf;
 143  font_info(symbol, int, symbol, font *);
 144  int contains(charinfo *);
 145  void set_bold(hunits);
 146  void unbold();
 147  void set_conditional_bold(int, hunits);
 148  void conditional_unbold(int);
 149  void set_track_kern(track_kerning_function &);
 150  void set_constant_space(constant_space_type, units = 0);
 151  int is_named(symbol);
 152  symbol get_name();
 153  tfont *get_tfont(font_size, int, int, int);
 154  hunits get_space_width(font_size, int);
 155  hunits get_narrow_space_width(font_size);
 156  hunits get_half_narrow_space_width(font_size);
 157  int get_bold(hunits *);
 158  int is_special();
 159  int is_style();
 160  friend symbol get_font_name(int, environment *);
 161  friend symbol get_style_name(int);
 162};
 163
 164class tfont_spec {
 165protected:
 166  symbol name;
 167  int input_position;
 168  font *fm;
 169  font_size size;
 170  char is_bold;
 171  char is_constant_spaced;
 172  int ligature_mode;
 173  int kern_mode;
 174  hunits bold_offset;
 175  hunits track_kern;			// add this to the width
 176  hunits constant_space_width;
 177  int height;
 178  int slant;
 179public:
 180  tfont_spec(symbol, int, font *, font_size, int, int);
 181  tfont_spec(const tfont_spec &spec) { *this = spec; }
 182  tfont_spec plain();
 183  int operator==(const tfont_spec &);
 184  friend tfont *font_info::get_tfont(font_size fs, int, int, int);
 185};
 186
 187class tfont : public tfont_spec {
 188  static tfont *tfont_list;
 189  tfont *next;
 190  tfont *plain_version;
 191public:
 192  tfont(tfont_spec &);
 193  int contains(charinfo *);
 194  hunits get_width(charinfo *c);
 195  int get_bold(hunits *);
 196  int get_constant_space(hunits *);
 197  hunits get_track_kern();
 198  tfont *get_plain();
 199  font_size get_size();
 200  symbol get_name();
 201  charinfo *get_lig(charinfo *c1, charinfo *c2);
 202  int get_kern(charinfo *c1, charinfo *c2, hunits *res);
 203  int get_input_position();
 204  int get_character_type(charinfo *);
 205  int get_height();
 206  int get_slant();
 207  vunits get_char_height(charinfo *);
 208  vunits get_char_depth(charinfo *);
 209  hunits get_char_skew(charinfo *);
 210  hunits get_italic_correction(charinfo *);
 211  hunits get_left_italic_correction(charinfo *);
 212  hunits get_subscript_correction(charinfo *);
 213  friend tfont *make_tfont(tfont_spec &);
 214};
 215
 216inline int env_definite_font(environment *env)
 217{
 218  return env->get_family()->make_definite(env->get_font());
 219}
 220
 221/* font_info functions */
 222
 223static font_info **font_table = 0;
 224static int font_table_size = 0;
 225
 226font_info::font_info(symbol nm, int n, symbol enm, font *f)
 227: last_tfont(0), number(n), last_size(0),
 228  internal_name(nm), external_name(enm), fm(f),
 229  is_bold(0), is_constant_spaced(CONSTANT_SPACE_NONE), last_ligature_mode(1),
 230  last_kern_mode(1), cond_bold_list(0), sf(0)
 231{
 232}
 233
 234inline int font_info::contains(charinfo *ci)
 235{
 236  return fm != 0 && fm->contains(ci->get_index());
 237}
 238
 239inline int font_info::is_special()
 240{
 241  return fm != 0 && fm->is_special();
 242}
 243
 244inline int font_info::is_style()
 245{
 246  return fm == 0;
 247}
 248
 249tfont *make_tfont(tfont_spec &spec)
 250{
 251  for (tfont *p = tfont::tfont_list; p; p = p->next)
 252    if (*p == spec)
 253      return p;
 254  return new tfont(spec);
 255}
 256
 257// this is the current_font, fontno is where we found the character,
 258// presumably a special font
 259
 260tfont *font_info::get_tfont(font_size fs, int height, int slant, int fontno)
 261{
 262  if (last_tfont == 0 || fs != last_size
 263      || height != last_height || slant != last_slant
 264      || global_ligature_mode != last_ligature_mode
 265      || global_kern_mode != last_kern_mode
 266      || fontno != number) {
 267	font_info *f = font_table[fontno];
 268	tfont_spec spec(f->external_name, f->number, f->fm, fs, height, slant);
 269	for (conditional_bold *p = cond_bold_list; p; p = p->next)
 270	  if (p->fontno == fontno) {
 271	    spec.is_bold = 1;
 272	    spec.bold_offset = p->offset;
 273	    break;
 274	  }
 275	if (!spec.is_bold && is_bold) {
 276	  spec.is_bold = 1;
 277	  spec.bold_offset = bold_offset;
 278	}
 279	spec.track_kern = track_kern.compute(fs.to_scaled_points());
 280	spec.ligature_mode = global_ligature_mode;
 281	spec.kern_mode = global_kern_mode;
 282	switch (is_constant_spaced) {
 283	case CONSTANT_SPACE_NONE:
 284	  break;
 285	case CONSTANT_SPACE_ABSOLUTE:
 286	  spec.is_constant_spaced = 1;
 287	  spec.constant_space_width = constant_space;
 288	  break;
 289	case CONSTANT_SPACE_RELATIVE:
 290	  spec.is_constant_spaced = 1;
 291	  spec.constant_space_width
 292	    = scale(constant_space*fs.to_scaled_points(),
 293		    units_per_inch,
 294		    36*72*sizescale);
 295	  break;
 296	default:
 297	  assert(0);
 298	}
 299	if (fontno != number)
 300	  return make_tfont(spec);
 301	last_tfont = make_tfont(spec);
 302	last_size = fs;
 303	last_height = height;
 304	last_slant = slant;
 305	last_ligature_mode = global_ligature_mode;
 306	last_kern_mode = global_kern_mode;
 307      }
 308  return last_tfont;
 309}
 310
 311int font_info::get_bold(hunits *res)
 312{
 313  if (is_bold) {
 314    *res = bold_offset;
 315    return 1;
 316  }
 317  else
 318    return 0;
 319}
 320
 321void font_info::unbold()
 322{
 323  if (is_bold) {
 324    is_bold = 0;
 325    flush();
 326  }
 327}
 328
 329void font_info::set_bold(hunits offset)
 330{
 331  if (!is_bold || offset != bold_offset) {
 332    is_bold = 1;
 333    bold_offset = offset;
 334    flush();
 335  }
 336}
 337
 338void font_info::set_conditional_bold(int fontno, hunits offset)
 339{
 340  for (conditional_bold *p = cond_bold_list; p; p = p->next)
 341    if (p->fontno == fontno) {
 342      if (offset != p->offset) {
 343	p->offset = offset;
 344	flush();
 345      }
 346      return;
 347    }
 348  cond_bold_list = new conditional_bold(fontno, offset, cond_bold_list);
 349}
 350
 351conditional_bold::conditional_bold(int f, hunits h, conditional_bold *x)
 352: next(x), fontno(f), offset(h)
 353{
 354}
 355
 356void font_info::conditional_unbold(int fontno)
 357{
 358  for (conditional_bold **p = &cond_bold_list; *p; p = &(*p)->next)
 359    if ((*p)->fontno == fontno) {
 360      conditional_bold *tem = *p;
 361      *p = (*p)->next;
 362      delete tem;
 363      flush();
 364      return;
 365    }
 366}
 367
 368void font_info::set_constant_space(constant_space_type type, units x)
 369{
 370  if (type != is_constant_spaced
 371      || (type != CONSTANT_SPACE_NONE && x != constant_space)) {
 372    flush();
 373    is_constant_spaced = type;
 374    constant_space = x;
 375  }
 376}
 377
 378void font_info::set_track_kern(track_kerning_function &tk)
 379{
 380  if (track_kern != tk) {
 381    track_kern = tk;
 382    flush();
 383  }
 384}
 385
 386void font_info::flush()
 387{
 388  last_tfont = 0;
 389}
 390
 391int font_info::is_named(symbol s)
 392{
 393  return internal_name == s;
 394}
 395
 396symbol font_info::get_name()
 397{
 398  return internal_name;
 399}
 400
 401symbol get_font_name(int fontno, environment *env)
 402{
 403  symbol f = font_table[fontno]->get_name();
 404  if (font_table[fontno]->is_style()) {
 405    return concat(env->get_family()->nm, f);
 406  }
 407  return f;
 408}
 409
 410symbol get_style_name(int fontno)
 411{
 412  if (font_table[fontno]->is_style())
 413    return font_table[fontno]->get_name();
 414  else
 415    return EMPTY_SYMBOL;
 416}
 417
 418hunits font_info::get_space_width(font_size fs, int space_sz)
 419{
 420  if (is_constant_spaced == CONSTANT_SPACE_NONE)
 421    return scale(hunits(fm->get_space_width(fs.to_scaled_points())),
 422			space_sz, 12);
 423  else if (is_constant_spaced == CONSTANT_SPACE_ABSOLUTE)
 424    return constant_space;
 425  else
 426    return scale(constant_space*fs.to_scaled_points(),
 427		 units_per_inch, 36*72*sizescale);
 428}
 429
 430hunits font_info::get_narrow_space_width(font_size fs)
 431{
 432  charinfo *ci = get_charinfo(symbol("|"));
 433  if (fm->contains(ci->get_index()))
 434    return hunits(fm->get_width(ci->get_index(), fs.to_scaled_points()));
 435  else
 436    return hunits(fs.to_units()/6);
 437}
 438
 439hunits font_info::get_half_narrow_space_width(font_size fs)
 440{
 441  charinfo *ci = get_charinfo(symbol("^"));
 442  if (fm->contains(ci->get_index()))
 443    return hunits(fm->get_width(ci->get_index(), fs.to_scaled_points()));
 444  else
 445    return hunits(fs.to_units()/12);
 446}
 447
 448/* tfont */
 449
 450tfont_spec::tfont_spec(symbol nm, int n, font *f,
 451		       font_size s, int h, int sl)
 452: name(nm), input_position(n), fm(f), size(s),
 453  is_bold(0), is_constant_spaced(0), ligature_mode(1), kern_mode(1),
 454  height(h), slant(sl)
 455{
 456  if (height == size.to_scaled_points())
 457    height = 0;
 458}
 459
 460int tfont_spec::operator==(const tfont_spec &spec)
 461{
 462  if (fm == spec.fm
 463      && size == spec.size
 464      && input_position == spec.input_position
 465      && name == spec.name
 466      && height == spec.height
 467      && slant == spec.slant
 468      && (is_bold
 469	  ? (spec.is_bold && bold_offset == spec.bold_offset)
 470	  : !spec.is_bold)
 471      && track_kern == spec.track_kern
 472      && (is_constant_spaced
 473	  ? (spec.is_constant_spaced
 474	     && constant_space_width == spec.constant_space_width)
 475	  : !spec.is_constant_spaced)
 476      && ligature_mode == spec.ligature_mode
 477      && kern_mode == spec.kern_mode)
 478    return 1;
 479  else
 480    return 0;
 481}
 482
 483tfont_spec tfont_spec::plain()
 484{
 485  return tfont_spec(name, input_position, fm, size, height, slant);
 486}
 487
 488hunits tfont::get_width(charinfo *c)
 489{
 490  if (is_constant_spaced)
 491    return constant_space_width;
 492  else if (is_bold)
 493    return (hunits(fm->get_width(c->get_index(), size.to_scaled_points()))
 494	    + track_kern + bold_offset);
 495  else
 496    return (hunits(fm->get_width(c->get_index(), size.to_scaled_points()))
 497	    + track_kern);
 498}
 499
 500vunits tfont::get_char_height(charinfo *c)
 501{
 502  vunits v = fm->get_height(c->get_index(), size.to_scaled_points());
 503  if (height != 0 && height != size.to_scaled_points())
 504    return scale(v, height, size.to_scaled_points());
 505  else
 506    return v;
 507}
 508
 509vunits tfont::get_char_depth(charinfo *c)
 510{
 511  vunits v = fm->get_depth(c->get_index(), size.to_scaled_points());
 512  if (height != 0 && height != size.to_scaled_points())
 513    return scale(v, height, size.to_scaled_points());
 514  else
 515    return v;
 516}
 517
 518hunits tfont::get_char_skew(charinfo *c)
 519{
 520  return hunits(fm->get_skew(c->get_index(), size.to_scaled_points(), slant));
 521}
 522
 523hunits tfont::get_italic_correction(charinfo *c)
 524{
 525  return hunits(fm->get_italic_correction(c->get_index(), size.to_scaled_points()));
 526}
 527
 528hunits tfont::get_left_italic_correction(charinfo *c)
 529{
 530  return hunits(fm->get_left_italic_correction(c->get_index(),
 531					       size.to_scaled_points()));
 532}
 533
 534hunits tfont::get_subscript_correction(charinfo *c)
 535{
 536  return hunits(fm->get_subscript_correction(c->get_index(),
 537					     size.to_scaled_points()));
 538}
 539
 540inline int tfont::get_input_position()
 541{
 542  return input_position;
 543}
 544
 545inline int tfont::contains(charinfo *ci)
 546{
 547  return fm->contains(ci->get_index());
 548}
 549
 550inline int tfont::get_character_type(charinfo *ci)
 551{
 552  return fm->get_character_type(ci->get_index());
 553}
 554
 555inline int tfont::get_bold(hunits *res)
 556{
 557  if (is_bold) {
 558    *res = bold_offset;
 559    return 1;
 560  }
 561  else
 562    return 0;
 563}
 564
 565inline int tfont::get_constant_space(hunits *res)
 566{
 567  if (is_constant_spaced) {
 568    *res = constant_space_width;
 569    return 1;
 570  }
 571  else
 572    return 0;
 573}
 574
 575inline hunits tfont::get_track_kern()
 576{
 577  return track_kern;
 578}
 579
 580inline tfont *tfont::get_plain()
 581{
 582  return plain_version;
 583}
 584
 585inline font_size tfont::get_size()
 586{
 587  return size;
 588}
 589
 590inline symbol tfont::get_name()
 591{
 592  return name;
 593}
 594
 595inline int tfont::get_height()
 596{
 597  return height;
 598}
 599
 600inline int tfont::get_slant()
 601{
 602  return slant;
 603}
 604
 605symbol SYMBOL_ff("ff");
 606symbol SYMBOL_fi("fi");
 607symbol SYMBOL_fl("fl");
 608symbol SYMBOL_Fi("Fi");
 609symbol SYMBOL_Fl("Fl");
 610
 611charinfo *tfont::get_lig(charinfo *c1, charinfo *c2)
 612{
 613  if (ligature_mode == 0)
 614    return 0;
 615  charinfo *ci = 0;
 616  if (c1->get_ascii_code() == 'f') {
 617    switch (c2->get_ascii_code()) {
 618    case 'f':
 619      if (fm->has_ligature(font::LIG_ff))
 620	ci = get_charinfo(SYMBOL_ff);
 621      break;
 622    case 'i':
 623      if (fm->has_ligature(font::LIG_fi))
 624	ci = get_charinfo(SYMBOL_fi);
 625      break;
 626    case 'l':
 627      if (fm->has_ligature(font::LIG_fl))
 628	ci = get_charinfo(SYMBOL_fl);
 629      break;
 630    }
 631  }
 632  else if (ligature_mode != 2 && c1->nm == SYMBOL_ff) {
 633    switch (c2->get_ascii_code()) {
 634    case 'i':
 635      if (fm->has_ligature(font::LIG_ffi))
 636	ci = get_charinfo(SYMBOL_Fi);
 637      break;
 638    case 'l':
 639      if (fm->has_ligature(font::LIG_ffl))
 640	ci = get_charinfo(SYMBOL_Fl);
 641      break;
 642    }
 643  }
 644  if (ci != 0 && fm->contains(ci->get_index()))
 645    return ci;
 646  return 0;
 647}
 648
 649inline int tfont::get_kern(charinfo *c1, charinfo *c2, hunits *res)
 650{
 651  if (kern_mode == 0)
 652    return 0;
 653  else {
 654    int n = fm->get_kern(c1->get_index(),
 655			 c2->get_index(),
 656			 size.to_scaled_points());
 657    if (n) {
 658      *res = hunits(n);
 659      return 1;
 660    }
 661    else
 662      return 0;
 663  }
 664}
 665
 666tfont *tfont::tfont_list = 0;
 667
 668tfont::tfont(tfont_spec &spec) : tfont_spec(spec)
 669{
 670  next = tfont_list;
 671  tfont_list = this;
 672  tfont_spec plain_spec = plain();
 673  tfont *p;
 674  for (p = tfont_list; p; p = p->next)
 675    if (*p == plain_spec) {
 676      plain_version = p;
 677      break;
 678    }
 679  if (!p)
 680    plain_version = new tfont(plain_spec);
 681}
 682
 683/* output_file */
 684
 685class real_output_file : public output_file {
 686#ifndef POPEN_MISSING
 687  int piped;
 688#endif
 689  int printing;        // decision via optional page list
 690  int output_on;       // \O[0] or \O[1] escape calls
 691  virtual void really_transparent_char(unsigned char) = 0;
 692  virtual void really_print_line(hunits x, vunits y, node *n,
 693				 vunits before, vunits after, hunits width) = 0;
 694  virtual void really_begin_page(int pageno, vunits page_length) = 0;
 695  virtual void really_copy_file(hunits x, vunits y, const char *filename);
 696  virtual void really_put_filename(const char *filename);
 697  virtual void really_on();
 698  virtual void really_off();
 699public:
 700  FILE *fp;
 701  real_output_file();
 702  ~real_output_file();
 703  void flush();
 704  void transparent_char(unsigned char);
 705  void print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width);
 706  void begin_page(int pageno, vunits page_length);
 707  void put_filename(const char *filename);
 708  void on();
 709  void off();
 710  int is_on();
 711  int is_printing();
 712  void copy_file(hunits x, vunits y, const char *filename);
 713};
 714
 715class suppress_output_file : public real_output_file {
 716public:
 717  suppress_output_file();
 718  void really_transparent_char(unsigned char);
 719  void really_print_line(hunits x, vunits y, node *n, vunits, vunits, hunits width);
 720  void really_begin_page(int pageno, vunits page_length);
 721};
 722
 723class ascii_output_file : public real_output_file {
 724public:
 725  ascii_output_file();
 726  void really_transparent_char(unsigned char);
 727  void really_print_line(hunits x, vunits y, node *n, vunits, vunits, hunits width);
 728  void really_begin_page(int pageno, vunits page_length);
 729  void outc(unsigned char c);
 730  void outs(const char *s);
 731};
 732
 733void ascii_output_file::outc(unsigned char c)
 734{
 735  fputc(c, fp);
 736}
 737
 738void ascii_output_file::outs(const char *s)
 739{
 740  fputc('<', fp);
 741  if (s)
 742    fputs(s, fp);
 743  fputc('>', fp);
 744}
 745
 746struct hvpair;
 747
 748class troff_output_file : public real_output_file {
 749  units hpos;
 750  units vpos;
 751  units output_vpos;
 752  units output_hpos;
 753  int force_motion;
 754  int current_size;
 755  int current_slant;
 756  int current_height;
 757  tfont *current_tfont;
 758  color *current_fill_color;
 759  color *current_glyph_color;
 760  int current_font_number;
 761  symbol *font_position;
 762  int nfont_positions;
 763  enum { TBUF_SIZE = 256 };
 764  char tbuf[TBUF_SIZE];
 765  int tbuf_len;
 766  int tbuf_kern;
 767  int begun_page;
 768  int cur_div_level;
 769  string tag_list;
 770  void do_motion();
 771  void put(char c);
 772  void put(unsigned char c);
 773  void put(int i);
 774  void put(unsigned int i);
 775  void put(const char *s);
 776  void set_font(tfont *tf);
 777  void flush_tbuf();
 778public:
 779  troff_output_file();
 780  ~troff_output_file();
 781  void trailer(vunits page_length);
 782  void put_char(charinfo *, tfont *, color *, color *);
 783  void put_char_width(charinfo *, tfont *, color *, color *, hunits, hunits);
 784  void right(hunits);
 785  void down(vunits);
 786  void moveto(hunits, vunits);
 787  void start_special(tfont *, color *, color *, int = 0);
 788  void start_special();
 789  void special_char(unsigned char c);
 790  void end_special();
 791  void word_marker();
 792  void really_transparent_char(unsigned char c);
 793  void really_print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width);
 794  void really_begin_page(int pageno, vunits page_length);
 795  void really_copy_file(hunits x, vunits y, const char *filename);
 796  void really_put_filename(const char *filename);
 797  void really_on();
 798  void really_off();
 799  void draw(char, hvpair *, int, font_size, color *, color *);
 800  void determine_line_limits (char code, hvpair *point, int npoints);
 801  void check_charinfo(tfont *tf, charinfo *ci);
 802  void glyph_color(color *c);
 803  void fill_color(color *c);
 804  int get_hpos() { return hpos; }
 805  int get_vpos() { return vpos; }
 806  void add_to_tag_list(string s);
 807  friend void space_char_hmotion_node::tprint(troff_output_file *);
 808  friend void unbreakable_space_node::tprint(troff_output_file *);
 809};
 810
 811static void put_string(const char *s, FILE *fp)
 812{
 813  for (; *s != '\0'; ++s)
 814    putc(*s, fp);
 815}
 816
 817inline void troff_output_file::put(char c)
 818{
 819  putc(c, fp);
 820}
 821
 822inline void troff_output_file::put(unsigned char c)
 823{
 824  putc(c, fp);
 825}
 826
 827inline void troff_output_file::put(const char *s)
 828{
 829  put_string(s, fp);
 830}
 831
 832inline void troff_output_file::put(int i)
 833{
 834  put_string(i_to_a(i), fp);
 835}
 836
 837inline void troff_output_file::put(unsigned int i)
 838{
 839  put_string(ui_to_a(i), fp);
 840}
 841
 842void troff_output_file::start_special(tfont *tf, color *gcol, color *fcol,
 843				      int no_init_string)
 844{
 845  set_font(tf);
 846  glyph_color(gcol);
 847  fill_color(fcol);
 848  flush_tbuf();
 849  do_motion();
 850  if (!no_init_string)
 851    put("x X ");
 852}
 853
 854void troff_output_file::start_special()
 855{
 856  flush_tbuf();
 857  do_motion();
 858  put("x X ");
 859}
 860
 861void troff_output_file::special_char(unsigned char c)
 862{
 863  put(c);
 864  if (c == '\n')
 865    put('+');
 866}
 867
 868void troff_output_file::end_special()
 869{
 870  put('\n');
 871}
 872
 873inline void troff_output_file::moveto(hunits h, vunits v)
 874{
 875  hpos = h.to_units();
 876  vpos = v.to_units();
 877}
 878
 879void troff_output_file::really_print_line(hunits x, vunits y, node *n,
 880					  vunits before, vunits after, hunits)
 881{
 882  moveto(x, y);
 883  while (n != 0) {
 884    // Check whether we should push the current troff state and use
 885    // the state at the start of the invocation of this diversion.
 886    if (n->div_nest_level > cur_div_level && n->push_state) {
 887      state.push_state(n->push_state);
 888      cur_div_level = n->div_nest_level;
 889    }
 890    // Has the current diversion level decreased?  Then we must pop the
 891    // troff state.
 892    while (n->div_nest_level < cur_div_level) {
 893      state.pop_state();
 894      cur_div_level = n->div_nest_level;
 895    }
 896    // Now check whether the state has changed.
 897    if ((is_on() || n->force_tprint())
 898	&& (state.changed(n->state) || n->is_tag() || n->is_special)) {
 899      flush_tbuf();
 900      do_motion();
 901      force_motion = 1;
 902      flush();
 903      state.flush(fp, n->state, tag_list);
 904      tag_list = string("");
 905      flush();
 906    }
 907    n->tprint(this);
 908    n = n->next;
 909  }
 910  flush_tbuf();
 911  // This ensures that transparent throughput will have a more predictable
 912  // position.
 913  do_motion();
 914  force_motion = 1;
 915  hpos = 0;
 916  put('n');
 917  put(before.to_units());
 918  put(' ');
 919  put(after.to_units());
 920  put('\n');
 921}
 922
 923inline void troff_output_file::word_marker()
 924{
 925  flush_tbuf();
 926  if (is_on())
 927    put('w');
 928}
 929
 930inline void troff_output_file::right(hunits n)
 931{
 932  hpos += n.to_units();
 933}
 934
 935inline void troff_output_file::down(vunits n)
 936{
 937  vpos += n.to_units();
 938}
 939
 940void troff_output_file::do_motion()
 941{
 942  if (force_motion) {
 943    put('V');
 944    put(vpos);
 945    put('\n');
 946    put('H');
 947    put(hpos);
 948    put('\n');
 949  }
 950  else {
 951    if (hpos != output_hpos) {
 952      units n = hpos - output_hpos;
 953      if (n > 0 && n < hpos) {
 954	put('h');
 955	put(n);
 956      }
 957      else {
 958	put('H');
 959	put(hpos);
 960      }
 961      put('\n');
 962    }
 963    if (vpos != output_vpos) {
 964      units n = vpos - output_vpos;
 965      if (n > 0 && n < vpos) {
 966	put('v');
 967	put(n);
 968      }
 969      else {
 970	put('V');
 971	put(vpos);
 972      }
 973      put('\n');
 974    }
 975  }
 976  output_vpos = vpos;
 977  output_hpos = hpos;
 978  force_motion = 0;
 979}
 980
 981void troff_output_file::flush_tbuf()
 982{
 983  if (!is_on()) {
 984    tbuf_len = 0;
 985    return;
 986  }
 987
 988  if (tbuf_len == 0)
 989    return;
 990  if (tbuf_kern == 0)
 991    put('t');
 992  else {
 993    put('u');
 994    put(tbuf_kern);
 995    put(' ');
 996  }
 997  check_output_limits(hpos, vpos);
 998  check_output_limits(hpos, vpos - current_size);
 999
1000  for (int i = 0; i < tbuf_len; i++)
1001    put(tbuf[i]);
1002  put('\n');
1003  tbuf_len = 0;
1004}
1005
1006void troff_output_file::check_charinfo(tfont *tf, charinfo *ci)
1007{
1008  if (!is_on())
1009    return;
1010
1011  int height = tf->get_char_height(ci).to_units();
1012  int width = tf->get_width(ci).to_units()
1013	      + tf->get_italic_correction(ci).to_units();
1014  int depth = tf->get_char_depth(ci).to_units();
1015  check_output_limits(output_hpos, output_vpos - height);
1016  check_output_limits(output_hpos + width, output_vpos + depth);
1017}
1018
1019void troff_output_file::put_char_width(charinfo *ci, tfont *tf,
1020				       color *gcol, color *fcol,
1021				       hunits w, hunits k)
1022{
1023  int kk = k.to_units();
1024  if (!is_on()) {
1025    flush_tbuf();
1026    hpos += w.to_units() + kk;
1027    return;
1028  }
1029  set_font(tf);
1030  unsigned char c = ci->get_ascii_code();
1031  if (c == '\0') {
1032    glyph_color(gcol);
1033    fill_color(fcol);
1034    flush_tbuf();
1035    do_motion();
1036    check_charinfo(tf, ci);
1037    if (ci->numbered()) {
1038      put('N');
1039      put(ci->get_number());
1040    }
1041    else {
1042      put('C');
1043      const char *s = ci->nm.contents();
1044      if (s[1] == 0) {
1045	put('\\');
1046	put(s[0]);
1047      }
1048      else
1049	put(s);
1050    }
1051    put('\n');
1052    hpos += w.to_units() + kk;
1053  }
1054  else if (tcommand_flag) {
1055    if (tbuf_len > 0 && hpos == output_hpos && vpos == output_vpos
1056	&& (!gcol || gcol == current_glyph_color)
1057	&& (!fcol || fcol == current_fill_color)
1058	&& kk == tbuf_kern
1059	&& tbuf_len < TBUF_SIZE) {
1060      check_charinfo(tf, ci);
1061      tbuf[tbuf_len++] = c;
1062      output_hpos += w.to_units() + kk;
1063      hpos = output_hpos;
1064      return;
1065    }
1066    glyph_color(gcol);
1067    fill_color(fcol);
1068    flush_tbuf();
1069    do_motion();
1070    check_charinfo(tf, ci);
1071    tbuf[tbuf_len++] = c;
1072    output_hpos += w.to_units() + kk;
1073    tbuf_kern = kk;
1074    hpos = output_hpos;
1075  }
1076  else {
1077    // flush_tbuf();
1078    int n = hpos - output_hpos;
1079    check_charinfo(tf, ci);
1080    // check_output_limits(output_hpos, output_vpos);
1081    if (vpos == output_vpos
1082	&& (!gcol || gcol == current_glyph_color)
1083	&& (!fcol || fcol == current_fill_color)
1084	&& n > 0 && n < 100 && !force_motion) {
1085      put(char(n/10 + '0'));
1086      put(char(n%10 + '0'));
1087      put(c);
1088      output_hpos = hpos;
1089    }
1090    else {
1091      glyph_color(gcol);
1092      fill_color(fcol);
1093      do_motion();
1094      put('c');
1095      put(c);
1096    }
1097    hpos += w.to_units() + kk;
1098  }
1099}
1100
1101void troff_output_file::put_char(charinfo *ci, tfont *tf,
1102				 color *gcol, color *fcol)
1103{
1104  flush_tbuf();
1105  if (!is_on())
1106    return;
1107  set_font(tf);
1108  unsigned char c = ci->get_ascii_code();
1109  if (c == '\0') {
1110    glyph_color(gcol);
1111    fill_color(fcol);
1112    flush_tbuf();
1113    do_motion();
1114    if (ci->numbered()) {
1115      put('N');
1116      put(ci->get_number());
1117    }
1118    else {
1119      put('C');
1120      const char *s = ci->nm.contents();
1121      if (s[1] == 0) {
1122	put('\\');
1123	put(s[0]);
1124      }
1125      else
1126	put(s);
1127    }
1128    put('\n');
1129  }
1130  else {
1131    int n = hpos - output_hpos;
1132    if (vpos == output_vpos
1133	&& (!gcol || gcol == current_glyph_color)
1134	&& (!fcol || fcol == current_fill_color)
1135	&& n > 0 && n < 100) {
1136      put(char(n/10 + '0'));
1137      put(char(n%10 + '0'));
1138      put(c);
1139      output_hpos = hpos;
1140    }
1141    else {
1142      glyph_color(gcol);
1143      fill_color(fcol);
1144      flush_tbuf();
1145      do_motion();
1146      put('c');
1147      put(c);
1148    }
1149  }
1150}
1151
1152// set_font calls `flush_tbuf' if necessary.
1153
1154void troff_output_file::set_font(tfont *tf)
1155{
1156  if (current_tfont == tf)
1157    return;
1158  flush_tbuf();
1159  int n = tf->get_input_position();
1160  symbol nm = tf->get_name();
1161  if (n >= nfont_positions || font_position[n] != nm) {
1162    put("x font ");
1163    put(n);
1164    put(' ');
1165    put(nm.contents());
1166    put('\n');
1167    if (n >= nfont_positions) {
1168      int old_nfont_positions = nfont_positions;
1169      symbol *old_font_position = font_position;
1170      nfont_positions *= 3;
1171      nfont_positions /= 2;
1172      if (nfont_positions <= n)
1173	nfont_positions = n + 10;
1174      font_position = new symbol[nfont_positions];
1175      memcpy(font_position, old_font_position,
1176	     old_nfont_positions*sizeof(symbol));
1177      a_delete old_font_position;
1178    }
1179    font_position[n] = nm;
1180  }
1181  if (current_font_number != n) {
1182    put('f');
1183    put(n);
1184    put('\n');
1185    current_font_number = n;
1186  }
1187  int size = tf->get_size().to_scaled_points();
1188  if (current_size != size) {
1189    put('s');
1190    put(size);
1191    put('\n');
1192    current_size = size;
1193  }
1194  int slant = tf->get_slant();
1195  if (current_slant != slant) {
1196    put("x Slant ");
1197    put(slant);
1198    put('\n');
1199    current_slant = slant;
1200  }
1201  int height = tf->get_height();
1202  if (current_height != height) {
1203    put("x Height ");
1204    put(height == 0 ? current_size : height);
1205    put('\n');
1206    current_height = height;
1207  }
1208  current_tfont = tf;
1209}
1210
1211// fill_color calls `flush_tbuf' and `do_motion' if necessary.
1212
1213void troff_output_file::fill_color(color *col)
1214{
1215  if (!col || current_fill_color == col)
1216    return;
1217  current_fill_color = col;
1218  if (!color_flag)
1219    return;
1220  flush_tbuf();
1221  do_motion();
1222  put("DF");
1223  unsigned int components[4];
1224  color_scheme cs;
1225  cs = col->get_components(components);
1226  switch (cs) {
1227  case DEFAULT:
1228    put('d');
1229    break;
1230  case RGB:
1231    put("r ");
1232    put(Red);
1233    put(' ');
1234    put(Green);
1235    put(' ');
1236    put(Blue);
1237    break;
1238  case CMY:
1239    put("c ");
1240    put(Cyan);
1241    put(' ');
1242    put(Magenta);
1243    put(' ');
1244    put(Yellow);
1245    break;
1246  case CMYK:
1247    put("k ");
1248    put(Cyan);
1249    put(' ');
1250    put(Magenta);
1251    put(' ');
1252    put(Yellow);
1253    put(' ');
1254    put(Black);
1255    break;
1256  case GRAY:
1257    put("g ");
1258    put(Gray);
1259    break;
1260  }
1261  put('\n');
1262}
1263
1264// glyph_color calls `flush_tbuf' and `do_motion' if necessary.
1265
1266void troff_output_file::glyph_color(color *col)
1267{
1268  if (!col || current_glyph_color == col)
1269    return;
1270  current_glyph_color = col;
1271  if (!color_flag)
1272    return;
1273  flush_tbuf();
1274  // grotty doesn't like a color command if the vertical position is zero.
1275  do_motion();
1276  put("m");
1277  unsigned int components[4];
1278  color_scheme cs;
1279  cs = col->get_components(components);
1280  switch (cs) {
1281  case DEFAULT:
1282    put('d');
1283    break;
1284  case RGB:
1285    put("r ");
1286    put(Red);
1287    put(' ');
1288    put(Green);
1289    put(' ');
1290    put(Blue);
1291    break;
1292  case CMY:
1293    put("c ");
1294    put(Cyan);
1295    put(' ');
1296    put(Magenta);
1297    put(' ');
1298    put(Yellow);
1299    break;
1300  case CMYK:
1301    put("k ");
1302    put(Cyan);
1303    put(' ');
1304    put(Magenta);
1305    put(' ');
1306    put(Yellow);
1307    put(' ');
1308    put(Black);
1309    break;
1310  case GRAY:
1311    put("g ");
1312    put(Gray);
1313    break;
1314  }
1315  put('\n');
1316}
1317
1318void troff_output_file::add_to_tag_list(string s)
1319{
1320  if (tag_list == string(""))
1321    tag_list = s;
1322  else {
1323    tag_list += string("\n");
1324    tag_list += s;
1325  }
1326}
1327
1328// determine_line_limits - works out the smallest box which will contain
1329//			   the entity, code, built from the point array.
1330void troff_output_file::determine_line_limits(char code, hvpair *point,
1331					      int npoints)
1332{
1333  int i, x, y;
1334
1335  if (!is_on())
1336    return;
1337
1338  switch (code) {
1339  case 'c':
1340  case 'C':
1341    // only the h field is used when defining a circle
1342    check_output_limits(output_hpos,
1343			output_vpos - point[0].h.to_units()/2);
1344    check_output_limits(output_hpos + point[0].h.to_units(),
1345			output_vpos + point[0].h.to_units()/2);
1346    break;
1347  case 'E':
1348  case 'e':
1349    check_output_limits(output_hpos,
1350			output_vpos - point[0].v.to_units()/2);
1351    check_output_limits(output_hpos + point[0].h.to_units(),
1352			output_vpos + point[0].v.to_units()/2);
1353    break;
1354  case 'P':
1355  case 'p':
1356    x = output_hpos;
1357    y = output_vpos;
1358    check_output_limits(x, y);
1359    for (i = 0; i < npoints; i++) {
1360      x += point[i].h.to_units();
1361      y += point[i].v.to_units();
1362      check_output_limits(x, y);
1363    }
1364    break;
1365  case 't':
1366    x = output_hpos;
1367    y = output_vpos;
1368    for (i = 0; i < npoints; i++) {
1369      x += point[i].h.to_units();
1370      y += point[i].v.to_units();
1371      check_output_limits(x, y);
1372    }
1373    break;
1374  case 'a':
1375    double c[2];
1376    int p[4];
1377    int minx, miny, maxx, maxy;
1378    x = output_hpos;
1379    y = output_vpos;
1380    p[0] = point[0].h.to_units();
1381    p[1] = point[0].v.to_units();
1382    p[2] = point[1].h.to_units();
1383    p[3] = point[1].v.to_units();
1384    if (adjust_arc_center(p, c)) {
1385      check_output_arc_limits(x, y,
1386			      p[0], p[1], p[2], p[3],
1387			      c[0], c[1],
1388			      &minx, &maxx, &miny, &maxy);
1389      check_output_limits(minx, miny);
1390      check_output_limits(maxx, maxy);
1391      break;
1392    }
1393    // fall through
1394  case 'l':
1395    x = output_hpos;
1396    y = output_vpos;
1397    check_output_limits(x, y);
1398    for (i = 0; i < npoints; i++) {
1399      x += point[i].h.to_units();
1400      y += point[i].v.to_units();
1401      check_output_limits(x, y);
1402    }
1403    break;
1404  default:
1405    x = output_hpos;
1406    y = output_vpos;
1407    for (i = 0; i < npoints; i++) {
1408      x += point[i].h.to_units();
1409      y += point[i].v.to_units();
1410      check_output_limits(x, y);
1411    }
1412  }
1413}
1414
1415void troff_output_file::draw(char code, hvpair *point, int npoints,
1416			     font_size fsize, color *gcol, color *fcol)
1417{
1418  int i;
1419  glyph_color(gcol);
1420  fill_color(fcol);
1421  flush_tbuf();
1422  do_motion();
1423  if (is_on()) {
1424    int size = fsize.to_scaled_points();
1425    if (current_size != size) {
1426      put('s');
1427      put(size);
1428      put('\n');
1429      current_size = size;
1430      current_tfont = 0;
1431    }
1432    put('D');
1433    put(code);
1434    if (code == 'c') {
1435      put(' ');
1436      put(point[0].h.to_units());
1437    }
1438    else
1439      for (i = 0; i < npoints; i++) {
1440	put(' ');
1441	put(point[i].h.to_units());
1442	put(' ');
1443	put(point[i].v.to_units());
1444      }
1445    determine_line_limits(code, point, npoints);
1446  }
1447
1448  for (i = 0; i < npoints; i++)
1449    output_hpos += point[i].h.to_units();
1450  hpos = output_hpos;
1451  if (code != 'e') {
1452    for (i = 0; i < npoints; i++)
1453      output_vpos += point[i].v.to_units();
1454    vpos = output_vpos;
1455  }
1456  if (is_on())
1457    put('\n');
1458}
1459
1460void troff_output_file::really_on()
1461{
1462  flush_tbuf();
1463  force_motion = 1;
1464  do_motion();
1465}
1466
1467void troff_output_file::really_off()
1468{
1469  flush_tbuf();
1470}
1471
1472void troff_output_file::really_put_filename(const char *filename)
1473{
1474  flush_tbuf();
1475  put("F ");
1476  put(filename);
1477  put('\n');
1478}
1479
1480void troff_output_file::really_begin_page(int pageno, vunits page_length)
1481{
1482  flush_tbuf();
1483  if (begun_page) {
1484    if (page_length > V0) {
1485      put('V');
1486      put(page_length.to_units());
1487      put('\n');
1488    }
1489  }
1490  else
1491    begun_page = 1;
1492  current_tfont = 0;
1493  current_font_number = -1;
1494  current_size = 0;
1495  // current_height = 0;
1496  // current_slant = 0;
1497  hpos = 0;
1498  vpos = 0;
1499  output_hpos = 0;
1500  output_vpos = 0;
1501  force_motion = 1;
1502  for (int i = 0; i < nfont_positions; i++)
1503    font_position[i] = NULL_SYMBOL;
1504  put('p');
1505  put(pageno);
1506  put('\n');
1507}
1508
1509void troff_output_file::really_copy_file(hunits x, vunits y,
1510					 const char *filename)
1511{
1512  moveto(x, y);
1513  flush_tbuf();
1514  do_motion();
1515  errno = 0;
1516  FILE *ifp = include_search_path.open_file_cautious(filename);
1517  if (ifp == 0)
1518    error("can't open `%1': %2", filename, strerror(errno));
1519  else {
1520    int c;
1521    while ((c = getc(ifp)) != EOF)
1522      put(char(c));
1523    fclose(ifp);
1524  }
1525  force_motion = 1;
1526  current_size = 0;
1527  current_tfont = 0;
1528  current_font_number = -1;
1529  for (int i = 0; i < nfont_positions; i++)
1530    font_position[i] = NULL_SYMBOL;
1531}
1532
1533void troff_output_file::really_transparent_char(unsigned char c)
1534{
1535  put(c);
1536}
1537
1538troff_output_file::~troff_output_file()
1539{
1540  a_delete font_position;
1541}
1542
1543void troff_output_file::trailer(vunits page_length)
1544{
1545  flush_tbuf();
1546  if (page_length > V0) {
1547    put("x trailer\n");
1548    put('V');
1549    put(page_length.to_units());
1550    put('\n');
1551  }
1552  put("x stop\n");
1553}
1554
1555troff_output_file::troff_output_file()
1556: current_slant(0), current_height(0), current_fill_color(0),
1557  current_glyph_color(0), nfont_positions(10), tbuf_len(0), begun_page(0),
1558  cur_div_level(0)
1559{
1560  font_position = new symbol[nfont_positions];
1561  put("x T ");
1562  put(device);
1563  put('\n');
1564  put("x res ");
1565  put(units_per_inch);
1566  put(' ');
1567  put(hresolution);
1568  put(' ');
1569  put(vresolution);
1570  put('\n');
1571  put("x init\n");
1572}
1573
1574/* output_file */
1575
1576output_file *the_output = 0;
1577
1578output_file::output_file()
1579{
1580}
1581
1582output_file::~output_file()
1583{
1584}
1585
1586void output_file::trailer(vunits)
1587{
1588}
1589
1590void output_file::put_filename(const char *)
1591{
1592}
1593
1594void output_file::on()
1595{
1596}
1597
1598void output_file::off()
1599{
1600}
1601
1602real_output_file::real_output_file()
1603: printing(0), output_on(1)
1604{
1605#ifndef POPEN_MISSING
1606  if (pipe_command) {
1607    if ((fp = popen(pipe_command, POPEN_WT)) != 0) {
1608      piped = 1;
1609      return;
1610    }
1611    error("pipe open failed: %1", strerror(errno));
1612  }
1613  piped = 0;
1614#endif /* not POPEN_MISSING */
1615  fp = stdout;
1616}
1617
1618real_output_file::~real_output_file()
1619{
1620  if (!fp)
1621    return;
1622  // To avoid looping, set fp to 0 before calling fatal().
1623  if (ferror(fp) || fflush(fp) < 0) {
1624    fp = 0;
1625    fatal("error writing output file");
1626  }
1627#ifndef POPEN_MISSING
1628  if (piped) {
1629    int result = pclose(fp);
1630    fp = 0;
1631    if (result < 0)
1632      fatal("pclose failed");
1633    if (!WIFEXITED(result))
1634      error("output process `%1' got fatal signal %2",
1635	    pipe_command,
1636	    WIFSIGNALED(result) ? WTERMSIG(result) : WSTOPSIG(result));
1637    else {
1638      int exit_status = WEXITSTATUS(result);
1639      if (exit_status != 0)
1640	error("output process `%1' exited with status %2",
1641	      pipe_command, exit_status);
1642    }
1643  }
1644  else
1645#endif /* not POPEN MISSING */
1646  if (fclose(fp) < 0) {
1647    fp = 0;
1648    fatal("error closing output file");
1649  }
1650}
1651
1652void real_output_file::flush()
1653{
1654  if (fflush(fp) < 0)
1655    fatal("error writing output file");
1656}
1657
1658int real_output_file::is_printing()
1659{
1660  return printing;
1661}
1662
1663void real_output_file::begin_page(int pageno, vunits page_length)
1664{
1665  printing = in_output_page_list(pageno);
1666  if (printing)
1667    really_begin_page(pageno, page_length);
1668}
1669
1670void real_output_file::copy_file(hunits x, vunits y, const char *filename)
1671{
1672  if (printing && output_on)
1673    really_copy_file(x, y, filename);
1674  check_output_limits(x.to_units(), y.to_units());
1675}
1676
1677void real_output_file::transparent_char(unsigned char c)
1678{
1679  if (printing && output_on)
1680    really_transparent_char(c);
1681}
1682
1683void real_output_file::print_line(hunits x, vunits y, node *n,
1684			     vunits before, vunits after, hunits width)
1685{
1686  if (printing)
1687    really_print_line(x, y, n, before, after, width);
1688  delete_node_list(n);
1689}
1690
1691void real_output_file::really_copy_file(hunits, vunits, const char *)
1692{
1693  // do nothing
1694}
1695
1696void real_output_file::put_filename(const char *filename)
1697{
1698  really_put_filename(filename);
1699}
1700
1701void real_output_file::really_put_filename(const char *)
1702{
1703}
1704
1705void real_output_file::on()
1706{
1707  really_on();
1708  if (output_on == 0)
1709    output_on = 1;
1710}
1711
1712void real_output_file::off()
1713{
1714  really_off();
1715  output_on = 0;
1716}
1717
1718int real_output_file::is_on()
1719{
1720  return output_on;
1721}
1722
1723void real_output_file::really_on()
1724{
1725}
1726
1727void real_output_file::really_off()
1728{
1729}
1730
1731/* ascii_output_file */
1732
1733void ascii_output_file::really_transparent_char(unsigned char c)
1734{
1735  putc(c, fp);
1736}
1737
1738void ascii_output_file::really_print_line(hunits, vunits, node *n,
1739					  vunits, vunits, hunits)
1740{
1741  while (n != 0) {
1742    n->ascii_print(this);
1743    n = n->next;
1744  }
1745  fputc('\n', fp);
1746}
1747
1748void ascii_output_file::really_begin_page(int /*pageno*/, vunits /*page_length*/)
1749{
1750  fputs("<beginning of page>\n", fp);
1751}
1752
1753ascii_output_file::ascii_output_file()
1754{
1755}
1756
1757/* suppress_output_file */
1758
1759suppress_output_file::suppress_output_file()
1760{
1761}
1762
1763void suppress_output_file::really_print_line(hunits, vunits, node *, vunits, vunits, hunits)
1764{
1765}
1766
1767void suppress_output_file::really_begin_page(int, vunits)
1768{
1769}
1770
1771void suppress_output_file::really_transparent_char(unsigned char)
1772{
1773}
1774
1775/* glyphs, ligatures, kerns, discretionary breaks */
1776
1777class charinfo_node : public node {
1778protected:
1779  charinfo *ci;
1780public:
1781  charinfo_node(charinfo *, statem *, int, node * = 0);
1782  int ends_sentence();
1783  int overlaps_vertically();
1784  int overlaps_horizontally();
1785};
1786
1787charinfo_node::charinfo_node(charinfo *c, statem *s, int pop, node *x)
1788: node(x, s, pop), ci(c)
1789{
1790}
1791
1792int charinfo_node::ends_sentence()
1793{
1794  if (ci->ends_sentence())
1795    return 1;
1796  else if (ci->transparent())
1797    return 2;
1798  else
1799    return 0;
1800}
1801
1802int charinfo_node::overlaps_horizontally()
1803{
1804  return ci->overlaps_horizontally();
1805}
1806
1807int charinfo_node::overlaps_vertically()
1808{
1809  return ci->overlaps_vertically();
1810}
1811
1812class glyph_node : public charinfo_node {
1813  static glyph_node *free_list;
1814protected:
1815  tfont *tf;
1816  color *gcol;
1817  color *fcol;		/* this is needed for grotty */
1818#ifdef STORE_WIDTH
1819  hunits wid;
1820  glyph_node(charinfo *, tfont *, color *, color *, hunits,
1821	     statem *, int, node * = 0);
1822#endif
1823public:
1824  void *operator new(size_t);
1825  void operator delete(void *);
1826  glyph_node(charinfo *, tfont *, color *, color *,
1827	     statem *, int, node * = 0);
1828  ~glyph_node() {}
1829  node *copy();
1830  node *merge_glyph_node(glyph_node *);
1831  node *merge_self(node *);
1832  hunits width();
1833  node *last_char_node();
1834  units size();
1835  void vertical_extent(vunits *, vunits *);
1836  hunits subscript_correction();
1837  hunits italic_correction();
1838  hunits left_italic_correction();
1839  hunits skew();
1840  hyphenation_type get_hyphenation_type();
1841  tfont *get_tfont();
1842  color *get_glyph_color();
1843  color *get_fill_color();
1844  void tprint(troff_output_file *);
1845  void zero_width_tprint(troff_output_file *);
1846  hyphen_list *get_hyphen_list(hyphen_list *, int *);
1847  node *add_self(node *, hyphen_list **);
1848  void ascii_print(ascii_output_file *);
1849  void asciify(macro *);
1850  int character_type();
1851  int same(node *);
1852  const char *type();
1853  int force_tprint();
1854  int is_tag();
1855  void debug_node();
1856};
1857
1858glyph_node *glyph_node::free_list = 0;
1859
1860class ligature_node : public glyph_node {
1861  node *n1;
1862  node *n2;
1863#ifdef STORE_WIDTH
1864  ligature_node(charinfo *, tfont *, color *, color *, hunits,
1865		node *, node *, statem *, int, node * = 0);
1866#endif
1867public:
1868  void *operator new(size_t);
1869  void operator delete(void *);
1870  ligature_node(charinfo *, tfont *, color *, color *,
1871		node *, node *, statem *, int, node * = 0);
1872  ~ligature_node();
1873  node *copy();
1874  node *add_self(node *, hyphen_list **);
1875  hyphen_list *get_hyphen_list(hyphen_list *, int *);
1876  void ascii_print(ascii_output_file *);
1877  void asciify(macro *);
1878  int same(node *);
1879  const char *type();
1880  int force_tprint();
1881  int is_tag();
1882};
1883
1884class kern_pair_node : public node {
1885  hunits amount;
1886  node *n1;
1887  node *n2;
1888public:
1889  kern_pair_node(hunits, node *, node *, statem *, int, node * = 0);
1890  ~kern_pair_node();
1891  node *copy();
1892  node *merge_glyph_node(glyph_node *);
1893  node *add_self(node *, hyphen_list **);
1894  hyphen_list *get_hyphen_list(hyphen_list *, int *);
1895  node *add_discretionary_hyphen();
1896  hunits width();
1897  node *last_char_node();
1898  hunits italic_correction();
1899  hunits subscript_correction();
1900  void tprint(troff_output_file *);
1901  hyphenation_type get_hyphenation_type();
1902  int ends_sentence();
1903  void ascii_print(ascii_output_file *);
1904  void asciify(macro *);
1905  int same(node *);
1906  const char *type();
1907  int force_tprint();
1908  int is_tag();
1909  void vertical_extent(vunits *, vunits *);
1910};
1911
1912class dbreak_node : public node {
1913  node *none;
1914  node *pre;
1915  node *post;
1916public:
1917  dbreak_node(node *, node *, statem *, int, node * = 0);
1918  ~dbreak_node();
1919  node *copy();
1920  node *merge_glyph_node(glyph_node *);
1921  node *add_discretionary_hyphen();
1922  hunits width();
1923  node *last_char_node();
1924  hunits italic_correction();
1925  hunits subscript_correction();
1926  void tprint(troff_output_file *);
1927  breakpoint *get_breakpoints(hunits width, int ns, breakpoint *rest = 0,
1928			      int is_inner = 0);
1929  int nbreaks();
1930  int ends_sentence();
1931  void split(int, node **, node **);
1932  hyphenation_type get_hyphenation_type();
1933  void ascii_print(ascii_output_file *);
1934  void asciify(macro *);
1935  int same(node *);
1936  const char *type();
1937  int force_tprint();
1938  int is_tag();
1939};
1940
1941void *glyph_node::operator new(size_t n)
1942{
1943  assert(n == sizeof(glyph_node));
1944  if (!free_list) {
1945    const int BLOCK = 1024;
1946    free_list = (glyph_node *)new char[sizeof(glyph_node)*BLOCK];
1947    for (int i = 0; i < BLOCK - 1; i++)
1948      free_list[i].next = free_list + i + 1;
1949    free_list[BLOCK-1].next = 0;
1950  }
1951  glyph_node *p = free_list;
1952  free_list = (glyph_node *)(free_list->next);
1953  p->next = 0;
1954  return p;
1955}
1956
1957void *ligature_node::operator new(size_t n)
1958{
1959  return new char[n];
1960}
1961
1962void glyph_node::operator delete(void *p)
1963{
1964  if (p) {
1965    ((glyph_node *)p)->next = free_list;
1966    free_list = (glyph_node *)p;
1967  }
1968}
1969
1970void ligature_node::operator delete(void *p)
1971{
1972  delete[] (char *)p;
1973}
1974
1975glyph_node::glyph_node(charinfo *c, tfont *t, color *gc, color *fc,
1976		       statem *s, int pop, node *x)
1977: charinfo_node(c, s, pop, x), tf(t), gcol(gc), fcol(fc)
1978{
1979#ifdef STORE_WIDTH
1980  wid = tf->get_width(ci);
1981#endif
1982}
1983
1984#ifdef STORE_WIDTH
1985glyph_node::glyph_node(charinfo *c, tfont *t,
1986		       color *gc, color *fc, hunits w,
1987		       statem *s, int pop, node *x)
1988: charinfo_node(c, s, pop, x), tf(t), gcol(gc), fcol(fc), wid(w)
1989{
1990}
1991#endif
1992
1993node *glyph_node::copy()
1994{
1995#ifdef STORE_WIDTH
1996  return new glyph_node(ci, tf, gcol, fcol, wid, state, div_nest_level);
1997#else
1998  return new glyph_node(ci, tf, gcol, fcol, state, div_nest_level);
1999#endif
2000}
2001
2002node *glyph_node::merge_self(node *nd)
2003{
2004  return nd->merge_glyph_node(this);
2005}
2006
2007int glyph_node::character_type()
2008{
2009  return tf->get_character_type(ci);
2010}
2011
2012node *glyph_node::add_self(node *n, hyphen_list **p)
2013{
2014  assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
2015  next = 0;
2016  node *nn;
2017  if (n == 0 || (nn = n->merge_glyph_node(this)) == 0) {
2018    next = n;
2019    nn = this;
2020  }
2021  if ((*p)->hyphen)
2022    nn = nn->add_discretionary_hyphen();
2023  hyphen_list *pp = *p;
2024  *p = (*p)->next;
2025  delete pp;
2026  return nn;
2027}
2028
2029units glyph_node::size()
2030{
2031  return tf->get_size().to_units();
2032}
2033
2034hyphen_list *glyph_node::get_hyphen_list(hyphen_list *tail, int *count)
2035{
2036  (*count)++;
2037  return new hyphen_list(ci->get_hyphenation_code(), tail);
2038}
2039
2040tfont *node::get_tfont()
2041{
2042  return 0;
2043}
2044
2045tfont *glyph_node::get_tfont()
2046{
2047  return tf;
2048}
2049
2050color *node::get_glyph_color()
2051{
2052  return 0;
2053}
2054
2055color *glyph_node::get_glyph_color()
2056{
2057  return gcol;
2058}
2059
2060color *node::get_fill_color()
2061{
2062  return 0;
2063}
2064
2065color *glyph_node::get_fill_color()
2066{
2067  return fcol;
2068}
2069
2070node *node::merge_glyph_node(glyph_node *)
2071{
2072  return 0;
2073}
2074
2075node *glyph_node::merge_glyph_node(glyph_node *gn)
2076{
2077  if (tf == gn->tf && gcol == gn->gcol && fcol == gn->fcol) {
2078    charinfo *lig;
2079    if ((lig = tf->get_lig(ci, gn->ci)) != 0) {
2080      node *next1 = next;
2081      next = 0;
2082      return new ligature_node(lig, tf, gcol, fcol, this, gn, state,
2083			       gn->div_nest_level, next1);
2084    }
2085    hunits kern;
2086    if (tf->get_kern(ci, gn->ci, &kern)) {
2087      node *next1 = next;
2088      next = 0;
2089      return new kern_pair_node(kern, this, gn, state,
2090				gn->div_nest_level, next1);
2091    }
2092  }
2093  return 0;
2094}
2095
2096#ifdef STORE_WIDTH
2097inline
2098#endif
2099hunits glyph_node::width()
2100{
2101#ifdef STORE_WIDTH
2102  return wid;
2103#else
2104  return tf->get_width(ci);
2105#endif
2106}
2107
2108node *glyph_node::last_char_node()
2109{
2110  return this;
2111}
2112
2113void glyph_node::vertical_extent(vunits *min, vunits *max)
2114{
2115  *min = -tf->get_char_height(ci);
2116  *max = tf->get_char_depth(ci);
2117}
2118
2119hunits glyph_node::skew()
2120{
2121  return tf->get_char_skew(ci);
2122}
2123
2124hunits glyph_node::subscript_correction()
2125{
2126  return tf->get_subscript_correction(ci);
2127}
2128
2129hunits glyph_node::italic_correction()
2130{
2131  return tf->get_italic_correction(ci);
2132}
2133
2134hunits glyph_node::left_italic_correction()
2135{
2136  return tf->get_left_italic_correction(ci);
2137}
2138
2139hyphenation_type glyph_node::get_hyphenation_type()
2140{
2141  return HYPHEN_MIDDLE;
2142}
2143
2144void glyph_node::ascii_print(ascii_output_file *ascii)
2145{
2146  unsigned char c = ci->get_ascii_code();
2147  if (c != 0)
2148    ascii->outc(c);
2149  else
2150    ascii->outs(ci->nm.contents());
2151}
2152
2153void glyph_node::debug_node()
2154{
2155  unsigned char c = ci->get_ascii_code();
2156  fprintf(stderr, "{ %s [", type());
2157  if (c)
2158    fprintf(stderr, "%c", c);
2159  else
2160    fputs(ci->nm.contents(), stderr);
2161  if (push_state)
2162    fprintf(stderr, " <push_state>");
2163  if (state)
2164    state->display_state();
2165  fprintf(stderr, " nest level %d", div_nest_level);
2166  fprintf(stderr, "]}\n");
2167  fflush(stderr);
2168}
2169
2170ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
2171			     node *gn1, node *gn2, statem *s,
2172			     int pop, node *x)
2173: glyph_node(c, t, gc, fc, s, pop, x), n1(gn1), n2(gn2)
2174{
2175}
2176
2177#ifdef STORE_WIDTH
2178ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
2179			     hunits w, node *gn1, node *gn2, statem *s,
2180			     int pop, node *x)
2181: glyph_node(c, t, gc, fc, w, s, pop, x), n1(gn1), n2(gn2)
2182{
2183}
2184#endif
2185
2186ligature_node::~ligature_node()
2187{
2188  delete n1;
2189  delete n2;
2190}
2191
2192node *ligature_node::copy()
2193{
2194#ifdef STORE_WIDTH
2195  return new ligature_node(ci, tf, gcol, fcol, wid, n1->copy(), n2->copy(),
2196			   state, div_nest_level);
2197#else
2198  return new ligature_node(ci, tf, gcol, fcol, n1->copy(), n2->copy(),
2199			   state, div_nest_level);
2200#endif
2201}
2202
2203void ligature_node::ascii_print(ascii_output_file *ascii)
2204{
2205  n1->ascii_print(ascii);
2206  n2->ascii_print(ascii);
2207}
2208
2209hyphen_list *ligature_node::get_hyphen_list(hyphen_list *tail, int *count)
2210{
2211  hyphen_list *hl = n2->get_hyphen_list(tail, count);
2212  return n1->get_hyphen_list(hl, count);
2213}
2214
2215node *ligature_node::add_self(node *n, hyphen_list **p)
2216{
2217  n = n1->add_self(n, p);
2218  n = n2->add_self(n, p);
2219  n1 = n2 = 0;
2220  delete this;
2221  return n;
2222}
2223
2224kern_pair_node::kern_pair_node(hunits n, node *first, node *second,
2225			       statem* s, int pop, node *x)
2226: node(x, s, pop), amount(n), n1(first), n2(second)
2227{
2228}
2229
2230dbreak_node::dbreak_node(node *n, node *p, statem *s, int pop, node *x)
2231: node(x, s, pop), none(n), pre(p), post(0)
2232{
2233}
2234
2235node *dbreak_node::merge_glyph_node(glyph_node *gn)
2236{
2237  glyph_node *gn2 = (glyph_node *)gn->copy();
2238  node *new_none = none ? none->merge_glyph_node(gn) : 0;
2239  node *new_post = post ? post->merge_glyph_node(gn2) : 0;
2240  if (new_none == 0 && new_post == 0) {
2241    delete gn2;
2242    return 0;
2243  }
2244  if (new_none != 0)
2245    none = new_none;
2246  else {
2247    gn->next = none;
2248    none = gn;
2249  }
2250  if (new_post != 0)
2251    post = new_post;
2252  else {
2253    gn2->next = post;
2254    post = gn2;
2255  }
2256  return this;
2257}
2258
2259node *kern_pair_node::merge_glyph_node(glyph_node *gn)
2260{
2261  node *nd = n2->merge_glyph_node(gn);
2262  if (nd == 0)
2263    return 0;
2264  n2 = nd;
2265  nd = n2->merge_self(n1);
2266  if (nd) {
2267    nd->next = next;
2268    n1 = 0;
2269    n2 = 0;
2270    delete this;
2271    return nd;
2272  }
2273  return this;
2274}
2275
2276hunits kern_pair_node::italic_correction()
2277{
2278  return n2->italic_correction();
2279}
2280
2281hunits kern_pair_node::subscript_correction()
2282{
2283  return n2->subscript_correction();
2284}
2285
2286void kern_pair_node::vertical_extent(vunits *min, vunits *max)
2287{
2288  n1->vertical_extent(min, max);
2289  vunits min2, max2;
2290  n2->vertical_extent(&min2, &max2);
2291  if (min2 < *min)
2292    *min = min2;
2293  if (max2 > *max)
2294    *max = max2;
2295}
2296
2297node *kern_pair_node::add_discretionary_hyphen()
2298{
2299  tfont *tf = n2->get_tfo

Large files files are truncated, but you can click here to view the full file