#!/usr/bin/perl # # Author: Dean Serenevy # Email: dean@cs.serenevy.net # URL: http://dean.serenevy.net/ # # This software is hereby placed into the public domain. If you use this # code, a simple comment giving credit or an email letting me know that you # find it useful would be courteous but is not required. # # The software is provided "as is" without warranty of any kind, either # expressed or implied including, but not limited to, the implied warranties # of merchantability and fitness for a particular purpose. In no event shall # the authors or copyright holders be liable for any claim, damages or other # liability, whether in an action of contract, tort or otherwise, arising # from, out of or in connection with the software or the use or other # dealings in the software. #----------------------------------------------------------------- # BEGIN Dean::Util code version 1.007 # # use Dean::Util qw/INCLUDE_POD SYSTEM QX qbash compile_latex tex2image/; =head3 SYSTEM Works like the perl L command except that any string expressions passed by reference will not be quoted. This allows, for example, pipes and redirects while still allowing safe escaping of arguments. If the first argument to C is a reference to the string "DEBUG" then the escaped command will be printed to STDERR before being executed. EXAMPLES: # A truly silent mplayer SYSTEM "mplayer", "Movie Files/LOTR: trailer 2.mov", \">/dev/null", \"2>/dev/null"; # multiple commands in one line. SYSTEM "echo", ";", \";", "echo", q/$( This is echoed properly )/; # See what exactly is happening. SYSTEM \"DEBUG", "echo", ";", \";", "echo", q/$( This is echoed properly )/; =cut #BEGIN: SYSTEM; depends: qbash sub SYSTEM { my $DEBUG = (@_ and ref $_[0] and ${$_[0]} eq 'DEBUG') ? shift : undef; my $cmd = join " ", map( ((ref($_)eq'SCALAR') ? $$_ : qbash($_)), @_ ); print STDERR $cmd,$/ if $DEBUG; system $cmd } #END: SYSTEM =head3 QX Works like a combination of L and the L operator. It behaves like L in that it is a subroutine which takes a list of string expressions that are quoted before being passed to the shell. Any string expressions passed by reference will not be quoted. This allows, for example, pipes and redirects while still allowing safe escaping of arguments. The return value is the C of the executed command, just like with the L operator. =cut #BEGIN: QX; depends: qbash sub QX { my $DEBUG = (@_ and ref $_[0] and ${$_[0]} eq 'DEBUG') ? shift : undef; my $cmd = join " ", map( ((ref($_)eq'SCALAR') ? $$_ : qbash($_)), @_ ); print STDERR $cmd,$/ if $DEBUG; qx"$cmd" } #END: QX =head3 qbash($) Returns a string quoted for bash-like shells. The string must contin only printable characters or spaces, otherwise the subroutine will C. The return value is an untainted string wrapped in single quotes C<'> that is ready to be passed to a shell. =cut #BEGIN: qbash { no warnings; eval 'qr/[^\pL\pM\pN\pP\pS\pZ[:print:]\s]/'; my $print = ($@) ? '[^[:print:]\s]' : '[^\pL\pM\pN\pP\pS\pZ[:print:]\s]'; # Should be: \pL\pM\pN\pP\pS\pZ[:print:]\s (but we don't have unicode everywhere) # equivalently: \p{Letter}\p{Mark}\p{Number}\p{Punctuation}\p{Symbol}\p{Separator} # was: [:print:]\s sub qbash($) { local $_ = shift; die "Unquotable expression: $_" if /$print/o; s/'/'\\''/g; /^([\w\-\+\.\/]+)$/s and return "$1"; # Pretty print simple things /^(.*)$/s and return "'$1'"; } } #END: qbash =head3 compile_latex Compiles a LaTeX file. The following options are accepted. =over 4 =item latex An integer specifying the number of times latex is to be run. Reasonable values are 1 (the default) or 2 (if your document has references which need to be resolved). =item print 1 or printer name. Will be printed using dvips. =item dvips 1/0 creates a PostScript file. =item dvipdf 1/0 creates a PDF file. =item bibtex 1/0 runs BibTeX at the right time. =item index 1/0 runs makeindex at the right time. =back =cut #BEGIN: compile_latex, depends: SYSTEM sub compile_latex { unless (exists $INC{"File/Spec.pm"}) { eval 'use File::Spec()'; die "Trying \"use File::Spec\": $@" if $@ } unless (exists $INC{"Cwd.pm"}) { eval 'use Cwd()'; die "Trying \"use Cwd\": $@" if $@ } my $file = shift; my ($f) = ($file =~ /^(.*?)\.tex$/); $f = $file unless defined $f; my %opt = (qw/latex 1/, @_); my $curr_dir = Cwd::cwd(); chdir( (File::Spec->splitpath($file))[1] ) or die "Could not change to TeX directory"; # latex SYSTEM(qw|latex --interaction batchmode|, $file, \">/dev/null 2>/dev/null") for 1..$opt{latex}; if ($opt{bibtex}) { # bibtex SYSTEM("bibtex", $f, \">/dev/null 2>/dev/null"); SYSTEM(qw|latex --interaction batchmode|, $file, \">/dev/null 2>/dev/null") for 1..$opt{latex}; } if ($opt{index}) { # makeindex SYSTEM("makeindex", $f, \">/dev/null 2>/dev/null"); SYSTEM(qw|latex --interaction batchmode|, $file, \">/dev/null 2>/dev/null") for 1..$opt{latex}; } # output and printing SYSTEM("dvips", $f, "-o", "$f.ps", \">/dev/null 2>/dev/null") if $opt{dvips}; SYSTEM("dvipdf", $f, \">/dev/null 2>/dev/null") if $opt{dvipdf}; SYSTEM("dvips", $f, "-f", \"2>/dev/null | lpr") if $opt{print} and $opt{print} eq "1"; SYSTEM("dvips", $f, "-P$opt{print}", \">/dev/null 2>/dev/null") if $opt{print} and $opt{print} ne "1"; chdir $curr_dir; } #END: compile_latex =head3 tex2image Given a string of LaTeX code, returns an image file as a "string". The following options may be provided after the LaTeX string. Also, all options available to L are accepted in this function. =over 4 =item file Save output to the indicated file instead of returning the image as a string. =item type Specify the save file type. This should be a standard "file extension" for the desired output (E.g. "gif" or "png"). The default output is an EPS file. (The ImageMagick command "convert" must be available on your system for this option to succeed.) =item header A header string placed between C<\documentclass{article}> and C<\begin{document}>. Only useful if input tex code does not include C<\begin{document}> or C<\documentclass{article}>. Note: C<\usepackage{color}> and C<\pagestyle{empty}> are always included if either C<\begin{document}> or C<\documentclass{article}> are missing in the provided LaTeX string. =item convert_args Additional arguments to pass to convert when making the image. By default this is C<["-transparent", "white"]>. =item X Specify the X resolution (default 144) =item Y Specify the Y resolution (default 144) =item color =item pagecolor Specify the color or page color. Each may be an RGB hex triplet ("#40036f", the "#" is required!) LaTeX named color (red E green E blue E yellow E cyan E magenta E black E white and perhaps others depending on the DVI driver), a single number representing gray value, an "r,g,b" triplet, or a "c,m,y,k" quadruple. All numbers are percentages between 0 and 1, inclusive. The default values are "black" and "white" respectively. =back =cut #BEGIN tex2image, depends: compile_latex, QX sub tex2image { unless (exists $INC{"File/Temp.pm"}) { eval 'use File::Temp()'; die "Trying \"use File::Temp\": $@" if $@ } my $tex = shift; my %opt = (X => 144, Y => 144, header => '', convert_args => [qw/-transparent white/], color => "black", pagecolor => "white", @_ ); unless (defined $opt{type}) { if (defined $opt{file} and $opt{file} =~ /\.(\w+)$/) { $opt{type} = $1 } else { $opt{type} = "eps" } } for (@opt{qw/color pagecolor/}) { /#([0-9a-fA-F]{6})/ and $_ = "[rgb]{".join(',',map hex($_)/255 ,grep/\S/,split/(..)/,$1)."}" and next; /[a-zA-Z]/ and $_ = "{$_}" and next; tr/,// == 0 and $_ = "[gray]{$_}" and next; tr/,// == 2 and $_ = "[rgb]{$_}" and next; tr/,// == 3 and $_ = "[cymk]{$_}" and next; } $opt{convert_args} = [] unless defined $opt{convert_args} and $opt{convert_args} ne ''; $opt{convert_args} = [split(/ /,$opt{convert_args})] unless ref($opt{convert_args}) eq 'ARRAY'; return unless $tex; $tex = <<" TEX" unless $tex =~ /begin\{document\}/s; \\documentclass{article} $opt{header} \\usepackage{color} \\pagestyle{empty} \\begin{document} \\color$opt{color} \\pagecolor$opt{pagecolor} $tex \\end{document} TEX $tex = <<" TEX" unless $tex =~ /documentclass/s; \\documentclass{article} $opt{header} \\pagestyle{empty} \\usepackage{color} \\color$opt{color} \\pagecolor$opt{pagecolor} $tex TEX my $dir = File::Temp::tempdir( CLEANUP => 1 ); my ($tex_fh, $file) = File::Temp::tempfile( DIR => $dir, SUFFIX => '.tex' ); $tex_fh or die "Error creating temporary files: $!"; print $tex_fh $tex; close $tex_fh; compile_latex($file, %opt); $file =~ s/\.tex$//; return unless -e "$file.dvi"; QX("cd", $dir, \";", qw/dvips -E -X/, $opt{X}, '-Y', $opt{Y}, '-o', "$file.eps", "$file.dvi", \"2>/dev/null"); if ($opt{type} ne "eps") { QX("cd", $dir, \";", "convert", @{$opt{convert_args}}, '-density', "$opt{X}x$opt{Y}", "$file.eps", "$file.$opt{type}"); } return unless -e "$file.$opt{type}"; if (defined $opt{file}) { system "mv", "$file.$opt{type}", $opt{file}; return( ($!) ? $! : 1 ); } else { my $f; open $f, "<", "$file.$opt{type}" or die "Error opening $file.$opt{type} for reading: $!"; binmode $f; local $/ = undef; my $x = <$f>; close $f; return $x; } } #END tex2image # # END Dean::Util code version 1.007 #----------------------------------------------------------------- use Getopt::Long; use File::Spec; $| = 1; my (%opt, $help, $tex, $ask_cleanup); GetOptions( "h|?|help" => \$help, "a|ask" => \$ask_cleanup, "x|X=s" => \$opt{X}, "y|Y=s" => \$opt{Y}, "color=s" => \$opt{color}, "pagecolor=s" => \$opt{pagecolor}, "convert_args=s" => \$opt{convert_args}, "web" => sub{ $opt{pagecolor} = '#777777'; $opt{convert_args} .= ' -transparent #777777' }, ); # Use defaults when not defined for (qw/convert_args color pagecolor X Y/) { delete $opt{$_} unless defined $opt{$_} } if ($help or !@ARGV) { my $cmd_name = (File::Spec->splitpath($0))[2]; print < } tex2image $tex, file => shift(), %opt, @ARGV; exit unless $ask_cleanup; print "Press to cleanup files."; scalar ;