1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
#!/usr/local/bin/ruby
# -*- Mode:Ruby; Coding:us-ascii-unix; fill-column:158 -*-
################################################################################################################################################################
##
# @file      latexit.rb
# @author    Mitch Richling <https://www.mitchr.me/>
# @brief     Typeset a snippet of LaTeX, and display it.@EOL
# @keywords  
# @std       Ruby1.8
# @see       
# @copyright 
#  @parblock
#  Copyright (c) 2010,2011,2016, Mitchell Jay Richling <https://www.mitchr.me> All rights reserved.
#
#  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
#
#  1. Redistributions of source code must retain the above copyright notice, this list of conditions, and the following disclaimer.
#
#  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the following disclaimer in the documentation
#     and/or other materials provided with the distribution.
#
#  3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without
#     specific prior written permission.
#
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
#  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
#  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#  @endparblock
# @filedetails
#
# Feed it LaTeX code via STDIN or as the first unrecognized argument, and it will typeset it, crop it, and display it on the screen.
#
#   Three step flow:  
#      0) read it 
#      1) typeset it  
#      2) crop it     
#      3) display it  (-s3)
#
# The ideal tool chain for steps 1-3) varies by platform:
#      Linux     -- pdflatex, pdfcrop, & xpdf.
#      MacOS X   -- xpdf may be replaced with Preview (via open).  
#      Old UNIX  -- latex, dvips, & gv.  
#
# The script makes an attempt to guess the best tool chain from what is available on the platform.  The last step may be skipped via -s3.
#
# Some effort is made to extract code from comment boxes.
# Markup: Lines matching /^\s*--/, will have '\par\hspace{2em} ' inserted at the start of the line.
#
# I normally use this script inside Emacs.  A tiny elisp wrapper allows me to highlight a bit of LaTeX and then fed to this script for display.  Emacs rocks!
#
################################################################################################################################################################

#---------------------------------------------------------------------------------------------------------------------------------------------------------------
require 'fileutils' 

#---------------------------------------------------------------------------------------------------------------------------------------------------------------
# Print stuff to STDOUT immediatly -- important on windows
$stdout.sync = true

#---------------------------------------------------------------------------------------------------------------------------------------------------------------
possibleFlows = [[['pdflatex: pdflatex %FILE%.tex'],                                 # If we don't use LaTeX standalone, then we have a crop step
                  ["pdfcrop: pdfcrop %FILE%.pdf %FILE%-c.pdf"],
                  ['xpdf: xpdf -fullscreen -z page %FILE%-c.pdf', 
                   'evince: evince -s %FILE%-c.pdf', 
                   'okular: okular --presentation %FILE%-c.pdf', 
                   'acroread: acroread %FILE%-c.pdf', 
                   'open: open %FILE%-c.pdf']],
                 [['latex: latex %FILE%.tex'],
                  ['dvips: dvips -E -o %FILE%.eps %FILE%.dvi'],             
                  ['gv: gv -presentation %FILE%.eps', 
                   #'gv: gv `xwininfo -root | grep .-geometry` -antialias %FILE%.eps', 
                   'ghostview: ghostview %FILE%.eps']],
                 [['pdflatex: pdflatex %FILE%.tex'], 
                  ['xpdf: xpdf -geometry 1000x300 -z page %FILE%.pdf',
                   'acroread: acroread %FILE%.pdf', 
                   'open: open %FILE%.pdf']],
                 [['latex: latex %FILE%.tex'], 
                  ['xdvi: xdvi %FILE%.dvi']]
                ];

#---------------------------------------------------------------------------------------------------------------------------------------------------------------
cmdLineCmds = Hash.new(nil)
skipSteps   = Hash.new(nil)
doDoxygen   = true
while ( !(ARGV.empty?) && (tmp = ARGV[0].match(/^-+([cs])([0-9]+)/))) do
  optn = tmp[1]
  step = tmp[2].to_i
  #puts("OPTN: #{optn}  STEP: #{step}")
  ARGV.shift
  if    (optn == 'c') then
    cmdLineCmds[step] = ARGV.shift
    STDERR.puts("ERROR(latexit.rb): The -c option is not yet supported")
    exit
  elsif (optn == 's') then
    skipSteps[step] = 1
    if(step != 3) then
      STDERR.puts("ERROR(latexit.rb): The -s option is only supported with step 3")
      exit
    end
  end
end

#---------------------------------------------------------------------------------------------------------------------------------------------------------------
cleanFlow = Array.new
cleanFlowPath = Array.new
possibleFlows.each do |flow|
  goodFlow = true
  flow.each do |flowStep|
    cleanFlowStep = nil
    flowStep.each do |stepOption|
      tmp = stepOption.match(/^([^:]+):/);
      tool = tmp[1]
      ENV['PATH'].split(File::PATH_SEPARATOR).each do |pathComp|
        posBin = File.join(pathComp, tool) + (((RUBY_PLATFORM =~ /mingw/) || (RUBY_PLATFORM =~ /cygwin/)) ? ".exe" : "")
        puts(pathComp.inspect)
        if (FileTest.exist?(posBin)) then
          cleanFlowStep = posBin
          break
        end
      end
      if (cleanFlowStep) then
        cleanFlow.push(stepOption)
        cleanFlowPath.push(cleanFlowStep)
        break
      else
        puts("WARNING: Results may be substandard.  Missing tool: #{tool}")
      end
    end
    if ( !(cleanFlowStep)) then
      cleanFlow     = Array.new
      cleanFlowPath = Array.new
      goodFlow = false
      break
    end
    if !(goodFlow) then
      break
    end
  end
  if (goodFlow) then
    break
  end
