Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
 
3
require_once(HTML2PS_DIR.'utils_array.php');
4
require_once(HTML2PS_DIR.'utils_graphic.php');
5
require_once(HTML2PS_DIR.'utils_url.php');
6
require_once(HTML2PS_DIR.'utils_text.php');
7
require_once(HTML2PS_DIR.'utils_units.php');
8
require_once(HTML2PS_DIR.'utils_number.php');
9
 
10
require_once(HTML2PS_DIR.'value.color.php');
11
 
12
require_once(HTML2PS_DIR.'config.parse.php');
13
 
14
require_once(HTML2PS_DIR.'flow_context.class.inc.php');
15
require_once(HTML2PS_DIR.'flow_viewport.class.inc.php');
16
 
17
require_once(HTML2PS_DIR.'output._interface.class.php');
18
require_once(HTML2PS_DIR.'output._generic.class.php');
19
require_once(HTML2PS_DIR.'output._generic.pdf.class.php');
20
require_once(HTML2PS_DIR.'output._generic.ps.class.php');
21
require_once(HTML2PS_DIR.'output.pdflib.old.class.php');
22
require_once(HTML2PS_DIR.'output.pdflib.1.6.class.php');
23
require_once(HTML2PS_DIR.'output.fpdf.class.php');
24
require_once(HTML2PS_DIR.'output.fastps.class.php');
25
require_once(HTML2PS_DIR.'output.fastps.l2.class.php');
26
require_once(HTML2PS_DIR.'output.png.class.php');
27
// require_once(HTML2PS_DIR.'output.pcl.class.php');
28
 
29
require_once(HTML2PS_DIR.'stubs.common.inc.php');
30
 
31
require_once(HTML2PS_DIR.'media.layout.inc.php');
32
 
33
require_once(HTML2PS_DIR.'box.php');
34
require_once(HTML2PS_DIR.'box.generic.php');
35
require_once(HTML2PS_DIR.'box.generic.formatted.php');
36
require_once(HTML2PS_DIR.'box.container.php');
37
require_once(HTML2PS_DIR.'box.generic.inline.php');
38
require_once(HTML2PS_DIR.'box.inline.php');
39
require_once(HTML2PS_DIR.'box.inline.control.php');
40
 
41
require_once(HTML2PS_DIR.'font.class.php');
42
require_once(HTML2PS_DIR.'font_factory.class.php');
43
 
44
require_once(HTML2PS_DIR.'box.br.php');
45
require_once(HTML2PS_DIR.'box.block.php');
46
require_once(HTML2PS_DIR.'box.page.php');
47
require_once(HTML2PS_DIR.'box.page.margin.class.php');
48
require_once(HTML2PS_DIR.'box.body.php');
49
require_once(HTML2PS_DIR.'box.block.inline.php');
50
require_once(HTML2PS_DIR.'box.button.php');
51
require_once(HTML2PS_DIR.'box.button.submit.php');
52
require_once(HTML2PS_DIR.'box.button.reset.php');
53
require_once(HTML2PS_DIR.'box.checkbutton.php');
54
require_once(HTML2PS_DIR.'box.form.php');
55
require_once(HTML2PS_DIR.'box.frame.php');
56
require_once(HTML2PS_DIR.'box.iframe.php');
57
require_once(HTML2PS_DIR.'box.input.text.php');
58
require_once(HTML2PS_DIR.'box.input.textarea.php');
59
require_once(HTML2PS_DIR.'box.input.password.php');
60
require_once(HTML2PS_DIR.'box.legend.php');
61
require_once(HTML2PS_DIR.'box.list-item.php');
62
require_once(HTML2PS_DIR.'box.null.php');
63
require_once(HTML2PS_DIR.'box.radiobutton.php');
64
require_once(HTML2PS_DIR.'box.select.php');
65
require_once(HTML2PS_DIR.'box.table.php');
66
require_once(HTML2PS_DIR.'box.table.cell.php');
67
require_once(HTML2PS_DIR.'box.table.cell.fake.php');
68
require_once(HTML2PS_DIR.'box.table.row.php');
69
require_once(HTML2PS_DIR.'box.table.section.php');
70
 
71
require_once(HTML2PS_DIR.'box.text.php');
72
require_once(HTML2PS_DIR.'box.text.string.php');
73
require_once(HTML2PS_DIR.'box.field.pageno.php');
74
require_once(HTML2PS_DIR.'box.field.pages.php');
75
 
76
require_once(HTML2PS_DIR.'box.whitespace.php');
77
 
