| 1 |
lars |
1 |
<?php
|
|
|
2 |
|
|
|
3 |
class StrategyTableLayoutAuto {
|
|
|
4 |
function StrategyTableLayoutAuto() {
|
|
|
5 |
}
|
|
|
6 |
|
|
|
7 |
function apply($table, &$context) {
|
|
|
8 |
$width = $table->get_width();
|
|
|
9 |
return $this->table_columns_fit($table, $width, $context);
|
|
|
10 |
}
|
|
|
11 |
|
|
|
12 |
function use_colspans(&$table, $widths, &$context, $width_fun, $minwc, $maxwc) {
|
|
|
13 |
$colspans = $table->get_colspans();
|
|
|
14 |
|
|
|
15 |
foreach ($colspans as $colspan) {
|
|
|
16 |
$cell = $table->content[$colspan->row]->content[$colspan->column];
|
|
|
17 |
|
|
|
18 |
// apply colspans to the corresponsing colspanned-cell dimension
|
|
|
19 |
//
|
|
|
20 |
$cell_width = $cell->$width_fun($context);
|
|
|
21 |
|
|
|
22 |
// now select the pre-calculated widths of columns covered by this cell
|
|
|
23 |
// select the list of resizable columns covered by this cell
|
|
|
24 |
$spanned_widths = array();
|
|
|
25 |
$spanned_resizable = array();
|
|
|
26 |
|
|
|
27 |
for ($i=$colspan->column; $i < $colspan->column+$colspan->size; $i++) {
|
|
|
28 |
$spanned_widths[] = $widths[$i];
|
|
|
29 |
$spanned_resizable[] = ($minwc[$i] != $maxwc[$i]);
|
|
|
30 |
}
|
|
|
31 |
|
|
|
32 |
// Sometimes we may encounter the colspan over the empty columns (I mean ALL columns are empty); in this case
|
|
|
33 |
// we need to make these columns reizable in order to fit colspanned cell contents
|
|
|
34 |
//
|
|
|
35 |
if (array_sum($spanned_widths) == 0) {
|
|
|
36 |
for ($i=0; $i<count($spanned_widths); $i++) {
|
|
|
37 |
$spanned_widths[$i] = EPSILON;
|
|
|
38 |
$spanned_resizable[$i] = true;
|
|
|
39 |
};
|
|
|
40 |
};
|
|
|
41 |
|
|
|
42 |
// The same problem may arise when all colspanned columns are not resizable; in this case we'll force all
|
|
|
43 |
// of them to be resized
|
|
|
44 |
$any_resizable = false;
|
|
|
45 |
for ($i=0; $i<count($spanned_widths); $i++) {
|
|
|
46 |
$any_resizable |= $spanned_resizable[$i];
|
|
|
47 |
};
|
|
|
48 |
if (!$any_resizable) {
|
|
|
49 |
for ($i=0; $i<count($spanned_widths); $i++) {
|
|
|
50 |
$spanned_resizable[$i] = true;
|
|
|
51 |
};
|
|
|
52 |
}
|
|
|
53 |
|
|
|
54 |
// Expand resizable columns
|
|
|
55 |
//
|
|
|
56 |
$spanned_widths = expand_to_with_flags($cell_width,$spanned_widths,$spanned_resizable);
|
|
|
57 |
|
|
|
58 |
// Store modified widths
|
|
|
59 |
array_splice($widths, $colspan->column, $colspan->size, $spanned_widths);
|
|
|
60 |
};
|
|
|
61 |
|
|
|
62 |
return $widths;
|
|
|
63 |
}
|
|
|
64 |
|
|
|
65 |
/**
|
|
|
66 |
* Fit table columns to the given width
|
|
|
67 |
*/
|
|
|
68 |
function table_columns_fit(&$table, $width, &$context) {
|
|
|
69 |
$minw = $table->get_table_columns_min_widths($context);
|
|
|
70 |
$maxw = $table->get_table_columns_max_widths($context);
|
|
|
71 |
|
|
|
72 |
$minw = $this->use_colspans($table, $minw, $context, 'get_min_width', $minw, $maxw);
|
|
|
73 |
$maxw = $this->use_colspans($table, $maxw, $context, 'get_max_width', $minw, $maxw);
|
|
|
74 |
|
|
|
75 |
// Store number of columns
|
|
|
76 |
$columns = count($minw);
|
|
|
77 |
|
|
|
78 |
// Apply column width constraints
|
|
|
79 |
$minwc = array();
|
|
|
80 |
$maxwc = array();
|
|
|
81 |
|
|
|
82 |
$cellpadding = $table->get_css_property(CSS_HTML2PS_CELLPADDING);
|
|
|
83 |
$cellspacing = $table->get_css_property(CSS_HTML2PS_CELLSPACING);
|
|
|
84 |
|
|
|
85 |
for ($i=0; $i<count($minw); $i++) {
|
|
|
86 |
$cwc = $table->get_cwc($i);
|
|
|
87 |
|
|
|
88 |
// Do not allow constrained max width be less than min width
|
|
|
89 |
// Do not allow constrained min width be less than min width
|
|
|
90 |
//
|
|
|
91 |
$table_width = $table->get_width();
|
|
|
92 |
|
|
|
93 |
$extra = 2*$cellpadding->getPoints() + $cellspacing->getPoints();
|
|
|
94 |
|
|
|
95 |
$minwc[$i] = max($minw[$i], $cwc->apply($minw[$i]-$extra, $table_width) + $extra);
|
|
|
96 |
$maxwc[$i] = max($minw[$i], $cwc->apply($maxw[$i]-$extra, $table_width) + $extra);
|
|
|
97 |
};
|
|
|
98 |
|
|
|
99 |
$minwc = $table->normalize_min_widths($width, $minw, $minwc);
|
|
|
100 |
$minwc = $table->_table_apply_colspans($minwc, $context, 'get_min_width', $minwc, $maxwc);
|
|
|
101 |
|
|
|
102 |
// We need to normalize widths for the case of colspans width is too big; for example:
|
|
|
103 |
// <table><tr><td width="100">
|
|
|
104 |
// <table><tr><td width="150">TEXT<td>TEXT<tr><td colspan="2" width="200">
|
|
|
105 |
// in this case table SHOULD NOT be expanded over the 100px!
|
|
|
106 |
//
|
|
|
107 |
// $minwc = $table->normalize_min_widths($width, $minw, $minwc);
|
|
|
108 |
$maxwc = $table->_table_apply_colspans($maxwc, $context, 'get_max_width', $minwc, $maxwc);
|
|
|
109 |
|
|
|
110 |
// Calculate actual widths
|
|
|
111 |
$widths = array();
|
|
|
112 |
// Calculate widths for all constrained columns
|
|
|
113 |
for ($i=0; $i < $columns; $i++) {
|
|
|
114 |
if ($table->is_constrained_column($i)) {
|
|
|
115 |
$widths[$i] = $minwc[$i];
|
|
|
116 |
}
|
|
|
117 |
}
|
|
|
118 |
|
|
|
119 |
// Quick fix for overconstrained tables: if table have width attribute AND its value is less than sum
|
|
|
120 |
// of constrained columns widths plus minimal widths of uncostrained columns, then we'll expand the width of table
|
|
|
121 |
// to fit all columns
|
|
|
122 |
// 1. calculate sum of constrained column widths
|
|
|
123 |
// 2. calculate sum of unconstrained column minimal widths
|
|
|
124 |
$sum_cw = 0;
|
|
|
125 |
$sum_ucw = 0;
|
|
|
126 |
for ($i=0; $i < $columns; $i++) {
|
|
|
127 |
if ($table->is_constrained_column($i)) {
|
|
|
128 |
$sum_cw += $widths[$i];
|
|
|
129 |
} else {
|
|
|
130 |
$sum_ucw += $minwc[$i];
|
|
|
131 |
}
|
|
|
132 |
}
|
|
|
133 |
|
|
|
134 |
// 3. compare these widths with the table width and choose the maximal value
|
|
|
135 |
$width = max($width, $sum_cw + $sum_ucw);
|
|
|
136 |
|
|
|
137 |
// Second pass - disctribute the rest of the width
|
|
|
138 |
|
|
|
139 |
// Explanation of the stuff below (I've really had problems with this small piece of code, especially
|
|
|
140 |
// when I was trying to fix "bugs" inside it)
|
|
|
141 |
//
|
|
|
142 |
// First of all, no column can be narrower than it minimal width (determined by its content)
|
|
|
143 |
// Note that constrained columns have their widths distributed above, so we can exclude them for now
|
|
|
144 |
// (just throw them out and imagine that table does not contain any width-constrained cols)
|
|
|
145 |
//
|
|
|
146 |
// Second, the relative widths of columns will have _appoximately_ the same ratio as
|
|
|
147 |
// their maximal content widths. (In exception of cases where the first rule will take place -
|
|
|
148 |
// say for the table containing two columns with the VERY long text in the first and one or two words
|
|
|
149 |
// in the second)
|
|
|
150 |
//
|
|
|
151 |
// In general, this approach can be inoptimal in case of _very_ different font sizes
|
|
|
152 |
// inside the cells, of, say big images; nevertheless, it will give a good approximate
|
|
|
153 |
// AND still fast enough (unlike fully correct methods involving evaluation of the content height of the cell)
|
|
|
154 |
//
|
|
|
155 |
// Thus, we do the following:
|
|
|
156 |
// - calculate the ratio of current column MAXIMAL ($current_max) width to the sum of MAXIMAL widths of all columns left
|
|
|
157 |
// (inluding current) second rule applied. Note that we need remember about column spans and select
|
|
|
158 |
// maxw or maxwc in order.
|
|
|
159 |
// - then check if the rest of width will be too small for other columns to fit and decrease current columns
|
|
|
160 |
// width (see MIN function call)
|
|
|
161 |
// - then check again if our width will be too small for current column to fit (and expand if nesessary) -
|
|
|
162 |
// MAX function call
|
|
|
163 |
for ($i=0; $i < $columns; $i++) {
|
|
|
164 |
if (!$table->is_constrained_column($i)) {
|
|
|
165 |
// Get undistributed width (total table width - width of constrained columns)
|
|
|
166 |
$rest = $width - array_sum($widths);
|
|
|
167 |
// get max width of column being processed
|
|
|
168 |
// If width is equal to zero, use max constrained width, as this column could be covered by colspan;
|
|
|
169 |
// If not, we lose nothing, because all constrained columns are already processed earlier, and no more
|
|
|
170 |
// columns except these two types can have different constrained and raw widths
|
|
|
171 |
$current_max = max($maxw[$i], $maxwc[$i]);
|
|
|
172 |
|
|
|
173 |
// Get sum of maximal constrained widths of unplaced columns
|
|
|
174 |
$sum_max_cw = 0;
|
|
|
175 |
$sum_min_cw = 0;
|
|
|
176 |
for ($j=0; $j<$columns; $j++) {
|
|
|
177 |
if (!isset($widths[$j])) {
|
|
|
178 |
$sum_max_cw += max($maxw[$j], $maxwc[$j]);
|
|
|
179 |
$sum_min_cw += max($minw[$j], $minwc[$j]);
|
|
|
180 |
};
|
|
|
181 |
};
|
|
|
182 |
|
|
|
183 |
// If some unplaced columns have maximal (constrained width) greater zero
|
|
|
184 |
if ($sum_max_cw > 0) {
|
|
|
185 |
$current_max = min($current_max * $rest / $sum_max_cw, $rest - $sum_min_cw + max($minwc[$i], $minw[$i]));
|
|
|
186 |
};
|
|
|
187 |
|
|
|
188 |
// Check for minimal width (either unconstrained or constrained) of current column
|
|
|
189 |
$current_max = max($current_max, $minw[$i] == 0 ? $minwc[$i] : $minw[$i]);
|
|
|
190 |
// Store calculated width
|
|
|
191 |
$widths[$i] = $current_max;
|
|
|
192 |
}
|
|
|
193 |
}
|
|
|
194 |
|
|
|
195 |
// Process the case of a lone empty table cell (used, for example, for its background color)
|
|
|
196 |
// as we're using floating point numbers, we cannot use equals sign
|
|
|
197 |
if (array_sum($widths) < EPSILON) {
|
|
|
198 |
for ($i=0; $i<count($widths); $i++) {
|
|
|
199 |
$widths[$i] = 0.01;
|
|
|
200 |
};
|
|
|
201 |
};
|
|
|
202 |
|
|
|
203 |
// now - the last attempt; if total width is less than box width, then we have a situation when either
|
|
|
204 |
// all columns AND table are width constrained or the HTML similar to the following:
|
|
|
205 |
//
|
|
|
206 |
// <table cellpadding="0" width="100%" bgcolor="green"><tbody><tr>
|
|
|
207 |
// <td colspan="2" bgcolor="yellow"></td>
|
|
|
208 |
// <td style="width: 100px;" bgcolor="cyan">TEXT
|
|
|
209 |
//
|
|
|
210 |
// e.g. empty column (with zero width) and fixed-width column.
|
|
|
211 |
//
|
|
|
212 |
$wc = $table->get_css_property(CSS_WIDTH);
|
|
|
213 |
if (!$wc->isNull()) {
|
|
|
214 |
if (array_sum($widths) < $width) {
|
|
|
215 |
// Let's make zero-width columns
|
|
|
216 |
// non-zero width (so that they columd be expanded) and re-try expanding columns
|
|
|
217 |
//
|
|
|
218 |
for ($i=0; $i<count($widths); $i++) {
|
|
|
219 |
if ($widths[$i] == 0) { $widths[$i] = EPSILON; };
|
|
|
220 |
};
|
|
|
221 |
|
|
|
222 |
// Now, if there's at least one non-costrained columns, try expanding them again
|
|
|
223 |
$flags = $table->get_non_constrained_width_flags();
|
|
|
224 |
if (!any_flag_set($flags)) {
|
|
|
225 |
$flags = $table->get_non_constant_constrained_width_flags();
|
|
|
226 |
if (!any_flag_set($flags)) {
|
|
|
227 |
$flags = $table->get_non_image_constrained_width_flags();
|
|
|
228 |
if (!any_flag_set($flags)) {
|
|
|
229 |
for ($i=0; $i<count($flags); $i++) { $flags[$i] = true; };
|
|
|
230 |
};
|
|
|
231 |
};
|
|
|
232 |
};
|
|
|
233 |
|
|
|
234 |
$widths = expand_to_with_flags($width,
|
|
|
235 |
$widths,
|
|
|
236 |
$flags);
|
|
|
237 |
};
|
|
|
238 |
|
|
|
239 |
// in case of overconstrained table (e.g. two columns with 20% widths), expand them
|
|
|
240 |
$widths = expand_to($width, $widths);
|
|
|
241 |
};
|
|
|
242 |
|
|
|
243 |
$table->put_full_width(array_sum($widths));
|
|
|
244 |
|
|
|
245 |
// Now we need to sort array by key keeping key-value associations in order for array_slice to work correctly
|
|
|
246 |
ksort($widths, SORT_NUMERIC);
|
|
|
247 |
|
|
|
248 |
return $widths;
|
|
|
249 |
}
|
|
|
250 |
}
|
|
|
251 |
|
|
|
252 |
?>
|