![]() ![]() | ![]() |
version 1.5, 2009/10/18 19:02:10 | version 1.52, 2014/10/14 02:16:06 | ||
---|---|---|---|
|
|
||
/* $Id$ */ | /* $Id$ */ | ||
/* | /* | ||
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@kth.se> | * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> | ||
* Copyright (c) 2011, 2014 Ingo Schwarze <schwarze@openbsd.org> | |||
* | * | ||
* Permission to use, copy, modify, and distribute this software for any | * Permission to use, copy, modify, and distribute this software for any | ||
* purpose with or without fee is hereby granted, provided that the above | * purpose with or without fee is hereby granted, provided that the above | ||
|
|
||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
*/ | */ | ||
#include "config.h" | |||
#include <sys/types.h> | #include <sys/types.h> | ||
#include <assert.h> | |||
#include <ctype.h> | #include <ctype.h> | ||
#include <stdio.h> | #include <stdio.h> | ||
#include <stdlib.h> | #include <stdlib.h> | ||
#include <string.h> | |||
#include <time.h> | |||
#include "mandoc_aux.h" | |||
#include "mandoc.h" | |||
#include "out.h" | #include "out.h" | ||
static void tblcalc_data(struct rofftbl *, struct roffcol *, | |||
const struct tbl_opts *, const struct tbl_dat *); | |||
static void tblcalc_literal(struct rofftbl *, struct roffcol *, | |||
const struct tbl_dat *); | |||
static void tblcalc_number(struct rofftbl *, struct roffcol *, | |||
const struct tbl_opts *, const struct tbl_dat *); | |||
/* | |||
/* | |||
* Convert a `scaling unit' to a consistent form, or fail. Scaling | * Convert a `scaling unit' to a consistent form, or fail. Scaling | ||
* units are documented in groff.7, mdoc.7, man.7. | * units are documented in groff.7, mdoc.7, man.7. | ||
*/ | */ | ||
|
|
||
i = hasd = 0; | i = hasd = 0; | ||
switch (*src) { | switch (*src) { | ||
case ('+'): | case '+': | ||
src++; | src++; | ||
break; | break; | ||
case ('-'): | case '-': | ||
buf[i++] = *src++; | buf[i++] = *src++; | ||
break; | break; | ||
default: | default: | ||
|
|
||
return(0); | return(0); | ||
while (i < BUFSIZ) { | while (i < BUFSIZ) { | ||
if ( ! isdigit((u_char)*src)) { | if ( ! isdigit((unsigned char)*src)) { | ||
if ('.' != *src) | if ('.' != *src) | ||
break; | break; | ||
else if (hasd) | else if (hasd) | ||
|
|
||
buf[i] = '\0'; | buf[i] = '\0'; | ||
switch (*src) { | switch (*src) { | ||
case ('c'): | case 'c': | ||
unit = SCALE_CM; | unit = SCALE_CM; | ||
break; | break; | ||
case ('i'): | case 'i': | ||
unit = SCALE_IN; | unit = SCALE_IN; | ||
break; | break; | ||
case ('P'): | case 'P': | ||
unit = SCALE_PC; | unit = SCALE_PC; | ||
break; | break; | ||
case ('p'): | case 'p': | ||
unit = SCALE_PT; | unit = SCALE_PT; | ||
break; | break; | ||
case ('f'): | case 'f': | ||
unit = SCALE_FS; | unit = SCALE_FS; | ||
break; | break; | ||
case ('v'): | case 'v': | ||
unit = SCALE_VS; | unit = SCALE_VS; | ||
break; | break; | ||
case ('m'): | case 'm': | ||
unit = SCALE_EM; | unit = SCALE_EM; | ||
break; | break; | ||
case ('\0'): | case '\0': | ||
if (SCALE_MAX == def) | if (SCALE_MAX == def) | ||
return(0); | return(0); | ||
unit = SCALE_BU; | unit = SCALE_EN; | ||
break; | break; | ||
case ('u'): | case 'u': | ||
unit = SCALE_BU; | unit = SCALE_BU; | ||
break; | break; | ||
case ('M'): | case 'M': | ||
unit = SCALE_MM; | unit = SCALE_MM; | ||
break; | break; | ||
case ('n'): | case 'n': | ||
unit = SCALE_EN; | unit = SCALE_EN; | ||
break; | break; | ||
default: | default: | ||
return(0); | return(0); | ||
} | } | ||
if ((dst->scale = atof(buf)) < 0) | /* FIXME: do this in the caller. */ | ||
dst->scale = 0; | if ((dst->scale = atof(buf)) < 0.0) | ||
dst->scale = 0.0; | |||
dst->unit = unit; | dst->unit = unit; | ||
dst->pt = hasd; | |||
return(1); | return(1); | ||
} | |||
/* | |||
* Calculate the abstract widths and decimal positions of columns in a | |||
* table. This routine allocates the columns structures then runs over | |||
* all rows and cells in the table. The function pointers in "tbl" are | |||
* used for the actual width calculations. | |||
*/ | |||
void | |||
tblcalc(struct rofftbl *tbl, const struct tbl_span *sp, | |||
size_t totalwidth) | |||
{ | |||
const struct tbl_dat *dp; | |||
struct roffcol *col; | |||
size_t ewidth, xwidth; | |||
int spans; | |||
int icol, maxcol, necol, nxcol; | |||
/* | |||
* Allocate the master column specifiers. These will hold the | |||
* widths and decimal positions for all cells in the column. It | |||
* must be freed and nullified by the caller. | |||
*/ | |||
assert(NULL == tbl->cols); | |||
tbl->cols = mandoc_calloc((size_t)sp->opts->cols, | |||
sizeof(struct roffcol)); | |||
for (maxcol = 0; sp; sp = sp->next) { | |||
if (TBL_SPAN_DATA != sp->pos) | |||
continue; | |||
spans = 1; | |||
/* | |||
* Account for the data cells in the layout, matching it | |||
* to data cells in the data section. | |||
*/ | |||
for (dp = sp->first; dp; dp = dp->next) { | |||
/* Do not used spanned cells in the calculation. */ | |||
if (0 < --spans) | |||
continue; | |||
spans = dp->spans; | |||
if (1 < spans) | |||
continue; | |||
icol = dp->layout->head->ident; | |||
if (maxcol < icol) | |||
maxcol = icol; | |||
col = tbl->cols + icol; | |||
col->flags |= dp->layout->flags; | |||
if (dp->layout->flags & TBL_CELL_WIGN) | |||
continue; | |||
tblcalc_data(tbl, col, sp->opts, dp); | |||
} | |||
} | |||
/* | |||
* Count columns to equalize and columns to maximize. | |||
* Find maximum width of the columns to equalize. | |||
* Find total width of the columns *not* to maximize. | |||
*/ | |||
necol = nxcol = 0; | |||
ewidth = xwidth = 0; | |||
for (icol = 0; icol <= maxcol; icol++) { | |||
col = tbl->cols + icol; | |||
if (col->flags & TBL_CELL_EQUAL) { | |||
necol++; | |||
if (ewidth < col->width) | |||
ewidth = col->width; | |||
} | |||
if (col->flags & TBL_CELL_WMAX) | |||
nxcol++; | |||
else | |||
xwidth += col->width; | |||
} | |||
/* | |||
* Equalize columns, if requested for any of them. | |||
* Update total width of the columns not to maximize. | |||
*/ | |||
if (necol) { | |||
for (icol = 0; icol <= maxcol; icol++) { | |||
col = tbl->cols + icol; | |||
if ( ! (col->flags & TBL_CELL_EQUAL)) | |||
continue; | |||
if (col->width == ewidth) | |||
continue; | |||
if (nxcol && totalwidth) | |||
xwidth += ewidth - col->width; | |||
col->width = ewidth; | |||
} | |||
} | |||
/* | |||
* If there are any columns to maximize, find the total | |||
* available width, deducting 3n margins between columns. | |||
* Distribute the available width evenly. | |||
*/ | |||
if (nxcol && totalwidth) { | |||
xwidth = totalwidth - 3*maxcol - xwidth; | |||
for (icol = 0; icol <= maxcol; icol++) { | |||
col = tbl->cols + icol; | |||
if ( ! (col->flags & TBL_CELL_WMAX)) | |||
continue; | |||
col->width = xwidth / nxcol--; | |||
xwidth -= col->width; | |||
} | |||
} | |||
} | |||
static void | |||
tblcalc_data(struct rofftbl *tbl, struct roffcol *col, | |||
const struct tbl_opts *opts, const struct tbl_dat *dp) | |||
{ | |||
size_t sz; | |||
/* Branch down into data sub-types. */ | |||
switch (dp->layout->pos) { | |||
case TBL_CELL_HORIZ: | |||
/* FALLTHROUGH */ | |||
case TBL_CELL_DHORIZ: | |||
sz = (*tbl->len)(1, tbl->arg); | |||
if (col->width < sz) | |||
col->width = sz; | |||
break; | |||
case TBL_CELL_LONG: | |||
/* FALLTHROUGH */ | |||
case TBL_CELL_CENTRE: | |||
/* FALLTHROUGH */ | |||
case TBL_CELL_LEFT: | |||
/* FALLTHROUGH */ | |||
case TBL_CELL_RIGHT: | |||
tblcalc_literal(tbl, col, dp); | |||
break; | |||
case TBL_CELL_NUMBER: | |||
tblcalc_number(tbl, col, opts, dp); | |||
break; | |||
case TBL_CELL_DOWN: | |||
break; | |||
default: | |||
abort(); | |||
/* NOTREACHED */ | |||
} | |||
} | |||
static void | |||
tblcalc_literal(struct rofftbl *tbl, struct roffcol *col, | |||
const struct tbl_dat *dp) | |||
{ | |||
size_t sz; | |||
const char *str; | |||
str = dp->string ? dp->string : ""; | |||
sz = (*tbl->slen)(str, tbl->arg); | |||
if (col->width < sz) | |||
col->width = sz; | |||
} | |||
static void | |||
tblcalc_number(struct rofftbl *tbl, struct roffcol *col, | |||
const struct tbl_opts *opts, const struct tbl_dat *dp) | |||
{ | |||
int i; | |||
size_t sz, psz, ssz, d; | |||
const char *str; | |||
char *cp; | |||
char buf[2]; | |||
/* | |||
* First calculate number width and decimal place (last + 1 for | |||
* non-decimal numbers). If the stored decimal is subsequent to | |||
* ours, make our size longer by that difference | |||
* (right-"shifting"); similarly, if ours is subsequent the | |||
* stored, then extend the stored size by the difference. | |||
* Finally, re-assign the stored values. | |||
*/ | |||
str = dp->string ? dp->string : ""; | |||
sz = (*tbl->slen)(str, tbl->arg); | |||
/* FIXME: TBL_DATA_HORIZ et al.? */ | |||
buf[0] = opts->decimal; | |||
buf[1] = '\0'; | |||
psz = (*tbl->slen)(buf, tbl->arg); | |||
if (NULL != (cp = strrchr(str, opts->decimal))) { | |||
buf[1] = '\0'; | |||
for (ssz = 0, i = 0; cp != &str[i]; i++) { | |||
buf[0] = str[i]; | |||
ssz += (*tbl->slen)(buf, tbl->arg); | |||
} | |||
d = ssz + psz; | |||
} else | |||
d = sz + psz; | |||
/* Adjust the settings for this column. */ | |||
if (col->decimal > d) { | |||
sz += col->decimal - d; | |||
d = col->decimal; | |||
} else | |||
col->width += d - col->decimal; | |||
if (sz > col->width) | |||
col->width = sz; | |||
if (d > col->decimal) | |||
col->decimal = d; | |||
} | } |