612 lines
12 KiB
C++
612 lines
12 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, 675 Mass Ave, Cambridge, MA 02139, USA. */
|
|
|
|
#include "eqn.h"
|
|
#include "pbox.h"
|
|
|
|
const char *current_roman_font;
|
|
|
|
char *gfont = 0;
|
|
char *grfont = 0;
|
|
char *gbfont = 0;
|
|
int gsize = 0;
|
|
|
|
int script_size_reduction = -1; // negative means reduce by a percentage
|
|
|
|
int positive_space = -1;
|
|
int negative_space = -1;
|
|
|
|
int minimum_size = 5;
|
|
|
|
int fat_offset = 4;
|
|
int body_height = 85;
|
|
int body_depth = 35;
|
|
|
|
int over_hang = 0;
|
|
int accent_width = 31;
|
|
int delimiter_factor = 900;
|
|
int delimiter_shortfall = 50;
|
|
|
|
int null_delimiter_space = 12;
|
|
int script_space = 5;
|
|
int thin_space = 17;
|
|
int medium_space = 22;
|
|
int thick_space = 28;
|
|
|
|
int num1 = 70;
|
|
int num2 = 40;
|
|
// we don't use num3, because we don't have \atop
|
|
int denom1 = 70;
|
|
int denom2 = 36;
|
|
int axis_height = 26; // in 100ths of an em
|
|
int sup1 = 42;
|
|
int sup2 = 37;
|
|
int sup3 = 28;
|
|
int default_rule_thickness = 4;
|
|
int sub1 = 20;
|
|
int sub2 = 23;
|
|
int sup_drop = 38;
|
|
int sub_drop = 5;
|
|
int x_height = 45;
|
|
int big_op_spacing1 = 11;
|
|
int big_op_spacing2 = 17;
|
|
int big_op_spacing3 = 20;
|
|
int big_op_spacing4 = 60;
|
|
int big_op_spacing5 = 10;
|
|
|
|
// These are for piles and matrices.
|
|
|
|
int baseline_sep = 140; // = num1 + denom1
|
|
int shift_down = 26; // = axis_height
|
|
int column_sep = 100; // = em space
|
|
int matrix_side_sep = 17; // = thin space
|
|
|
|
int nroff = 0; // should we grok ndefine or tdefine?
|
|
|
|
struct {
|
|
const char *name;
|
|
int *ptr;
|
|
} param_table[] = {
|
|
"fat_offset", &fat_offset,
|
|
"over_hang", &over_hang,
|
|
"accent_width", &accent_width,
|
|
"delimiter_factor", &delimiter_factor,
|
|
"delimiter_shortfall", &delimiter_shortfall,
|
|
"null_delimiter_space", &null_delimiter_space,
|
|
"script_space", &script_space,
|
|
"thin_space", &thin_space,
|
|
"medium_space", &medium_space,
|
|
"thick_space", &thick_space,
|
|
"num1", &num1,
|
|
"num2", &num2,
|
|
"denom1", &denom1,
|
|
"denom2", &denom2,
|
|
"axis_height", &axis_height,
|
|
"sup1", ¹,
|
|
"sup2", ²,
|
|
"sup3", ³,
|
|
"default_rule_thickness", &default_rule_thickness,
|
|
"sub1", &sub1,
|
|
"sub2", &sub2,
|
|
"sup_drop", &sup_drop,
|
|
"sub_drop", &sub_drop,
|
|
"x_height", &x_height,
|
|
"big_op_spacing1", &big_op_spacing1,
|
|
"big_op_spacing2", &big_op_spacing2,
|
|
"big_op_spacing3", &big_op_spacing3,
|
|
"big_op_spacing4", &big_op_spacing4,
|
|
"big_op_spacing5", &big_op_spacing5,
|
|
"minimum_size", &minimum_size,
|
|
"baseline_sep", &baseline_sep,
|
|
"shift_down", &shift_down,
|
|
"column_sep", &column_sep,
|
|
"matrix_side_sep", &matrix_side_sep,
|
|
"draw_lines", &draw_flag,
|
|
"body_height", &body_height,
|
|
"body_depth", &body_depth,
|
|
"nroff", &nroff,
|
|
0, 0
|
|
};
|
|
|
|
void set_param(const char *name, int value)
|
|
{
|
|
for (int i = 0; param_table[i].name != 0; i++)
|
|
if (strcmp(param_table[i].name, name) == 0) {
|
|
*param_table[i].ptr = value;
|
|
return;
|
|
}
|
|
error("unrecognised parameter `%1'", name);
|
|
}
|
|
|
|
int script_style(int style)
|
|
{
|
|
return style > SCRIPT_STYLE ? style - 2 : style;
|
|
}
|
|
|
|
int cramped_style(int style)
|
|
{
|
|
return (style & 1) ? style - 1 : style;
|
|
}
|
|
|
|
void set_space(int n)
|
|
{
|
|
if (n < 0)
|
|
negative_space = -n;
|
|
else
|
|
positive_space = n;
|
|
}
|
|
|
|
// Return 0 if the specified size is bad.
|
|
// The caller is responsible for giving the error message.
|
|
|
|
int set_gsize(const char *s)
|
|
{
|
|
const char *p = (*s == '+' || *s == '-') ? s + 1 : s;
|
|
char *end;
|
|
long n = strtol(p, &end, 10);
|
|
if (n <= 0 || *end != '\0' || n > INT_MAX)
|
|
return 0;
|
|
if (p > s) {
|
|
if (!gsize)
|
|
gsize = 10;
|
|
if (*s == '+') {
|
|
if (gsize > INT_MAX - n)
|
|
return 0;
|
|
gsize += int(n);
|
|
}
|
|
else {
|
|
if (gsize - n <= 0)
|
|
return 0;
|
|
gsize -= int(n);
|
|
}
|
|
}
|
|
else
|
|
gsize = int(n);
|
|
return 1;
|
|
}
|
|
|
|
void set_script_reduction(int n)
|
|
{
|
|
script_size_reduction = n;
|
|
}
|
|
|
|
const char *get_gfont()
|
|
{
|
|
return gfont ? gfont : "I";
|
|
}
|
|
|
|
const char *get_grfont()
|
|
{
|
|
return grfont ? grfont : "R";
|
|
}
|
|
|
|
const char *get_gbfont()
|
|
{
|
|
return gbfont ? gbfont : "B";
|
|
}
|
|
|
|
void set_gfont(const char *s)
|
|
{
|
|
a_delete gfont;
|
|
gfont = strsave(s);
|
|
}
|
|
|
|
void set_grfont(const char *s)
|
|
{
|
|
a_delete grfont;
|
|
grfont = strsave(s);
|
|
}
|
|
|
|
void set_gbfont(const char *s)
|
|
{
|
|
a_delete gbfont;
|
|
gbfont = strsave(s);
|
|
}
|
|
|
|
// this must be precisely 2 characters in length
|
|
#define COMPATIBLE_REG "0C"
|
|
|
|
void start_string()
|
|
{
|
|
printf(".nr " COMPATIBLE_REG " \\n(.C\n");
|
|
printf(".cp 0\n");
|
|
printf(".ds " LINE_STRING "\n");
|
|
}
|
|
|
|
void output_string()
|
|
{
|
|
printf("\\*[" LINE_STRING "]\n");
|
|
}
|
|
|
|
void restore_compatibility()
|
|
{
|
|
printf(".cp \\n(" COMPATIBLE_REG "\n");
|
|
}
|
|
|
|
void do_text(const char *s)
|
|
{
|
|
printf(".eo\n");
|
|
printf(".as " LINE_STRING " \"%s\n", s);
|
|
printf(".ec\n");
|
|
}
|
|
|
|
void set_minimum_size(int n)
|
|
{
|
|
minimum_size = n;
|
|
}
|
|
|
|
void set_script_size()
|
|
{
|
|
if (minimum_size < 0)
|
|
minimum_size = 0;
|
|
if (script_size_reduction >= 0)
|
|
printf(".ps \\n[.s]-%d>?%d\n", script_size_reduction, minimum_size);
|
|
else
|
|
printf(".ps (u;\\n[.s]*7+5/10>?%d)*1z\n", minimum_size);
|
|
}
|
|
|
|
int box::next_uid = 0;
|
|
|
|
box::box() : uid(next_uid++), spacing_type(ORDINARY_TYPE)
|
|
{
|
|
}
|
|
|
|
box::~box()
|
|
{
|
|
}
|
|
|
|
void box::top_level()
|
|
{
|
|
// debug_print();
|
|
// putc('\n', stderr);
|
|
box *b = this;
|
|
printf(".nr " SAVED_FONT_REG " \\n[.f]\n");
|
|
printf(".ft\n");
|
|
printf(".nr " SAVED_PREV_FONT_REG " \\n[.f]\n");
|
|
printf(".ft %s\n", get_gfont());
|
|
printf(".nr " SAVED_SIZE_REG " \\n[.s]z\n");
|
|
if (gsize > 0) {
|
|
char buf[INT_DIGITS + 1];
|
|
sprintf(buf, "%d", gsize);
|
|
b = new size_box(strsave(buf), b);
|
|
}
|
|
current_roman_font = get_grfont();
|
|
// This catches tabs used within \Z (which aren't allowed).
|
|
b->check_tabs(0);
|
|
int r = b->compute_metrics(DISPLAY_STYLE);
|
|
printf(".ft \\n[" SAVED_PREV_FONT_REG "]\n");
|
|
printf(".ft \\n[" SAVED_FONT_REG "]\n");
|
|
printf(".nr " MARK_OR_LINEUP_FLAG_REG " %d\n", r);
|
|
if (r == FOUND_MARK) {
|
|
printf(".nr " SAVED_MARK_REG " \\n[" MARK_REG "]\n");
|
|
printf(".nr " MARK_WIDTH_REG " 0\\n[" WIDTH_FORMAT "]\n", b->uid);
|
|
}
|
|
else if (r == FOUND_LINEUP)
|
|
printf(".if r" SAVED_MARK_REG " .as " LINE_STRING " \\h'\\n["
|
|
SAVED_MARK_REG "]u-\\n[" MARK_REG "]u'\n");
|
|
else
|
|
assert(r == FOUND_NOTHING);
|
|
// The problem here is that the argument to \f is read in copy mode,
|
|
// so we cannot use \E there; so we hide it in a string instead.
|
|
// Another problem is that if we use \R directly, then the space will
|
|
// prevent it working in a macro argument.
|
|
printf(".ds " SAVE_FONT_STRING " "
|
|
"\\R'" SAVED_INLINE_FONT_REG " \\\\n[.f]'"
|
|
"\\fP"
|
|
"\\R'" SAVED_INLINE_PREV_FONT_REG " \\\\n[.f]'"
|
|
"\\R'" SAVED_INLINE_SIZE_REG " \\\\n[.s]z'"
|
|
"\\s0"
|
|
"\\R'" SAVED_INLINE_PREV_SIZE_REG " \\\\n[.s]z'"
|
|
"\n"
|
|
".ds " RESTORE_FONT_STRING " "
|
|
"\\f[\\\\n[" SAVED_INLINE_PREV_FONT_REG "]]"
|
|
"\\f[\\\\n[" SAVED_INLINE_FONT_REG "]]"
|
|
"\\s'\\\\n[" SAVED_INLINE_PREV_SIZE_REG "]u'"
|
|
"\\s'\\\\n[" SAVED_INLINE_SIZE_REG "]u'"
|
|
"\n");
|
|
printf(".as " LINE_STRING " \\&\\E*[" SAVE_FONT_STRING "]");
|
|
printf("\\f[%s]", get_gfont());
|
|
printf("\\s'\\En[" SAVED_SIZE_REG "]u'");
|
|
current_roman_font = get_grfont();
|
|
b->output();
|
|
printf("\\E*[" RESTORE_FONT_STRING "]\n");
|
|
if (r == FOUND_LINEUP)
|
|
printf(".if r" SAVED_MARK_REG " .as " LINE_STRING " \\h'\\n["
|
|
MARK_WIDTH_REG "]u-\\n[" SAVED_MARK_REG "]u-(\\n["
|
|
WIDTH_FORMAT "]u-\\n[" MARK_REG "]u)'\n",
|
|
b->uid);
|
|
b->extra_space();
|
|
if (!inline_flag)
|
|
printf(".ne \\n[" HEIGHT_FORMAT "]u-%dM>?0+(\\n["
|
|
DEPTH_FORMAT "]u-%dM>?0)\n",
|
|
b->uid, body_height, b->uid, body_depth);
|
|
delete b;
|
|
next_uid = 0;
|
|
}
|
|
|
|
// gpic defines this register so as to make geqn not produce `\x's
|
|
#define EQN_NO_EXTRA_SPACE_REG "0x"
|
|
|
|
void box::extra_space()
|
|
{
|
|
printf(".if !r" EQN_NO_EXTRA_SPACE_REG " "
|
|
".nr " EQN_NO_EXTRA_SPACE_REG " 0\n");
|
|
if (positive_space >= 0 || negative_space >= 0) {
|
|
if (positive_space > 0)
|
|
printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] "
|
|
".as " LINE_STRING " \\x'-%dM'\n", positive_space);
|
|
if (negative_space > 0)
|
|
printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] "
|
|
".as " LINE_STRING " \\x'%dM'\n", negative_space);
|
|
positive_space = negative_space = -1;
|
|
}
|
|
else {
|
|
printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] "
|
|
".if \\n[" HEIGHT_FORMAT "]>%dM .as " LINE_STRING
|
|
" \\x'-(\\n[" HEIGHT_FORMAT
|
|
"]u-%dM)'\n",
|
|
uid, body_height, uid, body_height);
|
|
printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] "
|
|
".if \\n[" DEPTH_FORMAT "]>%dM .as " LINE_STRING
|
|
" \\x'\\n[" DEPTH_FORMAT
|
|
"]u-%dM'\n",
|
|
uid, body_depth, uid, body_depth);
|
|
}
|
|
}
|
|
|
|
int box::compute_metrics(int)
|
|
{
|
|
printf(".nr " WIDTH_FORMAT " 0\n", uid);
|
|
printf(".nr " HEIGHT_FORMAT " 0\n", uid);
|
|
printf(".nr " DEPTH_FORMAT " 0\n", uid);
|
|
return FOUND_NOTHING;
|
|
}
|
|
|
|
void box::compute_subscript_kern()
|
|
{
|
|
printf(".nr " SUB_KERN_FORMAT " 0\n", uid);
|
|
}
|
|
|
|
void box::compute_skew()
|
|
{
|
|
printf(".nr " SKEW_FORMAT " 0\n", uid);
|
|
}
|
|
|
|
void box::output()
|
|
{
|
|
}
|
|
|
|
void box::check_tabs(int)
|
|
{
|
|
}
|
|
|
|
int box::is_char()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int box::left_is_italic()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int box::right_is_italic()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void box::hint(unsigned)
|
|
{
|
|
}
|
|
|
|
void box::handle_char_type(int, int)
|
|
{
|
|
}
|
|
|
|
|
|
box_list::box_list(box *pp)
|
|
{
|
|
p = new box*[10];
|
|
for (int i = 0; i < 10; i++)
|
|
p[i] = 0;
|
|
maxlen = 10;
|
|
len = 1;
|
|
p[0] = pp;
|
|
}
|
|
|
|
void box_list::append(box *pp)
|
|
{
|
|
if (len + 1 > maxlen) {
|
|
box **oldp = p;
|
|
maxlen *= 2;
|
|
p = new box*[maxlen];
|
|
memcpy(p, oldp, sizeof(box*)*len);
|
|
a_delete oldp;
|
|
}
|
|
p[len++] = pp;
|
|
}
|
|
|
|
box_list::~box_list()
|
|
{
|
|
for (int i = 0; i < len; i++)
|
|
delete p[i];
|
|
a_delete p;
|
|
}
|
|
|
|
void box_list::list_check_tabs(int level)
|
|
{
|
|
for (int i = 0; i < len; i++)
|
|
p[i]->check_tabs(level);
|
|
}
|
|
|
|
|
|
pointer_box::pointer_box(box *pp) : p(pp)
|
|
{
|
|
spacing_type = p->spacing_type;
|
|
}
|
|
|
|
pointer_box::~pointer_box()
|
|
{
|
|
delete p;
|
|
}
|
|
|
|
int pointer_box::compute_metrics(int style)
|
|
{
|
|
int r = p->compute_metrics(style);
|
|
printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid);
|
|
printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid);
|
|
printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
|
|
return r;
|
|
}
|
|
|
|
void pointer_box::compute_subscript_kern()
|
|
{
|
|
p->compute_subscript_kern();
|
|
printf(".nr " SUB_KERN_FORMAT " \\n[" SUB_KERN_FORMAT "]\n", uid, p->uid);
|
|
}
|
|
|
|
void pointer_box::compute_skew()
|
|
{
|
|
p->compute_skew();
|
|
printf(".nr " SKEW_FORMAT " 0\\n[" SKEW_FORMAT "]\n",
|
|
uid, p->uid);
|
|
}
|
|
|
|
void pointer_box::check_tabs(int level)
|
|
{
|
|
p->check_tabs(level);
|
|
}
|
|
|
|
int simple_box::compute_metrics(int)
|
|
{
|
|
printf(".nr " WIDTH_FORMAT " 0\\w" DELIMITER_CHAR, uid);
|
|
output();
|
|
printf(DELIMITER_CHAR "\n");
|
|
printf(".nr " HEIGHT_FORMAT " 0>?\\n[rst]\n", uid);
|
|
printf(".nr " DEPTH_FORMAT " 0-\\n[rsb]>?0\n", uid);
|
|
printf(".nr " SUB_KERN_FORMAT " 0-\\n[ssc]>?0\n", uid);
|
|
printf(".nr " SKEW_FORMAT " 0\\n[skw]\n", uid);
|
|
return FOUND_NOTHING;
|
|
}
|
|
|
|
void simple_box::compute_subscript_kern()
|
|
{
|
|
// do nothing, we already computed it in do_metrics
|
|
}
|
|
|
|
void simple_box::compute_skew()
|
|
{
|
|
// do nothing, we already computed it in do_metrics
|
|
}
|
|
|
|
int box::is_simple()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int simple_box::is_simple()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
quoted_text_box::quoted_text_box(char *s) : text(s)
|
|
{
|
|
}
|
|
|
|
quoted_text_box::~quoted_text_box()
|
|
{
|
|
a_delete text;
|
|
}
|
|
|
|
void quoted_text_box::output()
|
|
{
|
|
if (text)
|
|
fputs(text, stdout);
|
|
}
|
|
|
|
tab_box::tab_box() : disabled(0)
|
|
{
|
|
}
|
|
|
|
// We treat a tab_box as having width 0 for width computations.
|
|
|
|
void tab_box::output()
|
|
{
|
|
if (!disabled)
|
|
printf("\\t");
|
|
}
|
|
|
|
void tab_box::check_tabs(int level)
|
|
{
|
|
if (level > 0) {
|
|
error("tabs allowed only at outermost level");
|
|
disabled = 1;
|
|
}
|
|
}
|
|
|
|
space_box::space_box()
|
|
{
|
|
spacing_type = SUPPRESS_TYPE;
|
|
}
|
|
|
|
void space_box::output()
|
|
{
|
|
printf("\\h'%dM'", thick_space);
|
|
}
|
|
|
|
half_space_box::half_space_box()
|
|
{
|
|
spacing_type = SUPPRESS_TYPE;
|
|
}
|
|
|
|
void half_space_box::output()
|
|
{
|
|
printf("\\h'%dM'", thin_space);
|
|
}
|
|
|
|
void box_list::list_debug_print(const char *sep)
|
|
{
|
|
p[0]->debug_print();
|
|
for (int i = 1; i < len; i++) {
|
|
fprintf(stderr, "%s", sep);
|
|
p[i]->debug_print();
|
|
}
|
|
}
|
|
|
|
void quoted_text_box::debug_print()
|
|
{
|
|
fprintf(stderr, "\"%s\"", (text ? text : ""));
|
|
}
|
|
|
|
void half_space_box::debug_print()
|
|
{
|
|
fprintf(stderr, "^");
|
|
}
|
|
|
|
void space_box::debug_print()
|
|
{
|
|
fprintf(stderr, "~");
|
|
}
|
|
|
|
void tab_box::debug_print()
|
|
{
|
|
fprintf(stderr, "<tab>");
|
|
}
|