4870 lines
94 KiB
C++
4870 lines
94 KiB
C++
// -*- C++ -*-
|
|
/* Copyright (C) 1989, 1990, 1991, 1992 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 2, or (at your option) any later
|
|
version.
|
|
|
|
groff is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along
|
|
with groff; see the file COPYING. If not, write to the Free Software
|
|
Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
|
|
#include "troff.h"
|
|
#include "symbol.h"
|
|
#include "dictionary.h"
|
|
#include "hvunits.h"
|
|
#include "env.h"
|
|
#include "request.h"
|
|
#include "node.h"
|
|
#include "token.h"
|
|
#include "charinfo.h"
|
|
#include "font.h"
|
|
#include "reg.h"
|
|
|
|
#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);
|
|
};
|
|
|
|
// 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);
|
|
};
|
|
|
|
struct tfont;
|
|
|
|
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 nm, int n, symbol enm, font *f);
|
|
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();
|
|
};
|
|
|
|
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 nm, int pos, 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();
|
|
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)
|
|
: internal_name(nm), external_name(enm), fm(f), number(n),
|
|
is_constant_spaced(CONSTANT_SPACE_NONE),
|
|
sf(0), is_bold(0), cond_bold_list(0),
|
|
last_ligature_mode(1), last_kern_mode(1),
|
|
last_tfont(0), last_size(0)
|
|
{
|
|
}
|
|
|
|
inline int font_info::contains(charinfo *ci)
|
|
{
|
|
return fm != 0 && fm->contains(ci->get_index());
|
|
}
|
|
|
|
inline int font_info::is_special()
|
|
{
|
|
return fm != 0 && fm->is_special();
|
|
}
|
|
|
|
inline int font_info::is_style()
|
|
{
|
|
return fm == 0;
|
|
}
|
|
|
|
// 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);
|
|
last_tfont = make_tfont(spec);
|
|
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)
|
|
: fontno(f), offset(h), next(x)
|
|
{
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
hunits font_info::get_space_width(font_size fs, int space_size)
|
|
{
|
|
if (is_constant_spaced == CONSTANT_SPACE_NONE)
|
|
return scale(hunits(fm->get_space_width(fs.to_scaled_points())),
|
|
space_size, 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->get_index()))
|
|
return hunits(fm->get_width(ci->get_index(), 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->get_index()))
|
|
return hunits(fm->get_width(ci->get_index(), 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->get_index(), size.to_scaled_points()))
|
|
+ track_kern + bold_offset);
|
|
else
|
|
return (hunits(fm->get_width(c->get_index(), size.to_scaled_points())) + track_kern);
|
|
}
|
|
|
|
vunits tfont::get_char_height(charinfo *c)
|
|
{
|
|
vunits v = fm->get_height(c->get_index(), 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->get_index(), 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->get_index(), size.to_scaled_points(), slant));
|
|
}
|
|
|
|
hunits tfont::get_italic_correction(charinfo *c)
|
|
{
|
|
return hunits(fm->get_italic_correction(c->get_index(), size.to_scaled_points()));
|
|
}
|
|
|
|
hunits tfont::get_left_italic_correction(charinfo *c)
|
|
{
|
|
return hunits(fm->get_left_italic_correction(c->get_index(),
|
|
size.to_scaled_points()));
|
|
}
|
|
|
|
hunits tfont::get_subscript_correction(charinfo *c)
|
|
{
|
|
return hunits(fm->get_subscript_correction(c->get_index(),
|
|
size.to_scaled_points()));
|
|
}
|
|
|
|
inline int tfont::get_input_position()
|
|
{
|
|
return input_position;
|
|
}
|
|
|
|
inline int tfont::contains(charinfo *ci)
|
|
{
|
|
return fm->contains(ci->get_index());
|
|
}
|
|
|
|
inline int tfont::get_character_type(charinfo *ci)
|
|
{
|
|
return fm->get_character_type(ci->get_index());
|
|
}
|
|
|
|
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 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->get_index()))
|
|
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->get_index(),
|
|
c2->get_index(),
|
|
size.to_scaled_points());
|
|
if (n) {
|
|
*res = hunits(n);
|
|
return 1;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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;
|
|
virtual void really_transparent_char(unsigned char) = 0;
|
|
virtual void really_print_line(hunits x, vunits y, node *n,
|
|
vunits before, vunits after) = 0;
|
|
virtual void really_begin_page(int pageno, vunits page_length) = 0;
|
|
virtual void really_copy_file(hunits x, vunits y, const char *filename);
|
|
protected:
|
|
FILE *fp;
|
|
public:
|
|
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);
|
|
void begin_page(int pageno, vunits page_length);
|
|
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);
|
|
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);
|
|
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;
|
|
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;
|
|
void do_motion();
|
|
void put(char c);
|
|
void put(unsigned char c);
|
|
void put(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 *ci, tfont *tf);
|
|
void put_char_width(charinfo *ci, tfont *tf, hunits w, hunits k);
|
|
void right(hunits);
|
|
void down(vunits);
|
|
void moveto(hunits, vunits);
|
|
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);
|
|
void really_begin_page(int pageno, vunits page_length);
|
|
void really_copy_file(hunits x, vunits y, const char *filename);
|
|
void draw(char, hvpair *, int, font_size);
|
|
int get_hpos() { return hpos; }
|
|
int get_vpos() { return vpos; }
|
|
};
|
|
|
|
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(itoa(i), fp);
|
|
}
|
|
|
|
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)
|
|
{
|
|
moveto(x, y);
|
|
while (n != 0) {
|
|
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();
|
|
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 (tbuf_len == 0)
|
|
return;
|
|
if (tbuf_kern == 0)
|
|
put('t');
|
|
else {
|
|
put('u');
|
|
put(tbuf_kern);
|
|
put(' ');
|
|
}
|
|
for (int i = 0; i < tbuf_len; i++)
|
|
put(tbuf[i]);
|
|
put('\n');
|
|
tbuf_len = 0;
|
|
}
|
|
|
|
void troff_output_file::put_char_width(charinfo *ci, tfont *tf, hunits w,
|
|
hunits k)
|
|
{
|
|
if (tf != current_tfont) {
|
|
flush_tbuf();
|
|
set_font(tf);
|
|
}
|
|
char c = ci->get_ascii_code();
|
|
int kk = k.to_units();
|
|
if (c == '\0') {
|
|
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');
|
|
hpos += w.to_units() + kk;
|
|
}
|
|
else if (tcommand_flag) {
|
|
if (tbuf_len > 0 && hpos == output_hpos && vpos == output_vpos
|
|
&& kk == tbuf_kern
|
|
&& tbuf_len < TBUF_SIZE) {
|
|
tbuf[tbuf_len++] = c;
|
|
output_hpos += w.to_units() + kk;
|
|
hpos = output_hpos;
|
|
return;
|
|
}
|
|
flush_tbuf();
|
|
do_motion();
|
|
tbuf[tbuf_len++] = c;
|
|
output_hpos += w.to_units() + kk;
|
|
tbuf_kern = kk;
|
|
hpos = output_hpos;
|
|
}
|
|
else {
|
|
// flush_tbuf();
|
|
int n = hpos - output_hpos;
|
|
if (vpos == output_vpos && n > 0 && n < 100 && !force_motion) {
|
|
put(char(n/10 + '0'));
|
|
put(char(n%10 + '0'));
|
|
put(c);
|
|
output_hpos = hpos;
|
|
}
|
|
else {
|
|
do_motion();
|
|
put('c');
|
|
put(c);
|
|
}
|
|
hpos += w.to_units() + kk;
|
|
}
|
|
}
|
|
|
|
void troff_output_file::put_char(charinfo *ci, tfont *tf)
|
|
{
|
|
flush_tbuf();
|
|
if (tf != current_tfont)
|
|
set_font(tf);
|
|
char c = ci->get_ascii_code();
|
|
if (c == '\0') {
|
|
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 && n > 0 && n < 100) {
|
|
put(char(n/10 + '0'));
|
|
put(char(n%10 + '0'));
|
|
put(c);
|
|
output_hpos = hpos;
|
|
}
|
|
else {
|
|
do_motion();
|
|
put('c');
|
|
put(c);
|
|
}
|
|
}
|
|
}
|
|
|
|
void troff_output_file::set_font(tfont *tf)
|
|
{
|
|
if (current_tfont == tf)
|
|
return;
|
|
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));
|
|
a_delete old_font_position;
|
|
}
|
|
font_position[n] = nm;
|
|
}
|
|
if (current_font_number != n) {
|
|
put('f');
|
|
put(n);
|
|
put('\n');
|
|
current_font_number = n;
|
|
}
|
|
int 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;
|
|
}
|
|
|
|
void troff_output_file::draw(char code, hvpair *point, int npoints,
|
|
font_size fsize)
|
|
{
|
|
flush_tbuf();
|
|
do_motion();
|
|
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);
|
|
int i;
|
|
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());
|
|
}
|
|
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;
|
|
}
|
|
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 = fopen(filename, "r");
|
|
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()
|
|
{
|
|
a_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_height(0), current_slant(0), tbuf_len(0), nfont_positions(10),
|
|
begun_page(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()
|
|
{
|
|
}
|
|
|
|
output_file::~output_file()
|
|
{
|
|
}
|
|
|
|
void output_file::trailer(vunits)
|
|
{
|
|
}
|
|
|
|
real_output_file::real_output_file()
|
|
: printing(0)
|
|
{
|
|
#ifndef POPEN_MISSING
|
|
if (pipe_command) {
|
|
if ((fp = popen(pipe_command, "w")) != 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;
|
|
// To avoid looping, set fp to 0 before calling fatal().
|
|
if (ferror(fp) || fflush(fp) < 0) {
|
|
fp = 0;
|
|
fatal("error writing output file");
|
|
}
|
|
#ifndef POPEN_MISSING
|
|
if (piped) {
|
|
int result = pclose(fp);
|
|
fp = 0;
|
|
if (result < 0)
|
|
fatal("pclose failed");
|
|
if ((result & 0x7f) != 0)
|
|
error("output process `%1' got fatal signal %2",
|
|
pipe_command, result & 0x7f);
|
|
else {
|
|
int exit_status = (result >> 8) & 0xff;
|
|
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("error closing output file");
|
|
}
|
|
}
|
|
|
|
void real_output_file::flush()
|
|
{
|
|
if (fflush(fp) < 0)
|
|
fatal("error writing output file");
|
|
}
|
|
|
|
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)
|
|
really_copy_file(x, y, filename);
|
|
}
|
|
|
|
void real_output_file::transparent_char(unsigned char c)
|
|
{
|
|
if (printing)
|
|
really_transparent_char(c);
|
|
}
|
|
|
|
void real_output_file::print_line(hunits x, vunits y, node *n,
|
|
vunits before, vunits after)
|
|
{
|
|
if (printing)
|
|
really_print_line(x, y, n, before, after);
|
|
delete_node_list(n);
|
|
}
|
|
|
|
void real_output_file::really_copy_file(hunits, vunits, const char *)
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
|
|
/* 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)
|
|
{
|
|
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("<beginning of page>\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)
|
|
{
|
|
}
|
|
|
|
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 *, node * = 0);
|
|
int ends_sentence();
|
|
int overlaps_vertically();
|
|
int overlaps_horizontally();
|
|
};
|
|
|
|
charinfo_node::charinfo_node(charinfo *c, node *x)
|
|
: ci(c), node(x)
|
|
{
|
|
}
|
|
|
|
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 {
|
|
static glyph_node *free_list;
|
|
protected:
|
|
tfont *tf;
|
|
#ifdef STORE_WIDTH
|
|
hunits wid;
|
|
glyph_node(charinfo *, tfont *, hunits, node * = 0);
|
|
#endif
|
|
public:
|
|
void *operator new(size_t);
|
|
void operator delete(void *);
|
|
glyph_node(charinfo *, tfont *, node * = 0);
|
|
~glyph_node() {}
|
|
node *copy();
|
|
node *merge_glyph_node(glyph_node *);
|
|
node *merge_self(node *);
|
|
#if defined(STORE_WIDTH) && defined(BROKEN_GXX_VIRTUAL_INLINE)
|
|
inline
|
|
#endif
|
|
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();
|
|
void tprint(troff_output_file *);
|
|
void zero_width_tprint(troff_output_file *);
|
|
hyphen_list *get_hyphen_list(hyphen_list *ss = 0);
|
|
node *add_self(node *, hyphen_list **);
|
|
void ascii_print(ascii_output_file *);
|
|
void asciify(macro *);
|
|
int character_type();
|
|
int same(node *);
|
|
const char *type();
|
|
};
|
|
|
|
glyph_node *glyph_node::free_list = 0;
|
|
|
|
class ligature_node : public glyph_node {
|
|
node *n1;
|
|
node *n2;
|
|
#ifdef STORE_WIDTH
|
|
ligature_node(charinfo *, tfont *, hunits, node *gn1, node *gn2, node *x = 0);
|
|
#endif
|
|
public:
|
|
void *operator new(size_t);
|
|
void operator delete(void *);
|
|
ligature_node(charinfo *, tfont *, node *gn1, node *gn2, node *x = 0);
|
|
~ligature_node();
|
|
node *copy();
|
|
node *add_self(node *, hyphen_list **);
|
|
hyphen_list *get_hyphen_list(hyphen_list *ss = 0);
|
|
void ascii_print(ascii_output_file *);
|
|
void asciify(macro *);
|
|
int same(node *);
|
|
const char *type();
|
|
};
|
|
|
|
class kern_pair_node : public node {
|
|
hunits amount;
|
|
node *n1;
|
|
node *n2;
|
|
public:
|
|
kern_pair_node(hunits n, node *first, node *second, node *x = 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 *ss = 0);
|
|
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();
|
|
void vertical_extent(vunits *, vunits *);
|
|
};
|
|
|
|
class dbreak_node : public node {
|
|
node *none;
|
|
node *pre;
|
|
node *post;
|
|
public:
|
|
dbreak_node(node *n, node *p, node *x = 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();
|
|
};
|
|
|
|
void *glyph_node::operator new(size_t n)
|
|
{
|
|
assert(n == sizeof(glyph_node));
|
|
if (!free_list) {
|
|
const int BLOCK = 1024;
|
|
free_list = (glyph_node *)new char[sizeof(glyph_node)*BLOCK];
|
|
for (int i = 0; i < BLOCK - 1; i++)
|
|
free_list[i].next = free_list + i + 1;
|
|
free_list[BLOCK-1].next = 0;
|
|
}
|
|
glyph_node *p = free_list;
|
|
free_list = (glyph_node *)(free_list->next);
|
|
p->next = 0;
|
|
return p;
|
|
}
|
|
|
|
void *ligature_node::operator new(size_t n)
|
|
{
|
|
return new char[n];
|
|
}
|
|
|
|
void glyph_node::operator delete(void *p)
|
|
{
|
|
if (p) {
|
|
((glyph_node *)p)->next = free_list;
|
|
free_list = (glyph_node *)p;
|
|
}
|
|
}
|
|
|
|
void ligature_node::operator delete(void *p)
|
|
{
|
|
delete p;
|
|
}
|
|
|
|
glyph_node::glyph_node(charinfo *c, tfont *t, node *x)
|
|
: charinfo_node(c, x), tf(t)
|
|
{
|
|
#ifdef STORE_WIDTH
|
|
wid = tf->get_width(ci);
|
|
#endif
|
|
}
|
|
|
|
#ifdef STORE_WIDTH
|
|
glyph_node::glyph_node(charinfo *c, tfont *t, hunits w, node *x)
|
|
: charinfo_node(c, x), tf(t), wid(w)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
node *glyph_node::copy()
|
|
{
|
|
#ifdef STORE_WIDTH
|
|
return new glyph_node(ci, tf, wid);
|
|
#else
|
|
return new glyph_node(ci, tf);
|
|
#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)
|
|
{
|
|
return new hyphen_list(ci->get_hyphenation_code(), tail);
|
|
}
|
|
|
|
|
|
tfont *node::get_tfont()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
tfont *glyph_node::get_tfont()
|
|
{
|
|
return tf;
|
|
}
|
|
|
|
node *node::merge_glyph_node(glyph_node * /*gn*/)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
node *glyph_node::merge_glyph_node(glyph_node *gn)
|
|
{
|
|
if (tf == gn->tf) {
|
|
charinfo *lig;
|
|
if ((lig = tf->get_lig(ci, gn->ci)) != 0) {
|
|
node *next1 = next;
|
|
next = 0;
|
|
return new ligature_node(lig, tf, this, gn, next1);
|
|
}
|
|
hunits kern;
|
|
if (tf->get_kern(ci, gn->ci, &kern)) {
|
|
node *next1 = next;
|
|
next = 0;
|
|
return new kern_pair_node(kern, this, gn, 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());
|
|
}
|
|
|
|
ligature_node::ligature_node(charinfo *c, tfont *t,
|
|
node *gn1, node *gn2, node *x)
|
|
: glyph_node(c, t, x), n1(gn1), n2(gn2)
|
|
{
|
|
}
|
|
|
|
#ifdef STORE_WIDTH
|
|
ligature_node::ligature_node(charinfo *c, tfont *t, hunits w,
|
|
node *gn1, node *gn2, node *x)
|
|
: glyph_node(c, t, w, x), n1(gn1), n2(gn2)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
ligature_node::~ligature_node()
|
|
{
|
|
delete n1;
|
|
delete n2;
|
|
}
|
|
|
|
node *ligature_node::copy()
|
|
{
|
|
#ifdef STORE_WIDTH
|
|
return new ligature_node(ci, tf, wid, n1->copy(), n2->copy());
|
|
#else
|
|
return new ligature_node(ci, tf, n1->copy(), n2->copy());
|
|
#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)
|
|
{
|
|
return n1->get_hyphen_list(n2->get_hyphen_list(tail));
|
|
}
|
|
|
|
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, node *x)
|
|
: node(x), n1(first), n2(second), amount(n)
|
|
{
|
|
}
|
|
|
|
dbreak_node::dbreak_node(node *n, node *p, node *x)
|
|
: node(x), 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 = n2->get_tfont();
|
|
if (tf) {
|
|
if (tf->contains(soft_hyphen_char)) {
|
|
node *next1 = next;
|
|
next = 0;
|
|
node *n = copy();
|
|
glyph_node *gn = new glyph_node(soft_hyphen_char, tf);
|
|
node *nn = n->merge_glyph_node(gn);
|
|
if (nn == 0) {
|
|
gn->next = n;
|
|
nn = gn;
|
|
}
|
|
return new dbreak_node(this, nn, 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());
|
|
}
|
|
|
|
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));
|
|
p->post = copy_node_list(post);
|
|
return p;
|
|
}
|
|
|
|
hyphen_list *node::get_hyphen_list(hyphen_list *tail)
|
|
{
|
|
return tail;
|
|
}
|
|
|
|
|
|
hyphen_list *kern_pair_node::get_hyphen_list(hyphen_list *tail)
|
|
{
|
|
return n1->get_hyphen_list(n2->get_hyphen_list(tail));
|
|
}
|
|
|
|
class hyphen_inhibitor_node : public node {
|
|
public:
|
|
hyphen_inhibitor_node(node *nd = 0);
|
|
node *copy();
|
|
int same(node *);
|
|
const char *type();
|
|
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";
|
|
}
|
|
|
|
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)) {
|
|
node *next1 = next;
|
|
next = 0;
|
|
node *n = copy();
|
|
glyph_node *gn = new glyph_node(soft_hyphen_char, tf);
|
|
node *n1 = n->merge_glyph_node(gn);
|
|
if (n1 == 0) {
|
|
gn->next = n;
|
|
n1 = gn;
|
|
}
|
|
return new dbreak_node(this, n1, 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;
|
|
}
|
|
|
|
hunits hmotion_node::width()
|
|
{
|
|
return n;
|
|
}
|
|
|
|
units node::size()
|
|
{
|
|
return points_to_units(10);
|
|
}
|
|
|
|
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 = n->last_char_node();
|
|
if (last)
|
|
return last;
|
|
}
|
|
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, node * = 0);
|
|
~italic_corrected_node();
|
|
node *copy();
|
|
void ascii_print(ascii_output_file *);
|
|
void asciify(macro *m);
|
|
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 *ss = 0);
|
|
int character_type();
|
|
void tprint(troff_output_file *);
|
|
hunits subscript_correction();
|
|
hunits skew();
|
|
node *add_self(node *, hyphen_list **);
|
|
const char *type();
|
|
};
|
|
|
|
node *node::add_italic_correction(hunits *width)
|
|
{
|
|
hunits ic = italic_correction();
|
|
if (ic.is_zero())
|
|
return this;
|
|
else {
|
|
node *next1 = next;
|
|
next = 0;
|
|
*width += ic;
|
|
return new italic_corrected_node(this, ic, next1);
|
|
}
|
|
}
|
|
|
|
italic_corrected_node::italic_corrected_node(node *nn, hunits xx, node *p)
|
|
: n(nn), x(xx), node(p)
|
|
{
|
|
assert(n != 0);
|
|
}
|
|
|
|
italic_corrected_node::~italic_corrected_node()
|
|
{
|
|
delete n;
|
|
}
|
|
|
|
node *italic_corrected_node::copy()
|
|
{
|
|
return new italic_corrected_node(n->copy(), x);
|
|
}
|
|
|
|
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)
|
|
{
|
|
return n->get_hyphen_list(tail);
|
|
}
|
|
|
|
int italic_corrected_node::character_type()
|
|
{
|
|
return n->character_type();
|
|
}
|
|
|
|
class break_char_node : public node {
|
|
node *ch;
|
|
char break_code;
|
|
public:
|
|
break_char_node(node *, 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 *s = 0);
|
|
void tprint(troff_output_file *);
|
|
void zero_width_tprint(troff_output_file *);
|
|
void ascii_print(ascii_output_file *);
|
|
void asciify(macro *m);
|
|
hyphenation_type get_hyphenation_type();
|
|
int overlaps_vertically();
|
|
int overlaps_horizontally();
|
|
units size();
|
|
tfont *get_tfont();
|
|
int same(node *);
|
|
const char *type();
|
|
};
|
|
|
|
break_char_node::break_char_node(node *n, int c, node *x)
|
|
: node(x), ch(n), break_code(c)
|
|
{
|
|
}
|
|
|
|
break_char_node::~break_char_node()
|
|
{
|
|
delete ch;
|
|
}
|
|
|
|
node *break_char_node::copy()
|
|
{
|
|
return new break_char_node(ch->copy(), break_code);
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
node *break_char_node::add_self(node *n, hyphen_list **p)
|
|
{
|
|
assert((*p)->hyphenation_code == 0);
|
|
if ((*p)->breakable && (break_code & 1)) {
|
|
n = new space_node(H0, n);
|
|
n->freeze_space();
|
|
}
|
|
next = n;
|
|
n = this;
|
|
if ((*p)->breakable && (break_code & 2)) {
|
|
n = new space_node(H0, 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)
|
|
{
|
|
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);
|
|
}
|
|
|
|
node *vertical_size_node::copy()
|
|
{
|
|
return new vertical_size_node(n);
|
|
}
|
|
|
|
node *hmotion_node::copy()
|
|
{
|
|
return new hmotion_node(n);
|
|
}
|
|
|
|
node *space_char_hmotion_node::copy()
|
|
{
|
|
return new space_char_hmotion_node(n);
|
|
}
|
|
|
|
node *vmotion_node::copy()
|
|
{
|
|
return new vmotion_node(n);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
node *hline_node::copy()
|
|
{
|
|
return new hline_node(x, n ? n->copy() : 0);
|
|
}
|
|
|
|
hunits hline_node::width()
|
|
{
|
|
return x < H0 ? H0 : x;
|
|
}
|
|
|
|
|
|
vline_node::~vline_node()
|
|
{
|
|
if (n)
|
|
delete n;
|
|
}
|
|
|
|
node *vline_node::copy()
|
|
{
|
|
return new vline_node(x, n ? n->copy() : 0);
|
|
}
|
|
|
|
hunits vline_node::width()
|
|
{
|
|
return n == 0 ? H0 : n->width();
|
|
}
|
|
|
|
|
|
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));
|
|
}
|
|
|
|
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() : max_width(H0), list(0)
|
|
{
|
|
}
|
|
|
|
overstrike_node::~overstrike_node()
|
|
{
|
|
delete_node_list(list);
|
|
}
|
|
|
|
node *overstrike_node::copy()
|
|
{
|
|
overstrike_node *on = new overstrike_node;
|
|
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() : max_width(H0), list(0)
|
|
{
|
|
}
|
|
|
|
bracket_node::~bracket_node()
|
|
{
|
|
delete_node_list(list);
|
|
}
|
|
|
|
node *bracket_node::copy()
|
|
{
|
|
bracket_node *on = new bracket_node;
|
|
for (node *tem = list; tem; tem = tem->next)
|
|
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)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#if 0
|
|
space_node *space_node::free_list = 0;
|
|
|
|
void *space_node::operator new(size_t n)
|
|
{
|
|
assert(n == sizeof(space_node));
|
|
if (!free_list) {
|
|
free_list = (space_node *)new char[sizeof(space_node)*BLOCK];
|
|
for (int i = 0; i < BLOCK - 1; i++)
|
|
free_list[i].next = free_list + i + 1;
|
|
free_list[BLOCK-1].next = 0;
|
|
}
|
|
space_node *p = free_list;
|
|
free_list = (space_node *)(free_list->next);
|
|
p->next = 0;
|
|
return p;
|
|
}
|
|
|
|
inline void space_node::operator delete(void *p)
|
|
{
|
|
if (p) {
|
|
((space_node *)p)->next = free_list;
|
|
free_list = (space_node *)p;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
space_node::space_node(hunits nn, node *p) : node(p), n(nn), set(0)
|
|
{
|
|
}
|
|
|
|
space_node::space_node(hunits nn, int s, node *p) : node(p), n(nn), set(s)
|
|
{
|
|
}
|
|
|
|
#if 0
|
|
space_node::~space_node()
|
|
{
|
|
}
|
|
#endif
|
|
|
|
node *space_node::copy()
|
|
{
|
|
return new space_node(n, set);
|
|
}
|
|
|
|
int space_node::nspaces()
|
|
{
|
|
return set ? 0 : 1;
|
|
}
|
|
|
|
int space_node::merge_space(hunits h)
|
|
{
|
|
n += h;
|
|
return 1;
|
|
}
|
|
|
|
hunits space_node::width()
|
|
{
|
|
return n;
|
|
}
|
|
|
|
void node::spread_space(int*, hunits*)
|
|
{
|
|
}
|
|
|
|
void space_node::spread_space(int *nspaces, hunits *desired_space)
|
|
{
|
|
if (!set) {
|
|
assert(*nspaces > 0);
|
|
if (*nspaces == 1) {
|
|
n += *desired_space;
|
|
*desired_space = H0;
|
|
}
|
|
else {
|
|
hunits extra = *desired_space / *nspaces;
|
|
*desired_space -= extra;
|
|
n += extra;
|
|
}
|
|
*nspaces -= 1;
|
|
set = 1;
|
|
}
|
|
}
|
|
|
|
void node::freeze_space()
|
|
{
|
|
}
|
|
|
|
void space_node::freeze_space()
|
|
{
|
|
set = 1;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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::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_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;
|
|
}
|
|
|
|
space_char_hmotion_node::space_char_hmotion_node(hunits i, node *next)
|
|
: hmotion_node(i, next)
|
|
{
|
|
}
|
|
|
|
void space_char_hmotion_node::asciify(macro *m)
|
|
{
|
|
m->append(' ');
|
|
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 width, int ns, breakpoint *rest,
|
|
int is_inner)
|
|
{
|
|
if (next->discardable())
|
|
return rest;
|
|
breakpoint *bp = new breakpoint;
|
|
bp->next = rest;
|
|
bp->width = width;
|
|
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->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 width, int ns,
|
|
breakpoint *rest, int is_inner)
|
|
{
|
|
breakpoint *bp = new breakpoint;
|
|
bp->next = rest;
|
|
bp->width = width;
|
|
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, &width, 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;
|
|
}
|
|
|
|
int node::interpret(macro *)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
special_node::special_node(const macro &m)
|
|
: mac(m)
|
|
{
|
|
}
|
|
|
|
int special_node::same(node *n)
|
|
{
|
|
return mac == ((special_node *)n)->mac;
|
|
}
|
|
|
|
const char *special_node::type()
|
|
{
|
|
return "special_node";
|
|
}
|
|
|
|
node *special_node::copy()
|
|
{
|
|
return new special_node(mac);
|
|
}
|
|
|
|
void special_node::tprint_start(troff_output_file *out)
|
|
{
|
|
out->start_special();
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
/* composite_node */
|
|
|
|
class composite_node : public charinfo_node {
|
|
node *n;
|
|
tfont *tf;
|
|
public:
|
|
composite_node(node *, charinfo *, tfont *, 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 *tail);
|
|
node *add_self(node *, hyphen_list **);
|
|
tfont *get_tfont();
|
|
int same(node *);
|
|
const char *type();
|
|
void vertical_extent(vunits *, vunits *);
|
|
vunits vertical_width();
|
|
};
|
|
|
|
composite_node::composite_node(node *p, charinfo *c, tfont *t, node *x)
|
|
: charinfo_node(c, 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);
|
|
}
|
|
|
|
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_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)
|
|
{
|
|
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 *min, vunits *max)
|
|
{
|
|
n = reverse_node_list(n);
|
|
node_list_vertical_extent(n, min, max);
|
|
n = reverse_node_list(n);
|
|
}
|
|
|
|
word_space_node::word_space_node(hunits d, node *x) : space_node(d, x)
|
|
{
|
|
}
|
|
|
|
word_space_node::word_space_node(hunits d, int s, node *x)
|
|
: space_node(d, s, x)
|
|
{
|
|
}
|
|
|
|
node *word_space_node::copy()
|
|
{
|
|
return new word_space_node(n, set);
|
|
}
|
|
|
|
void word_space_node::tprint(troff_output_file *out)
|
|
{
|
|
out->word_marker();
|
|
space_node::tprint(out);
|
|
}
|
|
|
|
unbreakable_space_node::unbreakable_space_node(hunits d, node *x)
|
|
: word_space_node(d, x)
|
|
{
|
|
}
|
|
|
|
unbreakable_space_node::unbreakable_space_node(hunits d, int s, node *x)
|
|
: word_space_node(d, s, x)
|
|
{
|
|
}
|
|
|
|
node *unbreakable_space_node::copy()
|
|
{
|
|
return new unbreakable_space_node(n, set);
|
|
}
|
|
|
|
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)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
hvpair::hvpair()
|
|
{
|
|
}
|
|
|
|
draw_node::draw_node(char c, hvpair *p, int np, font_size s)
|
|
: code(c), npoints(np), sz(s)
|
|
{
|
|
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)
|
|
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";
|
|
}
|
|
|
|
draw_node::~draw_node()
|
|
{
|
|
if (point)
|
|
a_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);
|
|
}
|
|
|
|
void draw_node::tprint(troff_output_file *out)
|
|
{
|
|
out->draw(code, point, npoints, sz);
|
|
}
|
|
|
|
/* tprint methods */
|
|
|
|
void glyph_node::tprint(troff_output_file *out)
|
|
{
|
|
tfont *ptf = tf->get_plain();
|
|
if (ptf == tf)
|
|
out->put_char_width(ci, ptf, 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);
|
|
out->right(offset);
|
|
}
|
|
out->put_char_width(ci, ptf, 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);
|
|
if (bold) {
|
|
out->right(offset);
|
|
out->put_char(ci, ptf);
|
|
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);
|
|
n->tprint(out);
|
|
out->right(xx - xx2);
|
|
}
|
|
else {
|
|
hunits rem = x - w*i;
|
|
if (rem > H0)
|
|
if (n->overlaps_horizontally()) {
|
|
n->tprint(out);
|
|
out->right(rem - w);
|
|
}
|
|
else
|
|
out->right(rem);
|
|
while (--i >= 0)
|
|
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);
|
|
n->tprint(out);
|
|
out->down(-h);
|
|
}
|
|
else {
|
|
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);
|
|
}
|
|
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->right(n);
|
|
}
|
|
|
|
void hmotion_node::tprint(troff_output_file *out)
|
|
{
|
|
out->right(n);
|
|
}
|
|
|
|
void vmotion_node::tprint(troff_output_file *out)
|
|
{
|
|
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);
|
|
}
|
|
|
|
node *make_composite_node(charinfo *s, environment *env)
|
|
{
|
|
int fontno = env_definite_font(env);
|
|
if (fontno < 0) {
|
|
error("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);
|
|
}
|
|
|
|
node *make_glyph_node(charinfo *s, environment *env, int no_error_message = 0)
|
|
{
|
|
int fontno = env_definite_font(env);
|
|
if (fontno < 0) {
|
|
error("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) {
|
|
if (s->numbered()) {
|
|
if (!no_error_message)
|
|
warning(WARN_CHAR, "can't find numbered character %1",
|
|
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) {
|
|
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 0
|
|
&& global_special_fonts == 0 && font_table[fontno]->sf == 0
|
|
#endif
|
|
) {
|
|
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 (!no_error_message && s->first_time_not_found()) {
|
|
unsigned char input_code = s->get_ascii_code();
|
|
if (input_code != 0) {
|
|
if (csgraph(input_code))
|
|
warning(WARN_CHAR, "can't find character `%1'", input_code);
|
|
else
|
|
warning(WARN_CHAR, "can't find character with input code %1",
|
|
int(input_code));
|
|
}
|
|
else
|
|
warning(WARN_CHAR, "can't find special character `%1'",
|
|
s->nm.contents());
|
|
}
|
|
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();
|
|
return new glyph_node(s, tf);
|
|
}
|
|
|
|
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());
|
|
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)
|
|
return make_composite_node(ci, env);
|
|
else
|
|
return make_glyph_node(ci, env);
|
|
}
|
|
|
|
int character_exists(charinfo *ci, environment *env)
|
|
{
|
|
if (ci->get_special_translation() != charinfo::TRANSLATE_NONE)
|
|
return 1;
|
|
charinfo *tem = ci->get_translation();
|
|
if (tem)
|
|
ci = tem;
|
|
if (ci->get_macro())
|
|
return 1;
|
|
node *nd = make_glyph_node(ci, env, 1);
|
|
if (nd) {
|
|
delete nd;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
node *node::add_char(charinfo *ci, environment *env, hunits *widthp)
|
|
{
|
|
node *res;
|
|
switch (ci->get_special_translation()) {
|
|
case charinfo::TRANSLATE_SPACE:
|
|
res = new space_char_hmotion_node(env->get_space_width(), this);
|
|
*widthp += res->width();
|
|
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) {
|
|
res = make_composite_node(ci, env);
|
|
if (res) {
|
|
res->next = this;
|
|
*widthp += res->width();
|
|
}
|
|
else
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
int break_code = 0;
|
|
if (ci->can_break_before())
|
|
break_code = 1;
|
|
if (ci->can_break_after())
|
|
break_code |= 2;
|
|
if (break_code) {
|
|
node *next1 = res->next;
|
|
res->next = 0;
|
|
res = new break_char_node(res, break_code, 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 vertical_size_node::same(node *nd)
|
|
{
|
|
return n == ((vertical_size_node *)nd)->n;
|
|
}
|
|
|
|
const char *vertical_size_node::type()
|
|
{
|
|
return "vertical_size_node";
|
|
}
|
|
|
|
int hmotion_node::same(node *nd)
|
|
{
|
|
return n == ((hmotion_node *)nd)->n;
|
|
}
|
|
|
|
const char *hmotion_node::type()
|
|
{
|
|
return "hmotion_node";
|
|
}
|
|
|
|
int space_char_hmotion_node::same(node *nd)
|
|
{
|
|
return n == ((space_char_hmotion_node *)nd)->n;
|
|
}
|
|
|
|
const char *space_char_hmotion_node::type()
|
|
{
|
|
return "space_char_hmotion_node";
|
|
}
|
|
|
|
int vmotion_node::same(node *nd)
|
|
{
|
|
return n == ((vmotion_node *)nd)->n;
|
|
}
|
|
|
|
const char *vmotion_node::type()
|
|
{
|
|
return "vmotion_node";
|
|
}
|
|
|
|
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 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 dummy_node::same(node * /*nd*/)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
const char *dummy_node::type()
|
|
{
|
|
return "dummy_node";
|
|
}
|
|
|
|
int transparent_dummy_node::same(node * /*nd*/)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
const char *transparent_dummy_node::type()
|
|
{
|
|
return "transparent_dummy_node";
|
|
}
|
|
|
|
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 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";
|
|
}
|
|
|
|
|
|
left_italic_corrected_node::left_italic_corrected_node(node *x)
|
|
: n(0), node(x)
|
|
{
|
|
}
|
|
|
|
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;
|
|
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::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 *min, vunits *max)
|
|
{
|
|
if (n)
|
|
n->vertical_extent(min, max);
|
|
else
|
|
node::vertical_extent(min, max);
|
|
}
|
|
|
|
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)
|
|
{
|
|
return n ? n->get_hyphen_list(tail) : tail;
|
|
}
|
|
|
|
node *left_italic_corrected_node::add_self(node *nd, hyphen_list **p)
|
|
{
|
|
if (n) {
|
|
nd = new left_italic_corrected_node(nd);
|
|
nd = n->add_self(nd, p);
|
|
n = 0;
|
|
delete this;
|
|
}
|
|
return nd;
|
|
}
|
|
|
|
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 bracket_node::same(node *nd)
|
|
{
|
|
return same_node_list(list, ((bracket_node *)nd)->list);
|
|
}
|
|
|
|
const char *bracket_node::type()
|
|
{
|
|
return "bracket_node";
|
|
}
|
|
|
|
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 glyph_node::same(node *nd)
|
|
{
|
|
return ci == ((glyph_node *)nd)->ci && tf == ((glyph_node *)nd)->tf;
|
|
}
|
|
|
|
const char *glyph_node::type()
|
|
{
|
|
return "glyph_node";
|
|
}
|
|
|
|
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 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 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 break_char_node::same(node *nd)
|
|
{
|
|
return (break_code == ((break_char_node *)nd)->break_code
|
|
&& same_node(ch, ((break_char_node *)nd)->ch));
|
|
}
|
|
|
|
const char *break_char_node::type()
|
|
{
|
|
return "break_char_node";
|
|
}
|
|
|
|
int line_start_node::same(node * /*nd*/)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
const char *line_start_node::type()
|
|
{
|
|
return "line_start_node";
|
|
}
|
|
|
|
int space_node::same(node *nd)
|
|
{
|
|
return n == ((space_node *)nd)->n && set == ((space_node *)nd)->set;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
const char *word_space_node::type()
|
|
{
|
|
return "word_space_node";
|
|
}
|
|
|
|
int unbreakable_space_node::same(node *nd)
|
|
{
|
|
return (n == ((unbreakable_space_node *)nd)->n
|
|
&& set == ((unbreakable_space_node *)nd)->set);
|
|
}
|
|
|
|
const char *unbreakable_space_node::type()
|
|
{
|
|
return "unbreakable_space_node";
|
|
}
|
|
|
|
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_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";
|
|
}
|
|
|
|
// 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 *));
|
|
a_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 int mount_font_no_translate(int n, symbol name, symbol external_name)
|
|
{
|
|
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;
|
|
void *p = font_dictionary.lookup(external_name);
|
|
if (p == 0) {
|
|
int not_found;
|
|
fm = font::load_font(external_name.contents(), ¬_found);
|
|
if (!fm) {
|
|
if (not_found)
|
|
warning(WARN_FONT, "can't find font `%1'", external_name.contents());
|
|
font_dictionary.lookup(external_name, &a_char);
|
|
return 0;
|
|
}
|
|
font_dictionary.lookup(name, fm);
|
|
}
|
|
else if (p == &a_char) {
|
|
#if 0
|
|
error("invalid font `%1'", external_name.contents());
|
|
#endif
|
|
return 0;
|
|
}
|
|
else
|
|
fm = (font*)p;
|
|
if (n >= font_table_size) {
|
|
if (n - font_table_size > 1000) {
|
|
error("font position too much larger than first unused position");
|
|
return 0;
|
|
}
|
|
grow_font_table(n);
|
|
}
|
|
else if (font_table[n] != 0)
|
|
delete font_table[n];
|
|
font_table[n] = new font_info(name, n, external_name, fm);
|
|
font_family::invalidate_fontno(n);
|
|
return 1;
|
|
}
|
|
|
|
int 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);
|
|
}
|
|
|
|
void 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;
|
|
}
|
|
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);
|
|
}
|
|
|
|
/* global functions */
|
|
|
|
void font_translate()
|
|
{
|
|
symbol from = get_name(1);
|
|
if (!from.is_null()) {
|
|
symbol to = get_name();
|
|
if (to.is_null() || from == to)
|
|
font_translation_dictionary.remove(from);
|
|
else
|
|
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(1);
|
|
if (!internal_name.is_null()) {
|
|
symbol external_name = get_long_name(0);
|
|
mount_font(n, internal_name, external_name); // ignore error
|
|
}
|
|
}
|
|
}
|
|
skip_line();
|
|
}
|
|
|
|
font_family::font_family(symbol s)
|
|
: nm(s), map_size(10)
|
|
{
|
|
map = new int[map_size];
|
|
for (int i = 0; i < map_size; i++)
|
|
map[i] = -1;
|
|
}
|
|
|
|
font_family::~font_family()
|
|
{
|
|
a_delete map;
|
|
}
|
|
|
|
int font_family::make_definite(int i)
|
|
{
|
|
if (i >= 0) {
|
|
if (i < map_size && map[i] >= 0)
|
|
return map[i];
|
|
else {
|
|
if (i < font_table_size && font_table[i] != 0) {
|
|
if (i >= map_size) {
|
|
int old_map_size = map_size;
|
|
int *old_map = map;
|
|
map_size *= 3;
|
|
map_size /= 2;
|
|
if (i >= map_size)
|
|
map_size = i + 10;
|
|
map = new int[map_size];
|
|
memcpy(map, old_map, old_map_size*sizeof(int));
|
|
a_delete old_map;
|
|
for (int j = old_map_size; j < map_size; j++)
|
|
map[j] = -1;
|
|
}
|
|
if (font_table[i]->is_style()) {
|
|
symbol sty = font_table[i]->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[i] = n;
|
|
}
|
|
else
|
|
return map[i] = i;
|
|
}
|
|
else
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
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 nm;
|
|
font_family *fam;
|
|
while (iter.get(&nm, (void **)&fam)) {
|
|
int map_size = fam->map_size;
|
|
if (n < map_size)
|
|
fam->map[n] = -1;
|
|
for (int i = 0; i < map_size; 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(1);
|
|
if (!internal_name.is_null())
|
|
mount_style(n, internal_name);
|
|
}
|
|
}
|
|
skip_line();
|
|
}
|
|
|
|
static int get_fontno()
|
|
{
|
|
int n;
|
|
tok.skip();
|
|
if (tok.delimiter()) {
|
|
symbol s = get_name(1);
|
|
if (!s.is_null()) {
|
|
n = symbol_fontno(s);
|
|
if (n < 0) {
|
|
n = next_available_font_position();
|
|
if (!mount_font(n, s))
|
|
return -1;
|
|
}
|
|
return curenv->get_family()->make_definite(n);
|
|
}
|
|
}
|
|
else if (get_integer(&n)) {
|
|
if (n < 0 || n >= font_table_size || font_table[n] == 0)
|
|
error("bad font number");
|
|
else
|
|
return curenv->get_family()->make_definite(n);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int underline_fontno = 2;
|
|
|
|
void underline_font()
|
|
{
|
|
int n = get_fontno();
|
|
if (n >= 0)
|
|
underline_fontno = n;
|
|
skip_line();
|
|
}
|
|
|
|
int get_underline_fontno()
|
|
{
|
|
return underline_fontno;
|
|
}
|
|
|
|
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()) {
|
|
int i = get_fontno();
|
|
if (i >= 0) {
|
|
special_font_list *tem = new special_font_list;
|
|
tem->n = i;
|
|
tem->next = 0;
|
|
*p = tem;
|
|
p = &(tem->next);
|
|
}
|
|
}
|
|
}
|
|
|
|
void font_special_request()
|
|
{
|
|
int n = get_fontno();
|
|
if (n >= 0)
|
|
read_special_fonts(&font_table[n]->sf);
|
|
skip_line();
|
|
}
|
|
|
|
|
|
void special_request()
|
|
{
|
|
read_special_fonts(&global_special_fonts);
|
|
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] != NULL;
|
|
}
|
|
|
|
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()
|
|
{
|
|
int n = get_fontno();
|
|
if (n >= 0) {
|
|
if (has_arg()) {
|
|
if (tok.delimiter()) {
|
|
int f = get_fontno();
|
|
if (f >= 0) {
|
|
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 {
|
|
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()
|
|
{
|
|
int n = get_fontno();
|
|
if (n >= 0) {
|
|
int 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()
|
|
{
|
|
int n = get_fontno();
|
|
if (n >= 0) {
|
|
int 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 itoa(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("fp", font_position);
|
|
init_request("sty", style);
|
|
init_request("cs", constant_space);
|
|
init_request("bd", bold_font);
|
|
init_request("uf", underline_font);
|
|
init_request("lg", ligature);
|
|
init_request("kern", kern_request);
|
|
init_request("tkf", track_kern);
|
|
init_request("special", special_request);
|
|
init_request("fspecial", font_special_request);
|
|
init_request("ftr", font_translate);
|
|
init_request("shc", set_soft_hyphen_char);
|
|
number_reg_dictionary.define(".fp", new next_available_font_position_reg);
|
|
number_reg_dictionary.define(".kern",
|
|
new constant_int_reg(&global_kern_mode));
|
|
number_reg_dictionary.define(".lg",
|
|
new constant_int_reg(&global_ligature_mode));
|
|
number_reg_dictionary.define(".P", new printing_reg);
|
|
soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);
|
|
}
|