% $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