/* Copyright (C) 1989-2020 Free Software Foundation, Inc.
Written by 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 3 of the License, 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 this program. If not, see . */
extern int debug_state;
#include "troff.h"
#ifdef HAVE_UNISTD_H
#include
#endif
#include "dictionary.h"
#include "hvunits.h"
#include "stringclass.h"
#include "mtsm.h"
#include "env.h"
#include "request.h"
#include "node.h"
#include "token.h"
#include "div.h"
#include "reg.h"
#include "font.h"
#include "charinfo.h"
#include "input.h"
#include "geometry.h"
#include "nonposix.h"
#ifdef _POSIX_VERSION
#include
#else /* not _POSIX_VERSION */
/* traditional Unix */
#define WIFEXITED(s) (((s) & 0377) == 0)
#define WEXITSTATUS(s) (((s) >> 8) & 0377)
#define WTERMSIG(s) ((s) & 0177)
#define WIFSTOPPED(s) (((s) & 0377) == 0177)
#define WSTOPSIG(s) (((s) >> 8) & 0377)
#define WIFSIGNALED(s) (((s) & 0377) != 0 && (((s) & 0377) != 0177))
#endif /* not _POSIX_VERSION */
// declarations to avoid friend name injections
class tfont;
class tfont_spec;
tfont *make_tfont(tfont_spec &);
/*
* how many boundaries of images have been written? Useful for
* debugging grohtml
*/
int image_no = 0;
static int suppress_start_page = 0;
#define STORE_WIDTH 1
symbol HYPHEN_SYMBOL("hy");
// Character used when a hyphen is inserted at a line break.
static charinfo *soft_hyphen_char;
enum constant_space_type {
CONSTANT_SPACE_NONE,
CONSTANT_SPACE_RELATIVE,
CONSTANT_SPACE_ABSOLUTE
};
struct special_font_list {
int n;
special_font_list *next;
};
special_font_list *global_special_fonts;
static int global_ligature_mode = 1;
static int global_kern_mode = 1;
class track_kerning_function {
int non_zero;
units min_size;
hunits min_amount;
units max_size;
hunits max_amount;
public:
track_kerning_function();
track_kerning_function(units, hunits, units, hunits);
int operator==(const track_kerning_function &);
int operator!=(const track_kerning_function &);
hunits compute(int point_size);
};
struct font_lookup_info {
int position;
int requested_position;
char *requested_name;
font_lookup_info();
};
font_lookup_info::font_lookup_info() : position(-1),
requested_position(-1), requested_name(0)
{
}
// embolden fontno when this is the current font
struct conditional_bold {
conditional_bold *next;
int fontno;
hunits offset;
conditional_bold(int, hunits, conditional_bold * = 0);
};
class font_info {
tfont *last_tfont;
int number;
font_size last_size;
int last_height;
int last_slant;
symbol internal_name;
symbol external_name;
font *fm;
char is_bold;
hunits bold_offset;
track_kerning_function track_kern;
constant_space_type is_constant_spaced;
units constant_space;
int last_ligature_mode;
int last_kern_mode;
conditional_bold *cond_bold_list;
void flush();
public:
special_font_list *sf;
font_info(symbol, int, symbol, font *);
int contains(charinfo *);
void set_bold(hunits);
void unbold();
void set_conditional_bold(int, hunits);
void conditional_unbold(int);
void set_track_kern(track_kerning_function &);
void set_constant_space(constant_space_type, units = 0);
int is_named(symbol);
symbol get_name();
tfont *get_tfont(font_size, int, int, int);
hunits get_space_width(font_size, int);
hunits get_narrow_space_width(font_size);
hunits get_half_narrow_space_width(font_size);
int get_bold(hunits *);
int is_special();
int is_style();
void set_zoom(int);
int get_zoom();
friend symbol get_font_name(int, environment *);
friend symbol get_style_name(int);
};
class tfont_spec {
protected:
symbol name;
int input_position;
font *fm;
font_size size;
char is_bold;
char is_constant_spaced;
int ligature_mode;
int kern_mode;
hunits bold_offset;
hunits track_kern; // add this to the width
hunits constant_space_width;
int height;
int slant;
public:
tfont_spec(symbol, int, font *, font_size, int, int);
tfont_spec(const tfont_spec &spec) { *this = spec; }
tfont_spec plain();
int operator==(const tfont_spec &);
friend tfont *font_info::get_tfont(font_size fs, int, int, int);
};
class tfont : public tfont_spec {
static tfont *tfont_list;
tfont *next;
tfont *plain_version;
public:
tfont(tfont_spec &);
int contains(charinfo *);
hunits get_width(charinfo *c);
int get_bold(hunits *);
int get_constant_space(hunits *);
hunits get_track_kern();
tfont *get_plain();
font_size get_size();
int get_zoom();
symbol get_name();
charinfo *get_lig(charinfo *c1, charinfo *c2);
int get_kern(charinfo *c1, charinfo *c2, hunits *res);
int get_input_position();
int get_character_type(charinfo *);
int get_height();
int get_slant();
vunits get_char_height(charinfo *);
vunits get_char_depth(charinfo *);
hunits get_char_skew(charinfo *);
hunits get_italic_correction(charinfo *);
hunits get_left_italic_correction(charinfo *);
hunits get_subscript_correction(charinfo *);
friend tfont *make_tfont(tfont_spec &);
};
inline int env_definite_font(environment *env)
{
return env->get_family()->make_definite(env->get_font());
}
/* font_info functions */
static font_info **font_table = 0;
static int font_table_size = 0;
font_info::font_info(symbol nm, int n, symbol enm, font *f)
: last_tfont(0), number(n), last_size(0),
internal_name(nm), external_name(enm), fm(f),
is_bold(0), is_constant_spaced(CONSTANT_SPACE_NONE), last_ligature_mode(1),
last_kern_mode(1), cond_bold_list(0), sf(0)
{
}
inline int font_info::contains(charinfo *ci)
{
return fm != 0 && fm->contains(ci->as_glyph());
}
inline int font_info::is_special()
{
return fm != 0 && fm->is_special();
}
inline int font_info::is_style()
{
return fm == 0;
}
void font_info::set_zoom(int zoom)
{
assert(fm != 0);
fm->set_zoom(zoom);
}
inline int font_info::get_zoom()
{
if (is_style())
return 0;
return fm->get_zoom();
}
tfont *make_tfont(tfont_spec &spec)
{
for (tfont *p = tfont::tfont_list; p; p = p->next)
if (*p == spec)
return p;
return new tfont(spec);
}
int env_get_zoom(environment *env)
{
int fontno = env->get_family()->make_definite(env->get_font());
return font_table[fontno]->get_zoom();
}
// this is the current_font, fontno is where we found the character,
// presumably a special font
tfont *font_info::get_tfont(font_size fs, int height, int slant, int fontno)
{
if (last_tfont == 0 || fs != last_size
|| height != last_height || slant != last_slant
|| global_ligature_mode != last_ligature_mode
|| global_kern_mode != last_kern_mode
|| fontno != number) {
font_info *f = font_table[fontno];
tfont_spec spec(f->external_name, f->number, f->fm, fs, height, slant);
for (conditional_bold *p = cond_bold_list; p; p = p->next)
if (p->fontno == fontno) {
spec.is_bold = 1;
spec.bold_offset = p->offset;
break;
}
if (!spec.is_bold && is_bold) {
spec.is_bold = 1;
spec.bold_offset = bold_offset;
}
spec.track_kern = track_kern.compute(fs.to_scaled_points());
spec.ligature_mode = global_ligature_mode;
spec.kern_mode = global_kern_mode;
switch (is_constant_spaced) {
case CONSTANT_SPACE_NONE:
break;
case CONSTANT_SPACE_ABSOLUTE:
spec.is_constant_spaced = 1;
spec.constant_space_width = constant_space;
break;
case CONSTANT_SPACE_RELATIVE:
spec.is_constant_spaced = 1;
spec.constant_space_width
= scale(constant_space*fs.to_scaled_points(),
units_per_inch,
36*72*sizescale);
break;
default:
assert(0);
}
if (fontno != number)
return make_tfont(spec);
// save font for comparison purposes
last_tfont = make_tfont(spec);
// save font related values not contained in tfont
last_size = fs;
last_height = height;
last_slant = slant;
last_ligature_mode = global_ligature_mode;
last_kern_mode = global_kern_mode;
}
return last_tfont;
}
int font_info::get_bold(hunits *res)
{
if (is_bold) {
*res = bold_offset;
return 1;
}
else
return 0;
}
void font_info::unbold()
{
if (is_bold) {
is_bold = 0;
flush();
}
}
void font_info::set_bold(hunits offset)
{
if (!is_bold || offset != bold_offset) {
is_bold = 1;
bold_offset = offset;
flush();
}
}
void font_info::set_conditional_bold(int fontno, hunits offset)
{
for (conditional_bold *p = cond_bold_list; p; p = p->next)
if (p->fontno == fontno) {
if (offset != p->offset) {
p->offset = offset;
flush();
}
return;
}
cond_bold_list = new conditional_bold(fontno, offset, cond_bold_list);
}
conditional_bold::conditional_bold(int f, hunits h, conditional_bold *x)
: next(x), fontno(f), offset(h)
{
}
void font_info::conditional_unbold(int fontno)
{
for (conditional_bold **p = &cond_bold_list; *p; p = &(*p)->next)
if ((*p)->fontno == fontno) {
conditional_bold *tem = *p;
*p = (*p)->next;
delete tem;
flush();
return;
}
}
void font_info::set_constant_space(constant_space_type type, units x)
{
if (type != is_constant_spaced
|| (type != CONSTANT_SPACE_NONE && x != constant_space)) {
flush();
is_constant_spaced = type;
constant_space = x;
}
}
void font_info::set_track_kern(track_kerning_function &tk)
{
if (track_kern != tk) {
track_kern = tk;
flush();
}
}
void font_info::flush()
{
last_tfont = 0;
}
int font_info::is_named(symbol s)
{
return internal_name == s;
}
symbol font_info::get_name()
{
return internal_name;
}
symbol get_font_name(int fontno, environment *env)
{
symbol f = font_table[fontno]->get_name();
if (font_table[fontno]->is_style()) {
return concat(env->get_family()->nm, f);
}
return f;
}
symbol get_style_name(int fontno)
{
if (font_table[fontno]->is_style())
return font_table[fontno]->get_name();
else
return EMPTY_SYMBOL;
}
hunits font_info::get_space_width(font_size fs, int space_sz)
{
if (is_constant_spaced == CONSTANT_SPACE_NONE)
return scale(hunits(fm->get_space_width(fs.to_scaled_points())),
space_sz, 12);
else if (is_constant_spaced == CONSTANT_SPACE_ABSOLUTE)
return constant_space;
else
return scale(constant_space*fs.to_scaled_points(),
units_per_inch, 36*72*sizescale);
}
hunits font_info::get_narrow_space_width(font_size fs)
{
charinfo *ci = get_charinfo(symbol("|"));
if (fm->contains(ci->as_glyph()))
return hunits(fm->get_width(ci->as_glyph(), fs.to_scaled_points()));
else
return hunits(fs.to_units()/6);
}
hunits font_info::get_half_narrow_space_width(font_size fs)
{
charinfo *ci = get_charinfo(symbol("^"));
if (fm->contains(ci->as_glyph()))
return hunits(fm->get_width(ci->as_glyph(), fs.to_scaled_points()));
else
return hunits(fs.to_units()/12);
}
/* tfont */
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)
{
if (height == size.to_scaled_points())
height = 0;
}
int tfont_spec::operator==(const tfont_spec &spec)
{
if (fm == spec.fm
&& size == spec.size
&& input_position == spec.input_position
&& name == spec.name
&& height == spec.height
&& slant == spec.slant
&& (is_bold
? (spec.is_bold && bold_offset == spec.bold_offset)
: !spec.is_bold)
&& track_kern == spec.track_kern
&& (is_constant_spaced
? (spec.is_constant_spaced
&& constant_space_width == spec.constant_space_width)
: !spec.is_constant_spaced)
&& ligature_mode == spec.ligature_mode
&& kern_mode == spec.kern_mode)
return 1;
else
return 0;
}
tfont_spec tfont_spec::plain()
{
return tfont_spec(name, input_position, fm, size, height, slant);
}
hunits tfont::get_width(charinfo *c)
{
if (is_constant_spaced)
return constant_space_width;
else if (is_bold)
return (hunits(fm->get_width(c->as_glyph(), size.to_scaled_points()))
+ track_kern + bold_offset);
else
return (hunits(fm->get_width(c->as_glyph(), size.to_scaled_points()))
+ track_kern);
}
vunits tfont::get_char_height(charinfo *c)
{
vunits v = fm->get_height(c->as_glyph(), size.to_scaled_points());
if (height != 0 && height != size.to_scaled_points())
return scale(v, height, size.to_scaled_points());
else
return v;
}
vunits tfont::get_char_depth(charinfo *c)
{
vunits v = fm->get_depth(c->as_glyph(), size.to_scaled_points());
if (height != 0 && height != size.to_scaled_points())
return scale(v, height, size.to_scaled_points());
else
return v;
}
hunits tfont::get_char_skew(charinfo *c)
{
return hunits(fm->get_skew(c->as_glyph(), size.to_scaled_points(), slant));
}
hunits tfont::get_italic_correction(charinfo *c)
{
return hunits(fm->get_italic_correction(c->as_glyph(), size.to_scaled_points()));
}
hunits tfont::get_left_italic_correction(charinfo *c)
{
return hunits(fm->get_left_italic_correction(c->as_glyph(),
size.to_scaled_points()));
}
hunits tfont::get_subscript_correction(charinfo *c)
{
return hunits(fm->get_subscript_correction(c->as_glyph(),
size.to_scaled_points()));
}
inline int tfont::get_input_position()
{
return input_position;
}
inline int tfont::contains(charinfo *ci)
{
return fm->contains(ci->as_glyph());
}
inline int tfont::get_character_type(charinfo *ci)
{
return fm->get_character_type(ci->as_glyph());
}
inline int tfont::get_bold(hunits *res)
{
if (is_bold) {
*res = bold_offset;
return 1;
}
else
return 0;
}
inline int tfont::get_constant_space(hunits *res)
{
if (is_constant_spaced) {
*res = constant_space_width;
return 1;
}
else
return 0;
}
inline hunits tfont::get_track_kern()
{
return track_kern;
}
inline tfont *tfont::get_plain()
{
return plain_version;
}
inline font_size tfont::get_size()
{
return size;
}
inline int tfont::get_zoom()
{
return fm->get_zoom();
}
inline symbol tfont::get_name()
{
return name;
}
inline int tfont::get_height()
{
return height;
}
inline int tfont::get_slant()
{
return slant;
}
symbol SYMBOL_ff("ff");
symbol SYMBOL_fi("fi");
symbol SYMBOL_fl("fl");
symbol SYMBOL_Fi("Fi");
symbol SYMBOL_Fl("Fl");
charinfo *tfont::get_lig(charinfo *c1, charinfo *c2)
{
if (ligature_mode == 0)
return 0;
charinfo *ci = 0;
if (c1->get_ascii_code() == 'f') {
switch (c2->get_ascii_code()) {
case 'f':
if (fm->has_ligature(font::LIG_ff))
ci = get_charinfo(SYMBOL_ff);
break;
case 'i':
if (fm->has_ligature(font::LIG_fi))
ci = get_charinfo(SYMBOL_fi);
break;
case 'l':
if (fm->has_ligature(font::LIG_fl))
ci = get_charinfo(SYMBOL_fl);
break;
}
}
else if (ligature_mode != 2 && c1->nm == SYMBOL_ff) {
switch (c2->get_ascii_code()) {
case 'i':
if (fm->has_ligature(font::LIG_ffi))
ci = get_charinfo(SYMBOL_Fi);
break;
case 'l':
if (fm->has_ligature(font::LIG_ffl))
ci = get_charinfo(SYMBOL_Fl);
break;
}
}
if (ci != 0 && fm->contains(ci->as_glyph()))
return ci;
return 0;
}
inline int tfont::get_kern(charinfo *c1, charinfo *c2, hunits *res)
{
if (kern_mode == 0)
return 0;
else {
int n = fm->get_kern(c1->as_glyph(),
c2->as_glyph(),
size.to_scaled_points());
if (n) {
*res = hunits(n);
return 1;
}
else
return 0;
}
}
tfont *tfont::tfont_list = 0;
tfont::tfont(tfont_spec &spec) : tfont_spec(spec)
{
next = tfont_list;
tfont_list = this;
tfont_spec plain_spec = plain();
tfont *p;
for (p = tfont_list; p; p = p->next)
if (*p == plain_spec) {
plain_version = p;
break;
}
if (!p)
plain_version = new tfont(plain_spec);
}
/* output_file */
class real_output_file : public output_file {
#ifndef POPEN_MISSING
int piped;
#endif
int printing; // decision via optional page list
int output_on; // \O[0] or \O[1] escape sequences
virtual void really_transparent_char(unsigned char) = 0;
virtual void really_print_line(hunits x, vunits y, node *n,
vunits before, vunits after, hunits width) = 0;
virtual void really_begin_page(int pageno, vunits page_length) = 0;
virtual void really_copy_file(hunits x, vunits y, const char *filename);
virtual void really_put_filename(const char *, int);
virtual void really_on();
virtual void really_off();
public:
FILE *fp;
real_output_file();
~real_output_file();
void flush();
void transparent_char(unsigned char);
void print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width);
void begin_page(int pageno, vunits page_length);
void put_filename(const char *, int);
void on();
void off();
int is_on();
int is_printing();
void copy_file(hunits x, vunits y, const char *filename);
};
class suppress_output_file : public real_output_file {
public:
suppress_output_file();
void really_transparent_char(unsigned char);
void really_print_line(hunits x, vunits y, node *n, vunits, vunits, hunits width);
void really_begin_page(int pageno, vunits page_length);
};
class ascii_output_file : public real_output_file {
public:
ascii_output_file();
void really_transparent_char(unsigned char);
void really_print_line(hunits x, vunits y, node *n, vunits, vunits, hunits width);
void really_begin_page(int pageno, vunits page_length);
void outc(unsigned char c);
void outs(const char *s);
};
void ascii_output_file::outc(unsigned char c)
{
fputc(c, fp);
}
void ascii_output_file::outs(const char *s)
{
fputc('<', fp);
if (s)
fputs(s, fp);
fputc('>', fp);
}
struct hvpair;
class troff_output_file : public real_output_file {
units hpos;
units vpos;
units output_vpos;
units output_hpos;
int force_motion;
int current_size;
int current_slant;
int current_height;
tfont *current_tfont;
color *current_fill_color;
color *current_glyph_color;
int current_font_number;
symbol *font_position;
int nfont_positions;
enum { TBUF_SIZE = 256 };
char tbuf[TBUF_SIZE];
int tbuf_len;
int tbuf_kern;
int begun_page;
int cur_div_level;
string tag_list;
void do_motion();
void put(char c);
void put(unsigned char c);
void put(int i);
void put(unsigned int i);
void put(const char *s);
void set_font(tfont *tf);
void flush_tbuf();
public:
troff_output_file();
~troff_output_file();
void trailer(vunits page_length);
void put_char(charinfo *, tfont *, color *, color *);
void put_char_width(charinfo *, tfont *, color *, color *, hunits, hunits);
void right(hunits);
void down(vunits);
void moveto(hunits, vunits);
void start_special(tfont *, color *, color *, int = 0);
void start_special();
void special_char(unsigned char c);
void end_special();
void word_marker();
void really_transparent_char(unsigned char c);
void really_print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width);
void really_begin_page(int pageno, vunits page_length);
void really_copy_file(hunits x, vunits y, const char *filename);
void really_put_filename(const char *, int);
void really_on();
void really_off();
void draw(char, hvpair *, int, font_size, color *, color *);
void determine_line_limits (char code, hvpair *point, int npoints);
void check_charinfo(tfont *tf, charinfo *ci);
void glyph_color(color *c);
void fill_color(color *c);
int get_hpos() { return hpos; }
int get_vpos() { return vpos; }
void add_to_tag_list(string s);
friend void space_char_hmotion_node::tprint(troff_output_file *);
friend void unbreakable_space_node::tprint(troff_output_file *);
};
static void put_string(const char *s, FILE *fp)
{
for (; *s != '\0'; ++s)
putc(*s, fp);
}
inline void troff_output_file::put(char c)
{
putc(c, fp);
}
inline void troff_output_file::put(unsigned char c)
{
putc(c, fp);
}
inline void troff_output_file::put(const char *s)
{
put_string(s, fp);
}
inline void troff_output_file::put(int i)
{
put_string(i_to_a(i), fp);
}
inline void troff_output_file::put(unsigned int i)
{
put_string(ui_to_a(i), fp);
}
void troff_output_file::start_special(tfont *tf, color *gcol, color *fcol,
int no_init_string)
{
set_font(tf);
glyph_color(gcol);
fill_color(fcol);
flush_tbuf();
do_motion();
if (!no_init_string)
put("x X ");
}
void troff_output_file::start_special()
{
flush_tbuf();
do_motion();
put("x X ");
}
void troff_output_file::special_char(unsigned char c)
{
put(c);
if (c == '\n')
put('+');
}
void troff_output_file::end_special()
{
put('\n');
}
inline void troff_output_file::moveto(hunits h, vunits v)
{
hpos = h.to_units();
vpos = v.to_units();
}
void troff_output_file::really_print_line(hunits x, vunits y, node *n,
vunits before, vunits after, hunits)
{
moveto(x, y);
while (n != 0) {
// Check whether we should push the current troff state and use
// the state at the start of the invocation of this diversion.
if (n->div_nest_level > cur_div_level && n->push_state) {
state.push_state(n->push_state);
cur_div_level = n->div_nest_level;
}
// Has the current diversion level decreased? Then we must pop the
// troff state.
while (n->div_nest_level < cur_div_level) {
state.pop_state();
cur_div_level = n->div_nest_level;
}
// Now check whether the state has changed.
if ((is_on() || n->force_tprint())
&& (state.changed(n->state) || n->is_tag() || n->is_special)) {
flush_tbuf();
do_motion();
force_motion = 1;
flush();
state.flush(fp, n->state, tag_list);
tag_list = string("");
flush();
}
n->tprint(this);
n = n->next;
}
flush_tbuf();
// This ensures that transparent throughput will have a more predictable
// position.
do_motion();
force_motion = 1;
hpos = 0;
put('n');
put(before.to_units());
put(' ');
put(after.to_units());
put('\n');
}
inline void troff_output_file::word_marker()
{
flush_tbuf();
if (is_on())
put('w');
}
inline void troff_output_file::right(hunits n)
{
hpos += n.to_units();
}
inline void troff_output_file::down(vunits n)
{
vpos += n.to_units();
}
void troff_output_file::do_motion()
{
if (force_motion) {
put('V');
put(vpos);
put('\n');
put('H');
put(hpos);
put('\n');
}
else {
if (hpos != output_hpos) {
units n = hpos - output_hpos;
if (n > 0 && n < hpos) {
put('h');
put(n);
}
else {
put('H');
put(hpos);
}
put('\n');
}
if (vpos != output_vpos) {
units n = vpos - output_vpos;
if (n > 0 && n < vpos) {
put('v');
put(n);
}
else {
put('V');
put(vpos);
}
put('\n');
}
}
output_vpos = vpos;
output_hpos = hpos;
force_motion = 0;
}
void troff_output_file::flush_tbuf()
{
if (!is_on()) {
tbuf_len = 0;
return;
}
if (tbuf_len == 0)
return;
if (tbuf_kern == 0)
put('t');
else {
put('u');
put(tbuf_kern);
put(' ');
}
check_output_limits(hpos, vpos);
check_output_limits(hpos, vpos - current_size);
for (int i = 0; i < tbuf_len; i++)
put(tbuf[i]);
put('\n');
tbuf_len = 0;
}
void troff_output_file::check_charinfo(tfont *tf, charinfo *ci)
{
if (!is_on())
return;
int height = tf->get_char_height(ci).to_units();
int width = tf->get_width(ci).to_units()
+ tf->get_italic_correction(ci).to_units();
int depth = tf->get_char_depth(ci).to_units();
check_output_limits(output_hpos, output_vpos - height);
check_output_limits(output_hpos + width, output_vpos + depth);
}
void troff_output_file::put_char_width(charinfo *ci, tfont *tf,
color *gcol, color *fcol,
hunits w, hunits k)
{
int kk = k.to_units();
if (!is_on()) {
flush_tbuf();
hpos += w.to_units() + kk;
return;
}
set_font(tf);
unsigned char c = ci->get_ascii_code();
if (c == '\0') {
glyph_color(gcol);
fill_color(fcol);
flush_tbuf();
do_motion();
check_charinfo(tf, ci);
if (ci->numbered()) {
put('N');
put(ci->get_number());
}
else {
put('C');
const char *s = ci->nm.contents();
if (s[1] == 0) {
put('\\');
put(s[0]);
}
else
put(s);
}
put('\n');
hpos += w.to_units() + kk;
}
else if (device_has_tcommand) {
if (tbuf_len > 0 && hpos == output_hpos && vpos == output_vpos
&& (!gcol || gcol == current_glyph_color)
&& (!fcol || fcol == current_fill_color)
&& kk == tbuf_kern
&& tbuf_len < TBUF_SIZE) {
check_charinfo(tf, ci);
tbuf[tbuf_len++] = c;
output_hpos += w.to_units() + kk;
hpos = output_hpos;
return;
}
glyph_color(gcol);
fill_color(fcol);
flush_tbuf();
do_motion();
check_charinfo(tf, ci);
tbuf[tbuf_len++] = c;
output_hpos += w.to_units() + kk;
tbuf_kern = kk;
hpos = output_hpos;
}
else {
// flush_tbuf();
int n = hpos - output_hpos;
check_charinfo(tf, ci);
// check_output_limits(output_hpos, output_vpos);
if (vpos == output_vpos
&& (!gcol || gcol == current_glyph_color)
&& (!fcol || fcol == current_fill_color)
&& n > 0 && n < 100 && !force_motion) {
put(char(n/10 + '0'));
put(char(n%10 + '0'));
put(c);
output_hpos = hpos;
}
else {
glyph_color(gcol);
fill_color(fcol);
do_motion();
put('c');
put(c);
}
hpos += w.to_units() + kk;
}
}
void troff_output_file::put_char(charinfo *ci, tfont *tf,
color *gcol, color *fcol)
{
flush_tbuf();
if (!is_on())
return;
set_font(tf);
unsigned char c = ci->get_ascii_code();
if (c == '\0') {
glyph_color(gcol);
fill_color(fcol);
flush_tbuf();
do_motion();
if (ci->numbered()) {
put('N');
put(ci->get_number());
}
else {
put('C');
const char *s = ci->nm.contents();
if (s[1] == 0) {
put('\\');
put(s[0]);
}
else
put(s);
}
put('\n');
}
else {
int n = hpos - output_hpos;
if (vpos == output_vpos
&& (!gcol || gcol == current_glyph_color)
&& (!fcol || fcol == current_fill_color)
&& n > 0 && n < 100) {
put(char(n/10 + '0'));
put(char(n%10 + '0'));
put(c);
output_hpos = hpos;
}
else {
glyph_color(gcol);
fill_color(fcol);
flush_tbuf();
do_motion();
put('c');
put(c);
}
}
}
// set_font calls 'flush_tbuf' if necessary.
void troff_output_file::set_font(tfont *tf)
{
if (current_tfont == tf)
return;
flush_tbuf();
int n = tf->get_input_position();
symbol nm = tf->get_name();
if (n >= nfont_positions || font_position[n] != nm) {
put("x font ");
put(n);
put(' ');
put(nm.contents());
put('\n');
if (n >= nfont_positions) {
int old_nfont_positions = nfont_positions;
symbol *old_font_position = font_position;
nfont_positions *= 3;
nfont_positions /= 2;
if (nfont_positions <= n)
nfont_positions = n + 10;
font_position = new symbol[nfont_positions];
memcpy(font_position, old_font_position,
old_nfont_positions*sizeof(symbol));
delete[] old_font_position;
}
font_position[n] = nm;
}
if (current_font_number != n) {
put('f');
put(n);
put('\n');
current_font_number = n;
}
int zoom = tf->get_zoom();
int size;
if (zoom)
size = scale(tf->get_size().to_scaled_points(),
zoom, 1000);
else
size = tf->get_size().to_scaled_points();
if (current_size != size) {
put('s');
put(size);
put('\n');
current_size = size;
}
int slant = tf->get_slant();
if (current_slant != slant) {
put("x Slant ");
put(slant);
put('\n');
current_slant = slant;
}
int height = tf->get_height();
if (current_height != height) {
put("x Height ");
put(height == 0 ? current_size : height);
put('\n');
current_height = height;
}
current_tfont = tf;
}
// fill_color calls 'flush_tbuf' and 'do_motion' if necessary.
void troff_output_file::fill_color(color *col)
{
if (!col || current_fill_color == col)
return;
current_fill_color = col;
if (!color_flag)
return;
flush_tbuf();
do_motion();
put("DF");
unsigned int components[4];
color_scheme cs;
cs = col->get_components(components);
switch (cs) {
case DEFAULT:
put('d');
break;
case RGB:
put("r ");
put(Red);
put(' ');
put(Green);
put(' ');
put(Blue);
break;
case CMY:
put("c ");
put(Cyan);
put(' ');
put(Magenta);
put(' ');
put(Yellow);
break;
case CMYK:
put("k ");
put(Cyan);
put(' ');
put(Magenta);
put(' ');
put(Yellow);
put(' ');
put(Black);
break;
case GRAY:
put("g ");
put(Gray);
break;
}
put('\n');
}
// glyph_color calls 'flush_tbuf' and 'do_motion' if necessary.
void troff_output_file::glyph_color(color *col)
{
if (!col || current_glyph_color == col)
return;
current_glyph_color = col;
if (!color_flag)
return;
flush_tbuf();
// grotty doesn't like a color command if the vertical position is zero.
do_motion();
put("m");
unsigned int components[4];
color_scheme cs;
cs = col->get_components(components);
switch (cs) {
case DEFAULT:
put('d');
break;
case RGB:
put("r ");
put(Red);
put(' ');
put(Green);
put(' ');
put(Blue);
break;
case CMY:
put("c ");
put(Cyan);
put(' ');
put(Magenta);
put(' ');
put(Yellow);
break;
case CMYK:
put("k ");
put(Cyan);
put(' ');
put(Magenta);
put(' ');
put(Yellow);
put(' ');
put(Black);
break;
case GRAY:
put("g ");
put(Gray);
break;
}
put('\n');
}
void troff_output_file::add_to_tag_list(string s)
{
if (tag_list == string(""))
tag_list = s;
else {
tag_list += string("\n");
tag_list += s;
}
}
// determine_line_limits - works out the smallest box which will contain
// the entity, code, built from the point array.
void troff_output_file::determine_line_limits(char code, hvpair *point,
int npoints)
{
int i, x, y;
if (!is_on())
return;
switch (code) {
case 'c':
case 'C':
// only the h field is used when defining a circle
check_output_limits(output_hpos,
output_vpos - point[0].h.to_units()/2);
check_output_limits(output_hpos + point[0].h.to_units(),
output_vpos + point[0].h.to_units()/2);
break;
case 'E':
case 'e':
check_output_limits(output_hpos,
output_vpos - point[0].v.to_units()/2);
check_output_limits(output_hpos + point[0].h.to_units(),
output_vpos + point[0].v.to_units()/2);
break;
case 'P':
case 'p':
x = output_hpos;
y = output_vpos;
check_output_limits(x, y);
for (i = 0; i < npoints; i++) {
x += point[i].h.to_units();
y += point[i].v.to_units();
check_output_limits(x, y);
}
break;
case 't':
x = output_hpos;
y = output_vpos;
for (i = 0; i < npoints; i++) {
x += point[i].h.to_units();
y += point[i].v.to_units();
check_output_limits(x, y);
}
break;
case 'a':
double c[2];
int p[4];
int minx, miny, maxx, maxy;
x = output_hpos;
y = output_vpos;
p[0] = point[0].h.to_units();
p[1] = point[0].v.to_units();
p[2] = point[1].h.to_units();
p[3] = point[1].v.to_units();
if (adjust_arc_center(p, c)) {
check_output_arc_limits(x, y,
p[0], p[1], p[2], p[3],
c[0], c[1],
&minx, &maxx, &miny, &maxy);
check_output_limits(minx, miny);
check_output_limits(maxx, maxy);
break;
}
// fall through
case 'l':
x = output_hpos;
y = output_vpos;
check_output_limits(x, y);
for (i = 0; i < npoints; i++) {
x += point[i].h.to_units();
y += point[i].v.to_units();
check_output_limits(x, y);
}
break;
default:
x = output_hpos;
y = output_vpos;
for (i = 0; i < npoints; i++) {
x += point[i].h.to_units();
y += point[i].v.to_units();
check_output_limits(x, y);
}
}
}
void troff_output_file::draw(char code, hvpair *point, int npoints,
font_size fsize, color *gcol, color *fcol)
{
int i;
glyph_color(gcol);
fill_color(fcol);
flush_tbuf();
do_motion();
if (is_on()) {
int size = fsize.to_scaled_points();
if (current_size != size) {
put('s');
put(size);
put('\n');
current_size = size;
current_tfont = 0;
}
put('D');
put(code);
if (code == 'c') {
put(' ');
put(point[0].h.to_units());
}
else
for (i = 0; i < npoints; i++) {
put(' ');
put(point[i].h.to_units());
put(' ');
put(point[i].v.to_units());
}
determine_line_limits(code, point, npoints);
}
for (i = 0; i < npoints; i++)
output_hpos += point[i].h.to_units();
hpos = output_hpos;
if (code != 'e') {
for (i = 0; i < npoints; i++)
output_vpos += point[i].v.to_units();
vpos = output_vpos;
}
if (is_on())
put('\n');
}
void troff_output_file::really_on()
{
flush_tbuf();
force_motion = 1;
do_motion();
}
void troff_output_file::really_off()
{
flush_tbuf();
}
void troff_output_file::really_put_filename(const char *filename, int po)
{
flush_tbuf();
put("x F ");
if (po)
put("<");
put(filename);
if (po)
put(">");
put('\n');
}
void troff_output_file::really_begin_page(int pageno, vunits page_length)
{
flush_tbuf();
if (begun_page) {
if (page_length > V0) {
put('V');
put(page_length.to_units());
put('\n');
}
}
else
begun_page = 1;
current_tfont = 0;
current_font_number = -1;
current_size = 0;
// current_height = 0;
// current_slant = 0;
hpos = 0;
vpos = 0;
output_hpos = 0;
output_vpos = 0;
force_motion = 1;
for (int i = 0; i < nfont_positions; i++)
font_position[i] = NULL_SYMBOL;
put('p');
put(pageno);
put('\n');
}
void troff_output_file::really_copy_file(hunits x, vunits y,
const char *filename)
{
moveto(x, y);
flush_tbuf();
do_motion();
errno = 0;
FILE *ifp = include_search_path.open_file_cautious(filename);
if (ifp == 0)
error("can't open '%1': %2", filename, strerror(errno));
else {
int c;
while ((c = getc(ifp)) != EOF)
put(char(c));
fclose(ifp);
}
force_motion = 1;
current_size = 0;
current_tfont = 0;
current_font_number = -1;
for (int i = 0; i < nfont_positions; i++)
font_position[i] = NULL_SYMBOL;
}
void troff_output_file::really_transparent_char(unsigned char c)
{
put(c);
}
troff_output_file::~troff_output_file()
{
delete[] font_position;
}
void troff_output_file::trailer(vunits page_length)
{
flush_tbuf();
if (page_length > V0) {
put("x trailer\n");
put('V');
put(page_length.to_units());
put('\n');
}
put("x stop\n");
}
troff_output_file::troff_output_file()
: current_slant(0), current_height(0), current_fill_color(0),
current_glyph_color(0), nfont_positions(10), tbuf_len(0), begun_page(0),
cur_div_level(0)
{
font_position = new symbol[nfont_positions];
put("x T ");
put(device);
put('\n');
put("x res ");
put(units_per_inch);
put(' ');
put(hresolution);
put(' ');
put(vresolution);
put('\n');
put("x init\n");
}
/* output_file */
output_file *the_output = 0;
output_file::output_file()
{
is_dying = false;
}
output_file::~output_file()
{
}
void output_file::trailer(vunits)
{
}
void output_file::put_filename(const char *, int)
{
}
void output_file::on()
{
}
void output_file::off()
{
}
real_output_file::real_output_file()
: printing(0), output_on(1)
{
#ifndef POPEN_MISSING
if (pipe_command) {
if ((fp = popen(pipe_command, POPEN_WT)) != 0) {
piped = 1;
return;
}
error("pipe open failed: %1", strerror(errno));
}
piped = 0;
#endif /* not POPEN_MISSING */
fp = stdout;
}
real_output_file::~real_output_file()
{
if (!fp)
return;
// Prevent destructor from recursing; see div.cpp:cleanup_and_exit().
is_dying = true;
// To avoid looping, set fp to 0 before calling fatal().
if (ferror(fp)) {
fp = 0;
fatal("error on output file stream");
}
else if (fflush(fp) < 0) {
fp = 0;
fatal("unable to flush output file: %1", strerror(errno));
}
#ifndef POPEN_MISSING
if (piped) {
int result = pclose(fp);
fp = 0;
if (result < 0)
fatal("unable to close pipe: %1", strerror(errno));
if (!WIFEXITED(result))
error("output process '%1' got fatal signal %2",
pipe_command,
WIFSIGNALED(result) ? WTERMSIG(result) : WSTOPSIG(result));
else {
int exit_status = WEXITSTATUS(result);
if (exit_status != 0)
error("output process '%1' exited with status %2",
pipe_command, exit_status);
}
}
else
#endif /* not POPEN MISSING */
if (fclose(fp) < 0) {
fp = 0;
fatal("unable to close output file: %1", strerror(errno));
}
}
void real_output_file::flush()
{
// To avoid looping, set fp to 0 before calling fatal().
if (fflush(fp) < 0) {
fp = 0;
fatal("unable to flush output file: %1", strerror(errno));
}
}
int real_output_file::is_printing()
{
return printing;
}
void real_output_file::begin_page(int pageno, vunits page_length)
{
printing = in_output_page_list(pageno);
if (printing)
really_begin_page(pageno, page_length);
}
void real_output_file::copy_file(hunits x, vunits y, const char *filename)
{
if (printing && output_on)
really_copy_file(x, y, filename);
check_output_limits(x.to_units(), y.to_units());
}
void real_output_file::transparent_char(unsigned char c)
{
if (printing && output_on)
really_transparent_char(c);
}
void real_output_file::print_line(hunits x, vunits y, node *n,
vunits before, vunits after, hunits width)
{
if (printing)
really_print_line(x, y, n, before, after, width);
delete_node_list(n);
}
void real_output_file::really_copy_file(hunits, vunits, const char *)
{
// do nothing
}
void real_output_file::put_filename(const char *filename, int po)
{
really_put_filename(filename, po);
}
void real_output_file::really_put_filename(const char *, int)
{
}
void real_output_file::on()
{
really_on();
if (output_on == 0)
output_on = 1;
}
void real_output_file::off()
{
really_off();
output_on = 0;
}
int real_output_file::is_on()
{
return output_on;
}
void real_output_file::really_on()
{
}
void real_output_file::really_off()
{
}
/* ascii_output_file */
void ascii_output_file::really_transparent_char(unsigned char c)
{
putc(c, fp);
}
void ascii_output_file::really_print_line(hunits, vunits, node *n,
vunits, vunits, hunits)
{
while (n != 0) {
n->ascii_print(this);
n = n->next;
}
fputc('\n', fp);
}
void ascii_output_file::really_begin_page(int /*pageno*/, vunits /*page_length*/)
{
fputs("\n", fp);
}
ascii_output_file::ascii_output_file()
{
}
/* suppress_output_file */
suppress_output_file::suppress_output_file()
{
}
void suppress_output_file::really_print_line(hunits, vunits, node *, vunits, vunits, hunits)
{
}
void suppress_output_file::really_begin_page(int, vunits)
{
}
void suppress_output_file::really_transparent_char(unsigned char)
{
}
/* glyphs, ligatures, kerns, discretionary breaks */
class charinfo_node : public node {
protected:
charinfo *ci;
public:
charinfo_node(charinfo *, statem *, int, node * = 0);
int ends_sentence();
int overlaps_vertically();
int overlaps_horizontally();
};
charinfo_node::charinfo_node(charinfo *c, statem *s, int pop, node *x)
: node(x, s, pop), ci(c)
{
}
int charinfo_node::ends_sentence()
{
if (ci->ends_sentence())
return 1;
else if (ci->transparent())
return 2;
else
return 0;
}
int charinfo_node::overlaps_horizontally()
{
return ci->overlaps_horizontally();
}
int charinfo_node::overlaps_vertically()
{
return ci->overlaps_vertically();
}
class glyph_node : public charinfo_node {
protected:
tfont *tf;
color *gcol;
color *fcol; /* this is needed for grotty */
#ifdef STORE_WIDTH
hunits wid;
glyph_node(charinfo *, tfont *, color *, color *, hunits,
statem *, int, node * = 0);
#endif
public:
glyph_node(charinfo *, tfont *, color *, color *,
statem *, int, node * = 0);
~glyph_node() {}
node *copy();
node *merge_glyph_node(glyph_node *);
node *merge_self(node *);
hunits width();
node *last_char_node();
units size();
void vertical_extent(vunits *, vunits *);
hunits subscript_correction();
hunits italic_correction();
hunits left_italic_correction();
hunits skew();
hyphenation_type get_hyphenation_type();
tfont *get_tfont();
color *get_glyph_color();
color *get_fill_color();
void tprint(troff_output_file *);
void zero_width_tprint(troff_output_file *);
hyphen_list *get_hyphen_list(hyphen_list *, int *);
node *add_self(node *, hyphen_list **);
void ascii_print(ascii_output_file *);
void asciify(macro *);
int character_type();
int same(node *);
const char *type();
int force_tprint();
int is_tag();
void debug_node();
};
class ligature_node : public glyph_node {
node *n1;
node *n2;
#ifdef STORE_WIDTH
ligature_node(charinfo *, tfont *, color *, color *, hunits,
node *, node *, statem *, int, node * = 0);
#endif
public:
void *operator new(size_t);
void operator delete(void *);
ligature_node(charinfo *, tfont *, color *, color *,
node *, node *, statem *, int, node * = 0);
~ligature_node();
node *copy();
node *add_self(node *, hyphen_list **);
hyphen_list *get_hyphen_list(hyphen_list *, int *);
void ascii_print(ascii_output_file *);
void asciify(macro *);
int same(node *);
const char *type();
int force_tprint();
int is_tag();
};
class kern_pair_node : public node {
hunits amount;
node *n1;
node *n2;
public:
kern_pair_node(hunits, node *, node *, statem *, int, node * = 0);
~kern_pair_node();
node *copy();
node *merge_glyph_node(glyph_node *);
node *add_self(node *, hyphen_list **);
hyphen_list *get_hyphen_list(hyphen_list *, int *);
node *add_discretionary_hyphen();
hunits width();
node *last_char_node();
hunits italic_correction();
hunits subscript_correction();
void tprint(troff_output_file *);
hyphenation_type get_hyphenation_type();
int ends_sentence();
void ascii_print(ascii_output_file *);
void asciify(macro *);
int same(node *);
const char *type();
int force_tprint();
int is_tag();
void vertical_extent(vunits *, vunits *);
};
class dbreak_node : public node {
node *none;
node *pre;
node *post;
public:
dbreak_node(node *, node *, statem *, int, node * = 0);
~dbreak_node();
node *copy();
node *merge_glyph_node(glyph_node *);
node *add_discretionary_hyphen();
hunits width();
node *last_char_node();
hunits italic_correction();
hunits subscript_correction();
void tprint(troff_output_file *);
breakpoint *get_breakpoints(hunits width, int ns, breakpoint *rest = 0,
int is_inner = 0);
int nbreaks();
int ends_sentence();
void split(int, node **, node **);
hyphenation_type get_hyphenation_type();
void ascii_print(ascii_output_file *);
void asciify(macro *);
int same(node *);
const char *type();
int force_tprint();
int is_tag();
};
void *ligature_node::operator new(size_t n)
{
return new char[n];
}
void ligature_node::operator delete(void *p)
{
delete[] (char *)p;
}
glyph_node::glyph_node(charinfo *c, tfont *t, color *gc, color *fc,
statem *s, int pop, node *x)
: charinfo_node(c, s, pop, x), tf(t), gcol(gc), fcol(fc)
{
#ifdef STORE_WIDTH
wid = tf->get_width(ci);
#endif
}
#ifdef STORE_WIDTH
glyph_node::glyph_node(charinfo *c, tfont *t,
color *gc, color *fc, hunits w,
statem *s, int pop, node *x)
: charinfo_node(c, s, pop, x), tf(t), gcol(gc), fcol(fc), wid(w)
{
}
#endif
node *glyph_node::copy()
{
#ifdef STORE_WIDTH
return new glyph_node(ci, tf, gcol, fcol, wid, state, div_nest_level);
#else
return new glyph_node(ci, tf, gcol, fcol, state, div_nest_level);
#endif
}
node *glyph_node::merge_self(node *nd)
{
return nd->merge_glyph_node(this);
}
int glyph_node::character_type()
{
return tf->get_character_type(ci);
}
node *glyph_node::add_self(node *n, hyphen_list **p)
{
assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
next = 0;
node *nn;
if (n == 0 || (nn = n->merge_glyph_node(this)) == 0) {
next = n;
nn = this;
}
if ((*p)->hyphen)
nn = nn->add_discretionary_hyphen();
hyphen_list *pp = *p;
*p = (*p)->next;
delete pp;
return nn;
}
units glyph_node::size()
{
return tf->get_size().to_units();
}
hyphen_list *glyph_node::get_hyphen_list(hyphen_list *tail, int *count)
{
(*count)++;
return new hyphen_list(ci->get_hyphenation_code(), tail);
}
tfont *node::get_tfont()
{
return 0;
}
tfont *glyph_node::get_tfont()
{
return tf;
}
color *node::get_glyph_color()
{
return 0;
}
color *glyph_node::get_glyph_color()
{
return gcol;
}
color *node::get_fill_color()
{
return 0;
}
color *glyph_node::get_fill_color()
{
return fcol;
}
node *node::merge_glyph_node(glyph_node *)
{
return 0;
}
node *glyph_node::merge_glyph_node(glyph_node *gn)
{
if (tf == gn->tf && gcol == gn->gcol && fcol == gn->fcol) {
charinfo *lig;
if ((lig = tf->get_lig(ci, gn->ci)) != 0) {
node *next1 = next;
next = 0;
return new ligature_node(lig, tf, gcol, fcol, this, gn, state,
gn->div_nest_level, next1);
}
hunits kern;
if (tf->get_kern(ci, gn->ci, &kern)) {
node *next1 = next;
next = 0;
return new kern_pair_node(kern, this, gn, state,
gn->div_nest_level, next1);
}
}
return 0;
}
#ifdef STORE_WIDTH
inline
#endif
hunits glyph_node::width()
{
#ifdef STORE_WIDTH
return wid;
#else
return tf->get_width(ci);
#endif
}
node *glyph_node::last_char_node()
{
return this;
}
void glyph_node::vertical_extent(vunits *min, vunits *max)
{
*min = -tf->get_char_height(ci);
*max = tf->get_char_depth(ci);
}
hunits glyph_node::skew()
{
return tf->get_char_skew(ci);
}
hunits glyph_node::subscript_correction()
{
return tf->get_subscript_correction(ci);
}
hunits glyph_node::italic_correction()
{
return tf->get_italic_correction(ci);
}
hunits glyph_node::left_italic_correction()
{
return tf->get_left_italic_correction(ci);
}
hyphenation_type glyph_node::get_hyphenation_type()
{
return HYPHEN_MIDDLE;
}
void glyph_node::ascii_print(ascii_output_file *ascii)
{
unsigned char c = ci->get_ascii_code();
if (c != 0)
ascii->outc(c);
else
ascii->outs(ci->nm.contents());
}
void glyph_node::debug_node()
{
unsigned char c = ci->get_ascii_code();
fprintf(stderr, "{ %s [", type());
if (c)
fprintf(stderr, "%c", c);
else
fprintf(stderr, "%s", ci->nm.contents());
if (push_state)
fprintf(stderr, " ");
if (state)
state->display_state();
fprintf(stderr, " nest level %d", div_nest_level);
fprintf(stderr, "]}\n");
fflush(stderr);
}
ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
node *gn1, node *gn2, statem *s,
int pop, node *x)
: glyph_node(c, t, gc, fc, s, pop, x), n1(gn1), n2(gn2)
{
}
#ifdef STORE_WIDTH
ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
hunits w, node *gn1, node *gn2, statem *s,
int pop, node *x)
: glyph_node(c, t, gc, fc, w, s, pop, x), n1(gn1), n2(gn2)
{
}
#endif
ligature_node::~ligature_node()
{
delete n1;
delete n2;
}
node *ligature_node::copy()
{
#ifdef STORE_WIDTH
return new ligature_node(ci, tf, gcol, fcol, wid, n1->copy(), n2->copy(),
state, div_nest_level);
#else
return new ligature_node(ci, tf, gcol, fcol, n1->copy(), n2->copy(),
state, div_nest_level);
#endif
}
void ligature_node::ascii_print(ascii_output_file *ascii)
{
n1->ascii_print(ascii);
n2->ascii_print(ascii);
}
hyphen_list *ligature_node::get_hyphen_list(hyphen_list *tail, int *count)
{
hyphen_list *hl = n2->get_hyphen_list(tail, count);
return n1->get_hyphen_list(hl, count);
}
node *ligature_node::add_self(node *n, hyphen_list **p)
{
n = n1->add_self(n, p);
n = n2->add_self(n, p);
n1 = n2 = 0;
delete this;
return n;
}
kern_pair_node::kern_pair_node(hunits n, node *first, node *second,
statem* s, int pop, node *x)
: node(x, s, pop), amount(n), n1(first), n2(second)
{
}
dbreak_node::dbreak_node(node *n, node *p, statem *s, int pop, node *x)
: node(x, s, pop), none(n), pre(p), post(0)
{
}
node *dbreak_node::merge_glyph_node(glyph_node *gn)
{
glyph_node *gn2 = (glyph_node *)gn->copy();
node *new_none = none ? none->merge_glyph_node(gn) : 0;
node *new_post = post ? post->merge_glyph_node(gn2) : 0;
if (new_none == 0 && new_post == 0) {
delete gn2;
return 0;
}
if (new_none != 0)
none = new_none;
else {
gn->next = none;
none = gn;
}
if (new_post != 0)
post = new_post;
else {
gn2->next = post;
post = gn2;
}
return this;
}
node *kern_pair_node::merge_glyph_node(glyph_node *gn)
{
node *nd = n2->merge_glyph_node(gn);
if (nd == 0)
return 0;
n2 = nd;
nd = n2->merge_self(n1);
if (nd) {
nd->next = next;
n1 = 0;
n2 = 0;
delete this;
return nd;
}
return this;
}
hunits kern_pair_node::italic_correction()
{
return n2->italic_correction();
}
hunits kern_pair_node::subscript_correction()
{
return n2->subscript_correction();
}
void kern_pair_node::vertical_extent(vunits *min, vunits *max)
{
n1->vertical_extent(min, max);
vunits min2, max2;
n2->vertical_extent(&min2, &max2);
if (min2 < *min)
*min = min2;
if (max2 > *max)
*max = max2;
}
node *kern_pair_node::add_discretionary_hyphen()
{
tfont *tf = n1->get_tfont();
if (tf) {
if (tf->contains(soft_hyphen_char)) {
color *gcol = n2->get_glyph_color();
color *fcol = n2->get_fill_color();
node *next1 = next;
next = 0;
node *n = copy();
glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol,
state, div_nest_level);
node *nn = n->merge_glyph_node(gn);
if (nn == 0) {
gn->next = n;
nn = gn;
}
return new dbreak_node(this, nn, state, div_nest_level, next1);
}
}
return this;
}
kern_pair_node::~kern_pair_node()
{
if (n1 != 0)
delete n1;
if (n2 != 0)
delete n2;
}
dbreak_node::~dbreak_node()
{
delete_node_list(pre);
delete_node_list(post);
delete_node_list(none);
}
node *kern_pair_node::copy()
{
return new kern_pair_node(amount, n1->copy(), n2->copy(), state,
div_nest_level);
}
node *copy_node_list(node *n)
{
node *p = 0;
while (n != 0) {
node *nn = n->copy();
nn->next = p;
p = nn;
n = n->next;
}
while (p != 0) {
node *pp = p->next;
p->next = n;
n = p;
p = pp;
}
return n;
}
void delete_node_list(node *n)
{
while (n != 0) {
node *tem = n;
n = n->next;
delete tem;
}
}
node *dbreak_node::copy()
{
dbreak_node *p = new dbreak_node(copy_node_list(none), copy_node_list(pre),
state, div_nest_level);
p->post = copy_node_list(post);
return p;
}
hyphen_list *node::get_hyphen_list(hyphen_list *tail, int *)
{
return tail;
}
hyphen_list *kern_pair_node::get_hyphen_list(hyphen_list *tail, int *count)
{
hyphen_list *hl = n2->get_hyphen_list(tail, count);
return n1->get_hyphen_list(hl, count);
}
class hyphen_inhibitor_node : public node {
public:
hyphen_inhibitor_node(node * = 0);
node *copy();
int same(node *);
const char *type();
int force_tprint();
int is_tag();
hyphenation_type get_hyphenation_type();
};
hyphen_inhibitor_node::hyphen_inhibitor_node(node *nd) : node(nd)
{
}
node *hyphen_inhibitor_node::copy()
{
return new hyphen_inhibitor_node;
}
int hyphen_inhibitor_node::same(node *)
{
return 1;
}
const char *hyphen_inhibitor_node::type()
{
return "hyphen_inhibitor_node";
}
int hyphen_inhibitor_node::force_tprint()
{
return 0;
}
int hyphen_inhibitor_node::is_tag()
{
return 0;
}
hyphenation_type hyphen_inhibitor_node::get_hyphenation_type()
{
return HYPHEN_INHIBIT;
}
/* add_discretionary_hyphen methods */
node *dbreak_node::add_discretionary_hyphen()
{
if (post)
post = post->add_discretionary_hyphen();
if (none)
none = none->add_discretionary_hyphen();
return this;
}
node *node::add_discretionary_hyphen()
{
tfont *tf = get_tfont();
if (!tf)
return new hyphen_inhibitor_node(this);
if (tf->contains(soft_hyphen_char)) {
color *gcol = get_glyph_color();
color *fcol = get_fill_color();
node *next1 = next;
next = 0;
node *n = copy();
glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol,
state, div_nest_level);
node *n1 = n->merge_glyph_node(gn);
if (n1 == 0) {
gn->next = n;
n1 = gn;
}
return new dbreak_node(this, n1, state, div_nest_level, next1);
}
return this;
}
node *node::merge_self(node *)
{
return 0;
}
node *node::add_self(node *n, hyphen_list ** /*p*/)
{
next = n;
return this;
}
node *kern_pair_node::add_self(node *n, hyphen_list **p)
{
n = n1->add_self(n, p);
n = n2->add_self(n, p);
n1 = n2 = 0;
delete this;
return n;
}
hunits node::width()
{
return H0;
}
node *node::last_char_node()
{
return 0;
}
int node::force_tprint()
{
return 0;
}
int node::is_tag()
{
return 0;
}
int node::get_break_code()
{
return 0;
}
hunits hmotion_node::width()
{
return n;
}
units node::size()
{
return points_to_units(10);
}
void node::debug_node()
{
fprintf(stderr, "{ %s ", type());
if (push_state)
fprintf(stderr, " ");
if (state)
fprintf(stderr, " ");
fprintf(stderr, " nest level %d", div_nest_level);
fprintf(stderr, " }\n");
fflush(stderr);
}
void node::debug_node_list()
{
node *n = next;
debug_node();
while (n != 0) {
n->debug_node();
n = n->next;
}
}
hunits kern_pair_node::width()
{
return n1->width() + n2->width() + amount;
}
node *kern_pair_node::last_char_node()
{
node *nd = n2->last_char_node();
if (nd)
return nd;
return n1->last_char_node();
}
hunits dbreak_node::width()
{
hunits x = H0;
for (node *n = none; n != 0; n = n->next)
x += n->width();
return x;
}
node *dbreak_node::last_char_node()
{
for (node *n = none; n; n = n->next) {
node *last_node = n->last_char_node();
if (last_node)
return last_node;
}
return 0;
}
hunits dbreak_node::italic_correction()
{
return none ? none->italic_correction() : H0;
}
hunits dbreak_node::subscript_correction()
{
return none ? none->subscript_correction() : H0;
}
class italic_corrected_node : public node {
node *n;
hunits x;
public:
italic_corrected_node(node *, hunits, statem *, int, node * = 0);
~italic_corrected_node();
node *copy();
void ascii_print(ascii_output_file *);
void asciify(macro *);
hunits width();
node *last_char_node();
void vertical_extent(vunits *, vunits *);
int ends_sentence();
int overlaps_horizontally();
int overlaps_vertically();
int same(node *);
hyphenation_type get_hyphenation_type();
tfont *get_tfont();
hyphen_list *get_hyphen_list(hyphen_list *, int *);
int character_type();
void tprint(troff_output_file *);
hunits subscript_correction();
hunits skew();
node *add_self(node *, hyphen_list **);
const char *type();
int force_tprint();
int is_tag();
};
node *node::add_italic_correction(hunits *wd)
{
hunits ic = italic_correction();
if (ic.is_zero())
return this;
else {
node *next1 = next;
next = 0;
*wd += ic;
return new italic_corrected_node(this, ic, state, div_nest_level, next1);
}
}
italic_corrected_node::italic_corrected_node(node *nn, hunits xx, statem *s,
int pop, node *p)
: node(p, s, pop), n(nn), x(xx)
{
assert(n != 0);
}
italic_corrected_node::~italic_corrected_node()
{
delete n;
}
node *italic_corrected_node::copy()
{
return new italic_corrected_node(n->copy(), x, state, div_nest_level);
}
hunits italic_corrected_node::width()
{
return n->width() + x;
}
void italic_corrected_node::vertical_extent(vunits *min, vunits *max)
{
n->vertical_extent(min, max);
}
void italic_corrected_node::tprint(troff_output_file *out)
{
n->tprint(out);
out->right(x);
}
hunits italic_corrected_node::skew()
{
return n->skew() - x/2;
}
hunits italic_corrected_node::subscript_correction()
{
return n->subscript_correction() - x;
}
void italic_corrected_node::ascii_print(ascii_output_file *out)
{
n->ascii_print(out);
}
int italic_corrected_node::ends_sentence()
{
return n->ends_sentence();
}
int italic_corrected_node::overlaps_horizontally()
{
return n->overlaps_horizontally();
}
int italic_corrected_node::overlaps_vertically()
{
return n->overlaps_vertically();
}
node *italic_corrected_node::last_char_node()
{
return n->last_char_node();
}
tfont *italic_corrected_node::get_tfont()
{
return n->get_tfont();
}
hyphenation_type italic_corrected_node::get_hyphenation_type()
{
return n->get_hyphenation_type();
}
node *italic_corrected_node::add_self(node *nd, hyphen_list **p)
{
nd = n->add_self(nd, p);
hunits not_interested;
nd = nd->add_italic_correction(¬_interested);
n = 0;
delete this;
return nd;
}
hyphen_list *italic_corrected_node::get_hyphen_list(hyphen_list *tail,
int *count)
{
return n->get_hyphen_list(tail, count);
}
int italic_corrected_node::character_type()
{
return n->character_type();
}
class break_char_node : public node {
node *ch;
char break_code;
char prev_break_code;
color *col;
public:
break_char_node(node *, int, int, color *, node * = 0);
break_char_node(node *, int, int, color *, statem *, int, node * = 0);
~break_char_node();
node *copy();
hunits width();
vunits vertical_width();
node *last_char_node();
int character_type();
int ends_sentence();
node *add_self(node *, hyphen_list **);
hyphen_list *get_hyphen_list(hyphen_list *, int *);
void tprint(troff_output_file *);
void zero_width_tprint(troff_output_file *);
void ascii_print(ascii_output_file *);
void asciify(macro *);
hyphenation_type get_hyphenation_type();
int overlaps_vertically();
int overlaps_horizontally();
units size();
tfont *get_tfont();
int same(node *);
const char *type();
int force_tprint();
int is_tag();
int get_break_code();
};
break_char_node::break_char_node(node *n, int bc, int pbc, color *c, node *x)
: node(x), ch(n), break_code(bc), prev_break_code(pbc), col(c)
{
}
break_char_node::break_char_node(node *n, int bc, int pbc, color *c,
statem *s, int pop, node *x)
: node(x, s, pop), ch(n), break_code(bc), prev_break_code(pbc), col(c)
{
}
break_char_node::~break_char_node()
{
delete ch;
}
node *break_char_node::copy()
{
return new break_char_node(ch->copy(), break_code, prev_break_code,
col, state, div_nest_level);
}
hunits break_char_node::width()
{
return ch->width();
}
vunits break_char_node::vertical_width()
{
return ch->vertical_width();
}
node *break_char_node::last_char_node()
{
return ch->last_char_node();
}
int break_char_node::character_type()
{
return ch->character_type();
}
int break_char_node::ends_sentence()
{
return ch->ends_sentence();
}
enum break_char_type {
CAN_BREAK_BEFORE = 0x01,
CAN_BREAK_AFTER = 0x02,
IGNORE_HCODES = 0x04,
PROHIBIT_BREAK_BEFORE = 0x08,
PROHIBIT_BREAK_AFTER = 0x10,
INTER_CHAR_SPACE = 0x20
};
node *break_char_node::add_self(node *n, hyphen_list **p)
{
int have_space_node = 0;
assert((*p)->hyphenation_code == 0);
if (break_code & CAN_BREAK_BEFORE) {
if ((*p)->breakable || break_code & IGNORE_HCODES) {
n = new space_node(H0, col, n);
n->freeze_space();
have_space_node = 1;
}
}
if (!have_space_node) {
if (prev_break_code & INTER_CHAR_SPACE
|| prev_break_code & PROHIBIT_BREAK_AFTER) {
if (break_code & PROHIBIT_BREAK_BEFORE)
// stretchable zero-width space not implemented yet
;
else {
// breakable, stretchable zero-width space not implemented yet
n = new space_node(H0, col, n);
n->freeze_space();
}
}
}
next = n;
n = this;
if (break_code & CAN_BREAK_AFTER) {
if ((*p)->breakable || break_code & IGNORE_HCODES) {
n = new space_node(H0, col, n);
n->freeze_space();
}
}
hyphen_list *pp = *p;
*p = (*p)->next;
delete pp;
return n;
}
hyphen_list *break_char_node::get_hyphen_list(hyphen_list *tail, int *)
{
return new hyphen_list(0, tail);
}
hyphenation_type break_char_node::get_hyphenation_type()
{
return HYPHEN_MIDDLE;
}
void break_char_node::ascii_print(ascii_output_file *ascii)
{
ch->ascii_print(ascii);
}
int break_char_node::overlaps_vertically()
{
return ch->overlaps_vertically();
}
int break_char_node::overlaps_horizontally()
{
return ch->overlaps_horizontally();
}
units break_char_node::size()
{
return ch->size();
}
tfont *break_char_node::get_tfont()
{
return ch->get_tfont();
}
node *extra_size_node::copy()
{
return new extra_size_node(n, state, div_nest_level);
}
extra_size_node::extra_size_node(vunits i, statem *s, int pop)
: node(0, s, pop), n(i)
{
}
extra_size_node::extra_size_node(vunits i)
: n(i)
{
}
node *vertical_size_node::copy()
{
return new vertical_size_node(n, state, div_nest_level);
}
vertical_size_node::vertical_size_node(vunits i, statem *s, int pop)
: node(0, s, pop), n(i)
{
}
vertical_size_node::vertical_size_node(vunits i)
: n(i)
{
}
node *hmotion_node::copy()
{
return new hmotion_node(n, was_tab, unformat, col, state, div_nest_level);
}
node *space_char_hmotion_node::copy()
{
return new space_char_hmotion_node(n, col, state, div_nest_level);
}
vmotion_node::vmotion_node(vunits i, color *c)
: n(i), col(c)
{
}
vmotion_node::vmotion_node(vunits i, color *c, statem *s, int pop)
: node(0, s, pop), n(i), col(c)
{
}
node *vmotion_node::copy()
{
return new vmotion_node(n, col, state, div_nest_level);
}
node *dummy_node::copy()
{
return new dummy_node;
}
node *transparent_dummy_node::copy()
{
return new transparent_dummy_node;
}
hline_node::~hline_node()
{
if (n)
delete n;
}
hline_node::hline_node(hunits i, node *c, node *nxt)
: node(nxt), x(i), n(c)
{
}
hline_node::hline_node(hunits i, node *c, statem *s, int pop, node *nxt)
: node(nxt, s, pop), x(i), n(c)
{
}
node *hline_node::copy()
{
return new hline_node(x, n ? n->copy() : 0, state, div_nest_level);
}
hunits hline_node::width()
{
return x < H0 ? H0 : x;
}
vline_node::vline_node(vunits i, node *c, node *nxt)
: node(nxt), x(i), n(c)
{
}
vline_node::vline_node(vunits i, node *c, statem *s, int pop, node *nxt)
: node(nxt, s, pop), x(i), n(c)
{
}
vline_node::~vline_node()
{
if (n)
delete n;
}
node *vline_node::copy()
{
return new vline_node(x, n ? n->copy() : 0, state, div_nest_level);
}
hunits vline_node::width()
{
return n == 0 ? H0 : n->width();
}
zero_width_node::zero_width_node(node *nd, statem *s, int pop)
: node(0, s, pop), n(nd)
{
}
zero_width_node::zero_width_node(node *nd)
: n(nd)
{
}
zero_width_node::~zero_width_node()
{
delete_node_list(n);
}
node *zero_width_node::copy()
{
return new zero_width_node(copy_node_list(n), state, div_nest_level);
}
int node_list_character_type(node *p)
{
int t = 0;
for (; p; p = p->next)
t |= p->character_type();
return t;
}
int zero_width_node::character_type()
{
return node_list_character_type(n);
}
void node_list_vertical_extent(node *p, vunits *min, vunits *max)
{
*min = V0;
*max = V0;
vunits cur_vpos = V0;
vunits v1, v2;
for (; p; p = p->next) {
p->vertical_extent(&v1, &v2);
v1 += cur_vpos;
if (v1 < *min)
*min = v1;
v2 += cur_vpos;
if (v2 > *max)
*max = v2;
cur_vpos += p->vertical_width();
}
}
void zero_width_node::vertical_extent(vunits *min, vunits *max)
{
node_list_vertical_extent(n, min, max);
}
overstrike_node::overstrike_node()
: list(0), max_width(H0)
{
}
overstrike_node::overstrike_node(statem *s, int pop)
: node(0, s, pop), list(0), max_width(H0)
{
}
overstrike_node::~overstrike_node()
{
delete_node_list(list);
}
node *overstrike_node::copy()
{
overstrike_node *on = new overstrike_node(state, div_nest_level);
for (node *tem = list; tem; tem = tem->next)
on->overstrike(tem->copy());
return on;
}
void overstrike_node::overstrike(node *n)
{
if (n == 0)
return;
hunits w = n->width();
if (w > max_width)
max_width = w;
node **p;
for (p = &list; *p; p = &(*p)->next)
;
n->next = 0;
*p = n;
}
hunits overstrike_node::width()
{
return max_width;
}
bracket_node::bracket_node()
: list(0), max_width(H0)
{
}
bracket_node::bracket_node(statem *s, int pop)
: node(0, s, pop), list(0), max_width(H0)
{
}
bracket_node::~bracket_node()
{
delete_node_list(list);
}
node *bracket_node::copy()
{
bracket_node *on = new bracket_node(state, div_nest_level);
node *last_node = 0;
node *tem;
if (list)
list->last = 0;
for (tem = list; tem; tem = tem->next) {
if (tem->next)
tem->next->last = tem;
last_node = tem;
}
for (tem = last_node; tem; tem = tem->last)
on->bracket(tem->copy());
return on;
}
void bracket_node::bracket(node *n)
{
if (n == 0)
return;
hunits w = n->width();
if (w > max_width)
max_width = w;
n->next = list;
list = n;
}
hunits bracket_node::width()
{
return max_width;
}
int node::nspaces()
{
return 0;
}
int node::merge_space(hunits, hunits, hunits)
{
return 0;
}
space_node::space_node(hunits nn, color *c, node *p)
: node(p, 0, 0), n(nn), set(0), was_escape_colon(0), col(c)
{
}
space_node::space_node(hunits nn, int s, int flag, color *c, statem *st,
int pop, node *p)
: node(p, st, pop), n(nn), set(s), was_escape_colon(flag), col(c)
{
}
#if 0
space_node::~space_node()
{
}
#endif
node *space_node::copy()
{
return new space_node(n, set, was_escape_colon, col, state, div_nest_level);
}
int space_node::force_tprint()
{
return 0;
}
int space_node::is_tag()
{
return 0;
}
int space_node::nspaces()
{
return set ? 0 : 1;
}
int space_node::merge_space(hunits h, hunits, hunits)
{
n += h;
return 1;
}
hunits space_node::width()
{
return n;
}
void node::spread_space(int*, hunits*)
{
}
void space_node::spread_space(int *n_spaces, hunits *desired_space)
{
if (!set) {
assert(*n_spaces > 0);
if (*n_spaces == 1) {
n += *desired_space;
*desired_space = H0;
}
else {
hunits extra = *desired_space / *n_spaces;
*desired_space -= extra;
n += extra;
}
*n_spaces -= 1;
set = 1;
}
}
void node::freeze_space()
{
}
void space_node::freeze_space()
{
set = 1;
}
void node::is_escape_colon()
{
}
void space_node::is_escape_colon()
{
was_escape_colon = 1;
}
diverted_space_node::diverted_space_node(vunits d, statem *s, int pop,
node *p)
: node(p, s, pop), n(d)
{
}
diverted_space_node::diverted_space_node(vunits d, node *p)
: node(p), n(d)
{
}
node *diverted_space_node::copy()
{
return new diverted_space_node(n, state, div_nest_level);
}
diverted_copy_file_node::diverted_copy_file_node(symbol s, statem *st,
int pop, node *p)
: node(p, st, pop), filename(s)
{
}
diverted_copy_file_node::diverted_copy_file_node(symbol s, node *p)
: node(p), filename(s)
{
}
node *diverted_copy_file_node::copy()
{
return new diverted_copy_file_node(filename, state, div_nest_level);
}
int node::ends_sentence()
{
return 0;
}
int kern_pair_node::ends_sentence()
{
switch (n2->ends_sentence()) {
case 0:
return 0;
case 1:
return 1;
case 2:
break;
default:
assert(0);
}
return n1->ends_sentence();
}
int node_list_ends_sentence(node *n)
{
for (; n != 0; n = n->next)
switch (n->ends_sentence()) {
case 0:
return 0;
case 1:
return 1;
case 2:
break;
default:
assert(0);
}
return 2;
}
int dbreak_node::ends_sentence()
{
return node_list_ends_sentence(none);
}
int node::overlaps_horizontally()
{
return 0;
}
int node::overlaps_vertically()
{
return 0;
}
int node::discardable()
{
return 0;
}
int space_node::discardable()
{
return set ? 0 : 1;
}
vunits node::vertical_width()
{
return V0;
}
vunits vline_node::vertical_width()
{
return x;
}
vunits vmotion_node::vertical_width()
{
return n;
}
int node::set_unformat_flag()
{
return 1;
}
int node::character_type()
{
return 0;
}
hunits node::subscript_correction()
{
return H0;
}
hunits node::italic_correction()
{
return H0;
}
hunits node::left_italic_correction()
{
return H0;
}
hunits node::skew()
{
return H0;
}
/* vertical_extent methods */
void node::vertical_extent(vunits *min, vunits *max)
{
vunits v = vertical_width();
if (v < V0) {
*min = v;
*max = V0;
}
else {
*max = v;
*min = V0;
}
}
void vline_node::vertical_extent(vunits *min, vunits *max)
{
if (n == 0)
node::vertical_extent(min, max);
else {
vunits cmin, cmax;
n->vertical_extent(&cmin, &cmax);
vunits h = n->size();
if (x < V0) {
if (-x < h) {
*min = x;
*max = V0;
}
else {
// we print the first character and then move up, so
*max = cmax;
// we print the last character and then move up h
*min = cmin + h;
if (*min > V0)
*min = V0;
*min += x;
}
}
else {
if (x < h) {
*max = x;
*min = V0;
}
else {
// we move down by h and then print the first character, so
*min = cmin + h;
if (*min > V0)
*min = V0;
*max = x + cmax;
}
}
}
}
/* ascii_print methods */
static void ascii_print_reverse_node_list(ascii_output_file *ascii, node *n)
{
if (n == 0)
return;
ascii_print_reverse_node_list(ascii, n->next);
n->ascii_print(ascii);
}
void dbreak_node::ascii_print(ascii_output_file *ascii)
{
ascii_print_reverse_node_list(ascii, none);
}
void kern_pair_node::ascii_print(ascii_output_file *ascii)
{
n1->ascii_print(ascii);
n2->ascii_print(ascii);
}
void node::ascii_print(ascii_output_file *)
{
}
void space_node::ascii_print(ascii_output_file *ascii)
{
if (!n.is_zero())
ascii->outc(' ');
}
void hmotion_node::ascii_print(ascii_output_file *ascii)
{
// this is pretty arbitrary
if (n >= points_to_units(2))
ascii->outc(' ');
}
void space_char_hmotion_node::ascii_print(ascii_output_file *ascii)
{
ascii->outc(' ');
}
/* asciify methods */
void node::asciify(macro *m)
{
m->append(this);
}
void glyph_node::asciify(macro *m)
{
unsigned char c = ci->get_asciify_code();
if (c == 0)
c = ci->get_ascii_code();
if (c != 0) {
m->append(c);
delete this;
}
else
m->append(this);
}
void kern_pair_node::asciify(macro *m)
{
n1->asciify(m);
n2->asciify(m);
n1 = n2 = 0;
delete this;
}
static void asciify_reverse_node_list(macro *m, node *n)
{
if (n == 0)
return;
asciify_reverse_node_list(m, n->next);
n->asciify(m);
}
void dbreak_node::asciify(macro *m)
{
asciify_reverse_node_list(m, none);
none = 0;
delete this;
}
void ligature_node::asciify(macro *m)
{
n1->asciify(m);
n2->asciify(m);
n1 = n2 = 0;
delete this;
}
void break_char_node::asciify(macro *m)
{
ch->asciify(m);
ch = 0;
delete this;
}
void italic_corrected_node::asciify(macro *m)
{
n->asciify(m);
n = 0;
delete this;
}
void left_italic_corrected_node::asciify(macro *m)
{
if (n) {
n->asciify(m);
n = 0;
}
delete this;
}
void hmotion_node::asciify(macro *m)
{
if (was_tab) {
m->append('\t');
delete this;
}
else
m->append(this);
}
space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c,
statem *s, int pop,
node *nxt)
: hmotion_node(i, c, s, pop, nxt)
{
}
space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c,
node *nxt)
: hmotion_node(i, c, 0, 0, nxt)
{
}
void space_char_hmotion_node::asciify(macro *m)
{
m->append(ESCAPE_SPACE);
delete this;
}
void space_node::asciify(macro *m)
{
if (was_escape_colon) {
m->append(ESCAPE_COLON);
delete this;
}
else
m->append(this);
}
void word_space_node::asciify(macro *m)
{
for (width_list *w = orig_width; w; w = w->next)
m->append(' ');
delete this;
}
void unbreakable_space_node::asciify(macro *m)
{
m->append(ESCAPE_TILDE);
delete this;
}
void line_start_node::asciify(macro *)
{
delete this;
}
void vertical_size_node::asciify(macro *)
{
delete this;
}
breakpoint *node::get_breakpoints(hunits /*width*/, int /*nspaces*/,
breakpoint *rest, int /*is_inner*/)
{
return rest;
}
int node::nbreaks()
{
return 0;
}
breakpoint *space_node::get_breakpoints(hunits wd, int ns,
breakpoint *rest, int is_inner)
{
if (next && next->discardable())
return rest;
breakpoint *bp = new breakpoint;
bp->next = rest;
bp->width = wd;
bp->nspaces = ns;
bp->hyphenated = 0;
if (is_inner) {
assert(rest != 0);
bp->index = rest->index + 1;
bp->nd = rest->nd;
}
else {
bp->nd = this;
bp->index = 0;
}
return bp;
}
int space_node::nbreaks()
{
if (next && next->discardable())
return 0;
else
return 1;
}
static breakpoint *node_list_get_breakpoints(node *p, hunits *widthp,
int ns, breakpoint *rest)
{
if (p != 0) {
rest = p->get_breakpoints(*widthp,
ns,
node_list_get_breakpoints(p->next, widthp, ns,
rest),
1);
*widthp += p->width();
}
return rest;
}
breakpoint *dbreak_node::get_breakpoints(hunits wd, int ns,
breakpoint *rest, int is_inner)
{
breakpoint *bp = new breakpoint;
bp->next = rest;
bp->width = wd;
for (node *tem = pre; tem != 0; tem = tem->next)
bp->width += tem->width();
bp->nspaces = ns;
bp->hyphenated = 1;
if (is_inner) {
assert(rest != 0);
bp->index = rest->index + 1;
bp->nd = rest->nd;
}
else {
bp->nd = this;
bp->index = 0;
}
return node_list_get_breakpoints(none, &wd, ns, bp);
}
int dbreak_node::nbreaks()
{
int i = 1;
for (node *tem = none; tem != 0; tem = tem->next)
i += tem->nbreaks();
return i;
}
void node::split(int /*where*/, node ** /*prep*/, node ** /*postp*/)
{
assert(0);
}
void space_node::split(int where, node **pre, node **post)
{
assert(where == 0);
*pre = next;
*post = 0;
delete this;
}
static void node_list_split(node *p, int *wherep, node **prep, node **postp)
{
if (p == 0)
return;
int nb = p->nbreaks();
node_list_split(p->next, wherep, prep, postp);
if (*wherep < 0) {
p->next = *postp;
*postp = p;
}
else if (*wherep < nb) {
p->next = *prep;
p->split(*wherep, prep, postp);
}
else {
p->next = *prep;
*prep = p;
}
*wherep -= nb;
}
void dbreak_node::split(int where, node **prep, node **postp)
{
assert(where >= 0);
if (where == 0) {
*postp = post;
post = 0;
if (pre == 0)
*prep = next;
else {
node *tem;
for (tem = pre; tem->next != 0; tem = tem->next)
;
tem->next = next;
*prep = pre;
}
pre = 0;
delete this;
}
else {
*prep = next;
where -= 1;
node_list_split(none, &where, prep, postp);
none = 0;
delete this;
}
}
hyphenation_type node::get_hyphenation_type()
{
return HYPHEN_BOUNDARY;
}
hyphenation_type dbreak_node::get_hyphenation_type()
{
return HYPHEN_INHIBIT;
}
hyphenation_type kern_pair_node::get_hyphenation_type()
{
return HYPHEN_MIDDLE;
}
hyphenation_type dummy_node::get_hyphenation_type()
{
return HYPHEN_MIDDLE;
}
hyphenation_type transparent_dummy_node::get_hyphenation_type()
{
return HYPHEN_MIDDLE;
}
hyphenation_type hmotion_node::get_hyphenation_type()
{
return HYPHEN_MIDDLE;
}
hyphenation_type space_char_hmotion_node::get_hyphenation_type()
{
return HYPHEN_MIDDLE;
}
hyphenation_type overstrike_node::get_hyphenation_type()
{
return HYPHEN_MIDDLE;
}
hyphenation_type space_node::get_hyphenation_type()
{
if (was_escape_colon)
return HYPHEN_MIDDLE;
return HYPHEN_BOUNDARY;
}
hyphenation_type unbreakable_space_node::get_hyphenation_type()
{
return HYPHEN_MIDDLE;
}
int node::interpret(macro *)
{
return 0;
}
special_node::special_node(const macro &m, int n)
: mac(m), no_init_string(n)
{
font_size fs = curenv->get_font_size();
int char_height = curenv->get_char_height();
int char_slant = curenv->get_char_slant();
int fontno = env_definite_font(curenv);
tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fontno);
if (curenv->is_composite())
tf = tf->get_plain();
gcol = curenv->get_glyph_color();
fcol = curenv->get_fill_color();
is_special = 1;
}
special_node::special_node(const macro &m, tfont *t,
color *gc, color *fc,
statem *s, int pop,
int n)
: node(0, s, pop), mac(m), tf(t), gcol(gc), fcol(fc), no_init_string(n)
{
is_special = 1;
}
int special_node::same(node *n)
{
return mac == ((special_node *)n)->mac
&& tf == ((special_node *)n)->tf
&& gcol == ((special_node *)n)->gcol
&& fcol == ((special_node *)n)->fcol
&& no_init_string == ((special_node *)n)->no_init_string;
}
const char *special_node::type()
{
return "special_node";
}
int special_node::ends_sentence()
{
return 2;
}
int special_node::force_tprint()
{
return 0;
}
int special_node::is_tag()
{
return 0;
}
node *special_node::copy()
{
return new special_node(mac, tf, gcol, fcol, state, div_nest_level,
no_init_string);
}
void special_node::tprint_start(troff_output_file *out)
{
out->start_special(tf, gcol, fcol, no_init_string);
}
void special_node::tprint_char(troff_output_file *out, unsigned char c)
{
out->special_char(c);
}
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),
image_id(0)
{
}
suppress_node::suppress_node(symbol f, char p, int id)
: is_on(2), emit_limits(0), filename(f), position(p), image_id(id)
{
is_special = 1;
}
suppress_node::suppress_node(int issue_limits, int on_or_off,
symbol f, char p, int id,
statem *s, int pop)
: node(0, s, pop), is_on(on_or_off), emit_limits(issue_limits), filename(f),
position(p), image_id(id)
{
}
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)
&& (image_id == ((suppress_node *)n)->image_id));
}
const char *suppress_node::type()
{
return "suppress_node";
}
node *suppress_node::copy()
{
return new suppress_node(emit_limits, is_on, filename, position, image_id,
state, div_nest_level);
}
/* tag_node */
tag_node::tag_node()
: delayed(0)
{
is_special = 1;
}
tag_node::tag_node(string s, int delay)
: tag_string(s), delayed(delay)
{
is_special = !delay;
}
tag_node::tag_node(string s, statem *st, int pop, int delay)
: node(0, st, pop), tag_string(s), delayed(delay)
{
is_special = !delay;
}
node *tag_node::copy()
{
return new tag_node(tag_string, state, div_nest_level, delayed);
}
void tag_node::tprint(troff_output_file *out)
{
if (delayed)
out->add_to_tag_list(tag_string);
else
out->state.add_tag(out->fp, tag_string);
}
int tag_node::same(node *nd)
{
return tag_string == ((tag_node *)nd)->tag_string
&& delayed == ((tag_node *)nd)->delayed;
}
const char *tag_node::type()
{
return "tag_node";
}
int tag_node::force_tprint()
{
return !delayed;
}
int tag_node::is_tag()
{
return !delayed;
}
int tag_node::ends_sentence()
{
return 2;
}
// Get contents of register `p` as integer.
// Used only by suppress_node::tprint().
static int get_register(const char *p)
{
assert(p != 0 /* nullptr */);
reg *r = (reg *)register_dictionary.lookup(p);
assert(r != 0 /* nullptr */);
units value;
assert(r->get_value(&value));
return int(value);
}
// Get contents of register `p` as string.
// Used only by suppress_node::tprint().
static const char *get_string(const char *p)
{
assert(p != 0 /* nullptr */);
reg *r = (reg *)register_dictionary.lookup(p);
assert(r != 0 /* nullptr */);
return r->get_string();
}
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 need to remember the start of the image and its name (\O5). But
* we won't always need this information; for instance, \O2 is used to
* produce a bounding box with no associated image or position thereof.
*/
static char last_position = 0;
static const char *image_filename = "";
static size_t image_filename_len = 0;
static int subimage_counter = 0;
/*
* 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 = topdiv->get_page_number();
// Does the node have an associated position and file name?
if (is_on == 2) {
// Save them for future bounding box limits.
last_position = position;
image_filename = strsave(filename.contents());
image_filename_len = strlen(image_filename);
}
else { // is_on = 0 or 1
// Now check whether the suppress node requires us to issue limits.
if (emit_limits) {
const size_t namebuflen = 8192;
char name[namebuflen] = { '\0' };
// Jump through a flaming hoop to avoid a "format nonliteral"
// warning from blindly using sprintf...and avoid trouble from
// mischievous image stems.
//
// Keep this format string synced with pre-html:makeFileName().
const char format[] = "%d";
const size_t format_len = strlen(format);
const char *percent_position = strstr(image_filename, format);
if (percent_position) {
subimage_counter++;
assert(sizeof subimage_counter <= 8);
// A 64-bit signed int produces up to 19 decimal digits.
char *subimage_number = (char *)malloc(20); // 19 digits + \0
if (0 == subimage_number)
fatal("memory allocation failure");
// Replace the %d in the filename with this number.
size_t enough = image_filename_len + 19 - format_len;
char *new_name = (char *)malloc(enough);
if (0 == new_name)
fatal("memory allocation failure");
ptrdiff_t prefix_length = percent_position - image_filename;
strncpy(new_name, image_filename, prefix_length);
sprintf(subimage_number, "%d", subimage_counter);
size_t number_length = strlen(subimage_number);
strcpy(new_name + prefix_length, subimage_number);
// Skip over the format in the source string.
const char *suffix_src = image_filename + prefix_length
+ format_len;
char *suffix_dst = new_name + prefix_length + number_length;
strcpy(suffix_dst, suffix_src);
// Ensure the new string fits with room for a terminal '\0'.
const size_t len = strlen(new_name);
if (len > (namebuflen - 1))
error("constructed file name in suppressed output escape"
" sequence is too long (>= %1 bytes); skipping image",
(int)namebuflen);
else
strncpy(name, new_name, (namebuflen - 1));
free(new_name);
free(subimage_number);
}
else {
if (image_filename_len > (namebuflen - 1))
error("file name in suppressed output escape sequence is too"
" long (>= %1 bytes); skipping image", (int)namebuflen);
else
strcpy(name, image_filename);
}
if (is_html) {
switch (last_position) {
case 'c':
out->start_special();
put(out, "devtag:.centered-image");
break;
case 'r':
out->start_special();
put(out, "devtag:.right-image");
break;
case 'l':
out->start_special();
put(out, "devtag:.left-image");
break;
case 'i':
;
default:
;
}
out->end_special();
out->start_special();
put(out, "devtag:.auto-image ");
put(out, name);
out->end_special();
}
else {
// postscript (or other device)
if (suppress_start_page > 0
&& (current_page != suppress_start_page))
error("suppression limit registers span more than a page;"
" grohtml-info for image %1 will be wrong", image_no);
// if (topdiv->get_page_number() != suppress_start_page)
// fprintf(stderr, "end of image and topdiv page = %d and"
// " suppress_start_page = %d\n",
// topdiv->get_page_number(), suppress_start_page);
// `name` 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:%s\n",
topdiv->get_page_number(),
get_register("opminx"), get_register("opminy"),
get_register("opmaxx"), get_register("opmaxy"),
// page offset + line length
get_register(".o") + get_register(".l"),
name, hresolution, vresolution, get_string(".F"),
get_string(".c"));
fflush(stderr);
}
}
else { // We are not emitting limits.
if (is_on) {
out->on();
reset_output_registers();
}
else
out->off();
suppress_start_page = current_page;
}
} // is_on
}
int suppress_node::force_tprint()
{
return is_on;
}
int suppress_node::is_tag()
{
return is_on;
}
hunits suppress_node::width()
{
return H0;
}
/* composite_node */
class composite_node : public charinfo_node {
node *n;
tfont *tf;
public:
composite_node(node *, charinfo *, tfont *, statem *, int, node * = 0);
~composite_node();
node *copy();
hunits width();
node *last_char_node();
units size();
void tprint(troff_output_file *);
hyphenation_type get_hyphenation_type();
void ascii_print(ascii_output_file *);
void asciify(macro *);
hyphen_list *get_hyphen_list(hyphen_list *, int *);
node *add_self(node *, hyphen_list **);
tfont *get_tfont();
int same(node *);
const char *type();
int force_tprint();
int is_tag();
void vertical_extent(vunits *, vunits *);
vunits vertical_width();
};
composite_node::composite_node(node *p, charinfo *c, tfont *t, statem *s,
int pop, node *x)
: charinfo_node(c, s, pop, x), n(p), tf(t)
{
}
composite_node::~composite_node()
{
delete_node_list(n);
}
node *composite_node::copy()
{
return new composite_node(copy_node_list(n), ci, tf, state, div_nest_level);
}
hunits composite_node::width()
{
hunits x;
if (tf->get_constant_space(&x))
return x;
x = H0;
for (node *tem = n; tem; tem = tem->next)
x += tem->width();
hunits offset;
if (tf->get_bold(&offset))
x += offset;
x += tf->get_track_kern();
return x;
}
node *composite_node::last_char_node()
{
return this;
}
vunits composite_node::vertical_width()
{
vunits v = V0;
for (node *tem = n; tem; tem = tem->next)
v += tem->vertical_width();
return v;
}
units composite_node::size()
{
return tf->get_size().to_units();
}
hyphenation_type composite_node::get_hyphenation_type()
{
return HYPHEN_MIDDLE;
}
void composite_node::asciify(macro *m)
{
unsigned char c = ci->get_asciify_code();
if (c == 0)
c = ci->get_ascii_code();
if (c != 0) {
m->append(c);
delete this;
}
else
m->append(this);
}
void composite_node::ascii_print(ascii_output_file *ascii)
{
unsigned char c = ci->get_ascii_code();
if (c != 0)
ascii->outc(c);
else
ascii->outs(ci->nm.contents());
}
hyphen_list *composite_node::get_hyphen_list(hyphen_list *tail, int *count)
{
(*count)++;
return new hyphen_list(ci->get_hyphenation_code(), tail);
}
node *composite_node::add_self(node *nn, hyphen_list **p)
{
assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
next = nn;
nn = this;
if ((*p)->hyphen)
nn = nn->add_discretionary_hyphen();
hyphen_list *pp = *p;
*p = (*p)->next;
delete pp;
return nn;
}
tfont *composite_node::get_tfont()
{
return tf;
}
node *reverse_node_list(node *n)
{
node *r = 0;
while (n) {
node *tem = n;
n = n->next;
tem->next = r;
r = tem;
}
return r;
}
void composite_node::vertical_extent(vunits *minimum, vunits *maximum)
{
n = reverse_node_list(n);
node_list_vertical_extent(n, minimum, maximum);
n = reverse_node_list(n);
}
width_list::width_list(hunits w, hunits s)
: width(w), sentence_width(s), next(0)
{
}
width_list::width_list(width_list *w)
: width(w->width), sentence_width(w->sentence_width), next(0)
{
}
word_space_node::word_space_node(hunits d, color *c, width_list *w, node *x)
: space_node(d, c, x), orig_width(w), unformat(0)
{
}
word_space_node::word_space_node(hunits d, int s, color *c, width_list *w,
int flag, statem *st, int pop, node *x)
: space_node(d, s, 0, c, st, pop, x), orig_width(w), unformat(flag)
{
}
word_space_node::~word_space_node()
{
width_list *w = orig_width;
while (w != 0) {
width_list *tmp = w;
w = w->next;
delete tmp;
}
}
node *word_space_node::copy()
{
assert(orig_width != 0);
width_list *w_old_curr = orig_width;
width_list *w_new_curr = new width_list(w_old_curr);
width_list *w_new = w_new_curr;
w_old_curr = w_old_curr->next;
while (w_old_curr != 0) {
w_new_curr->next = new width_list(w_old_curr);
w_new_curr = w_new_curr->next;
w_old_curr = w_old_curr->next;
}
return new word_space_node(n, set, col, w_new, unformat, state,
div_nest_level);
}
int word_space_node::set_unformat_flag()
{
unformat = 1;
return 1;
}
void word_space_node::tprint(troff_output_file *out)
{
out->fill_color(col);
out->word_marker();
out->right(n);
}
int word_space_node::merge_space(hunits h, hunits sw, hunits ssw)
{
n += h;
assert(orig_width != 0);
width_list *w = orig_width;
for (; w->next; w = w->next)
;
w->next = new width_list(sw, ssw);
return 1;
}
unbreakable_space_node::unbreakable_space_node(hunits d, color *c, node *x)
: word_space_node(d, c, 0, x)
{
}
unbreakable_space_node::unbreakable_space_node(hunits d, int s,
color *c, statem *st, int pop,
node *x)
: word_space_node(d, s, c, 0, 0, st, pop, x)
{
}
node *unbreakable_space_node::copy()
{
return new unbreakable_space_node(n, set, col, state, div_nest_level);
}
int unbreakable_space_node::force_tprint()
{
return 0;
}
int unbreakable_space_node::is_tag()
{
return 0;
}
breakpoint *unbreakable_space_node::get_breakpoints(hunits, int,
breakpoint *rest, int)
{
return rest;
}
int unbreakable_space_node::nbreaks()
{
return 0;
}
void unbreakable_space_node::split(int, node **, node **)
{
assert(0);
}
int unbreakable_space_node::merge_space(hunits, hunits, hunits)
{
return 0;
}
hvpair::hvpair()
{
}
draw_node::draw_node(char c, hvpair *p, int np, font_size s,
color *gc, color *fc)
: npoints(np), sz(s), gcol(gc), fcol(fc), code(c)
{
point = new hvpair[npoints];
for (int i = 0; i < npoints; i++)
point[i] = p[i];
}
draw_node::draw_node(char c, hvpair *p, int np, font_size s,
color *gc, color *fc, statem *st, int pop)
: node(0, st, pop), npoints(np), sz(s), gcol(gc), fcol(fc), code(c)
{
point = new hvpair[npoints];
for (int i = 0; i < npoints; i++)
point[i] = p[i];
}
int draw_node::same(node *n)
{
draw_node *nd = (draw_node *)n;
if (code != nd->code || npoints != nd->npoints || sz != nd->sz
|| gcol != nd->gcol || fcol != nd->fcol)
return 0;
for (int i = 0; i < npoints; i++)
if (point[i].h != nd->point[i].h || point[i].v != nd->point[i].v)
return 0;
return 1;
}
const char *draw_node::type()
{
return "draw_node";
}
int draw_node::force_tprint()
{
return 0;
}
int draw_node::is_tag()
{
return 0;
}
draw_node::~draw_node()
{
if (point)
delete[] point;
}
hunits draw_node::width()
{
hunits x = H0;
for (int i = 0; i < npoints; i++)
x += point[i].h;
return x;
}
vunits draw_node::vertical_width()
{
if (code == 'e')
return V0;
vunits x = V0;
for (int i = 0; i < npoints; i++)
x += point[i].v;
return x;
}
node *draw_node::copy()
{
return new draw_node(code, point, npoints, sz, gcol, fcol, state,
div_nest_level);
}
void draw_node::tprint(troff_output_file *out)
{
out->draw(code, point, npoints, sz, gcol, fcol);
}
/* tprint methods */
void glyph_node::tprint(troff_output_file *out)
{
tfont *ptf = tf->get_plain();
if (ptf == tf)
out->put_char_width(ci, ptf, gcol, fcol, width(), H0);
else {
hunits offset;
int bold = tf->get_bold(&offset);
hunits w = ptf->get_width(ci);
hunits k = H0;
hunits x;
int cs = tf->get_constant_space(&x);
if (cs) {
x -= w;
if (bold)
x -= offset;
hunits x2 = x/2;
out->right(x2);
k = x - x2;
}
else
k = tf->get_track_kern();
if (bold) {
out->put_char(ci, ptf, gcol, fcol);
out->right(offset);
}
out->put_char_width(ci, ptf, gcol, fcol, w, k);
}
}
void glyph_node::zero_width_tprint(troff_output_file *out)
{
tfont *ptf = tf->get_plain();
hunits offset;
int bold = tf->get_bold(&offset);
hunits x;
int cs = tf->get_constant_space(&x);
if (cs) {
x -= ptf->get_width(ci);
if (bold)
x -= offset;
x = x/2;
out->right(x);
}
out->put_char(ci, ptf, gcol, fcol);
if (bold) {
out->right(offset);
out->put_char(ci, ptf, gcol, fcol);
out->right(-offset);
}
if (cs)
out->right(-x);
}
void break_char_node::tprint(troff_output_file *t)
{
ch->tprint(t);
}
void break_char_node::zero_width_tprint(troff_output_file *t)
{
ch->zero_width_tprint(t);
}
void hline_node::tprint(troff_output_file *out)
{
if (x < H0) {
out->right(x);
x = -x;
}
if (n == 0) {
out->right(x);
return;
}
hunits w = n->width();
if (w <= H0) {
error("horizontal line drawing character must have positive width");
out->right(x);
return;
}
int i = int(x/w);
if (i == 0) {
hunits xx = x - w;
hunits xx2 = xx/2;
out->right(xx2);
if (out->is_on())
n->tprint(out);
out->right(xx - xx2);
}
else {
hunits rem = x - w*i;
if (rem > H0) {
if (n->overlaps_horizontally()) {
if (out->is_on())
n->tprint(out);
out->right(rem - w);
}
else
out->right(rem);
}
while (--i >= 0)
if (out->is_on())
n->tprint(out);
}
}
void vline_node::tprint(troff_output_file *out)
{
if (n == 0) {
out->down(x);
return;
}
vunits h = n->size();
int overlaps = n->overlaps_vertically();
vunits y = x;
if (y < V0) {
y = -y;
int i = y / h;
vunits rem = y - i*h;
if (i == 0) {
out->right(n->width());
out->down(-rem);
}
else {
while (--i > 0) {
n->zero_width_tprint(out);
out->down(-h);
}
if (overlaps) {
n->zero_width_tprint(out);
out->down(-rem);
if (out->is_on())
n->tprint(out);
out->down(-h);
}
else {
if (out->is_on())
n->tprint(out);
out->down(-h - rem);
}
}
}
else {
int i = y / h;
vunits rem = y - i*h;
if (i == 0) {
out->down(rem);
out->right(n->width());
}
else {
out->down(h);
if (overlaps)
n->zero_width_tprint(out);
out->down(rem);
while (--i > 0) {
n->zero_width_tprint(out);
out->down(h);
}
if (out->is_on())
n->tprint(out);
}
}
}
void zero_width_node::tprint(troff_output_file *out)
{
if (!n)
return;
if (!n->next) {
n->zero_width_tprint(out);
return;
}
int hpos = out->get_hpos();
int vpos = out->get_vpos();
node *tem = n;
while (tem) {
tem->tprint(out);
tem = tem->next;
}
out->moveto(hpos, vpos);
}
void overstrike_node::tprint(troff_output_file *out)
{
hunits pos = H0;
for (node *tem = list; tem; tem = tem->next) {
hunits x = (max_width - tem->width())/2;
out->right(x - pos);
pos = x;
tem->zero_width_tprint(out);
}
out->right(max_width - pos);
}
void bracket_node::tprint(troff_output_file *out)
{
if (list == 0)
return;
int npieces = 0;
node *tem;
for (tem = list; tem; tem = tem->next)
++npieces;
vunits h = list->size();
vunits totalh = h*npieces;
vunits y = (totalh - h)/2;
out->down(y);
for (tem = list; tem; tem = tem->next) {
tem->zero_width_tprint(out);
out->down(-h);
}
out->right(max_width);
out->down(totalh - y);
}
void node::tprint(troff_output_file *)
{
}
void node::zero_width_tprint(troff_output_file *out)
{
int hpos = out->get_hpos();
int vpos = out->get_vpos();
tprint(out);
out->moveto(hpos, vpos);
}
void space_node::tprint(troff_output_file *out)
{
out->fill_color(col);
out->right(n);
}
void hmotion_node::tprint(troff_output_file *out)
{
out->fill_color(col);
out->right(n);
}
void space_char_hmotion_node::tprint(troff_output_file *out)
{
out->fill_color(col);
if (is_html) {
// we emit the space width as a negative glyph index
out->flush_tbuf();
out->do_motion();
out->put('N');
out->put(-n.to_units());
out->put('\n');
}
out->right(n);
}
void vmotion_node::tprint(troff_output_file *out)
{
out->fill_color(col);
out->down(n);
}
void kern_pair_node::tprint(troff_output_file *out)
{
n1->tprint(out);
out->right(amount);
n2->tprint(out);
}
static void tprint_reverse_node_list(troff_output_file *out, node *n)
{
if (n == 0)
return;
tprint_reverse_node_list(out, n->next);
n->tprint(out);
}
void dbreak_node::tprint(troff_output_file *out)
{
tprint_reverse_node_list(out, none);
}
void composite_node::tprint(troff_output_file *out)
{
hunits bold_offset;
int is_bold = tf->get_bold(&bold_offset);
hunits track_kern = tf->get_track_kern();
hunits constant_space;
int is_constant_spaced = tf->get_constant_space(&constant_space);
hunits x = H0;
if (is_constant_spaced) {
x = constant_space;
for (node *tem = n; tem; tem = tem->next)
x -= tem->width();
if (is_bold)
x -= bold_offset;
hunits x2 = x/2;
out->right(x2);
x -= x2;
}
if (is_bold) {
int hpos = out->get_hpos();
int vpos = out->get_vpos();
tprint_reverse_node_list(out, n);
out->moveto(hpos, vpos);
out->right(bold_offset);
}
tprint_reverse_node_list(out, n);
if (is_constant_spaced)
out->right(x);
else
out->right(track_kern);
}
static node *make_composite_node(charinfo *s, environment *env)
{
int fontno = env_definite_font(env);
if (fontno < 0) {
error("cannot format composite glyph: no current font");
return 0;
}
assert(fontno < font_table_size && font_table[fontno] != 0);
node *n = charinfo_to_node_list(s, env);
font_size fs = env->get_font_size();
int char_height = env->get_char_height();
int char_slant = env->get_char_slant();
tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant,
fontno);
if (env->is_composite())
tf = tf->get_plain();
return new composite_node(n, s, tf, 0, 0, 0);
}
static node *make_glyph_node(charinfo *s, environment *env,
bool want_warnings = true)
{
int fontno = env_definite_font(env);
if (fontno < 0) {
error("cannot format glyph: no current font");
return 0;
}
assert(fontno < font_table_size && font_table[fontno] != 0);
int fn = fontno;
int found = font_table[fontno]->contains(s);
if (!found) {
macro *mac = s->get_macro();
if (mac && s->is_fallback())
return make_composite_node(s, env);
if (s->numbered()) {
if (want_warnings)
warning(WARN_CHAR, "character code %1 not defined in current"
" font", s->get_number());
return 0;
}
special_font_list *sf = font_table[fontno]->sf;
while (sf != 0 && !found) {
fn = sf->n;
if (font_table[fn])
found = font_table[fn]->contains(s);
sf = sf->next;
}
if (!found) {
symbol f = font_table[fontno]->get_name();
string gl(f.contents());
gl += ' ';
gl += s->nm.contents();
gl += '\0';
charinfo *ci = get_charinfo(symbol(gl.contents()));
if (ci && ci->get_macro())
return make_composite_node(ci, env);
}
if (!found) {
sf = global_special_fonts;
while (sf != 0 && !found) {
fn = sf->n;
if (font_table[fn])
found = font_table[fn]->contains(s);
sf = sf->next;
}
}
if (!found)
if (mac && s->is_special())
return make_composite_node(s, env);
if (!found) {
for (fn = 0; fn < font_table_size; fn++)
if (font_table[fn]
&& font_table[fn]->is_special()
&& font_table[fn]->contains(s)) {
found = 1;
break;
}
}
if (!found) {
if (want_warnings && s->first_time_not_found()) {
unsigned char input_code = s->get_ascii_code();
if (input_code != 0) {
if (csgraph(input_code))
warning(WARN_CHAR, "character '%1' not defined",
input_code);
else
warning(WARN_CHAR, "character with input code %1 not"
" defined", int(input_code));
}
else if (s->nm.contents()) {
const char *nm = s->nm.contents();
const char *backslash = (nm[1] == 0) ? "\\" : "";
warning(WARN_CHAR, "special character '%1%2' not defined",
backslash, nm);
}
}
return 0;
}
}
font_size fs = env->get_font_size();
int char_height = env->get_char_height();
int char_slant = env->get_char_slant();
tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fn);
if (env->is_composite())
tf = tf->get_plain();
color *gcol = env->get_glyph_color();
color *fcol = env->get_fill_color();
return new glyph_node(s, tf, gcol, fcol, 0, 0);
}
node *make_node(charinfo *ci, environment *env)
{
switch (ci->get_special_translation()) {
case charinfo::TRANSLATE_SPACE:
return new space_char_hmotion_node(env->get_space_width(),
env->get_fill_color());
case charinfo::TRANSLATE_STRETCHABLE_SPACE:
return new unbreakable_space_node(env->get_space_width(),
env->get_fill_color());
case charinfo::TRANSLATE_DUMMY:
return new dummy_node;
case charinfo::TRANSLATE_HYPHEN_INDICATOR:
error("translation to \\%% ignored in this context");
break;
}
charinfo *tem = ci->get_translation();
if (tem)
ci = tem;
macro *mac = ci->get_macro();
if (mac && ci->is_normal())
return make_composite_node(ci, env);
else
return make_glyph_node(ci, env);
}
bool character_exists(charinfo *ci, environment *env)
{
if (ci->get_special_translation() != charinfo::TRANSLATE_NONE)
return true;
charinfo *tem = ci->get_translation();
if (tem)
ci = tem;
if (ci->get_macro())
return true;
node *nd = make_glyph_node(ci, env, false /* don't want warnings */);
if (nd) {
delete nd;
return true;
}
return false;
}
node *node::add_char(charinfo *ci, environment *env,
hunits *widthp, int *spacep, node **glyph_comp_np)
{
node *res;
switch (ci->get_special_translation()) {
case charinfo::TRANSLATE_SPACE:
res = new space_char_hmotion_node(env->get_space_width(),
env->get_fill_color(), this);
*widthp += res->width();
return res;
case charinfo::TRANSLATE_STRETCHABLE_SPACE:
res = new unbreakable_space_node(env->get_space_width(),
env->get_fill_color(), this);
res->freeze_space();
*widthp += res->width();
*spacep += res->nspaces();
return res;
case charinfo::TRANSLATE_DUMMY:
return new dummy_node(this);
case charinfo::TRANSLATE_HYPHEN_INDICATOR:
return add_discretionary_hyphen();
}
charinfo *tem = ci->get_translation();
if (tem)
ci = tem;
macro *mac = ci->get_macro();
if (mac && ci->is_normal()) {
res = make_composite_node(ci, env);
if (res) {
res->next = this;
*widthp += res->width();
if (glyph_comp_np)
*glyph_comp_np = res;
}
else {
if (glyph_comp_np)
*glyph_comp_np = res;
return this;
}
}
else {
node *gn = make_glyph_node(ci, env);
if (gn == 0)
return this;
else {
hunits old_width = width();
node *p = gn->merge_self(this);
if (p == 0) {
*widthp += gn->width();
gn->next = this;
res = gn;
}
else {
*widthp += p->width() - old_width;
res = p;
}
if (glyph_comp_np)
*glyph_comp_np = res;
}
}
int break_code = 0;
if (ci->can_break_before())
break_code = CAN_BREAK_BEFORE;
if (ci->can_break_after())
break_code |= CAN_BREAK_AFTER;
if (ci->ignore_hcodes())
break_code |= IGNORE_HCODES;
if (ci->prohibit_break_before())
break_code = PROHIBIT_BREAK_BEFORE;
if (ci->prohibit_break_after())
break_code |= PROHIBIT_BREAK_AFTER;
if (ci->inter_char_space())
break_code |= INTER_CHAR_SPACE;
if (break_code) {
node *next1 = res->next;
res->next = 0;
res = new break_char_node(res, break_code, get_break_code(),
env->get_fill_color(), next1);
}
return res;
}
#ifdef __GNUG__
inline
#endif
int same_node(node *n1, node *n2)
{
if (n1 != 0) {
if (n2 != 0)
return n1->type() == n2->type() && n1->same(n2);
else
return 0;
}
else
return n2 == 0;
}
int same_node_list(node *n1, node *n2)
{
while (n1 && n2) {
if (n1->type() != n2->type() || !n1->same(n2))
return 0;
n1 = n1->next;
n2 = n2->next;
}
return !n1 && !n2;
}
int extra_size_node::same(node *nd)
{
return n == ((extra_size_node *)nd)->n;
}
const char *extra_size_node::type()
{
return "extra_size_node";
}
int extra_size_node::force_tprint()
{
return 0;
}
int extra_size_node::is_tag()
{
return 0;
}
int vertical_size_node::same(node *nd)
{
return n == ((vertical_size_node *)nd)->n;
}
const char *vertical_size_node::type()
{
return "vertical_size_node";
}
int vertical_size_node::set_unformat_flag()
{
return 0;
}
int vertical_size_node::force_tprint()
{
return 0;
}
int vertical_size_node::is_tag()
{
return 0;
}
int hmotion_node::same(node *nd)
{
return n == ((hmotion_node *)nd)->n
&& col == ((hmotion_node *)nd)->col;
}
const char *hmotion_node::type()
{
return "hmotion_node";
}
int hmotion_node::set_unformat_flag()
{
unformat = 1;
return 1;
}
int hmotion_node::force_tprint()
{
return 0;
}
int hmotion_node::is_tag()
{
return 0;
}
node *hmotion_node::add_self(node *nd, hyphen_list **p)
{
next = nd;
hyphen_list *pp = *p;
*p = (*p)->next;
delete pp;
return this;
}
hyphen_list *hmotion_node::get_hyphen_list(hyphen_list *tail, int *)
{
return new hyphen_list(0, tail);
}
int space_char_hmotion_node::same(node *nd)
{
return n == ((space_char_hmotion_node *)nd)->n
&& col == ((space_char_hmotion_node *)nd)->col;
}
const char *space_char_hmotion_node::type()
{
return "space_char_hmotion_node";
}
int space_char_hmotion_node::force_tprint()
{
return 0;
}
int space_char_hmotion_node::is_tag()
{
return 0;
}
node *space_char_hmotion_node::add_self(node *nd, hyphen_list **p)
{
next = nd;
hyphen_list *pp = *p;
*p = (*p)->next;
delete pp;
return this;
}
hyphen_list *space_char_hmotion_node::get_hyphen_list(hyphen_list *tail,
int *)
{
return new hyphen_list(0, tail);
}
int vmotion_node::same(node *nd)
{
return n == ((vmotion_node *)nd)->n
&& col == ((vmotion_node *)nd)->col;
}
const char *vmotion_node::type()
{
return "vmotion_node";
}
int vmotion_node::force_tprint()
{
return 0;
}
int vmotion_node::is_tag()
{
return 0;
}
int hline_node::same(node *nd)
{
return x == ((hline_node *)nd)->x && same_node(n, ((hline_node *)nd)->n);
}
const char *hline_node::type()
{
return "hline_node";
}
int hline_node::force_tprint()
{
return 0;
}
int hline_node::is_tag()
{
return 0;
}
int vline_node::same(node *nd)
{
return x == ((vline_node *)nd)->x && same_node(n, ((vline_node *)nd)->n);
}
const char *vline_node::type()
{
return "vline_node";
}
int vline_node::force_tprint()
{
return 0;
}
int vline_node::is_tag()
{
return 0;
}
int dummy_node::same(node * /*nd*/)
{
return 1;
}
const char *dummy_node::type()
{
return "dummy_node";
}
int dummy_node::force_tprint()
{
return 0;
}
int dummy_node::is_tag()
{
return 0;
}
int transparent_dummy_node::same(node * /*nd*/)
{
return 1;
}
const char *transparent_dummy_node::type()
{
return "transparent_dummy_node";
}
int transparent_dummy_node::force_tprint()
{
return 0;
}
int transparent_dummy_node::is_tag()
{
return 0;
}
int transparent_dummy_node::ends_sentence()
{
return 2;
}
int zero_width_node::same(node *nd)
{
return same_node_list(n, ((zero_width_node *)nd)->n);
}
const char *zero_width_node::type()
{
return "zero_width_node";
}
int zero_width_node::force_tprint()
{
return 0;
}
int zero_width_node::is_tag()
{
return 0;
}
int italic_corrected_node::same(node *nd)
{
return (x == ((italic_corrected_node *)nd)->x
&& same_node(n, ((italic_corrected_node *)nd)->n));
}
const char *italic_corrected_node::type()
{
return "italic_corrected_node";
}
int italic_corrected_node::force_tprint()
{
return 0;
}
int italic_corrected_node::is_tag()
{
return 0;
}
left_italic_corrected_node::left_italic_corrected_node(node *xx)
: node(xx), n(0)
{
}
left_italic_corrected_node::left_italic_corrected_node(statem *s, int pop,
node *xx)
: node(xx, s, pop), n(0)
{
}
left_italic_corrected_node::~left_italic_corrected_node()
{
delete n;
}
node *left_italic_corrected_node::merge_glyph_node(glyph_node *gn)
{
if (n == 0) {
hunits lic = gn->left_italic_correction();
if (!lic.is_zero()) {
x = lic;
n = gn;
return this;
}
}
else {
node *nd = n->merge_glyph_node(gn);
if (nd) {
n = nd;
x = n->left_italic_correction();
return this;
}
}
return 0;
}
node *left_italic_corrected_node::copy()
{
left_italic_corrected_node *nd =
new left_italic_corrected_node(state, div_nest_level);
if (n) {
nd->n = n->copy();
nd->x = x;
}
return nd;
}
void left_italic_corrected_node::tprint(troff_output_file *out)
{
if (n) {
out->right(x);
n->tprint(out);
}
}
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::is_tag()
{
return 0;
}
int left_italic_corrected_node::same(node *nd)
{
return (x == ((left_italic_corrected_node *)nd)->x
&& same_node(n, ((left_italic_corrected_node *)nd)->n));
}
void left_italic_corrected_node::ascii_print(ascii_output_file *out)
{
if (n)
n->ascii_print(out);
}
hunits left_italic_corrected_node::width()
{
return n ? n->width() + x : H0;
}
void left_italic_corrected_node::vertical_extent(vunits *minimum,
vunits *maximum)
{
if (n)
n->vertical_extent(minimum, maximum);
else
node::vertical_extent(minimum, maximum);
}
hunits left_italic_corrected_node::skew()
{
return n ? n->skew() + x/2 : H0;
}
hunits left_italic_corrected_node::subscript_correction()
{
return n ? n->subscript_correction() : H0;
}
hunits left_italic_corrected_node::italic_correction()
{
return n ? n->italic_correction() : H0;
}
int left_italic_corrected_node::ends_sentence()
{
return n ? n->ends_sentence() : 0;
}
int left_italic_corrected_node::overlaps_horizontally()
{
return n ? n->overlaps_horizontally() : 0;
}
int left_italic_corrected_node::overlaps_vertically()
{
return n ? n->overlaps_vertically() : 0;
}
node *left_italic_corrected_node::last_char_node()
{
return n ? n->last_char_node() : 0;
}
tfont *left_italic_corrected_node::get_tfont()
{
return n ? n->get_tfont() : 0;
}
hyphenation_type left_italic_corrected_node::get_hyphenation_type()
{
if (n)
return n->get_hyphenation_type();
else
return HYPHEN_MIDDLE;
}
hyphen_list *left_italic_corrected_node::get_hyphen_list(hyphen_list *tail,
int *count)
{
return n ? n->get_hyphen_list(tail, count) : tail;
}
node *left_italic_corrected_node::add_self(node *nd, hyphen_list **p)
{
if (n) {
nd = new left_italic_corrected_node(state, div_nest_level, nd);
nd = n->add_self(nd, p);
n = 0;
delete this;
return nd;
}
else {
next = nd;
return this;
}
}
int left_italic_corrected_node::character_type()
{
return n ? n->character_type() : 0;
}
int overstrike_node::same(node *nd)
{
return same_node_list(list, ((overstrike_node *)nd)->list);
}
const char *overstrike_node::type()
{
return "overstrike_node";
}
int overstrike_node::force_tprint()
{
return 0;
}
int overstrike_node::is_tag()
{
return 0;
}
node *overstrike_node::add_self(node *n, hyphen_list **p)
{
next = n;
hyphen_list *pp = *p;
*p = (*p)->next;
delete pp;
return this;
}
hyphen_list *overstrike_node::get_hyphen_list(hyphen_list *tail, int *)
{
return new hyphen_list(0, tail);
}
int bracket_node::same(node *nd)
{
return same_node_list(list, ((bracket_node *)nd)->list);
}
const char *bracket_node::type()
{
return "bracket_node";
}
int bracket_node::force_tprint()
{
return 0;
}
int bracket_node::is_tag()
{
return 0;
}
int composite_node::same(node *nd)
{
return ci == ((composite_node *)nd)->ci
&& same_node_list(n, ((composite_node *)nd)->n);
}
const char *composite_node::type()
{
return "composite_node";
}
int composite_node::force_tprint()
{
return 0;
}
int composite_node::is_tag()
{
return 0;
}
int glyph_node::same(node *nd)
{
return ci == ((glyph_node *)nd)->ci
&& tf == ((glyph_node *)nd)->tf
&& gcol == ((glyph_node *)nd)->gcol
&& fcol == ((glyph_node *)nd)->fcol;
}
const char *glyph_node::type()
{
return "glyph_node";
}
int glyph_node::force_tprint()
{
return 0;
}
int glyph_node::is_tag()
{
return 0;
}
int ligature_node::same(node *nd)
{
return (same_node(n1, ((ligature_node *)nd)->n1)
&& same_node(n2, ((ligature_node *)nd)->n2)
&& glyph_node::same(nd));
}
const char *ligature_node::type()
{
return "ligature_node";
}
int ligature_node::force_tprint()
{
return 0;
}
int ligature_node::is_tag()
{
return 0;
}
int kern_pair_node::same(node *nd)
{
return (amount == ((kern_pair_node *)nd)->amount
&& same_node(n1, ((kern_pair_node *)nd)->n1)
&& same_node(n2, ((kern_pair_node *)nd)->n2));
}
const char *kern_pair_node::type()
{
return "kern_pair_node";
}
int kern_pair_node::force_tprint()
{
return 0;
}
int kern_pair_node::is_tag()
{
return 0;
}
int dbreak_node::same(node *nd)
{
return (same_node_list(none, ((dbreak_node *)nd)->none)
&& same_node_list(pre, ((dbreak_node *)nd)->pre)
&& same_node_list(post, ((dbreak_node *)nd)->post));
}
const char *dbreak_node::type()
{
return "dbreak_node";
}
int dbreak_node::force_tprint()
{
return 0;
}
int dbreak_node::is_tag()
{
return 0;
}
int break_char_node::same(node *nd)
{
return break_code == ((break_char_node *)nd)->break_code
&& col == ((break_char_node *)nd)->col
&& same_node(ch, ((break_char_node *)nd)->ch);
}
const char *break_char_node::type()
{
return "break_char_node";
}
int break_char_node::force_tprint()
{
return 0;
}
int break_char_node::is_tag()
{
return 0;
}
int break_char_node::get_break_code()
{
return break_code;
}
int line_start_node::same(node * /*nd*/)
{
return 1;
}
const char *line_start_node::type()
{
return "line_start_node";
}
int line_start_node::force_tprint()
{
return 0;
}
int line_start_node::is_tag()
{
return 0;
}
int space_node::same(node *nd)
{
return n == ((space_node *)nd)->n
&& set == ((space_node *)nd)->set
&& col == ((space_node *)nd)->col;
}
const char *space_node::type()
{
return "space_node";
}
int word_space_node::same(node *nd)
{
return n == ((word_space_node *)nd)->n
&& set == ((word_space_node *)nd)->set
&& col == ((word_space_node *)nd)->col;
}
const char *word_space_node::type()
{
return "word_space_node";
}
int word_space_node::force_tprint()
{
return 0;
}
int word_space_node::is_tag()
{
return 0;
}
void unbreakable_space_node::tprint(troff_output_file *out)
{
out->fill_color(col);
if (is_html) {
// we emit the space width as a negative glyph index
out->flush_tbuf();
out->do_motion();
out->put('N');
out->put(-n.to_units());
out->put('\n');
}
out->right(n);
}
int unbreakable_space_node::same(node *nd)
{
return n == ((unbreakable_space_node *)nd)->n
&& set == ((unbreakable_space_node *)nd)->set
&& col == ((unbreakable_space_node *)nd)->col;
}
const char *unbreakable_space_node::type()
{
return "unbreakable_space_node";
}
node *unbreakable_space_node::add_self(node *nd, hyphen_list **p)
{
next = nd;
hyphen_list *pp = *p;
*p = (*p)->next;
delete pp;
return this;
}
hyphen_list *unbreakable_space_node::get_hyphen_list(hyphen_list *tail, int *)
{
return new hyphen_list(0, tail);
}
int diverted_space_node::same(node *nd)
{
return n == ((diverted_space_node *)nd)->n;
}
const char *diverted_space_node::type()
{
return "diverted_space_node";
}
int diverted_space_node::force_tprint()
{
return 0;
}
int diverted_space_node::is_tag()
{
return 0;
}
int diverted_copy_file_node::same(node *nd)
{
return filename == ((diverted_copy_file_node *)nd)->filename;
}
const char *diverted_copy_file_node::type()
{
return "diverted_copy_file_node";
}
int diverted_copy_file_node::force_tprint()
{
return 0;
}
int diverted_copy_file_node::is_tag()
{
return 0;
}
// Grow the font_table so that its size is > n.
static void grow_font_table(int n)
{
assert(n >= font_table_size);
font_info **old_font_table = font_table;
int old_font_table_size = font_table_size;
font_table_size = font_table_size ? (font_table_size*3)/2 : 10;
if (font_table_size <= n)
font_table_size = n + 10;
font_table = new font_info *[font_table_size];
if (old_font_table_size)
memcpy(font_table, old_font_table,
old_font_table_size*sizeof(font_info *));
delete[] old_font_table;
for (int i = old_font_table_size; i < font_table_size; i++)
font_table[i] = 0;
}
dictionary font_translation_dictionary(17);
static symbol get_font_translation(symbol nm)
{
void *p = font_translation_dictionary.lookup(nm);
return p ? symbol((char *)p) : nm;
}
dictionary font_dictionary(50);
static bool mount_font_no_translate(int n, symbol name,
symbol external_name,
bool check_only = false)
{
assert(n >= 0);
// We store the address of this char in `font_dictionary` to indicate
// that we've previously tried to mount the font and failed.
static char a_char;
font *fm = 0 /* nullptr */;
void *p = font_dictionary.lookup(external_name);
if (0 /* nullptr */ == p) {
fm = font::load_font(external_name.contents(), check_only);
if (check_only)
return fm != 0 /* nullptr */;
if (0 /* nullptr */ == fm) {
(void)font_dictionary.lookup(external_name, &a_char);
return false;
}
(void)font_dictionary.lookup(name, fm);
}
else if (p == &a_char) {
return false;
}
else
fm = (font*)p;
if (check_only)
return true;
if (n >= font_table_size) {
if (n - font_table_size > 1000) {
error("font position too much larger than first unused position");
return false;
}
grow_font_table(n);
}
else if (font_table[n] != 0 /* nullptr */)
delete font_table[n];
font_table[n] = new font_info(name, n, external_name, fm);
font_family::invalidate_fontno(n);
return true;
}
bool mount_font(int n, symbol name, symbol external_name)
{
assert(n >= 0);
name = get_font_translation(name);
if (external_name.is_null())
external_name = name;
else
external_name = get_font_translation(external_name);
return mount_font_no_translate(n, name, external_name);
}
int check_font(symbol fam, symbol name)
{
if (check_style(name))
name = concat(fam, name);
return mount_font_no_translate(0, name, name, true /* check only */);
}
int check_style(symbol s)
{
int i = symbol_fontno(s);
return i < 0 ? 0 : font_table[i]->is_style();
}
bool mount_style(int n, symbol name)
{
assert(n >= 0);
if (n >= font_table_size) {
if (n - font_table_size > 1000) {
error("font position too much larger than first unused position");
return false;
}
grow_font_table(n);
}
else if (font_table[n] != 0)
delete font_table[n];
font_table[n] = new font_info(get_font_translation(name), n,
NULL_SYMBOL, 0);
font_family::invalidate_fontno(n);
return true;
}
/* global functions */
void font_translate()
{
symbol from = get_name(true /* required */);
if (!from.is_null()) {
symbol to = get_name();
if (to.is_null() || from == to)
font_translation_dictionary.remove(from);
else
(void)font_translation_dictionary.lookup(from, (void *)to.contents());
}
skip_line();
}
void font_position()
{
int n;
if (get_integer(&n)) {
if (n < 0)
error("negative font position");
else {
symbol internal_name = get_name(true /* required */);
if (!internal_name.is_null()) {
symbol external_name = get_long_name();
if (!mount_font(n, internal_name, external_name)) {
string msg;
if (external_name != 0 /* nullptr */)
msg += string(" from file '") + external_name.contents()
+ string("'");
msg += '\0';
error("cannot load font '%1'%2 for mounting",
internal_name.contents(), msg.contents());
}
}
}
}
skip_line();
}
font_family::font_family(symbol s)
: map_size(10), nm(s)
{
map = new int[map_size];
for (int i = 0; i < map_size; i++)
map[i] = -1;
}
font_family::~font_family()
{
delete[] map;
}
// Resolve a requested font mounting position to a mounting position
// usable by the output driver. (Positions 1 through 4 are typically
// allocated to styles, and are not usable thus.) A return value of
// `-1` indicates failure.
int font_family::make_definite(int mounting_position)
{
assert(mounting_position >= 0);
int pos = mounting_position;
if (pos < 0)
return -1;
if (pos < map_size && map[pos] >= 0)
return map[pos];
if (!(pos < font_table_size && font_table[pos] != 0))
return -1;
if (pos >= map_size) {
int old_map_size = map_size;
int *old_map = map;
map_size *= 3;
map_size /= 2;
if (pos >= map_size)
map_size = pos + 10;
map = new int[map_size];
memcpy(map, old_map, old_map_size * sizeof (int));
delete[] old_map;
for (int j = old_map_size; j < map_size; j++)
map[j] = -1;
}
if (!(font_table[pos]->is_style()))
return map[pos] = pos;
symbol sty = font_table[pos]->get_name();
symbol f = concat(nm, sty);
int n;
// Don't use symbol_fontno, because that might return a style and
// because we don't want to translate the name.
for (n = 0; n < font_table_size; n++)
if (font_table[n] != 0 && font_table[n]->is_named(f)
&& !font_table[n]->is_style())
break;
if (n >= font_table_size) {
n = next_available_font_position();
if (!mount_font_no_translate(n, f, f))
return -1;
}
return map[pos] = n;
}
dictionary family_dictionary(5);
font_family *lookup_family(symbol nm)
{
font_family *f = (font_family *)family_dictionary.lookup(nm);
if (!f) {
f = new font_family(nm);
(void)family_dictionary.lookup(nm, f);
}
return f;
}
void font_family::invalidate_fontno(int n)
{
assert(n >= 0 && n < font_table_size);
dictionary_iterator iter(family_dictionary);
symbol nam;
font_family *fam;
while (iter.get(&nam, (void **)&fam)) {
int mapsize = fam->map_size;
if (n < mapsize)
fam->map[n] = -1;
for (int i = 0; i < mapsize; i++)
if (fam->map[i] == n)
fam->map[i] = -1;
}
}
void style()
{
int n;
if (get_integer(&n)) {
if (n < 0)
error("negative font position");
else {
symbol internal_name = get_name(true /* required */);
if (!internal_name.is_null())
(void) mount_style(n, internal_name);
}
}
skip_line();
}
static void font_lookup_error(font_lookup_info& finfo,
const char *msg)
{
if (finfo.requested_name)
error("cannot load font '%1' %2", finfo.requested_name, msg);
else
error("cannot load font at position %1 %2",
finfo.requested_position, msg);
}
// Read the next token and look it up as a font name or position number.
// Return lookup success. Store, in the supplied struct argument, the
// requested name or position, and the position actually resolved; -1
// means not found (see `font_lookup_info` constructor).
static bool has_font(font_lookup_info *finfo)
{
int n;
tok.skip();
if (tok.usable_as_delimiter()) {
symbol s = get_name(true /* required */);
finfo->requested_name = (char *)s.contents();
if (!s.is_null()) {
n = symbol_fontno(s);
if (n < 0) {
n = next_available_font_position();
if (mount_font(n, s))
finfo->position = n;
}
finfo->position = curenv->get_family()->make_definite(n);
}
}
else if (get_integer(&n)) {
finfo->requested_position = n;
if (!(n < 0 || n >= font_table_size || font_table[n] == 0))
finfo->position = curenv->get_family()->make_definite(n);
}
return (finfo->position != -1);
}
static int underline_fontno = 2;
void underline_font()
{
font_lookup_info finfo;
if (!has_font(&finfo))
font_lookup_error(finfo, "to make it the underline font");
else
underline_fontno = finfo.position;
skip_line();
}
int get_underline_fontno()
{
return underline_fontno;
}
void define_font_special_character()
{
font_lookup_info finfo;
if (!has_font(&finfo)) {
font_lookup_error(finfo, "to define font-specific fallback glyph");
// Normally we skip the remainder of the line unconditionally at the
// end of a request-implementing function, but do_define_character()
// will eat the rest of it for us.
skip_line();
}
else {
symbol f = font_table[finfo.position]->get_name();
do_define_character(CHAR_FONT_SPECIAL, f.contents());
}
}
void remove_font_special_character()
{
font_lookup_info finfo;
if (!has_font(&finfo))
font_lookup_error(finfo, "to remove font-specific fallback glyph");
else {
symbol f = font_table[finfo.position]->get_name();
while (!tok.is_newline() && !tok.is_eof()) {
if (!tok.is_space() && !tok.is_tab()) {
charinfo *s = tok.get_char(true /* required */);
string gl(f.contents());
gl += ' ';
gl += s->nm.contents();
gl += '\0';
charinfo *ci = get_charinfo(symbol(gl.contents()));
if (!ci)
break;
macro *m = ci->set_macro(0);
if (m)
delete m;
}
tok.next();
}
}
skip_line();
}
static void read_special_fonts(special_font_list **sp)
{
special_font_list *s = *sp;
*sp = 0;
while (s != 0) {
special_font_list *tem = s;
s = s->next;
delete tem;
}
special_font_list **p = sp;
while (has_arg()) {
font_lookup_info finfo;
if (!has_font(&finfo))
font_lookup_error(finfo, "to mark it as special");
else {
special_font_list *tem = new special_font_list;
tem->n = finfo.position;
tem->next = 0;
*p = tem;
p = &(tem->next);
}
}
}
void font_special_request()
{
font_lookup_info finfo;
if (!has_font(&finfo))
font_lookup_error(finfo, "to mark other fonts as special"
" contingently upon it"); // a mouthful :-/
else
read_special_fonts(&font_table[finfo.position]->sf);
skip_line();
}
void special_request()
{
read_special_fonts(&global_special_fonts);
skip_line();
}
void font_zoom_request()
{
font_lookup_info finfo;
if (!has_font(&finfo))
font_lookup_error(finfo, "to set a zoom factor for it");
else {
int n = finfo.position;
if (font_table[n]->is_style())
warning(WARN_FONT, "can't set zoom factor for a style");
else {
int zoom;
if (has_arg() && get_integer(&zoom)) {
if (zoom < 0)
warning(WARN_FONT, "can't use negative zoom factor");
else
font_table[n]->set_zoom(zoom);
}
else
font_table[n]->set_zoom(0);
}
}
skip_line();
}
int next_available_font_position()
{
int i;
for (i = 1; i < font_table_size && font_table[i] != 0; i++)
;
return i;
}
int symbol_fontno(symbol s)
{
s = get_font_translation(s);
for (int i = 0; i < font_table_size; i++)
if (font_table[i] != 0 && font_table[i]->is_named(s))
return i;
return -1;
}
int is_good_fontno(int n)
{
return n >= 0 && n < font_table_size && font_table[n] != 0;
}
int get_bold_fontno(int n)
{
if (n >= 0 && n < font_table_size && font_table[n] != 0) {
hunits offset;
if (font_table[n]->get_bold(&offset))
return offset.to_units() + 1;
else
return 0;
}
else
return 0;
}
hunits env_digit_width(environment *env)
{
node *n = make_glyph_node(charset_table['0'], env);
if (n) {
hunits x = n->width();
delete n;
return x;
}
else
return H0;
}
hunits env_space_width(environment *env)
{
int fn = env_definite_font(env);
font_size fs = env->get_font_size();
if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
return scale(fs.to_units()/3, env->get_space_size(), 12);
else
return font_table[fn]->get_space_width(fs, env->get_space_size());
}
hunits env_sentence_space_width(environment *env)
{
int fn = env_definite_font(env);
font_size fs = env->get_font_size();
if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
return scale(fs.to_units()/3, env->get_sentence_space_size(), 12);
else
return font_table[fn]->get_space_width(fs, env->get_sentence_space_size());
}
hunits env_half_narrow_space_width(environment *env)
{
int fn = env_definite_font(env);
font_size fs = env->get_font_size();
if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
return 0;
else
return font_table[fn]->get_half_narrow_space_width(fs);
}
hunits env_narrow_space_width(environment *env)
{
int fn = env_definite_font(env);
font_size fs = env->get_font_size();
if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
return 0;
else
return font_table[fn]->get_narrow_space_width(fs);
}
void bold_font()
{
font_lookup_info finfo;
if (!has_font(&finfo))
font_lookup_error(finfo, "for emboldening");
else {
int n = finfo.position;
if (has_arg()) {
// This is a bit non-orthogonal, but faithful to CSTR #54. We can
// only conditionally embolden a font specified by name, not
// position, so ".bd S B 4" works but ".bd 5 3 4" does not. The
// latter bolds the font at position 5 unconditionally, and
// ignores the third argument.
if (tok.usable_as_delimiter()) {
font_lookup_info finfo2;
if (!has_font(&finfo2))
font_lookup_error(finfo2, "for conditional emboldening");
else {
int f = finfo2.position;
units offset;
if (has_arg() && get_number(&offset, 'u') && offset >= 1)
font_table[f]->set_conditional_bold(n, hunits(offset - 1));
else
font_table[f]->conditional_unbold(n);
}
}
else {
font_lookup_info finfo2;
if (!has_font(&finfo2))
font_lookup_error(finfo2, "for conditional emboldening");
units offset;
if (get_number(&offset, 'u') && offset >= 1)
font_table[n]->set_bold(hunits(offset - 1));
else
font_table[n]->unbold();
}
}
else
font_table[n]->unbold();
}
skip_line();
}
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)
{
}
int track_kerning_function::operator==(const track_kerning_function &tk)
{
if (non_zero)
return (tk.non_zero
&& min_size == tk.min_size
&& min_amount == tk.min_amount
&& max_size == tk.max_size
&& max_amount == tk.max_amount);
else
return !tk.non_zero;
}
int track_kerning_function::operator!=(const track_kerning_function &tk)
{
if (non_zero)
return (!tk.non_zero
|| min_size != tk.min_size
|| min_amount != tk.min_amount
|| max_size != tk.max_size
|| max_amount != tk.max_amount);
else
return tk.non_zero;
}
hunits track_kerning_function::compute(int size)
{
if (non_zero) {
if (max_size <= min_size)
return min_amount;
else if (size <= min_size)
return min_amount;
else if (size >= max_size)
return max_amount;
else
return (scale(max_amount, size - min_size, max_size - min_size)
+ scale(min_amount, max_size - size, max_size - min_size));
}
else
return H0;
}
void track_kern()
{
font_lookup_info finfo;
if (!has_font(&finfo))
font_lookup_error(finfo, "for track kerning");
else {
int n = finfo.position, min_s, max_s;
hunits min_a, max_a;
if (has_arg()
&& get_number(&min_s, 'z')
&& get_hunits(&min_a, 'p')
&& get_number(&max_s, 'z')
&& get_hunits(&max_a, 'p')) {
track_kerning_function tk(min_s, min_a, max_s, max_a);
font_table[n]->set_track_kern(tk);
}
else {
track_kerning_function tk;
font_table[n]->set_track_kern(tk);
}
}
skip_line();
}
void constant_space()
{
font_lookup_info finfo;
if (!has_font(&finfo))
font_lookup_error(finfo, "for constant spacing");
else {
int n = finfo.position, x, y;
if (!has_arg() || !get_integer(&x))
font_table[n]->set_constant_space(CONSTANT_SPACE_NONE);
else {
if (!has_arg() || !get_number(&y, 'z'))
font_table[n]->set_constant_space(CONSTANT_SPACE_RELATIVE, x);
else
font_table[n]->set_constant_space(CONSTANT_SPACE_ABSOLUTE,
scale(y*x,
units_per_inch,
36*72*sizescale));
}
}
skip_line();
}
void ligature()
{
int lig;
if (has_arg() && get_integer(&lig) && lig >= 0 && lig <= 2)
global_ligature_mode = lig;
else
global_ligature_mode = 1;
skip_line();
}
void kern_request()
{
int k;
if (has_arg() && get_integer(&k))
global_kern_mode = k != 0;
else
global_kern_mode = 1;
skip_line();
}
void set_soft_hyphen_char()
{
soft_hyphen_char = get_optional_char();
if (!soft_hyphen_char)
soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);
skip_line();
}
void init_output()
{
if (suppress_output_flag)
the_output = new suppress_output_file;
else if (ascii_output_flag)
the_output = new ascii_output_file;
else
the_output = new troff_output_file;
}
class next_available_font_position_reg : public reg {
public:
const char *get_string();
};
const char *next_available_font_position_reg::get_string()
{
return i_to_a(next_available_font_position());
}
class printing_reg : public reg {
public:
const char *get_string();
};
const char *printing_reg::get_string()
{
if (the_output)
return the_output->is_printing() ? "1" : "0";
else
return "0";
}
void init_node_requests()
{
init_request("bd", bold_font);
init_request("cs", constant_space);
init_request("fp", font_position);
init_request("fschar", define_font_special_character);
init_request("fspecial", font_special_request);
init_request("fzoom", font_zoom_request);
init_request("ftr", font_translate);
init_request("kern", kern_request);
init_request("lg", ligature);
init_request("rfschar", remove_font_special_character);
init_request("shc", set_soft_hyphen_char);
init_request("special", special_request);
init_request("sty", style);
init_request("tkf", track_kern);
init_request("uf", underline_font);
register_dictionary.define(".fp", new next_available_font_position_reg);
register_dictionary.define(".kern",
new readonly_register(&global_kern_mode));
register_dictionary.define(".lg",
new readonly_register(&global_ligature_mode));
register_dictionary.define(".P", new printing_reg);
soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);
}
// Local Variables:
// fill-column: 72
// mode: C++
// End:
// vim: set cindent noexpandtab shiftwidth=2 textwidth=72: