Almost-minimal filesystem based blog.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3500 lines
105 KiB

  1. # $Id: Textile.pm,v 1.16 2004/02/19 14:52:58 brad Exp $
  2. package Text::Textile;
  3. use strict;
  4. use Exporter;
  5. @Text::Textile::ISA = qw(Exporter);
  6. use vars qw(@EXPORT_OK $VERSION $debug);
  7. @EXPORT_OK = qw(textile);
  8. $VERSION = 2.03;
  9. $debug = 0;
  10. sub new {
  11. my $class = shift;
  12. my %options = @_;
  13. $options{filters} ||= {};
  14. $options{charset} ||= 'iso-8859-1';
  15. $options{char_encoding} = 1 unless exists $options{char_encoding};
  16. $options{do_quotes} = 1 unless exists $options{do_quotes};
  17. $options{trim_spaces} = 0 unless exists $options{trim_spaces};
  18. $options{smarty_mode} = 1 unless exists $options{smarty_mode};
  19. $options{preserve_spaces} = 0 unless exists $options{preserve_spaces};
  20. $options{head_offset} = 0 unless exists $options{head_offset};
  21. my $self = bless \%options, $class;
  22. if (exists $options{css}) {
  23. $self->css($options{css});
  24. }
  25. $options{macros} ||= $self->default_macros();
  26. if (exists $options{flavor}) {
  27. $self->flavor($options{flavor});
  28. } else {
  29. $self->flavor('xhtml1/css');
  30. }
  31. $self;
  32. }
  33. # getter/setter methods...
  34. sub set {
  35. my $self = shift;
  36. my $opt = shift;
  37. if (ref $opt eq 'HASH') {
  38. $self->set($_, $opt->{$_}) foreach %$opt;
  39. } else {
  40. my $value = shift;
  41. # the following options have special set methods
  42. # that activate upon setting:
  43. if ($opt eq 'charset') {
  44. $self->charset($value);
  45. } elsif ($opt eq 'css') {
  46. $self->css($value);
  47. } elsif ($opt eq 'flavor') {
  48. $self->flavor($value);
  49. } else {
  50. $self->{$opt} = $value;
  51. }
  52. }
  53. }
  54. sub get {
  55. my $self = shift;
  56. return $self->{shift} if @_;
  57. undef;
  58. }
  59. sub disable_html {
  60. my $self = shift;
  61. if (@_) {
  62. $self->{disable_html} = shift;
  63. }
  64. $self->{disable_html} || 0;
  65. }
  66. sub head_offset {
  67. my $self = shift;
  68. if (@_) {
  69. $self->{head_offset} = shift;
  70. }
  71. $self->{head_offset} || 0;
  72. }
  73. sub flavor {
  74. my $self = shift;
  75. if (@_) {
  76. my $flavor = shift;
  77. $self->{flavor} = $flavor;
  78. if ($flavor =~ m/^xhtml(\d)?(\D|$)/) {
  79. if ($1 eq '2') {
  80. $self->{_line_open} = '<l>';
  81. $self->{_line_close} = '</l>';
  82. $self->{_blockcode_open} = '<blockcode>';
  83. $self->{_blockcode_close} = '</blockcode>';
  84. $self->{css_mode} = 1;
  85. } else {
  86. # xhtml 1.x
  87. $self->{_line_open} = '';
  88. # Brennen modified this very line, yes indeed.
  89. $self->{_line_close} = '';
  90. $self->{_blockcode_open} = '<pre><code>';
  91. $self->{_blockcode_close} = '</code></pre>';
  92. $self->{css_mode} = 1;
  93. }
  94. } elsif ($flavor =~ m/^html/) {
  95. $self->{_line_open} = '';
  96. $self->{_line_close} = '<br>';
  97. $self->{_blockcode_open} = '<pre><code>';
  98. $self->{_blockcode_close} = '</code></pre>';
  99. $self->{css_mode} = $flavor =~ m/\/css/;
  100. }
  101. $self->_css_defaults() if $self->{css_mode} && !exists $self->{css};
  102. }
  103. $self->{flavor};
  104. }
  105. sub css {
  106. my $self = shift;
  107. if (@_) {
  108. my $css = shift;
  109. if (ref $css eq 'HASH') {
  110. $self->{css} = $css;
  111. $self->{css_mode} = 1;
  112. } else {
  113. $self->{css_mode} = $css;
  114. $self->_css_defaults() if $self->{css_mode} && !exists $self->{css};
  115. }
  116. }
  117. $self->{css_mode} ? $self->{css} : 0;
  118. }
  119. sub charset {
  120. my $self = shift;
  121. if (@_) {
  122. $self->{charset} = shift;
  123. if ($self->{charset} =~ m/^utf-?8$/i) {
  124. $self->char_encoding(0);
  125. } else {
  126. $self->char_encoding(1);
  127. }
  128. }
  129. $self->{charset};
  130. }
  131. sub docroot {
  132. my $self = shift;
  133. $self->{docroot} = shift if @_;
  134. $self->{docroot};
  135. }
  136. sub trim_spaces {
  137. my $self = shift;
  138. $self->{trim_spaces} = shift if @_;
  139. $self->{trim_spaces};
  140. }
  141. sub filter_param {
  142. my $self = shift;
  143. $self->{filter_param} = shift if @_;
  144. $self->{filter_param};
  145. }
  146. sub preserve_spaces {
  147. my $self = shift;
  148. $self->{preserve_spaces} = shift if @_;
  149. $self->{preserve_spaces};
  150. }
  151. sub filters {
  152. my $self = shift;
  153. $self->{filters} = shift if @_;
  154. $self->{filters};
  155. }
  156. sub char_encoding {
  157. my $self = shift;
  158. $self->{char_encoding} = shift if @_;
  159. $self->{char_encoding};
  160. }
  161. sub handle_quotes {
  162. my $self = shift;
  163. $self->{do_quotes} = shift if @_;
  164. $self->{do_quotes};
  165. }
  166. # end of getter/setter methods
  167. # a URL discovery regex. This is from Mastering Regex from O'Reilly.
  168. # Some modifications by Brad Choate <brad@bradchoate.com>
  169. use vars qw($urlre $blocktags $clstyre $clstypadre $clstyfiltre
  170. $alignre $valignre $halignre $imgalignre $tblalignre
  171. $codere $punct);
  172. $urlre = qr{
  173. # Must start out right...
  174. (?=[a-zA-Z0-9./#])
  175. # Match the leading part (proto://hostname, or just hostname)
  176. (?:
  177. # ftp://, http://, or https:// leading part
  178. (?:ftp|https?|telnet|nntp)://(?:\w+(?::\w+)?@)?[-\w]+(?:\.\w[-\w]*)+
  179. |
  180. (?:mailto:)?[-\+\w]+\@[-\w]+(?:\.\w[-\w]*)+
  181. |
  182. # or, try to find a hostname with our more specific sub-expression
  183. (?i: [a-z0-9] (?:[-a-z0-9]*[a-z0-9])? \. )+ # sub domains
  184. # Now ending .com, etc. For these, require lowercase
  185. (?-i: com\b
  186. | edu\b
  187. | biz\b
  188. | gov\b
  189. | in(?:t|fo)\b # .int or .info
  190. | mil\b
  191. | net\b
  192. | org\b
  193. | museum\b
  194. | aero\b
  195. | coop\b
  196. | name\b
  197. | pro\b
  198. | [a-z][a-z]\b # two-letter country codes
  199. )
  200. )?
  201. # Allow an optional port number
  202. (?: : \d+ )?
  203. # The rest of the URL is optional, and begins with / . . .
  204. (?:
  205. /?
  206. # The rest are heuristics for what seems to work well
  207. [^.!,?;:"'<>()\[\]{}\s\x7F-\xFF]*
  208. (?:
  209. [.!,?;:]+ [^.!,?;:"'<>()\[\]{}\s\x7F-\xFF]+ #'"
  210. )*
  211. )?
  212. }x;
  213. $punct = qr{[\!"#\$%&'()\*\+,\-\./:;<=>\?@\[\\\]\^_`{\|}\~]};
  214. $valignre = qr/[\-^~]/;
  215. $tblalignre = qr/[<>=]/;
  216. $halignre = qr/(?:<>|[<>=])/;
  217. $alignre = qr/(?:$valignre|<>$valignre?|$valignre?<>|$valignre?$halignre?|$halignre?$valignre?)(?!\w)/;
  218. $imgalignre = qr/(?:[<>]|$valignre){1,2}/;
  219. $clstypadre = qr/
  220. (?:\([A-Za-z0-9_\- \#]+\))
  221. |
  222. (?:{
  223. (?: \( [^)]+ \) | [^}] )+
  224. })
  225. |
  226. (?:\(+? (?![A-Za-z0-9_\-\#]) )
  227. |
  228. (?:\)+?)
  229. |
  230. (?: \[ [a-zA-Z\-]+? \] )
  231. /x;
  232. $clstyre = qr!
  233. (?:\([A-Za-z0-9_\- \#]+\))
  234. |
  235. (?:{
  236. [A-Za-z0-9_\-](?: \( [^)]+ \) | [^}] )+
  237. })
  238. |
  239. (?: \[ [a-zA-Z\-]+? \] )
  240. !x;
  241. $clstyfiltre = qr/
  242. (?:\([A-Za-z0-9_\- \#]+\))
  243. |
  244. (?:{
  245. [A-Za-z0-9_\-](?: \( [^)]+ \) | [^}] )+
  246. })
  247. |
  248. (?:\|[^\|]+\|)
  249. |
  250. (?:\(+?(?![A-Za-z0-9_\-\#]))
  251. |
  252. (?:\)+)
  253. |
  254. (?: \[ [a-zA-Z]+? \] )
  255. /x;
  256. $codere = qr!
  257. (?:
  258. [\[{]
  259. @ # opening
  260. (?:\[([A-Za-z0-9]+)\])? # $1: language id
  261. (.+?) # $2: code
  262. @ # closing
  263. [\]}]
  264. )
  265. |
  266. (?:
  267. (?:^|(?<=[\s\(]))
  268. @ # opening
  269. (?:\[([A-Za-z0-9]+)\])? # $3: language id
  270. ([^\s].+?[^\s]) # $4: code itself
  271. @ # closing
  272. (?:$|(?=$punct{1,2}|\s))
  273. )
  274. !x;
  275. $blocktags = qr{
  276. <
  277. (( /? ( h[1-6]
  278. | p
  279. | pre
  280. | div
  281. | table
  282. | t[rdh]
  283. | [ou]l
  284. | li
  285. | block(?:quote|code)
  286. | form
  287. | input
  288. | select
  289. | option
  290. | textarea
  291. )
  292. [ >]
  293. )
  294. | !--
  295. )
  296. }x;
  297. sub process {
  298. my $self = shift;
  299. $self->textile(@_);
  300. }
  301. sub textile {
  302. my $self = shift;
  303. my ($str) = @_;
  304. # disable warnings for the sake of various regex that
  305. # have optional matches
  306. local $^W = 0;
  307. if (!ref $self) {
  308. # oops -- procedural technique used, so make
  309. # set $str to $self and instantiate a new object
  310. # for self
  311. $str = $self;
  312. $self = new Text::Textile;
  313. }
  314. # quick translator for abbreviated block names
  315. # to their tag
  316. my %macros = ('bq' => 'blockquote');
  317. # an array to hold any portions of the text to be preserved
  318. # without further processing by Textile
  319. my @repl;
  320. # strip out extra newline characters. we're only matching for \n herein
  321. #$str =~ s!(?:\r?\n|\r)!\n!g;
  322. $str =~ s!(?:\015?\012|\015)!\n!g;
  323. # optionally remove trailing spaces
  324. $str =~ s/ +$//gm if $self->{trim_spaces};
  325. # preserve contents of the '==', 'pre', 'blockcode' sections
  326. $str =~ s{(^|\n\n)==(.+?)==($|\n\n)}
  327. {$1."\n\n"._repl(\@repl, $self->format_block(text => $2))."\n\n".$3}ges;
  328. unless ($self->{disable_html}) {
  329. # preserve style, script tag contents
  330. $str =~ s!(<(style|script)(?:>| .+?>).*?</\2>)!_repl(\@repl, $1)!ges;
  331. # preserve HTML comments
  332. $str =~ s|(<!--.+?-->)|_repl(\@repl, $1)|ges;
  333. # preserve pre block contents, encode contents by default
  334. my $pre_start = scalar(@repl);
  335. $str =~ s{(<pre(?: [^>]*)?>)(.+?)(</pre>)}
  336. {"\n\n"._repl(\@repl, $1.$self->encode_html($2, 1).$3)."\n\n"}ges;
  337. # fix code tags within pre blocks we just saved.
  338. for (my $i = $pre_start; $i < scalar(@repl); $i++) {
  339. $repl[$i] =~ s|&lt;(/?)code(.*?)&gt;|<$1code$2>|gs;
  340. }
  341. # preserve code blocks by default, encode contents
  342. $str =~ s{(<code(?: [^>]+)?>)(.+?)(</code>)}
  343. {_repl(\@repl, $1.$self->encode_html($2, 1).$3)}ges;
  344. # encode blockcode tag (an XHTML 2 tag) and encode it's
  345. # content by default
  346. $str =~ s{(<blockcode(?: [^>]+)?>)(.+?)(</blockcode>)}
  347. {"\n\n"._repl(\@repl, $1.$self->encode_html($2, 1).$3)."\n\n"}ges;
  348. # preserve PHPish, ASPish code
  349. $str =~ s!(<([\?\%]).*?(\2)>)!_repl(\@repl, $1)!ges;
  350. }
  351. # pass through and remove links that follow this format
  352. # [id_without_spaces (optional title text)]url
  353. # lines like this are stripped from the content, and can be
  354. # referred to using the "link text":id_without_spaces syntax
  355. my %links;
  356. $str =~ s{(?:\n|^) [ ]* \[ ([^ ]+?) [ ]*? (?:\( (.+?) \) )? \] ((?:(?:ftp|https?|telnet|nntp)://|/)[^ ]+?) [ ]* (\n|$)}
  357. {($links{$1} = {url => $3, title => $2}),"$4"}gemx;
  358. local $self->{links} = \%links;
  359. # eliminate starting/ending blank lines
  360. $str =~ s/^\n+//s;
  361. $str =~ s/\n+$//s;
  362. # split up text into paragraph blocks, capturing newlines too
  363. my @para = split /(\n{2,})/, $str;
  364. my ($block, $bqlang, $filter, $class, $sticky, @lines,
  365. $style, $stickybuff, $lang, $clear);
  366. my $out = '';
  367. foreach my $para (@para) {
  368. if ($para =~ m/^\n+$/s) {
  369. if ($sticky && defined $stickybuff) {
  370. $stickybuff .= $para;
  371. } else {
  372. $out .= $para;
  373. }
  374. next;
  375. }
  376. if ($sticky) {
  377. $sticky++;
  378. } else {
  379. $block = undef;
  380. $class = undef;
  381. $style = '';
  382. $lang = undef;
  383. }
  384. my ($id, $cite, $align, $padleft, $padright, @lines, $buffer);
  385. if ($para =~ m/^(h[1-6]|p|bq|bc|fn\d+)
  386. ((?:$clstyfiltre*|$halignre)*)
  387. (\.\.?)
  388. (?::(\d+|$urlre))?\ /gx) {
  389. if ($sticky) {
  390. if ($block eq 'bc') {
  391. # close our blockcode section
  392. $out =~ s/\n\n$//;
  393. $out .= $self->{_blockcode_close}."\n\n";
  394. } elsif ($block eq 'bq') {
  395. $out =~ s/\n\n$//;
  396. $out .= '</blockquote>'."\n\n";
  397. } elsif ($block eq 'table') {
  398. my $table_out = $self->format_table(text => $stickybuff);
  399. $table_out = '' if !defined $table_out;
  400. $out .= $table_out;
  401. $stickybuff = undef;
  402. } elsif ($block eq 'dl') {
  403. my $dl_out = $self->format_deflist(text => $stickybuff);
  404. $dl_out = '' if !defined $dl_out;
  405. $out .= $dl_out;
  406. $stickybuff = undef;
  407. }
  408. $sticky = 0;
  409. }
  410. # block macros: h[1-6](class)., bq(class)., bc(class)., p(class).
  411. #warn "paragraph: [[$para]]\n\tblock: $1\n\tparams: $2\n\tcite: $4";
  412. $block = $1;
  413. my $params = $2;
  414. $cite = $4;
  415. if ($3 eq '..') {
  416. $sticky = 1;
  417. } else {
  418. $sticky = 0;
  419. $class = undef;
  420. $bqlang = undef;
  421. $lang = undef;
  422. $style = '';
  423. $filter = undef;
  424. }
  425. if ($block =~ m/^h([1-6])$/) {
  426. if ($self->{head_offset}) {
  427. $block = 'h' . ($1 + $self->{head_offset});
  428. }
  429. }
  430. if ($params =~ m/($halignre+)/) {
  431. $align = $1;
  432. $params =~ s/$halignre+//;
  433. }
  434. if (defined $params) {
  435. if ($params =~ m/\|(.+)\|/) {
  436. $filter = $1;
  437. $params =~ s/\|.+?\|//;
  438. }
  439. if ($params =~ m/{([^}]+)}/) {
  440. $style = $1;
  441. $style =~ s/\n/ /g;
  442. $params =~ s/{[^}]+}//g;
  443. }
  444. if ($params =~ m/\(([A-Za-z0-9_\-\ ]+?)(?:\#(.+?))?\)/ ||
  445. $params =~ m/\(([A-Za-z0-9_\-\ ]+?)?(?:\#(.+?))\)/) {
  446. if ($1 || $2) {
  447. $class = $1;
  448. $id = $2;
  449. if ($class) {
  450. $params =~ s/\([A-Za-z0-9_\-\ ]+?(#.*?)?\)//g;
  451. } elsif ($id) {
  452. $params =~ s/\(#.+?\)//g;
  453. }
  454. }
  455. }
  456. if ($params =~ m/(\(+)/) {
  457. $padleft = length($1);
  458. $params =~ s/\(+//;
  459. }
  460. if ($params =~ m/(\)+)/) {
  461. $padright = length($1);
  462. $params =~ s/\)+//;
  463. }
  464. if ($params =~ m/\[(.+?)\]/) {
  465. $lang = $1;
  466. if ($block eq 'bc') {
  467. $bqlang = $lang;
  468. $lang = undef;
  469. }
  470. $params =~ s/\[.+?\]//;
  471. }
  472. }
  473. #warn "settings:\n\tblock: $block\n\tpadleft: $padleft\n\tpadright: $padright\n\tclass: $class\n\tstyle: $style\n\tid: $id\n\tfilter: $filter\n\talign: $align\n\tlang: $lang\n\tsticky: $sticky";
  474. $para = substr($para, pos($para));
  475. } elsif ($para =~ m|^<textile#(\d+)>$|) {
  476. $buffer = $repl[$1-1];
  477. } elsif ($para =~ m/^clear([<>]+)?\.$/) {
  478. if ($1 eq '<') {
  479. $clear = 'left';
  480. } elsif ($1 eq '>') {
  481. $clear = 'right';
  482. } else {
  483. $clear = 'both';
  484. }
  485. next;
  486. } elsif ($sticky && (defined $stickybuff) &&
  487. ($block eq 'table' || $block eq 'dl')) {
  488. $stickybuff .= $para;
  489. next;
  490. } elsif ($para =~ m/^(?:$halignre|$clstypadre*)*
  491. [\*\#]
  492. (?:$halignre|$clstypadre*)*
  493. \ /x) {
  494. # '*', '#' prefix means a list
  495. $buffer = $self->format_list(text => $para);
  496. } elsif ($para =~ m/^(?:table(?:$tblalignre|$clstypadre*)*
  497. (\.\.?)\s+)?
  498. (?:_|$alignre|$clstypadre*)*\|/x) {
  499. # handle wiki-style tables
  500. if (defined $1 && ($1 eq '..')) {
  501. $block = 'table';
  502. $stickybuff = $para;
  503. $sticky = 1;
  504. next;
  505. } else {
  506. $buffer = $self->format_table(text => $para);
  507. }
  508. } elsif ($para =~ m/^(?:dl(?:$clstyre)*(\.\.?)\s+)/) {
  509. # handle definition lists
  510. if (defined $1 && ($1 eq '..')) {
  511. $block = 'dl';
  512. $stickybuff = $para;
  513. $sticky = 1;
  514. next;
  515. } else {
  516. $buffer = $self->format_deflist(text => $para);
  517. }
  518. }
  519. if (defined $buffer) {
  520. $out .= $buffer;
  521. next;
  522. }
  523. @lines = split /\n/, $para;
  524. next unless @lines;
  525. $block ||= 'p';
  526. $buffer = '';
  527. my $pre = '';
  528. my $post = '';
  529. if ($block eq 'bc') {
  530. if ($sticky <= 1) {
  531. $pre .= $self->{_blockcode_open};
  532. $pre =~ s/>$//s;
  533. $pre .= qq{ language="$bqlang"} if $bqlang;
  534. if ($align) {
  535. my $alignment = _halign($align);
  536. if ($self->{css_mode}) {
  537. if (($padleft || $padright) &&
  538. (($alignment eq 'left') || ($alignment eq 'right'))) {
  539. $style .= ';float:'.$alignment;
  540. } else {
  541. $style .= ';text-align:'.$alignment;
  542. }
  543. $class .= ' '.$self->{css}{"class_align_$alignment"} || $alignment;
  544. } else {
  545. $pre .= qq{ align="$alignment"} if $alignment;
  546. }
  547. }
  548. $style .= qq{;padding-left:${padleft}em} if $padleft;
  549. $style .= qq{;padding-right:${padright}em} if $padright;
  550. $style .= qq{;clear:${clear}} if $clear;
  551. $class =~ s/^ // if $class;
  552. $pre .= qq{ class="$class"} if $class;
  553. $pre .= qq{ id="$id"} if $id;
  554. $style =~ s/^;// if $style;
  555. $pre .= qq{ style="$style"} if $style;
  556. $pre .= qq{ lang="$lang"} if $lang;
  557. $pre .= '>';
  558. $lang = undef;
  559. $bqlang = undef;
  560. $clear = undef;
  561. }
  562. $para =~ s{(?:^|(?<=[\s>])|([{[]))
  563. ==(.+?)==
  564. (?:$|([\]}])|(?=$punct{1,2}|\s))}
  565. {_repl(\@repl, $self->format_block(text => $2, inline => 1, pre => $1, post => $3))}gesx;
  566. $buffer .= $self->encode_html_basic($para, 1);
  567. $buffer =~ s/&lt;textile#(\d+)&gt;/<textile#$1>/g;
  568. if ($sticky == 0) {
  569. $post .= $self->{_blockcode_close};
  570. }
  571. $out .= $pre . $buffer . $post;
  572. next;
  573. } elsif ($block eq 'bq') {
  574. if ($sticky <= 1) {
  575. $pre .= '<blockquote';
  576. if ($align) {
  577. my $alignment = _halign($align);
  578. if ($self->{css_mode}) {
  579. if (($padleft || $padright) &&
  580. (($alignment eq 'left') || ($alignment eq 'right'))) {
  581. $style .= ';float:'.$alignment;
  582. } else {
  583. $style .= ';text-align:'.$alignment;
  584. }
  585. $class .= ' '.$self->{css}{"class_align_$alignment"} || $alignment;
  586. } else {
  587. $pre .= qq{ align="$alignment"} if $alignment;
  588. }
  589. }
  590. $style .= qq{;padding-left:${padleft}em} if $padleft;
  591. $style .= qq{;padding-right:${padright}em} if $padright;
  592. $style .= qq{;clear:${clear}} if $clear;
  593. $class =~ s/^ // if $class;
  594. $pre .= qq{ class="$class"} if $class;
  595. $pre .= qq{ id="$id"} if $id;
  596. $style =~ s/^;// if $style;
  597. $pre .= qq{ style="$style"} if $style;
  598. $pre .= qq{ lang="$lang"} if $lang;
  599. $pre .= qq{ cite="} . $self->format_url(url => $cite) . '"' if defined $cite; #'
  600. $pre .= '>';
  601. $clear = undef;
  602. }
  603. $pre .= '<p>';
  604. } elsif ($block =~ m/fn(\d+)/) {
  605. my $fnum = $1;
  606. $pre .= '<p';
  607. $class .= ' '.$self->{css}{class_footnote} if $self->{css}{class_footnote};
  608. if ($align) {
  609. my $alignment = _halign($align);
  610. if ($self->{css_mode}) {
  611. if (($padleft || $padright) &&
  612. (($alignment eq 'left') || ($alignment eq 'right'))) {
  613. $style .= ';float:'.$alignment;
  614. } else {
  615. $style .= ';text-align:'.$alignment;
  616. }
  617. $class .= $self->{css}{"class_align_$alignment"} || $alignment;
  618. } else {
  619. $pre .= qq{ align="$alignment"};
  620. }
  621. }
  622. $style .= qq{;padding-left:${padleft}em} if $padleft;
  623. $style .= qq{;padding-right:${padright}em} if $padright;
  624. $style .= qq{;clear:${clear}} if $clear;
  625. $class =~ s/^ // if $class;
  626. $pre .= qq{ class="$class"} if $class;
  627. $pre .= qq{ id="}.($self->{css}{id_footnote_prefix}||'fn').$fnum.'"';
  628. $style =~ s/^;// if $style;
  629. $pre .= qq{ style="$style"} if $style;
  630. $pre .= qq{ lang="$lang"} if $lang;
  631. $pre .= '>';
  632. $pre .= '<sup>'.$fnum.'</sup> ';
  633. # we can close like a regular paragraph tag now
  634. $block = 'p';
  635. $clear = undef;
  636. } else {
  637. $pre .= '<' . ($macros{$block} || $block);
  638. if ($align) {
  639. my $alignment = _halign($align);
  640. if ($self->{css_mode}) {
  641. if (($padleft || $padright) &&
  642. (($alignment eq 'left') || ($alignment eq 'right'))) {
  643. $style .= ';float:'.$alignment;
  644. } else {
  645. $style .= ';text-align:'.$alignment;
  646. }
  647. $class .= ' '.$self->{css}{"class_align_$alignment"} || $alignment;
  648. } else {
  649. $pre .= qq{ align="$alignment"};
  650. }
  651. }
  652. $style .= qq{;padding-left:${padleft}em} if $padleft;
  653. $style .= qq{;padding-right:${padright}em} if $padright;
  654. $style .= qq{;clear:${clear}} if $clear;
  655. $class =~ s/^ // if $class;
  656. $pre .= qq{ class="$class"} if $class;
  657. $pre .= qq{ id="$id"} if $id;
  658. $style =~ s/^;// if $style;
  659. $pre .= qq{ style="$style"} if $style;
  660. $pre .= qq{ lang="$lang"} if $lang;
  661. $pre .= qq{ cite="} . $self->format_url(url => $cite) . '"' if defined $cite && $block eq 'bq'; #'
  662. $pre .= '>';
  663. $clear = undef;
  664. }
  665. $buffer = $self->format_paragraph(text => $para);
  666. if ($block eq 'bq') {
  667. $post .= '</p>' if $buffer !~ m/<p[ >]/;
  668. if ($sticky == 0) {
  669. $post .= '</blockquote>';
  670. }
  671. } else {
  672. $post .= '</' . $block . '>';
  673. }
  674. if ($buffer =~ m/$blocktags/) {
  675. $buffer =~ s/^\n\n//s;
  676. $out .= $buffer;
  677. } else {
  678. $buffer = $self->format_block(text => "|$filter|".$buffer, inline => 1) if defined $filter;
  679. $out .= $pre . $buffer . $post;
  680. }
  681. }
  682. if ($sticky) {
  683. if ($block eq 'bc') {
  684. # close our blockcode section
  685. $out .= $self->{_blockcode_close}; # . "\n\n";
  686. } elsif ($block eq 'bq') {
  687. $out .= '</blockquote>'; # . "\n\n";
  688. } elsif (($block eq 'table') && ($stickybuff)) {
  689. my $table_out = $self->format_table(text => $stickybuff);
  690. $out .= $table_out if defined $table_out;
  691. } elsif (($block eq 'dl') && ($stickybuff)) {
  692. my $dl_out = $self->format_deflist(text => $stickybuff);
  693. $out .= $dl_out if defined $dl_out;
  694. }
  695. }
  696. # cleanup-- restore preserved blocks
  697. my $i = scalar(@repl);
  698. $out =~ s!(?:<|&lt;)textile#$i(?:>|&gt;)!$_!, $i-- while $_ = pop @repl;
  699. # scan for br, hr tags that are not closed and close them
  700. # only for xhtml! just the common ones -- don't fret over input
  701. # and the like.
  702. if ($self->{flavor} =~ m/^xhtml/i) {
  703. $out =~ s/(<(?:img|br|hr)[^>]*?(?<!\/))>/$1 \/>/g;
  704. }
  705. $out;
  706. }
  707. sub format_paragraph {
  708. my $self = shift;
  709. my (%args) = @_;
  710. my $buffer = exists $args{text} ? $args{text} : '';
  711. my @repl;
  712. $buffer =~ s{(?:^|(?<=[\s>])|([{[]))
  713. ==(.+?)==
  714. (?:$|([\]}])|(?=$punct{1,2}|\s))}
  715. {_repl(\@repl, $self->format_block(text => $2, inline => 1, pre => $1, post => $3))}gesx;
  716. my $tokens;
  717. if ($buffer =~ m/</ && (!$self->{disable_html})) { # optimization -- no point in tokenizing if we
  718. # have no tags to tokenize
  719. $tokens = _tokenize($buffer);
  720. } else {
  721. $tokens = [['text', $buffer]];
  722. }
  723. my $result = '';
  724. foreach my $token (@$tokens) {
  725. my $text = $token->[1];
  726. if ($token->[0] eq 'tag') {
  727. $text =~ s/&(?!amp;)/&amp;/g;
  728. $result .= $text;
  729. } else {
  730. $text = $self->format_inline(text => $text);
  731. $result .= $text;
  732. }
  733. }
  734. # now, add line breaks for lines that contain plaintext
  735. my @lines = split /\n/, $result;
  736. $result = '';
  737. my $needs_closing = 0;
  738. foreach my $line (@lines) {
  739. if (($line !~ m/($blocktags)/)
  740. && (($line =~ m/^[^<]/ || $line =~ m/>[^<]/)
  741. || ($line !~ m/<img /))) {
  742. if ($self->{_line_open}) {
  743. $result .= "\n" if $result ne '';
  744. $result .= $self->{_line_open} . $line . $self->{_line_close};
  745. } else {
  746. if ($needs_closing) {
  747. $result .= $self->{_line_close} ."\n";
  748. } else {
  749. $needs_closing = 1;
  750. $result .= "\n" if $result ne '';
  751. }
  752. $result .= $line;
  753. }
  754. } else {
  755. if ($needs_closing) {
  756. $result .= $self->{_line_close} ."\n";
  757. } else {
  758. $result .= "\n" if $result ne '';
  759. }
  760. $result .= $line;
  761. $needs_closing = 0;
  762. }
  763. }
  764. # at this point, we will restore the \001's to \n's (reversing
  765. # the step taken in _tokenize).
  766. #$result =~ s/\r/\n/g;
  767. $result =~ s/\001/\n/g;
  768. my $i = scalar(@repl);
  769. $result =~ s|<textile#$i>|$_|, $i-- while $_ = pop @repl;
  770. # quotalize
  771. if ($self->{do_quotes}) {
  772. $result = $self->process_quotes($result);
  773. }
  774. $result;
  775. }
  776. {
  777. my @qtags = (['**', 'b', '(?<!\*)\*\*(?!\*)', '\*'],
  778. ['__', 'i', '(?<!_)__(?!_)', '_'],
  779. ['??', 'cite', '\?\?(?!\?)', '\?'],
  780. ['*', 'strong', '(?<!\*)\*(?!\*)', '\*'],
  781. ['_', 'em', '(?<!_)_(?!_)', '_'],
  782. ['-', 'del', '(?<!\-)\-(?!\-)', '-'],
  783. ['+', 'ins', '(?<!\+)\+(?!\+)', '\+'],
  784. ['++', 'big', '(?<!\+)\+\+(?!\+)', '\+\+'],
  785. ['--', 'small', '(?<!\-)\-\-(?!\-)', '\-\-'],
  786. ['~', 'sub', '(?<!\~)\~(?![\\\/~])', '\~']);
  787. sub format_inline {
  788. my $self = shift;
  789. my (%args) = @_;
  790. my $text = exists $args{text} ? $args{text} : '';
  791. my @repl;
  792. $text =~ s!$codere!_repl(\@repl, $self->format_code(text => $2.$4, lang => $1.$3))!gem;
  793. # images must be processed before encoding the text since they might
  794. # have the <, > alignment specifiers...
  795. # !blah (alt)! -> image
  796. $text =~ s!(?:^|(?<=[\s>])|([{[])) # $1: open brace/bracket
  797. \! # opening
  798. ($imgalignre?) # $2: optional alignment
  799. ($clstypadre*) # $3: optional CSS class/id
  800. ($imgalignre?) # $4: optional alignment
  801. (?:\s*) # space between alignment/css stuff
  802. ([^\s\(\!]+) # $5: filename
  803. (\s*[^\(\!]*(?:\([^\)]+\))?[^\!]*) # $6: extras (alt text)
  804. \! # closing
  805. (?::(\d+|$urlre))? # $7: optional URL
  806. (?:$|([\]}])|(?=$punct{1,2}|\s))# $8: closing brace/bracket
  807. !_repl(\@repl, $self->format_image(pre => $1, src => $5, align => $2||$4, extra => $6, url => $7, clsty => $3, post => $8))!gemx;
  808. $text =~ s!(?:^|(?<=[\s>])|([{[])) # $1: open brace/bracket
  809. \% # opening
  810. ($halignre?) # $2: optional alignment
  811. ($clstyre*) # $3: optional CSS class/id
  812. ($halignre?) # $4: optional alignment
  813. (?:\s*) # spacing
  814. ([^\%]+?) # $5: text
  815. \% # closing
  816. (?::(\d+|$urlre))? # $6: optional URL
  817. (?:$|([\]}])|(?=$punct{1,2}|\s))# $7: closing brace/bracket
  818. !_repl(\@repl, $self->format_span(pre => $1,text => $5,align => $2||$4, cite => $6, clsty => $3, post => $7))!gemx;
  819. $text = $self->encode_html($text);
  820. $text =~ s!&lt;textile#(\d+)&gt;!<textile#$1>!g;
  821. $text =~ s!&amp;quot;!&#34;!g;
  822. $text =~ s!&amp;(([a-z]+|#\d+);)!&$1!g;
  823. $text =~ s!&quot;!"!g; #"
  824. # These create markup with entities. Do first and 'save' result for later:
  825. # "text":url -> hyperlink
  826. # links with brackets surrounding
  827. my $parenre = qr/\( (?: [^()] )* \)/x;
  828. $text =~ s!(
  829. [{[]
  830. (?:
  831. (?:" # quote character
  832. ($clstyre*)? # $2: optional CSS class/id
  833. ([^"]+?) # $3: link text
  834. (?:\( ( (?:[^()]|$parenre)*) \))? # $4: optional link title
  835. " # closing quote
  836. )
  837. |
  838. (?:' # open single quote
  839. ($clstyre*)? # $5: optional CSS class/id
  840. ([^']+?) # $6: link text
  841. (?:\( ( (?:[^()]|$parenre)*) \))? # $7: optional link title
  842. ' # closing quote
  843. )
  844. )
  845. :(.+?) # $8: URL suffix
  846. [\]}]
  847. )
  848. !_repl(\@repl, $self->format_link(text => $1,linktext => $3.$6, title => $self->encode_html_basic($4.$7), url => $8, clsty => $2.$5))!gemx;
  849. $text =~ s!((?:^|(?<=[\s>\(])) # $1: open brace/bracket
  850. (?: (?:" # quote character "
  851. ($clstyre*)? # $2: optional CSS class/id
  852. ([^"]+?) # $3: link text "
  853. (?:\( ( (?:[^()]|$parenre)*) \))? # $4: optional link title
  854. " # closing quote # "
  855. )
  856. |
  857. (?:' # open single quote '
  858. ($clstyre*)? # $5: optional CSS class/id
  859. ([^']+?) # $6: link text '
  860. (?:\( ( (?:[^()]|$parenre)*) \))? # $7: optional link title
  861. ' # closing quote '
  862. )
  863. )
  864. :(\d+|$urlre) # $8: URL suffix
  865. (?:$|(?=$punct{1,2}|\s))) # $9: closing brace/bracket
  866. !_repl(\@repl, $self->format_link(text => $1, linktext => $3.$6, title => $self->encode_html_basic($4.$7), url => $8, clsty => $2.$5))!gemx;
  867. if ($self->{flavor} =~ m/^xhtml2/) {
  868. # citation with cite link
  869. $text =~ s!(?:^|(?<=[\s>'"\(])|([{[])) # $1: open brace/bracket '
  870. \?\? # opening '??'
  871. ([^\?]+?) # $2: characters (can't contain '?')
  872. \?\? # closing '??'
  873. :(\d+|$urlre) # $3: optional citation URL
  874. (?:$|([\]}])|(?=$punct{1,2}|\s))# $4: closing brace/bracket
  875. !_repl(\@repl, $self->format_cite(pre => $1,text => $2,cite => $3,post => $4))!gemx;
  876. }
  877. # footnotes
  878. if ($text =~ m/[^ ]\[\d+\]/) {
  879. my $fntag = '<sup';
  880. $fntag .= ' class="'.$self->{css}{class_footnote}.'"' if $self->{css}{class_footnote};
  881. $fntag .= '><a href="#'.($self->{css}{id_footnote_prefix}||'fn');
  882. $text =~ s|([^ ])\[(\d+)\]|$1$fntag$2">$2</a></sup>|g;
  883. }
  884. # translate macros:
  885. $text =~ s{(\{)(.+?)(\})}
  886. {$self->format_macro(pre => $1, post => $3, macro => $2)}gex;
  887. # these were present with textile 1 and are common enough
  888. # to not require macro braces...
  889. # (tm) -> &trade;
  890. $text =~ s|[\(\[]TM[\)\]]|&#8482;|gi;
  891. # (c) -> &copy;
  892. $text =~ s|[\(\[]C[\)\]]|&#169;|gi;
  893. # (r) -> &reg;
  894. $text =~ s|[\(\[]R[\)\]]|&#174;|gi;
  895. if ($self->{preserve_spaces}) {
  896. # replace two spaces with an em space
  897. $text =~ s/(?<!\s)\ \ (?!=\s)/&#8195;/g;
  898. }
  899. my $redo = $text =~ m/[\*_\?\-\+\^\~]/;
  900. my $last = $text;
  901. while ($redo) {
  902. # simple replacements...
  903. $redo = 0;
  904. foreach my $tag (@qtags) {
  905. my ($f, $r, $qf, $cls) = @$tag;
  906. if ($text =~ s/(?:^|(?<=[\s>'"])|([{[])) # "' $1 - pre
  907. $qf #
  908. (?:($clstyre*))? # $2 - attributes
  909. ([^$cls\s].*?) # $3 - content
  910. (?<=\S)$qf #
  911. (?:$|([\]}])|(?=$punct{1,2}|\s)) # $4 - post
  912. /$self->format_tag(tag => $r, marker => $f, pre => $1, text => $3, clsty => $2, post => $4)/gemx) {
  913. $redo ||= $last ne $text;
  914. $last = $text;
  915. }
  916. }
  917. }
  918. # superscript is an even simpler replacement...
  919. $text =~ s/(?<!\^)\^(?!\^)(.+?)(?<!\^)\^(?!\^)/<sup>$1<\/sup>/g;
  920. # ABC(Aye Bee Cee) -> acronym
  921. $text =~ s{\b([A-Z][A-Za-z0-9]*?[A-Z0-9]+?)\b(?:[(]([^)]*)[)])}
  922. {_repl(\@repl,qq{<acronym title="}.$self->encode_html_basic($2).qq{">$1</acronym>})}ge;
  923. # ABC -> 'capped' span
  924. if (my $caps = $self->{css}{class_caps}) {
  925. $text =~ s/(^|[^"][>\s]) # "
  926. ((?:[A-Z](?:[A-Z0-9\.,']|\&amp;){2,}\ *)+?) # '
  927. (?=[^A-Z\.0-9]|$)
  928. /$1._repl(\@repl, qq{<span class="$caps">$2<\/span>})/gemx;
  929. }
  930. # nxn -> n&times;n
  931. $text =~ s!((?:[0-9\.]0|[1-9]|\d['"])\ ?)x(\ ?\d)!$1&#215;$2!g;
  932. # translate these entities to the Unicode equivalents:
  933. $text =~ s/&#133;/&#8230;/g;
  934. $text =~ s/&#145;/&#8216;/g;
  935. $text =~ s/&#146;/&#8217;/g;
  936. $text =~ s/&#147;/&#8220;/g;
  937. $text =~ s/&#148;/&#8221;/g;
  938. $text =~ s/&#150;/&#8211;/g;
  939. $text =~ s/&#151;/&#8212;/g;
  940. # Restore replacements done earlier:
  941. my $i = scalar(@repl);
  942. $text =~ s|<textile#$i>|$_|, $i-- while $_ = pop @repl;
  943. # translate entities to characters for highbit stuff since
  944. # we're using utf8
  945. # removed for backward compatability with older versions of Perl
  946. #if ($self->{charset} =~ m/^utf-?8$/i) {
  947. # # translate any unicode entities to native UTF-8
  948. # $text =~ s/\&\#(\d+);/($1 > 127) ? pack('U',$1) : chr($1)/ge;
  949. #}
  950. $text;
  951. }
  952. }
  953. {
  954. # pull in charnames, but only for Perl 5.8 or later (and
  955. # disable strict subs for backward compatability
  956. my $Have_Charnames = 0;
  957. if ($] >= 5.008) {
  958. eval 'use charnames qw(:full);';
  959. $Have_Charnames = 1;
  960. }
  961. sub format_macro {
  962. my $self = shift;
  963. my %attrs = @_;
  964. my $macro = $attrs{macro};
  965. if (defined $self->{macros}->{$macro}) {
  966. return $self->{macros}->{$macro};
  967. }
  968. # handle full unicode name translation
  969. if ($Have_Charnames) {
  970. # charnames::vianame is only available in Perl 5.8.0 and later...
  971. if (defined (my $unicode = charnames::vianame(uc($macro)))) {
  972. return '&#'.$unicode.';';
  973. }
  974. }
  975. return $attrs{pre}.$macro.$attrs{post};
  976. }
  977. }
  978. sub format_cite {
  979. my $self = shift;
  980. my (%args) = @_;
  981. my $pre = exists $args{pre} ? $args{pre} : '';
  982. my $text = exists $args{text} ? $args{text} : '';
  983. my $cite = $args{cite};
  984. my $post = exists $args{post} ? $args{post} : '';
  985. _strip_borders(\$pre, \$post);
  986. my $tag = $pre.'<cite';
  987. if (($self->{flavor} =~ m/^xhtml2/) && defined $cite && $cite) {
  988. $cite = $self->format_url(url => $cite);
  989. $tag .= qq{ cite="$cite"};
  990. } else {
  991. $post .= ':';
  992. }
  993. $tag .= '>';
  994. $tag . $self->format_inline(text => $text) . '</cite>'.$post;
  995. }
  996. sub format_code {
  997. my $self = shift;
  998. my (%args) = @_;
  999. my $code = exists $args{text} ? $args{text} : '';
  1000. my $lang = $args{lang};
  1001. $code = $self->encode_html($code, 1);
  1002. $code =~ s/&lt;textile#(\d+)&gt;/<textile#$1>/g;
  1003. my $tag = '<code';
  1004. $tag .= " language=\"$lang\"" if $lang;
  1005. $tag . '>' . $code . '</code>';
  1006. }
  1007. sub format_classstyle {
  1008. my $self = shift;
  1009. my ($clsty, $class, $style) = @_;
  1010. $class =~ s/^ //;
  1011. my ($lang, $padleft, $padright, $id);
  1012. if ($clsty && ($clsty =~ m/{([^}]+)}/)) {
  1013. my $_style = $1;
  1014. $_style =~ s/\n/ /g;
  1015. $style .= ';'.$_style;
  1016. $clsty =~ s/{[^}]+}//g;
  1017. }
  1018. if ($clsty && ($clsty =~ m/\(([A-Za-z0-9_\- ]+?)(?:#(.+?))?\)/ ||
  1019. $clsty =~ m/\(([A-Za-z0-9_\- ]+?)?(?:#(.+?))\)/)) {
  1020. if ($1 || $2) {
  1021. if ($class) {
  1022. $class = $1 . ' ' . $class;
  1023. } else {
  1024. $class = $1;
  1025. }
  1026. $id = $2;
  1027. if ($class) {
  1028. $clsty =~ s/\([A-Za-z0-9_\- ]+?(#.*?)?\)//g;
  1029. }
  1030. if ($id) {
  1031. $clsty =~ s/\(#.+?\)//g;
  1032. }
  1033. }
  1034. }
  1035. if ($clsty && ($clsty =~ m/(\(+)/)) {
  1036. $padleft = length($1);
  1037. $clsty =~ s/\(+//;
  1038. }
  1039. if ($clsty && ($clsty =~ m/(\)+)/)) {
  1040. $padright = length($1);
  1041. $clsty =~ s/\)+//;
  1042. }
  1043. if ($clsty && ($clsty =~ m/\[(.+?)\]/)) {
  1044. $lang = $1;
  1045. $clsty =~ s/\[.+?\]//g;
  1046. }
  1047. my $attrs = '';
  1048. $style .= qq{;padding-left:${padleft}em} if $padleft;
  1049. $style .= qq{;padding-right:${padright}em} if $padright;
  1050. $style =~ s/^;//;
  1051. $class =~ s/^ //;
  1052. $class =~ s/ $//;
  1053. $attrs .= qq{ class="$class"} if $class;
  1054. $attrs .= qq{ id="$id"} if $id;
  1055. $attrs .= qq{ style="$style"} if $style;
  1056. $attrs .= qq{ lang="$lang"} if $lang;
  1057. $attrs =~ s/^ //;
  1058. $attrs;
  1059. }
  1060. sub format_tag {
  1061. my $self = shift;
  1062. my (%args) = @_;
  1063. my $tagname = $args{tag};
  1064. my $text = exists $args{text} ? $args{text} : '';
  1065. my $pre = exists $args{pre} ? $args{pre} : '';
  1066. my $post = exists $args{post} ? $args{post} : '';
  1067. my $clsty = exists $args{clsty} ? $args{clsty} : '';
  1068. _strip_borders(\$pre, \$post);
  1069. my $tag = "<$tagname";
  1070. my $attr = $self->format_classstyle($clsty);
  1071. $tag .= qq{ $attr} if $attr;
  1072. $tag .= qq{>$text</$tagname>};
  1073. $pre.$tag.$post;
  1074. }
  1075. sub format_deflist {
  1076. my $self = shift;
  1077. my (%args) = @_;
  1078. my $str = exists $args{text} ? $args{text} : '';
  1079. my $clsty;
  1080. my @lines = split /\n/, $str;
  1081. if ($lines[0] =~ m/^(dl($clstyre*?)\.\.?(?:\ +|$))/) {
  1082. $clsty = $2;
  1083. $lines[0] = substr($lines[0], length($1));
  1084. }
  1085. sub add_term {
  1086. my ($self, $dt, $dd) = @_;
  1087. my ($dtattr, $ddattr);
  1088. my $dtlang;
  1089. if ($dt =~ m/^($clstyre*)/) {
  1090. my $param = $1;
  1091. $dtattr = $self->format_classstyle($param);
  1092. if ($param =~ m/\[([A-Za-z]+?)\]/) {
  1093. $dtlang = $1;
  1094. }
  1095. $dt = substr($dt, length($param));
  1096. }
  1097. if ($dd =~ m/^($clstyre*)/) {
  1098. my $param = $1;
  1099. # if the language was specified for the term,
  1100. # then apply it to the definition as well (unless
  1101. # already specified of course)
  1102. if ($dtlang && ($param =~ m/\[([A-Za-z]+?)\]/)) {
  1103. undef $dtlang;
  1104. }
  1105. $ddattr = $self->format_classstyle(($dtlang ? "[$dtlang]" : '') . $param);
  1106. $dd = substr($dd, length($param));
  1107. }
  1108. my $out = '<dt';
  1109. $out .= qq{ $dtattr} if $dtattr;
  1110. $out .= '>' . $self->format_inline(text => $dt) . '</dt>' . "\n";
  1111. if ($dd =~ m/\n\n/) {
  1112. $dd = $self->textile($dd) if $dd =~ m/\n\n/;
  1113. } else {
  1114. $dd = $self->format_paragraph(text => $dd);
  1115. }
  1116. $out .= '<dd';
  1117. $out .= qq{ $ddattr} if $ddattr;
  1118. $out .= '>' . $dd . '</dd>' . "\n";
  1119. }
  1120. my ($dt, $dd);
  1121. my $out = '';
  1122. foreach my $line (@lines) {
  1123. if ($line =~ m/^((?:$clstyre*)(?:[^\ ].*?)(?<!["'\ ])):([^\ \/].*)$/) {
  1124. $out .= add_term($self, $dt, $dd) if ($dt && $dd);
  1125. $dt = $1;
  1126. $dd = $2;
  1127. } else {
  1128. $dd .= "\n" . $line;
  1129. }
  1130. }
  1131. $out .= add_term($self, $dt, $dd) if $dt && $dd;
  1132. my $tag = '<dl';
  1133. my $attr = $self->format_classstyle($clsty) if $clsty;
  1134. $tag .= qq{ $attr} if $attr;
  1135. $tag .= '>'."\n";
  1136. $tag.$out."</dl>\n";
  1137. }
  1138. sub format_list {
  1139. my $self = shift;
  1140. my (%args) = @_;
  1141. my $str = exists $args{text} ? $args{text} : '';
  1142. my %list_tags = ('*' => 'ul', '#' => 'ol');
  1143. my @lines = split /\n/, $str;
  1144. my @stack;
  1145. my $last_depth = 0;
  1146. my $item = '';
  1147. my $out = '';
  1148. foreach my $line (@lines) {
  1149. if ($line =~ m/^((?:$clstypadre*|$halignre)*)
  1150. ([\#\*]+)
  1151. ((?:$halignre|$clstypadre*)*)
  1152. \ (.+)$/x) {
  1153. if ($item ne '') {
  1154. if ($item =~ m/\n/) {
  1155. if ($self->{_line_open}) {
  1156. $item =~ s/(<li[^>]*>|^)/$1$self->{_line_open}/gm;
  1157. $item =~ s/(\n|$)/$self->{_line_close}$1/gs;
  1158. } else {
  1159. $item =~ s/(\n)/$self->{_line_close}$1/gs;
  1160. }
  1161. }
  1162. $out .= $item;
  1163. $item = '';
  1164. }
  1165. my $type = substr($2, 0, 1);
  1166. my $depth = length($2);
  1167. my $blockparam = $1;
  1168. my $itemparam = $3;
  1169. $line = $4;
  1170. my ($blockclsty, $blockalign, $blockattr, $itemattr, $itemclsty,
  1171. $itemalign);
  1172. if ($blockparam =~ m/($clstypadre+)/) {
  1173. $blockclsty = $1;
  1174. }
  1175. if ($blockparam =~ m/($halignre+)/) {
  1176. $blockalign = $1;
  1177. }
  1178. if ($itemparam =~ m/($clstypadre+)/) {
  1179. $itemclsty = $1;
  1180. }
  1181. if ($itemparam =~ m/($halignre+)/) {
  1182. $itemalign = $1;
  1183. }
  1184. $itemattr = $self->format_classstyle($itemclsty) if $itemclsty;
  1185. if ($depth > $last_depth) {
  1186. for (my $j = $last_depth; $j < $depth; $j++) {
  1187. $out .= qq{<$list_tags{$type}};
  1188. push @stack, $type;
  1189. if ($blockclsty) {
  1190. $blockattr = $self->format_classstyle($blockclsty);
  1191. $out .= ' '.$blockattr if $blockattr;
  1192. }
  1193. $out .= ">\n<li";
  1194. $out .= qq{ $itemattr} if $itemattr;
  1195. $out .= ">";
  1196. }
  1197. } elsif ($depth < $last_depth) {
  1198. for (my $j = $depth; $j < $last_depth; $j++) {
  1199. $out .= "</li>\n" if $j == $depth;
  1200. my $type = pop @stack;
  1201. $out .= qq{</$list_tags{$type}>\n</li>\n};
  1202. }
  1203. if ($depth) {
  1204. $out .= '<li';
  1205. $out .= qq{ $itemattr} if $itemattr;
  1206. $out .= '>';
  1207. }
  1208. } else {
  1209. $out .= "</li>\n<li";
  1210. $out .= qq{ $itemattr} if $itemattr;
  1211. $out .= '>';
  1212. }
  1213. $last_depth = $depth;
  1214. }
  1215. $item .= "\n" if $item ne '';
  1216. $item .= $self->format_paragraph(text => $line);
  1217. }
  1218. if ($item =~ m/\n/) {
  1219. if ($self->{_line_open}) {
  1220. $item =~ s/(<li[^>]*>|^)/$1$self->{_line_open}/gm;
  1221. $item =~ s/(\n|$)/$self->{_line_close}$1/gs;
  1222. } else {
  1223. $item =~ s/(\n)/$self->{_line_close}$1/gs;
  1224. }
  1225. }
  1226. $out .= $item;
  1227. for (my $j = 1; $j <= $last_depth; $j++) {
  1228. $out .= '</li>' if $j == 1;
  1229. my $type = pop @stack;
  1230. $out .= "\n".'</'.$list_tags{$type}.'>';
  1231. $out .= '</li>' if $j != $last_depth;
  1232. }
  1233. $out;
  1234. }
  1235. sub format_block {
  1236. my $self = shift;
  1237. my (%args) = @_;
  1238. my $str = exists $args{text} ? $args{text} : '';
  1239. my $inline = $args{inline};
  1240. my $pre = exists $args{pre} ? $args{pre} : '';
  1241. my $post = exists $args{post} ? $args{post} : '';
  1242. _strip_borders(\$pre, \$post);
  1243. my ($filters) = $str =~ m/^(\|(?:(?:[a-z0-9_\-]+)\|)+)/;
  1244. if ($filters) {
  1245. my $filtreg = quotemeta($filters);
  1246. $str =~ s/^$filtreg//;
  1247. $filters =~ s/^\|//;
  1248. $filters =~ s/\|$//;
  1249. my @filters = split /\|/, $filters;
  1250. $str = $self->apply_filters(text => $str, filters => \@filters);
  1251. my $count = scalar(@filters);
  1252. if ($str =~ s!(<p>){$count}!$1!gs) {
  1253. $str =~ s!(</p>){$count}!$1!gs;
  1254. $str =~ s!(<br( /)?>){$count}!$1!gs;
  1255. }
  1256. }
  1257. if ($inline) {
  1258. # strip off opening para, closing para, since we're
  1259. # operating within an inline block
  1260. $str =~ s/^\s*<p[^>]*>//;
  1261. $str =~ s/<\/p>\s*$//;
  1262. }
  1263. $pre.$str.$post;
  1264. }
  1265. sub format_link {
  1266. my $self = shift;
  1267. my (%args) = @_;
  1268. my $text = exists $args{text} ? $args{text} : '';
  1269. my $linktext = exists $args{linktext} ? $args{linktext} : '';
  1270. my $title = $args{title};
  1271. my $url = $args{url};
  1272. my $clsty = $args{clsty};
  1273. if (!defined $url || $url eq '') {
  1274. return $text;
  1275. }
  1276. if (exists $self->{links} && exists $self->{links}{$url}) {
  1277. $title ||= $self->{links}{$url}{title};
  1278. $url = $self->{links}{$url}{url};
  1279. }
  1280. $linktext =~ s/ +$//;
  1281. $linktext = $self->format_paragraph(text => $linktext);
  1282. $url = $self->format_url(linktext => $linktext, url => $url);
  1283. my $tag = qq{<a href="$url"};
  1284. my $attr = $self->format_classstyle($clsty);
  1285. $tag .= qq{ $attr} if $attr;
  1286. if (defined $title) {
  1287. $title =~ s/^\s+//;
  1288. $tag .= qq{ title="$title"} if length($title);
  1289. }
  1290. $tag .= qq{>$linktext</a>};
  1291. $tag;
  1292. }
  1293. sub format_url {
  1294. my $self = shift;
  1295. my (%args) = @_;
  1296. my $url = defined $args{url} ? $args{url} : '';
  1297. if ($url =~ m/^(mailto:)?([-\+\w]+\@[-\w]+(\.\w[-\w]*)+)$/) {
  1298. $url = 'mailto:'.$self->mail_encode($2);
  1299. }
  1300. if ($url !~ m!^(/|\./|\.\./|#)!) {
  1301. $url = "http://$url" if $url !~ m!^(https?|ftp|mailto|nntp|telnet)!;
  1302. }
  1303. $url =~ s/&(?!amp;)/&amp;/g;
  1304. $url =~ s/\ /\+/g;
  1305. $url =~ s/^((?:.+?)\?)(.+)$/$1.$self->encode_url($2)/ge;
  1306. $url;
  1307. }
  1308. sub format_span {
  1309. my $self = shift;
  1310. my (%args) = @_;
  1311. my $text = exists $args{text} ? $args{text} : '';
  1312. my $pre = exists $args{pre} ? $args{pre} : '';
  1313. my $post = exists $args{post} ? $args{post} : '';
  1314. my $align = $args{align};
  1315. my $cite = exists $args{cite} ? $args{cite} : '';
  1316. my $clsty = $args{clsty};
  1317. _strip_borders(\$pre, \$post);
  1318. my ($class, $style);
  1319. my $tag = qq{<span};
  1320. $style = '';
  1321. if (defined $align) {
  1322. if ($self->{css_mode}) {
  1323. my $alignment = _halign($align);
  1324. $style .= qq{;float:$alignment} if $alignment;
  1325. $class .= ' '.$self->{css}{"class_align_$alignment"} if $alignment;
  1326. } else {
  1327. my $alignment = _halign($align) || _valign($align);
  1328. $tag .= qq{ align="$alignment"} if $alignment;
  1329. }
  1330. }
  1331. my $attr = $self->format_classstyle($clsty, $class, $style);
  1332. $tag .= qq{ $attr} if $attr;
  1333. if (defined $cite) {
  1334. $cite =~ s/^://;
  1335. $cite = $self->format_url(url => $cite);
  1336. $tag .= qq{ cite="$cite"};
  1337. }
  1338. $pre.$tag.'>'.$self->format_paragraph(text => $text).'</span>'.$post;
  1339. }
  1340. sub format_image {
  1341. my $self = shift;
  1342. my (%args) = @_;
  1343. my $src = exists $args{src} ? $args{src} : '';
  1344. my $extra = $args{extra};
  1345. my $align = $args{align};
  1346. my $pre = exists $args{pre} ? $args{pre} : '';
  1347. my $post = exists $args{post} ? $args{post} : '';
  1348. my $link = $args{url};
  1349. my $clsty = $args{clsty};
  1350. _strip_borders(\$pre, \$post);
  1351. return $pre.'!!'.$post if length($src) == 0;
  1352. my $tag;
  1353. if ($self->{flavor} =~ m/^xhtml2/) {
  1354. my $type; # poor man's mime typing. need to extend this externally
  1355. if ($src =~ m/(?:\.jpeg|\.jpg)$/i) {
  1356. $type = 'image/jpeg';
  1357. } elsif ($src =~ m/\.gif$/i) {
  1358. $type = 'image/gif';
  1359. } elsif ($src =~ m/\.png$/i) {
  1360. $type = 'image/png';
  1361. } elsif ($src =~ m/\.tiff$/i) {
  1362. $type = 'image/tiff';
  1363. }
  1364. $tag = qq{<object};
  1365. $tag .= qq{ type="$type"} if $type;
  1366. $tag .= qq{ data="$src"};
  1367. } else {
  1368. $tag = qq{<img src="$src"};
  1369. }
  1370. my ($class, $style);
  1371. if (defined $align) {
  1372. if ($self->{css_mode}) {
  1373. my $alignment = _halign($align);
  1374. $style .= qq{;float:$alignment} if $alignment;
  1375. $class .= ' '.$alignment if $alignment;
  1376. $alignment = _valign($align);
  1377. if ($alignment) {
  1378. my $imgvalign = ($alignment =~ m/(top|bottom)/ ? 'text-' . $alignment : $alignment);
  1379. $style .= qq{;vertical-align:$imgvalign} if $imgvalign;
  1380. $class .= ' '.$self->{css}{"class_align_$alignment"} if $alignment;
  1381. }
  1382. } else {
  1383. my $alignment = _halign($align) || _valign($align);
  1384. $tag .= qq{ align="$alignment"} if $alignment;
  1385. }
  1386. }
  1387. my ($pctw, $pcth, $w, $h, $alt);
  1388. if (defined $extra) {
  1389. ($alt) = $extra =~ m/\(([^\)]+)\)/;
  1390. $extra =~ s/\([^\)]+\)//;
  1391. my ($pct) = ($extra =~ m/(^|\s)(\d+)%(\s|$)/)[1];
  1392. if (!$pct) {
  1393. ($pctw, $pcth) = ($extra =~ m/(^|\s)(\d+)%x(\d+)%(\s|$)/)[1,2];
  1394. } else {
  1395. $pctw = $pcth = $pct;
  1396. }
  1397. if (!$pctw && !$pcth) {
  1398. ($w,$h) = ($extra =~ m/(^|\s)(\d+|\*)x(\d+|\*)(\s|$)/)[1,2];
  1399. $w = '' if $w eq '*';
  1400. $h = '' if $h eq '*';
  1401. if (!$w) {
  1402. ($w) = ($extra =~ m/(^|[,\s])(\d+)w([\s,]|$)/)[1];
  1403. }
  1404. if (!$h) {
  1405. ($h) = ($extra =~ m/(^|[,\s])(\d+)h([\s,]|$)/)[1];
  1406. }
  1407. }
  1408. }
  1409. $alt = '' unless defined $alt;
  1410. if ($self->{flavor} !~ m/^xhtml2/) {
  1411. $tag .= ' alt="' . $self->encode_html_basic($alt) . '"';
  1412. }
  1413. if ($w && $h) {
  1414. if ($self->{flavor} !~ m/^xhtml2/) {
  1415. $tag .= qq{ height="$h" width="$w"};
  1416. } else {
  1417. $style .= qq{;height:$h}.qq{px;width:$w}.q{px};
  1418. }
  1419. } else {
  1420. my ($image_w, $image_h) = $self->image_size($src);
  1421. if (($image_w && $image_h) && ($w || $h)) {
  1422. # image size determined, but only width or height specified
  1423. if ($w && !$h) {
  1424. # width defined, scale down height proportionately
  1425. $h = int($image_h * ($w / $image_w));
  1426. } elsif ($h && !$w) {
  1427. $w = int($image_w * ($h / $image_h));
  1428. }
  1429. } else {
  1430. $w = $image_w;
  1431. $h = $image_h;
  1432. }
  1433. if ($w && $h) {
  1434. if ($pctw || $pcth) {
  1435. $w = int($w * $pctw / 100);
  1436. $h = int($h * $pcth / 100);
  1437. }
  1438. if ($self->{flavor} !~ m/^xhtml2/) {
  1439. $tag .= qq{ height="$h" width="$w"};
  1440. } else {
  1441. $style .= qq{;height:$h}.qq{px;width:$w}.q{px};
  1442. }
  1443. }
  1444. }
  1445. my $attr = $self->format_classstyle($clsty, $class, $style);
  1446. $tag .= qq{ $attr} if $attr;
  1447. if ($self->{flavor} =~ m/^xhtml2/) {
  1448. $tag .= '><p>' . $self->encode_html_basic($alt) . '</p></object>';
  1449. } elsif ($self->{flavor} =~ m/^xhtml/) {
  1450. $tag .= ' />';
  1451. } else {
  1452. $tag .= '>';
  1453. }
  1454. if (defined $link) {
  1455. $link =~ s/^://;
  1456. $link = $self->format_url(url => $link);
  1457. $tag = '<a href="'.$link.'">'.$tag.'</a>';
  1458. }
  1459. $pre.$tag.$post;
  1460. }
  1461. sub format_table {
  1462. my $self = shift;
  1463. my (%args) = @_;
  1464. my $str = exists $args{text} ? $args{text} : '';
  1465. my @lines = split /\n/, $str;
  1466. my @rows;
  1467. my $line_count = scalar(@lines);
  1468. for (my $i = 0; $i < $line_count; $i++) {
  1469. if ($lines[$i] !~ m/\|\s*$/) {
  1470. if ($i + 1 < $line_count) {
  1471. $lines[$i+1] = $lines[$i] . "\n" . $lines[$i+1] if $i+1 <= $#lines;
  1472. } else {
  1473. push @rows, $lines[$i];
  1474. }
  1475. } else {
  1476. push @rows, $lines[$i];
  1477. }
  1478. }
  1479. my ($tid, $tpadl, $tpadr, $tlang);
  1480. my $tclass = '';
  1481. my $tstyle = '';
  1482. my $talign = '';
  1483. if ($rows[0] =~ m/^table[^\.]/) {
  1484. my $row = $rows[0];
  1485. $row =~ s/^table//;
  1486. my $params = 1;
  1487. # process row parameters until none are left
  1488. while ($params) {
  1489. if ($row =~ m/^($tblalignre)/) {
  1490. # found row alignment
  1491. $talign .= $1;
  1492. $row = substr($row, length($1)) if $1;
  1493. redo if $1;
  1494. }
  1495. if ($row =~ m/^($clstypadre)/) {
  1496. # found a class/id/style/padding indicator
  1497. my $clsty = $1;
  1498. $row = substr($row, length($clsty)) if $clsty;
  1499. if ($clsty =~ m/{([^}]+)}/) {
  1500. $tstyle = $1;
  1501. $clsty =~ s/{([^}]+)}//;
  1502. redo if $tstyle;
  1503. }
  1504. if ($clsty =~ m/\(([A-Za-z0-9_\- ]+?)(?:#(.+?))?\)/ ||
  1505. $clsty =~ m/\(([A-Za-z0-9_\- ]+?)?(?:#(.+?))\)/) {
  1506. if ($1 || $2) {
  1507. $tclass = $1;
  1508. $tid = $2;
  1509. redo;
  1510. }
  1511. }
  1512. $tpadl = length($1) if $clsty =~ m/(\(+)/;
  1513. $tpadr = length($1) if $clsty =~ m/(\)+)/;
  1514. $tlang = $1 if $clsty =~ m/\[(.+?)\]/;
  1515. redo if $clsty;
  1516. }
  1517. $params = 0;
  1518. }
  1519. $row =~ s/\.\s+//;
  1520. $rows[0] = $row;
  1521. }
  1522. my $out = '';
  1523. my @cols = split /\|/, $rows[0].' ';
  1524. my (@colalign, @rowspans);
  1525. foreach my $row (@rows) {
  1526. my @cols = split /\|/, $row.' ';
  1527. my $colcount = $#cols;
  1528. pop @cols;
  1529. my $colspan = 0;
  1530. my $row_out = '';
  1531. my ($rowclass, $rowid, $rowalign, $rowstyle, $rowheader);
  1532. $cols[0] = '' if !defined $cols[0];
  1533. if ($cols[0] =~ m/_/) {
  1534. $cols[0] =~ s/_//g;
  1535. $rowheader = 1;
  1536. }
  1537. if ($cols[0] =~ m/{([^}]+)}/) {
  1538. $rowstyle = $1;
  1539. $cols[0] =~ s/{[^}]+}//g;
  1540. }
  1541. if ($cols[0] =~ m/\(([^\#]+?)?(#(.+))?\)/) {
  1542. $rowclass = $1;
  1543. $rowid = $3;
  1544. $cols[0] =~ s/\([^\)]+\)//g;
  1545. }
  1546. $rowalign = $1 if $cols[0] =~ m/($alignre)/;
  1547. for (my $c = $colcount - 1; $c > 0; $c--) {
  1548. if ($rowspans[$c]) {
  1549. $rowspans[$c]--;
  1550. next if $rowspans[$c] > 1;
  1551. }
  1552. my ($colclass, $colid, $header, $colparams, $colpadl, $colpadr, $collang);
  1553. my $colstyle = '';
  1554. my $colalign = $colalign[$c];
  1555. my $col = pop @cols;
  1556. $col ||= '';
  1557. my $attrs = '';
  1558. if ($col =~ m/^(((_|[\/\\]\d+|$alignre|$clstypadre)+)\. )/) {
  1559. my $colparams = $2;
  1560. $col = substr($col, length($1));
  1561. my $params = 1;
  1562. # keep processing column parameters until there
  1563. # are none left...
  1564. while ($params) {
  1565. if ($colparams =~ m/^(_|$alignre)/g) {
  1566. # found alignment or heading indicator
  1567. $attrs .= $1;
  1568. $colparams = substr($colparams, pos($colparams)) if $1;
  1569. redo if $1;
  1570. }
  1571. if ($colparams =~ m/^($clstypadre)/g) {
  1572. # found a class/id/style/padding marker
  1573. my $clsty = $1;
  1574. $colparams = substr($colparams, pos($colparams)) if $clsty;
  1575. if ($clsty =~ m/{([^}]+)}/) {
  1576. $colstyle = $1;
  1577. $clsty =~ s/{([^}]+)}//;
  1578. }
  1579. if ($clsty =~ m/\(([A-Za-z0-9_\- ]+?)(?:#(.+?))?\)/ ||
  1580. $clsty =~ m/\(([A-Za-z0-9_\- ]+?)?(?:#(.+?))\)/) {
  1581. if ($1 || $2) {
  1582. $colclass = $1;
  1583. $colid = $2;
  1584. if ($colclass) {
  1585. $clsty =~ s/\([A-Za-z0-9_\- ]+?(#.*?)?\)//g;
  1586. } elsif ($colid) {
  1587. $clsty =~ s/\(#.+?\)//g;
  1588. }
  1589. }
  1590. }
  1591. if ($clsty =~ m/(\(+)/) {
  1592. $colpadl = length($1);
  1593. $clsty =~ s/\(+//;
  1594. }
  1595. if ($clsty =~ m/(\)+)/) {
  1596. $colpadr = length($1);
  1597. $clsty =~ s/\)+//;
  1598. }
  1599. if ($clsty =~ m/\[(.+?)\]/) {
  1600. $collang = $1;
  1601. $clsty =~ s/\[.+?\]//;
  1602. }
  1603. redo if $clsty;
  1604. }
  1605. if ($colparams =~ m/^\\(\d+)/) {
  1606. $colspan = $1;
  1607. $colparams = substr($colparams, length($1)+1);
  1608. redo if $1;
  1609. }
  1610. if ($colparams =~ m/\/(\d+)/) {
  1611. $rowspans[$c] = $1 if $1;
  1612. $colparams = substr($colparams, length($1)+1);
  1613. redo if $1;
  1614. }
  1615. $params = 0;
  1616. }
  1617. }
  1618. if (length($attrs)) {
  1619. $header = 1 if $attrs =~ m/_/;
  1620. $colalign = '' if $attrs =~ m/($alignre)/ && length($1);
  1621. # determine column alignment
  1622. if ($attrs =~ m/<>/) {
  1623. $colalign .= '<>';
  1624. } elsif ($attrs =~ m/</) {
  1625. $colalign .= '<';
  1626. } elsif ($attrs =~ m/=/) {
  1627. $colalign = '=';
  1628. } elsif ($attrs =~ m/>/) {
  1629. $colalign = '>';
  1630. }
  1631. if ($attrs =~ m/\^/) {
  1632. $colalign .= '^';
  1633. } elsif ($attrs =~ m/~/) {
  1634. $colalign .= '~';
  1635. } elsif ($attrs =~ m/-/) {
  1636. $colalign .= '-';
  1637. }
  1638. }
  1639. $header = 1 if $rowheader;
  1640. $colalign[$c] = $colalign if $header;
  1641. $col =~ s/^ +//; $col =~ s/ +$//;
  1642. if (length($col)) {
  1643. # create one cell tag
  1644. my $rowspan = $rowspans[$c] || 0;
  1645. my $col_out = '<' . ($header ? 'th' : 'td');
  1646. if (defined $colalign) {
  1647. # horizontal, vertical alignment
  1648. my $halign = _halign($colalign);
  1649. $col_out .= qq{ align="$halign"} if $halign;
  1650. my $valign = _valign($colalign);
  1651. $col_out .= qq{ valign="$valign"} if $valign;
  1652. }
  1653. # apply css attributes, row, column spans
  1654. $colstyle .= qq{;padding-left:${colpadl}em} if $colpadl;
  1655. $colstyle .= qq{;padding-right:${colpadr}em} if $colpadr;
  1656. $col_out .= qq{ class="$colclass"} if $colclass;
  1657. $col_out .= qq{ id="$colid"} if $colid;
  1658. $colstyle =~ s/^;// if $colstyle;
  1659. $col_out .= qq{ style="$colstyle"} if $colstyle;
  1660. $col_out .= qq{ lang="$collang"} if $collang;
  1661. $col_out .= qq{ colspan="$colspan"} if $colspan > 1;
  1662. $col_out .= qq{ rowspan="$rowspan"} if ($rowspan||0) > 1;
  1663. $col_out .= '>';
  1664. # if the content of this cell has newlines OR matches
  1665. # our paragraph block signature, process it as a full-blown
  1666. # textile document
  1667. if (($col =~ m/\n\n/) ||
  1668. ($col =~ m/^(?:$halignre|$clstypadre*)*
  1669. [\*\#]
  1670. (?:$clstypadre*|$halignre)*\ /x)) {
  1671. $col_out .= $self->textile($col);
  1672. } else {
  1673. $col_out .= $self->format_paragraph(text => $col);
  1674. }
  1675. $col_out .= '</' . ($header ? 'th' : 'td') . '>';
  1676. $row_out = $col_out . $row_out;
  1677. $colspan = 0 if $colspan;
  1678. } else {
  1679. $colspan = 1 if $colspan == 0;
  1680. $colspan++;
  1681. }
  1682. }
  1683. if ($colspan > 1) {
  1684. # handle the spanned column if we came up short
  1685. $colspan--;
  1686. $row_out = qq{<td}
  1687. . ($colspan>1 ? qq{ colspan="$colspan"} : '')
  1688. . qq{></td>$row_out};
  1689. }
  1690. # build one table row
  1691. $out .= qq{<tr};
  1692. if ($rowalign) {
  1693. my $valign = _valign($rowalign);
  1694. $out .= qq{ valign="$valign"} if $valign;
  1695. }
  1696. $out .= qq{ class="$rowclass"} if $rowclass;
  1697. $out .= qq{ id="$rowid"} if $rowid;
  1698. $out .= qq{ style="$rowstyle"} if $rowstyle;
  1699. $out .= qq{>$row_out</tr>};
  1700. }
  1701. # now, form the table tag itself
  1702. my $table = '';
  1703. $table .= qq{<table};
  1704. if ($talign) {
  1705. if ($self->{css_mode}) {
  1706. # horizontal alignment
  1707. my $alignment = _halign($talign);
  1708. if ($talign eq '=') {
  1709. $tstyle .= ';margin-left:auto;margin-right:auto';
  1710. } else {
  1711. $tstyle .= ';float:'.$alignment if $alignment;
  1712. }
  1713. $tclass .= ' '.$alignment if $alignment;
  1714. } else {
  1715. my $alignment = _halign($talign);
  1716. $table .= qq{ align="$alignment"} if $alignment;
  1717. }
  1718. }
  1719. $tstyle .= qq{;padding-left:${tpadl}em} if $tpadl;
  1720. $tstyle .= qq{;padding-right:${tpadr}em} if $tpadr;
  1721. $tclass =~ s/^ // if $tclass;
  1722. $table .= qq{ class="$tclass"} if $tclass;
  1723. $table .= qq{ id="$tid"} if $tid;
  1724. $tstyle =~ s/^;// if $tstyle;
  1725. $table .= qq{ style="$tstyle"} if $tstyle;
  1726. $table .= qq{ lang="$tlang"} if $tlang;
  1727. $table .= qq{ cellspacing="0"} if $tclass || $tid || $tstyle;
  1728. $table .= qq{>$out</table>};
  1729. if ($table =~ m|<tr></tr>|) {
  1730. # exception -- something isn't right so return fail case
  1731. return undef;
  1732. }
  1733. $table;
  1734. }
  1735. sub apply_filters {
  1736. my $self = shift;
  1737. my (%args) = @_;
  1738. my $text = $args{text};
  1739. return '' unless defined $text;
  1740. my $list = $args{filters};
  1741. my $filters = $self->{filters};
  1742. return $text unless (ref $filters) eq 'HASH';
  1743. my $param = $self->filter_param;
  1744. foreach my $filter (@$list) {
  1745. next unless exists $filters->{$filter};
  1746. if ((ref $filters->{$filter}) eq 'CODE') {
  1747. $text = $filters->{$filter}->($text, $param);
  1748. }
  1749. }
  1750. $text;
  1751. }
  1752. # minor utility / formatting routines
  1753. {
  1754. my $Have_Entities = eval 'use HTML::Entities; 1' ? 1 : 0;
  1755. sub encode_html {
  1756. my $self = shift;
  1757. my($html, $can_double_encode) = @_;
  1758. return '' unless defined $html;
  1759. if ($Have_Entities && $self->{char_encoding}) {
  1760. $html = HTML::Entities::encode_entities($html);
  1761. } else {
  1762. $html = $self->encode_html_basic($html, $can_double_encode);
  1763. }
  1764. $html;
  1765. }
  1766. sub decode_html {
  1767. my $self = shift;
  1768. my ($html) = @_;
  1769. $html =~ s!&quot;!"!g;
  1770. $html =~ s!&amp;!&!g;
  1771. $html =~ s!&lt;!<!g;
  1772. $html =~ s!&gt;!>!g;
  1773. $html;
  1774. }
  1775. sub encode_html_basic {
  1776. my $self = shift;
  1777. my($html, $can_double_encode) = @_;
  1778. return '' unless defined $html;
  1779. return $html unless $html =~ m/[^\w\s]/;
  1780. if ($can_double_encode) {
  1781. $html =~ s!&!&amp;!g;
  1782. } else {
  1783. ## Encode any & not followed by something that looks like
  1784. ## an entity, numeric or otherwise.
  1785. $html =~ s/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w{1,8});)/&amp;/g;
  1786. }
  1787. $html =~ s!"!&quot;!g;
  1788. $html =~ s!<!&lt;!g;
  1789. $html =~ s!>!&gt;!g;
  1790. $html;
  1791. }
  1792. }
  1793. {
  1794. my $Have_ImageSize = eval 'use Image::Size; 1' ? 1 : 0;
  1795. sub image_size {
  1796. my $self = shift;
  1797. my ($file) = @_;
  1798. if ($Have_ImageSize) {
  1799. if (-f $file) {
  1800. return Image::Size::imgsize($file);
  1801. } else {
  1802. if (my $docroot = $self->docroot) {
  1803. require File::Spec;
  1804. my $fullpath = File::Spec->catfile($docroot, $file);
  1805. if (-f $fullpath) {
  1806. return Image::Size::imgsize($fullpath);
  1807. }
  1808. }
  1809. }
  1810. }
  1811. undef;
  1812. }
  1813. }
  1814. sub encode_url {
  1815. my $self = shift;
  1816. my($str) = @_;
  1817. $str =~ s!([^A-Za-z0-9_\.\-\+\&=\%;])!
  1818. ord($1) > 255 ? '%u' . (uc sprintf("%04x", ord($1)))
  1819. : '%' . (uc sprintf("%02x", ord($1)))!egx;
  1820. $str;
  1821. }
  1822. sub mail_encode {
  1823. my $self = shift;
  1824. my ($addr) = @_;
  1825. # granted, this is simple, but it gives off warm fuzzies
  1826. $addr =~ s!([^\$])!
  1827. ord($1) > 255 ? '%u' . (uc sprintf("%04x", ord($1)))
  1828. : '%' . (uc sprintf("%02x", ord($1)))!egx;
  1829. $addr;
  1830. }
  1831. sub process_quotes {
  1832. # stub routine for now. subclass and implement.
  1833. my $self = shift;
  1834. my ($str) = @_;
  1835. $str;
  1836. }
  1837. # a default set of macros for the {...} macro syntax
  1838. # just a handy way to write a lot of the international characters
  1839. # and some commonly used symbols
  1840. sub default_macros {
  1841. my $self = shift;
  1842. # <, >, " must be html entities in the macro text since
  1843. # those values are escaped by the time they are processed
  1844. # for macros.
  1845. return {
  1846. 'c|' => '&#162;', # CENT SIGN
  1847. '|c' => '&#162;', # CENT SIGN
  1848. 'L-' => '&#163;', # POUND SIGN
  1849. '-L' => '&#163;', # POUND SIGN
  1850. 'Y=' => '&#165;', # YEN SIGN
  1851. '=Y' => '&#165;', # YEN SIGN
  1852. '(c)' => '&#169;', # COPYRIGHT SIGN
  1853. '&lt;&lt;' => '&#171;', # LEFT-POINTING DOUBLE ANGLE QUOTATION
  1854. '(r)' => '&#174;', # REGISTERED SIGN
  1855. '+_' => '&#177;', # PLUS-MINUS SIGN
  1856. '_+' => '&#177;', # PLUS-MINUS SIGN
  1857. '&gt;&gt;' => '&#187;', # RIGHT-POINTING DOUBLE ANGLE QUOTATION
  1858. '1/4' => '&#188;', # VULGAR FRACTION ONE QUARTER
  1859. '1/2' => '&#189;', # VULGAR FRACTION ONE HALF
  1860. '3/4' => '&#190;', # VULGAR FRACTION THREE QUARTERS
  1861. 'A`' => '&#192;', # LATIN CAPITAL LETTER A WITH GRAVE
  1862. '`A' => '&#192;', # LATIN CAPITAL LETTER A WITH GRAVE
  1863. 'A\'' => '&#193;', # LATIN CAPITAL LETTER A WITH ACUTE
  1864. '\'A' => '&#193;', # LATIN CAPITAL LETTER A WITH ACUTE
  1865. 'A^' => '&#194;', # LATIN CAPITAL LETTER A WITH CIRCUMFLEX
  1866. '^A' => '&#194;', # LATIN CAPITAL LETTER A WITH CIRCUMFLEX
  1867. 'A~' => '&#195;', # LATIN CAPITAL LETTER A WITH TILDE
  1868. '~A' => '&#195;', # LATIN CAPITAL LETTER A WITH TILDE
  1869. 'A"' => '&#196;', # LATIN CAPITAL LETTER A WITH DIAERESIS
  1870. '"A' => '&#196;', # LATIN CAPITAL LETTER A WITH DIAERESIS
  1871. 'Ao' => '&#197;', # LATIN CAPITAL LETTER A WITH RING ABOVE
  1872. 'oA' => '&#197;', # LATIN CAPITAL LETTER A WITH RING ABOVE
  1873. 'AE' => '&#198;', # LATIN CAPITAL LETTER AE
  1874. 'C,' => '&#199;', # LATIN CAPITAL LETTER C WITH CEDILLA
  1875. ',C' => '&#199;', # LATIN CAPITAL LETTER C WITH CEDILLA
  1876. 'E`' => '&#200;', # LATIN CAPITAL LETTER E WITH GRAVE
  1877. '`E' => '&#200;', # LATIN CAPITAL LETTER E WITH GRAVE
  1878. 'E\'' => '&#201;', # LATIN CAPITAL LETTER E WITH ACUTE
  1879. '\'E' => '&#201;', # LATIN CAPITAL LETTER E WITH ACUTE
  1880. 'E^' => '&#202;', # LATIN CAPITAL LETTER E WITH CIRCUMFLEX
  1881. '^E' => '&#202;', # LATIN CAPITAL LETTER E WITH CIRCUMFLEX
  1882. 'E"' => '&#203;', # LATIN CAPITAL LETTER E WITH DIAERESIS
  1883. '"E' => '&#203;', # LATIN CAPITAL LETTER E WITH DIAERESIS
  1884. 'I`' => '&#204;', # LATIN CAPITAL LETTER I WITH GRAVE
  1885. '`I' => '&#204;', # LATIN CAPITAL LETTER I WITH GRAVE
  1886. 'I\'' => '&#205;', # LATIN CAPITAL LETTER I WITH ACUTE
  1887. '\'I' => '&#205;', # LATIN CAPITAL LETTER I WITH ACUTE
  1888. 'I^' => '&#206;', # LATIN CAPITAL LETTER I WITH CIRCUMFLEX
  1889. '^I' => '&#206;', # LATIN CAPITAL LETTER I WITH CIRCUMFLEX
  1890. 'I"' => '&#207;', # LATIN CAPITAL LETTER I WITH DIAERESIS
  1891. '"I' => '&#207;', # LATIN CAPITAL LETTER I WITH DIAERESIS
  1892. 'D-' => '&#208;', # LATIN CAPITAL LETTER ETH
  1893. '-D' => '&#208;', # LATIN CAPITAL LETTER ETH
  1894. 'N~' => '&#209;', # LATIN CAPITAL LETTER N WITH TILDE
  1895. '~N' => '&#209;', # LATIN CAPITAL LETTER N WITH TILDE
  1896. 'O`' => '&#210;', # LATIN CAPITAL LETTER O WITH GRAVE
  1897. '`O' => '&#210;', # LATIN CAPITAL LETTER O WITH GRAVE
  1898. 'O\'' => '&#211;', # LATIN CAPITAL LETTER O WITH ACUTE
  1899. '\'O' => '&#211;', # LATIN CAPITAL LETTER O WITH ACUTE
  1900. 'O^' => '&#212;', # LATIN CAPITAL LETTER O WITH CIRCUMFLEX
  1901. '^O' => '&#212;', # LATIN CAPITAL LETTER O WITH CIRCUMFLEX
  1902. 'O~' => '&#213;', # LATIN CAPITAL LETTER O WITH TILDE
  1903. '~O' => '&#213;', # LATIN CAPITAL LETTER O WITH TILDE
  1904. 'O"' => '&#214;', # LATIN CAPITAL LETTER O WITH DIAERESIS
  1905. '"O' => '&#214;', # LATIN CAPITAL LETTER O WITH DIAERESIS
  1906. 'O/' => '&#216;', # LATIN CAPITAL LETTER O WITH STROKE
  1907. '/O' => '&#216;', # LATIN CAPITAL LETTER O WITH STROKE
  1908. 'U`' => '&#217;', # LATIN CAPITAL LETTER U WITH GRAVE
  1909. '`U' => '&#217;', # LATIN CAPITAL LETTER U WITH GRAVE
  1910. 'U\'' => '&#218;', # LATIN CAPITAL LETTER U WITH ACUTE
  1911. '\'U' => '&#218;', # LATIN CAPITAL LETTER U WITH ACUTE
  1912. 'U^' => '&#219;', # LATIN CAPITAL LETTER U WITH CIRCUMFLEX
  1913. '^U' => '&#219;', # LATIN CAPITAL LETTER U WITH CIRCUMFLEX
  1914. 'U"' => '&#220;', # LATIN CAPITAL LETTER U WITH DIAERESIS
  1915. '"U' => '&#220;', # LATIN CAPITAL LETTER U WITH DIAERESIS
  1916. 'Y\'' => '&#221;', # LATIN CAPITAL LETTER Y WITH ACUTE
  1917. '\'Y' => '&#221;', # LATIN CAPITAL LETTER Y WITH ACUTE
  1918. 'a`' => '&#224;', # LATIN SMALL LETTER A WITH GRAVE
  1919. '`a' => '&#224;', # LATIN SMALL LETTER A WITH GRAVE
  1920. 'a\'' => '&#225;', # LATIN SMALL LETTER A WITH ACUTE
  1921. '\'a' => '&#225;', # LATIN SMALL LETTER A WITH ACUTE
  1922. 'a^' => '&#226;', # LATIN SMALL LETTER A WITH CIRCUMFLEX
  1923. '^a' => '&#226;', # LATIN SMALL LETTER A WITH CIRCUMFLEX
  1924. 'a~' => '&#227;', # LATIN SMALL LETTER A WITH TILDE
  1925. '~a' => '&#227;', # LATIN SMALL LETTER A WITH TILDE
  1926. 'a"' => '&#228;', # LATIN SMALL LETTER A WITH DIAERESIS
  1927. '"a' => '&#228;', # LATIN SMALL LETTER A WITH DIAERESIS
  1928. 'ao' => '&#229;', # LATIN SMALL LETTER A WITH RING ABOVE
  1929. 'oa' => '&#229;', # LATIN SMALL LETTER A WITH RING ABOVE
  1930. 'ae' => '&#230;', # LATIN SMALL LETTER AE
  1931. 'c,' => '&#231;', # LATIN SMALL LETTER C WITH CEDILLA
  1932. ',c' => '&#231;', # LATIN SMALL LETTER C WITH CEDILLA
  1933. 'e`' => '&#232;', # LATIN SMALL LETTER E WITH GRAVE
  1934. '`e' => '&#232;', # LATIN SMALL LETTER E WITH GRAVE
  1935. 'e\'' => '&#233;', # LATIN SMALL LETTER E WITH ACUTE
  1936. '\'e' => '&#233;', # LATIN SMALL LETTER E WITH ACUTE
  1937. 'e^' => '&#234;', # LATIN SMALL LETTER E WITH CIRCUMFLEX
  1938. '^e' => '&#234;', # LATIN SMALL LETTER E WITH CIRCUMFLEX
  1939. 'e"' => '&#235;', # LATIN SMALL LETTER E WITH DIAERESIS
  1940. '"e' => '&#235;', # LATIN SMALL LETTER E WITH DIAERESIS
  1941. 'i`' => '&#236;', # LATIN SMALL LETTER I WITH GRAVE
  1942. '`i' => '&#236;', # LATIN SMALL LETTER I WITH GRAVE
  1943. 'i\'' => '&#237;', # LATIN SMALL LETTER I WITH ACUTE
  1944. '\'i' => '&#237;', # LATIN SMALL LETTER I WITH ACUTE
  1945. 'i^' => '&#238;', # LATIN SMALL LETTER I WITH CIRCUMFLEX
  1946. '^i' => '&#238;', # LATIN SMALL LETTER I WITH CIRCUMFLEX
  1947. 'i"' => '&#239;', # LATIN SMALL LETTER I WITH DIAERESIS
  1948. '"i' => '&#239;', # LATIN SMALL LETTER I WITH DIAERESIS
  1949. 'n~' => '&#241;', # LATIN SMALL LETTER N WITH TILDE
  1950. '~n' => '&#241;', # LATIN SMALL LETTER N WITH TILDE
  1951. 'o`' => '&#242;', # LATIN SMALL LETTER O WITH GRAVE
  1952. '`o' => '&#242;', # LATIN SMALL LETTER O WITH GRAVE
  1953. 'o\'' => '&#243;', # LATIN SMALL LETTER O WITH ACUTE
  1954. '\'o' => '&#243;', # LATIN SMALL LETTER O WITH ACUTE
  1955. 'o^' => '&#244;', # LATIN SMALL LETTER O WITH CIRCUMFLEX
  1956. '^o' => '&#244;', # LATIN SMALL LETTER O WITH CIRCUMFLEX
  1957. 'o~' => '&#245;', # LATIN SMALL LETTER O WITH TILDE
  1958. '~o' => '&#245;', # LATIN SMALL LETTER O WITH TILDE
  1959. 'o"' => '&#246;', # LATIN SMALL LETTER O WITH DIAERESIS
  1960. '"o' => '&#246;', # LATIN SMALL LETTER O WITH DIAERESIS
  1961. ':-' => '&#247;', # DIVISION SIGN
  1962. '-:' => '&#247;', # DIVISION SIGN
  1963. 'o/' => '&#248;', # LATIN SMALL LETTER O WITH STROKE
  1964. '/o' => '&#248;', # LATIN SMALL LETTER O WITH STROKE
  1965. 'u`' => '&#249;', # LATIN SMALL LETTER U WITH GRAVE
  1966. '`u' => '&#249;', # LATIN SMALL LETTER U WITH GRAVE
  1967. 'u\'' => '&#250;', # LATIN SMALL LETTER U WITH ACUTE
  1968. '\'u' => '&#250;', # LATIN SMALL LETTER U WITH ACUTE
  1969. 'u^' => '&#251;', # LATIN SMALL LETTER U WITH CIRCUMFLEX
  1970. '^u' => '&#251;', # LATIN SMALL LETTER U WITH CIRCUMFLEX
  1971. 'u"' => '&#252;', # LATIN SMALL LETTER U WITH DIAERESIS
  1972. '"u' => '&#252;', # LATIN SMALL LETTER U WITH DIAERESIS
  1973. 'y\'' => '&#253;', # LATIN SMALL LETTER Y WITH ACUTE
  1974. '\'y' => '&#253;', # LATIN SMALL LETTER Y WITH ACUTE
  1975. 'y"' => '&#255', # LATIN SMALL LETTER Y WITH DIAERESIS
  1976. '"y' => '&#255', # LATIN SMALL LETTER Y WITH DIAERESIS
  1977. 'OE' => '&#338;', # LATIN CAPITAL LIGATURE OE
  1978. 'oe' => '&#339;', # LATIN SMALL LIGATURE OE
  1979. '*' => '&#2022;', # BULLET
  1980. 'Fr' => '&#8355;', # FRENCH FRANC SIGN
  1981. 'L=' => '&#8356;', # LIRA SIGN
  1982. '=L' => '&#8356;', # LIRA SIGN
  1983. 'Rs' => '&#8360;', # RUPEE SIGN
  1984. 'C=' => '&#8364;', # EURO SIGN
  1985. '=C' => '&#8364;', # EURO SIGN
  1986. 'tm' => '&#8482;', # TRADE MARK SIGN
  1987. '&lt;-' => '&#8592;', # LEFTWARDS ARROW
  1988. '-&gt;' => '&#8594;', # RIGHTWARDS ARROW
  1989. '&lt;=' => '&#8656;', # LEFTWARDS DOUBLE ARROW
  1990. '=&gt;' => '&#8658;', # RIGHTWARDS DOUBLE ARROW
  1991. '=/' => '&#8800;', # NOT EQUAL TO
  1992. '/=' => '&#8800;', # NOT EQUAL TO
  1993. '&lt;_' => '&#8804;', # LESS-THAN OR EQUAL TO
  1994. '_&lt;' => '&#8804;', # LESS-THAN OR EQUAL TO
  1995. '&gt;_' => '&#8805;', # GREATER-THAN OR EQUAL TO
  1996. '_&gt;' => '&#8805;', # GREATER-THAN OR EQUAL TO
  1997. ':(' => '&#9785;', # WHITE FROWNING FACE
  1998. ':)' => '&#9786;', # WHITE SMILING FACE
  1999. 'spade' => '&#9824;', # BLACK SPADE SUIT
  2000. 'club' => '&#9827;', # BLACK CLUB SUIT
  2001. 'heart' => '&#9829;', # BLACK HEART SUIT
  2002. 'diamond' => '&#9830;', # BLACK DIAMOND SUIT
  2003. };
  2004. }
  2005. # "private", internal routines
  2006. sub _css_defaults {
  2007. my $self = shift;
  2008. my %css_defaults = (
  2009. class_align_right => 'right',
  2010. class_align_left => 'left',
  2011. class_align_center => 'center',
  2012. class_align_top => 'top',
  2013. class_align_bottom => 'bottom',
  2014. class_align_middle => 'middle',
  2015. class_align_justify => 'justify',
  2016. class_caps => 'caps',
  2017. class_footnote => 'footnote',
  2018. id_footnote_prefix => 'fn',
  2019. );
  2020. $self->css(\%css_defaults);
  2021. }
  2022. sub _halign {
  2023. my ($align) = @_;
  2024. if ($align =~ m/<>/) {
  2025. return 'justify';
  2026. } elsif ($align =~ m/</) {
  2027. return 'left';
  2028. } elsif ($align =~ m/>/) {
  2029. return 'right';
  2030. } elsif ($align =~ m/=/) {
  2031. return 'center';
  2032. }
  2033. return '';
  2034. }
  2035. sub _valign {
  2036. my ($align) = @_;
  2037. if ($align =~ m/\^/) {
  2038. return 'top';
  2039. } elsif ($align =~ m/~/) {
  2040. return 'bottom';
  2041. } elsif ($align =~ m/-/) {
  2042. return 'middle';
  2043. }
  2044. return '';
  2045. }
  2046. sub _imgalign {
  2047. my ($align) = @_;
  2048. $align =~ s/(<>|=)//g;
  2049. return _valign($align) || _halign($align);
  2050. }
  2051. sub _strip_borders {
  2052. my ($pre, $post) = @_;
  2053. if ($$post && $$pre && ((my $open = substr($$pre, 0, 1)) =~ m/[{[]/)) {
  2054. my $close = substr($$post, 0, 1);
  2055. if ((($open eq '{') && ($close eq '}')) ||
  2056. (($open eq '[') && ($close eq ']'))) {
  2057. $$pre = substr($$pre, 1);
  2058. $$post = substr($$post, 1);
  2059. } else {
  2060. $close = substr($$post, -1, 1) if $close !~ m/[}\]]/;
  2061. if ((($open eq '{') && ($close eq '}')) ||
  2062. (($open eq '[') && ($close eq ']'))) {
  2063. $$pre = substr($$pre, 1);
  2064. $$post = substr($$post, 0, length($$post) - 1);
  2065. }
  2066. }
  2067. }
  2068. }
  2069. sub _repl {
  2070. push @{$_[0]}, $_[1];
  2071. '<textile#'.(scalar(@{$_[0]})).'>';
  2072. }
  2073. sub _tokenize {
  2074. my $str = shift;
  2075. my $pos = 0;
  2076. my $len = length $str;
  2077. my @tokens;
  2078. my $depth = 6;
  2079. my $nested_tags = join('|', ('(?:</?[A-Za-z0-9:]+ \s? (?:[^<>]') x $depth)
  2080. . (')*>)' x $depth);
  2081. my $match = qr/(?s: <! ( -- .*? -- \s* )+ > )| # comment
  2082. (?s: <\? .*? \?> )| # processing instruction
  2083. (?s: <\% .*? \%> )| # ASP-like
  2084. (?:$nested_tags)|
  2085. (?:$codere)/x; # nested tags
  2086. while ($str =~ m/($match)/g) {
  2087. my $whole_tag = $1;
  2088. my $sec_start = pos $str;
  2089. my $tag_start = $sec_start - length $whole_tag;
  2090. if ($pos < $tag_start) {
  2091. push @tokens, ['text', substr($str, $pos, $tag_start - $pos)];
  2092. }
  2093. if ($whole_tag =~ m/^[[{]?\@/) {
  2094. push @tokens, ['text', $whole_tag];
  2095. } else {
  2096. # this clever hack allows us to preserve \n within tags.
  2097. # this is restored at the end of the format_paragraph method
  2098. #$whole_tag =~ s/\n/\r/g;
  2099. $whole_tag =~ s/\n/\001/g;
  2100. push @tokens, ['tag', $whole_tag];
  2101. }
  2102. $pos = pos $str;
  2103. }
  2104. push @tokens, ['text', substr($str, $pos, $len - $pos)] if $pos < $len;
  2105. \@tokens;
  2106. }
  2107. 1;
  2108. __END__
  2109. =head1 NAME
  2110. Text::Textile - A humane web text generator.
  2111. =head1 SYNOPSIS
  2112. use Text::Textile qw(textile);
  2113. my $text = <<EOT;
  2114. h1. Heading
  2115. A _simple_ demonstration of Textile markup.
  2116. * One
  2117. * Two
  2118. * Three
  2119. "More information":http://www.textism.com/tools/textile is available.
  2120. EOT
  2121. # procedural usage
  2122. my $html = textile($text);
  2123. print $html;
  2124. # OOP usage
  2125. my $textile = new Text::Textile;
  2126. $html = $textile->process($text);
  2127. print $html;
  2128. =head1 ABSTRACT
  2129. Text::Textile is a Perl-based implementation of Dean Allen's Textile
  2130. syntax. Textile is shorthand for doing common formatting tasks.
  2131. =head1 METHODS
  2132. =head2 new( [%options] )
  2133. Instantiates a new Text::Textile object. Optional options
  2134. can be passed to initialize the object. Attributes for the
  2135. options key are the same as the get/set method names
  2136. documented here.
  2137. =head2 set( $attribute, $value )
  2138. Used to set Textile attributes. Attribute names are the same
  2139. as the get/set method names documented here.
  2140. =head2 get( $attribute )
  2141. Used to get Textile attributes. Attribute names are the same
  2142. as the get/set method names documented here.
  2143. =head2 disable_html( [$disable] )
  2144. Gets or sets the "disable html" control, which allows you to
  2145. prevent HTML tags from being used within the text processed.
  2146. Any HTML tags encountered will be removed if disable html is
  2147. enabled. Default behavior is to allow HTML.
  2148. =head2 flavor( [$flavor] )
  2149. Assigns the HTML flavor of output from Text::Textile. Currently
  2150. these are the valid choices: html, xhtml (behaves like "xhtml1"),
  2151. xhtml1, xhtml2. Default flavor is "xhtml1".
  2152. Note that the xhtml2 flavor support is experimental and incomplete
  2153. (and will remain that way until the XHTML 2.0 draft becomes a
  2154. proper recommendation).
  2155. =head2 css( [$css] )
  2156. Gets or sets the css support for Textile. If css is enabled,
  2157. Textile will emit CSS rules. You may pass a 1 or 0 to enable
  2158. or disable CSS behavior altogether. If you pass a hashref,
  2159. you may assign the CSS class names that are used by
  2160. Text::Textile. The following key names for such a hash are
  2161. recognized:
  2162. =over
  2163. =item class_align_right
  2164. defaults to 'right'
  2165. =item class_align_left
  2166. defaults to 'left'
  2167. =item class_align_center
  2168. defaults to 'center'
  2169. =item class_align_top
  2170. defaults to 'top'
  2171. =item class_align_bottom
  2172. defaults to 'bottom'
  2173. =item class_align_middle
  2174. defaults to 'middle'
  2175. =item class_align_justify
  2176. defaults to 'justify'
  2177. =item class_caps
  2178. defaults to 'caps'
  2179. =item class_footnote
  2180. defaults to 'footnote'
  2181. =item id_footnote_prefix
  2182. defaults to 'fn'
  2183. =back
  2184. =head2 charset( [$charset] )
  2185. Gets or sets the character set targetted for publication.
  2186. At this time, Text::Textile only changes its behavior
  2187. if the 'utf-8' character set is assigned.
  2188. Specifically, if utf-8 is requested, any special characters
  2189. created by Textile will be output as native utf-8 characters
  2190. rather than HTML entities.
  2191. =head2 docroot( [$path] )
  2192. Gets or sets the physical file path to root of document files.
  2193. This path is utilized when images are referenced and size
  2194. calculations are needed (the Image::Size module is used to read
  2195. the image dimensions).
  2196. =head2 trim_spaces( [$trim] )
  2197. Gets or sets the 'trim spaces' control flag. If enabled, this
  2198. will clear any lines that have only spaces on them (the newline
  2199. itself will remain).
  2200. =head2 preserve_spaces( [$preserve] )
  2201. Gets or sets the 'preserve spaces' control flag. If enabled, this
  2202. will replace any double spaces within the paragraph data with the
  2203. &#8195; HTML entity (wide space). The default is 0. Spaces will
  2204. pass through to the browser unchanged and render as a single space.
  2205. Note that this setting has no effect on spaces within E<lt>preE<gt>,
  2206. E<lt>codeE<gt> blocks or E<lt>scriptE<gt> sections.
  2207. =head2 filter_param( [$data] )
  2208. Gets or sets a parameter that is passed to filters.
  2209. =head2 filters( [\%filters] )
  2210. Gets or sets a list of filters to make available for
  2211. Text::Textile to use. Returns a hash reference of the currently
  2212. assigned filters.
  2213. =head2 char_encoding( [$encode] )
  2214. Gets or sets the character encoding logical flag. If character
  2215. encoding is enabled, the HTML::Entities package is used to
  2216. encode special characters. If character encoding is disabled,
  2217. only <, >, " and & are encoded to HTML entities.
  2218. =head2 handle_quotes( [$handle] )
  2219. Gets or sets the "smart quoting" control flag. Returns the
  2220. current setting.
  2221. =head2 process( $str )
  2222. Alternative method for invoking the textile method.
  2223. =head2 textile( $str )
  2224. Can be called either procedurally or as a method. Transforms
  2225. $str using Textile markup rules.
  2226. =head2 format_paragraph( [$args] )
  2227. Processes a single paragraph. The following attributes are
  2228. allowed:
  2229. =over
  2230. =item text
  2231. The text to be processed.
  2232. =back
  2233. =head2 format_inline( [%args] )
  2234. Processes an inline string (plaintext) for Textile syntax.
  2235. The following attributes are allowed:
  2236. =over
  2237. =item text
  2238. The text to be processed.
  2239. =back
  2240. =head2 format_macro( %args )
  2241. Responsible for processing a particular macro. Arguments passed
  2242. include:
  2243. =over
  2244. =item pre
  2245. open brace character
  2246. =item post
  2247. close brace character
  2248. =item macro
  2249. the macro to be executed
  2250. =back
  2251. The return value from this method would be the replacement
  2252. text for the macro given. If the macro is not defined, it will
  2253. return pre + macro + post, thereby preserving the original
  2254. macro string.
  2255. =head2 format_cite( %args )
  2256. Processes text for a citation tag. The following attributes
  2257. are allowed:
  2258. =over
  2259. =item pre
  2260. Any text that comes before the citation.
  2261. =item text
  2262. The text that is being cited.
  2263. =item cite
  2264. The URL of the citation.
  2265. =item post
  2266. Any text that follows the citation.
  2267. =back
  2268. =head2 format_code( %args )
  2269. Processes '@...@' type blocks (code snippets). The following
  2270. attributes are allowed:
  2271. =over
  2272. =item text
  2273. The text of the code itself.
  2274. =item lang
  2275. The language (programming language) for the code.
  2276. =back
  2277. =head2 format_classstyle( $clsty, $class, $style )
  2278. Returns a string of tag attributes to accomodate the class,
  2279. style and symbols present in $clsty.
  2280. $clsty is checked for:
  2281. =over
  2282. =item C<{...}>
  2283. style rules. If present, they are appended to $style.
  2284. =item C<(...#...)>
  2285. class and/or ID name declaration
  2286. =item C<(> (one or more)
  2287. pad left characters
  2288. =item C<)> (one or more)
  2289. pad right characters
  2290. =item C<[ll]>
  2291. language declaration
  2292. =back
  2293. The attribute string returned will contain any combination
  2294. of class, id, style and/or lang attributes.
  2295. =head2 format_tag( %args )
  2296. Constructs an HTML tag. Accepted arguments:
  2297. =over
  2298. =item tag
  2299. the tag to produce
  2300. =item text
  2301. the text to output inside the tag
  2302. =item pre
  2303. text to produce before the tag
  2304. =item post
  2305. text to produce following the tag
  2306. =item clsty
  2307. class and/or style attributes that should be assigned to the tag.
  2308. =back
  2309. =head2 format_list( %args )
  2310. Takes a Textile formatted list (numeric or bulleted) and
  2311. returns the markup for it. Text that is passed in requires
  2312. substantial parsing, so the format_list method is a little
  2313. involved. But it should always produce a proper ordered
  2314. or unordered list. If it cannot (due to misbalanced input),
  2315. it will return the original text. Arguments accepted:
  2316. =over
  2317. =item text
  2318. The text to be processed.
  2319. =back
  2320. =head2 format_block( %args )
  2321. Processes '==xxxxx==' type blocks for filters. A filter
  2322. would follow the open '==' sequence and is specified within
  2323. pipe characters, like so:
  2324. ==|filter|text to be filtered==
  2325. You may specify multiple filters in the filter portion of
  2326. the string. Simply comma delimit the filters you desire
  2327. to execute. Filters are defined using the filters method.
  2328. =head2 format_link( %args )
  2329. Takes the Textile link attributes and transforms them into
  2330. a hyperlink.
  2331. =head2 format_url( %args )
  2332. Takes the given $url and transforms it appropriately.
  2333. =head2 format_span( %args )
  2334. =head2 format_image( %args )
  2335. Returns markup for the given image. $src is the location of
  2336. the image, $extra contains the optional height/width and/or
  2337. alt text. $url is an optional hyperlink for the image. $class
  2338. holds the optional CSS class attribute.
  2339. Arguments you may pass:
  2340. =over
  2341. =item src
  2342. The 'src' (URL) for the image. This may be a local path,
  2343. ideally starting with a '/'. Images can be located within
  2344. the file system if the docroot method is used to specify
  2345. where the docroot resides. If the image can be found, the
  2346. image_size method is used to determine the dimensions of
  2347. the image.
  2348. =item extra
  2349. Additional parameters for the image. This would include
  2350. alt text, height/width specification or scaling instructions.
  2351. =item align
  2352. Alignment attribute.
  2353. =item pre
  2354. Text to produce prior to the tag.
  2355. =item post
  2356. Text to produce following the tag.
  2357. =item link
  2358. Optional URL to connect with the image tag.
  2359. =item clsty
  2360. Class and/or style attributes.
  2361. =back
  2362. =head2 format_table( %args )
  2363. Takes a Wiki-ish string of data and transforms it into a full
  2364. table.
  2365. =head2 apply_filters( %args )
  2366. The following attributes are allowed:
  2367. =over
  2368. =item text
  2369. The text to be processed.
  2370. =item filters
  2371. An array reference of filter names to run for the given text.
  2372. =back
  2373. =head2 encode_html( $html, $can_double_encode )
  2374. Encodes input $html string, escaping characters as needed
  2375. to HTML entities. This relies on the HTML::Entities package
  2376. for full effect. If unavailable, encode_html_basic is used
  2377. as a fallback technique. If the "char_encoding" flag is
  2378. set to false, encode_html_basic is used exclusively.
  2379. =head2 decode_html( $html )
  2380. Decodes HTML entities in $html to their natural character
  2381. equivelants.
  2382. =head2 encode_html_basic( $html, $can_double_encode )
  2383. Encodes the input $html string for the following characters:
  2384. E<lt>, E<gt>, & and ". If $can_double_encode is true, all
  2385. ampersand characters are escaped even if they already were.
  2386. If $can_double_encode is false, ampersands are only escaped
  2387. when they aren't part of a HTML entity already.
  2388. =head2 image_size( $file )
  2389. Returns the size for the image identified in $file. This
  2390. method relies upon the Image::Size Perl package. If unavailable,
  2391. image_size will return undef. Otherwise, the expected return
  2392. value is a list of the width and height (in that order), in
  2393. pixels.
  2394. =head2 encode_url( $str )
  2395. Encodes the query portion of a URL, escaping characters
  2396. as necessary.
  2397. =head2 mail_encode( $email )
  2398. Encodes the email address in $email for 'mailto:' links.
  2399. =head2 process_quotes( $str )
  2400. Processes string, formatting plain quotes into curly quotes.
  2401. =head2 default_macros
  2402. Returns a hashref of macros that are assigned to be processed by
  2403. default within the format_inline method.
  2404. =head2 _halign( $alignment )
  2405. Returns the alignment keyword depending on the symbol passed.
  2406. =over
  2407. =item C<E<lt>E<gt>>
  2408. becomes 'justify'
  2409. =item C<E<lt>>
  2410. becomes 'left'
  2411. =item C<E<gt>>
  2412. becomes 'right'
  2413. =item C<=>
  2414. becomes 'center'
  2415. =back
  2416. =head2 _valign( $alignment )
  2417. Returns the alignment keyword depending on the symbol passed.
  2418. =over
  2419. =item C<^>
  2420. becomes 'top'
  2421. =item C<~>
  2422. becomes 'bottom'
  2423. =item C<->
  2424. becomes 'middle'
  2425. =back
  2426. =head2 _imgalign( $alignment )
  2427. Returns the alignment keyword depending on the symbol passed.
  2428. The following alignment symbols are recognized, and given
  2429. preference in the order listed:
  2430. =over
  2431. =item C<^>
  2432. becomes 'top'
  2433. =item C<~>
  2434. becomes 'bottom'
  2435. =item C<->
  2436. becomes 'middle'
  2437. =item C<E<lt>>
  2438. becomes 'left'
  2439. =item C<E<gt>>
  2440. becomes 'right'
  2441. =back
  2442. =head2 _repl( \@arr, $str )
  2443. An internal routine that takes a string and appends it to an array.
  2444. It returns a marker that is used later to restore the preserved
  2445. string.
  2446. =head2 _tokenize( $str )
  2447. An internal routine responsible for breaking up a string into
  2448. individual tag and plaintext elements.
  2449. =head2 _css_defaults
  2450. Sets the default CSS names for CSS controlled markup. This
  2451. is an internal function that should not be called directly.
  2452. =head2 _strip_borders( $pre, $post )
  2453. This utility routine will take 'border' characters off of
  2454. the given $pre and $post strings if they match one of these
  2455. conditions:
  2456. $pre starts with '[', $post ends with ']'
  2457. $pre starts with '{', $post ends with '}'
  2458. If neither condition is met, then the $pre and $post
  2459. values are left untouched.
  2460. =head1 SYNTAX
  2461. Text::Textile processes text in units of blocks and lines.
  2462. A block might also be considered a paragraph, since blocks
  2463. are separated from one another by a blank line. Blocks
  2464. can begin with a signature that helps identify the rest
  2465. of the block content. Block signatures include:
  2466. =over
  2467. =item p
  2468. A paragraph block. This is the default signature if no
  2469. signature is explicitly given. Paragraphs are formatted
  2470. with all the inline rules (see inline formatting) and
  2471. each line receives the appropriate markup rules for
  2472. the flavor of HTML in use. For example, newlines for XHTML
  2473. content receive a E<lt>br /E<gt> tag at the end of the line
  2474. (with the exception of the last line in the paragraph).
  2475. Paragraph blocks are enclosed in a E<lt>pE<gt> tag.
  2476. =item pre
  2477. A pre-formatted block of text. Textile will not add any
  2478. HTML tags for individual lines. Whitespace is also preserved.
  2479. Note that within a "pre" block, E<lt> and E<gt> are
  2480. translated into HTML entities automatically.
  2481. =item bc
  2482. A "bc" signature is short for "block code", which implies
  2483. a preformatted section like the 'pre' block, but it also
  2484. gets a E<lt>codeE<gt> tag (or for XHTML 2, a E<lt>blockcodeE<gt>
  2485. tag is used instead).
  2486. Note that within a "bc" block, E<lt> and E<gt> are
  2487. translated into HTML entities automatically.
  2488. =item table
  2489. For composing HTML tables. See the "TABLES" section for more
  2490. information.
  2491. =item bq
  2492. A "bq" signature is short for "block quote". Paragraph text
  2493. formatting is applied to these blocks and they are enclosed
  2494. in a E<lt>blockquoteE<gt> tag as well as E<lt>pE<gt> tags
  2495. within.
  2496. =item h1, h2, h3, h4, h5, h6
  2497. Headline signatures that produce E<lt>h1E<gt>, etc. tags.
  2498. You can adjust the relative output of these using the
  2499. head_offset attribute.
  2500. =item clear
  2501. A 'clear' signature is simply used to indicate that the next
  2502. block should emit a CSS style attribute that clears any
  2503. floating elements. The default behavior is to clear "both",
  2504. but you can use the left (E<lt>) or right (E<gt>) alignment
  2505. characters to indicate which side to clear.
  2506. =item dl
  2507. A "dl" signature is short for "definition list". See the
  2508. "LISTS" section for more information.
  2509. =item fn
  2510. A "fn" signature is short for "footnote". You add a number
  2511. following the "fn" keyword to number the footnote. Footnotes
  2512. are output as paragraph tags but are given a special CSS
  2513. class name which can be used to style them as you see fit.
  2514. =back
  2515. All signatures should end with a period and be followed
  2516. with a space. Inbetween the signature and the period, you
  2517. may use several parameters to further customize the block.
  2518. These include:
  2519. =over
  2520. =item C<{style rule}>
  2521. A CSS style rule. Style rules can span multiple lines.
  2522. =item C<[ll]>
  2523. A language identifier (for a "lang" attribute).
  2524. =item C<(class)> or C<(#id)> or C<(class#id)>
  2525. For CSS class and id attributes.
  2526. =item C<E<gt>>, C<E<lt>>, C<=>, C<E<lt>E<gt>>
  2527. Modifier characters for alignment. Right-justification, left-justification,
  2528. centered, and full-justification.
  2529. =item C<(> (one or more)
  2530. Adds padding on the left. 1em per "(" character is applied.
  2531. When combined with the align-left or align-right modifier,
  2532. it makes the block float.
  2533. =item C<)> (one or more)
  2534. Adds padding on the right. 1em per ")" character is applied.
  2535. When combined with the align-left or align-right modifier,
  2536. it makes the block float.
  2537. =item C<|filter|> or C<|filter|filter|filter|>
  2538. A filter may be invoked to further format the text for this
  2539. signature. If one or more filters are identified, the text
  2540. will be processed first using the filters and then by
  2541. Textile's own block formatting rules.
  2542. =back
  2543. =head2 Extended Blocks
  2544. Normally, a block ends with the first blank line encountered.
  2545. However, there are situations where you may want a block to continue
  2546. for multiple paragraphs of text. To cause a given block signature
  2547. to stay active, use two periods in your signature instead of one.
  2548. This will tell Textile to keep processing using that signature
  2549. until it hits the next signature is found.
  2550. For example:
  2551. bq.. This is paragraph one of a block quote.
  2552. This is paragraph two of a block quote.
  2553. p. Now we're back to a regular paragraph.
  2554. You can apply this technique to any signature (although for
  2555. some it doesn't make sense, like "h1" for example). This is
  2556. especially useful for "bc" blocks where your code may
  2557. have many blank lines scattered through it.
  2558. =head2 Escaping
  2559. Sometimes you want Textile to just get out of the way and
  2560. let you put some regular HTML markup in your document. You
  2561. can disable Textile formatting for a given block using the '=='
  2562. escape mechanism:
  2563. p. Regular paragraph
  2564. ==
  2565. Escaped portion -- will not be formatted
  2566. by Textile at all
  2567. ==
  2568. p. Back to normal.
  2569. You can also use this technique within a Textile block,
  2570. temporarily disabling the inline formatting functions:
  2571. p. This is ==*a test*== of escaping.
  2572. =head2 Inline Formatting
  2573. Formatting within a block of text is covered by the "inline"
  2574. formatting rules. These operators must be placed up against
  2575. text/punctuation to be recognized. These include:
  2576. =over
  2577. =item E<42>C<strong>E<42>
  2578. Translates into E<lt>strongE<gt>strongE<lt>/strongE<gt>.
  2579. =item C<_emphasis_>
  2580. Translates into E<lt>emE<gt>emphasisE<lt>/emE<gt>.
  2581. =item E<42>E<42>C<bold>E<42>E<42>
  2582. Translates into E<lt>bE<gt>boldE<lt>/bE<gt>.
  2583. =item C<__italics__>
  2584. Translates into E<lt>iE<gt>italicsE<lt>/iE<gt>.
  2585. =item C<++bigger++>
  2586. Translates into E<lt>bigE<gt>biggerE<lt>/bigE<gt>.
  2587. =item C<--smaller-->
  2588. Translates into: E<lt>smallE<gt>smallerE<lt>/smallE<gt>.
  2589. =item C<-deleted text->
  2590. Translates into E<lt>delE<gt>deleted textE<lt>/delE<gt>.
  2591. =item C<+inserted text+>
  2592. Translates into E<lt>insE<gt>inserted textE<lt>/insE<gt>.
  2593. =item C<^superscript^>
  2594. Translates into E<lt>supE<gt>superscriptE<lt>/supE<gt>.
  2595. =item C<~subscript~>
  2596. Translates into E<lt>subE<gt>subscriptE<lt>/subE<gt>.
  2597. =item C<%span%>
  2598. Translates into E<lt>spanE<gt>spanE<lt>/spanE<gt>.
  2599. =item C<@code@>
  2600. Translates into E<lt>codeE<gt>codeE<lt>/codeE<gt>. Note
  2601. that within a '@...@' section, E<lt> and E<gt> are
  2602. translated into HTML entities automatically.
  2603. =back
  2604. Inline formatting operators accept the following modifiers:
  2605. =over
  2606. =item C<{style rule}>
  2607. A CSS style rule.
  2608. =item C<[ll]>
  2609. A language identifier (for a "lang" attribute).
  2610. =item C<(class)> or C<(#id)> or C<(class#id)>
  2611. For CSS class and id attributes.
  2612. =back
  2613. =head3 Examples
  2614. Textile is *way* cool.
  2615. Textile is *_way_* cool.
  2616. Now this won't work, because the formatting
  2617. characters need whitespace before and after
  2618. to be properly recognized.
  2619. Textile is way c*oo*l.
  2620. However, you can supply braces or brackets to
  2621. further clarify that you want to format, so
  2622. this would work:
  2623. Textile is way c[*oo*]l.
  2624. =head2 Footnotes
  2625. You can create footnotes like this:
  2626. And then he went on a long trip[1].
  2627. By specifying the brackets with a number inside, Textile will
  2628. recognize that as a footnote marker. It will replace that with
  2629. a construct like this:
  2630. And then he went on a long
  2631. trip<sup class="footnote"><a href="#fn1">1</a></sup>
  2632. To supply the content of the footnote, place it at the end of your
  2633. document using a "fn" block signature:
  2634. fn1. And there was much rejoicing.
  2635. Which creates a paragraph that looks like this:
  2636. <p class="footnote" id="fn1"><sup>1</sup> And there was
  2637. much rejoicing.</p>
  2638. =head2 Links
  2639. Textile defines a shorthand for formatting hyperlinks.
  2640. The format looks like this:
  2641. "Text to display":http://example.com
  2642. In addition to this, you can add 'title' text to your link:
  2643. "Text to display (Title text)":http://example.com
  2644. The URL portion of the link supports relative paths as well
  2645. as other protocols like ftp, mailto, news, telnet, etc.
  2646. "E-mail me please":mailto:someone@example.com
  2647. You can also use single quotes instead of double-quotes if
  2648. you prefer. As with the inline formatting rules, a hyperlink
  2649. must be surrounded by whitespace to be recognized (an
  2650. exception to this is common punctuation which can reside
  2651. at the end of the URL). If you have to place a URL next to
  2652. some other text, use the bracket or brace trick to do that:
  2653. You["gotta":http://example.com]seethis!
  2654. Textile supports an alternate way to compose links. You can
  2655. optionally create a lookup list of links and refer to them
  2656. separately. To do this, place one or more links in a block
  2657. of it's own (it can be anywhere within your document):
  2658. [excom]http://example.com
  2659. [exorg]http://example.org
  2660. For a list like this, the text in the square brackets is
  2661. used to uniquely identify the link given. To refer to that
  2662. link, you would specify it like this:
  2663. "Text to display":excom
  2664. Once you've defined your link lookup table, you can use
  2665. the identifiers any number of times.
  2666. =head2 Images
  2667. Images are identified by the following pattern:
  2668. !/path/to/image!
  2669. Image attributes may also be specified:
  2670. !/path/to/image 10x20!
  2671. Which will render an image 10 pixels wide and 20 pixels high.
  2672. Another way to indicate width and height:
  2673. !/path/to/image 10w 20h!
  2674. You may also redimension the image using a percentage.
  2675. !/path/to/image 20%x40%!
  2676. Which will render the image at 20% of it's regular width
  2677. and 40% of it's regular height.
  2678. Or specify one percentage to resize proprotionately:
  2679. !/path/to/image 20%!
  2680. Alt text can be given as well:
  2681. !/path/to/image (Alt text)!
  2682. The path of the image may refer to a locally hosted image or
  2683. can be a full URL.
  2684. You can also use the following modifiers after the opening '!'
  2685. character:
  2686. =over
  2687. =item C<E<lt>>
  2688. Align the image to the left (causes the image to float if
  2689. CSS options are enabled).
  2690. =item C<E<gt>>
  2691. Align the image to the right (causes the image to float if
  2692. CSS options are enabled).
  2693. =item C<-> (dash)
  2694. Aligns the image to the middle.
  2695. =item C<^>
  2696. Aligns the image to the top.
  2697. =item C<~> (tilde)
  2698. Aligns the image to the bottom.
  2699. =item C<{style rule}>
  2700. Applies a CSS style rule to the image.
  2701. =item C<(class)> or C<(#id)> or C<(class#id)>
  2702. Applies a CSS class and/or id to the image.
  2703. =item C<(> (one or more)
  2704. Pads 1em on the left for each '(' character.
  2705. =item C<)> (one or more)
  2706. Pads 1em on the right for each ')' character.
  2707. =back
  2708. =head2 Character Replacements
  2709. A few simple, common symbols are automatically replaced:
  2710. (c)
  2711. (r)
  2712. (tm)
  2713. In addition to these, there are a whole set of character
  2714. macros that are defined by default. All macros are enclosed
  2715. in curly braces. These include:
  2716. {c|} or {|c} cent sign
  2717. {L-} or {-L} pound sign
  2718. {Y=} or {=Y} yen sign
  2719. Many of these macros can be guessed. For example:
  2720. {A'} or {'A}
  2721. {a"} or {"a}
  2722. {1/4}
  2723. {*}
  2724. {:)}
  2725. {:(}
  2726. =head2 Lists
  2727. Textile also supports ordered and unordered lists.
  2728. You simply place an asterisk or pound sign, followed
  2729. with a space at the start of your lines.
  2730. Simple lists:
  2731. * one
  2732. * two
  2733. * three
  2734. Multi-level lists:
  2735. * one
  2736. ** one A
  2737. ** one B
  2738. *** one B1
  2739. * two
  2740. ** two A
  2741. ** two B
  2742. * three
  2743. Ordered lists:
  2744. # one
  2745. # two
  2746. # three
  2747. Styling lists:
  2748. (class#id)* one
  2749. * two
  2750. * three
  2751. The above sets the class and id attributes for the E<lt>ulE<gt>
  2752. tag.
  2753. *(class#id) one
  2754. * two
  2755. * three
  2756. The above sets the class and id attributes for the first E<lt>liE<gt>
  2757. tag.
  2758. Definition lists:
  2759. dl. textile:a cloth, especially one manufactured by weaving
  2760. or knitting; a fabric
  2761. format:the arrangement of data for storage or display.
  2762. Note that there is no space between the term and definition. The
  2763. term must be at the start of the line (or following the "dl"
  2764. signature as shown above).
  2765. =head2 Tables
  2766. Textile supports tables. Tables must be in their own block and
  2767. must have pipe characters delimiting the columns. An optional
  2768. block signature of "table" may be used, usually for applying
  2769. style, class, id or other options to the table element itself.
  2770. From the simple:
  2771. |a|b|c|
  2772. |1|2|3|
  2773. To the complex:
  2774. table(fig). {color:red}_|Top|Row|
  2775. {color:blue}|/2. Second|Row|
  2776. |_{color:green}. Last|
  2777. Modifiers can be specified for the table signature itself,
  2778. for a table row (prior to the first 'E<verbar>' character) and
  2779. for any cell (following the 'E<verbar>' for that cell). Note that for
  2780. cells, a period followed with a space must be placed after
  2781. any modifiers to distinguish the modifier from the cell content.
  2782. Modifiers allowed are:
  2783. =over
  2784. =item C<{style rule}>
  2785. A CSS style rule.
  2786. =item C<(class)> or C<(#id)> or C<(class#id)>
  2787. A CSS class and/or id attribute.
  2788. =item C<(> (one or more)
  2789. Adds 1em of padding to the left for each '(' character.
  2790. =item C<)> (one or more)
  2791. Adds 1em of padding to the right for each ')' character.
  2792. =item C<E<lt>>
  2793. Aligns to the left (floats to left for tables if combined with the
  2794. ')' modifier).
  2795. =item C<E<gt>>
  2796. Aligns to the right (floats to right for tables if combined with
  2797. the '(' modifier).
  2798. =item C<=>
  2799. Aligns to center (sets left, right margins to 'auto' for tables).
  2800. =item C<E<lt>E<gt>>
  2801. For cells only. Justifies text.
  2802. =item C<^>
  2803. For rows and cells only. Aligns to the top.
  2804. =item C<~> (tilde)
  2805. For rows and cells only. Aligns to the bottom.
  2806. =item C<_> (underscore)
  2807. Can be applied to a table row or cell to indicate a header
  2808. row or cell.
  2809. =item C<\2> or C<\3> or C<\4>, etc.
  2810. Used within cells to indicate a colspan of 2, 3, 4, etc. columns.
  2811. When you see "\", think "push forward".
  2812. =item C</2> or C</3> or C</4>, etc.
  2813. Used within cells to indicate a rowspan or 2, 3, 4, etc. rows.
  2814. When you see "/", think "push downward".
  2815. =back
  2816. When a cell is identified as a header cell and an alignment
  2817. is specified, that becomes the default alignment for
  2818. cells below it. You can always override this behavior by
  2819. specifying an alignment for one of the lower cells.
  2820. =head2 CSS Notes
  2821. When CSS is enabled (and it is by default), CSS class names
  2822. are automatically applied in certain situations.
  2823. =over
  2824. =item Aligning a block or span or other element to
  2825. left, right, etc.
  2826. "left" for left justified, "right" for right justified,
  2827. "center" for centered text, "justify" for full-justified
  2828. text.
  2829. =item Aligning an image to the top or bottom
  2830. "top" for top alignment, "bottom" for bottom alignment,
  2831. "middle" for middle alignment.
  2832. =item Footnotes
  2833. "footnote" is applied to the paragraph tag for the
  2834. footnote text itself. An id of "fn" plus the footnote
  2835. number is placed on the paragraph for the footnote as
  2836. well. For the footnote superscript tag, a class of
  2837. "footnote" is used.
  2838. =item Capped text
  2839. For a series of characters that are uppercased, a
  2840. span is placed around them with a class of "caps".
  2841. =back
  2842. =head2 Miscellaneous
  2843. Textile tries to do it's very best to ensure proper XHTML
  2844. syntax. It will even attempt to fix errors you may introduce
  2845. writing in HTML yourself. Unescaped '&' characters within
  2846. URLs will be properly escaped. Singlet tags such as br, img
  2847. and hr are checked for the '/' terminator (and it's added
  2848. if necessary). The best way to make sure you produce valid
  2849. XHTML with Textile is to not use any HTML markup at all--
  2850. use the Textile syntax and let it produce the markup for you.
  2851. =head1 LICENSE
  2852. This software is licensed under the same terms as Perl itself.
  2853. Please see L<ARTISTIC> for license details.
  2854. =head1 AUTHOR & COPYRIGHT
  2855. Text::Textile was written by Brad Choate, brad@bradchoate.com.
  2856. It is an adaptation of Textile, developed by Dean Allen of Textism.com.
  2857. =cut