#!/usr/bin/perl use strict; use Getopt::Std; use File::Slurp qw/slurp/; use Config::AutoConf; use Digest::MD5 'md5_hex'; use Memoize; memoize("check_tools_for"); our $VERSION = '0.02'; my $n = 0; my @temporary_files; my $DEBUG = 0; my %opts; getopts('sQDho:', \%opts) or die "Use -h to see usage information.\n"; help() if $opts{h}; $DEBUG = 1 if $opts{D}; ## Possivelmente podemos passar a usar o RegExp::Common, nao? :) ##jj talvez tenhas razão our $cam2tex_counter = 0; my $bl0 = qr([^{}]*); my $bl1 = qr(\{$bl0\}); my $bl2 = qr(\{$bl0($bl1*$bl0)*\}); my $bl3 = qr(\{$bl0($bl2*$bl0)*\}); my $bl4 = qr(\{$bl0($bl3*$bl0)*\}); # Called for # \import_x{file} --> proc_file sub syst{ my $a=shift; print LOG "...system($a)\n"; system($a); } sub systopen { my $a = shift; print LOG "...open($a|)\n"; open X, "$a|" or die $!; 1 while (); close X; } my %importer_types = ( ### type -> function ; ### function = filename * options * md5 -> latexStr html => sub { my ($f, $op, $md5) = @_; my $o = $md5; if (! -f "$o.pdf") { $f =~ m/html?$/ or die "Invalid extension for HTML inclusion [$f]\n"; syst "htmldoc --textfont times --footer ... --header ... --webpage -f $o.pdf $f"; } temp($o); return "\\includepdf[pagecommand={},pages=-]{$o}"; }, pod => sub { my ($f, $op, $md5) = @_; my $o = $md5; my $def = { "h1level" => 2, (%{$op}) }; if (! -f "$o.tex") { syst "pod2latex -h1level $def->{h1level} -out $o $f"; } my $r = slurp("$o.tex"); $r =~ s/section\{(.*?)\}/"section{". ucfirst(lc($1)) ."}"/ge; return $r; }, makefileg => sub { my ($f, $op, $md5) = @_; $op = {scale => 0.7, (%{$op}) }; my $tmpfile = "_${md5}_"; if (!-f "$tmpfile.pdf") { gvmake($op, $f, temp("$tmpfile.pdf")); } delete $op->{root}; delete $op->{trim_mode}; delete $op->{rankdir}; return "\\includegraphics[".hash2texargs($op)."]{$tmpfile.pdf}"; }, dot => sub { my ($f, $op, $md5) = @_; my $o=$md5; my $def={scale => "0.9", pagecommand => "", frame => "true", nup => "1x2", pages => "-", delta => "0.5cm 0.5cm", (%{$op})}; if (! -f "$o.pdf") { ## \begin{import_dot}[neato,width=\textwidth] if (defined($op->{neato})) { syst "neato -Tpdf $f > _$f.pdf"; } elsif (defined($op->{twopi})) { syst "twopi -Tpdf $f > _$f.pdf"; } elsif (defined($op->{circo})) { syst "circo -Tpdf $f > _$f.pdf"; } else { syst "dot -Tpdf $f > _$f.pdf"; } systopen("pdfcrop _$f.pdf $o.pdf"); unlink "_$f.pdf"; } temp("$o.pdf"); delete($op->{twopi}); delete($op->{circo}); delete($op->{neato}); return "\\includegraphics[".hash2texargs($op)."]{$o.pdf}"; }, slides => sub { my ($f, $op,$md5) = @_; my $def = { scale => "0.9", pagecommand => "", frame => "true", nup => "1x2", pages => "-", delta => "0.5cm 0.5cm", (%{$op})}; return "\\includepdf[".hash2texargs($def)."]{$f}"; }, ); # Called for # \inline_x{...} e \begin{import_x}....\end{import_x} --> proc_str ### type -> function ### function = str * options * md5 -> latexStr my %environment_types = ( abc => sub { my ($f, $op, $md5) = @_; my $abcop =""; $abcop = "-a $op->{a}" if $op->{a}; my $midiop = ""; $midiop = "-Q $op->{t}" if $op->{t}; my $pin = '\fbox{\textmusicalnote}'; $pin = '$\odot$' if $op->{pin}eq "odot"; $pin = '\fbox{\textmusicalnote}' if $op->{pin}eq "note"; my $incipit=""; $n++; print LOG "abc $n - _${md5}_.abc\n"; my $tmpfile = "_${md5}_"; $incipit = "\\incipit{$tmpfile}" if $op->{incipit}; my $sound= ""; if (!-f "$tmpfile.pdf") { open (F, ">$tmpfile.abc") or die; print F $f,"\n"; close F; syst "abcm2ps $abcop $tmpfile.abc -O _$tmpfile.ps"; syst "ps2pdf _$tmpfile.ps "; if($op->{incipit}){ my $incipitop =""; $incipitop="-M" if $op->{incipit} eq "nolyrics"; syst "abc2incipit $incipitop $tmpfile.abc"; } systopen("pdfcrop _$tmpfile.pdf $tmpfile.pdf"); } if($op->{mp3}){ if (!-f "$tmpfile.mp3") { syst "abc2midi $tmpfile.abc $midiop -o _$tmpfile.mid > _.log"; syst "timidity --quiet -Ow _$tmpfile.mid -o _$tmpfile.wav > _.log"; syst "lame --quiet _$tmpfile.wav $tmpfile.mp3 > _.log"; unlink "_$tmpfile.wav"; } # $displayPin="\\def\\displayPin#1{\\marginpar[]{#1}\\\\}" # $sound="\\displayPin{\\attachfile[color=1 1 1]{$tmpfile.mp3}}"; $sound="\\displayPin{\\textattachfile[color=1 0 0]{$tmpfile.mp3}{$pin}}"; } elsif($op->{wav}){ if (!-f "$tmpfile.wav") { syst "abc2midi $tmpfile.abc $midiop -o _$tmpfile.mid > _.log"; syst "timidity --quiet -Ow _$tmpfile.mid -o $tmpfile.wav > _.log"; } # $sound="\\displayPin{\\attachfile[color=1 1 1]{$tmpfile.wav}}"; $sound="\\displayPin{\\textattachfile[color=1 0 0]{$tmpfile.wav}{$pin}}"; } elsif($op->{midi}){ if (!-f "$tmpfile.mid") { syst "abc2midi $tmpfile.abc $midiop -o $tmpfile.mid > _.log"; } $sound="\\displayPin{\\textattachfile[color=1 0 0]{$tmpfile.mid}{$pin}}"; } unlink "_$tmpfile.pdf", "_$tmpfile.ps"; ## unlink "$tmpfile.abc"; delete $op->{mp3}; delete $op->{wav}; delete $op->{midi}; delete $op->{t}; delete $op->{pin}; delete $op->{incipit}; delete $op->{a}; temp("$tmpfile.pdf"); return $incipit ."\\includegraphics[".hash2texargs($op)."]{$tmpfile}". $sound; }, dot => sub { my ($f, $op, $md5) = @_; my $t = 'dot'; $n++; print LOG "dot $n - _${md5}_.dot\n"; my $tmpfile = "_${md5}_"; if (! -f "$tmpfile.pdf") { open (F, ">$tmpfile.$t") or die; print F $f; close F; if (defined($op->{neato})) { syst "neato -Tpdf $tmpfile.$t > _$tmpfile.pdf"; } elsif (defined($op->{twopi})) { syst "twopi -Tpdf $tmpfile.$t > _$tmpfile.pdf"; } elsif (defined($op->{circo})) { syst "circo -Tpdf $tmpfile.$t > _$tmpfile.pdf"; } else { syst "dot -Tpdf $tmpfile.$t > _$tmpfile.pdf"; } systopen("pdfcrop _$tmpfile.pdf $tmpfile.pdf"); unlink "_$tmpfile.pdf"; unlink "$tmpfile.$t"; } delete $op->{neato}; delete $op->{circo}; delete $op->{twopi}; temp("$tmpfile.pdf"); return "\\includegraphics[".hash2texargs($op)."]{$tmpfile}"; }, makefileg => sub { my ($f, $op, $md5) = @_; $op = {scale => 0.7, (%{$op}) }; $n++; print LOG "makefileg $n - _${md5}_.make\n"; my $tmpfile = "_${md5}_"; if (!-f "$tmpfile.pdf") { open (F, ">$tmpfile") or die; print F $f; close F; gvmake($op, $tmpfile, temp("$tmpfile.pdf")); unlink $tmpfile; } delete $op->{root}; delete $op->{trim_mode}; delete $op->{rankdir}; return "\\includegraphics[".hash2texargs($op)."]{$tmpfile.pdf}"; }, csv => sub { my ($f,$op) = @_; $op = {fs => "\t", head => 1, lhead => 0, %$op}; $n++; print LOG "csv $n\n"; my @l = split(/\n/,$f) ; shift(@l) while(not $l[0] =~ /\S/); my @fl =""; @fl = (split( $op->{fs}, shift(@l))); my $cols= join("",(map {"|l"} @fl ))."|"; my $final="\\begin{tabular}{$cols}\\hline\n"; $final .= join("&",@fl)."\\\\"; $final .= '\hline' if $op->{head}; for(@l){ if(/^[-_]+$/) { $final .= "\n\\hline";} else { $final .= "\n". join(" & ",split( $op->{fs}, $_)). '\\\\';} } $final.='\hline\end{tabular}'; return $final; }, gnuplot => sub { my ($f, $op, $md5) = @_; $n++; print LOG "gnuplot $n - _${md5}_\n"; my $tmpfile = "_${md5}_"; if (!-f "$tmpfile.tex") { open (F, ">$tmpfile.gnuplot") or die; print F "set terminal latex\nset output '$tmpfile.tex'\n"; print F $f; close F; syst "gnuplot $tmpfile.gnuplot"; unlink "$tmpfile.gnuplot"; } temp("$tmpfile.tex"); return "\\input{$tmpfile.tex}"; }, camila => sub { my ($f, $op, $md5) = @_; $n++; print LOG "camila $n - _${md5}_\n"; my $tmpfile = "_${md5}_"; if (! -f "$tmpfile.tex") { # -- ver bichomp -- $f = substr($f, 1, length($f)-2); $f =~ s/;?(\s*)$/;$1/; $f =~ s/ENDTYPE;?(\s*)$/ENDTYPE$1/; open F, "| tee $tmpfile.camila | cam2tex > $tmpfile.tex" or die "can't cam2tex\n"; print F $f; close F; } my $res = slurp("$tmpfile.tex"); $res =~ s/\\\\\s*$//; # isto ate' ja' parece java!!! if ($op->{framed}) { my $length = ($op->{framed} =~ /(\d+)/)?$1:0; my $save_fs = ""; if (!$cam2tex_counter) { $save_fs = "\\newlength{\\camtmp}\\setlength\\camtmp{\\FrameSep}\n"; $cam2tex_counter = 1; } my $set_fs = "\\setlength\\FrameSep{${length}mm}\n"; my $restore_fs = "\\setlength\\FrameSep{\\camtmp}\n"; $res = "$save_fs$set_fs\n\\begin{framed} \n $res \n \\end{framed} \n$restore_fs\n\n"; } return $res; }, html => sub { my ($f, $op, $md5) = @_; # my $o=$f; # $o =~ s/html?$/pdf/ or die("invalid extension\n"); # system("htmldoc --textfont times --footer ... --header ... --webpage -f $o $f"); # return "\\includepdf[pagecommand={},pages=-]{$o}" }, pod => sub { my ($f, $op, $md5) = @_; # my $o=$$; # my $def={"h1level" => 2, # (%{$op})}; # system("pod2latex -h1level $def->{h1level} -out $o $f"); # my $r = slurp("$o.tex"); # $r =~ s/section\{(.*?)\}/"section{". ucfirst(lc($1)) ."}"/ge; # unlink("$o.tex"); # return $r; }, ); open LOG, ">teximporter$$.log" or die "Cannot open teximporter$$.log logfile.\n"; undef $/; while(<>) { my %save = (); my $n1 = 0; s{( \\begin\{[Vv]erbatim\} .*? \\end\{[Vv]erbatim\}) }{ $save{++$n1}=$1;"__SSAVE${n1}__"}xges; ## save verbatim s{([^\\]) (\%.*) }{ $save{++$n1}=$2;"$1__SSAVE${n1}__"}xge; ## save comm s{ \\input \{ ([^\}]+) \} }{ $a = $1; system "teximporter $a > __$a" if -f $a; system "teximporter $a.tex > __$a.tex" if -f "$a.tex"; "\\input{__$a}" }gex; s{ (\\begin\{import_(\w+)\} (?: \[ (.*?) \] )? ((?:.|\n)*?) \\end\{import_\2\}) }{ my $md5 = md5_hex($1); proc_str($md5, $2, $4, args2hash($3)) }xge; s{ (\\begin\{_(\w+)\} (?: \[ (.*?) \] )? ((?:.|\n)*?) \\end\{_\2\}) }{ if ($environment_types{$2}){ my $md5 = md5_hex($1); proc_str($md5, $2, $4, args2hash($3)); } else { $1 } }xge unless $opts{s}; s{ (\\_(\w+) (?: \[ (.*?) \] )? ($bl4)) }{ if ($environment_types{$2}) { my $md5 = md5_hex($1); proc_str($md5, $2, bichomp($4), args2hash($3)); } else { $1 } }xge unless $opts{s}; s{ (\\inline_(\w+) (?: \[ (.*?) \] )? ($bl4)) }{ my $md5 = md5_hex($1); proc_str($md5, $2, bichomp($4), args2hash($3)) }xge; s{ \\import_(\w+) (?: \[ (.*?) \] )? \{ (.*?) \} }{ proc_file($1, $3, args2hash($2)) }xge; s{__SSAVE(\d+)__}{$save{$1}}g; print; } print STDERR "Temporary files: @temporary_files\n" if $DEBUG; close LOG; # Save a file on temporary_files array sub temp { push @temporary_files, @_; return $_[0]; } sub bichomp{ my $a=shift; $a =~ s/^{//; $a =~ s/}$//; $a; } sub args2hash { my %save = (); my $n = 0; my $a1 = shift; $a1 =~ s/(\{.*?\})/$save{++$n}=$1;"__SAVE${n}__"/ge; $a1 =~ s/,/=,=/g; $a1 =~ s/__SAVE(\d+)__/$save{$1}/g; return { map { (m/(\w+)=(.*)/) ? ($1=>$2) : ($_=>"true") } split(/\s*=,=\s*/,$a1) }; } sub hash2texargs { my $a = shift; die "invalid hash ref $a\n" unless ref($a) eq "HASH"; return join(",", (map { "$_=$a->{$_}" } keys %$a)); } sub proc_file { my ($t,$f,$op) = @_; $op ||= {}; my $sl=slurp($f); my $md5 = -f $f ? md5_hex($sl) : md5_hex(localtime); warn(" Processing a $t primitive...\n") unless $opts{Q}; if (exists($importer_types{$t})) { check_tools_for($t); return $importer_types{$t}->($f, $op, $md5); } elsif (exists($environment_types{$t})) { # check_tools_for($t); # return $environment_types{$t}->(slurp($f), $op, $md5); return proc_str($md5,$t,$sl,$op); } else { warn ("unknown import file type: '$t'\n"); } } sub proc_str { my ($md5, $t, $f,$op)=@_; $op ||= {}; warn(" Processing a $t primitive...\n") unless $opts{Q}; if (exists($environment_types{$t})) { check_tools_for($t); return $environment_types{$t}->($f, $op, $md5); } else { warn "unknown inline/environment type: '$t'\n"; } } sub gvmake { local $/ = "\n"; my %opt = ( trim_mode => 0 , rankdir => 1); if (ref($_[0]) eq "HASH") { %opt = (%opt , %{shift(@_)}); } my ($makefile, $outfile) = @_; my $parser = Makefile::GraphViz->new; $parser->parse($makefile) or die $parser->error; my $gv = $parser->plot( ($opt{root} || $parser->target) , init_args => { rankdir => $opt{rankdir}, # width => undef, height => undef, }, trim_mode => $opt{trim_mode}, ); ## $gv->as_canon("$outfile-1.dot"); $gv->as_ps("$outfile-1.ps"); syst "ps2pdf -sPAPERSIZE=a2 $outfile-1.ps $outfile-1.pdf"; systopen("pdfcrop $outfile-1.pdf $outfile"); unlink "$outfile-1.pdf"; unlink "$outfile-1.ps"; } sub help { print <<"_EOC_"; Usage: $0 file.tex > processed_file.tex Options: ... _EOC_ exit 1; } sub check_tools_for { my $format = shift; if($format eq "makefileg"){ eval { use Makefile::GraphViz; } } my %formats = ( html => [qw.htmldoc.], dot => [qw.dot pdfcrop neato twopi.], gnuplot => [qw.ps2pdf gnuplot.], makefileg => [qw.dot ps2pdf pdfcrop.], pod => [qw.pod2latex.], abc => [qw.abcm2ps ps2pdf pdfcrop abc2midi.], camila => [qw.cam2tex.], csv => [], ); my %tools = ( htmldoc => "http://www.easysw.com/htmldoc/", ps2pdf => "http://www.ghostscript.com/", pod2latex => "http://www.cpan.org/", dot => "http://www.graphviz.org/", twopi => "http://www.graphviz.org/", neato => "http://www.graphviz.org/", gnuplot => "http://www.gnuplot.info/", cam2tex => "http://natura.di.uminho.pt/download/sources/cam2tex", pdfcrop => "check texlive or other TeX distribution", abcm2ps => "http://abcplus.sourceforge.net/", abc2midi => "package abcMIDI", ); return 1 unless exists $formats{$format}; for my $tool (@{$formats{$format}}) { Config::AutoConf->check_prog($tool) or die "$tool is needed to use $format\nPlease install from $tools{$tool}"; } return 1; } __END__ =head1 NAME teximporter - a preprocessor to use HTML, GnuPlot, GraphViz and other tools directly from LaTeX =head1 SYNOPSIS teximporter [options] a.tex > b.tex teximporter -s a.tex > b.tex (ignore \_fmt{...} commands) =head1 DESCRIPTION C command was design to help in the task of include external file formats into \LaTeX{}. You can: =over 4 =item * Import a foreign file \import_fmt{file} =item * Using a foreign format \inline_fmt{extract of language fmt} or \_fmt{extract of language fmt} or \begin{import_fmt} extract of language fmt \end{import_fmt} =back Supported formats (fmt in the examples above) are: HTML POD (Perl documentation format) slides (PDF slides) camila (specification language ) gnuplot dot (graphViz) makefileg (import makefile as a graph) abc (abc music format) csv (csv table) For instance, you can import an HTML file with \import_html{file.html} or you can draw a Graphviz graph with \begin{import_dot} digraph "Makefile" { f -> o; o -> o; } \end{import_dot} =head1 Options -h help -s ignore \_fmt{...} notation -D keep temporary files to help debuging (not yet avail) -Q quiet mode -o=file.pdf =head1 Specific formats documentation Some commands support additional configuration options, that are described here. =head2 Import slides The C command supports the following options: nup=2x3 (def nup=1x2) pages={3,4,9-12} (def all pages) and also do pdfpages atributes: scale= (def "0.9") pagecommand= (def "") frame= (def "true") delta= (def "0.5cm 0.5cm") =head2 Import makefiles The C supports the following options: root=symbol scale=0.4 (def: 0.7) trim_mode=1 ignore makefile commands (def: 0) =head2 Import Abc music notation The C supports the following options: mp3 to generate attatched MP3 midi to generate attatched MIDI wav to generate attatched Wav incipit to generate incipit entry in the index t=120 (define quarter time for acustic music) a=0.90 to make notes more compact ( [0,1] see abcm2ps) + options form includegraphics =head2 Import POD documentation The C supports the following options: h1level=3 (def h1level=2) =head2 Example Some command examples: \import_html{usos_corpora.html} \import_slides[nup=2x3]{/home/jj/docvs/escolaDeVerao/ta.pdf} \import_slides[pages={3,4,9-12}]{/home/jj/docvs/escolaDeVerao/ta.pdf} \import_pod[h1level=3]{teximporter} \begin{import_gnuplot} ... \end{import_gnuplot} \begin{import_makefileg} ... \end{import_makefileg} =head2 External Tools Requirements =over 4 =item * htmldoc -- to translate HTML to PDF =item * Makefile::GraphViz -- to import Makefiles; =item * pod2latex -- to import POD =item * pdfcrop -- to import Makefiles =item * ps2pdf -- to import Makefiles =item * graphViz -- to import Makefiles and Dot =item * gnuplot =back =head1 AUTHOR J.Joao Almeida, jj@di.uminho.pt Alberto Simoes, ambs@cpan.org =head1 SEE ALSO perl(1). LaTeX ABC, abcm2ps abc2midi =cut