1 : <?php
2 :
3 : /**
4 : * \Midi\Reporting\HtmlFormatter
5 : *
6 : * @package Midi
7 : * @subpackage Reporting
8 : * @copyright © 2009 Tommy Montgomery <http://phpmidiparser.com/>
9 : * @since 1.0
10 : */
11 :
12 : namespace Midi\Reporting;
13 :
14 : use \Midi\Chunk;
15 : use \Midi\Delta;
16 : use \Midi\FileHeader;
17 : use \Midi\TrackHeader;
18 : use \Midi\Version;
19 : use \Midi\Event;
20 : use \Midi\Event\ChannelEvent;
21 : use \Midi\Event\MetaEvent;
22 : use \Midi\Util\Util;
23 :
24 : /**
25 : * Formats parse results into HTML
26 : *
27 : * @package Midi
28 : * @subpackage Reporting
29 : * @since 1.0
30 : */
31 1 : class HtmlFormatter extends Formatter {
32 :
33 : /**
34 : * The number of the current track
35 : *
36 : * @var int
37 : */
38 : protected $currentTrack;
39 :
40 : /**
41 : * @var int
42 : */
43 : protected $offset;
44 :
45 : /**
46 : * @var bool
47 : */
48 : private $multiFile;
49 :
50 : private $delta;
51 :
52 : /**
53 : * @since 1.0
54 : */
55 : public function __construct() {
56 23 : $this->currentTrack = 0;
57 23 : $this->offset = 0;
58 23 : $this->multiFile = false;
59 23 : $this->delta = null;
60 23 : }
61 :
62 : /**
63 : * Sets whether this formatter is for multiple HTML files
64 : * or a single HTML file
65 : *
66 : * @since 1.0
67 : *
68 : * @param bool $multiFile
69 : */
70 : public function setMultiFile($multiFile) {
71 2 : $this->multiFile = (bool)$multiFile;
72 2 : }
73 :
74 : /**
75 : * Formats the file pointer offset
76 : *
77 : * @since 1.0
78 : *
79 : * @param int $offset
80 : * @return string
81 : */
82 : public function formatOffset($offset) {
83 1 : $offset = '0x' . strtoupper(str_pad(dechex($offset), 8, '0', STR_PAD_LEFT));
84 1 : return "<td><tt>$offset</tt></td>";
85 : }
86 :
87 : /**
88 : * @since 1.0
89 : *
90 : * @return string
91 : */
92 : public function beforeFile() {
93 2 : $page = ($this->multiFile) ? '<div id="page"> </div>' : '';
94 2 : $title = ($this->multiFile) ? 'page' : 'track';
95 :
96 : return <<<HTML
97 : <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
98 : "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
99 :
100 : <html xmlns="http://www.w3.org/1999/xhtml" lang="en">
101 : <head>
102 : <title>MIDI Parse Results</title>
103 : <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
104 : <link type="text/css" href="./midiparser.css" rel="stylesheet"/>
105 : <script type="text/javascript" src="./midiparser.js"></script>
106 : </head>
107 :
108 : <body>
109 : $page
110 : <div id="wrapper">
111 : <div id="nav">
112 2 : <div class="nav" id="nav-previous" title="previous $title"></div>
113 2 : <div class="nav" id="nav-next" title="next $title"></div>
114 : </div>
115 : <div id="content">
116 : <table id="parse-results">
117 2 :
118 2 : HTML;
119 : }
120 :
121 : /**
122 : * @since 1.0
123 : * @uses getChunkClass()
124 : * @uses formatOffset()
125 : * @uses Util::binaryToHex()
126 : * @uses Chunk::toBinary()
127 : * @uses Chunk::getLength()
128 : *
129 : * @param Chunk $chunk
130 : * @return string
131 : */
132 : public function beforeChunk(Chunk $chunk) {
133 8 : $text = '';
134 :
135 8 : if ($chunk instanceof Delta) {
136 4 : $this->delta = $chunk;
137 8 : } else if (!($chunk instanceof Event)) {
138 2 : $class = $this->getChunkClass($chunk);
139 2 : $text = "<tr class=\"$class\">" . $this->formatOffset($this->offset);
140 2 : $text .= '<td><tt>' . wordwrap(implode(' ', Util::binaryToHex($chunk->toBinary())), 23, '<br />') . '</tt></td>';
141 2 : $this->offset += $chunk->getLength();
142 2 : }
143 :
144 8 : return $text;
145 : }
146 :
147 : /**
148 : * Gets the CSS class for the chunk type
149 : *
150 : * @since 1.0
151 : *
152 : * @param Chunk $chunk
153 : * @return string
154 : */
155 : private function getChunkClass(Chunk $chunk) {
156 4 : if ($chunk instanceof ChannelEvent) {
157 1 : return 'channel';
158 3 : } else if ($chunk instanceof MetaEvent) {
159 1 : return 'meta';
160 : } else {
161 2 : return 'header';
162 : }
163 : }
164 :
165 : /**
166 : * @since 1.0
167 : *
168 : * @param Chunk $chunk
169 : * @return string
170 : */
171 : public function afterChunk(Chunk $chunk) {
172 1 : return $chunk instanceof Delta ? '' : '</tr>';
173 : }
174 :
175 : /**
176 : * @since 1.0
177 : *
178 : * @return string
179 : */
180 : public function beforeFileHeader(FileHeader $fileHeader) {
181 1 : return "\t\t\t\t\t<tbody>\n";
182 : }
183 :
184 : /**
185 : * @since 1.0
186 : * @uses FileHeader::getData()
187 : *
188 : * @param FileHeader $fileHeader
189 : * @return string
190 : */
191 : public function formatFileHeader(FileHeader $fileHeader) {
192 1 : list($format, $tracks, $timeDivision) = $fileHeader->getData();
193 1 : $text = '<td><a name="file-header"></a>';
194 1 : $text .= "MIDI file format: $format, # of tracks: $tracks, Time division: $timeDivision";
195 1 : $text .= '</td>';
196 1 : return $text;
197 : }
198 :
199 : /**
200 : * @since 1.0
201 : *
202 : * @param FileHeader $fileHeader
203 : * @return string
204 : */
205 : public function afterFileHeader(FileHeader $fileHeader) {
206 1 : return "\t\t\t\t\t</tbody>\n";
207 : }
208 :
209 : /**
210 : * @since 1.0
211 : *
212 : * @return string
213 : */
214 : public function beforeTrack() {
215 1 : $this->currentTrack++;
216 1 : $track = 'track' . $this->currentTrack;
217 : return <<<HTML
218 : <tbody>
219 : <tr class="trackheader">
220 : <td colspan="3">
221 1 : <a class="track" id="$track" name="$track"></a>
222 1 : Track $this->currentTrack
223 : </td>
224 : </tr>
225 : </tbody>
226 : <tbody>
227 1 :
228 1 : HTML;
229 : }
230 :
231 : /**
232 : * @since 1.0
233 : * @uses TrackHeader::getData()
234 : *
235 : * @param TrackHeader $trackHeader
236 : * @return string
237 : */
238 : public function formatTrackHeader(TrackHeader $trackHeader) {
239 1 : list($size) = $trackHeader->getData();
240 1 : return "<td>Track size: $size bytes</td>";
241 : }
242 :
243 : /**
244 : * @since 1.0
245 : * @uses getChunkClass()
246 : * @uses formatOffset()
247 : * @uses Delta::toBinary()
248 : * @uses Util::binaryToHex()
249 : * @uses Event::toBinary()
250 : * @uses Event::getLength()
251 : * @uses Delta::getLength()
252 : *
253 : * @param Event $event
254 : * @return string
255 : */
256 : public function beforeEvent(Event $event) {
257 3 : if ($this->delta === null) {
258 1 : return '';
259 : }
260 :
261 2 : $class = $this->getChunkClass($event);
262 2 : $text = "<tr class=\"$class\">" . $this->formatOffset($this->offset);
263 :
264 2 : $deltaHex = Util::binaryToHex($this->delta->toBinary());
265 2 : $delta = '<span class="delta">' . wordwrap(implode(' ', $deltaHex), 23, '<br />') . '</span>';
266 :
267 2 : $eventHex = Util::binaryToHex($event->toBinary());
268 :
269 2 : $lineLength = 23 - (strlen(implode(' ', $deltaHex)) % 23);
270 :
271 2 : $eventSegment = wordwrap(implode(' ', $eventHex), $lineLength, '|');
272 2 : $bar = strpos($eventSegment, '|');
273 2 : if ($bar !== false) {
274 2 : $eventSegment = substr($eventSegment, 0, $bar) . '<br />' . wordwrap(substr($eventSegment, $bar + 1), 23, '<br />');
275 2 : }
276 :
277 2 : $text .= '<td><tt>' . $delta . ' ' . $eventSegment . '</tt></td>';
278 2 : $this->offset += $event->getLength() + $this->delta->getLength();
279 2 : return $text;
280 : }
281 :
282 : /**
283 : * @since 1.0
284 : * @uses Event::__toString()
285 : *
286 : * @param Event $event
287 : * @return string
288 : */
289 : public function formatEvent(Event $event) {
290 2 : if ($this->delta === null) {
291 1 : return '';
292 : }
293 :
294 1 : list($ticks) = $this->delta->getData();
295 1 : return '<td><span class="delta">[' . $ticks . ' ticks]</span> ' . (string)$event . '</td>';
296 : }
297 :
298 : /**
299 : * @since 1.0
300 : *
301 : * @return string
302 : */
303 : public function afterTrack() {
304 1 : return "\t\t\t\t\t</tbody>\n";
305 : }
306 :
307 : /**
308 : * @since 1.0
309 : *
310 : * @param float $parseTime
311 : * @param float $totalTime
312 : * @return string
313 : */
314 : public function afterFile($parseTime, $totalTime) {
315 2 : if ($this->multiFile) {
316 1 : $parseTime = '';
317 1 : } else {
318 1 : $parseTime = ' in ' . round($parseTime, 3) . ' seconds';
319 : }
320 :
321 2 : $date = date('M j, Y g:i:s A');
322 2 : $name = Version::NAME . ' ' . Version::VERSION;
323 2 : $author = Version::AUTHOR;
324 :
325 : return <<<HTML
326 : </table>
327 : </div>
328 :
329 : <div id="footer">
330 : <p>
331 2 : Generated by <a title="$name" href="http://phpmidiparser.com/">$name</a>$parseTime on $date
332 : <br />
333 2 : © 2009 <a title="by $author" href="http://tommymontgomery.com/">$author</a>
334 : </p>
335 : </div>
336 : </div>
337 : </body>
338 2 : </html>
339 2 : HTML;
340 : }
341 :
342 : /**
343 : * @since 1.0
344 : *
345 : * @return HtmlPostProcessor
346 : */
347 : public function getPostProcessor() {
348 1 : return new HtmlPostProcessor();
349 : }
350 :
351 : }
352 :
|