function _mailsystem_html_to_text_table in Mail System 7.2
Same name and namespace in other branches
- 8.2 html_to_text.inc \_mailsystem_html_to_text_table()
- 6.2 html_to_text.inc \_mailsystem_html_to_text_table()
- 7.3 html_to_text.inc \_mailsystem_html_to_text_table()
Helper function for _mailsystem_html_to_text().
Renders a <table> DOM Node into plain text. Attributes such as rowspan, colspan, padding, border, etc. are ignored.
Parameters
DOMNode $node: The DOMNode corresponding to the <table> tag and its contents.
$allowed_tags: The list of allowed tags passed to _mailsystem_html_to_text().
array &$notes: A writeable array of footnote reference numbers, keyed by their respective hyperlink destination urls.
$table_width: The desired maximum table width, after word-wrapping each table cell.
Return value
A plain text representation of the table.
See also
1 call to _mailsystem_html_to_text_table()
- _mailsystem_html_to_text in ./
html_to_text.inc - Helper function for drupal_html_to_text().
File
- ./
html_to_text.inc, line 476 - Copy of drupal_html_to_text improvements from issue #299138.
Code
function _mailsystem_html_to_text_table(DOMNode $node, $allowed_tags = NULL, array &$notes = array(), $table_width = 80) {
$eol = variable_get('mail_line_endings', MAIL_LINE_ENDINGS);
$header = array();
$footer = array();
$body = array();
$text = $eol;
$current = $node;
while (TRUE) {
if (isset($current->tagName)) {
switch ($current->tagName) {
case 'caption':
// The table caption is added first.
$text = _mailsystem_html_to_text($current, $allowed_tags, $notes, $table_width);
break;
case 'tr':
switch ($current->parentNode->tagName) {
case 'thead':
$header[] = $current;
break;
case 'tfoot':
$footer[] = $current;
break;
default:
// Either 'tbody' or 'table'.
$body[] = $current;
break;
}
break;
default:
if ($current
->hasChildNodes()) {
$current = $current->firstChild;
continue 2;
}
}
}
do {
if ($current->nextSibling) {
$current = $current->nextSibling;
continue 2;
}
$current = $current->parentNode;
} while ($current && !$current
->isSameNode($node));
break;
}
// Merge the thead, tbody, and tfoot sections together.
if ($rows = array_merge($header, $body, $footer)) {
$num_rows = count($rows);
// First just count the number of columns.
$num_cols = 0;
foreach ($rows as $row) {
$row_cols = 0;
foreach ($row->childNodes as $cell) {
if (isset($cell->tagName) && in_array($cell->tagName, array(
'td',
'th',
))) {
$row_cols++;
}
}
$num_cols = max($num_cols, $row_cols);
}
// If any columns were found, calculate each column height and width.
if ($num_cols) {
// Set up a binary search for best wrap width for each column.
$max = max($table_width - $num_cols - 1, 1);
$max_wraps = array_fill(0, $num_cols, $max);
$try = max(intval(($table_width - 1) / $num_cols - 1), 1);
$try_wraps = array_fill(0, $num_cols, $try);
$min_wraps = array_fill(0, $num_cols, 1);
// Start searching...
$change = FALSE;
do {
$change = FALSE;
$widths = array_fill(0, $num_cols, 0);
$heights = array_fill(0, $num_rows, 0);
$table = array_fill(0, $num_rows, array_fill(0, $num_cols, ''));
$breaks = array_fill(0, $num_cols, FALSE);
foreach ($rows as $i => $row) {
$j = 0;
foreach ($row->childNodes as $cell) {
if (!isset($cell->tagName) || !in_array($cell->tagName, array(
'td',
'th',
))) {
// Skip text nodes.
continue;
}
// Render the cell contents.
$cell = _mailsystem_html_to_text($cell, $allowed_tags, $notes, $try_wraps[$j]);
// Trim leading line-breaks and trailing whitespace.
// chr(160) is the non-breaking space character.
$cell = rtrim(ltrim($cell, $eol), ' ' . $eol . chr(160));
$table[$i][$j] = $cell;
if ($cell > '') {
// Split the cell into lines.
$lines = explode($eol, $cell);
// The row height is the maximum number of lines among all the
// cells in that row.
$heights[$i] = max($heights[$i], count($lines));
foreach ($lines as $line) {
$this_width = drupal_strlen($line);
// The column width is the maximum line width among all the
// lines in that column.
if ($this_width > $widths[$j]) {
$widths[$j] = $this_width;
// If the longest line in a column contains at least one
// space character, then the table can be made narrower.
$breaks[$j] = strpos(' ', $line) !== FALSE;
}
}
}
$j++;
}
}
// Calculate the total table width;
$this_width = array_sum($widths) + $num_cols + 1;
if ($this_width > $table_width) {
// Wider than desired.
if (!in_array(TRUE, $breaks)) {
// If there are no more break points, then the table is already as
// narrow as it can get, so we're done.
break;
}
foreach ($try_wraps as $i => $wrap) {
$max_wraps[$i] = min($max_wraps[$i], $wrap);
if ($breaks[$i]) {
$new_wrap = intval(($min_wraps[$i] + $max_wraps[$i]) / 2);
$new_wrap = min($new_wrap, $widths[$i] - 1);
$new_wrap = max($new_wrap, $min_wraps[$i]);
}
else {
// There's no point in trying to make the column narrower than
// the widest un-wrappable line in the column.
$min_wraps[$i] = $widths[$i];
$new_wrap = $widths[$i];
}
if ($try_wraps[$i] > $new_wrap) {
$try_wraps[$i] = $new_wrap;
$change = TRUE;
}
}
}
elseif ($this_width < $table_width) {
// Narrower than desired.
foreach ($try_wraps as $i => $wrap) {
if ($min_wraps[$i] < $wrap) {
$min_wraps[$i] = $wrap;
}
$new_wrap = intval(($min_wraps[$i] + $max_wraps[$i]) / 2);
$new_wrap = max($new_wrap, $widths[$i] + 1);
$new_wrap = min($new_wrap, $max_wraps[$i]);
if ($try_wraps[$i] < $new_wrap) {
$try_wraps[$i] = $new_wrap;
$change = TRUE;
}
}
}
} while ($change);
// Pad each cell to column width and line height.
for ($i = 0; $i < $num_rows; $i++) {
if ($heights[$i]) {
for ($j = 0; $j < $num_cols; $j++) {
$cell = $table[$i][$j];
// Pad each cell to the maximum number of lines in that row.
$lines = array_pad(explode($eol, $cell), $heights[$i], '');
foreach ($lines as $k => $line) {
// Pad each line to the maximum width in that column.
$repeat = $widths[$j] - drupal_strlen($line);
if ($repeat > 0) {
// chr(160) is the non-breaking space character.
$lines[$k] .= str_repeat(chr(160), $repeat);
}
}
$table[$i][$j] = $lines;
}
}
}
// Generate the row separator line.
$separator = '+';
for ($i = 0; $i < $num_cols; $i++) {
$separator .= str_repeat('-', $widths[$i]) . '+';
}
$separator .= $eol;
for ($i = 0; $i < $num_rows; $i++) {
$text .= $separator;
if (!$heights[$i]) {
continue;
}
$row = $table[$i];
// For each row, iterate first by lines within the row.
for ($k = 0; $k < $heights[$i]; $k++) {
// Add a vertical-bar at the beginning of each row line.
$row_line = '|';
$trimmed = '';
// Within each row line, iterate by cells within that line.
for ($j = 0; $j < $num_cols; $j++) {
// Add a vertical bar at the end of each cell line.
$row_line .= $row[$j][$k] . '|';
// chr(160) is the non-breaking space character.
$trimmed .= trim($row[$j][$k], ' ' . $eol . chr(160));
}
if ($trimmed > '') {
// Only print rows that are non-empty.
$text .= $row_line . $eol;
}
}
}
// Final output ends with a row separator.
$text .= $separator;
}
}
// Make sure formatted table content doesn't line-wrap.
// chr(160) is the non-breaking space character.
return str_replace(' ', chr(160), $text);
}