#!/usr/bin/perl -w # # Anand Natrajan (www.anandnatrajan.com) # # Program to convert embedded tbl file formats to HTML. # I/P and O/P: STDIN and STDOUT use File::Basename; use Getopt::Long; $| = 1; $MyName = basename($0); $opt_border = 0; $opt_pad = ""; $opt_space = ""; $opt_hstripe = 0; $opt_vstripe = 0; $opt_delim = "\t"; sub Usage { print STDERR <] [-border ] [-pad ] [-space ] [-hstripe ] [-vstripe ] [-notbl] EOF if ($#_ > -1) { print STDERR < : Specify delimiter for fields in table. Default: \"$opt_delim\". -border : Specify width of the border between cells. Default: \"$opt_border\". -pad : Specify the padding between cells. Default: \"$opt_pad\". -space : Specify the spacing between cells. Default: \"$opt_space\". -hstripe : Specify the number of colours for row stripes. Default: \"$opt_hstripe\". -vstripe : Specify the number of colours for column stripes. Default: \"$opt_vstripe\". -notbl : Treat the entire file as data with no tbl specifications. $MyName requires no input files and outputs no files. All inputs and outputs are from STDIN and STDOUT respectively. The input format must conform to syntax for tbl. See \"man tbl\" for details. Briefly, a tbl file has the following syntax: .TS l c r s n. Left Centre RightSpan Number .TE where the 2nd line indicates formatting. Except the last, the following lines are data to be formatted. $MyName adds capitalised versions for the format letters for row spans. EOF } exit 0; } sub About { print STDERR < \$opt_help, "about" => \$opt_about, "debug", "v|verbose", "border=n", "pad=n", "space=n", "hstripe=n", "vstripe=n", "delim=s", "notbl") or &Usage; &Usage(1) if ($opt_help); &About if ($opt_about); $opt_v = 1 if (defined($opt_debug)); @lines = <>; @output = (); while ($#lines > -1) { if ($lines[0] =~ /^\.TS/ or $opt_notbl) { shift(@lines) if (!$opt_notbl); # shift only if tbl formatting is present my $border = (defined($opt_border) and $opt_border !~ /^\s*$/) ? " border=\"$opt_border\"" : ""; my $cellpad = (defined($opt_pad) and $opt_pad !~ /^\s*$/) ? " cellpadding=\"$opt_pad\"" : ""; my $cellspace = (defined($opt_space) and $opt_space !~ /^\s*$/) ? " cellspacing=\"$opt_space\"" : ""; push(@output, "\n"); push(@output, "\n"); my $informs = 1; # initially in the field specification lines my $inheads = 1; # initially in the heading lines my $datarownum = -1; # count of which data row we're in my @formats = my @maxbefore = my @maxafter = (); my @prevvalues = my @prevstrides = my @strides = (); my $stridemark = 0; while (($#lines > -1) && (($_ = $lines[0]) !~ /^\.TE/)) { shift(@lines); chomp; if ($informs) { # put a fake format if no tbl format is present # else, save the formats if ($opt_notbl) { push(@formats, "l."); } else { push(@formats, $_); } if ($formats[-1] =~ /\./) # till end-of-formats seen { chop($formats[-1]); $informs = 0; # if tbl format present, skip to actual data next if (!$opt_notbl); } } if (!$informs) # catches the case when tbl formats absent { # close header rows if we're in the last format line if ($#formats == 0 and $inheads) { push(@output, "\n"); push(@output, "\n"); $inheads = 0; } # assume one row per line and delimiter-separated columns @_ = split(/$opt_delim/); my @fieldforms = split(/\s+/, $formats[0]); die "First column cannot be a span specification.\n" if ($fieldforms[0] =~ /^s/); my $bgcolour = ""; # find the background colour for this row $bgcolour = " bgcolor=\"" . &colour(++$datarownum % $opt_hstripe + 1, $opt_hstripe) . "\"" if (!$inheads and $opt_hstripe > 0); push(@output, "\t\n"); for (my $i = 0; $i <= $#_; $i++) { my $align = ""; my $span = 1; my $datacolnum = $i; # count which data column we're in if ($fieldforms[0] =~ /^[LRCN]/) # spanning rows { if (!defined($prevvalues[$i]) or $prevvalues[$i] ne $_[$i]) { # put a temporary mark for later span count $align = " rowspan=#$stridemark"; $prevstrides[$i] = $stridemark++; $strides[$prevstrides[$i]] = 1; $prevvalues[$i] = $_[$i]; } else { $strides[$prevstrides[$i]]++; shift(@fieldforms) if ($#fieldforms > 0); while ($#fieldforms >= 0 and $fieldforms[0] =~ /^s/) { shift(@fieldforms); } next; } } if ($fieldforms[0] =~ /^[lL]/) { $align .= " align=\"left\""; } elsif ($fieldforms[0] =~ /^[rR]/) { $align .= " align=\"right\""; } elsif ($fieldforms[0] =~ /^[cC]/) { $align .= " align=\"center\""; } elsif ($fieldforms[0] =~ /^[nN]/) { $_[$i] = 0 if ($_[$i] =~ /^\s*$/); # numbers have to be aligned by padding with spaces my $precount = length(int(abs($number = $_[$i]))); $maxbefore[$i] = 0 if (!defined($maxbefore[$i])); $maxafter[$i] = 0 if (!defined($maxafter[$i])); $maxbefore[$i] = $precount if ($precount > $maxbefore[$i]); my $decdigits = (($pos = index($number, ".")) > -1) ? substr($number, $pos + 1) : ""; # if exponent used, expand it $decdigits =~ s/[eE].*$//; $decdigits =~ s/0*$//; my $postcount = length($decdigits) - ((($pos = index(lc($number), "e")) > -1) ? substr($number, $pos + 1) : 0); $postcount = 0 if ($postcount < 0); $maxafter[$i] = $postcount if ($postcount > $maxafter[$i]); # save the field number, number of digits before # decimal and number of digits after decimal $align .= " align=numeric$i.$precount.$postcount"; } shift(@fieldforms) if ($#fieldforms > 0); while ($#fieldforms >= 0 and $fieldforms[0] =~ /^s/) { $span++; shift(@fieldforms); } $span = ($span > 1) ? " colspan=\"" . $span . "\"" : ""; # find the background colour for this column my $bgcolour = ""; $bgcolour = " bgcolor=" . &colour($datacolnum % $opt_vstripe + 1, $opt_vstripe) if ($opt_vstripe > 0); # if a horizontal stripe is also demanded, then do # something fancy to simulate both my $bgcolour2 = ""; $bgcolour2 = " bgcolor=\"" . &colour($datarownum % $opt_hstripe + 1, $opt_hstripe) . "\"" if ($opt_hstripe > 0 and $opt_vstripe > 0); $bgcolour = &avgcolour($bgcolour, $bgcolour2); # if we're in the headers, take advantage of that my $elem = $inheads ? "th" : "td"; push(@output, "\t\t<$elem$bgcolour$span$align>$_[$i]\n"); # push(@output, "\t\t\n"); } push(@output, "\t\n"); shift(@formats) if ($#formats > 0); } } push(@output, "\n"); push(@output, "\n"); # Currently, we don't have anything to add here push(@output, "\n"); push(@output, "\n"); foreach (@output) { if ($_ =~ /align=numeric/) { # align numbers to right and pad end with spaces s/align=numeric(\d*).(\d*)\.(\d*)/align="right"/; my $numform = sprintf("%%.%df", $postcount = $3); if ($maxafter[$i = $1] > 0) { my $padding = $maxafter[$i] - $postcount; $padding++ if ($postcount == 0); while ($padding > 0) { $padding--; $numform .= " "; } } $number = substr($_, $firstdigit = index($_, ">") + 1, index($_, "" . sprintf($numform, $number) . "\n"; } if ($_ =~ /rowspan=#/) { # replace row span marks with actual counts /rowspan=#(\d*)/; if ($strides[$stridemark = $1] == 1) { s/ rowspan=#\d*//; } else { s/rowspan=#\d*/rowspan="$strides[$stridemark]"/; } } } shift(@lines); # shift even if tbl headers are present and notbl set } else { push(@output, $lines[0]); shift(@lines); } } print @output;