1: <?php
2: 3: 4: 5:
6:
7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22:
23: class Horde_Text_Textile {
24:
25: 26: 27:
28: const REGEX_A_HLGN = '(?:\<(?!>)|(?<!<)\>|\<\>|\=|[()]+(?! ))';
29:
30: 31: 32:
33: const REGEX_A_VLGN = '[\-^~]';
34:
35: 36: 37:
38: const REGEX_A = '(?:(?:\<(?!>)|(?<!<)\>|\<\>|\=|[()]+(?! ))|[\-^~])*';
39:
40: 41: 42:
43: const REGEX_S = '(?:(?:\\\\\d+)|(?:\/\d+))*';
44:
45: 46: 47:
48: const REGEX_C = '(?:(?:\([^)]+\))|(?:\{[^}]+\})|(?:\[[^]]+\])|(?:\<(?!>)|(?<!<)\>|\<\>|\=|[()]+(?! )))*';
49:
50: 51: 52:
53: const REGEX_PUNCT = '\!"#\$%&\'\*\+,-\.\/\:;\=\?@\\\^_`\|~';
54:
55: 56: 57:
58: const REGEX_URL = '[\w"$\-_.+!*\'(),";\/?:@=&%#{}|\\^~\[\]`]';
59:
60: 61: 62:
63: const REGEX_BLOCK_TAGS = 'bq|bc|notextile|pre|h[1-6]|fn\d+|p';
64:
65: 66: 67: 68:
69: public static $GLYPH_QUOTE_SINGLE_OPEN = '‘';
70: public static $GLYPH_QUOTE_SINGLE_CLOSE = '’';
71: public static $GLYPH_QUOTE_DOUBLE_OPEN = '“';
72: public static $GLYPH_QUOTE_DOUBLE_CLOSE = '”';
73: public static $GLYPH_APOSTROPHE = '’';
74: public static $GLYPH_PRIME = '′';
75: public static $GLYPH_PRIME_DOUBLE = '″';
76: public static $GLYPH_ELLIPSIS = '…';
77: public static $GLYPH_EMDASH = '—';
78: public static $GLYPH_ENDASH = '–';
79: public static $GLYPH_DIMENSION = '×';
80: public static $GLYPH_TRADEMARK = '™';
81: public static $GLYPH_REGISTERED = '®';
82: public static $GLYPH_COPYRIGHT = '©';
83: public static $GLYPH_RETURN_ARROW = '↩';
84:
85: 86: 87: 88: 89:
90: public $images = true;
91:
92: 93: 94: 95: 96:
97: public $rel = '';
98:
99: 100: 101: 102: 103:
104: protected $_shelf = array();
105:
106: public function transform($text, $rel = '')
107: {
108: if ($rel) {
109: $this->rel = ' rel="' . $rel . '" ';
110: }
111:
112: $text = $this->cleanWhiteSpace($text);
113: $text = $this->getRefs($text);
114: $text = $this->block($text);
115:
116: return $this->retrieve($text);
117: }
118:
119: 120: 121: 122:
123: public function parseBlockAttributes($in, $element = '')
124: {
125: $style = '';
126: $class = '';
127: $lang = '';
128: $colspan = '';
129: $rowspan = '';
130: $id = '';
131: $atts = '';
132:
133: if (!empty($in)) {
134: $matched = $in;
135: if ($element == 'td') {
136: if (preg_match("/\\\\(\d+)/", $matched, $csp)) $colspan = $csp[1];
137: if (preg_match("/\/(\d+)/", $matched, $rsp)) $rowspan = $rsp[1];
138: }
139:
140: if ($element == 'td' || $element == 'tr') {
141: if (preg_match('/(' . self::REGEX_A_VLGN . ')/', $matched, $vert))
142: $style[] = 'vertical-align:' . $this->vAlign($vert[1]) . ';';
143: }
144:
145: if (preg_match('/\{([^}]*)\}/', $matched, $sty)) {
146: $style[] = rtrim($sty[1], ';') . ';';
147: $matched = str_replace($sty[0], '', $matched);
148: }
149:
150: if (preg_match('/\[([^]]+)\]/U', $matched, $lng)) {
151: $lang = $lng[1];
152: $matched = str_replace($lng[0], '', $matched);
153: }
154:
155: if (preg_match('/\(([^()]+)\)/U', $matched, $cls)) {
156: $class = $cls[1];
157: $matched = str_replace($cls[0], '', $matched);
158: }
159:
160: if (preg_match('/([(]+)/', $matched, $pl)) {
161: $style[] = 'padding-left:' . strlen($pl[1]) . 'em;';
162: $matched = str_replace($pl[0], '', $matched);
163: }
164:
165: if (preg_match('/([)]+)/', $matched, $pr)) {
166: $style[] = 'padding-right:' . strlen($pr[1]) . 'em;';
167: $matched = str_replace($pr[0], '', $matched);
168: }
169:
170: if (preg_match('/(' . self::REGEX_A_HLGN . ')/', $matched, $horiz)) {
171: $style[] = 'text-align:' . $this->hAlign($horiz[1]) . ';';
172: }
173:
174: if (preg_match('/^(.*)#(.*)$/', $class, $ids)) {
175: $id = $ids[2];
176: $class = $ids[1];
177: }
178:
179: return
180: ($style ? ' style="' . implode('', $style) . '"' : '')
181: . ($class ? ' class="' . $class . '"' : '')
182: . ($lang ? ' lang="' . $lang . '"' : '')
183: . ($id ? ' id="' . $id . '"' : '')
184: . ($colspan ? ' colspan="' . $colspan . '"' : '')
185: . ($rowspan ? ' rowspan="' . $rowspan . '"' : '');
186: }
187:
188: return '';
189: }
190:
191: 192: 193:
194: public function hasRawText($text)
195: {
196:
197:
198: $r = trim(preg_replace('@<(p|blockquote|div|form|table|ul|ol|pre|h\d)[^>]*?>.*</\1>@s', '', trim($text)));
199: $r = trim(preg_replace('@<\/?(p|blockquote|div|form|table|ul|ol|pre|h\d)[^>]*?\/?>@s', '', $r));
200: $r = trim(preg_replace('@<(hr|br)[^>]*?/>@', '', $r));
201: return '' != $r;
202: }
203:
204: 205: 206:
207: public function table($text)
208: {
209: $text = $text . "\n\n";
210: return preg_replace_callback("/^(?:table(_?" . self::REGEX_S . self::REGEX_A . self::REGEX_C . ")\. ?\n)?^(" . self::REGEX_A . self::REGEX_C . "\.? ?\|.*\|)\n\n/smU",
211: array($this, 'fTable'), $text);
212: }
213:
214: 215: 216:
217: public function fTable($matches)
218: {
219: $tatts = $this->parseBlockAttributes($matches[1], 'table');
220:
221: foreach (preg_split("/\|$/m", $matches[2], -1, PREG_SPLIT_NO_EMPTY) as $row) {
222: if (preg_match("/^(" . self::REGEX_A . self::REGEX_C . "\. )(.*)/m", ltrim($row), $rmtch)) {
223: $ratts = $this->parseBlockAttributes($rmtch[1], 'tr');
224: $row = $rmtch[2];
225: } else {
226: $ratts = '';
227: }
228: $cells = array();
229: foreach (explode('|', $row) as $cell) {
230: $ctyp = 'd';
231: if (preg_match("/^_/", $cell)) {
232: $ctyp = 'h';
233: }
234: if (preg_match("/^(_?" . self::REGEX_S . self::REGEX_A . self::REGEX_C . "\. )(.*)/", $cell, $cmtch)) {
235: $catts = $this->parseBlockAttributes($cmtch[1], 'td');
236: $cell = $cmtch[2];
237: } else {
238: $catts = '';
239: }
240:
241: $cell = $this->paragraph($this->span($cell));
242: if (trim($cell) != '') {
243: $cells[] = "\t\t\t<t$ctyp$catts>$cell</t$ctyp>";
244: }
245: }
246: $rows[] = "\t\t<tr$ratts>\n" . implode("\n", $cells) . ($cells ? "\n" : '') . "\t\t</tr>";
247: unset($cells, $catts);
248: }
249: return "\t<table$tatts>\n" . implode("\n", $rows) . "\n\t</table>\n\n";
250: }
251:
252: 253: 254:
255: public function lists($text)
256: {
257: return preg_replace_callback("/^([#*]+" . self::REGEX_C . ".*)$(?![^#*])/smU", array($this, 'fList'), $text);
258: }
259:
260: 261: 262:
263: public function fList($m)
264: {
265: $out = array();
266: $lines = explode("\n", $m[0]);
267: for ($i = 0, $i_max = count($lines); $i < $i_max; $i++) {
268: $line = $lines[$i];
269: $nextline = isset($lines[$i + 1]) ? $lines[$i + 1] : false;
270:
271: if (preg_match("/^([#*]+)(" . self::REGEX_A . self::REGEX_C . ") (.*)$/s", $line, $m)) {
272: list(, $tl, $atts, $content) = $m;
273: $nl = '';
274: if (preg_match("/^([#*]+)\s.*/", $nextline, $nm)) {
275: $nl = $nm[1];
276: }
277: $level = strlen($tl);
278: if (!isset($lists[$tl])) {
279: $lists[$tl] = true;
280: $atts = $this->parseBlockAttributes($atts);
281: $line = str_repeat("\t", $level) . '<' . $this->lT($tl) . "l$atts>\n" . str_repeat("\t", $level + 1) . '<li>' . $this->paragraph($content);
282: } else {
283: $line = str_repeat("\t", $level + 1) . '<li>' . $this->paragraph($content);
284: }
285:
286: if (strlen($nl) <= strlen($tl)) {
287: $line .= '</li>';
288: }
289: foreach (array_reverse($lists) as $k => $v) {
290: if (strlen($k) > strlen($nl)) {
291: $line .= "\n" . str_repeat("\t", $level--) . '</' . $this->lT($k) . 'l>';
292: if (strlen($k) > 1) {
293: $line .= '</li>';
294: }
295: unset($lists[$k]);
296: }
297: }
298: }
299:
300: $out[] = $line;
301: }
302:
303: return implode("\n", $out);
304: }
305:
306: 307: 308:
309: public function lT($in)
310: {
311: return substr($in, 0, 1) == '#' ? 'o' : 'u';
312: }
313:
314: 315: 316:
317: public function doPBr($in)
318: {
319: return preg_replace_callback('@<(p)([^>]*?)>(.*)(</\1>)@s', array($this, 'doBr'), $in);
320: }
321:
322: 323: 324:
325: public function doBr($m)
326: {
327: $content = preg_replace("@(.+)(?<!<br>|<br />)\n(?![#*\s|])@", "\$1<br />\n", $m[3]);
328: return '<' . $m[1] . $m[2] . '>' . $content . $m[4];
329: }
330:
331: 332: 333:
334: public function block($text)
335: {
336: $tag = 'p';
337: $atts = $cite = $graf = $ext = '';
338:
339: $text = explode("\n\n", $text);
340: foreach ($text as $line) {
341: $anon = 0;
342: if (preg_match('/^(' . self::REGEX_BLOCK_TAGS . ')(' . self::REGEX_A . self::REGEX_C . ')\.(\.?)(?::(\S+))? (.*)$/s', $line, $m)) {
343: if ($ext) {
344:
345: $out[count($out) - 1] .= $c1;
346: }
347:
348: list(, $tag, $atts, $ext, $cite, $graf) = $m;
349: list($o1, $o2, $content, $c2, $c1) = $this->fBlock(array(0, $tag, $atts, $ext, $cite, $graf));
350:
351:
352:
353: if ($ext) {
354: $line = $o1 . $o2 . $content . $c2;
355: } else {
356: $line = $o1 . $o2 . $content . $c2 . $c1;
357: }
358: } else {
359:
360: $anon = 1;
361: if ($ext || !preg_match('/^ /', $line)) {
362: list($o1, $o2, $content, $c2, $c1) = $this->fBlock(array(0, $tag, $atts, $ext, $cite, $line));
363:
364:
365: if ($tag == 'p' && !$this->hasRawText($content)) {
366: $line = $content;
367: } else {
368: $line = $o2 . $content . $c2;
369: }
370: } else {
371: $line = $this->paragraph($line);
372: }
373: }
374:
375: $line = preg_replace('/<br>/', '<br />', $this->doPBr($line));
376:
377: if ($ext && $anon) {
378: $out[count($out) - 1] .= "\n" . $line;
379: } else {
380: $out[] = $line;
381: }
382:
383: if (!$ext) {
384: $tag = 'p';
385: $atts = '';
386: $cite = '';
387: $graf = '';
388: }
389: }
390: if ($ext) {
391: $out[count($out) - 1] .= $c1;
392: }
393: return implode("\n\n", $out);
394: }
395:
396: 397: 398:
399: public function fBlock($m)
400: {
401: list(, $tag, $atts, $ext, $cite, $content) = $m;
402: $atts = $this->parseBlockAttributes($atts);
403:
404: $o1 = $o2 = $c2 = $c1 = '';
405:
406: if (preg_match('/fn(\d+)/', $tag, $fns)) {
407: $tag = 'p';
408: $fnid = $fns[1];
409: $atts .= ' id="fn' . $fnid . '"';
410: $content = '<sup>' . $fns[1] . '</sup> ' . $content . ' <a href="#fnr' . $fnid . '">' . self::$GLYPH_RETURN_ARROW . '</a>';
411: }
412:
413: if ($tag == 'bq') {
414: $cite = $this->checkRefs($cite);
415: $cite = ($cite != '') ? ' cite="' . $cite . '"' : '';
416: $o1 = '<blockquote' . $cite . $atts . ">\n";
417: $o2 = "<p$atts>";
418: $c2 = '</p>';
419: $c1 = "\n</blockquote>";
420: } elseif ($tag == 'bc') {
421: $o1 = "<pre$atts>";
422: $o2 = "<code$atts>";
423: $c2 = '</code>';
424: $c1 = '</pre>';
425: $content = $this->shelve($this->encodeHtml(rtrim($content, "\n") . "\n"));
426: } elseif ($tag == 'notextile') {
427: $content = $this->shelve($content);
428: $o1 = $o2 = '';
429: $c1 = $c2 = '';
430: } elseif ($tag == 'pre') {
431: $content = $this->shelve($this->encodeHtml(rtrim($content, "\n") . "\n"));
432: $o1 = "<pre$atts>";
433: $o2 = $c2 = '';
434: $c1 = '</pre>';
435: } else {
436: $o2 = "<$tag$atts>";
437: $c2 = "</$tag>";
438: }
439:
440: return array($o1, $o2, $this->paragraph($content), $c2, $c1);
441: }
442:
443: 444: 445: 446:
447: public function paragraph($text)
448: {
449: $text = $this->code($this->noTextile($text));
450: $text = $this->links($text);
451: if ($this->images) {
452: $text = $this->image($text);
453: }
454:
455: $text = $this->table($this->lists($text));
456: $text = $this->glyphs($this->footnoteRef($this->span($text)));
457: return rtrim($text, "\n");
458: }
459:
460: 461: 462:
463: public function span($text)
464: {
465: $qtags = array('\*\*', '\*', '\?\?', '-', '__', '_', '%', '\+', '~', '\^');
466: $pnct = ".,\"'?!;:";
467:
468: foreach ($qtags as $f) {
469: $text = preg_replace_callback("/
470: (?:^|(?<=[\s>$pnct])|([{[]))
471: ($f)(?!$f)
472: (" . self::REGEX_C . ")
473: (?::(\S+))?
474: ([^\s$f]+|\S[^$f\n]*[^\s$f\n])
475: ([$pnct]*)
476: $f
477: (?:$|([\]}])|(?=[[:punct:]]{1,2}|\s))
478: /x", array($this, 'fSpan'), $text);
479: }
480: return $text;
481: }
482:
483: 484: 485:
486: public function fSpan($m)
487: {
488: $qtags = array(
489: '*' => 'strong',
490: '**' => 'b',
491: '??' => 'cite',
492: '_' => 'em',
493: '__' => 'i',
494: '-' => 'del',
495: '%' => 'span',
496: '+' => 'ins',
497: '~' => 'sub',
498: '^' => 'sup',
499: );
500:
501: list(, , $tag, $atts, $cite, $content, $end) = $m;
502: $tag = $qtags[$tag];
503: $atts = $this->parseBlockAttributes($atts)
504: . ($cite ? 'cite="' . $cite . '"' : '');
505:
506: return "<$tag$atts>$content$end</$tag>";
507: }
508:
509: 510: 511:
512: public function links($text)
513: {
514: $punct = preg_quote('!"#$%&\'*+,-./:;=?@\\^_`|~', '/');
515: return preg_replace_callback('/
516: (^|(?<=[\s>.' . self::REGEX_PUNCT . '\(])|([{[])) # $pre
517: " # $start
518: (' . self::REGEX_C . ') # $atts
519: ([^"]+) # $text
520: \s?
521: (?:\(([^)]+)\)(?="))? # $title
522: ":
523: (' . self::REGEX_URL . '+) # $url
524: (\/)? # $slash
525: ([^\w\/;]*) # $post
526: (?:([\]}])|(?=\s|$|\)))
527: /Ux', array($this, 'fLink'), $text);
528: }
529:
530: 531: 532:
533: public function fLink($m)
534: {
535: list(, $pre, $start, $atts, $text, $title, $url, $slash, $post) = $m;
536:
537: $atts = $this->parseBlockAttributes($atts)
538: . ($title != '') ? ' title="' . $this->encodeHtml($title) . '"' : '';
539:
540: if ($this->images) {
541: $text = $this->image($text);
542: }
543: $text = $this->glyphs($this->span($text));
544:
545: $url = $this->checkRefs($url);
546:
547: return $this->shelve('<a href="'
548: . $this->encodeHtml($url . $slash)
549: . '"' . $atts . ($this->rel ? ' rel="' . $this->rel . '" ' : '') . '>'
550: . $text . '</a>' . $post);
551: }
552:
553: 554: 555:
556: public function getRefs($text)
557: {
558: return preg_replace_callback("/(?<=^|\s)\[(.+)\]((?:http:\/\/|\/)\S+)(?=\s|$)/U",
559: array($this, 'refs'), $text);
560: }
561:
562: 563: 564:
565: public function refs($m)
566: {
567: list(, $flag, $url) = $m;
568: $this->urlrefs[$flag] = $url;
569: return '';
570: }
571:
572: 573: 574:
575: public function checkRefs($text)
576: {
577: return isset($this->urlrefs[$text]) ? $this->urlrefs[$text] : $text;
578: }
579:
580: 581: 582:
583: public function image($text)
584: {
585: return preg_replace_callback("/
586: (?:[[{])? # pre
587: \! # opening !
588: (\<|\=|\>)?? # optional alignment attributes
589: (" . self::REGEX_C . ") # optional style, class attributes
590: (?:\. )? # optional dot-space
591: ([^\s(!]+) # presume this is the src
592: \s? # optional space
593: (?:\(([^\)]+)\))? # optional title
594: \! # closing
595: (?::(\S+))? # optional href
596: (?:[\]}]|(?=\s|$)) # lookahead: space or end of string
597: /Ux", array($this, 'fImage'), $text);
598: }
599:
600: 601: 602:
603: public function fImage($m)
604: {
605: list(, $algn, $atts, $url) = $m;
606: $title = isset($m[4]) ? $m[4] : '';
607: $atts = $this->parseBlockAttributes($atts)
608: . ($algn != '' ? ' align="' . $this->iAlign($algn) . '"' : '')
609: . ($title ? ' title="' . $title . '"' : '')
610: . ' alt="' . $title . '"';
611:
612: $href = isset($m[5]) ? $this->checkRefs($m[5]) : '';
613: $url = $this->checkRefs($url);
614:
615: return ($href ? '<a href="' . $href . '">' : '')
616: . '<img src="' . $url . '"' . $atts . ' />'
617: . ($href ? '</a>' : '');
618: }
619:
620: 621: 622:
623: public function code($text)
624: {
625: $text = $this->doSpecial($text, '<code>', '</code>', 'fCode');
626: $text = $this->doSpecial($text, '@', '@', 'fCode');
627: $text = $this->doSpecial($text, '<pre>', '</pre>', 'fPre');
628: return $text;
629: }
630:
631: 632: 633:
634: public function fCode($m)
635: {
636: @list(, $before, $text, $after) = $m;
637: return $before . $this->shelve('<code>' . $this->encodeHtml($text, false) . '</code>') . $after;
638: }
639:
640: 641: 642:
643: public function fPre($m)
644: {
645: @list(, $before, $text, $after) = $m;
646: return $before . '<pre>' . $this->shelve($this->encodeHtml($text, false)) . '</pre>' . $after;
647: }
648:
649: 650: 651:
652: public function shelve($val)
653: {
654: $i = uniqid(mt_rand());
655: $this->_shelf[$i] = $val;
656: return $i;
657: }
658:
659: 660: 661:
662: public function retrieve($text)
663: {
664: if (is_array($this->_shelf)) {
665: do {
666: $old = $text;
667: $text = strtr($text, $this->_shelf);
668: } while ($text != $old);
669: }
670: return $text;
671: }
672:
673: 674: 675:
676: public function cleanWhiteSpace($text)
677: {
678: return preg_replace(array("/\r\n/", "/\n{3,}/", "/\n *\n/"),
679: array("\n", "\n\n", "\n\n"),
680: $text);
681: }
682:
683: 684: 685:
686: public function doSpecial($text, $start, $end, $method = 'fSpecial')
687: {
688: return preg_replace_callback('/(^|\s|[[({>])' . preg_quote($start, '/') . '(.*?)' . preg_quote($end, '/') . '(\s|$|[\])}])?/ms',
689: array($this, $method), $text);
690: }
691:
692: 693: 694:
695: public function fSpecial($m)
696: {
697:
698: @list(, $before, $text, $after) = $m;
699: return $before . $this->shelve($this->encodeHtml($text)) . $after;
700: }
701:
702: 703: 704:
705: public function noTextile($text)
706: {
707: $text = $this->doSpecial($text, '<notextile>', '</notextile>', 'fTextile');
708: return $this->doSpecial($text, '==', '==', 'fTextile');
709: }
710:
711: 712: 713:
714: public function fTextile($m)
715: {
716: @list(, $before, $notextile, $after) = $m;
717: return $before . $this->shelve($notextile) . $after;
718: }
719:
720: 721: 722:
723: public function ($text)
724: {
725: return preg_replace('/\b\[([0-9]+)\](\s)?/U',
726: '<sup><a id="fnr$1" href="#fn$1">$1</a></sup>$2',
727: $text);
728: }
729:
730: 731: 732:
733: public function glyphs($text)
734: {
735: $glyph_search = array(
736: '/(\w)\'(\w)/',
737: '/(\s)\'(\d+\w?)\b(?!\')/',
738: '/(\S)\'(?=\s|[[:punct:]]|<|$)/',
739: '/\'/',
740: '/(\S)\"(?=\s|[[:punct:]]|<|$)/',
741: '/"/',
742: '/\b([A-Z][A-Z0-9]{2,})\b(?:[(]([^)]*)[)])/',
743: '/\b([A-Z][A-Z\'\-]+[A-Z])(?=[\s.,\)>])/',
744: '/\b( )?\.{3}/',
745: '/(\s?)--(\s?)/',
746: '/\s-(?:\s|$)/',
747: '/(\d+)( ?)x( ?)(?=\d+)/',
748: '/\b ?[([]TM[])]/i',
749: '/\b ?[([]R[])]/i',
750: '/\b ?[([]C[])]/i',
751: );
752:
753: $glyph_replace = array(
754: '$1' . self::$GLYPH_APOSTROPHE . '$2',
755: '$1' . self::$GLYPH_APOSTROPHE . '$2',
756: '$1' . self::$GLYPH_QUOTE_SINGLE_CLOSE,
757: self::$GLYPH_QUOTE_SINGLE_OPEN,
758: '$1' . self::$GLYPH_QUOTE_DOUBLE_CLOSE,
759: self::$GLYPH_QUOTE_DOUBLE_OPEN,
760: '<acronym title="$2">$1</acronym>',
761: '<span class="caps">$1</span>',
762: '$1' . self::$GLYPH_ELLIPSIS,
763: self::$GLYPH_EMDASH,
764: ' ' . self::$GLYPH_ENDASH . ' ',
765: '$1' . self::$GLYPH_DIMENSION,
766: self::$GLYPH_TRADEMARK,
767: self::$GLYPH_REGISTERED,
768: self::$GLYPH_COPYRIGHT,
769: );
770:
771: $text = preg_split('/(<.*>)/U', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
772: foreach ($text as $line) {
773: if (!preg_match('/<.*>/', $line)) {
774: $line = preg_replace($glyph_search, $glyph_replace, $line);
775: }
776: $glyph_out[] = $line;
777: }
778:
779: return implode('', $glyph_out);
780: }
781:
782: 783: 784:
785: public function iAlign($in)
786: {
787: $vals = array(
788: '<' => 'left',
789: '=' => 'center',
790: '>' => 'right');
791: return isset($vals[$in]) ? $vals[$in] : '';
792: }
793:
794: 795: 796:
797: public function hAlign($in)
798: {
799: $vals = array(
800: '<' => 'left',
801: '=' => 'center',
802: '>' => 'right',
803: '<>' => 'justify');
804: return isset($vals[$in]) ? $vals[$in] : '';
805: }
806:
807: 808: 809:
810: public function vAlign($in)
811: {
812: $vals = array(
813: '^' => 'top',
814: '-' => 'middle',
815: '~' => 'bottom');
816: return isset($vals[$in]) ? $vals[$in] : '';
817: }
818:
819: 820: 821:
822: public function encodeHtml($str, $quotes = true)
823: {
824: $a = array(
825: '&' => '&',
826: '<' => '<',
827: '>' => '>',
828: );
829: if ($quotes) {
830: $a = $a + array(
831: "'" => ''',
832: '"' => '"',
833: );
834: }
835:
836: return strtr($str, $a);
837: }
838:
839: }
840: