PageRenderTime 84ms CodeModel.GetById 10ms app.highlight 65ms RepoModel.GetById 1ms app.codeStats 1ms

/app/vendors/pipeline.class.php

https://bitbucket.org/ducnv66/bbb-admin
PHP | 1228 lines | 874 code | 243 blank | 111 comment | 71 complexity | 6501f99a66cf0bba4d6a84ac698f499e MD5 | raw file
   1<?php
   2
   3require_once(HTML2PS_DIR.'utils_array.php');
   4require_once(HTML2PS_DIR.'utils_graphic.php');
   5require_once(HTML2PS_DIR.'utils_url.php');
   6require_once(HTML2PS_DIR.'utils_text.php');
   7require_once(HTML2PS_DIR.'utils_units.php');
   8require_once(HTML2PS_DIR.'utils_number.php');
   9
  10require_once(HTML2PS_DIR.'value.color.php');
  11
  12require_once(HTML2PS_DIR.'config.parse.php');
  13
  14require_once(HTML2PS_DIR.'flow_context.class.inc.php');
  15require_once(HTML2PS_DIR.'flow_viewport.class.inc.php');
  16
  17require_once(HTML2PS_DIR.'output._interface.class.php');
  18require_once(HTML2PS_DIR.'output._generic.class.php');
  19require_once(HTML2PS_DIR.'output._generic.pdf.class.php');
  20require_once(HTML2PS_DIR.'output._generic.ps.class.php');
  21require_once(HTML2PS_DIR.'output.pdflib.old.class.php');
  22require_once(HTML2PS_DIR.'output.pdflib.1.6.class.php');
  23require_once(HTML2PS_DIR.'output.fpdf.class.php');
  24require_once(HTML2PS_DIR.'output.fastps.class.php');
  25require_once(HTML2PS_DIR.'output.fastps.l2.class.php');
  26require_once(HTML2PS_DIR.'output.png.class.php');
  27// require_once(HTML2PS_DIR.'output.pcl.class.php');
  28
  29require_once(HTML2PS_DIR.'stubs.common.inc.php');
  30
  31require_once(HTML2PS_DIR.'media.layout.inc.php');
  32
  33require_once(HTML2PS_DIR.'box.php');
  34require_once(HTML2PS_DIR.'box.generic.php');
  35require_once(HTML2PS_DIR.'box.generic.formatted.php');
  36require_once(HTML2PS_DIR.'box.container.php');
  37require_once(HTML2PS_DIR.'box.generic.inline.php');
  38require_once(HTML2PS_DIR.'box.inline.php');
  39require_once(HTML2PS_DIR.'box.inline.control.php');
  40
  41require_once(HTML2PS_DIR.'font.class.php');
  42require_once(HTML2PS_DIR.'font_factory.class.php');
  43
  44require_once(HTML2PS_DIR.'box.br.php');
  45require_once(HTML2PS_DIR.'box.block.php');
  46require_once(HTML2PS_DIR.'box.page.php');
  47require_once(HTML2PS_DIR.'box.page.margin.class.php');
  48require_once(HTML2PS_DIR.'box.body.php');
  49require_once(HTML2PS_DIR.'box.block.inline.php');
  50require_once(HTML2PS_DIR.'box.button.php');
  51require_once(HTML2PS_DIR.'box.button.submit.php');
  52require_once(HTML2PS_DIR.'box.button.reset.php');
  53require_once(HTML2PS_DIR.'box.checkbutton.php');
  54require_once(HTML2PS_DIR.'box.form.php');
  55require_once(HTML2PS_DIR.'box.frame.php');
  56require_once(HTML2PS_DIR.'box.iframe.php');
  57require_once(HTML2PS_DIR.'box.input.text.php');
  58require_once(HTML2PS_DIR.'box.input.textarea.php');
  59require_once(HTML2PS_DIR.'box.input.password.php');
  60require_once(HTML2PS_DIR.'box.legend.php');
  61require_once(HTML2PS_DIR.'box.list-item.php');
  62require_once(HTML2PS_DIR.'box.null.php');
  63require_once(HTML2PS_DIR.'box.radiobutton.php');
  64require_once(HTML2PS_DIR.'box.select.php');
  65require_once(HTML2PS_DIR.'box.table.php');
  66require_once(HTML2PS_DIR.'box.table.cell.php');
  67require_once(HTML2PS_DIR.'box.table.cell.fake.php');
  68require_once(HTML2PS_DIR.'box.table.row.php');
  69require_once(HTML2PS_DIR.'box.table.section.php');
  70
  71require_once(HTML2PS_DIR.'box.text.php');
  72require_once(HTML2PS_DIR.'box.text.string.php');
  73require_once(HTML2PS_DIR.'box.field.pageno.php');
  74require_once(HTML2PS_DIR.'box.field.pages.php');
  75
  76require_once(HTML2PS_DIR.'box.whitespace.php');
  77
  78require_once(HTML2PS_DIR.'box.img.php'); // Inherited from the text box!
  79require_once(HTML2PS_DIR.'box.input.img.php');
  80
  81require_once(HTML2PS_DIR.'box.utils.text-align.inc.php');
  82
  83require_once(HTML2PS_DIR.'manager.encoding.php');
  84
  85require_once(HTML2PS_DIR.'ps.unicode.inc.php');
  86require_once(HTML2PS_DIR.'ps.utils.inc.php');
  87require_once(HTML2PS_DIR.'ps.whitespace.inc.php');
  88
  89require_once(HTML2PS_DIR.'ps.image.encoder.inc.php');
  90require_once(HTML2PS_DIR.'ps.image.encoder.simple.inc.php');
  91require_once(HTML2PS_DIR.'ps.l2.image.encoder.stream.inc.php');
  92require_once(HTML2PS_DIR.'ps.l3.image.encoder.stream.inc.php');
  93
  94require_once(HTML2PS_DIR.'tag.body.inc.php');
  95require_once(HTML2PS_DIR.'tag.font.inc.php');
  96require_once(HTML2PS_DIR.'tag.frame.inc.php');
  97require_once(HTML2PS_DIR.'tag.input.inc.php');
  98require_once(HTML2PS_DIR.'tag.img.inc.php');
  99require_once(HTML2PS_DIR.'tag.select.inc.php');
 100require_once(HTML2PS_DIR.'tag.span.inc.php');
 101require_once(HTML2PS_DIR.'tag.table.inc.php');
 102require_once(HTML2PS_DIR.'tag.td.inc.php');
 103require_once(HTML2PS_DIR.'tag.utils.inc.php');
 104
 105require_once(HTML2PS_DIR.'tree.navigation.inc.php');
 106
 107require_once(HTML2PS_DIR.'html.attrs.inc.php');
 108
 109require_once(HTML2PS_DIR.'xhtml.autoclose.inc.php');
 110require_once(HTML2PS_DIR.'xhtml.utils.inc.php');
 111require_once(HTML2PS_DIR.'xhtml.tables.inc.php');
 112require_once(HTML2PS_DIR.'xhtml.p.inc.php');
 113require_once(HTML2PS_DIR.'xhtml.lists.inc.php');
 114require_once(HTML2PS_DIR.'xhtml.deflist.inc.php');
 115require_once(HTML2PS_DIR.'xhtml.script.inc.php');
 116require_once(HTML2PS_DIR.'xhtml.entities.inc.php');
 117require_once(HTML2PS_DIR.'xhtml.comments.inc.php');
 118require_once(HTML2PS_DIR.'xhtml.style.inc.php');
 119require_once(HTML2PS_DIR.'xhtml.selects.inc.php');
 120
 121require_once(HTML2PS_DIR.'background.image.php');
 122require_once(HTML2PS_DIR.'background.position.php');
 123
 124require_once(HTML2PS_DIR.'list-style.image.php');
 125
 126require_once(HTML2PS_DIR.'height.php');
 127require_once(HTML2PS_DIR.'width.php');
 128
 129require_once(HTML2PS_DIR.'css.counter.php');
 130require_once(HTML2PS_DIR.'css.counter.collection.php');
 131
 132require_once(HTML2PS_DIR.'css.colors.inc.php');
 133
 134require_once(HTML2PS_DIR.'css.constants.inc.php');
 135require_once(HTML2PS_DIR.'css.inc.php');
 136require_once(HTML2PS_DIR.'css.state.class.php');
 137require_once(HTML2PS_DIR.'css.cache.class.php');
 138require_once(HTML2PS_DIR.'css.property.handler.class.php');
 139require_once(HTML2PS_DIR.'css.property.stringset.class.php');
 140require_once(HTML2PS_DIR.'css.property.sub.class.php');
 141require_once(HTML2PS_DIR.'css.property.sub.field.class.php');
 142require_once(HTML2PS_DIR.'css.utils.inc.php');
 143
 144require_once(HTML2PS_DIR.'css.background.attachment.inc.php');
 145require_once(HTML2PS_DIR.'css.background.color.inc.php');
 146require_once(HTML2PS_DIR.'css.background.image.inc.php');
 147require_once(HTML2PS_DIR.'css.background.repeat.inc.php');
 148require_once(HTML2PS_DIR.'css.background.position.inc.php');
 149require_once(HTML2PS_DIR.'css.background.inc.php');
 150
 151require_once(HTML2PS_DIR.'css.border.inc.php');
 152require_once(HTML2PS_DIR.'css.border.style.inc.php');
 153require_once(HTML2PS_DIR.'css.border.collapse.inc.php');
 154require_once(HTML2PS_DIR.'css.bottom.inc.php');
 155require_once(HTML2PS_DIR.'css.clear.inc.php');
 156require_once(HTML2PS_DIR.'css.color.inc.php');
 157require_once(HTML2PS_DIR.'css.direction.inc.php');
 158require_once(HTML2PS_DIR.'css.html2ps.html.content.inc.php');
 159require_once(HTML2PS_DIR.'css.html2ps.pseudoelements.inc.php');
 160require_once(HTML2PS_DIR.'css.html2ps.pixels.php');
 161require_once(HTML2PS_DIR.'css.content.inc.php');
 162require_once(HTML2PS_DIR.'css.display.inc.php');
 163require_once(HTML2PS_DIR.'css.float.inc.php');
 164require_once(HTML2PS_DIR.'css.font.inc.php');
 165require_once(HTML2PS_DIR.'css.height.inc.php');
 166require_once(HTML2PS_DIR.'css.min-height.inc.php');
 167require_once(HTML2PS_DIR.'css.max-height.inc.php');
 168require_once(HTML2PS_DIR.'css.left.inc.php');
 169require_once(HTML2PS_DIR.'css.letter-spacing.inc.php');
 170
 171require_once(HTML2PS_DIR.'css.list-style-image.inc.php');
 172require_once(HTML2PS_DIR.'css.list-style-position.inc.php');
 173require_once(HTML2PS_DIR.'css.list-style-type.inc.php');
 174require_once(HTML2PS_DIR.'css.list-style.inc.php');
 175
 176require_once(HTML2PS_DIR.'css.margin.inc.php');
 177require_once(HTML2PS_DIR.'css.overflow.inc.php');
 178require_once(HTML2PS_DIR.'css.padding.inc.php');
 179
 180require_once(HTML2PS_DIR.'css.page.inc.php');
 181require_once(HTML2PS_DIR.'css.page-break.inc.php');
 182require_once(HTML2PS_DIR.'css.page-break-after.inc.php');
 183require_once(HTML2PS_DIR.'css.page-break-before.inc.php');
 184require_once(HTML2PS_DIR.'css.page-break-inside.inc.php');
 185require_once(HTML2PS_DIR.'css.orphans.inc.php');
 186require_once(HTML2PS_DIR.'css.size.inc.php');
 187require_once(HTML2PS_DIR.'css.widows.inc.php');
 188
 189require_once(HTML2PS_DIR.'css.position.inc.php');
 190require_once(HTML2PS_DIR.'css.right.inc.php');
 191require_once(HTML2PS_DIR.'css.property.declaration.php');
 192require_once(HTML2PS_DIR.'css.rules.inc.php');
 193require_once(HTML2PS_DIR.'css.ruleset.class.php');
 194require_once(HTML2PS_DIR.'css.selectors.inc.php');
 195require_once(HTML2PS_DIR.'css.table-layout.inc.php');
 196require_once(HTML2PS_DIR.'css.text-align.inc.php');
 197require_once(HTML2PS_DIR.'css.text-decoration.inc.php');
 198require_once(HTML2PS_DIR.'css.text-transform.inc.php');
 199require_once(HTML2PS_DIR.'css.text-indent.inc.php');
 200require_once(HTML2PS_DIR.'css.top.inc.php');
 201require_once(HTML2PS_DIR.'css.vertical-align.inc.php');
 202require_once(HTML2PS_DIR.'css.visibility.inc.php');
 203require_once(HTML2PS_DIR.'css.white-space.inc.php');
 204require_once(HTML2PS_DIR.'css.width.inc.php');
 205require_once(HTML2PS_DIR.'css.word-spacing.inc.php');
 206require_once(HTML2PS_DIR.'css.z-index.inc.php');
 207
 208require_once(HTML2PS_DIR.'css.pseudo.add.margin.inc.php');
 209require_once(HTML2PS_DIR.'css.pseudo.align.inc.php');
 210require_once(HTML2PS_DIR.'css.pseudo.cellspacing.inc.php');
 211require_once(HTML2PS_DIR.'css.pseudo.cellpadding.inc.php');
 212require_once(HTML2PS_DIR.'css.pseudo.form.action.inc.php');
 213require_once(HTML2PS_DIR.'css.pseudo.form.radiogroup.inc.php');
 214require_once(HTML2PS_DIR.'css.pseudo.link.destination.inc.php');
 215require_once(HTML2PS_DIR.'css.pseudo.link.target.inc.php');
 216require_once(HTML2PS_DIR.'css.pseudo.listcounter.inc.php');
 217require_once(HTML2PS_DIR.'css.pseudo.localalign.inc.php');
 218require_once(HTML2PS_DIR.'css.pseudo.nowrap.inc.php');
 219require_once(HTML2PS_DIR.'css.pseudo.table.border.inc.php');
 220
 221// After all CSS utilities and constants have been initialized, load the default (precomiled) CSS stylesheet
 222require_once(HTML2PS_DIR.'converter.class.php');
 223require_once(HTML2PS_DIR.'treebuilder.class.php');
 224require_once(HTML2PS_DIR.'image.class.php');
 225
 226require_once(HTML2PS_DIR.'fetched_data._interface.class.php');
 227require_once(HTML2PS_DIR.'fetched_data._html.class.php');
 228require_once(HTML2PS_DIR.'fetched_data.url.class.php');
 229require_once(HTML2PS_DIR.'fetched_data.file.class.php');
 230
 231require_once(HTML2PS_DIR.'filter.data._interface.class.php');
 232require_once(HTML2PS_DIR.'filter.data.doctype.class.php');
 233
 234require_once(HTML2PS_DIR.'filter.data.utf8.class.php');
 235require_once(HTML2PS_DIR.'filter.data.ucs2.class.php');
 236
 237require_once(HTML2PS_DIR.'filter.data.html2xhtml.class.php');
 238require_once(HTML2PS_DIR.'filter.data.xhtml2xhtml.class.php');
 239
 240require_once(HTML2PS_DIR.'parser._interface.class.php');
 241require_once(HTML2PS_DIR.'parser.xhtml.class.php');
 242
 243require_once(HTML2PS_DIR.'filter.pre._interface.class.php');
 244require_once(HTML2PS_DIR.'filter.pre.fields.class.php');
 245require_once(HTML2PS_DIR.'filter.pre.headfoot.class.php');
 246require_once(HTML2PS_DIR.'filter.pre.footnotes.class.php');
 247require_once(HTML2PS_DIR.'filter.pre.height-constraint.class.php');
 248
 249require_once(HTML2PS_DIR.'layout._interface.class.php');
 250require_once(HTML2PS_DIR.'layout.default.class.php');
 251require_once(HTML2PS_DIR.'layout.page.breaks.php');
 252
 253require_once(HTML2PS_DIR.'filter.post._interface.class.php');
 254require_once(HTML2PS_DIR.'filter.post.positioned.class.php');
 255require_once(HTML2PS_DIR.'filter.post.postponed.class.php');
 256
 257require_once(HTML2PS_DIR.'filter.output._interface.class.php');
 258require_once(HTML2PS_DIR.'filter.output.ps2pdf.class.php');
 259require_once(HTML2PS_DIR.'filter.output.gzip.class.php');
 260
 261require_once(HTML2PS_DIR.'destination._interface.class.php');
 262require_once(HTML2PS_DIR.'destination._http.class.php');
 263require_once(HTML2PS_DIR.'destination.browser.class.php');
 264require_once(HTML2PS_DIR.'destination.download.class.php');
 265require_once(HTML2PS_DIR.'destination.file.class.php');
 266
 267require_once(HTML2PS_DIR.'xml.validation.inc.php');
 268
 269require_once(HTML2PS_DIR.'content_type.class.php');
 270require_once(HTML2PS_DIR.'dispatcher.class.php');
 271require_once(HTML2PS_DIR.'observer.class.php');
 272
 273require_once(HTML2PS_DIR.'strategy.page.break.simple.php');
 274require_once(HTML2PS_DIR.'strategy.page.break.smart.php');
 275
 276require_once(HTML2PS_DIR.'strategy.link.rendering.normal.php');
 277require_once(HTML2PS_DIR.'strategy.position.absolute.php');
 278require_once(HTML2PS_DIR.'strategy.width.absolute.positioned.php');
 279require_once(HTML2PS_DIR.'autofix.url.php');
 280
 281require_once(HTML2PS_DIR.'fetcher._interface.class.php');
 282require_once(HTML2PS_DIR.'features/_factory.php');
 283
 284require_once(HTML2PS_DIR.'css.property.collection.php');
 285require_once(HTML2PS_DIR.'css.rules.page.inc.php');
 286
 287require_once(HTML2PS_DIR.'css/lexer.php');
 288require_once(HTML2PS_DIR.'css/parser.php');
 289require_once(HTML2PS_DIR.'css/stream.string.php');
 290require_once(HTML2PS_DIR.'css/processor.php');
 291
 292class Pipeline {
 293  var $fetchers;
 294  var $data_filters;
 295  var $error_message;
 296  var $parser;
 297  var $pre_tree_filters;
 298  var $layout_engine;
 299  var $post_tree_filters;
 300  var $output_driver;
 301  var $output_filters;
 302  var $destination;
 303
 304  var $_base_url;
 305
 306  var $_page_at_rules;
 307  var $_counters;
 308  var $_footnotes;
 309
 310  var $_cssState;
 311  var $_css;
 312  var $_defaultCSS;
 313
 314  var $_dispatcher;
 315
 316  var $_current_page_name;
 317
 318  var $_page_break_strategy;
 319
 320  function Pipeline() {
 321    $this->_css = array();
 322
 323    $this->_counters = array();
 324    $this->_footnotes = array();
 325
 326    $this->_base_url = array('');
 327    $this->_reset_page_at_rules();
 328
 329    $this->pre_tree_filters = array();
 330
 331    $this->_dispatcher =& new DispatcherPdf();
 332
 333    $this->_dispatcher->add_event('before-page-heights');
 334    $this->_dispatcher->add_event('before-page');
 335    $this->_dispatcher->add_event('after-page');
 336    $this->_dispatcher->add_event('before-batch-item');
 337    $this->_dispatcher->add_event('after-batch-item');
 338    $this->_dispatcher->add_event('after-parse');
 339    $this->_dispatcher->add_event('before-document');
 340    $this->_dispatcher->add_event('after-document');
 341    $this->_dispatcher->add_event('before-batch');
 342    $this->_dispatcher->add_event('after-batch');
 343
 344    $this->_page_break_strategy = new StrategyPageBreakSimple();
 345  }
 346
 347  function add_feature($feature_name, $params = array()) {
 348    $feature_object =& FeatureFactory::get($feature_name);
 349    if (is_null($feature_object)) {
 350      die(sprintf('No feature "%s" found', $feature_name));
 351    };
 352
 353    $feature_object->install($this, $params);
 354  }
 355
 356  function add_fetcher(&$fetcher) {
 357    array_unshift($this->fetchers, $fetcher);
 358  }
 359
 360  function calculate_page_heights(&$media, &$box) {
 361    return $this->_page_break_strategy->run($this, $media, $box);
 362  }
 363
 364  function clear_box_id_map() {
 365    $GLOBALS['__html_box_id_map'] = array();
 366  }
 367
 368  function close() {
 369    $this->_dispatcher->fire('after-batch', array('pipeline' => &$this));
 370
 371    $this->output_driver->close();
 372    $this->_output();
 373    $this->output_driver->release();   
 374
 375    // Non HTML-specific cleanup
 376    //
 377    ImageFactory::clear_cache();
 378  }
 379
 380  function configure($options) {
 381    $defaults = array('compress'      => false,
 382                      'cssmedia'      => 'screen',
 383                      'debugbox'      => false,
 384                      'debugnoclip'   => false,
 385                      'draw_page_border' => false,
 386                      'encoding'      => '',
 387                      'html2xhtml'    => true,
 388                      'imagequality_workaround' => false,
 389                      'landscape'     => false,
 390                      'margins'       => array('left' => 30,
 391                                               'right' => 15,
 392                                               'top' => 15,
 393                                               'bottom' => 15),
 394                      'media'         => 'A4',
 395                      'method'        => 'fpdf',
 396                      'mode'          => 'html',
 397                      'output'        => 0,
 398                      'pagewidth'     => 800,
 399                      'pdfversion'    => "1.2",
 400                      'ps2pdf'        => false,
 401                      'pslevel'       => 3,
 402                      'renderfields'  => false,
 403                      'renderforms'   => false,
 404                      'renderimages'  => true,
 405                      'renderlinks'   => false,
 406                      'scalepoints'   => true,
 407                      'smartpagebreak' => true,
 408                      'transparency_workaround' => false
 409                      );
 410
 411    // As a reminder: If the input arrays have the same string keys, then the later value for that key will overwrite the previous one.
 412    $GLOBALS['g_config'] = array_merge($defaults, $options);
 413
 414    // Note that CSS media names should be case-insensitive
 415    $GLOBALS['g_config']['cssmedia'] = strtolower($GLOBALS['g_config']['cssmedia']);
 416
 417    if ($GLOBALS['g_config']['smartpagebreak']) {
 418      $this->_page_break_strategy = new StrategyPageBreakSmart();
 419    } else {
 420      $this->_page_break_strategy = new StrategyPageBreakSimple();
 421    };
 422  }
 423
 424  function _addFootnote(&$note_call) {
 425    $this->_footnotes[] =& $note_call;
 426  }
 427
 428//   function _fillContent($content) {
 429//     $filled = "";
 430
 431//     while (preg_match("/^.*?('.*?'|\".*?\"|counter\(.*?\))(.*)$/", $content, $matches)) {
 432//       $data    = $matches[1];
 433//       $content = $matches[2];
 434      
 435//       if ($data{0} != '\'' && $data{0} != '"') {
 436//         $filled .= $this->_fillContentCounter($data);
 437//       } else {
 438//         $filled .= $this->_fillContentString($data);
 439//       };
 440//     };
 441
 442//     return $filled;
 443//   }
 444
 445//   function _fillContentString($content) {
 446//     $unescaped_content = css_process_escapes($content);
 447//     $unquoted_content = css_remove_value_quotes($unescaped_content);
 448//     return $unquoted_content;
 449//   }
 450
 451//   function _fillContentCounter($content) {
 452//     preg_match("/counter\((.*?)\)/", $content, $matches);
 453//     return $this->get_counter($matches[1]);
 454//   }
 455
 456  function &get_counters() {
 457    $counter_collection =& new CSSCounterCollection();
 458
 459    foreach ($this->_counters as $counter_name => $counter_value) {
 460      $counter =& new CSSCounter($counter_name);
 461      $counter->set($counter_value);
 462      $counter_collection->add($counter);
 463    };
 464
 465    return $counter_collection;
 466  }
 467
 468  function &get_dispatcher() {
 469    return $this->_dispatcher;
 470  }
 471
 472  function get_counter($counter) {
 473    if (isset($this->_counters[$counter])) { 
 474      return $this->_counters[$counter];
 475    };
 476
 477    /**
 478     * CSS  2.1:   Counters  that  are   not  in  the  scope   of  any
 479     * 'counter-reset',  are assumed  to have  been  reset to  0 by  a
 480     * 'counter-reset' on the root element.
 481     */
 482    return 0;
 483  }
 484
 485  function reset_counter($counter, $value) {
 486    $this->_counters[$counter] = $value;
 487  }
 488
 489  function increment_counter($counter, $value) {
 490    $this->_counters[$counter] += $value;
 491  }
 492
 493  function add_at_rule_page($at_rule) {
 494    $selector =& $at_rule->getSelector();
 495    $type = $selector->get_type();
 496    $this->_page_at_rules[$type][] = $at_rule;
 497  }
 498
 499  function _reset_page_at_rules() {
 500    $this->_page_at_rules = array(CSS_PAGE_SELECTOR_ALL   => array(),
 501                                  CSS_PAGE_SELECTOR_FIRST => array(),
 502                                  CSS_PAGE_SELECTOR_LEFT  => array(),
 503                                  CSS_PAGE_SELECTOR_RIGHT => array(),
 504                                  CSS_PAGE_SELECTOR_NAMED => array());
 505  }
 506
 507  function &get_default_css() {
 508    return $this->_defaultCSS;
 509  }
 510
 511  function &get_current_css() {
 512    return $this->_css[0];
 513  }
 514
 515  function &get_current_css_state() {
 516    return $this->_cssState[0];
 517  }
 518
 519  function push_css() {
 520    array_unshift($this->_css, new CSSRuleset());
 521  }
 522
 523  function pop_css() {
 524    array_shift($this->_css);
 525  }
 526
 527  /**
 528   * Note that different pages  may define different margin boxes (for
 529   * example,  left and right  pages may  have different  headers). In
 530   * this  case, we  should  process  @page rules  in  order of  their
 531   * specificity (no selector  < :left / :right <  :first) and extract
 532   * margin boxes to be drawn
 533   *
 534   * @param $page_no Integer current page index (1-based)
 535   * @param $media 
 536   */
 537  function render_margin_boxes($page_no, &$media) {
 538    $boxes =& $this->reflow_margin_boxes($page_no, $media);
 539
 540    foreach ($boxes as $selector => $box) {
 541      $boxes[$selector]->show($this->output_driver);
 542    };
 543
 544    // Memleak fix
 545    for ($i=0, $size = count($boxes); $i < $size; $i++) {
 546      $boxes[$i]->destroy();
 547    };
 548    unset($boxes);
 549  }
 550
 551  function get_page_media($page_no, &$media) {
 552    $page_rules =& $this->get_page_rules($page_no);
 553    $size_landscape = $page_rules->get_property_value(CSS_SIZE);
 554    if (!is_null($size_landscape)) {
 555      $media->set_width($size_landscape['size']['width']);
 556      $media->set_height($size_landscape['size']['height']);
 557      $media->set_landscape($size_landscape['landscape']);
 558    };
 559
 560    $margins = $page_rules->get_property_value(CSS_MARGIN);
 561    if (!is_null($margins)) {
 562      $media->margins['left'] = $margins->left->calc(mm2pt($media->get_width())) / mm2pt(1) / pt2pt(1);
 563      $media->margins['right'] = $margins->right->calc(mm2pt($media->get_width())) / mm2pt(1) / pt2pt(1);
 564      $media->margins['top'] = $margins->top->calc(mm2pt($media->get_height())) / mm2pt(1) / pt2pt(1);
 565      $media->margins['bottom'] = $margins->bottom->calc(mm2pt($media->get_height())) / mm2pt(1) / pt2pt(1);
 566    };
 567
 568    $left_margin = $page_rules->get_property_value(CSS_MARGIN_LEFT);
 569    if (!is_null($left_margin)) {
 570      $media->margins['left'] = $left_margin->calc(mm2pt($media->get_width())) / mm2pt(1) / pt2pt(1);
 571    };
 572
 573    $right_margin = $page_rules->get_property_value(CSS_MARGIN_RIGHT);
 574    if (!is_null($right_margin)) {
 575      $media->margins['right'] = $right_margin->calc(mm2pt($media->get_width())) / mm2pt(1) / pt2pt(1);
 576    };
 577
 578    $top_margin = $page_rules->get_property_value(CSS_MARGIN_TOP);
 579    if (!is_null($top_margin)) {
 580      $media->margins['top'] = $top_margin->calc(mm2pt($media->get_height())) / mm2pt(1) / pt2pt(1);
 581    };
 582
 583    $bottom_margin = $page_rules->get_property_value(CSS_MARGIN_BOTTOM);
 584    if (!is_null($bottom_margin)) {
 585      $media->margins['bottom'] = $bottom_margin->calc(mm2pt($media->get_height())) / mm2pt(1) / pt2pt(1);
 586    };
 587
 588    $pixels = $page_rules->get_property_value(CSS_HTML2PS_PIXELS);
 589    if (!is_null($pixels)) {
 590      $media->set_pixels($pixels);
 591    };
 592  }
 593
 594  function &get_page_rules($page_no) {
 595    $collection =& new CSSPropertyCollection();
 596
 597    foreach ($this->_page_at_rules[CSS_PAGE_SELECTOR_ALL] as $rule) {
 598      $collection->merge($rule->css);
 599    };
 600
 601    /**
 602     * Check which one of :right/:left selector is applicable (assuming that first page matches :right)
 603     */
 604    if ($page_no % 2 == 0) {
 605      foreach ($this->_page_at_rules[CSS_PAGE_SELECTOR_LEFT] as $rule) {
 606        $collection->merge($rule->css);
 607      };
 608    } else {
 609      foreach ($this->_page_at_rules[CSS_PAGE_SELECTOR_RIGHT] as $rule) {
 610        $collection->merge($rule->css);
 611      };
 612    };
 613
 614    if ($page_no == 1) {
 615      foreach ($this->_page_at_rules[CSS_PAGE_SELECTOR_FIRST] as $rule) {
 616        $collection->merge($rule->css);
 617      };
 618    };
 619
 620    return $collection;
 621  }
 622
 623  function &reflow_page_box($page_no, &$media) {
 624    $rules =& $this->get_page_rules($page_no);
 625    $box =& BoxPage::create($this, $rules);
 626    $box->reflow($media);
 627    return $box;
 628  }
 629
 630  function render_page_box($page_no, &$media) {
 631    $box =& $this->reflow_page_box($page_no, $media);
 632    $box->show($this->output_driver);
 633    $box->destroy();
 634    unset($box);
 635  }
 636
 637  function &reflow_margin_boxes($page_no, &$media) {
 638    $at_rules = $this->_getMarginBoxes($page_no, $media);
 639
 640    $boxes = array();
 641    foreach ($at_rules as $at_rule) {
 642      $selector = $at_rule->getSelector();
 643      $boxes[$selector] =& BoxPageMargin::create($this, $at_rule);
 644    };
 645
 646    foreach ($boxes as $selector => $box) {
 647      $linebox_started     = false;
 648      $previous_whitespace = false;
 649      $boxes[$selector]->reflow_whitespace($linebox_started, $previous_whitespace);
 650      $boxes[$selector]->reflow_text($this->output_driver);
 651    };
 652
 653    foreach ($boxes as $selector => $box) {
 654      $boxes[$selector]->reflow($this->output_driver, 
 655                                $media,
 656                                $boxes);
 657    };
 658
 659    return $boxes;
 660  }
 661
 662  /**
 663   * Note that "+" operation on arrays will preserve existing elements; thus
 664   * we need to process @page rules in order of decreasing specificity
 665   *
 666   */
 667  function _getMarginBoxes($page_no, $media) {
 668    $applicable_margin_boxes = array();
 669
 670    /**
 671     * Check if :first selector is applicable
 672     */
 673    if ($page_no == 1) {
 674      foreach ($this->_page_at_rules[CSS_PAGE_SELECTOR_FIRST] as $rule) {
 675        $applicable_margin_boxes = $applicable_margin_boxes + $rule->getAtRuleMarginBoxes();
 676      };
 677    };
 678
 679    /**
 680     * Check which one of :right/:left selector is applicable (assuming that first page matches :right)
 681     */
 682    if ($page_no % 2 == 0) {
 683      foreach ($this->_page_at_rules[CSS_PAGE_SELECTOR_LEFT] as $rule) {
 684        $applicable_margin_boxes = $applicable_margin_boxes +  $rule->getAtRuleMarginBoxes();
 685      };
 686    } else {
 687      foreach ($this->_page_at_rules[CSS_PAGE_SELECTOR_RIGHT] as $rule) {
 688        $applicable_margin_boxes = $applicable_margin_boxes +  $rule->getAtRuleMarginBoxes();
 689      };
 690    };
 691
 692    /**
 693     * Extract margin boxes from plain @page rules
 694     */
 695    foreach ($this->_page_at_rules[CSS_PAGE_SELECTOR_ALL] as $rule) {
 696      $applicable_margin_boxes = $applicable_margin_boxes + $rule->getAtRuleMarginBoxes();
 697    };
 698
 699    if (!isset($applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_TOP])) { 
 700      $applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_TOP] =& 
 701        new CSSAtRuleMarginBox(CSS_MARGIN_BOX_SELECTOR_TOP,$this); 
 702    };
 703
 704    if (!isset($applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_TOP_LEFT_CORNER])) { 
 705      $applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_TOP_LEFT_CORNER] =& 
 706        new CSSAtRuleMarginBox(CSS_MARGIN_BOX_SELECTOR_TOP_LEFT_CORNER,$this); 
 707    };
 708
 709    if (!isset($applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_TOP_LEFT])) { 
 710      $applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_TOP_LEFT] =& 
 711        new CSSAtRuleMarginBox(CSS_MARGIN_BOX_SELECTOR_TOP_LEFT,$this); 
 712    };
 713
 714    if (!isset($applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_TOP_CENTER])) { 
 715      $applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_TOP_CENTER] =& 
 716        new CSSAtRuleMarginBox(CSS_MARGIN_BOX_SELECTOR_TOP_CENTER,$this); 
 717    };
 718
 719    if (!isset($applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_TOP_RIGHT])) { 
 720      $applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_TOP_RIGHT] =& 
 721        new CSSAtRuleMarginBox(CSS_MARGIN_BOX_SELECTOR_TOP_RIGHT,$this); 
 722    };
 723
 724    if (!isset($applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_TOP_RIGHT_CORNER])) { 
 725      $applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_TOP_RIGHT_CORNER] =& 
 726        new CSSAtRuleMarginBox(CSS_MARGIN_BOX_SELECTOR_TOP_RIGHT_CORNER,$this); 
 727    };
 728
 729    if (!isset($applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_BOTTOM])) { 
 730      $applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_BOTTOM] =& 
 731        new CSSAtRuleMarginBox(CSS_MARGIN_BOX_SELECTOR_BOTTOM,$this); 
 732    };
 733
 734    if (!isset($applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_BOTTOM_LEFT_CORNER])) { 
 735      $applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_BOTTOM_LEFT_CORNER] =& 
 736        new CSSAtRuleMarginBox(CSS_MARGIN_BOX_SELECTOR_BOTTOM_LEFT_CORNER,$this); 
 737    };
 738
 739    if (!isset($applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_BOTTOM_LEFT])) { 
 740      $applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_BOTTOM_LEFT] =& 
 741        new CSSAtRuleMarginBox(CSS_MARGIN_BOX_SELECTOR_BOTTOM_LEFT,$this); 
 742    };
 743
 744    if (!isset($applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_BOTTOM_CENTER])) { 
 745      $applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_BOTTOM_CENTER] =& 
 746        new CSSAtRuleMarginBox(CSS_MARGIN_BOX_SELECTOR_BOTTOM_CENTER,$this); 
 747    };
 748
 749    if (!isset($applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_BOTTOM_RIGHT])) { 
 750      $applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_BOTTOM_RIGHT] =& 
 751        new CSSAtRuleMarginBox(CSS_MARGIN_BOX_SELECTOR_BOTTOM_RIGHT,$this); 
 752    };
 753
 754    if (!isset($applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_BOTTOM_RIGHT_CORNER])) { 
 755      $applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_BOTTOM_RIGHT_CORNER] =& 
 756        new CSSAtRuleMarginBox(CSS_MARGIN_BOX_SELECTOR_BOTTOM_RIGHT_CORNER,$this); 
 757    };
 758
 759    if (!isset($applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_LEFT_TOP])) { 
 760      $applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_LEFT_TOP] =& 
 761        new CSSAtRuleMarginBox(CSS_MARGIN_BOX_SELECTOR_LEFT_TOP,$this); 
 762    };
 763
 764    if (!isset($applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_LEFT_MIDDLE])) { 
 765      $applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_LEFT_MIDDLE] =& 
 766        new CSSAtRuleMarginBox(CSS_MARGIN_BOX_SELECTOR_LEFT_MIDDLE,$this); 
 767    };
 768
 769    if (!isset($applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_LEFT_BOTTOM])) { 
 770      $applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_LEFT_BOTTOM] =& 
 771        new CSSAtRuleMarginBox(CSS_MARGIN_BOX_SELECTOR_LEFT_BOTTOM,$this); 
 772    };
 773
 774    if (!isset($applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_RIGHT_TOP])) { 
 775      $applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_RIGHT_TOP] =& 
 776        new CSSAtRuleMarginBox(CSS_MARGIN_BOX_SELECTOR_RIGHT_TOP,$this); 
 777    };
 778
 779    if (!isset($applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_RIGHT_MIDDLE])) { 
 780      $applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_RIGHT_MIDDLE] =& 
 781        new CSSAtRuleMarginBox(CSS_MARGIN_BOX_SELECTOR_RIGHT_MIDDLE,$this); 
 782    };
 783
 784    if (!isset($applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_RIGHT_BOTTOM])) { 
 785      $applicable_margin_boxes[CSS_MARGIN_BOX_SELECTOR_RIGHT_BOTTOM] =&
 786        new CSSAtRuleMarginBox(CSS_MARGIN_BOX_SELECTOR_RIGHT_BOTTOM,$this); 
 787    };
 788
 789    return $applicable_margin_boxes;
 790  }
 791
 792  function _process_item($data_id, &$media, $offset=0) {    
 793    $this->_dispatcher->fire('before-batch-item', array('pipeline' => &$this));
 794
 795    $box =& $this->_layout_item($data_id, $media, $offset, $context, $postponed_filter);
 796
 797    if (is_null($box)) {
 798      error_log(sprintf(_('Could not fetch: %s'), (string)$data_id));
 799      return true;
 800    };
 801
 802    $this->_show_item($box, $offset, $context, $media, $postponed_filter);
 803
 804    // Clear CSS for this item 
 805    $this->pop_css();
 806    $this->_defaultCSS = null;
 807
 808    // Memory leak fix: caused by circular references?
 809    $box->destroy();
 810
 811    $this->_dispatcher->fire('after-batch-item', array('pipeline' => &$this));
 812    return true;
 813  }
 814
 815  function _show_item(&$box, $offset, &$context, &$media, &$postponed_filter) {
 816    $context->sort_absolute_positioned_by_z_index();
 817
 818    $this->_dispatcher->fire('before-page-heights', array('pipeline' => &$this, 
 819                                                          'document' => &$box,
 820                                                          'media' => &$media));
 821
 822    // Make batch-processing offset
 823    $page_heights = $this->calculate_page_heights($media, $box);
 824    $box->offset(0, $offset);
 825
 826    $box->reflow_anchors($this->output_driver, $this->output_driver->anchors, $page_heights);
 827    
 828    $this->_dispatcher->fire('before-document', array('pipeline' => &$this, 
 829                                                      'document' => &$box,
 830                                                      'page-heights' => &$page_heights,
 831                                                      'media' => &$media));
 832
 833    $expected_pages = count($page_heights);
 834    $this->output_driver->set_expected_pages($expected_pages);
 835    $this->reset_counter('pages', $expected_pages);
 836    $this->reset_counter('page',  0);
 837
 838    // Output PDF pages using chosen PDF driver
 839    for ($i=0; $i<$expected_pages; $i++) {
 840      $this->get_page_media(1, $media);
 841
 842      $this->output_driver->update_media($media);
 843      $this->_setupScales($media);
 844
 845      $current_page_offset = $i == 0 ? 0 : $page_heights[$i-1];
 846      $current_page_height = $page_heights[$i];
 847
 848      $this->output_driver->next_page($current_page_offset);
 849
 850      // Preparen list of postponed (floating and relative-positioned) boxes for the current page
 851      $postponed_filter->process($box, null, $this);
 852
 853      $this->reset_counter('footnote', 0);
 854      $this->increment_counter('page', 1);
 855
 856      $this->output_driver->save();
 857
 858      /**
 859       * Note that margin boxes should be rendered before 'setup_clip', as it will trim all
 860       * content rendered outside the 'main' page area
 861       */
 862      $this->render_margin_boxes($i+1, $media);
 863      $this->render_page_box($i+1, $media);
 864
 865      $this->output_driver->setPageHeight($current_page_height);
 866      $this->output_driver->setup_clip();
 867
 868      $this->_dispatcher->fire('before-page', array('pipeline' => &$this, 
 869                                                    'document' => &$box, 
 870                                                    'pageno' => $i));
 871
 872      if (is_null($box->show($this->output_driver))) { 
 873        error_log('Pipeline::_process_item: output routine failed');
 874        return null; 
 875      };
 876
 877      /**
 878       * Show postponed boxes - relative and floating boxes, as they should be 
 879       * shown over boxes on the same layer
 880       */
 881      $this->output_driver->show_postponed();
 882
 883      $this->renderAbsolutePositioned($context);
 884      $this->output_driver->restore();
 885      $this->renderFixedPositioned($context);
 886      $this->renderFootnotes();
 887
 888      global $g_config;
 889      if ($g_config['draw_page_border']) { 
 890        $this->output_driver->draw_page_border(); 
 891      };
 892
 893      $this->_dispatcher->fire('after-page', array('pipeline' => &$this, 
 894                                                   'document' => &$box, 
 895                                                   'pageno' => $i));
 896    };
 897
 898    $this->_dispatcher->fire('after-document', array('pipeline' => &$this, 
 899                                                     'document' => &$box));
 900  }
 901
 902  function _output() {
 903    $temporary_output_filename = $this->output_driver->get_filename();
 904
 905    for ($i=0; $i<count($this->output_filters); $i++) {
 906      $temporary_output_filename = $this->output_filters[$i]->process($temporary_output_filename);
 907    };
 908
 909    // Determine the content type of the result
 910    $content_type = null;
 911    $i = count($this->output_filters)-1;
 912    while (($i >= 0) && (is_null($content_type))) {
 913      $content_type = $this->output_filters[$i]->content_type();
 914      $i--;
 915    };
 916
 917    if (is_null($content_type)) {
 918      $content_type = $this->output_driver->content_type();
 919    };
 920
 921    $this->destination->process($temporary_output_filename, $content_type);
 922    unlink($temporary_output_filename);
 923  }
 924
 925  function scan_styles(&$root) {
 926    $css_processor =& new CSSProcessor(); 
 927    $css_processor->set_pipeline($this);
 928
 929    $this->push_css();
 930    $ruleset =& $this->get_current_css();
 931    $css_processor->scan_node($root, $ruleset);
 932  }
 933
 934  function set_destination(&$destination) {
 935    $this->destination =& $destination;
 936  }
 937
 938  function set_output_driver(&$output_driver) {
 939    $this->output_driver =& $output_driver;
 940  }
 941
 942  function &fetch($data_id) {
 943    if (count($this->fetchers) == 0) { 
 944      ob_start();
 945      include(HTML2PS_DIR.'/templates/error._no_fetchers.tpl');
 946      $this->error_message = ob_get_contents();
 947      ob_end_clean();
 948
 949      $null = null;
 950      return $null; 
 951    };
 952
 953    // Fetch data
 954    for ($i=0; $i<count($this->fetchers); $i++) {
 955      $data = $this->fetchers[$i]->get_data($data_id);
 956
 957      if ($data != null) {
 958        $this->push_base_url($this->fetchers[$i]->get_base_url());
 959        return $data;
 960      };
 961    };
 962
 963    if (defined('DEBUG_MODE')) {
 964      error_log(sprintf('Could not fetch %s', $data_id));
 965    };
 966
 967    $null = null;
 968    return $null;
 969  }
 970  
 971  function process($data_id, &$media) {
 972    return $this->process_batch(array($data_id), $media);
 973  }
 974
 975  function _setupScales(&$media) {
 976    global $g_config;
 977    global $g_px_scale;
 978    global $g_pt_scale;
 979
 980    $g_px_scale = floor(mm2pt($media->width() - $media->margins['left'] - $media->margins['right'])) / $media->pixels;
 981
 982    if ($g_config['scalepoints']) {
 983      $g_pt_scale = $g_px_scale * 1.33; // This is a magic number, just don't touch it, or everything will explode!
 984    } else {
 985      $g_pt_scale = 1.0;
 986    };
 987  }
 988
 989  /**
 990   * Processes an set of URLs ot once; every URL is rendered on the separate page and 
 991   * merged to one PDF file.
 992   *
 993   * Note: to reduce peak memory requirement, URLs are processed one-after-one.
 994   *
 995   * @param Array $data_id_array Array of page identifiers to be processed (usually URLs or files paths)
 996   * @param Media $media Object describing the media to render for (size, margins, orientaiton & resolution)
 997   */
 998  function process_batch($data_id_array, &$media) {
 999    $this->clear_box_id_map();
1000
1001    // Save and disable magic_quotes_runtime
1002    $mq_runtime = get_magic_quotes_runtime();
1003    set_magic_quotes_runtime(0);
1004
1005    $this->_prepare($media);
1006
1007    $this->_dispatcher->fire('before-batch', array('pipeline' => &$this));
1008
1009    $i = 0;
1010    $offset = 0;
1011    foreach ($data_id_array as $data_id) {      
1012      $this->_process_item($data_id, $media, $offset);
1013
1014      $i++;
1015      $offset = $this->output_driver->offset;
1016    };
1017
1018    $this->close();
1019
1020    // Restore magic_quotes_runtime setting
1021    set_magic_quotes_runtime($mq_runtime);
1022
1023    return true;
1024  }
1025
1026  function error_message() {
1027    $message = file_get_contents(HTML2PS_DIR.'/templates/error._header.tpl');
1028
1029    $message .= $this->error_message;
1030
1031    for ($i=0; $i<count($this->fetchers); $i++) {
1032      $message .= $this->fetchers[$i]->error_message();
1033    };
1034
1035    $message .= $this->output_driver->error_message();
1036    
1037    $message .= file_get_contents(HTML2PS_DIR.'/templates/error._footer.tpl');
1038    return $message;
1039  }
1040
1041  function push_base_url($url) {
1042    array_unshift($this->_base_url, $url);
1043  }
1044
1045  function pop_base_url() {
1046    array_shift($this->_base_url);
1047  }
1048
1049  function get_base_url() {
1050    return $this->_base_url[0];
1051  }
1052
1053  function &get_output_driver() {
1054    return $this->output_driver;
1055  }  
1056
1057  function guess_url($src) {
1058    return guess_url($src, $this->get_base_url());
1059  }
1060
1061  function renderFootnotes() {
1062    /**
1063     * Render every footnote defined (note-call element is visible) on a current page
1064     */
1065
1066    $footnote_y = $this->output_driver->getFootnoteTop() - FOOTNOTE_LINE_TOP_GAP - FOOTNOTE_LINE_BOTTOM_GAP;
1067    $footnote_x = $this->output_driver->getPageLeft();
1068    $footnotes_found = false;
1069
1070    foreach ($this->_footnotes as $footnote) {
1071      // Note that footnote area for current page have been already defined,
1072      // as show_foonote is called after note-call boxes were placed.
1073      if ($this->output_driver->contains($footnote->_note_call_box)) { 
1074        $footnotes_found = true;
1075        $footnote_y = $footnote->show_footnote($this->output_driver, 
1076                                               $footnote_x,
1077                                               $footnote_y);
1078        $footnote_y -= FOOTNOTE_GAP;
1079      };
1080    };
1081
1082    /**
1083     * Draw thin line separating footnotes from page content
1084     */
1085    if ($footnotes_found) {
1086      $this->output_driver->setrgbcolor(0,0,0);
1087      $this->output_driver->moveto($this->output_driver->getPageLeft(),
1088                                   $this->output_driver->getFootnoteTop() - FOOTNOTE_LINE_TOP_GAP);
1089      $this->output_driver->lineto($this->output_driver->getPageLeft() + $this->output_driver->getPageWidth()*FOOTNOTE_LINE_PERCENT/100,
1090                                   $this->output_driver->getFootnoteTop() - FOOTNOTE_LINE_TOP_GAP);
1091      $this->output_driver->stroke();
1092    };
1093  }
1094
1095  function renderAbsolutePositioned(&$context) {
1096    for ($j=0, $size = count($context->absolute_positioned); $j<$size; $j++) {
1097      $current_box =& $context->absolute_positioned[$j];
1098      if ($current_box->get_css_property(CSS_VISIBILITY) === VISIBILITY_VISIBLE) {
1099        $this->output_driver->save();
1100        $current_box->_setupClip($this->output_driver);
1101        if (is_null($current_box->show($this->output_driver))) {
1102          return null;
1103        };
1104        $this->output_driver->restore();
1105      };
1106    };
1107    $this->output_driver->show_postponed_in_absolute();
1108  }
1109
1110  function renderFixedPositioned(&$context) {
1111    for ($j=0, $size = count($context->fixed_positioned); $j<$size; $j++) {
1112      $current_box =& $context->fixed_positioned[$j];
1113      if ($current_box->get_css_property(CSS_VISIBILITY) === VISIBILITY_VISIBLE) {
1114        $this->output_driver->save();
1115        $current_box->_setupClip($this->output_driver);
1116        if (is_null($current_box->show_fixed($this->output_driver))) { 
1117          return null;
1118        };
1119        $this->output_driver->restore();
1120      };
1121    };
1122    $this->output_driver->show_postponed_in_fixed();
1123  }
1124
1125  function _prepare(&$media) {
1126    $this->_setupScales($media);
1127    $GLOBALS['g_media'] =& $media;
1128    $this->output_driver->reset($media);
1129  }
1130
1131  function &_layout_item($data_id, &$media, $offset, &$context, &$postponed_filter) {
1132    $this->_reset_page_at_rules();
1133
1134    $css_cache = CSSCache::get();
1135    $this->_defaultCSS = $css_cache->compile('resource://default.css', 
1136                                             file_get_contents(HTML2PS_DIR.'/default.css'),
1137                                             $this);
1138    $this->_css = array();
1139    $this->push_css();
1140
1141    $this->_cssState = array(new CSSState(CSS::get()));
1142
1143    $font = $this->_cssState[0]->get_property(CSS_FONT);
1144    $font->units2pt(0);
1145    $this->_cssState[0]->set_property(CSS_FONT, $font);
1146
1147    $data = $this->fetch($data_id);
1148
1149    if (is_null($data)) { 
1150      $dummy = null;
1151      return $dummy;
1152    };
1153
1154    // Run raw data filters
1155    for ($i=0; $i<count($this->data_filters); $i++) {
1156      $data = $this->data_filters[$i]->process($data);
1157    };
1158
1159    // Parse the raw data
1160    $box =& $this->parser->process($data->get_content(), $this, $media);
1161
1162    $this->_dispatcher->fire('after-parse', array('pipeline' => &$this,
1163                                                  'document' => &$box,
1164                                                  'media' => $media));
1165
1166    /**
1167     * Run obligatory tree filters
1168     */
1169
1170    /**
1171     * height-constraint processing filter;
1172     */
1173    $filter = new PreTreeFilterHeightConstraint();
1174    $filter->process($box, $data, $this);
1175
1176    /**
1177     * Footnote support filter
1178     */
1179    $filter = new PreTreeFilterFootnotes();
1180    $filter->process($box, $data, $this);
1181
1182    // Run pre-layout tree filters
1183    for ($i=0, $size = count($this->pre_tree_filters); $i < $size; $i++) {
1184      $this->pre_tree_filters[$i]->process($box, $data, $this);
1185    };
1186
1187    $context = new FlowContext;
1188
1189    /**
1190     * Extract absolute/fixed positioned boxes
1191     */
1192    $positioned_filter = new PostTreeFilterPositioned($context);
1193    $positioned_filter->process($box, null, $this);
1194
1195    $postponed_filter = new PostTreeFilterPostponed($this->output_driver);
1196    $postponed_filter->process($box, null, $this);
1197
1198    $this->output_driver->prepare();
1199
1200    $status = $this->layout_engine->process($box, $media, $this->output_driver, $context);
1201    if (is_null($status)) { 
1202      error_log('Pipeline::_process_item: layout routine failed');
1203      $dummy = null;
1204      return $dummy;
1205    };
1206
1207    // Run post-layout tree filters
1208    for ($i=0; $i<count($this->post_tree_filters); $i++) {
1209      $this->post_tree_filters[$i]->process($box);
1210    };
1211
1212    return $box;
1213  }
1214
1215  function &getDispatcher() {
1216    return $this->_dispatcher;
1217  }
1218
1219  function get_current_page_name() {
1220    return $this->_current_page_name;
1221  }
1222
1223  function set_current_page_name($name) {
1224    $this->_current_page_name = $name;
1225  }
1226}
1227
1228?>