golden hour
/home/doctorbruno/public_html/doctorbruno.info/taeionline/support/include
⬆️ Go Up
Upload
File/Folder
Size
Actions
.MANIFEST
194.97 KB
Del
OK
.htaccess
14 B
Del
OK
JSON.php
33.13 KB
Del
OK
PasswordHash.php
6.92 KB
Del
OK
Spyc.php
31.74 KB
Del
OK
UniversalClassLoader.php
8.61 KB
Del
OK
ajax.admin.php
7.29 KB
Del
OK
ajax.config.php
4.61 KB
Del
OK
ajax.content.php
9.76 KB
Del
OK
ajax.draft.php
13.03 KB
Del
OK
ajax.email.php
2.33 KB
Del
OK
ajax.export.php
1006 B
Del
OK
ajax.filter.php
874 B
Del
OK
ajax.forms.php
13.84 KB
Del
OK
ajax.i18n.php
5.1 KB
Del
OK
ajax.kbase.php
2.93 KB
Del
OK
ajax.note.php
2.05 KB
Del
OK
ajax.orgs.php
11.73 KB
Del
OK
ajax.plugins.php
2.15 KB
Del
OK
ajax.schedule.php
4.29 KB
Del
OK
ajax.search.php
12.72 KB
Del
OK
ajax.sequence.php
3.2 KB
Del
OK
ajax.staff.php
11.62 KB
Del
OK
ajax.tasks.php
33.87 KB
Del
OK
ajax.thread.php
8.81 KB
Del
OK
ajax.tickets.php
79.7 KB
Del
OK
ajax.tips.php
1.66 KB
Del
OK
ajax.upgrader.php
2.24 KB
Del
OK
ajax.users.php
18.33 KB
Del
OK
api.cron.php
1.06 KB
Del
OK
api.tickets.php
10.36 KB
Del
OK
class.2fa.php
7.76 KB
Del
OK
class.ajax.php
1.45 KB
Del
OK
class.api.php
15.31 KB
Del
OK
class.app.php
1.49 KB
Del
OK
class.attachment.php
6.95 KB
Del
OK
class.auth.php
50.73 KB
Del
OK
class.avatar.php
6.44 KB
Del
OK
class.banlist.php
2.58 KB
Del
OK
class.base32.php
4.07 KB
Del
OK
class.businesshours.php
7.08 KB
Del
OK
class.canned.php
9.05 KB
Del
OK
class.captcha.php
1.73 KB
Del
OK
class.category.php
11.14 KB
Del
OK
class.charset.php
3.41 KB
Del
OK
class.cli.php
9.48 KB
Del
OK
class.client.php
15.39 KB
Del
OK
class.collaborator.php
5.65 KB
Del
OK
class.company.php
2.65 KB
Del
OK
class.config.php
60.36 KB
Del
OK
class.controller.php
1.15 KB
Del
OK
class.cron.php
3.6 KB
Del
OK
class.crypto.php
18.92 KB
Del
OK
class.csrf.php
2.38 KB
Del
OK
class.dept.php
33.21 KB
Del
OK
class.dispatcher.php
7.12 KB
Del
OK
class.draft.php
6.23 KB
Del
OK
class.dynamic_forms.php
64.98 KB
Del
OK
class.email.php
49 KB
Del
OK
class.error.php
1.69 KB
Del
OK
class.export.php
32.92 KB
Del
OK
class.faq.php
15.05 KB
Del
OK
class.file.php
34.59 KB
Del
OK
class.filter.php
31.9 KB
Del
OK
class.filter_action.php
23.67 KB
Del
OK
class.format.php
44.3 KB
Del
OK
class.forms.php
195.58 KB
Del
OK
class.http.php
7.16 KB
Del
OK
class.i18n.php
24.28 KB
Del
OK
class.import.php
6.9 KB
Del
OK
class.json.php
2.71 KB
Del
OK
class.knowledgebase.php
5.8 KB
Del
OK
class.list.php
42 KB
Del
OK
class.lock.php
4.05 KB
Del
OK
class.log.php
1.55 KB
Del
OK
class.mail.php
35.76 KB
Del
OK
class.mailer.php
25.75 KB
Del
OK
class.mailfetch.php
9.72 KB
Del
OK
class.mailparse.php
30.99 KB
Del
OK
class.message.php
6.42 KB
Del
OK
class.migrater.php
5.2 KB
Del
OK
class.misc.php
7.5 KB
Del
OK
class.model.php
2.3 KB
Del
OK
class.nav.php
14.15 KB
Del
OK
class.note.php
2.39 KB
Del
OK
class.oauth2.php
4.04 KB
Del
OK
class.organization.php
22.45 KB
Del
OK
class.orm.php
120.9 KB
Del
OK
class.osticket.php
20.64 KB
Del
OK
class.ostsession.php
21.86 KB
Del
OK
class.page.php
10.59 KB
Del
OK
class.pagenate.php
5.53 KB
Del
OK
class.passwd.php
1.21 KB
Del
OK
class.pdf.php
3.83 KB
Del
OK
class.plugin.php
36.13 KB
Del
OK
class.priority.php
1.81 KB
Del
OK
class.queue.php
103.51 KB
Del
OK
class.report.php
11.76 KB
Del
OK
class.role.php
11.27 KB
Del
OK
class.schedule.php
46.28 KB
Del
OK
class.search.php
61.66 KB
Del
OK
class.sequence.php
7.27 KB
Del
OK
class.session.php
19.25 KB
Del
OK
class.setup.php
3.55 KB
Del
OK
class.signal.php
4.16 KB
Del
OK
class.sla.php
9.11 KB
Del
OK
class.staff.php
60.21 KB
Del
OK
class.task.php
56.21 KB
Del
OK
class.team.php
12.31 KB
Del
OK
class.template.php
23.45 KB
Del
OK
class.thread.php
108.96 KB
Del
OK
class.thread_actions.php
17.08 KB
Del
OK
class.ticket.php
167.39 KB
Del
OK
class.timezone.php
21.94 KB
Del
OK
class.topic.php
20.06 KB
Del
OK
class.translation.php
34.79 KB
Del
OK
class.upgrader.php
13.76 KB
Del
OK
class.user.php
43.4 KB
Del
OK
class.usersession.php
7.55 KB
Del
OK
class.util.php
10.07 KB
Del
OK
class.validator.php
12.94 KB
Del
OK
class.variable.php
11.93 KB
Del
OK
class.xml.php
3.23 KB
Del
OK
class.yaml.php
1.15 KB
Del
OK
cli
-
Del
OK
client
-
Del
OK
config
-
Del
OK
fpdf
-
Del
OK
htmLawed.php
53.53 KB
Del
OK
html2text.php
33.71 KB
Del
OK
i18n
-
Del
OK
index.php
37 B
Del
OK
laminas-mail
-
Del
OK
mpdf
-
Del
OK
mysqli.php
9.55 KB
Del
OK
ost-config.php
5.63 KB
Del
OK
ost-sampleconfig.php
6.24 KB
Del
OK
pear
-
Del
OK
plugins
-
Del
OK
staff
-
Del
OK
tnef_decoder.php
19.82 KB
Del
OK
upgrader
-
Del
OK
Edit: html2text.php
<?php /****************************************************************************** * Copyright (c) 2010 Jevon Wright and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Jevon Wright - initial API and implementation * Jared Hancock - html table implementation ****************************************************************************/ /** * Tries to convert the given HTML into a plain text format - best suited for * e-mail display, etc. * * <p>In particular, it tries to maintain the following features: * <ul> * <li>Links are maintained, with the 'href' copied over * <li>Information in the <head> is lost * </ul> * * @param html the input HTML * @return the HTML converted, as best as possible, to text */ function convert_html_to_text($html, $width=74) { $html = fix_newlines($html); $doc = new DOMDocument('1.0', 'utf-8'); if (strpos($html, '<?xml ') === false) $html = '<?xml encoding="utf-8"?>'.$html; # <?php (4vim) if (!@$doc->loadHTML($html)) return $html; // Thanks, http://us3.php.net/manual/en/domdocument.loadhtml.php#95251 // dirty fix -- remove the inserted processing instruction foreach ($doc->childNodes as $item) { if ($item->nodeType == XML_PI_NODE) { $doc->removeChild($item); // remove hack break; } } $elements = identify_node($doc); // Add the default stylesheet $elements->getRoot()->addStylesheet( HtmlStylesheet::fromArray(array( 'html' => array('white-space' => 'pre'), # Don't wrap footnotes 'center' => array('text-align' => 'center'), 'p' => array('margin-bottom' => '1em'), 'pre' => array('white-space' => 'pre'), 'u' => array('text-decoration' => 'underline'), 'a' => array('text-decoration' => 'underline'), 'b' => array('text-transform' => 'uppercase'), 'strong' => array('text-transform' => 'uppercase'), 'h4' => array('text-transform' => 'uppercase'), // Crazy M$ styles '.MsoNormal' => array('margin' => 0, 'margin-bottom' => 0.0001), '.MsoPlainText' => array('margin' => 0, 'margin-bottom' => 0.0001), )) ); $options = array(); if (is_object($elements)) $output = $elements->render($width, $options); else $output = $elements; return trim($output); } /** * Unify newlines; in particular, \r\n becomes \n, and * then \r becomes \n. This means that all newlines (Unix, Windows, Mac) * all become \ns. * * @param text text with any number of \r, \r\n and \n combinations * @return the fixed text */ function fix_newlines($text) { // replace \r\n to \n // remove \rs $text = str_replace("\r\n?", "\n", $text); return $text; } function identify_node($node, $parent=null) { if ($node instanceof DOMText) return $node; if ($node instanceof DOMDocument) return identify_node($node->childNodes->item(1), $parent); if ($node instanceof DOMDocumentType || $node instanceof DOMComment) // ignore return ""; $name = strtolower($node->nodeName); // start whitespace switch ($name) { case "hr": return new HtmlHrElement($node, $parent); case "br": return new HtmlBrElement($node, $parent); case "style": $parent->getRoot()->addStylesheet(new HtmlStylesheet($node)); case "title": case "meta": case "script": case "link": // ignore these tags return ""; case "head": case "html": case "body": case "center": case "div": case "p": case "pre": return new HtmlBlockElement($node, $parent); case "blockquote": return new HtmlBlockquoteElement($node, $parent); case "cite": return new HtmlCiteElement($node, $parent); case "h1": case "h2": case "h3": case "h4": case "h5": case "h6": return new HtmlHeadlineElement($node, $parent); case "a": return new HtmlAElement($node, $parent); case "ol": return new HtmlListElement($node, $parent); case "ul": return new HtmlUnorderedListElement($node, $parent); case 'table': return new HtmlTable($node, $parent); case "img": return new HtmlImgElement($node, $parent); case "code": return new HtmlCodeElement($node, $parent); default: // print out contents of unknown tags //if ($node->hasChildNodes() && $node->childNodes->length == 1) // return identify_node($node->childNodes->item(0), $parent); return new HtmlInlineElement($node, $parent); } } class HtmlInlineElement { var $children = array(); var $style = false; var $stylesheets = array(); var $footnotes = array(); var $ws = false; function __construct($node, $parent) { $this->parent = $parent; $this->node = $node; $this->traverse($node); $this->style = new CssStyleRules(); if ($node instanceof DomElement && ($style = $this->node->getAttribute('style'))) $this->style->add($style); } function traverse($node) { if ($node && $node->hasChildNodes()) { for ($i = 0; $i < $node->childNodes->length; $i++) { $n = $node->childNodes->item($i); $this->children[] = identify_node($n, $this); } } } function render($width, $options) { $output = ''; $after_block = false; $this->ws = $this->getStyle('white-space', 'normal'); // Direction if ($this->node) $dir = $this->node->getAttribute('dir'); // Ensure we have a value, but don't emit a control char unless // direction is declared $this->dir = $dir ?: 'ltr'; switch (strtolower($dir)) { case 'ltr': $output .= "\xE2\x80\x8E"; # LEFT-TO-RIGHT MARK break; case 'rtl': $output .= "\xE2\x80\x8F"; # RIGHT-TO-LEFT MARK break; } foreach ($this->children as $c) { if ($c instanceof DOMText) { // Collapse white-space $more = $c->wholeText; switch ($this->ws) { case 'pre': case 'pre-wrap': break; case 'nowrap': case 'pre-line': case 'normal': default: if ($after_block) $more = ltrim($more); if ($this instanceof HtmlBlockElement && trim($more) == '') // Ignore pure whitespace in-between elements inside // block elements $more = ''; $more = preg_replace('/[ \r\n\t\f]+/mu', ' ', $more); } } elseif ($c instanceof HtmlInlineElement) { $more = $c->render($width, $options); } else { $more = $c; if (!$after_block) // Prepend a newline. Block elements should start to the // far left $output .= "\n"; } $after_block = ($c instanceof HtmlBlockElement); if ($more instanceof PreFormattedText) $output = new PreFormattedText($output . $more); elseif (is_string($more)) $output .= $more; } switch ($this->getStyle('text-transform', 'none')) { case 'uppercase': $output = mb_strtoupper($output); break; } switch ($this->getStyle('text-decoration', 'none')) { case 'underline': // Split diacritics and underline chars which do not go below // the baseline if (class_exists('Normalizer')) $output = Normalizer::normalize($output, Normalizer::FORM_D); $output = preg_replace("/[a-fhik-or-xzA-PR-Z0-9#]/u", "$0\xcc\xb2", $output); break; } if ($this->footnotes) { $output = rtrim($output, "\n"); $output .= "\n\n" . str_repeat('-', $width/2) . "\n"; $id = 1; foreach ($this->footnotes as $name=>$content) $output .= sprintf("[%d] %s\n", $id++, $content); } return $output; } function getWeight() { if (!isset($this->weight)) { $this->weight = 0; foreach ($this->children as $c) { if ($c instanceof HtmlInlineElement) $this->weight += $c->getWeight(); elseif ($c instanceof DomText) $this->weight += mb_strwidth2($c->wholeText); } } return $this->weight; } function setStyle($property, $value) { $this->style->set($property, $value); } function getStyle($property, $default=null, $tag=false, $classes=false) { if ($this->style && $this->style->has($property)) return $this->style->get($property, $default); if ($this->node && $tag === false) $tag = $this->node->nodeName; if ($classes === false) { if ($this->node && ($c = $this->node->getAttribute('class'))) $classes = explode(' ', $c); else $classes = array(); } if ($this->stylesheets) { foreach ($this->stylesheets as $sheet) if ($s = $sheet->get($tag, $classes)) return $s->get($property, $default); } elseif ($this->parent) { return $this->getRoot()->getStyle($property, $default, $tag, $classes); } else { return $default; } } function getRoot() { if (!$this->parent) return $this; elseif (!isset($this->root)) $this->root = $this->parent->getRoot(); return $this->root; } function addStylesheet(&$s) { $this->stylesheets[] = $s; } function addFootNote($name, $content) { $this->footnotes[$content] = $content; return count($this->footnotes); } } class HtmlBlockElement extends HtmlInlineElement { var $min_width = false; var $pad_left; var $pad_right; function __construct($node, $parent) { parent::__construct($node, $parent); $this->pad_left = str_repeat(' ', $this->getStyle('padding-left', 0.0)); $this->pad_right = str_repeat(' ', $this->getStyle('padding-right', 0.0)); } function render($width, $options) { // Allow room for the border. // TODO: Consider left-right padding and margin $bw = $this->getStyle('border-width', 0); if ($bw) $width -= 4; $output = parent::render($width, $options); if ($output instanceof PreFormattedText) // TODO: Consider CSS rules return $output; // Leading and trailing whitespace is ignored in block elements $output = trim($output); if (!strlen($output)) return ""; // Padding $width -= strlen($this->pad_left) + strlen($this->pad_right); // Wordwrap the content to the width switch ($this->ws) { case 'nowrap': case 'pre': break; case 'pre-line': case 'pre-wrap': case 'normal': default: $output = mb_wordwrap($output, $width, "\n", true); } // Justification static $aligns = array( 'left' => STR_PAD_RIGHT, 'right' => STR_PAD_LEFT, 'center' => STR_PAD_BOTH, ); $talign = $this->getStyle('text-align', 'none'); $self = $this; if (isset($aligns[$talign])) { // Explode lines, justify, implode again $output = array_map(function($l) use ($talign, $aligns, $width, $self) { return $self->pad_left.mb_str_pad($l, $width, ' ', $aligns[$talign]).$self->pad_right; }, explode("\n", $output) ); $output = implode("\n", $output); } // Apply left and right padding, if specified elseif ($this->pad_left || $this->pad_right) { $output = array_map(function($l) use ($self) { return $self->pad_left.$l.$self->pad_right; }, explode("\n", $output) ); $output = implode("\n", $output); } // Border if ($bw) $output = self::borderize($output, $width); // Margin $mb = $this->getStyle('margin-bottom', 0.0) + $this->getStyle('padding-bottom', 0.0); $output .= str_repeat("\n", (int)$mb); return $output."\n"; } static function borderize($what, $width) { $output = ',-'.str_repeat('-', $width)."-.\n"; foreach (explode("\n", $what) as $l) $output .= '| '.mb_str_pad($l, $width)." |\n"; $output .= '`-'.str_repeat('-', $width)."-'\n"; return $output; } function getMinWidth() { if ($this->min_width === false) { foreach ($this->children as $c) { if ($c instanceof HtmlBlockElement) $this->min_width = max($c->getMinWidth(), $this->min_width); elseif ($c instanceof DomText) $this->min_width = max(max(array_map('mb_strwidth2', explode(' ', $c->wholeText))), $this->min_width); } } return $this->min_width + strlen($this->pad_left) + strlen($this->pad_right); } } class HtmlBrElement extends HtmlBlockElement { function render($width, $options) { return "\n"; } } class HtmlHrElement extends HtmlBlockElement { function render($width, $options) { return str_repeat("\xE2\x94\x80", $width)."\n"; } function getWeight() { return 1; } function getMinWidth() { return 0; } } class HtmlHeadlineElement extends HtmlBlockElement { function render($width, $options) { $line = false; if (!($headline = parent::render($width, $options))) return ""; switch ($this->node->nodeName) { case 'h1': $line = "\xE2\x95\x90"; # U+2505 break; case 'h2': $line = "\xE2\x94\x81"; # U+2501 break; case 'h3': $line = "\xE2\x94\x80"; # U+2500 break; default: return $headline; } $length = max(array_map('mb_strwidth2', explode("\n", $headline))); $headline .= str_repeat($line, $length) . "\n"; return $headline; } } class HtmlBlockquoteElement extends HtmlBlockElement { function render($width, $options) { return str_replace("\n", "\n> ", rtrim(parent::render($width-2, $options)))."\n"; } function getWeight() { return parent::getWeight()+2; } } class HtmlCiteElement extends HtmlBlockElement { function render($width, $options) { $lines = explode("\n", ltrim(parent::render($width-3, $options))); $lines[0] = "-- " . $lines[0]; // Right justification foreach ($lines as &$l) $l = mb_str_pad($l, $width, " ", STR_PAD_LEFT); unset($l); return implode("\n", $lines); } } class HtmlImgElement extends HtmlInlineElement { function render($width, $options) { // Images are returned as [alt: title] $title = $this->node->getAttribute("title"); if ($title) $title = ": $title"; $alt = $this->node->getAttribute("alt"); return "[image:$alt$title] "; } function getWeight() { return mb_strwidth2($this->node->getAttribute("alt")) + 8; } } class HtmlAElement extends HtmlInlineElement { function render($width, $options) { // links are returned in [text](link) format $output = parent::render($width, $options); $href = $this->node->getAttribute("href"); if ($href == null) { // it doesn't link anywhere if ($this->node->getAttribute("name") != null) { $output = "[$output]"; } } elseif (strpos($href, 'mailto:') === 0) { $href = substr($href, 7); $output = (($href != $output) ? "$href " : '') . "<$output>"; } elseif (mb_strwidth2($href) > $width / 2) { if (mb_strwidth2($output) > $width / 2) { // Parse URL and use relative path part if ($PU = parse_url($output)) $output = $PU['host'] . $PU['path']; } if ($href != $output) $id = $this->getRoot()->addFootnote($output, $href); $output = "[$output][$id]"; } elseif ($href != $output) { $output = "[$output]($href)"; } return $output; } function getWeight() { return parent::getWeight() + 4; } } class HtmlListElement extends HtmlBlockElement { var $marker = " %d. "; function render($width, $options) { $options['marker'] = $this->marker; return parent::render($width, $options); } function traverse($node, $number=1) { if ($node instanceof DOMText) return; switch (strtolower($node->nodeName)) { case "li": $this->children[] = new HtmlListItem($node, $this->parent, $number++); return; // Anything else is ignored } for ($i = 0; $i < $node->childNodes->length; $i++) $this->traverse($node->childNodes->item($i), $number); } } class HtmlUnorderedListElement extends HtmlListElement { var $marker = " * "; } class HtmlListItem extends HtmlBlockElement { function __construct($node, $parent, $number) { parent::__construct($node, $parent); $this->number = $number; } function render($width, $options) { $prefix = sprintf($options['marker'], $this->number); $lines = explode("\n", trim(parent::render($width-mb_strwidth2($prefix), $options))); $lines[0] = $prefix . $lines[0]; return new PreFormattedText( implode("\n".str_repeat(" ", mb_strwidth2($prefix)), $lines)."\n"); } } class HtmlCodeElement extends HtmlInlineElement { function render($width, $options) { $content = parent::render($width-2, $options); if (strpos($content, "\n")) return "```\n".trim($content)."\n```\n"; else return "`$content`"; } } class HtmlTable extends HtmlBlockElement { var $body; var $foot; var $rows; var $border = true; var $padding = true; function __construct($node, $parent) { $this->body = array(); $this->foot = array(); $this->rows = &$this->body; parent::__construct($node, $parent); $A = $this->node->getAttribute('border'); if (isset($A)) $this->border = (bool) $A; $A = $this->node->getAttribute('cellpadding'); if (isset($A)) $this->padding = (bool) $A; } function getMinWidth() { if (false === $this->min_width) { foreach ($this->rows as $r) foreach ($r as $cell) $this->min_width = max($this->min_width, $cell->getMinWidth()); } return $this->min_width + ($this->border ? 2 : 0) + ($this->padding ? 2 : 0); } function getWeight() { if (!isset($this->weight)) { $this->weight = 0; foreach ($this->rows as $r) foreach ($r as $cell) $this->weight += $cell->getWeight(); } return $this->weight; } function traverse($node) { if ($node instanceof DOMText) return; $name = strtolower($node->nodeName); switch ($name) { case 'th': case 'td': $this->row[] = new HtmlTableCell($node, $this->parent); // Don't descend into this node. It should be handled by the // HtmlTableCell::traverse return; case 'tr': unset($this->row); $this->row = array(); $this->rows[] = &$this->row; break; case 'caption': $this->caption = new HtmlBlockElement($node, $this->parent); return; case 'tbody': case 'thead': unset($this->rows); $this->rows = &$this->body; break; case 'tfoot': unset($this->rows); $this->rows = &$this->foot; break; } for ($i = 0; $i < $node->childNodes->length; $i++) $this->traverse($node->childNodes->item($i)); } /** * Ensure that no column is below its minimum width. Each column that is * below its minimum will borrow from a column that is above its * minimum. The process will continue until all columns are above their * minimums or all columns are below their minimums. */ function _fixupWidths(&$widths, $mins) { foreach ($widths as $i=>$w) { if ($w < $mins[$i]) { // Borrow from another column -- the furthest one away from // its minimum width $best = 0; $bestidx = false; foreach ($widths as $j=>$w) { if ($i == $j) continue; if ($w > $mins[$j]) { if ($w - $mins[$j] > $best) { $best = $w - $mins[$j]; $bestidx = $j; } } } if ($bestidx !== false) { $widths[$bestidx]--; $widths[$i]++; return $this->_fixupWidths($widths, $mins); } } } } function render($width, $options) { $cols = 0; $rows = array_merge($this->body, $this->foot); # Count the number of columns foreach ($rows as $r) $cols = max($cols, count($r)); if (!$cols) return ''; # Find the largest cells in all columns $weights = $mins = array_fill(0, $cols, 0); foreach ($rows as $r) { $i = 0; foreach ($r as $cell) { for ($j=0; $j<$cell->cols; $j++) { // TODO: Use cell-specified width $weights[$i] = max($weights[$i], $cell->getWeight()); $mins[$i] = max($mins[$i], $cell->getMinWidth()); } $i += $cell->cols; } } # Subtract internal padding and borders from the available width $inner_width = $width - ($this->border ? $cols + 1 : 0) - ($this->padding ? $cols*2 : 0); # Optimal case, where the preferred width of all the columns is # doable if (array_sum($weights) <= $inner_width) $widths = $weights; # Worst case, where the minimum size of the columns exceeds the # available width elseif (array_sum($mins) > $inner_width) $widths = $mins; # Most likely case, where the table can be fit into the available # width else { $total = array_sum($weights); $widths = array(); foreach ($weights as $c) $widths[] = (int)($inner_width * $c / $total); $this->_fixupWidths($widths, $mins); } $outer_width = array_sum($widths) + ($this->border ? $cols + 1 : 0) + ($this->padding ? $cols * 2 : 0); $contents = array(); $heights = array(); foreach ($rows as $y=>$r) { $heights[$y] = 0; for ($x = 0, $i = 0; $x < $cols; $i++) { if (!isset($r[$i])) { // No cell at the end of this row $contents[$y][$i][] = ""; break; } $cell = $r[$i]; # Compute the effective cell width for spanned columns # Add extra space for the unneeded border padding for # spanned columns $cwidth = ($this->border ? ($cell->cols - 1) : 0) + ($this->padding ? ($cell->cols - 1) * 2 : 0); for ($j = 0; $j < $cell->cols; $j++) $cwidth += $widths[$x+$j]; # Stash the computed width so it doesn't need to be # recomputed again below $cell->width = $cwidth; unset($data); $data = explode("\n", $cell->render($cwidth, $options)); // NOTE: block elements have trailing newline $heights[$y] = max(count($data)-1, $heights[$y]); $contents[$y][$i] = &$data; $x += $cell->cols; } } # Build the header $header = ""; if ($this->border) { $padding = $this->padding ? '-' : ''; for ($i = 0; $i < $cols; $i++) { $header .= '+'.$padding.str_repeat("-", $widths[$i]).$padding; } $header .= "+\n"; } # Emit the rows if (isset($this->caption)) { $this->caption = $this->caption->render($outer_width, $options); } $border = $this->border ? '|' : ''; $padding = $this->padding ? ' ' : ''; foreach ($rows as $y=>$r) { $output .= $header; for ($x = 0, $k = 0; $k < $heights[$y]; $k++) { $output .= $border; foreach ($r as $x=>$cell) { $content = (isset($contents[$y][$x][$k])) ? $contents[$y][$x][$k] : ""; $output .= $padding.mb_str_pad($content, $cell->width).$padding.$border; $x += $cell->cols; } $output .= "\n"; } } $output .= $header; return new PreFormattedText($output); } } class HtmlTableCell extends HtmlBlockElement { function __construct($node, $parent) { parent::__construct($node, $parent); $this->cols = $node->getAttribute('colspan'); $this->rows = $node->getAttribute('rowspan'); if (!$this->cols) $this->cols = 1; if (!$this->rows) $this->rows = 1; // Upgrade old attributes if ($A = $this->node->getAttribute('align')) $this->setStyle('text-align', $A); } function render($width, $options) { return parent::render($width, $options); } function getWeight() { return parent::getWeight() / ($this->cols * $this->rows); } function getMinWidth() { return max(4, parent::getMinWidth() / $this->cols); } } class HtmlStylesheet { function __construct($node=null) { if (!$node) return; // We really only care about tags and classes $rules = array(); preg_match_all('/([^{]+)\{((\s*[\w-]+:\s*[^;}]+;?)+)\s*\}/m', $node->textContent, $rules, PREG_SET_ORDER); $this->rules = array(); $m = array(); foreach ($rules as $r) { list(,$selector,$props) = $r; $props = new CssStyleRules($props); foreach (explode(',', $selector) as $s) { // Only allow tag and class selectors if (preg_match('/^([\w-]+)?(\.[\w_-]+)?$/m', trim($s), $m)) // XXX: Technically, a selector could be listed more // than once, and the rules should be aggregated. $this->rules[$m[0]] = &$props; } unset($props); } } function get($tag, $classes=array()) { // Honor CSS specificity foreach ($this->rules as $selector=>$rules) foreach ($classes as $c) if ($selector == "$tag.$c" || $selector == ".$c") return $rules; foreach ($this->rules as $selector=>$rules) if ($selector == $tag) return $rules; } static function fromArray($selectors) { $self = new HtmlStylesheet(); foreach ($selectors as $s=>$rules) $self->rules[$s] = CssStyleRules::fromArray($rules); return $self; } } class CssStyleRules { var $rules = array(); static $compact_rules = array( 'padding' => 1, ); function __construct($rules='') { if ($rules) $this->add($rules); } function add($rules) { foreach (explode(';', $rules) as $r) { if (strpos($r, ':') === false) continue; list($prop, $val) = explode(':', $r); $prop = trim($prop); // TODO: Explode compact rules, like 'border', 'margin', etc. if (isset(self::$compact_rules[$prop])) $this->expand($prop, trim($val)); else $this->rules[$prop] = trim($val); } } function expand($prop, $val) { switch (strtolower($prop)) { case 'padding': @list($a, $b, $c, $d) = preg_split('/\s+/', $val); if (!isset($b)) { $d = $c = $b = $a; } elseif (!isset($c)) { $d = $b; $c = $a; } elseif (!isset($d)) { $d = $b; } $this->rules['padding-top'] = $a; $this->styles['padding-right'] = $b; $this->rules['padding-bottom'] = $c; $this->rules['padding-left'] = $d; } } function has($prop) { return isset($this->rules[$prop]); } function get($prop, $default=0.0) { if (!isset($this->rules[$prop])) return $default; else $val = $this->rules[$prop]; if (is_string($val)) { switch (true) { case is_float($default): $simple = floatval($val); $units = substr($val, -2); // Cache the conversion $val = $this->rules[$prop] = self::convert($simple, $units); } } return $val; } function set($prop, $value) { $this->rules[$prop] = $value; } static function convert($value, $units, $max=0) { if ($value === null) return $value; // Converts common CSS units to units of characters switch ($units) { default: if (substr($units, -1) == '%') { return ((float) $value) * 0.01 * $max; } case 'px': // 600px =~ 60chars return (int) ($value / 10.0); case 'pt': return $value / 12.0; case 'em': return $value; } } static function fromArray($rules) { $self = new CssStyleRules(''); $self->rules = &$rules; return $self; } } class PreFormattedText { function __construct($text) { $this->text = $text; } function __toString() { return $this->text; } } if (!function_exists('mb_strwidth')) { function mb_strwidth($string) { return mb_strlen($string); } } function mb_strwidth2($string) { $junk = array(); return mb_strwidth($string) - preg_match_all("/\p{M}/u", $string, $junk); } // Thanks http://www.php.net/manual/en/function.wordwrap.php#107570 // @see http://www.tads.org/t3doc/doc/htmltads/linebrk.htm // for some more line breaking characters and rules // XXX: This does not wrap Chinese characters well // @see http://xml.ascc.net/en/utf-8/faq/zhl10n-faq-xsl.html#qb1 // for some more rules concerning Chinese chars function mb_wordwrap($string, $width=75, $break="\n", $cut=false) { if ($cut) { // Match anything 1 to $width chars long followed by whitespace or EOS, // otherwise match anything $width chars long $search = '/((?>[^\n\p{M}]\p{M}*){1,'.$width.'})(?:[ \n]|$|(\p{Ps}))|((?>[^\n\p{M}]\p{M}*){' .$width.'})/uS'; # <?php $replace = '$1$3'.$break.'$2'; } else { // Anchor the beginning of the pattern with a lookahead // to avoid crazy backtracking when words are longer than $width $search = '/(?=[\s\p{Ps}])(.{1,'.$width.'})(?:\s|$|(\p{Ps}))/uS'; $replace = '$1'.$break.'$2'; } return rtrim(preg_replace($search, $replace, $string), $break); } if (!function_exists('mb_str_pad')) { // Thanks http://www.php.net/manual/en/ref.mbstring.php#90611 function mb_str_pad($input, $pad_length, $pad_string=" ", $pad_style=STR_PAD_RIGHT) { $match = array(); $marks = preg_match_all('/\p{M}/u', $input, $match); return str_pad($input, strlen($input)-mb_strwidth($input)+$marks+$pad_length, $pad_string, $pad_style); } } // Enable use of html2text from command line // The syntax is the following: php html2text.php file.html do { if (PHP_SAPI != 'cli') break; if (empty ($_SERVER['argc']) || $_SERVER['argc'] < 2) break; if (empty ($_SERVER['PHP_SELF']) || FALSE === strpos ($_SERVER['PHP_SELF'], 'html2text.php') ) break; $file = $argv[1]; $width = 74; if (isset($argv[2])) $width = (int) $argv[2]; elseif (isset($ENV['COLUMNS'])) $width = $ENV['COLUMNS']; require_once(dirname(__file__).'/../bootstrap.php'); Bootstrap::i18n_prep(); echo convert_html_to_text (file_get_contents ($file), $width); } while (0);
Save