Blame | Letzte Änderung | Log anzeigen | RSS feed
<?php// $Header: /cvsroot/html2ps/box.inline.php,v 1.53 2007/01/24 18:55:44 Konstantin Exp $require_once(HTML2PS_DIR.'encoding.inc.php');define('SYMBOL_SHY', code_to_utf8(0xAD));define('BROKEN_SYMBOL', chr(0xC2));class LineBox {var $top;var $right;var $bottom;var $left;function LineBox() { }function ©() {$box =& new LineBox;$box->top = $this->top;$box->right = $this->right;$box->bottom = $this->bottom;$box->left = $this->left;return $box;}function offset($dx, $dy) {$this->top += $dy;$this->bottom += $dy;$this->left += $dx;$this->right += $dx;}function create(&$box) {$lbox = new LineBox;$lbox->top = $box->get_top();$lbox->right = $box->get_right();$lbox->bottom = $box->get_bottom();$lbox->left = $box->get_left();// $lbox->bottom = $box->get_top() - $box->get_baseline() - $box->get_descender();// $lbox->top = $box->get_top() - $box->get_baseline() + $box->get_ascender();return $lbox;}function extend(&$box) {$base = $box->get_top() - $box->get_baseline();$this->top = max($this->top, $base + $box->get_ascender());$this->right = max($this->right, $box->get_right());$this->bottom = min($this->bottom, $base - $box->get_descender());// Left edge of the line box should never be modified}function fake_box(&$box) {// Create the fake box object$fake_state = new CSSState(CSS::get());$fake_state->pushState();$fake = null;$fake_box = new BlockBox($fake);$fake_box->readCSS($fake_state);// Setup fake box size$fake_box->put_left($this->left);$fake_box->put_width($this->right - $this->left);$fake_box->put_top($this->top - $box->baseline);$fake_box->put_height($this->top - $this->bottom);// Setup padding value$fake_box->setCSSProperty(CSS_PADDING, $box->get_css_property(CSS_PADDING));// Setup fake box border and background$fake_box->setCSSProperty(CSS_BACKGROUND, $box->get_css_property(CSS_BACKGROUND));$fake_box->setCSSProperty(CSS_BORDER, $box->get_css_property(CSS_BORDER));return $fake_box;}}class InlineBox extends GenericInlineBox {var $_lines;function InlineBox() {// Call parent's constructor$this->GenericInlineBox();// Clear the list of line boxes inside this box$this->_lines = array();}function &create(&$root, &$pipeline) {// Create contents of this inline boxif ($root->node_type() == XML_TEXT_NODE) {$css_state =& $pipeline->get_current_css_state();$box = InlineBox::create_from_text($root->content,$css_state->get_property(CSS_WHITE_SPACE),$pipeline);return $box;} else {$box =& new InlineBox();$css_state =& $pipeline->get_current_css_state();$box->readCSS($css_state);// Initialize content$child = $root->first_child();while ($child) {$child_box =& create_pdf_box($child, $pipeline);$box->add_child($child_box);$child = $child->next_sibling();};// Add fake whitespace box with zero size for the anchor spans// We need this, as "reflow" functions will automatically remove empty inline boxes from the// document tree//if ($box->is_null()) {$css_state->pushState();$css_state->set_property(CSS_FONT_SIZE, Value::fromData(0.01, UNIT_PT));$whitespace = WhitespaceBox::create($pipeline);$whitespace->readCSS($css_state);$box->add_child($whitespace);$css_state->popState();};}return $box;}function &create_from_text($text, $white_space, &$pipeline) {$box =& new InlineBox();$box->readCSS($pipeline->get_current_css_state());// Apply/inherit text-related CSS properties$css_state =& $pipeline->get_current_css_state();$css_state->pushDefaultTextState();require_once(HTML2PS_DIR.'inline.content.builder.factory.php');$inline_content_builder =& InlineContentBuilderFactory::get($white_space);$inline_content_builder->build($box, $text, $pipeline);// Clear the CSS stack$css_state->popState();return $box;}function &get_line_box($index) {$line_box =& $this->_lines[$index];return $line_box;}function get_line_box_count() {return count($this->_lines);}// Inherited from GenericFormattedBoxfunction process_word($raw_content, &$pipeline) {if ($raw_content === '') {return false;}$ptr = 0;$word = '';$hyphens = array();$encoding = 'iso-8859-1';$manager_encoding =& ManagerEncoding::get();$text_box =& TextBox::create_empty($pipeline);$len = strlen($raw_content);while ($ptr < $len) {$char = $manager_encoding->get_next_utf8_char($raw_content, $ptr);// Check if current char is a soft hyphen character. It it is,// remove it from the word (as it should not be drawn normally)// and store its locationif ($char == SYMBOL_SHY) {$hyphens[] = strlen($word);} else {$mapping = $manager_encoding->get_mapping($char);/*** If this character is not found in predefined encoding vectors,* we'll use "Custom" encoding and add single-character TextBox** @TODO: handle characters without known glyph names*/if (is_null($mapping)) {/*** No mapping to default encoding vectors found for this character*//*** Add last word*/if ($word !== '') {$text_box->add_subword($word, $encoding, $hyphens);};/*** Add current symbol*/$custom_char = $manager_encoding->add_custom_char(utf8_to_code($char));$text_box->add_subword($custom_char, $manager_encoding->get_current_custom_encoding_name(), $hyphens);$word = '';} else {if (isset($mapping[$encoding])) {$word .= $mapping[$encoding];} else {// This condition prevents empty text boxes from appearing; say, if word starts with a national// character, an () - text box with no letters will be generated, in rare case causing a random line// wraps, if container is narrowif ($word !== '') {$text_box->add_subword($word, $encoding, $hyphens);};reset($mapping);list($encoding, $add) = each($mapping);$word = $mapping[$encoding];$hyphens = array();};};};};if ($word !== '') {$text_box->add_subword($word, $encoding, $hyphens);};$this->add_child($text_box);return true;}function show(&$driver) {if ($this->get_css_property(CSS_POSITION) == POSITION_RELATIVE) {// Postponereturn true;};return $this->_show($driver);}function show_postponed(&$driver) {return $this->_show($driver);}function _show(&$driver) {// Show line boxes background and borders$size = $this->get_line_box_count();for ($i=0; $i<$size; $i++) {$line_box = $this->get_line_box($i);$fake_box = $line_box->fake_box($this);$background = $this->get_css_property(CSS_BACKGROUND);$border = $this->get_css_property(CSS_BORDER);$background->show($driver, $fake_box);$border->show($driver, $fake_box);};// Show content$size = count($this->content);for ($i=0; $i < $size; $i++) {if (is_null($this->content[$i]->show($driver))) {return null;};}return true;}// Initialize next line box inside this inline//// Adds the next element to _lines array inside the current object and initializes it with the// $box parameters//// @param $box child box which will be first in this line box// @param $line_no number of line box//function init_line(&$box, &$line_no) {$line_box = LineBox::create($box);$this->_lines[$line_no] = $line_box;}// Extends the existing line box to include the given child// OR starts new line box, if current child is to the left of the box right edge// (which should not happen white the line box is filled)//// @param $box child box which will be first in this line box// @param $line_no number of line box//function extend_line(&$box, $line_no) {if (!isset($this->_lines[$line_no])) {// New line box started$this->init_line($box, $line_no);return $line_no;};// Check if this box starts a new lineif ($box->get_left() < $this->_lines[$line_no]->right) {$line_no++;$this->init_line($box, $line_no);return $line_no;};$this->_lines[$line_no]->extend($box);return $line_no;}function merge_line(&$box, $line_no) {$start_line = 0;if ($line_no > 0 && count($box->_lines) > 0) {if ($this->_lines[$line_no-1]->right + EPSILON > $box->_lines[0]->left) {$this->_lines[$line_no-1]->right = max($box->_lines[0]->right, $this->_lines[$line_no-1]->right);$this->_lines[$line_no-1]->top = max($box->_lines[0]->top, $this->_lines[$line_no-1]->top);$this->_lines[$line_no-1]->bottom = min($box->_lines[0]->bottom, $this->_lines[$line_no-1]->bottom);$start_line = 1;};};$size = count($box->_lines);for ($i=$start_line; $i<$size; $i++) {$this->_lines[] = $box->_lines[$i]->copy();};return count($this->_lines);}function reflow_static(&$parent, &$context) {GenericFormattedBox::reflow($parent, $context);// Note that inline boxes (actually SPANS)// are never added to the parent's line boxes// Move current box to the parent's current coordinates// Note that span box will start at the far left of the parent, NOT on its current X!// Also, note that inline box can have margins, padding and borders!$this->put_left($parent->get_left());$this->put_top($parent->get_top() - $this->get_extra_top());// first line of the SPAN will be offset to its parent current-x// PLUS the left padding of current span!$parent->_current_x += $this->get_extra_left();$this->_current_x = $parent->_current_x;// Note that the same operation IS NOT applied to parent current-y!// The padding space is just extended to the top possibly OVERLAPPING the above boxes.$this->width = 0;// Reflow contents$size = count($this->content);for ($i=0; $i<$size; $i++) {$child =& $this->content[$i];// Add current element into _parent_ line box and reflow it$child->reflow($parent, $context);// In general, if inline box centained whitespace box only,// it could be removed during reflow function call;// let's check it and skip to next child//// if no children left AT ALL (so this box is empty), just exit// Track the real height of the inline box; it will be used by other functions// (say, functions calculating content height)$this->extend_height($child->get_bottom_margin());};// Apply right extra space value (padding + border + margin)$parent->_current_x += $this->get_extra_right();// Margins of inline boxes are not collapsedif ($this->get_first_data()) {$context->pop_collapsed_margin();$context->push_collapsed_margin( 0 );};}function reflow_inline() {$line_no = 0;$size = count($this->content);for ($i=0; $i<$size; $i++) {$child =& $this->content[$i];$child->reflow_inline();if (!$child->is_null()) {if (is_a($child,'InlineBox')) {$line_no = $this->merge_line($child, $line_no);} else {$line_no = $this->extend_line($child, $line_no);};};};}function reflow_whitespace(&$linebox_started, &$previous_whitespace) {/*** Anchors could have no content at all (like <a name="test"></a>).* We should not remove such anchors, as this will break internal links* in the document.*/$dest = $this->get_css_property(CSS_HTML2PS_LINK_DESTINATION);if (!is_null($dest)) {return;};$size = count($this->content);for ($i=0; $i<$size; $i++) {$child =& $this->content[$i];$child->reflow_whitespace($linebox_started, $previous_whitespace);};if ($this->is_null()) {$this->parent->remove($this);};}function get_extra_line_left() {return $this->get_extra_left() + ($this->parent ? $this->parent->get_extra_line_left() : 0);}function get_extra_line_right() {return $this->get_extra_right() + ($this->parent ? $this->parent->get_extra_line_right() : 0);}/*** As "nowrap" properties applied to block-level boxes only, we may use simplified version of* 'get_min_width' here*/function get_min_width(&$context) {if (isset($this->_cache[CACHE_MIN_WIDTH])) {return $this->_cache[CACHE_MIN_WIDTH];}$content_size = count($this->content);/*** If box does not have any content, its minimal width is determined by extra horizontal space*/if ($content_size == 0) {return $this->_get_hor_extra();};$minw = $this->content[0]->get_min_width($context);for ($i=1; $i<$content_size; $i++) {$item = $this->content[$i];if (!$item->out_of_flow()) {$minw = max($minw, $item->get_min_width($context));};}// Apply width constraint to min width. Return maximal value$wc = $this->get_css_property(CSS_WIDTH);$min_width = max($minw, $wc->apply($minw, $this->parent->get_width())) + $this->_get_hor_extra();$this->_cache[CACHE_MIN_WIDTH] = $min_width;return $min_width;}// Restore default behaviour, as this class is a ContainerBox descendantfunction get_max_width_natural(&$context, $limit=10E6) {return $this->get_max_width($context, $limit);}function offset($dx, $dy) {$size = count($this->_lines);for ($i=0; $i<$size; $i++) {$this->_lines[$i]->offset($dx, $dy);};GenericInlineBox::offset($dx, $dy);}/*** Deprecated*/function getLineBoxCount() {return $this->get_line_box_count();}function &getLineBox($index) {return $this->get_line_box($index);}};?>