end

#---------------------------------------------------------------------------------------------------------------------------------------------------------------
if ( cleanFlow.empty?) then
  puts("Could not find enough tools to work!")
else
  tmpDir = [ '/tmp/', "#{ENV['TEMP']}", "#{ENV['HOME']}/tmp" ].find { |p| FileTest.directory?(p) }

  if ( !(FileTest.directory?(tmpDir))) then
    puts("Could not find a temp directory (set TEMP environment variable)!")
  else
  
  Dir.chdir(tmpDir)
  fileName = 'temp'

  FileUtils.rm_rf(File.join(tmpDir, "#{fileName}.pdf"))
  FileUtils.rm_rf(File.join(tmpDir, "#{fileName}.tex"))
  FileUtils.rm_rf(File.join(tmpDir, "#{fileName}.aux"))
  FileUtils.rm_rf(File.join(tmpDir, "#{fileName}.log"))
  FileUtils.rm_rf(File.join(tmpDir, "#{fileName}.pdf"))
  FileUtils.rm_rf(File.join(tmpDir, "#{fileName}-p.pdf"))
  FileUtils.rm_rf(File.join(tmpDir, "#{fileName}-c.pdf"))

  doxygenMode = true
  doubleSlashMode = 'auto'
  lineZapRE = {/^\s*(\/\/)+\s*/ => 0,  # c99, c++
               /^\s*#+\s*/      => 0,  # sh, R, perl, ruby
               /^\s*\!+\s*/     => 0,  # F90, F95, F03, F08
               /^\s*C+\s*/      => 0,  # F77
               /^\s*;+\s*/      => 0}  # lisp, elisp

  puts("GENERATING TEX...")
  open("#{fileName}.tex", "w") do |file|
    #file.puts('\documentclass{standalone} \usepackage{amssymb} \usepackage{amsmath} \begin{document} \pagestyle{empty} \setlength{\parindent}{0pt} \setlength{\parskip}{10pt} ')
    file.puts('\documentclass{article}    \usepackage[paperwidth=25in,paperheight=20in]{geometry} \usepackage{amssymb} \usepackage{amsmath} \begin{document} \pagestyle{empty} \setlength{\parindent}{0pt} \setlength{\parskip}{10pt} \begin{minipage}{7in} \setlength{\parindent}{0pt} \setlength{\parskip}{2ex plus 0.5ex minus 0.2ex} ')
    if ( (ARGV[0].nil?) || (ARGV[0]=='-') ) then
      lineArray = STDIN.readlines()
      # Figure out which prefixes are on all lines.  Figure out if all \ chars appear in pairs if doubleSlashMode==auto
      lineCount = 0
      checkDoubleSlashes = (doubleSlashMode == 'auto')
      if checkDoubleSlashes then
        doubleSlashMode = true
      end
      lineArray.each do |line|
        lineZapRE.each_key do |theRe|
          if ( (line.match(theRe)) || (line.match(/^\s*$/))) then
            lineZapRE[theRe] += 1
          end
        end
        line.scan(/\\+/) do |tok|
          if ( (tok.length % 2) != 0) then
            checkDoubleSlashes = false
            doubleSlashMode    = false
          end
        end
        lineCount += 1
      end
      zapRE = nil
      lineZapRE.each do |theRe, lCnt|
        if (lineCount == lCnt) then
          zapRE = theRe
        end
      end
      # Process each line, and push into TeX file
      lineArray.each do |line|
        if (zapRE) then
          line = line.sub(zapRE, '')
        end
        if (doubleSlashMode) then
          line = line.gsub('\\\\', '\\')
        end
        if (doxygenMode) then
          line = line.gsub('\f$', '$').gsub('\f[', '\[').gsub('\f]', '\]')
        end
        if(doDoxygen) then
          line = line.gsub(/[@\\]f\$/, '$')
          line = line.gsub(/[@\\]f[\[\]]/, '$$')
        end
        if(line.match(/^\s*--\s+/)) then
          line = line.sub(/^\s*--/, '\par\hspace{2em} --')
        end
        file.puts(line)
      end
    else
      file.puts(ARGV[0])
    end
    #file.puts('\end{document}')
    file.puts('\end{minipage} \end{document}')
  end

  # execute flow: Render, crop, and view
  step = 1
  cleanFlow.each_with_index do |commandAndLine, index|
    if ( !(skipSteps.member?(step))) then
      commandPath = cleanFlowPath[index]
      command, commandLine = commandAndLine.split(/:\s+/, 2)
      commandExpanded = commandLine.gsub(command, commandPath).gsub('%FILE%', 'temp')
      puts("FLOW STEP: #{step} COMMAND: #{command.inspect}")
      if ((RUBY_PLATFORM =~ /mingw/) || (RUBY_PLATFORM =~ /cygwin/)) then
       system(commandLine.gsub('%FILE%', 'temp'))
      else        
        system(commandLine.gsub(command, commandPath).gsub('%FILE%', 'temp'))
      end
    end
    step += 1
  end
  end
end