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