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 box if ($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 GenericFormattedBox function 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 location if ($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 narrow if ($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) { // Postpone return 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 line if ($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 collapsed if ($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 ). * 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 descendant function 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); } }; ?>