1<?php 2 3class 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?>