// -*- C++ -*-
/* Copyright (C) 2002-2020 Free Software Foundation, Inc.
*
* Gaius Mulley (gaius@glam.ac.uk) wrote html-table.cpp
*
* html-table.h
*
* provides the methods necessary to handle indentation and tab
* positions using html tables.
*/
/*
This file is part of groff.
groff is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or
(at your option) any later version.
groff is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
#include "driver.h"
#include "stringclass.h"
#include "cset.h"
#include "html-table.h"
#include "ctype.h"
#include "html.h"
#include "html-text.h"
#if !defined(TRUE)
# define TRUE (1==1)
#endif
#if !defined(FALSE)
# define FALSE (1==0)
#endif
extern html_dialect dialect;
tabs::tabs ()
: tab(NULL)
{
}
tabs::~tabs ()
{
delete_list();
}
/*
* delete_list - frees the tab list and sets tab to NULL.
*/
void tabs::delete_list (void)
{
tab_position *p = tab;
tab_position *q;
while (p != NULL) {
q = p;
p = p->next;
delete q;
}
tab = NULL;
}
void tabs::clear (void)
{
delete_list();
}
/*
* compatible - returns TRUE if the tab stops in, s, do
* not conflict with the current tab stops.
* The new tab stops are _not_ placed into
* this class.
*/
int tabs::compatible (const char *s)
{
char align;
int total=0;
tab_position *last = tab;
if (last == NULL)
return FALSE; // no tab stops defined
// move over tag name
while ((*s != (char)0) && !isspace(*s))
s++;
while (*s != (char)0 && last != NULL) {
// move over white space
while ((*s != (char)0) && isspace(*s))
s++;
// collect alignment
align = *s;
// move over alignment
s++;
// move over white space
while ((*s != (char)0) && isspace(*s))
s++;
// collect tab position
total = atoi(s);
// move over tab position
while ((*s != (char)0) && !isspace(*s))
s++;
if (last->alignment != align || last->position != total)
return FALSE;
last = last->next;
}
return TRUE;
}
/*
* init - scans the string, s, and initializes the tab stops.
*/
void tabs::init (const char *s)
{
char align;
int total=0;
tab_position *last = NULL;
clear(); // remove any tab stops
// move over tag name
while ((*s != (char)0) && !isspace(*s))
s++;
while (*s != (char)0) {
// move over white space
while ((*s != (char)0) && isspace(*s))
s++;
// collect alignment
align = *s;
// move over alignment
s++;
// move over white space
while ((*s != (char)0) && isspace(*s))
s++;
// collect tab position
total = atoi(s);
// move over tab position
while ((*s != (char)0) && !isspace(*s))
s++;
if (last == NULL) {
tab = new tab_position;
last = tab;
} else {
last->next = new tab_position;
last = last->next;
}
last->alignment = align;
last->position = total;
last->next = NULL;
}
}
/*
* check_init - define tab stops using, s, providing none already exist.
*/
void tabs::check_init (const char *s)
{
if (tab == NULL)
init(s);
}
/*
* find_tab - returns the tab number corresponding to the position, pos.
*/
int tabs::find_tab (int pos)
{
tab_position *p;
int i=0;
for (p = tab; p != NULL; p = p->next) {
i++;
if (p->position == pos)
return i;
}
return 0;
}
/*
* get_tab_pos - returns the, nth, tab position
*/
int tabs::get_tab_pos (int n)
{
tab_position *p;
n--;
for (p = tab; (p != NULL) && (n>0); p = p->next) {
n--;
if (n == 0)
return p->position;
}
return 0;
}
char tabs::get_tab_align (int n)
{
tab_position *p;
n--;
for (p = tab; (p != NULL) && (n>0); p = p->next) {
n--;
if (n == 0)
return p->alignment;
}
return 'L';
}
/*
* dump_tab - display tab positions
*/
void tabs::dump_tabs (void)
{
int i=1;
tab_position *p;
for (p = tab; p != NULL; p = p->next) {
printf("tab %d is %d\n", i, p->position);
i++;
}
}
/*
* html_table - methods
*/
html_table::html_table (simple_output *op, int linelen)
: out(op), columns(NULL), linelength(linelen), last_col(NULL), start_space(FALSE)
{
tab_stops = new tabs();
}
html_table::~html_table ()
{
cols *c;
if (tab_stops != NULL)
delete tab_stops;
c = columns;
while (columns != NULL) {
columns = columns->next;
delete c;
c = columns;
}
}
/*
* remove_cols - remove a list of columns as defined by, c.
*/
void html_table::remove_cols (cols *c)
{
cols *p;
while (c != NULL) {
p = c;
c = c->next;
delete p;
}
}
/*
* set_linelength - sets the line length value in this table.
* It also adds an extra blank column to the
* table should linelen exceed the last column.
*/
void html_table::set_linelength (int linelen)
{
cols *p = NULL;
cols *c;
linelength = linelen;
for (c = columns; c != NULL; c = c->next) {
if (c->right > linelength) {
c->right = linelength;
remove_cols(c->next);
c->next = NULL;
return;
}
p = c;
}
if (p != NULL && p->right > 0 && linelength > p->right)
add_column(p->no+1, p->right, linelength, 'L');
}
/*
* get_effective_linelength -
*/
int html_table::get_effective_linelength (void)
{
if (columns != NULL)
return linelength - columns->left;
else
return linelength;
}
/*
* add_indent - adds the indent to a table.
*/
void html_table::add_indent (int indent)
{
if (columns != NULL && columns->left > indent)
add_column(0, indent, columns->left, 'L');
}
/*
* emit_table_header - emits the html header for this table.
*/
void html_table::emit_table_header (int space)
{
if (columns == NULL)
return;
// dump_table();
last_col = NULL;
if (linelength > 0) {
out->nl();
out->nl();
out->put_string("
put_string(">")
.nl();
if (dialect == xhtml)
emit_colspan();
out->put_string("put_string(" style=\"margin-top: ");
out->put_string(STYLE_VERTICAL_SPACE);
out->put_string("\"");
}
out->put_string(">").nl();
}
}
/*
* get_right - returns the right most position of this column.
*/
int html_table::get_right (cols *c)
{
if (c != NULL && c->right > 0)
return c->right;
if (c->next != NULL)
return c->left;
return linelength;
}
/*
* set_space - assigns start_space. Used to determine the
* vertical alignment when generating the next table row.
*/
void html_table::set_space (int space)
{
start_space = space;
}
/*
* emit_colspan - emits a series of colspan entries defining the
* table columns.
*/
void html_table::emit_colspan (void)
{
cols *b = columns;
cols *c = columns;
int width = 0;
out->put_string("");
while (c != NULL) {
if (b != NULL && b != c && is_gap(b))
/*
* blank column for gap
*/
out->put_string("")
.nl();
width = (get_right(c)*100 + get_effective_linelength()/2)
/ get_effective_linelength()
- (c->left*100 + get_effective_linelength()/2)
/get_effective_linelength();
switch (c->alignment) {
case 'C':
out->put_string("")
.nl();
break;
case 'R':
out->put_string("")
.nl();
break;
default:
out->put_string("")
.nl();
}
b = c;
c = c->next;
}
out->put_string("").nl();
}
/*
* emit_td - writes out a tag with a corresponding width
* if the dialect is html4.
*/
void html_table::emit_td (int percentage, const char *s)
{
if (percentage) {
if (dialect == html4) {
out->put_string(" | put_string(s);
out->nl();
}
else {
out->put_string(" | put_string(s);
out->nl();
}
}
}
/*
* emit_col - moves onto column, n.
*/
void html_table::emit_col (int n)
{
cols *c = columns;
cols *b = columns;
int width = 0;
// must be a different row
if (last_col != NULL && n <= last_col->no)
emit_new_row();
while (c != NULL && c->no < n)
c = c->next;
// can we find column, n?
if (c != NULL && c->no == n) {
// shutdown previous column
if (last_col != NULL)
out->put_string(" | ").nl();
// find previous column
if (last_col == NULL)
b = columns;
else
b = last_col;
// have we a gap?
if (last_col != NULL) {
emit_td(is_gap(b), ">");
b = b->next;
}
// move across to column n
while (b != c) {
// we compute the difference after converting positions
// to avoid rounding errors
width = (get_right(b)*100 + get_effective_linelength()/2)
/ get_effective_linelength()
- (b->left*100 + get_effective_linelength()/2)
/get_effective_linelength();
emit_td(width, ">");
// have we a gap?
emit_td(is_gap(b), ">");
b = b->next;
}
width = (get_right(b)*100 + get_effective_linelength()/2)
/ get_effective_linelength()
- (b->left*100 + get_effective_linelength()/2)
/get_effective_linelength();
switch (b->alignment) {
case 'C':
emit_td(width, " align=center>");
break;
case 'R':
emit_td(width, " align=right>");
break;
default:
emit_td(width);
}
// remember column, b
last_col = b;
}
}
/*
* finish_row -
*/
void html_table::finish_row (void)
{
int n = 0;
cols *c;
if (last_col != NULL) {
for (c = last_col->next; c != NULL; c = c->next)
n = c->no;
if (n > 0)
emit_col(n);
#if 1
if (last_col != NULL) {
out->put_string("");
last_col = NULL;
}
#endif
out->put_string("
").nl();
}
}
/*
* emit_new_row - move to the next row.
*/
void html_table::emit_new_row (void)
{
finish_row();
out->put_string("put_string(" style=\"margin-top: ");
out->put_string(STYLE_VERTICAL_SPACE);
out->put_string("\"");
}
out->put_string(">").nl();
start_space = FALSE;
last_col = NULL;
}
void html_table::emit_finish_table (void)
{
finish_row();
out->put_string("
");
}
/*
* add_column - adds a column. It returns FALSE if hstart..hend
* crosses into a different columns.
*/
int html_table::add_column (int coln, int hstart, int hend, char align)
{
cols *c = get_column(coln);
if (c == NULL)
return insert_column(coln, hstart, hend, align);
else
return modify_column(c, hstart, hend, align);
}
/*
* get_column - returns the column, coln.
*/
cols *html_table::get_column (int coln)
{
cols *c = columns;
while (c != NULL && coln != c->no)
c = c->next;
if (c != NULL && coln == c->no)
return c;
else
return NULL;
}
/*
* insert_column - inserts a column, coln.
* It returns TRUE if it does not bump into
* another column.
*/
int html_table::insert_column (int coln, int hstart, int hend, char align)
{
cols *c = columns;
cols *l = columns;
cols *n = NULL;
while (c != NULL && c->no < coln) {
l = c;
c = c->next;
}
if (l != NULL && l->no>coln && hend > l->left)
return FALSE; // new column bumps into previous one
l = NULL;
c = columns;
while (c != NULL && c->no < coln) {
l = c;
c = c->next;
}
if ((l != NULL) && (hstart < l->right))
return FALSE; // new column bumps into previous one
if ((l != NULL) && (l->next != NULL) &&
(l->next->left < hend))
return FALSE; // new column bumps into next one
n = new cols;
if (l == NULL) {
n->next = columns;
columns = n;
} else {
n->next = l->next;
l->next = n;
}
n->left = hstart;
n->right = hend;
n->no = coln;
n->alignment = align;
return TRUE;
}
/*
* modify_column - given a column, c, modify the width to
* contain hstart..hend.
* It returns TRUE if it does not clash with
* the next or previous column.
*/
int html_table::modify_column (cols *c, int hstart, int hend, char align)
{
cols *l = columns;
while (l != NULL && l->next != c)
l = l->next;
if ((l != NULL) && (hstart < l->right))
return FALSE; // new column bumps into previous one
if ((c->next != NULL) && (c->next->left < hend))
return FALSE; // new column bumps into next one
if (c->left > hstart)
c->left = hstart;
if (c->right < hend)
c->right = hend;
c->alignment = align;
return TRUE;
}
/*
* find_tab_column - finds the column number for position, pos.
* It searches through the list tab stops.
*/
int html_table::find_tab_column (int pos)
{
// remember the first column is reserved for untabbed glyphs
return tab_stops->find_tab(pos)+1;
}
/*
* find_column - find the column number for position, pos.
* It searches through the list of columns.
*/
int html_table::find_column (int pos)
{
int p=0;
cols *c;
for (c = columns; c != NULL; c = c->next) {
if (c->left > pos)
return p;
p = c->no;
}
return p;
}
/*
* no_columns - returns the number of table columns (rather than tabs)
*/
int html_table::no_columns (void)
{
int n=0;
cols *c;
for (c = columns; c != NULL; c = c->next)
n++;
return n;
}
/*
* is_gap - returns the gap between column, c, and the next column.
*/
int html_table::is_gap (cols *c)
{
if (c == NULL || c->right <= 0 || c->next == NULL)
return 0;
else
// we compute the difference after converting positions
// to avoid rounding errors
return (c->next->left*100 + get_effective_linelength()/2)
/ get_effective_linelength()
- (c->right*100 + get_effective_linelength()/2)
/ get_effective_linelength();
}
/*
* no_gaps - returns the number of table gaps between the columns
*/
int html_table::no_gaps (void)
{
int n=0;
cols *c;
for (c = columns; c != NULL; c = c->next)
if (is_gap(c))
n++;
return n;
}
/*
* get_tab_pos - returns the, nth, tab position
*/
int html_table::get_tab_pos (int n)
{
return tab_stops->get_tab_pos(n);
}
char html_table::get_tab_align (int n)
{
return tab_stops->get_tab_align(n);
}
void html_table::dump_table (void)
{
if (columns != NULL) {
cols *c;
for (c = columns; c != NULL; c = c->next) {
printf("column %d %d..%d %c\n", c->no, c->left, c->right, c->alignment);
}
} else
tab_stops->dump_tabs();
}
/*
* html_indent - creates an indent with indentation, ind, given
* a line length of linelength.
*/
html_indent::html_indent (simple_output *op, int ind, int pageoffset, int linelength)
{
table = new html_table(op, linelength);
table->add_column(1, ind+pageoffset, linelength, 'L');
table->add_indent(pageoffset);
in = ind;
pg = pageoffset;
ll = linelength;
}
html_indent::~html_indent (void)
{
end();
delete table;
}
void html_indent::begin (int space)
{
if (in + pg == 0) {
if (space) {
table->out->put_string(" style=\"margin-top: ");
table->out->put_string(STYLE_VERTICAL_SPACE);
table->out->put_string("\"");
}
}
else {
//
// we use exactly the same mechanism for calculating
// indentation as html_table::emit_col
//
table->out->put_string(" style=\"margin-left:")
.put_number(((in + pg) * 100 + ll/2) / ll -
(ll/2)/ll)
.put_string("%;");
if (space) {
table->out->put_string(" margin-top: ");
table->out->put_string(STYLE_VERTICAL_SPACE);
}
table->out->put_string("\"");
}
}
void html_indent::end (void)
{
}
/*
* get_reg - collects the registers as supplied during initialization.
*/
void html_indent::get_reg (int *ind, int *pageoffset, int *linelength)
{
*ind = in;
*pageoffset = pg;
*linelength = ll;
}