% $Header: /cvsroot/html2ps/postscript/table.ps,v 1.1 2005/12/18 07:21:38 Konstantin Exp $
/table-get-row-height-constraints {% => Box
get-table-content-rows % => Rows
{
row-get-height-constraint
exch
array-append
}
[]
2 index
reduce % => Rows RHCs
exch pop % => RHCs
} def
/put-table-column-width-constraint % => WC Box
{
dup is-table {
dup table-get-cwc-raw % => WC Box CWC
1 index get-current-column
1 add % => WC Box CWC CC(1-based)
2 index get-table-content-rows % => WC Box CWC CC Rows
length % => WC Box CWC CC CR
4 index % => WC Box CWC CC CR WC
3 array astore % => WC Box CWC [CC CR WC]
exch array-prepend % => WC Box CWC'
1 index table-put-cwc-raw % => WC Box
} if
pop pop
} def
/table-get-cwc { % => Table
[] % => Table [](CWC)
1 index get-current-column
dup 0 ge {
1 add
{wc-none}
array-extend
1 index table-get-cwc-raw % => Table CWC RawCWCS
{ % => Table CWC RawCWC[CC CR WC]
aload pop % => Table CWC CC CR WC
% Ignore column-width constraints for colspanned columns
1 index
3 index
6 index
table-have-colspan 1 eq {
exch pop % => Table CWC CC WC
exch 1 sub exch % => Table CWC CC(0-based) WC
2 index length 2 index 1 add lt {
3 1 roll {wc-none}
array-extend % => Table WC CWC'
array-prepend % => Table CWC''
} {
2 index
3 1 roll
put
} ifelse
} {
pop pop pop
} ifelse
} forall
exch pop
} {
pop pop pop []
} ifelse
} def
% Get the cell-width constraint
% @param R row number (zero based)
% @param C column number (zero based)
% @param Table table being processed
% returns false if no constraint; WC function if constraint have been found
/table-get-cwc-raw-at { % => R C Table
[] % => R C Table []
1 index table-get-cwc-raw { % => R C Table [] CurrentWC
aload pop % => R C Table [] C' R' WC
6 index 2 index eq
6 index 4 index eq and { % => R C Table [] C' R' WC
exch pop
exch pop
exch array-append % => R C Table [WC]
} {
pop pop pop
} ifelse % => R C Table [WC]
} forall
4 1 roll
pop pop pop
dup length 0 gt {
0 get
} {
pop false
} ifelse
} def
/table-get-cwc-raw { % => Table
get-content % => Content
5 get % => CWC
} def
/table-put-cwc-raw { % => CWC Table
get-content % => CWC Content
5 % => CWC Content 5
3 2 roll % => Content 5 CWC
put % =>
} def
/make-table-box { % => W Cols Rows
0 3 1 roll % => W 0(H) Cols Rows
2 copy make-table
[] [] % => W 0(H) Cols Rows [] [](ColSpans) [](RowSpans)
[] % => W 0(H) Cols Rows [] [](ColSpans) [](RowSpans) [](ColumnWidthConstraints)
6 array astore % => W 0(H) [Cols Rows [] [](ColSpans) [](RowSpans)](Content)
0 0 0 0
5 4 roll % => W H 0 0 0 0 Content
0 % => W H 0 0 0 0 Content 0(Baseline)
/show-table-box % => W H 0 0 0 0 Content 0(Baseline) DisplayFun
0 % => W H 0 0 0 0 Content 0(Baseline) DisplayFun 0(DefaultBaseline)
[0 0 0 0]
make-box
{flow-table} 1 index put-flow-fun
% Mark box as table
dup get-box-dict
/Display /table put
} def
/make-default-table-box { % => W
0 0 make-table-box
dup get-box-dict
/Width get
/Auto false put
} def
% PREDICATES
/is-constrained-column { % => WC
0 get /wc-none cvx ne
} def % => Flag
% ROW-RELATED
/table-fit-row { % => R Row Box ColsWidth
% Get total height of current row
3 2 roll % => R Box ColsWidth Row
2 index
% table-row-height accepts 1-based row indices
4 index 1 add
2 index
table-row-height % => R Box ColsWidth Row RH
% Get current Y coordinate (indicated the bottom edge of current row)
dup 2 div
4 index get-current-y
% Calculate middle Y coordinate of this row
exch sub % => R Box ColsWidth Row RH Middle
% Row baseline information
2 index get-row-baseline % => R Box ColsWidth Row RH Middle RBaseline
exch
4 3 roll % => R Box ColsWidth RH RBaseline Middle Row
{ % => R Box ColsWidth RH RBaseline Middle Cell
4 copy
apply-valign % => R Box ColsWidth RH RBaseline Middle Cell
% extend cell height to fit the row vertically
1 index % => R Box ColsWidth RH RBaseline Middle Cell Middle
6 index get-current-y % => R Box ColsWidth RH RBaseline Middle Cell Middle CY
exch sub % => R Box ColsWidth RH RBaseline Middle Cell H/2
2 mul % => R Box ColsWidth RH RBaseline Middle Cell H
dup 2 index
get-full-height % => R Box ColsWidth RH RBaseline Middle Cell H H CellH
gt { % => R Box ColsWidth RH RBaseline Middle Cell H
2 copy exch
put-full-height % => R Box ColsWidth RH RBaseline Middle Cell H
} if
% align the top edge of extended cell with the row top edge
2 div 2 index exch
add % => R Box ColsWidth RH RBaseline Middle Cell TY
1 index put-top
pop
} forall % => R Box ColsWidth RH RBaseline Middle
pop pop pop pop pop pop % =>
} def
/table-row-height { % => Box R Row
1 exch % => Box R 1(C) Row
0 exch % => Box R 1(C) 0(H) Row
{ % => Box R C H Cell
3 index
3 index % => Box R C H Cell R C
6 index % => Box R C H Cell R C Box
table-have-rowspan % => Box R C H Cell Span
1 le {
get-full-height
max % => Box R C H
} {
pop
} ifelse % => Box R C H
exch 1 add exch % => Box R C+1 H
} forall % => Box R C+1 H
4 1 roll
pop pop pop % => H
} def
/in-table-resize-rows { % => Box R OY RHs Rows
dup length 0 gt { % => Box R OY RHs Rows
1 index 0 get % => Box R OY RHs Rows H
1 index 0 get % => Box R OY RHs Rows H Row
5 index % => Box R OY RHs Rows H Row R
7 index % => Box R OY RHs Rows H Row R Box
exch % => Box R OY RHs Rows H Row Box R
3 2 roll % => Box R OY RHs Rows H Box R Row
table-row-height % => Box R OY RHs Rows H OldH
7 1 roll % => OldH Box R OY RHs Rows H
1 index 0 get % => OldH Box R OY RHs Rows H Row
dup get-row-baseline
exch % => OldH Box R OY RHs Rows H RowBaseLine Row
{ % => OldH Box R OY RHs Rows H RowBaseLine Cell
% align top edge of the expanded cell and the top edge of row
% note that table content already have vertical alignment, so it should not
% be affected by this action
dup get-left % => OldH Box R OY RHs Rows H RowBaseLine Cell X
8 index get-top-internal
7 index sub % => OldH Box R OY RHs Rows H RowBaseLine Cell X Y
2 index
move-to-box % => OldH Box R OY RHs Rows H RowBaseLine Cell
% re-try to vertical align the cell contents using new row height
1 index % => OldH Box R OY RHs Rows H RowBaseLine Cell RowBaseline
8 index get-top
7 index sub
4 index 2 div
sub % => OldH Box R OY RHs Rows H RowBaseLine Cell Baseline Middle
2 index % => OldH Box R OY RHs Rows H RowBaseLine Cell Baseline Middle Cell
5 index 4 1 roll % => OldH Box R OY RHs Rows H RowBaseLine Cell H Baseline Middle Cell
apply-valign % => OldH Box R OY RHs Rows H RowBaseLine Cell
% expand cell to the row height
2 index
1 index % => OldH Box R OY RHs Rows H RowBaseLine Cell H Cell
put-full-height % => OldH Box R OY RHs Rows H RowBaseLine Cell
pop
} forall % => OldH Box R OY RHs Rows H RowBaseLine
pop % => OldH Box R OY RHs Rows H
% Calculate new offset from the table top (old offset+current row height)
7 6 roll % => Box R OY RHs Rows H OldH
pop % => Box R OY RHs Rows H
4 3 roll % => Box R RHs Rows H OY
add % => Box R RHs Rows OY'
% remove processed element for row array
3 1 roll % => Box R OY' RHs Rows
array-pop-first % => Box R OY RHs Rows'
% remove processed element for row heights array
4 1 roll
array-pop-first % => Box Rows' R OY RHs'
% increase row index
4 2 roll
1 add % => Box OY RHs' Rows' R+1
% process next row recusively
4 1 roll % => Box R+1 OY RHs' Rows'
in-table-resize-rows
} if
} def
/table-resize-rows { % => Box RHs
dup sum % => Box RHs FH
2 index put-height % => Box RHs
1 exch % => Box 1(R) RHs
0 exch % => Box 1(R) 0(Ofs) RHs
3 index
get-table-content-rows % => Box 1(R) 0(Ofs) RHs Rows
in-table-resize-rows % => Box 1(R) OY [] []
pop pop pop pop pop
} def
/table-rows-heights { % => Box
dup get-table-content-rows % => Box Rows
[] exch % => Box [](Heights) Rows
1 exch % => Box [](Heights) 1(R) Rows
{ % => Box Heights R Row
1 index exch % => Box Heights R R Row
4 index 3 1 roll % => Box Heights R Box R Row
table-row-height % => Box Heights R H
2 index array-prepend % => Box Heights R Heights'
3 1 roll % => Box Heights' Heights R
exch pop % => Box Heights R
1 add % => Box Heights R+1
} forall % => Box Heights R
pop
exch pop % => Heights
} def
% Modify minimal column width using column span information
/get-max-width-table-column { % => Box C Column
1 exch
0 exch { % => Box C 1(R) 0(ColumnMinWidth) ColumnElement
2 index % => Box C 1(R) 0(ColumnMinWidth) ColumnElement R
4 index % => Box C 1(R) 0(ColumnMinWidth) ColumnElement R C
6 index
table-have-colspan 1 gt { % => Box C R Width Element
pop % => Box C R Width
} {
get-max-width % => Box C R Width CWidth
max % => Box C R Width'
} ifelse
exch 1 add exch % => Box C R+1 Width'
} forall % => Box C RL Width'
exch pop % => Box C Width'
2 index get-hor-extra
sub
3 1 roll pop pop % => Width
} def
/get-min-width-table-column { % => Box C Column
1 exch
0 exch { % => Box C 1(R) 0(ColumnMinWidth) ColumnElement
2 index % => Box C 1(R) 0(ColumnMinWidth) ColumnElement R
4 index % => Box C 1(R) 0(ColumnMinWidth) ColumnElement R C
6 index % => Box C 1(R) 0(ColumnMinWidth) ColumnElement R C Box
table-have-colspan 1 gt { % => Box C R Width Element
pop % => Box C R Width
} {
% dup get-td-dict
% /NoWrap get {
% dup get-max-width
% 1 index get-min-width
% max exch pop
% } {
get-min-width % => Box C R Width CWidth
% } ifelse
max % => Box C R Width'
} ifelse
exch 1 add exch % => Box C R+1 Width'
} forall % => Box C RL Width'
exch pop % => Box C Width'
2 index get-hor-extra
sub
3 1 roll pop pop % => Width
} def
/get-table-columns-min-widths { % => Box
dup get-table-content-columns % => Box ColumnsList
1 exch
[] exch % => Box 1(C) [] ColumnsList
{ % => Box C [](Widths) Column
3 index % => Box C Widths Column Box
3 index % => Box C Widths Column Box C
3 2 roll % => Box C Widths Box C Column
get-min-width-table-column % => Box C Widths ColW
exch array-prepend % => Box C Widths
exch 1 add exch % => Box C+1 TotalMinWidth
} forall % => Box C MinWidth
exch pop % => Box MinWidths
exch pop % => MinWidths
} def
/get-table-columns-max-widths { % => Box
dup get-table-content-columns % => Box ColumnsList
1 exch
[] exch % => Box 1(C) [] ColumnsList
{ % => Box C [](Widths) Column
3 index % => Box C Widths Column Box
3 index % => Box C Widths Column Box C
3 2 roll % => Box C Widths Box C Column
get-max-width-table-column % => Box C Widths ColW
exch array-prepend % => Box C Widths
exch 1 add exch % => Box C+1 TotalMinWidth
} forall % => Box C MaxWidth
exch pop % => Box MaxWidths
% Use column width constraints - column should not be wider its constrained width
1 index get-width % => Box MaxWidths BW
2 index table-normalize-cwc % => Box MaxWidths BW CWCs
2 index % => Box MaxWidths BW CWCs MaxWidths
{ 1 index is-fraction
{
6 index false 3 index exec 3 1 roll pop pop
}
{ exch false exch exec } ifelse }
zip-with
exch pop exch pop % => Box MaxWidhts
exch pop % => MinWidths
} def
/table-apply-colspans-minw { % => Flags Widths Fun Box
dup table-get-colspans % => Flags Widths Fun Box Colspans
{ % => Flags Widths Fun Box Colspan[Size R C]
dup 0 get % => Flags Widths Fun Box Colspan[Size R C] Size
1 index 1 get % => Flags Widths Fun Box Colspan[Size R C] Size R
2 index 2 get % => Flags Widths Fun Box Colspan[Size R C] Size R C
% Calculate colspanned cell width (using appropriate function passed in the stack when
% calling this function)
4 index % => Flags Widths Fun Box Colspan[Size R C] Size R C Box
table-get-cell-content % => Flags Widths Fun Box Colspan[Size R C] Size Cell
4 index exec % => Flags Widths Fun Box Colspan[Size R C] Size CellWidth
% apply cell width constraint, if any
2 index aload pop
3 2 roll pop % => Flags Widths Fun Box Colspan[Size R C] Size CellWidth R C
5 index table-get-cwc-raw-at % => Flags Widths Fun Box Colspan[Size R C] Size CellWidth WC
dup false ne {
dup is-fraction {
4 index no-width-constraint not {
4 index get-width exch
false exch exec
max
} {
pop
} ifelse
} {
false exch exec
} ifelse
} {
pop
} ifelse % => Flags Widths Fun Box Colspan[Size R C] Size CellWidth'
% now select the pre-calculated widths of columns covered by this cell
2 index 2 get % => Flags Widths Fun Box Colspan[Size R C] Size CellWidth C
1 sub % => Flags Widths Fun Box Colspan[Size R C] Size CellWidth C(ZeroBased)
3 2 roll % => Flags Widths Fun Box Colspan[Size R C] CellWidth C(ZeroBased) Size
1 index % => Flags Widths Fun Box Colspan[Size R C] CellWidth C Size C
exch % => Flags Widths Fun Box Colspan[Size R C] CellWidth C C Size
7 index % => Flags Widths Fun Box Colspan[Size R C] CellWidth C C Size Widths
3 1 roll % => Flags Widths Fun Box Colspan[Size R C] CellWidth C Widths C Size
getinterval % => Flags Widths Fun Box Colspan[Size R C] CellWidth C SWidths
% select the list of resizable columns covered by this cell
3 2 roll exch % => Flags Widths Fun Box Colspan[Size R C] C CellWidth SWidths
7 index % => Flags Widths Fun Box Colspan[Size R C] C CellWidth SWidths Flags
3 index % => Flags Widths Fun Box Colspan[Size R C] C CellWidth SWidths Flags C
5 index 0 get % => Flags Widths Fun Box Colspan[Size R C] C CellWidth SWidths Flags C Size
getinterval % => Flags Widths Fun Box Colspan[Size R C] C CellWidth SWidths SFlags
% Subtract sum width of non-resizable columns this cell spans over from the cell width
% Non-resizable columns are marked as 'false' in SFlags
2 copy {
{ pop 0 } if
} zip-with sum % => Flags Widths Fun Box Colspan[Size R C] C CellWidth SWidths SFlags Non-resizable-width
4 3 roll
exch sub
% do not allow target width drop below zero
0 max
3 1 roll
% Expand resizable columns to
expand-to-with-flags % => Flags Widths Fun Box Colspan[Size R C] C SWidths'
% store modified widths
5 index % => Flags Widths Fun Box Colspan[Size R C] C SWidths' Widths
3 1 roll % => Flags Widths Fun Box Colspan[Size R C] Widths C SWidths'
putinterval % => Flags Widths' Fun Box Colspan[Size R C]
pop % => Flags Widths' Fun Box
} forall % => Flags Widths' Fun Box
pop pop % => Widths'
exch pop
} def
/table-apply-colspans { % => Widths Fun Box
dup table-get-colspans % => Widths Fun Box Colspans
% Scan all cell spanning several columns
{ % => Widths Fun Box Colspan[Size R C]
% Get current colspan contents
dup 0 get % => Widths Fun Box Colspan[Size R C] Size
1 index 1 get % => Widths Fun Box Colspan[Size R C] Size R
2 index 2 get % => Widths Fun Box Colspan[Size R C] Size R C
4 index % => Widths Fun Box Colspan[Size R C] Size R C Box
table-get-cell-content % => Widths Fun Box Colspan[Size R C] Size Cell
% Calculate its width (by calling the appropriate function passed; usually it will be
% get-min-width or get-max-width)
4 index exec % => Widths Fun Box Colspan[Size R C] Size CellWidth
% apply cell width constraint, if any
2 index aload pop
3 2 roll pop % => Flags Widths Fun Box Colspan[Size R C] Size CellWidth R C
5 index table-get-cwc-raw-at % => Flags Widths Fun Box Colspan[Size R C] Size CellWidth WC
dup false ne {
dup is-fraction {
4 index no-width-constraint not {
4 index get-width exch
false exch exec
max
} {
pop
} ifelse
} {
false exch exec
} ifelse
} {
pop
} ifelse % => Flags Widths Fun Box Colspan[Size R C] Size CellWidth'
% Get the width list of columns covered by current column span
2 index 2 get % => Widths Fun Box Colspan[Size R C] Size CellWidth C
1 sub % => Widths Fun Box Colspan[Size R C] Size CellWidth C(ZeroBased)
3 2 roll % => Widths Fun Box Colspan[Size R C] CellWidth C(ZeroBased) Size
1 index % => Widths Fun Box Colspan[Size R C] CellWidth C Size C
exch % => Widths Fun Box Colspan[Size R C] CellWidth C C Size
7 index % => Widths Fun Box Colspan[Size R C] CellWidth C C Size Widths
3 1 roll % => Widths Fun Box Colspan[Size R C] CellWidth C Widths C Size
getinterval % => Widths Fun Box Colspan[Size R C] CellWidth C SWidths
% expand these columns to fit spanned data
3 2 roll exch % => Widths Fun Box Colspan[Size R C] C CellWidth SWidths
expand-to % => Widths Fun Box Colspan[Size R C] C SWidths'
% Store changed widths
5 index % => Widths Fun Box Colspan[Size R C] C SWidths' Widths
3 1 roll % => Widths Fun Box Colspan[Size R C] Widths C SWidths'
putinterval % => Widths' Fun Box Colspan[Size R C]
pop % => Widths' Fun Box
} forall % => Widths' Fun Box
pop pop % => Widths'
} def
/table-column-widths { % => Box
dup get-width % => Box Width
table-columns-fit
} def
/table-columns-fit { % => Box Widht
% Check if there's any columns in table
1 index get-table-content-columns
length 0 gt {
in-table-columns-fit
} {
pop pop []
} ifelse
} def
% columns-fit helper function
/fit-cwidth { % => MW MCW TW-TCW TMCW-TMW
2 index
4 index
sub % => MW MCW TW-TCW TMCW-TMW MCW-MW
exch div % => MW MCW TW-TCW MCW-MW/TMCW-TMW
mul
add
% to avoid problems with negative cell widths
0 max
exch pop
} def
/in-table-columns-fit { % => Box Width
1 index
get-table-content-columns % => Box Width Columns
2 index % => Box Width Columns Box
get-table-columns-min-widths % => Box Width Columns MinWidths
3 index
get-table-columns-max-widths % => Box Width Columns MinWidths MaxWidths
1 index
{ max }
zip-with
% Use data on the spanned cells
% exch % => Box Width Columns MaxWidths MinWidths
% 2 copy { ne } zip-with % => Box Width Columns MaxWidths MaxWidths [MaxWidthsCi<>MinWidthsCi]
% exch % => Box Width Columns MaxWidths ResizableFlags MinWidths
% { get-min-width } 6 index % => Box Width Columns ManWidths ResizableFlags MinWidths Fun Box
% table-apply-colspans-minw % => Box Width Columns MaxWidths MinWidth
% exch % => Box Width Columns MinWidth MaxWidths
% { get-max-width } 5 index % => Box Width Columns MinWidth MaxWidths Fun Box
% table-apply-colspans % => Box Width Columns MinWidth MaxWidths
3 2 roll
pop % => Box Width MinWidths MaxWidths
% Apply column width constraints to minimum widths
2 index % => Box Width MinWidths MaxWidths BW
4 index table-normalize-cwc % => Box Width MinWidths MaxWidths BW CWCs
3 index % => Box Width MinWidths MaxWidths BW CWCs MinWidths
{ 1 index is-fraction
{
6 index false 3 index exec 3 1 roll pop pop
}
{ exch false exch exec } ifelse }
zip-with
exch pop
% Apply column width constraints to maximum widths
3 index % => Box Width MinWidths MaxWidths MinWidthsC BW
5 index table-normalize-cwc % => Box Width MinWidths MaxWidths MinWidthsC BW CWCs
3 index % => Box Width MinWidths MaxWidths MinWidthsC BW CWCs maxWidth
{ 1 index is-fraction
{
% 6 index = BW
6 index false 3 index exec 3 1 roll pop pop
}
{ exch false exch exec } ifelse }
zip-with
exch pop % => ox Width MinWidths MaxWidths MinWidthsC MaxWidths
% Do not allow constrained max width be less than min width
3 index
{ max }
zip-with % => Box Width MinWidths MaxWidths MinWidthsC MaxWidthsC
% Do not allow constrained min width be less than min width
3 index
2 index
{ max }
zip-with % => Box Width MinWidths MaxWidths MinWidthsC MaxWidthsC W'
3 2 roll % => Box Width MinWidths MaxWidths MaxWidthsC W' MinWidthsC
pop % => Box Width MinWidths MaxWidths MaxWidthsC W'
exch % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC
% Check if sum of constrained widths is too big
% Note that we compare sum of constrained width with the MAXIMAL value of table width and
% sum of uncostrained minimal width; it will prevent from unneeded collapsing of table cells
% if table content will expand its width anyway
1 index sum % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC SUM(MinWidthsC')
% find the "template" value
5 index
5 index sum
max
% compare
gt {
% now we should scale columns to fit table width
1 index sum
% find template value
5 index
5 index sum
max
% Calculate the amount if difference between minimal and constrained minimal width for each columns
exch sub % => Box Width MinWidths MaxWidths MinWidthsC MaxWidthsC Width-MinWC
4 index % => Box Width MinWidths MaxWidths MinWidthsC MaxWidthsC Width-MinWC MinWidth
3 index % => Box Width MinWidths MaxWidths MinWidthsC MaxWidthsC Width-MinWC MinWidth MinWidthC
{exch sub} zip-with sum % => Box Width MinWidths MaxWidths MinWidthsC MaxWidthsC Width-MinWC CWDelta
dup 0 gt {
5 index % => Box Width MinWidths MaxWidths MinWidthsC MaxWidthsC Width-MinWC CWDelta MinW
4 index % => Box Width MinWidths MaxWidths MinWidthsC MaxWidthsC Width-MinWC CWDelta MinW MinWC
4 2 roll % => Box Width MinWidths MaxWidths MinWidthsC MaxWidthsC MinW MinWC Width-MinWC CWDelta
/fit-cwidth cvx % => Box Width MinWidths MaxWidths MinWidthsC MaxWidthsC MinW MinWC Width-MinWC CWDelta cit-cwidth
3 array astore cvx % => Box Width MinWidths MaxWidths MinWidthsC MaxWidthsC MinW MinWC {Width-MinWC CWDelta cit-cwidth}
zip-with % => Box Width MinWidths MaxWidths MinWidthsC MaxWidthsC MinWC'
exch 3 2 roll pop % => Box Width MinWidths MaxWidths MinWC' MaxWidthsC
} {
% if no difference is found, we can collapse no columns
pop pop % => Box Width MinWidths MaxWidths MinWidthsC MaxWidthsC
} ifelse
} if
% Use data on the spanned cells (again)
exch % => Box Width MinWidths MaxWidths MaxWidthsC MinWidthsC
2 copy { ne } zip-with % => Box Width MinWidths MaxWidths MaxWidthsC MinWidthsC [MaxWidthsCi<>MinWidthsCi]
exch % => Box Width MinWidths MaxWidths MaxWidthsC ResizableFlags MinWidthsC
{ get-min-width } 7 index % => Box Width MinWidths MaxWidths MaxWidthsC ResizableFlags MinWidthsC Fun Box
table-apply-colspans-minw % => Box Width MinWidths MaxWidths MaxWidthsC MinWidthC
exch % => Box Width MinWidths MaxWidths MinWidthC MaxWidthsC
{ get-max-width } 6 index % => Box Width MinWidths MaxWidths MinWidthC MaxWidthsC Fun Box
table-apply-colspans % => Box Width MinWidths MaxWidths MinWidthC MaxWidthsC
% Calculate actual widths
% Prepare width array
[]
1 index length 0
array-extend % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths
% First pass - calculate widths for all constrained columns
6 index table-normalize-cwc % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths CWCs
0 1 2 index length 1 sub { % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths CWCs I
2 copy get
is-constrained-column {
2 index
1 index
6 index 3 index get
put
} if
pop
} for
% Quick fix for overconstrained tables: if table have width attribute AND its value is less than sum
% of constrained columns widths plus minimal widths of uncostrained columns, then we'll expand the width of table
% to fit all columns
% 1. calculate sum of constrained column widths
1 index sum % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths CWCs SumConW
% 2. calculate sum of unconstrained column minimal widths
2 index
7 index
{ 1 index 0 gt { pop pop 0 } { exch pop } ifelse }
zip-with sum % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths CWCs SumConW SumUnconW
% 3. compare these widths with the table width
add
dup 8 index gt { % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths CWCs SumConW+SumUnconW
8 7 roll pop
7 1 roll
} {
pop
} ifelse
% Second pass - distribute the rest of the width
% Explanation of the stuff below (I've really had problems with this small piece of code, especially
% when I was trying to fix "bugs" inside it)
%
% First of all, no column can be narrower than it minimal width (determined by its content)
% Note that constrained columns have their widths distributed above, so we can exclude them for now
% (just throw them out and imagine that table does not contain any width-constrained cols)
%
% Second, the relative widths of columns will have _appoximately_ the same ratio as
% their maximal content widths. (In exception of cases where the first rule will take place -
% say for the table containing two columns with the VERY long text in the first and one or two words
% in the second)
%
% In general, this approach can be inoptimal in case of _very_ different font sizes
% inside the cells, of, say big images; nevertheless, it will give a good approximate
% AND still fast enough (unlike fully correct methods involving evaluation of the content height of the cell)
%
% Thus, we do the following:
% - calculate the ratio of current column MAXIMAL ($current_max) width to the sum of MAXIMAL widths of all columns left
% (inluding current) second rule applied. Note that we need remember about column spans and select
% maxw or maxwc in order.
% - then check if the rest of width will be too small for other columns to fit and decrease current columns
% width (see MIN function call)
% - then check again if our width will be too small for current column to fit (and expand if nesessary) -
% MAX function call
0 1 2 index length 1 sub { % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths CWCs I
2 copy get
% Process only uncostrained columns
is-constrained-column not {
2 index
1 index % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths CWCs I Widths I
% Get undistibuted width (total table width - width of constrained columns)
9 index % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths CWCs I Widths I Width
2 index sum sub % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths CWCs I Widths I WidthRest
% Get max width of column being processed
8 index 2 index get % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths CWCs I Widths I WidthRest MaxW
7 index 3 index get max % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths CWCs I Widths I WidthRest MaxW
% If width is equal to zero, use max constrained width, as this column could be covered by colspan;
% If not, we lose nothing, because all constrained columns are already processed earlier, and no more
% columns except these two types can have different constrained and raw widths
dup 0 eq {
pop
6 index 2 index get
} if
% Get sum of maximal constrained widths of unplaced columns
3 index % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths CWCs I Widths I WidthRest MaxW Widths
8 index % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths CWCs I Widths I WidthRest MaxW Widths MaxWidthsC
{ 1 index 0 eq {exch pop} {pop pop 0} ifelse }
zip-with
sum % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths CWCs I Widths I WidthRest MW MWR
% Get sum of minimal constrained widths of unplaced columns
4 index % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths CWCs I Widths I WidthRest MaxW MaxWR Widths
10 index % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths CWCs I Widths I WidthRest MaxW MaxWR Widths MaxWidthsC
{ 1 index 0 eq {exch pop} {pop pop 0} ifelse }
zip-with
sum % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths CWCs I Widths I WidthRest MaxW MaxWR MinWR
3 index exch sub % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths CWCs I Widths I WidthRest MaxW MaxWR WidthRest-MinWR
% add current columns min width
12 index 5 index get
11 index 6 index get max
add
4 1 roll % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths CWCs I Widths I WidthRest-MinWR WidthRest MaxW MaxWR
% If some unplaced columns have maximal (constrained width) greater zero
dup 0 gt {
% Calculate the appropriate fraction of free table width for the current column
div % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths CWCs I Widths I WidthRest-MinWR WidthRest MinWE MaxW MaxWR
mul % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths CWCs I Widths I WidthRest-MinWR W(WidthRest*MW/MWR)
min
% Remove minimal width of current column;
% 9 index 2 index get
% sub
% This will make calculated width not less than raw minimal column with. See also line marked with (*)
% 0 max
} {
pop pop pop pop 0
} ifelse
% (*) Add the minimal width of current column
9 index 2 index get
dup 0 eq {
pop
7 index 2 index get
} if
max % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths CWCs I Widths I W
put
} if
pop
} for
pop % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths
% in case of overconstrained table (e.g. two columns with 20% widths), expand them
dup sum % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths SumWidth
dup 0 gt {
6 index
exch div
dup 1 gt {
map-scale
} { pop } ifelse
} {
pop
} ifelse % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths
% now - the last attempt; if total width is less than box width, just expand the very first column to fit
dup sum 6 index lt {
dup sum % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths SumWidth
6 index
exch sub % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths Delta
1 index 0 get % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths Delta W0
add % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths W0'
1 index exch
0 exch put % => Box Width MinWidths MaxWidths MinWidthsC' MaxWidthsC Widths
} if
7 1 roll
pop pop pop
pop pop pop
} def
% Modify heights of table cells spanning several rows
/table-fit-rowspans { % => Table
dup table-rows-heights % => Table RHs
1 index table-get-rowspans % => Table RHs RS
% scan all cells spanning several rows
{ % => Table RHs RowSpan
% calculate the bottom edge of current cell
aload pop % => Box RHs Span R C
2 copy % => Box RHs Span R C R C
6 index % => Box RHs Span R C R C Box
table-get-cell-content % => Box RHs Span R C Cell
{
get-bottom
min
}
1 index get-top
2 index get-content
reduce
% reset the height of the cell to its content height (as it have been probably fitted to the top row spanned-over)
1 index get-top
sub neg
1 index put-height
% now check if cell height is less than sum spanned rows height
dup get-full-height % => Box RHs Span R C Cell CellFH
5 index % => Box RHs Span R C Cell CellFH RHs
4 index 1 sub % => Box RHs Span R C Cell CellFH RHs R
6 index % => Box RHs Span R C Cell CellFH RHs R Span
getinterval sum % => Box RHs Span R C Cell CellFH RowH
2 copy lt {
% vertical-align current cell
% calculate (approximate) row baseline
6 index % => Box RHs Span R C Cell CellFH RowH RHs
0 6 index 1 sub % => Box RHs Span R C Cell CellFH RowH RHs 0 R(ZeroBased)
getinterval sum neg % => Box RHs Span R C Cell CellFH RowH -SpannedRHs
8 index get-top-internal
add % => Box RHs Span R C Cell CellFH RowH RBaseline
% calculate row middle coordinate
dup 2 index 2 div sub % => Box RHs Span R C Cell CellFH RowH RBaseline Middle
2 index
2 index
2 index
7 index
apply-valign
pop pop
% Make cell fill all available vertical space
dup % => Box RHs Span R C Cell CellFH RowH RowH
3 index
put-full-height % => Box RHs Span R C Cell CellFH RowH
pop pop pop
} {
pop pop pop
} ifelse % => Box RHs Span R C
pop pop pop
} forall
pop pop
} def
/table-have-colspan { % => Row Col Table
table-get-colspans
1 % => Row Col Spans 1
exch % => Row Col 1 Spans
{ % => Row Col CS Span
dup 1 get % => Row Col CS Span Row
1 index 2 get % => Row Col CS Span Row Col
4 index eq % => Row Col CS Span Row CEq
exch % => Row Col CS Span CEq Row
5 index eq % => Row Col CS Span CEq REq
and % => Row Col CS Span Match
{
0 get exch pop
} {
pop
} ifelse
} forall % => Row Col CS
3 1 roll % => CS Row Col
pop pop % => CS
} def
/table-have-rowspan { % => Row Col Table
table-get-rowspans
1 % => Row Col Spans 1
exch % => Row Col 1 Spans
{ % => Row Col CS Span
dup 1 get % => Row Col CS Span Row
1 index 2 get % => Row Col CS Span Row Col
4 index eq % => Row Col CS Span Row CEq
exch % => Row Col CS Span CEq Row
5 index eq % => Row Col CS Span CEq REq
and % => Row Col CS Span Match
{
0 get exch pop
} {
pop
} ifelse
} forall % => Row Col CS
3 1 roll % => CS Row Col
pop pop % => CS
} def
/table-mark-rowspan { % => Table SpanSize
1 index
get-table-content-rows % => Table SpanSize Rows
dup length % => Table SpanSize Rows CurRow
1 index array-last % => Table SpanSize Rows CurRow LastRow
length % => Table SpanSize Rows CurRow CurColumn
3 2 roll pop % => Table SpanSize CurRow CurColumn
3 index % => Table SpanSize CurRow CurColumn Table
table-add-rowspan % => Table
} def
/table-mark-colspan { % => Table SpanSize
1 index
get-table-content-rows % => Table SpanSize Rows
dup length % => Table SpanSize Rows CurRow
1 index array-last % => Table SpanSize Rows CurRow LastRow
length % => Table SpanSize Rows CurRow CurColumn
3 2 roll pop % => Table SpanSize CurRow CurColumn
3 index % => Table SpanSize CurRow CurColumn Table
table-add-colspan % => Table
} def
/normalize-row { % => Length Row
aload length % => Length R1 .. RN N
dup % => Length R1 .. RN N N
dup 2 add % => Length R1 .. RN N N N+2
index % => Length R1 .. RN N N Length
exch sub dup % => Length R1 .. RN N Delta Delta
replicate-row % => Length R1 .. RN N []..[] Delta 0
pop % => Length R1 .. RN N []..[] Delta
dup 2 add % => Length R1 .. RN N []..[] Delta Delta+2
dup 1 sub % => Length R1 .. RN N []..[] Delta Delta+2 Delta+1
roll % => Length R1 .. RN []..[] Delta N
add % => Length R1 .. RN []..[] Delta+N
array astore % => Length Row'
exch pop % => Row'
} def
/table-slide-cw { % => R C Box
dup table-get-cwc-raw % => R C Box RawCWCS
{ % => R C Box RawCWC
aload % => R C Box !C !R WC [!C !R WC]
2 index 7 index eq {
3 index 6 index ge {
dup
0 5 index 1 add put % => R C Box !C !R WC [!C+1 !R WC]
} if
} if % => R C Box !C !R WC [!C !R WC]
pop pop pop pop
} forall
pop pop pop
} def
/do-slide-rs { % => ... RS' [SS SR SC] R C
2 index 1 get % => ... RS' [SS SR SC] R C SR
2 index eq { % => ... RS' [SS SR SC] R C
2 index 2 get % => ... RS' [SS SR SC] R C SC
1 index ge { % => ... RS' [SS SR SC] R C
2 index 2 get % => ... RS' [SS SR SC] R C SC
1 add % => ... RS' [SS SR SC] R C SC+1
3 index exch
2 exch % => ... RS' [SS SR SC] R C [..] 2 SC+1
put % => ... RS' [SS SR SC+1] R C
% FIXME: reorder rowspans after this operation?
} if
} if
pop pop
exch array-prepend
} def
/table-slide-rs { % => R C Box
2 index 2 index /do-slide-rs % => R C Box {fun}
cvx 3 array astore cvx
[]
2 index table-get-rowspans
reduce
1 index table-put-rowspans % => R C Box
pop pop pop % =>
} def
/do-slide-cs { % => ... RS' [SS SR SC] R C
2 index 1 get % => ... RS' [SS SR SC] R C SR
2 index eq { % => ... RS' [SS SR SC] R C
2 index 2 get % => ... RS' [SS SR SC] R C SC
1 index ge { % => ... RS' [SS SR SC] R C
2 index 2 get % => ... RS' [SS SR SC] R C SC
1 add % => ... RS' [SS SR SC] R C SC+1
3 index exch
2 exch % => ... RS' [SS SR SC] R C [..] 2 SC+1
put % => ... RS' [SS SR SC+1] R C
% FIXME: reorder rowspans after this operation?
} if
} if
pop pop
exch array-prepend
} def
/table-slide-cs { % => R C Box
2 index 2 index /do-slide-cs % => R C Box {fun}
cvx 3 array astore cvx
[]
2 index table-get-colspans
reduce
1 index table-put-colspans % => R C Box
pop pop pop % =>
} def
/table-fake-cell { % => R C Box
3 copy
table-slide-cw % => R C Box
3 copy
table-slide-rs % => R C Box
3 copy
table-slide-cs % => R C Box
dup
get-table-content-rows % => R C Box Rows
dup % => R C Box Rows Rows
4 index 1 sub % => R C Box Rows Rows R(0)
dup
2 index length 1 sub gt {
pop pop pop pop pop pop
} {
get % => R C Box Rows Row
3 index 1 sub exch % => R C Box Rows C(ZeroBased) Row
box-block-create % => R C Box Rows C Row FBox
{show-fake-block-box}
1 index put-display
exch % => R C Box Rows C FBox Row
dup length 3 index lt {
3 1 roll
array-extend
} {
array-insert % => R C Box Rows Row'
} ifelse
dup length % => R C Box Rows Row' RL'
3 index get-content % => R C Box Rows Row' RL' [C R Content]
exch 0 exch % => R C Box Rows Row' [C R Content] 1 RL'
2 index 0 get max
put
4 index 1 sub % => R C Box Rows Row' R(ZeroBased)
exch % => R C Box Rows R(ZeroBased) Row'
put % => R C Box
pop pop pop % =>
} ifelse
} def
/in-normalize-rowspans { % => Size Row Col Box
3 index 1 gt {
2 index 1 add % => Size Row Col Box Row+1
2 index % => Size Row Col Box Row+1 Col
2 index
table-fake-cell % => Size Row Col Box
4 2 roll % => Col Box Size Row
1 add % => Col Box Size Row+1
4 1 roll % => Row+1 Col Box Size
1 sub % => Row+1 Col Box Size-1
4 1 roll % => Size-1 Row+1 Col Box
in-normalize-rowspans % =>
} {
pop pop pop pop % =>
} ifelse
} def
/normalize-rowspans { % => Box
dup table-get-rowspans % => Box Rowspans
{ % => Box Rowspan
aload pop % => Box Size Row Col
3 index % => Box Size Row Col Box
in-normalize-rowspans % => Box
} forall % => Box
dup get-table-content-rows % => Box Rows
length % => Box RN
1 index table-get-rowspans % => Box RN Rowspans
{ % => Box RN Rowspan
dup 1 get % => Box RN Rowspan RSR
dup % => Box RN Rowspan RSR RSR
2 index 0 get % => Box RN Rowspan RSR RSR RSS
add 1 sub % => Box RN Rowspan RSR RSR+RSS-1
3 index min % => Box RN RowSpan RSR SpanEnd
exch sub 1 add % => Box RN RowSpan SpanSize
0 exch put % => Box RN
} forall % => Box RN
pop pop % =>
} def
/normalize-table { % => Box
dup normalize-rowspans
dup get-content % => Box Content
get-children-from-content % => Box Rows
dup dup % => Box Rows Rows Rows
0 exch % => Box Rows Rows 0 Rows
{ % => Box Rows Rows 0 Row
length
max
} forall % => Box Rows Rows MaxRowLength
0 exch % => Box Rows Rows 0 MaxRowLength
3 2 roll % => Box Rows 0 MaxRowLength Rows
{ % => Box Rows RowNo MaxRowLength Row
1 index exch % => Box Rows RowNo MaxRowLength MaxRowLength Row
normalize-row % => Box Rows RowNo MaxRowLength Row'
3 index 3 index % => Box Rows RowNo MaxRowLength Row' Rows RowNo
3 2 roll % => Box Rows RowNo MaxRowLength Rows RowNo Row'
put % => Box Rows RowNo MaxRowLength
exch 1 add exch % => Box Rows RowNo+1 MaxRowLength
} forall
pop pop pop
pop
} def
/in-make-table-row { % => RowData Size
dup 0 gt {
1 index % => RowData Size RowData
aload % => RowData Size Data1 ... DataN Arr
length % => RowData Size Data1 ... DataN N
1 add % => RowData Size Data1 ... DataN N+1
[] exch % => RowData Size Data1 ... DataN [] N+1
array astore % => RowData Size NewRowData
3 2 roll pop % => Size NewRowData
exch % => NewRowData Size
1 sub % => NewRowData Size-1
in-make-table-row
} {
pop
} ifelse
} def
/make-table-row { % => Size
[] exch % => [](EmptyRow) Size
in-make-table-row
} def
/in-make-table { % => TableData Cols Rows
dup 0 gt {
2 index % => TableData Cols Rows TableData
aload % => TableData Cols Rows Data1 ... DataN TableData
length % => TableData Cols Rows Data1 ... DataN N
1 add % => TableData Cols Rows Data1 ... DataN N+1
dup 1 add index
make-table-row
exch % => TableData Cols Rows Data1 ... DataN NewRow N+1
array astore % => TableData Cols Rows NewTableData
4 3 roll pop % => Cols Rows NewTableData
3 1 roll % => NewTableData Cols Rows
1 sub % => NewTableData Cols Rows-1
in-make-table
} {
pop pop
} ifelse
} def
/make-table { % => Cols Rows
[] 3 1 roll
in-make-table
} def
/in-add-table-cell { % => Box Cell
1 index get-content % => Box Cell [Cols Rows Content](RawContent)
2 get % => Box Cell Rows
dup length 1 sub % => Box Cell Rows LastIndex
dup
2 index exch get % => Box Cell Rows LastIndex LastRow
aload length % => Box Cell Rows LastIndex Cell1 .. CellN N
dup 3 add % => Box Cell Rows LastIndex Cell1 .. CellN N N+4
index exch % => Box Cell Rows LastIndex Cell1 .. CellN Cell N
1 add
array astore % => Box Cell Rows LastIndex NewRow
dup length % => Box Cell Rows LastIndex NewRow NewRowLength
5 1 roll % => Box NewRowLength Cell Rows LastIndex NewRow
put
pop % => Box NewRowLength
1 index get-content
dup 0 get % => Box NewRowLength Content Cols
3 2 roll % => Box Content Cols NewRowLength
max
0 exch put % => Box
} def
/add-table-cell { % => Table Child
1 index is-table {
% setup parent reference in the child box
dup get-box-dict
/Parent 3 index put
% set display property
dup get-box-dict
/Display /table-cell put
% Check if any rows have been added to the table
% Add one if no rows still exists
1 index get-table-content-rows % => Rows
length 0 eq {
1 index add-table-row pop
} if
2 copy
in-add-table-cell % => Table Child Table
pop % => Table Child
% If table-cell had width constaint, propagate it to the column
dup no-width-constraint not {
dup get-width-constraint
2 index put-table-column-width-constraint
{wc-none} 1 index put-width-constraint
} if
pop
} {
% if parent is not a table, just ignore this box
pop
} ifelse
} def