package Text::Textile;
use strict;
use warnings;
use base 'Exporter';
our @EXPORT_OK = qw(textile);
our $VERSION = 2.12;
our $debug = 0;
sub new {
my $class = shift;
my %options = @_;
$options{filters} ||= {};
$options{charset} ||= 'iso-8859-1';
for (qw( char_encoding do_quotes smarty_mode )) {
$options{$_} = 1 unless exists $options{$_};
}
for (
qw( trim_spaces preserve_spaces head_offset disable_encode_entities ))
{
$options{$_} = 0 unless exists $options{$_};
}
my $self = bless \%options, $class;
if ( exists $options{css} ) {
$self->css( $options{css} );
}
$options{macros} ||= $self->default_macros();
if ( exists $options{flavor} ) {
$self->flavor( $options{flavor} );
}
else {
$self->flavor('xhtml1/css');
}
return $self;
}
# getter/setter methods...
sub set {
my $self = shift;
my $opt = shift;
if ( ref $opt eq 'HASH' ) {
$self->set( $_, $opt->{$_} ) foreach %{$opt};
}
else {
my $value = shift;
# the following options have special set methods
# that activate upon setting:
if ( $opt eq 'charset' ) {
$self->charset($value);
}
elsif ( $opt eq 'css' ) {
$self->css($value);
}
elsif ( $opt eq 'flavor' ) {
$self->flavor($value);
}
else {
$self->{$opt} = $value;
}
}
return;
}
sub get {
my $self = shift;
return $self->{shift} if @_;
return undef;
}
sub disable_html {
my $self = shift;
if (@_) {
$self->{disable_html} = shift;
}
return $self->{disable_html} || 0;
}
sub head_offset {
my $self = shift;
if (@_) {
$self->{head_offset} = shift;
}
return $self->{head_offset} || 0;
}
sub flavor {
my $self = shift;
if (@_) {
my $flavor = shift;
$self->{flavor} = $flavor;
if ( $flavor =~ m/^xhtml(\d)?(\D|$)/ ) {
if ( $1 eq '2' ) {
$self->{_line_open} = '
';
$self->{_blockcode_open} = '
';
$self->{_blockcode_close} = '
';
$self->{css_mode} = 1;
}
}
elsif ( $flavor =~ m/^html/ ) {
$self->{_line_open} = '';
$self->{_line_close} = '';
$self->{_blockcode_close} = '
';
$self->{css_mode} = $flavor =~ m/\/css/;
}
$self->_css_defaults() if $self->{css_mode} && !exists $self->{css};
}
return $self->{flavor};
}
sub css {
my $self = shift;
if (@_) {
my $css = shift;
if ( ref $css eq 'HASH' ) {
$self->{css} = $css;
$self->{css_mode} = 1;
}
else {
$self->{css_mode} = $css;
$self->_css_defaults()
if $self->{css_mode} && !exists $self->{css};
}
}
return $self->{css_mode} ? $self->{css} : 0;
}
sub charset {
my $self = shift;
if (@_) {
$self->{charset} = shift;
if ( $self->{charset} =~ m/^utf-?8$/i ) {
$self->char_encoding(0);
}
else {
$self->char_encoding(1);
}
}
return $self->{charset};
}
sub docroot {
my $self = shift;
$self->{docroot} = shift if @_;
return $self->{docroot};
}
sub trim_spaces {
my $self = shift;
$self->{trim_spaces} = shift if @_;
return $self->{trim_spaces};
}
sub filter_param {
my $self = shift;
$self->{filter_param} = shift if @_;
return $self->{filter_param};
}
sub preserve_spaces {
my $self = shift;
$self->{preserve_spaces} = shift if @_;
return $self->{preserve_spaces};
}
sub filters {
my $self = shift;
$self->{filters} = shift if @_;
return $self->{filters};
}
sub char_encoding {
my $self = shift;
$self->{char_encoding} = shift if @_;
return $self->{char_encoding};
}
sub disable_encode_entities {
my $self = shift;
$self->{disable_encode_entities} = shift if @_;
return $self->{disable_encode_entities};
}
sub handle_quotes {
my $self = shift;
$self->{do_quotes} = shift if @_;
return $self->{do_quotes};
}
# end of getter/setter methods
# a URL discovery regex. This is from Mastering Regex from O'Reilly.
# Some modifications by Brad Choate ]*)?>)(.+?)()} {"\n\n"._repl(\@repl, $1.$self->encode_html($2, 1).$3)."\n\n"}ges; # fix code tags within pre blocks we just saved. for ( my $i = $pre_start; $i < scalar(@repl); $i++ ) { $repl[$i] =~ s{<(/?)code(.*?)>}{<$1code$2>}gs; } # preserve code blocks by default, encode contents $str =~ s{(
]+)?>)(.+?)(
)}
{_repl(\@repl, $1.$self->encode_html($2, 1).$3)}ges;
# encode blockcode tag (an XHTML 2 tag) and encode it's
# content by default
$str =~ s{({css_mode} ) { if (( $padleft || $padright ) && ( ( $alignment eq 'left' ) || ( $alignment eq 'right' ) ) ) { $style .= ';float:' . $alignment; } else { $style .= ';text-align:' . $alignment; } $class .= ' ' . $self->{css}{"class_align_$alignment"} || $alignment; } else { $pre .= qq{ align="$alignment"} if $alignment; } } $style .= qq{;padding-left:${padleft}em} if $padleft; $style .= qq{;padding-right:${padright}em} if $padright; $style .= qq{;clear:${clear}} if $clear; $class =~ s/^ // if $class; $pre .= qq{ class="$class"} if $class; $pre .= qq{ id="$id"} if $id; $style =~ s/^;// if $style; $pre .= qq{ style="$style"} if $style; $pre .= qq{ lang="$lang"} if $lang; $pre .= q{ cite="} . $self->format_url( url => $cite ) . '"' if defined $cite; $pre .= '>'; $clear = undef; } $pre .= ''; } } else { $post .= '' . $block . '>'; } if ( $buffer =~ m/$blocktags/ ) { $buffer =~ s/^\n\n//s; $out .= $buffer; } else { $buffer = $self->format_block( text => "|$filter|" . $buffer, inline => 1 ) if defined $filter; $out .= $pre . $buffer . $post; } } if ($sticky) { if ( $block eq 'bc' ) { # close our blockcode section $out .= $self->{_blockcode_close}; # . "\n\n"; } elsif ( $block eq 'bq' ) { $out .= ''; # . "\n\n"; } elsif ( ( $block eq 'table' ) && ($stickybuff) ) { my $table_out = $self->format_table( text => $stickybuff ); $out .= $table_out if defined $table_out; } elsif ( ( $block eq 'dl' ) && ($stickybuff) ) { my $dl_out = $self->format_deflist( text => $stickybuff ); $out .= $dl_out if defined $dl_out; } } # cleanup-- restore preserved blocks my $i = scalar(@repl); $out =~ s!(?:<|<)textile#$i(?:>|>)!$_!, $i-- while local $_ = pop @repl; # scan for br, hr tags that are not closed and close them # only for xhtml! just the common ones -- don't fret over input # and the like. if ( $self->{flavor} =~ m/^xhtml/i ) { $out =~ s/(<(?:img|br|hr)[^>]*?(?/$1 \/>/g; } return $out; } sub format_paragraph { my $self = shift; my (%args) = @_; my $buffer = defined $args{text} ? $args{text} : ''; my @repl; $buffer =~ s{(?:^|(?<=[\s>])|([{[])) ==(.+?)== (?:$|([\]}])|(?=$punct{1,2}|\s))} {_repl(\@repl, $self->format_block(text => $2, inline => 1, pre => $1, post => $3))}gesx; my $tokens; if ( $buffer =~ m/ && ( !$self->{disable_html} ) ) { # optimization -- no point in tokenizing if we # have no tags to tokenize $tokens = _tokenize($buffer); } else { $tokens = [ [ 'text', $buffer ] ]; } my $result = ''; foreach my $token ( @{$tokens} ) { my $text = $token->[1]; if ( $token->[0] eq 'tag' ) { $text =~ s/&(?!amp;)/&/g; $result .= $text; } else { $text = $self->format_inline( text => $text ); $result .= $text; } } # now, add line breaks for lines that contain plaintext my @lines = split /\n/, $result; $result = ''; my $needs_closing = 0; foreach my $line (@lines) { if (( $line !~ m/($blocktags)/ ) && ( ( $line =~ m/^[^<]/ || $line =~ m/>[^<]/ ) || ( $line !~ m/'; } elsif ( $block =~ m/fn(\d+)/ ) { my $fnum = $1; $pre .= '
{css}{class_footnote} if $self->{css}{class_footnote}; if ($align) { my $alignment = _halign($align); if ( $self->{css_mode} ) { if (( $padleft || $padright ) && ( ( $alignment eq 'left' ) || ( $alignment eq 'right' ) ) ) { $style .= ';float:' . $alignment; } else { $style .= ';text-align:' . $alignment; } $class .= $self->{css}{"class_align_$alignment"} || $alignment; } else { $pre .= qq{ align="$alignment"}; } } $style .= qq{;padding-left:${padleft}em} if $padleft; $style .= qq{;padding-right:${padright}em} if $padright; $style .= qq{;clear:${clear}} if $clear; $class =~ s/^ // if $class; $pre .= qq{ class="$class"} if $class; $pre .= qq{ id="} . ( $self->{css}{id_footnote_prefix} || 'fn' ) . $fnum . '"'; $style =~ s/^;// if $style; $pre .= qq{ style="$style"} if $style; $pre .= qq{ lang="$lang"} if $lang; $pre .= '>'; $pre .= '' . $fnum . ' '; # we can close like a regular paragraph tag now $block = 'p'; $clear = undef; } else { $pre .= '<' . ( $macros{$block} || $block ); if ($align) { my $alignment = _halign($align); if ( $self->{css_mode} ) { if (( $padleft || $padright ) && ( ( $alignment eq 'left' ) || ( $alignment eq 'right' ) ) ) { $style .= ';float:' . $alignment; } else { $style .= ';text-align:' . $alignment; } $class .= ' ' . $self->{css}{"class_align_$alignment"} || $alignment; } else { $pre .= qq{ align="$alignment"}; } } $style .= qq{;padding-left:${padleft}em} if $padleft; $style .= qq{;padding-right:${padright}em} if $padright; $style .= qq{;clear:${clear}} if $clear; $class =~ s/^ // if $class; $pre .= qq{ class="$class"} if $class; $pre .= qq{ id="$id"} if $id; $style =~ s/^;// if $style; $pre .= qq{ style="$style"} if $style; $pre .= qq{ lang="$lang"} if $lang; $pre .= qq{ cite="} . $self->format_url( url => $cite ) . '"' if defined $cite && $block eq 'bq'; #' $pre .= '>'; $clear = undef; } $buffer = $self->format_paragraph( text => $para ); if ( $block eq 'bq' ) { $post .= '
' if $buffer !~ m/]/; if ( $sticky == 0 ) { $post .= '
' . $code . '
';
}
sub format_classstyle {
my $self = shift;
my ( $clsty, $class, $style ) = @_;
$style = '' if not defined $style;
$class =~ s/^ // if defined $class;
my ( $lang, $padleft, $padright, $id );
if ( $clsty && ( $clsty =~ m/{([^}]+)}/ ) ) {
my $_style = $1;
$_style =~ s/\n/ /g;
$style .= ';' . $_style;
$clsty =~ s/{[^}]+}//g;
}
if ($clsty
&& ( $clsty =~ m/\(([A-Za-z0-9_\- ]+?)(?:#(.+?))?\)/
|| $clsty =~ m/\(([A-Za-z0-9_\- ]+?)?(?:#(.+?))\)/ )
)
{
if ( $1 || $2 ) {
if ($class) {
$class = $1 . ' ' . $class;
}
else {
$class = $1;
}
$id = $2;
if ($class) {
$clsty =~ s/\([A-Za-z0-9_\- ]+?(#.*?)?\)//g;
}
if ($id) {
$clsty =~ s/\(#.+?\)//g;
}
}
}
if ( $clsty && ( $clsty =~ m/(\(+)/ ) ) {
$padleft = length($1);
$clsty =~ s/\(+//;
}
if ( $clsty && ( $clsty =~ m/(\)+)/ ) ) {
$padright = length($1);
$clsty =~ s/\)+//;
}
if ( $clsty && ( $clsty =~ m/\[(.+?)\]/ ) ) {
$lang = $1;
$clsty =~ s/\[.+?\]//g;
}
my $attrs = '';
$style .= qq{;padding-left:${padleft}em} if $padleft;
$style .= qq{;padding-right:${padright}em} if $padright;
$style =~ s/^;//;
if ($class) {
$class =~ s/^ //;
$class =~ s/ $//;
$attrs .= qq{ class="$class"};
}
$attrs .= qq{ id="$id"} if $id;
$attrs .= qq{ style="$style"} if $style;
$attrs .= qq{ lang="$lang"} if $lang;
$attrs =~ s/^ //;
return $attrs;
}
sub format_tag {
my $self = shift;
my (%args) = @_;
my $tagname = $args{tag};
my $text = defined $args{text} ? $args{text} : '';
my $pre = defined $args{pre} ? $args{pre} : '';
my $post = defined $args{post} ? $args{post} : '';
my $clsty = defined $args{clsty} ? $args{clsty} : '';
_strip_borders( \$pre, \$post );
my $tag = "<$tagname";
my $attr = $self->format_classstyle($clsty);
$tag .= qq{ $attr} if $attr;
$tag .= qq{>$text$tagname>};
return $pre . $tag . $post;
}
sub format_deflist {
my $self = shift;
my (%args) = @_;
my $str = defined $args{text} ? $args{text} : '';
my $clsty;
my @lines = split /\n/, $str;
if ( $lines[0] =~ m/^(dl($clstyre*?)\.\.?(?:\ +|$))/ ) {
$clsty = $2;
$lines[0] = substr( $lines[0], length($1) );
}
my ( $dt, $dd );
my $out = '';
foreach my $line (@lines) {
if ($line =~ m/^((?:$clstyre*)(?:[^\ ].*?)(?format_classstyle($clsty) if $clsty;
$tag .= qq{ $attr} if $attr;
$tag .= '>' . "\n";
return $tag . $out . "\n";
}
sub add_term {
my ( $self, $dt, $dd ) = @_;
my ( $dtattr, $ddattr );
my $dtlang;
if ( $dt =~ m/^($clstyre*)/ ) {
my $param = $1;
$dtattr = $self->format_classstyle($param);
if ( $param =~ m/\[([A-Za-z]+?)\]/ ) {
$dtlang = $1;
}
$dt = substr( $dt, length($param) );
}
if ( $dd =~ m/^($clstyre*)/ ) {
my $param = $1;
# if the language was specified for the term,
# then apply it to the definition as well (unless
# already specified of course)
if ( $dtlang && ( $param =~ m/\[([A-Za-z]+?)\]/ ) ) {
undef $dtlang;
}
$ddattr = $self->format_classstyle(
( $dtlang ? "[$dtlang]" : '' ) . $param );
$dd = substr( $dd, length($param) );
}
my $out = '){$count}!$1!gs ) { $str =~ s!(
){$count}!$1!gs; $str =~ s!(]*>//; $str =~ s/<\/p>\s*$//; } return $pre . $str . $post; } sub format_link { my $self = shift; my (%args) = @_; my $text = defined $args{text} ? $args{text} : ''; my $linktext = defined $args{linktext} ? $args{linktext} : ''; my $title = $args{title}; my $url = $args{url}; my $clsty = $args{clsty}; if ( !defined $url || $url eq '' ) { return $text; } if ( $self->{links} && $self->{links}{$url} ) { $title ||= $self->{links}{$url}{title}; $url = $self->{links}{$url}{url}; } $linktext =~ s/ +$//; $linktext = $self->format_paragraph( text => $linktext ); $url = $self->format_url( linktext => $linktext, url => $url ); my $tag = qq{format_classstyle($clsty); $tag .= qq{ $attr} if $attr; if ( defined $title ) { $title =~ s/^\s+//; $tag .= qq{ title="$title"} if length($title); } $tag .= qq{>$linktext}; return $tag; } sub format_url { my $self = shift; my (%args) = @_; my $url = defined $args{url} ? $args{url} : ''; if ( $url =~ m/^(mailto:)?([-\+\w]+\@[-\w]+(\.\w[-\w]*)+)$/ ) { $url = 'mailto:' . $self->mail_encode($2); } if ( $url !~ m{^(/|\./|\.\./|#)} ) { $url = "http://$url" if $url !~ m{^(?:https?|ftp|mailto|nntp|telnet)}; } $url =~ s/&(?!amp;)/&/g; $url =~ s/ /\+/g; $url =~ s/^((?:.+?)\?)(.+)$/$1.$self->encode_url($2)/ge; return $url; } sub format_span { my $self = shift; my (%args) = @_; my $text = defined $args{text} ? $args{text} : ''; my $pre = defined $args{pre} ? $args{pre} : ''; my $post = defined $args{post} ? $args{post} : ''; my $cite = defined $args{cite} ? $args{cite} : ''; my $align = $args{align}; my $clsty = $args{clsty}; _strip_borders( \$pre, \$post ); my ( $class, $style ); my $tag = qq{{css_mode} ) { my $alignment = _halign($align); $style .= qq{;float:$alignment} if $alignment; $class .= ' ' . $self->{css}{"class_align_$alignment"} if $alignment; } else { my $alignment = _halign($align) || _valign($align); $tag .= qq{ align="$alignment"} if $alignment; } } my $attr = $self->format_classstyle( $clsty, $class, $style ); $tag .= qq{ $attr} if $attr; if ( defined $cite ) { $cite =~ s/^://; $cite = $self->format_url( url => $cite ); $tag .= qq{ cite="$cite"}; } return $pre . $tag . '>' . $self->format_paragraph( text => $text ) . '' . $post; } sub format_image { my $self = shift; my (%args) = @_; my $src = defined $args{src} ? $args{src} : ''; my $pre = defined $args{pre} ? $args{pre} : ''; my $post = defined $args{post} ? $args{post} : ''; my $extra = $args{extra}; my $align = $args{align}; my $link = $args{url}; my $clsty = $args{clsty}; _strip_borders( \$pre, \$post ); return $pre . '!!' . $post if length($src) == 0; my $tag; if ( $self->{flavor} =~ m/^xhtml2/ ) { my $type; # poor man's mime typing. need to extend this externally if ( $src =~ m/(?:\.jpeg|\.jpg)$/i ) { $type = 'image/jpeg'; } elsif ( $src =~ m/\.gif$/i ) { $type = 'image/gif'; } elsif ( $src =~ m/\.png$/i ) { $type = 'image/png'; } elsif ( $src =~ m/\.tiff$/i ) { $type = 'image/tiff'; } $tag = qq{'; } elsif ( $self->{flavor} =~ m/^xhtml/ ) { $tag .= ' />'; } else { $tag .= '>'; } if ( defined $link ) { $link =~ s/^://; $link = $self->format_url( url => $link ); $tag = '' . $tag . ''; } return $pre . $tag . $post; } sub format_table { my $self = shift; my (%args) = @_; my $str = defined $args{text} ? $args{text} : ''; my @lines = split /\n/, $str; my @rows; my $line_count = scalar(@lines); for ( my $i = 0; $i < $line_count; $i++ ) { if ( $lines[$i] !~ m/\|\s*$/ ) { if ( $i + 1 < $line_count ) { $lines[ $i + 1 ] = $lines[$i] . "\n" . $lines[ $i + 1 ] if $i + 1 <= $#lines; } else { push @rows, $lines[$i]; } } else { push @rows, $lines[$i]; } } my ( $tid, $tpadl, $tpadr, $tlang ); my $tclass = ''; my $tstyle = ''; my $talign = ''; if ( $rows[0] =~ m/^table[^\.]/ ) { my $row = $rows[0]; $row =~ s/^table//; my $params = 1; # process row parameters until none are left while ($params) { if ( $row =~ m/^($tblalignre)/ ) { # found row alignment $talign .= $1; $row = substr( $row, length($1) ) if $1; redo if $1; } if ( $row =~ m/^($clstypadre)/ ) { # found a class/id/style/padding indicator my $clsty = $1; $row = substr( $row, length($clsty) ) if $clsty; if ( $clsty =~ m/{([^}]+)}/ ) { $tstyle = $1; $clsty =~ s/{([^}]+)}//; redo if $tstyle; } if ( $clsty =~ m/\(([A-Za-z0-9_\- ]+?)(?:#(.+?))?\)/ || $clsty =~ m/\(([A-Za-z0-9_\- ]+?)?(?:#(.+?))\)/ ) { if ( $1 || $2 ) { $tclass = $1; $tid = $2; redo; } } $tpadl = length($1) if $clsty =~ m/(\(+)/; $tpadr = length($1) if $clsty =~ m/(\)+)/; $tlang = $1 if $clsty =~ m/\[(.+?)\]/; redo if $clsty; } $params = 0; } $row =~ s/\.\s+//; $rows[0] = $row; } my $out = ''; my @cols = split /\|/, $rows[0] . ' '; my ( @colalign, @rowspans ); foreach my $row (@rows) { my @cols = split /\|/, $row . ' '; my $colcount = $#cols; pop @cols; my $colspan = 0; my $row_out = ''; my ( $rowclass, $rowid, $rowalign, $rowstyle, $rowheader ); $cols[0] = '' if !defined $cols[0]; if ( $cols[0] =~ m/_/ ) { $cols[0] =~ s/_//g; $rowheader = 1; } if ( $cols[0] =~ m/{([^}]+)}/ ) { $rowstyle = $1; $cols[0] =~ s/{[^}]+}//g; } if ( $cols[0] =~ m/\(([^\#]+?)?(#(.+))?\)/ ) { $rowclass = $1; $rowid = $3; $cols[0] =~ s/\([^\)]+\)//g; } $rowalign = $1 if $cols[0] =~ m/($alignre)/; for ( my $c = $colcount - 1; $c > 0; $c-- ) { if ( $rowspans[$c] ) { $rowspans[$c]--; next if $rowspans[$c] > 1; } my ($colclass, $colid, $header, $colparams, $colpadl, $colpadr, $collang ); my $colstyle = ''; my $colalign = $colalign[$c]; my $col = pop @cols; $col ||= ''; my $attrs = ''; if ( $col =~ m/^(((_|[\/\\]\d+|$alignre|$clstypadre)+)\. )/ ) { my $colparams = $2; $col = substr( $col, length($1) ); my $params = 1; # keep processing column parameters until there # are none left... while ($params) { if ( $colparams =~ m/^(_|$alignre)/g ) { # found alignment or heading indicator $attrs .= $1; $colparams = substr( $colparams, pos($colparams) ) if $1; redo if $1; } if ( $colparams =~ m/^($clstypadre)/g ) { # found a class/id/style/padding marker my $clsty = $1; $colparams = substr( $colparams, pos($colparams) ) if $clsty; if ( $clsty =~ m/{([^}]+)}/ ) { $colstyle = $1; $clsty =~ s/{([^}]+)}//; } if ( $clsty =~ m/\(([A-Za-z0-9_\- ]+?)(?:#(.+?))?\)/ || $clsty =~ m/\(([A-Za-z0-9_\- ]+?)?(?:#(.+?))\)/ ) { if ( $1 || $2 ) { $colclass = $1; $colid = $2; if ($colclass) { $clsty =~ s/\([A-Za-z0-9_\- ]+?(#.*?)?\)//g; } elsif ($colid) { $clsty =~ s/\(#.+?\)//g; } } } if ( $clsty =~ m/(\(+)/ ) { $colpadl = length($1); $clsty =~ s/\(+//; } if ( $clsty =~ m/(\)+)/ ) { $colpadr = length($1); $clsty =~ s/\)+//; } if ( $clsty =~ m/\[(.+?)\]/ ) { $collang = $1; $clsty =~ s/\[.+?\]//; } redo if $clsty; } if ( $colparams =~ m/^\\(\d+)/ ) { $colspan = $1; $colparams = substr( $colparams, length($1) + 1 ); redo if $1; } if ( $colparams =~ m/\/(\d+)/ ) { $rowspans[$c] = $1 if $1; $colparams = substr( $colparams, length($1) + 1 ); redo if $1; } $params = 0; } } if ( length($attrs) ) { $header = 1 if $attrs =~ m/_/; $colalign = '' if $attrs =~ m/($alignre)/ && length($1); # determine column alignment if ( $attrs =~ m/<>/ ) { $colalign .= '<>'; } elsif ( $attrs =~ m/ ) { $colalign .= '<'; } elsif ( $attrs =~ m/=/ ) { $colalign = '='; } elsif ( $attrs =~ m/>/ ) { $colalign = '>'; } if ( $attrs =~ m/\^/ ) { $colalign .= '^'; } elsif ( $attrs =~ m/~/ ) { $colalign .= '~'; } elsif ( $attrs =~ m/-/ ) { $colalign .= '-'; } } $header = 1 if $rowheader; $colalign[$c] = $colalign if $header; $col =~ s/^ +//; $col =~ s/ +$//; if ( length($col) ) { # create one cell tag my $rowspan = $rowspans[$c] || 0; my $col_out = '<' . ( $header ? 'th' : 'td' ); if ( defined $colalign ) { # horizontal, vertical alignment my $halign = _halign($colalign); $col_out .= qq{ align="$halign"} if $halign; my $valign = _valign($colalign); $col_out .= qq{ valign="$valign"} if $valign; } # apply css attributes, row, column spans $colstyle .= qq{;padding-left:${colpadl}em} if $colpadl; $colstyle .= qq{;padding-right:${colpadr}em} if $colpadr; $col_out .= qq{ class="$colclass"} if $colclass; $col_out .= qq{ id="$colid"} if $colid; $colstyle =~ s/^;// if $colstyle; $col_out .= qq{ style="$colstyle"} if $colstyle; $col_out .= qq{ lang="$collang"} if $collang; $col_out .= qq{ colspan="$colspan"} if $colspan > 1; $col_out .= qq{ rowspan="$rowspan"} if ( $rowspan || 0 ) > 1; $col_out .= '>'; # if the content of this cell has newlines OR matches # our paragraph block signature, process it as a full-blown # textile document if (( $col =~ m/\n\n/ ) || ($col =~ m/^(?:$halignre|$clstypadre*)* [\*\#] (?:$clstypadre*|$halignre)*\ /x ) ) { $col_out .= $self->textile($col); } else { $col_out .= $self->format_paragraph( text => $col ); } $col_out .= '' . ( $header ? 'th' : 'td' ) . '>'; $row_out = $col_out . $row_out; $colspan = 0 if $colspan; } else { $colspan = 1 if $colspan == 0; $colspan++; } } if ( $colspan > 1 ) { # handle the spanned column if we came up short $colspan--; $row_out = q{
>>,
C<< >> or C<<