Class: LatexToPdf

Inherits:
Object
  • Object
show all
Defined in:
lib/rails-latex/latex_to_pdf.rb

Class Method Summary collapse

Class Method Details

.configObject



3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# File 'lib/rails-latex/latex_to_pdf.rb', line 3

def self.config
  @config||={
    :recipe => [
    #  {
    #    :command => 'pdflatex',
    #    :arguments => ['-halt-on-error', '-shell-escape', '-interaction=batchmode'],
    #    :extra_arguments => [],
    #    :runs => 1
    #  }
    ],
    :command => 'pdflatex',
    :arguments => ['-halt-on-error'],
    :default_arguments => ['-shell-escape', '-interaction=batchmode'],
    workdir: ->() { "#{Process.pid}-#{Thread.current.hash}" },
    preservework: false,
    basedir: File.join(Rails.root, 'tmp', 'rails-latex'),
    :parse_runs => 1
  }
end

.escape_latex(text) ⇒ Object

Escapes LaTex special characters in text so that they wont be interpreted as LaTex commands.

This method will use RedCloth to do the escaping if available.



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
# File 'lib/rails-latex/latex_to_pdf.rb', line 108

def self.escape_latex(text)
  # :stopdoc:
  unless instance_variable_defined?(:@latex_escaper) && @latex_escaper
    if defined?(RedCloth::Formatters::LATEX)
      class << (@latex_escaper=RedCloth.new(''))
        include RedCloth::Formatters::LATEX
      end
    else
      class << (@latex_escaper=Object.new)
        ESCAPE_RE=/([{}_$&%#])|([\\^~|<>])/
        ESC_MAP={
          '\\' => 'backslash',
          '^' => 'asciicircum',
          '~' => 'asciitilde',
          '|' => 'bar',
          '<' => 'less',
          '>' => 'greater',
        }

        def latex_esc(text)   # :nodoc:
          text.gsub(ESCAPE_RE) {|m|
            if $1
              "\\#{m}"
            else
              "\\text#{ESC_MAP[m]}{}"
            end
          }
        end
      end
    end
    # :startdoc:
  end

  @latex_escaper.latex_esc(text.to_s.to_str).html_safe
end

.generate_pdf(code, config) ⇒ Object

Converts a string of LaTeX code into a binary string of PDF.

By default, pdflatex is used to convert the file and creates the directory #{Rails.root}/tmp/rails-latex/ to store intermediate files.

The config argument defaults to LatexToPdf.config but can be overridden using @latex_config.



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
# File 'lib/rails-latex/latex_to_pdf.rb', line 30

def self.generate_pdf(code, config)
  config = self.config.merge(config)
  recipe = config[:recipe]

  # Deprecated legacy mode, if no recipe found
  if recipe.length == 0
    if config != self.config
      Rails.logger.warn("Using command, arguments and parse_runs is deprecated in favor of recipe")
    end
    # Regression fix -- ability to override some arguments (-halt-on-error) but not other (-interaction),
    #                   this is expected behaviour as seen in test_broken_doc_overriding_halt_on_error.
    #                   Will be fixed in rails-latex 3
    recipe = [{
      :command => config[:command],
      :arguments => config[:arguments] + config[:default_arguments],
      :runs => config[:parse_runs]
    }]
  end

  # Create directory, prepare additional supporting files (.cls, .sty, ...)
  dir = File.join(config[:basedir], config[:workdir].call)
  input = File.join(dir, 'input.tex')
  FileUtils.mkdir_p(dir)
  supporting = config[:supporting]
  if supporting.kind_of?(String) or supporting.kind_of?(Pathname) or (supporting.kind_of?(Array) and supporting.length > 0)
    FileUtils.cp_r(supporting, dir)
  end
  File.open(input,'wb') {|io| io.write(code)}

  # Process recipe
  recipe.each do |item|
    command = item[:command] || config[:command]
    runs = item[:runs] || config[:parse_runs]
    args = item[:arguments] || config[:arguments] + config[:default_arguments]
    args += item[:extra_arguments].to_a + ['input']
    kwargs = {:out => ["input.log", "a"]}
    Rails.logger.info "Running '#{command} #{args.join(' ')}' in #{dir} #{runs} times..."
    Process.waitpid(
      fork do
        begin
          Dir.chdir dir
          (runs - 1).times do
            clean_exit = system command, *args, **kwargs
            Process.exit! 1 unless clean_exit
          end
          exec command, *args, **kwargs
        rescue
          File.open("input.log", 'a'){|io|
            io.write("#{$!.message}:\n#{$!.backtrace.join("\n")}\n")
          }
        ensure
          Process.exit! 1
        end
      end
    )
  end

  # Finish
  if $?.exitstatus.zero? && File.exist?(pdf_file=input.sub(/\.tex$/,'.pdf'))
    cmd = config[:preservework] ? :cp : :mv
    FileUtils.send(cmd, input, File.join(config[:basedir], 'input.tex'))
    FileUtils.send(cmd, input.sub(/\.tex$/,'.log'),
                   File.join(config[:basedir], 'input.log'))
    result = File.read(pdf_file)
    FileUtils.rm_rf(dir) unless config[:preservework]
  else
    raise RailsLatex::ProcessingError.new(
      "rails-latex failed: See #{input.sub(/\.tex$/,'.log')} for details",
      File.open(input).read,
      File.open(input.sub(/\.tex$/,'.log')).read
    )
  end
  result
end