78
require_once(HTML2PS_DIR.'box.img.php'); // Inherited from the text box!
79
require_once(HTML2PS_DIR.'box.input.img.php');
80
 
81
require_once(HTML2PS_DIR.'box.utils.text-align.inc.php');
82
 
83
require_once(HTML2PS_DIR.'manager.encoding.php');
84
 
85
require_once(HTML2PS_DIR.'ps.unicode.inc.php');
86
require_once(HTML2PS_DIR.'ps.utils.inc.php');
87
require_once(HTML2PS_DIR.'ps.whitespace.inc.php');
88
 
89
require_once(HTML2PS_DIR.'ps.image.encoder.inc.php');
90
require_once(HTML2PS_DIR.'ps.image.encoder.simple.inc.php');
91
require_once(HTML2PS_DIR.'ps.l2.image.encoder.stream.inc.php');
92
require_once(HTML2PS_DIR.'ps.l3.image.encoder.stream.inc.php');
93
 
94
require_once(HTML2PS_DIR.'tag.body.inc.php');
95
require_once(HTML2PS_DIR.'tag.font.inc.php');
96
require_once(HTML2PS_DIR.'tag.frame.inc.php');
97
require_once(HTML2PS_DIR.'tag.input.inc.php');
98
require_once(HTML2PS_DIR.'tag.img.inc.php');
99
require_once(HTML2PS_DIR.'tag.select.inc.php');
100
require_once(HTML2PS_DIR.'tag.span.inc.php');
101
require_once(HTML2PS_DIR.'tag.table.inc.php');
102
require_once(HTML2PS_DIR.'tag.td.inc.php');
103
require_once(HTML2PS_DIR.'tag.utils.inc.php');
104
 
105
require_once(HTML2PS_DIR.'tree.navigation.inc.php');
106
 
107
require_once(HTML2PS_DIR.'html.attrs.inc.php');
108
 
109
require_once(HTML2PS_DIR.'xhtml.autoclose.inc.php');
110
require_once(HTML2PS_DIR.'xhtml.utils.inc.php');
111
require_once(HTML2PS_DIR.'xhtml.tables.inc.php');
112
require_once(HTML2PS_DIR.'xhtml.p.inc.php');
113
require_once(HTML2PS_DIR.'xhtml.lists.inc.php');
114
require_once(HTML2PS_DIR.'xhtml.deflist.inc.php');
115
require_once(HTML2PS_DIR.'xhtml.script.inc.php');
116
require_once(HTML2PS_DIR.'xhtml.entities.inc.php');
117
require_once(HTML2PS_DIR.'xhtml.comments.inc.php');
118
require_once(HTML2PS_DIR.'xhtml.style.inc.php');
119
require_once(HTML2PS_DIR.'xhtml.selects.inc.php');
120
 
121
require_once(HTML2PS_DIR.'background.image.php');
122
require_once(HTML2PS_DIR.'background.position.php');
123
 
124
require_once(HTML2PS_DIR.'list-style.image.php');
125
 
126
require_once(HTML2PS_DIR.'height.php');
127
require_once(HTML2PS_DIR.'width.php');
128
 
129
require_once(HTML2PS_DIR.'css.counter.php');
130
require_once(HTML2PS_DIR.'css.counter.collection.php');
131
 
132
require_once(HTML2PS_DIR.'css.colors.inc.php');
133
 
134
require_once(HTML2PS_DIR.'css.constants.inc.php');
135
require_once(HTML2PS_DIR.'css.inc.php');
136
require_once(HTML2PS_DIR.'css.state.class.php');
137
require_once(HTML2PS_DIR.'css.cache.class.php');
138
require_once(HTML2PS_DIR.'css.property.handler.class.php');
139
require_once(HTML2PS_DIR.'css.property.stringset.class.php');
140
require_once(HTML2PS_DIR.'css.property.sub.class.php');
141
require_once(HTML2PS_DIR.'css.property.sub.field.class.php');
142
require_once(HTML2PS_DIR.'css.utils.inc.php');
143
require_once(HTML2PS_DIR.'css.parse.inc.php');
144
require_once(HTML2PS_DIR.'css.parse.media.inc.php');
145
 
146
require_once(HTML2PS_DIR.'css.background.attachment.inc.php');
147
require_once(HTML2PS_DIR.'css.background.color.inc.php');
148
require_once(HTML2PS_DIR.'css.background.image.inc.php');
149
require_once(HTML2PS_DIR.'css.background.repeat.inc.php');
150
require_once(HTML2PS_DIR.'css.background.position.inc.php');
151
require_once(HTML2PS_DIR.'css.background.inc.php');
152
 
