1: <?php
2: /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3: /**
4: * HTML renderer
5: *
6: * PHP versions 4 and 5
7: *
8: * LICENSE: This source file is subject to version 3.0 of the PHP license
9: * that is available through the world-wide-web at the following URI:
10: * http://www.php.net/license/3_0.txt. If you did not receive a copy of
11: * the PHP License and are unable to obtain it through the web, please
12: * send a note to license@php.net so we can mail you a copy immediately.
13: *
14: * @category Text
15: * @package Text_Highlighter
16: * @author Andrey Demenev <demenev@gmail.com>
17: * @copyright 2004-2006 Andrey Demenev
18: * @license http://www.php.net/license/3_0.txt PHP License
19: * @version CVS: $Id: Html.php,v 1.2 2007/06/29 06:56:34 ssttoo Exp $
20: * @link http://pear.php.net/package/Text_Highlighter
21: */
22:
23: /**
24: * @ignore
25: */
26:
27: require_once dirname(__FILE__).'/../Renderer.php';
28: require_once dirname(__FILE__).'/../Renderer/Array.php';
29:
30: // BC trick : only define constants if Text/Highlighter.php
31: // is not yet included
32: if (!defined('HL_NUMBERS_LI')) {
33: /**#@+
34: * Constant for use with $options['numbers']
35: */
36: /**
37: * use numbered list, deprecated, use HL_NUMBERS_OL instaed
38: * @deprecated
39: */
40: define ('HL_NUMBERS_LI' , 1);
41: /**
42: * Use 2-column table with line numbers in left column and code in right column.
43: */
44: define ('HL_NUMBERS_TABLE' , 2);
45: /**#@-*/
46: }
47:
48:
49: /**#@+
50: * Constant for use with $options['numbers']
51: */
52: /**
53: * Use numbered list
54: */
55: define ('HL_NUMBERS_OL', 1);
56: /**
57: * Use non-numbered list
58: */
59: define ('HL_NUMBERS_UL', 3);
60: /**#@-*/
61:
62:
63: /**
64: * HTML renderer
65: *
66: * Elements of $options argument of constructor (each being optional):
67: *
68: * - 'numbers' - Line numbering style 0 or {@link HL_NUMBERS_TABLE}
69: * or {@link HL_NUMBERS_UL} or {@link HL_NUMBERS_OL}
70: * - 'numbers_start' - starting number for numbered lines
71: * - 'tabsize' - Tab size
72: * - 'style_map' - Mapping of keywords to formatting rules using inline styles
73: * - 'class_map' - Mapping of keywords to formatting rules using class names
74: * - 'doclinks' - array that has keys "url", "target" and "elements", used for
75: * generating links to online documentation
76: * - 'use_language' - class names will be prefixed with language, like "php-reserved" or "css-code"
77: *
78: * Example of setting documentation links:
79: * $options['doclinks'] = array(
80: * 'url' => 'http://php.net/%s',
81: * 'target' => '_blank',
82: * 'elements' => array('reserved', 'identifier')
83: * );
84: *
85: * Example of setting class names map:
86: * $options['class_map'] = array(
87: * 'main' => 'my-main',
88: * 'table' => 'my-table',
89: * 'gutter' => 'my-gutter',
90: * 'brackets' => 'my-brackets',
91: * 'builtin' => 'my-builtin',
92: * 'code' => 'my-code',
93: * 'comment' => 'my-comment',
94: * 'default' => 'my-default',
95: * 'identifier' => 'my-identifier',
96: * 'inlinedoc' => 'my-inlinedoc',
97: * 'inlinetags' => 'my-inlinetags',
98: * 'mlcomment' => 'my-mlcomment',
99: * 'number' => 'my-number',
100: * 'quotes' => 'my-quotes',
101: * 'reserved' => 'my-reserved',
102: * 'special' => 'my-special',
103: * 'string' => 'my-string',
104: * 'url' => 'my-url',
105: * 'var' => 'my-var',
106: * );
107: *
108: * Example of setting styles mapping:
109: * $options['style_map'] = array(
110: * 'main' => 'color: black',
111: * 'table' => 'border: 1px solid black',
112: * 'gutter' => 'background-color: yellow',
113: * 'brackets' => 'color: blue',
114: * 'builtin' => 'color: red',
115: * 'code' => 'color: green',
116: * 'comment' => 'color: orange',
117: * // ....
118: * );
119: *
120: *
121: * @author Andrey Demenev <demenev@gmail.com>
122: * @category Text
123: * @package Text_Highlighter
124: * @copyright 2004-2006 Andrey Demenev
125: * @license http://www.php.net/license/3_0.txt PHP License
126: * @version Release: 0.7.1
127: * @link http://pear.php.net/package/Text_Highlighter
128: */
129:
130: class Text_Highlighter_Renderer_Html extends Text_Highlighter_Renderer_Array
131: {
132:
133: /**#@+
134: * @access private
135: */
136:
137: /**
138: * Line numbering style
139: *
140: * @var integer
141: */
142: var $_numbers = 0;
143:
144: /**
145: * For numberered lines - where to start
146: *
147: * @var integer
148: */
149: var $_numbers_start = 0;
150:
151: /**
152: * Tab size
153: *
154: * @var integer
155: */
156: var $_tabsize = 4;
157:
158: /**
159: * Highlighted code
160: *
161: * @var string
162: */
163: var $_output = '';
164:
165: /**
166: * Mapping of keywords to formatting rules using inline styles
167: *
168: * @var array
169: */
170: var $_style_map = array();
171:
172: /**
173: * Mapping of keywords to formatting rules using class names
174: *
175: * @var array
176: */
177: var $_class_map = array(
178: 'main' => 'hl-main',
179: 'table' => 'hl-table',
180: 'gutter' => 'hl-gutter',
181: 'brackets' => 'hl-brackets',
182: 'builtin' => 'hl-builtin',
183: 'code' => 'hl-code',
184: 'comment' => 'hl-comment',
185: 'default' => 'hl-default',
186: 'identifier' => 'hl-identifier',
187: 'inlinedoc' => 'hl-inlinedoc',
188: 'inlinetags' => 'hl-inlinetags',
189: 'mlcomment' => 'hl-mlcomment',
190: 'number' => 'hl-number',
191: 'quotes' => 'hl-quotes',
192: 'reserved' => 'hl-reserved',
193: 'special' => 'hl-special',
194: 'string' => 'hl-string',
195: 'url' => 'hl-url',
196: 'var' => 'hl-var',
197: );
198:
199: /**
200: * Setup for links to online documentation
201: *
202: * This is an array with keys:
203: * - url, ex. http://php.net/%s
204: * - target, ex. _blank, default - no target
205: * - elements, default is <code>array('reserved', 'identifier')</code>
206: *
207: * @var array
208: */
209: var $_doclinks = array();
210:
211: /**#@-*/
212:
213: /**
214: * Resets renderer state
215: *
216: * @access protected
217: *
218: *
219: * Descendents of Text_Highlighter call this method from the constructor,
220: * passing $options they get as parameter.
221: */
222: function reset()
223: {
224: $this->_output = '';
225: if (isset($this->_options['numbers'])) {
226: $this->_numbers = (int)$this->_options['numbers'];
227: if ($this->_numbers != HL_NUMBERS_LI
228: && $this->_numbers != HL_NUMBERS_UL
229: && $this->_numbers != HL_NUMBERS_OL
230: && $this->_numbers != HL_NUMBERS_TABLE
231: ) {
232: $this->_numbers = 0;
233: }
234: }
235: if (isset($this->_options['tabsize'])) {
236: $this->_tabsize = $this->_options['tabsize'];
237: }
238: if (isset($this->_options['numbers_start'])) {
239: $this->_numbers_start = intval($this->_options['numbers_start']);
240: }
241: if (isset($this->_options['doclinks']) &&
242: is_array($this->_options['doclinks']) &&
243: !empty($this->_options['doclinks']['url'])
244: ) {
245:
246: $this->_doclinks = $this->_options['doclinks']; // keys: url, target, elements array
247:
248: if (empty($this->_options['doclinks']['elements'])) {
249: $this->_doclinks['elements'] = array('reserved', 'identifier');
250: }
251: }
252: if (isset($this->_options['style_map'])) {
253: $this->_style_map = $this->_options['style_map'];
254: }
255: if (isset($this->_options['class_map'])) {
256: $this->_class_map = array_merge($this->_class_map, $this->_options['class_map']);
257: }
258: $this->_htmlspecialchars = true;
259:
260: }
261:
262:
263: /**
264: * Given a CSS class name, returns the class name
265: * with language name prepended, if necessary
266: *
267: * @access private
268: *
269: * @param string $class Token class
270: */
271: function _getFullClassName($class)
272: {
273: if (!empty($this->_options['use_language'])) {
274: $the_class = $this->_language . '-' . $class;
275: } else {
276: $the_class = $class;
277: }
278: return $the_class;
279: }
280:
281: /**
282: * Signals that no more tokens are available
283: *
284: * @access public
285: */
286: function finalize()
287: {
288:
289: // get parent's output
290: parent::finalize();
291: $output = parent::getOutput();
292: if(empty($output))
293: return;
294:
295: $html_output = '';
296:
297: $numbers_li = false;
298:
299: if (
300: $this->_numbers == HL_NUMBERS_LI ||
301: $this->_numbers == HL_NUMBERS_UL ||
302: $this->_numbers == HL_NUMBERS_OL
303: )
304: {
305: $numbers_li = true;
306: }
307:
308: // loop through each class=>content pair
309: foreach ($output AS $token) {
310:
311: if ($this->_enumerated) {
312: $key = false;
313: $the_class = $token[0];
314: $content = $token[1];
315: } else {
316: $key = key($token);
317: $the_class = $key;
318: $content = $token[$key];
319: }
320:
321: $span = $this->_getStyling($the_class);
322: $decorated_output = $this->_decorate($content, $key);
323: //print "<pre> token = ".var_export($token, true)." -- span = " . htmlentities($span). "-- deco = ".$decorated_output."</pre>\n";
324: $html_output .= sprintf($span, $decorated_output);
325: }
326:
327: // format lists
328: if (!empty($this->_numbers) && $numbers_li == true) {
329:
330: //$html_output = "<pre>".$html_output."</pre>";
331: // additional whitespace for browsers that do not display
332: // empty list items correctly
333: $this->_output = '<li><pre> ' . str_replace("\n", "</pre></li>\n<li><pre> ", $html_output) . '</pre></li>';
334:
335:
336: $start = '';
337: if ($this->_numbers == HL_NUMBERS_OL && intval($this->_numbers_start) > 0) {
338: $start = ' start="' . $this->_numbers_start . '"';
339: }
340:
341: $list_tag = 'ol';
342: if ($this->_numbers == HL_NUMBERS_UL) {
343: $list_tag = 'ul';
344: }
345:
346:
347: $this->_output = '<' . $list_tag . $start
348: . ' ' . $this->_getStyling('main', false) . '>'
349: . $this->_output . '</'. $list_tag .'>';
350:
351: // render a table
352: } else if ($this->_numbers == HL_NUMBERS_TABLE) {
353:
354:
355: $start_number = 0;
356: if (intval($this->_numbers_start)) {
357: $start_number = $this->_numbers_start - 1;
358: }
359:
360: $numbers = '';
361:
362: $nlines = substr_count($html_output,"\n")+1;
363: for ($i=1; $i <= $nlines; $i++) {
364: $numbers .= ($start_number + $i) . "\n";
365: }
366: $this->_output = '<table ' . $this->_getStyling('table', false) . ' width="100%"><tr>' .
367: '<td '. $this->_getStyling('gutter', false) .' align="right" valign="top">' .
368: '<pre>' . $numbers . '</pre></td><td '. $this->_getStyling('main', false) .
369: ' valign="top"><pre>' .
370: $html_output . '</pre></td></tr></table>';
371: }
372: if (!$this->_numbers) {
373: $this->_output = '<pre>' . $html_output . '</pre>';
374: }
375: $this->_output = '<div ' . $this->_getStyling('main', false) . '>' . $this->_output . '</div>';
376: }
377:
378:
379: /**
380: * Provides additional formatting to a keyword
381: *
382: * @param string $content Keyword
383: * @return string Keyword with additional formatting
384: * @access public
385: *
386: */
387: function _decorate($content, $key = false)
388: {
389: // links to online documentation
390: if (!empty($this->_doclinks) &&
391: !empty($this->_doclinks['url']) &&
392: in_array($key, $this->_doclinks['elements'])
393: ) {
394:
395: $link = '<a href="'. sprintf($this->_doclinks['url'], $content) . '"';
396: if (!empty($this->_doclinks['target'])) {
397: $link.= ' target="' . $this->_doclinks['target'] . '"';
398: }
399: $link .= '>';
400: $link.= $content;
401: $link.= '</a>';
402:
403: $content = $link;
404:
405: }
406:
407: return $content;
408: }
409:
410: /**
411: * Returns <code>class</code> and/or <code>style</code> attribute,
412: * optionally enclosed in a <code>span</code> tag
413: *
414: * @param string $class Class name
415: * @paran boolean $span_tag Whether or not to return styling attributes in a <code>>span<</code> tag
416: * @return string <code>span</code> tag or just a <code>class</code> and/or <code>style</code> attributes
417: * @access private
418: */
419: function _getStyling($class, $span_tag = true)
420: {
421: $attrib = '';
422: if (!empty($this->_style_map) &&
423: !empty($this->_style_map[$class])
424: ) {
425: $attrib = 'style="'. $this->_style_map[$class] .'"';
426: }
427: if (!empty($this->_class_map) &&
428: !empty($this->_class_map[$class])
429: ) {
430: if ($attrib) {
431: $attrib .= ' ';
432: }
433: $attrib .= 'class="'. $this->_getFullClassName($this->_class_map[$class]) .'"';
434: }
435:
436: if ($span_tag) {
437: $span = '<span ' . $attrib . '>%s</span>';
438: return $span;
439: } else {
440: return $attrib;
441: }
442:
443: }
444: }
445:
446: /*
447: * Local variables:
448: * tab-width: 4
449: * c-basic-offset: 4
450: * c-hanging-comment-ender-p: nil
451: * End:
452: */
453:
454: ?>