1<?php
2
3class FeatureToc {
4  var $_anchor_locator;
5  var $_document_updater;
6
7  function FeatureToc() {
8    $this->set_anchor_locator(new FeatureTocAnchorLocatorHeaders());
9    $this->set_document_updater(new FeatureTocDocumentUpdaterPrependPage());
10  }
11
12  function handle_after_parse($params) {
13    $pipeline =& $params['pipeline'];
14    $document =& $params['document'];
15    $media =& $params['media'];
16
17    $toc =& $this->find_toc_anchors($pipeline, $media, $document);
18    $this->update_document($toc, $pipeline, $media, $document);
19  }
20
21  function handle_before_document($params) {
22    $pipeline =& $params['pipeline'];
23    $document =& $params['document'];
24    $media =& $params['media'];
25    $page_heights =& $params['page-heights'];
26
27    $toc =& $this->find_toc_anchors($pipeline, $media, $document);
28    $this->update_page_numbers($toc, $pipeline, $document, $page_heights, $media);
29  }
30
31  function &find_toc_anchors(&$pipeline, &$media, &$document) {
32    $locator =& $this->get_anchor_locator();
33    $toc =& $locator->run($pipeline, $media, $document);
34    return $toc;
35  }
36
37  function &get_anchor_locator() {
38    return $this->_anchor_locator;
39  }
40
41  function &get_document_updater() {
42    return $this->_document_updater;
43  }
44
45  function guess_page(&$element, $page_heights, &$media) {
46    $page_index = 0;
47    $bottom = mm2pt($media->height() - $media->margins['top']);
48    do {
49      $bottom -= $page_heights[$page_index];
50      $page_index ++;
51    } while ($element->get_top() < $bottom);
52
53    return $page_index;
54  }
55
56  function install(&$pipeline, $params) {
57    $dispatcher =& $pipeline->get_dispatcher();
58    $dispatcher->add_observer('after-parse', array(&$this, 'handle_after_parse'));
59    $dispatcher->add_observer('before-document', array(&$this, 'handle_before_document'));
60
61    if (isset($params['location'])) {
62      switch ($params['location']) {
63      case 'placeholder':
64        $this->set_document_updater(new FeatureTocDocumentUpdaterPlaceholder());
65        break;
66      case 'before':
67        $this->set_document_updater(new FeatureTocDocumentUpdaterPrependPage());
68        break;
69      case 'after':
70      default:
71        $this->set_document_updater(new FeatureTocDocumentUpdaterAppendPage());
72        break;
73      };
74    };
75  }
76
77  function set_anchor_locator(&$locator) {
78    $this->_anchor_locator =& $locator;
79  }
80
81  function set_document_updater(&$updater) {
82    $this->_document_updater =& $updater;
83  }
84
85  function make_toc_name_element_id($index) {
86    return sprintf('html2ps-toc-name-%d', $index);
87  }
88
89  function make_toc_page_element_id($index) {
90    return sprintf('html2ps-toc-page-%d', $index);
91  }
92
93  function update_document(&$toc, &$pipeline, &$media, &$document) {
94    $code = '';
95    $index = 1;
96    foreach ($toc as $toc_element) {
97      $code .= sprintf('
98<div id="html2ps-toc-%s" class="html2ps-toc-wrapper html2ps-toc-%d-wrapper">
99<div id="%s" class="html2ps-toc-name html2ps-toc-%d-name"><a href="#%s">%s</a></div>
100<div id="%s" class="html2ps-toc-page html2ps-toc-%d-page">0000</div>
101</div>%s',
102                       $index,
103                       $toc_element['level'],
104                       $this->make_toc_name_element_id($index),
105                       $toc_element['level'],
106                       $toc_element['anchor'],
107                       $toc_element['name'],
108                       $this->make_toc_page_element_id($index),
109                       $toc_element['level'],
110                       "\n");
111      $index++;
112    };
113
114    $toc_box_document =& $pipeline->parser->process('<body><div>'.$code.'</div></body>', $pipeline, $media);
115    $context =& new FlowContext();
116    $pipeline->layout_engine->process($toc_box_document, $media, $pipeline->get_output_driver(), $context);
117    $toc_box =& $toc_box_document->content[0];
118
119    $document_updater =& $this->get_document_updater();
120    $document_updater->run($toc_box, $media, $document);
121  }
122
123  function update_page_numbers(&$toc, &$pipeline, &$document, &$page_heights, &$media) {
124    for ($i = 0, $size = count($toc); $i < $size; $i++) {
125      $toc_element =& $document->get_element_by_id($this->make_toc_page_element_id($i+1));
126      $element =& $toc[$i]['element'];
127
128      $toc_element->content[0]->content[0]->words[0] = $this->guess_page($element, $page_heights, $media);
129    };
130  }
131}
132
133class FeatureTocAnchorLocatorHeaders {
134  var $_locations;
135  var $_last_generated_anchor_id;
136
137  function FeatureTocAnchorLocatorHeaders() {
138    $this->set_locations(array());
139    $this->_last_generated_anchor_id = 0;
140  }
141
142  function generate_toc_anchor_id() {
143    $this->_last_generated_anchor_id++;
144    $id = $this->_last_generated_anchor_id;
145    return sprintf('html2ps-toc-element-%d', $id);
146  }
147
148  function get_locations() {
149    return $this->_locations;
150  }
151
152  function process_node($params) {
153    $node =& $params['node'];
154
155    if (preg_match('/^h(\d)$/i', $node->get_tagname(), $matches)) {
156      if (!$node->get_id()) {
157        $id = $this->generate_toc_anchor_id();
158        $node->set_id($id);
159      };
160
161      $this->_locations[] = array('name' => $node->get_content(),
162                                  'level' => (int)$matches[1],
163                                  'anchor' => $node->get_id(),
164                                  'element' => &$node);
165    };
166  }
167
168  function &run(&$pipeline, &$media, &$document) {
169    $this->set_locations(array());
170    $walker =& new TreeWalkerDepthFirst(array(&$this, 'process_node'));
171    $walker->run($document);
172    $locations = $this->get_locations();
173
174    foreach ($locations as $location) {
175      $location['element']->setCSSProperty(CSS_HTML2PS_LINK_DESTINATION, $location['element']->get_id());
176
177      // $id = $location['element']->get_id();
178      // $pipeline->output_driver->anchors[$id] =& $location['element']->make_anchor($media, $id);
179    };
180
181    return $locations;
182  }
183
184  function set_locations($locations) {
185    $this->_locations = $locations;
186  }
187}
188
189class FeatureTocDocumentUpdaterAppendPage {
190  function FeatureTocDocumentUpdaterAppendPage() {
191  }
192
193  function run(&$toc_box, &$media, &$document) {
194    $toc_box->setCSSProperty(CSS_PAGE_BREAK_BEFORE, PAGE_BREAK_ALWAYS);
195    $document->append_child($toc_box);
196  }
197}
198
199class FeatureTocDocumentUpdaterPrependPage {
200  function FeatureTocDocumentUpdaterPrependPage() {
201  }
202
203  function run(&$toc_box, &$media, &$document) {
204    $toc_box->setCSSProperty(CSS_PAGE_BREAK_AFTER, PAGE_BREAK_ALWAYS);
205    $document->insert_before($toc_box, $document->content[0]);
206  }
207}
208
209class FeatureTocDocumentUpdaterPlaceholder {
210  function FeatureTocDocumentUpdaterPlaceholder() {
211  }
212
213  function run(&$toc_box, &$media, &$document) {
214    $placeholder =& $document->get_element_by_id('html2ps-toc');
215    $placeholder->append_child($toc_box);
216  }
217}
218
219?>