]>
git.cameronkatri.com Git - mandoc.git/blob - out.c
1 /* $Id: out.c,v 1.73 2018/08/19 23:10:28 schwarze Exp $ */
3 * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2011,2014,2015,2017,2018 Ingo Schwarze <schwarze@openbsd.org>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include <sys/types.h>
29 #include "mandoc_aux.h"
33 static void tblcalc_data(struct rofftbl
*, struct roffcol
*,
34 const struct tbl_opts
*, const struct tbl_dat
*,
36 static void tblcalc_literal(struct rofftbl
*, struct roffcol
*,
37 const struct tbl_dat
*, size_t);
38 static void tblcalc_number(struct rofftbl
*, struct roffcol
*,
39 const struct tbl_opts
*, const struct tbl_dat
*);
43 * Parse the *src string and store a scaling unit into *dst.
44 * If the string doesn't specify the unit, use the default.
45 * If no default is specified, fail.
46 * Return a pointer to the byte after the last byte used,
47 * or NULL on total failure.
50 a2roffsu(const char *src
, struct roffsu
*dst
, enum roffscale def
)
54 dst
->unit
= def
== SCALE_MAX
? SCALE_BU
: def
;
55 dst
->scale
= strtod(src
, &endptr
);
101 * Calculate the abstract widths and decimal positions of columns in a
102 * table. This routine allocates the columns structures then runs over
103 * all rows and cells in the table. The function pointers in "tbl" are
104 * used for the actual width calculations.
107 tblcalc(struct rofftbl
*tbl
, const struct tbl_span
*sp
,
108 size_t offset
, size_t rmargin
)
111 const struct tbl_opts
*opts
;
112 const struct tbl_dat
*dp
;
114 size_t ewidth
, xwidth
;
116 int icol
, maxcol
, necol
, nxcol
, quirkcol
;
119 * Allocate the master column specifiers. These will hold the
120 * widths and decimal positions for all cells in the column. It
121 * must be freed and nullified by the caller.
124 assert(NULL
== tbl
->cols
);
125 tbl
->cols
= mandoc_calloc((size_t)sp
->opts
->cols
,
126 sizeof(struct roffcol
));
129 for (maxcol
= -1; sp
; sp
= sp
->next
) {
130 if (TBL_SPAN_DATA
!= sp
->pos
)
134 * Account for the data cells in the layout, matching it
135 * to data cells in the data section.
137 for (dp
= sp
->first
; dp
; dp
= dp
->next
) {
138 /* Do not used spanned cells in the calculation. */
144 icol
= dp
->layout
->col
;
145 while (maxcol
< icol
)
146 tbl
->cols
[++maxcol
].spacing
= SIZE_MAX
;
147 col
= tbl
->cols
+ icol
;
148 col
->flags
|= dp
->layout
->flags
;
149 if (dp
->layout
->flags
& TBL_CELL_WIGN
)
151 if (dp
->layout
->wstr
!= NULL
&&
152 dp
->layout
->width
== 0 &&
153 a2roffsu(dp
->layout
->wstr
, &su
, SCALE_EN
)
156 (*tbl
->sulen
)(&su
, tbl
->arg
);
157 if (col
->width
< dp
->layout
->width
)
158 col
->width
= dp
->layout
->width
;
159 if (dp
->layout
->spacing
!= SIZE_MAX
&&
160 (col
->spacing
== SIZE_MAX
||
161 col
->spacing
< dp
->layout
->spacing
))
162 col
->spacing
= dp
->layout
->spacing
;
163 tblcalc_data(tbl
, col
, opts
, dp
,
165 dp
->layout
->width
? dp
->layout
->width
:
166 rmargin
? (rmargin
+ sp
->opts
->cols
/ 2)
167 / (sp
->opts
->cols
+ 1) : 0);
172 * Align numbers with text.
173 * Count columns to equalize and columns to maximize.
174 * Find maximum width of the columns to equalize.
175 * Find total width of the columns *not* to maximize.
180 for (icol
= 0; icol
<= maxcol
; icol
++) {
181 col
= tbl
->cols
+ icol
;
182 if (col
->width
> col
->nwidth
)
183 col
->decimal
+= (col
->width
- col
->nwidth
) / 2;
185 col
->width
= col
->nwidth
;
186 if (col
->spacing
== SIZE_MAX
|| icol
== maxcol
)
188 if (col
->flags
& TBL_CELL_EQUAL
) {
190 if (ewidth
< col
->width
)
193 if (col
->flags
& TBL_CELL_WMAX
)
196 xwidth
+= col
->width
;
200 * Equalize columns, if requested for any of them.
201 * Update total width of the columns not to maximize.
205 for (icol
= 0; icol
<= maxcol
; icol
++) {
206 col
= tbl
->cols
+ icol
;
207 if ( ! (col
->flags
& TBL_CELL_EQUAL
))
209 if (col
->width
== ewidth
)
211 if (nxcol
&& rmargin
)
212 xwidth
+= ewidth
- col
->width
;
218 * If there are any columns to maximize, find the total
219 * available width, deducting 3n margins between columns.
220 * Distribute the available width evenly.
223 if (nxcol
&& rmargin
) {
225 (opts
->opts
& (TBL_OPT_BOX
| TBL_OPT_DBOX
) ?
226 2 : !!opts
->lvert
+ !!opts
->rvert
);
227 if (rmargin
<= offset
+ xwidth
)
229 xwidth
= rmargin
- offset
- xwidth
;
232 * Emulate a bug in GNU tbl width calculation that
233 * manifests itself for large numbers of x-columns.
234 * Emulating it for 5 x-columns gives identical
235 * behaviour for up to 6 x-columns.
239 quirkcol
= xwidth
% nxcol
+ 2;
240 if (quirkcol
!= 3 && quirkcol
!= 4)
247 for (icol
= 0; icol
<= maxcol
; icol
++) {
248 col
= tbl
->cols
+ icol
;
249 if ( ! (col
->flags
& TBL_CELL_WMAX
))
251 col
->width
= (double)xwidth
* ++necol
/ nxcol
253 if (necol
== quirkcol
)
255 ewidth
+= col
->width
;
261 tblcalc_data(struct rofftbl
*tbl
, struct roffcol
*col
,
262 const struct tbl_opts
*opts
, const struct tbl_dat
*dp
, size_t mw
)
266 /* Branch down into data sub-types. */
268 switch (dp
->layout
->pos
) {
270 case TBL_CELL_DHORIZ
:
271 sz
= (*tbl
->len
)(1, tbl
->arg
);
276 case TBL_CELL_CENTRE
:
279 tblcalc_literal(tbl
, col
, dp
, mw
);
281 case TBL_CELL_NUMBER
:
282 tblcalc_number(tbl
, col
, opts
, dp
);
292 tblcalc_literal(struct rofftbl
*tbl
, struct roffcol
*col
,
293 const struct tbl_dat
*dp
, size_t mw
)
295 const char *str
; /* Beginning of the first line. */
296 const char *beg
; /* Beginning of the current line. */
297 char *end
; /* End of the current line. */
298 size_t lsz
; /* Length of the current line. */
299 size_t wsz
; /* Length of the current word. */
301 if (dp
->string
== NULL
|| *dp
->string
== '\0')
303 str
= mw
? mandoc_strdup(dp
->string
) : dp
->string
;
305 for (beg
= str
; beg
!= NULL
&& *beg
!= '\0'; beg
= end
) {
306 end
= mw
? strchr(beg
, ' ') : NULL
;
312 wsz
= (*tbl
->slen
)(beg
, tbl
->arg
);
313 if (mw
&& lsz
&& lsz
+ 1 + wsz
<= mw
)
317 if (col
->width
< lsz
)
325 tblcalc_number(struct rofftbl
*tbl
, struct roffcol
*col
,
326 const struct tbl_opts
*opts
, const struct tbl_dat
*dp
)
328 const char *cp
, *lastdigit
, *lastpoint
;
332 if (dp
->string
== NULL
|| *dp
->string
== '\0')
336 * Find the last digit and
337 * the last decimal point that is adjacent to a digit.
338 * The alignment indicator "\&" overrides everything.
341 lastdigit
= lastpoint
= NULL
;
342 for (cp
= dp
->string
; cp
[0] != '\0'; cp
++) {
343 if (cp
[0] == '\\' && cp
[1] == '&') {
344 lastdigit
= lastpoint
= cp
;
346 } else if (cp
[0] == opts
->decimal
&&
347 (isdigit((unsigned char)cp
[1]) ||
348 (cp
> dp
->string
&& isdigit((unsigned char)cp
[-1]))))
350 else if (isdigit((unsigned char)cp
[0]))
354 /* Not a number, treat as a literal string. */
356 totsz
= (*tbl
->slen
)(dp
->string
, tbl
->arg
);
357 if (lastdigit
== NULL
) {
358 if (col
->width
< totsz
)
363 /* Measure the width of the integer part. */
365 if (lastpoint
== NULL
)
366 lastpoint
= lastdigit
+ 1;
369 for (cp
= dp
->string
; cp
< lastpoint
; cp
++) {
371 intsz
+= (*tbl
->slen
)(buf
, tbl
->arg
);
375 * If this number has more integer digits than all numbers
376 * seen on earlier lines, shift them all to the right.
377 * If it has fewer, shift this number to the right.
380 if (intsz
> col
->decimal
) {
381 col
->nwidth
+= intsz
- col
->decimal
;
382 col
->decimal
= intsz
;
384 totsz
+= col
->decimal
- intsz
;
386 /* Update the maximum total width seen so far. */
388 if (totsz
> col
->nwidth
)