153
require_once(HTML2PS_DIR.'css.border.inc.php');
154
require_once(HTML2PS_DIR.'css.border.style.inc.php');
155
require_once(HTML2PS_DIR.'css.border.collapse.inc.php');
156
require_once(HTML2PS_DIR.'css.bottom.inc.php');
157
require_once(HTML2PS_DIR.'css.clear.inc.php');
158
require_once(HTML2PS_DIR.'css.color.inc.php');
159
require_once(HTML2PS_DIR.'css.direction.inc.php');
160
require_once(HTML2PS_DIR.'css.html2ps.html.content.inc.php');
161
require_once(HTML2PS_DIR.'css.html2ps.pseudoelements.inc.php');
162
require_once(HTML2PS_DIR.'css.html2ps.pixels.php');
163
require_once(HTML2PS_DIR.'css.content.inc.php');
164
require_once(HTML2PS_DIR.'css.display.inc.php');
165
require_once(HTML2PS_DIR.'css.float.inc.php');
166
require_once(HTML2PS_DIR.'css.font.inc.php');
167
require_once(HTML2PS_DIR.'css.height.inc.php');
168
require_once(HTML2PS_DIR.'css.min-height.inc.php');
169
require_once(HTML2PS_DIR.'css.max-height.inc.php');
170
require_once(HTML2PS_DIR.'css.left.inc.php');
171
require_once(HTML2PS_DIR.'css.letter-spacing.inc.php');
172
 
173
require_once(HTML2PS_DIR.'css.list-style-image.inc.php');
174
require_once(HTML2PS_DIR.'css.list-style-position.inc.php');
175
require_once(HTML2PS_DIR.'css.list-style-type.inc.php');
176
require_once(HTML2PS_DIR.'css.list-style.inc.php');
177
 
178
require_once(HTML2PS_DIR.'css.margin.inc.php');
179
require_once(HTML2PS_DIR.'css.overflow.inc.php');
180
require_once(HTML2PS_DIR.'css.padding.inc.php');
181
 
182
require_once(HTML2PS_DIR.'css.page.inc.php');
183
require_once(HTML2PS_DIR.'css.page-break.inc.php');
184
require_once(HTML2PS_DIR.'css.page-break-after.inc.php');
185
require_once(HTML2PS_DIR.'css.page-break-before.inc.php');
186
require_once(HTML2PS_DIR.'css.page-break-inside.inc.php');
187
require_once(HTML2PS_DIR.'css.orphans.inc.php');
188
require_once(HTML2PS_DIR.'css.size.inc.php');
189
require_once(HTML2PS_DIR.'css.widows.inc.php');
190
 
191
require_once(HTML2PS_DIR.'css.position.inc.php');
192
require_once(HTML2PS_DIR.'css.right.inc.php');
193
require_once(HTML2PS_DIR.'css.property.declaration.php');
194
require_once(HTML2PS_DIR.'css.rules.inc.php');
195
require_once(HTML2PS_DIR.'css.ruleset.class.php');
196
require_once(HTML2PS_DIR.'css.selectors.inc.php');
197
require_once(HTML2PS_DIR.'css.table-layout.inc.php');
198
require_once(HTML2PS_DIR.'css.text-align.inc.php');
199
require_once(HTML2PS_DIR.'css.text-decoration.inc.php');
200
require_once(HTML2PS_DIR.'css.text-transform.inc.php');
201
require_once(HTML2PS_DIR.'css.text-indent.inc.php');
202
require_once(HTML2PS_DIR.'css.top.inc.php');
203
require_once(HTML2PS_DIR.'css.vertical-align.inc.php');
204
require_once(HTML2PS_DIR.'css.visibility.inc.php');
205
require_once(HTML2PS_DIR.'css.white-space.inc.php');
206
require_once(HTML2PS_DIR.'css.width.inc.php');
207
require_once(HTML2PS_DIR.'css.word-spacing.inc.php');
208
require_once(HTML2PS_DIR.'css.z-index.inc.php');
209
 
210
require_once(HTML2PS_DIR.'css.pseudo.add.margin.inc.php');
211
require_once(HTML2PS_DIR.'css.pseudo.align.inc.php');
212
require_once(HTML2PS_DIR.'css.pseudo.cellspacing.inc.php');
213
require_once(HTML2PS_DIR.'css.pseudo.cellpadding.inc.php');
214
require_once(HTML2PS_DIR.'css.pseudo.form.action.inc.php');
215
require_once(HTML2PS_DIR.'css.pseudo.form.radiogroup.inc.php');
216
require_once(HTML2PS_DIR.'css.pseudo.link.destination.inc.php');
217
require_once(HTML2PS_DIR.'css.pseudo.link.target.inc.php');
218
require_once(HTML2PS_DIR.'css.pseudo.listcounter.inc.php');
219
require_once(HTML2PS_DIR.'css.pseudo.localalign.inc.php');
220
require_once(HTML2PS_DIR.'css.pseudo.nowrap.inc.php');
221
require_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
224
require_once(HTML2PS_DIR.'converter.class.php');
225
require_once(HTML2PS_DIR.'treebuilder.class.php');
226
require_once(HTML2PS_DIR.'image.class.php');
227
 
228
require_once(HTML2PS_DIR.'fetched_data._interface.class.php');
229
require_once(HTML2PS_DIR.'fetched_data._html.class.php');
230
require_once(HTML2PS_DIR.'fetched_data.url.class.php');
231
require_once(HTML2PS_DIR.'fetched_data.file.class.php');
232
 
233
require_once(HTML2PS_DIR.'filter.data._interface.class.php');
234
require_once(HTML2PS_DIR.'filter.data.doctype.class.php');
235
 
236
require_once(HTML2PS_DIR.'filter.data.utf8.class.php');
237
require_once(HTML2PS_DIR.'filter.data.ucs2.class.php');
238
 
239
require_once(HTML2PS_DIR.'filter.data.html2xhtml.class.php');
240
require_once(HTML2PS_DIR.'filter.data.xhtml2xhtml.class.php');
241
 
242
require_once(HTML2PS_DIR.'parser._interface.class.php');
243
require_once(HTML2PS_DIR.'parser.xhtml.class.php');
244
 
245
require_once(HTML2PS_DIR.'filter.pre._interface.class.php');
246
require_once(HTML2PS_DIR.'filter.pre.fields.class.php');
247
require_once(HTML2PS_DIR.'filter.pre.headfoot.class.php');
248
require_once(HTML2PS_DIR.'filter.pre.footnotes.class.php');
249
require_once(HTML2PS_DIR.'filter.pre.height-constraint.class.php');
250
 
251
require_once(HTML2PS_DIR.'layout._interface.class.php');
252
require_once(HTML2PS_DIR.'layout.default.class.php');
253
require_once(HTML2PS_DIR.'layout.page.breaks.php');
254
 
255
require_once(HTML2PS_DIR.'filter.post._interface.class.php');
256
require_once(HTML2PS_DIR.'filter.post.positioned.class.php');
257
require_once(HTML2PS_DIR.'filter.post.postponed.class.php');
258
 
259
require_once(HTML2PS_DIR.'filter.output._interface.class.php');
260
require_once(HTML2PS_DIR.'filter.output.ps2pdf.class.php');
261
require_once(HTML2PS_DIR.'filter.output.gzip.class.php');
262
 
263
require_once(HTML2PS_DIR.'destination._interface.class.php');
264
require_once(HTML2PS_DIR.'destination._http.class.php');
265
require_once(HTML2PS_DIR.'destination.browser.class.php');
266
require_once(HTML2PS_DIR.'destination.download.class.php');
267
require_once(HTML2PS_DIR.'destination.file.class.php');
268
 
269
require_once(HTML2PS_DIR.'xml.validation.inc.php');
270
 
271
require_once(HTML2PS_DIR.'content_type.class.php');
272
require_once(HTML2PS_DIR.'dispatcher.class.php');
273
require_once(HTML2PS_DIR.'observer.class.php');
274
 
275
require_once(HTML2PS_DIR.'strategy.page.break.simple.php');
276
require_once(HTML2PS_DIR.'strategy.page.break.smart.php');
277
 
278
require_once(HTML2PS_DIR.'strategy.link.rendering.normal.php');
279
require_once(HTML2PS_DIR.'strategy.position.absolute.php');
280
require_once(HTML2PS_DIR.'strategy.width.absolute.positioned.php');
281
require_once(HTML2PS_DIR.'autofix.url.php');
282
 
283
require_once(HTML2PS_DIR.'fetcher._interface.class.php');
284
require_once(HTML2PS_DIR.'features/_factory.php');
285
 
286
class 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
?>