-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathrlatex.rb
326 lines (280 loc) · 9.58 KB
/
rlatex.rb
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
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
require 'rubygems'
gem 'trollop'
gem 'facets'
require 'fileutils'
require 'tempfile'
require 'trollop'
require 'facets/string/titlecase'
require 'json'
VERSION = "1.2"
SUB_COMMANDS = %w{new add-package add-section compile templates}
TEMP_FILE = "temp"
PACKAGES_END_MARKER = "%%% packages (end)"
SECTIONS_END_MARKER = "%%% sections (end)"
TEMPLATES_DIR = ENV['HOME'] + "/.rlatex/templates"
HELP = <<-EOS
This is rlatex #{VERSION}, a ruby command line utility for LaTeX projects scaffolding.
usage: ruby rlatex.rb <command> [options]
where <command> can be:
new <project> [options]: creates the project scaffolding.
It accepts the following options:
--class <class>, to specify the document class. Default is set to article.
--sections <sections>, to specify the sections and subsections.
--author <author>, to specify the author.
--title <title>, to specify the document title.
--date <date>, to specify the date, default set to \\today
--font-size <size>, format allowed: <SIZE>pt, e.g. 11pt
--packages <packages>, to add extra packages
--template <template>, to use a template
add-section <section> [where]: adds a section to the document.
Valid "wheres" are:
--before <section>: inserts the new section before section.
--after <section>: inserts the new section after section.
templates: shows the available templates
compile: compile the LaTeX document and puts everything in the output directory.
EOS
class LatexCreator
def initialize
if not File.exists? ENV['HOME'] + '/.rlatex'
FileUtils.mkdir ENV['HOME'] + '/.rlatex'
FileUtils.mkdir ENV['HOME'] + '/.rlatex/templates'
File.new(ENV['HOME'] + '/.rlatex/conf', "w").close
end
end
def new_project(name, author, title, date, font_size, language, dclass, sections, packages, template)
if not File.exists? name
@name = name
@title = title
@date = date
@language = language
@font_size = font_size
@class = dclass
@packages = packages
load_template template if not template.nil?
@sections = @sections || sections
@author = author
FileUtils.mkdir name
FileUtils.mkdir "#{name}/contents"
FileUtils.mkdir "#{name}/pictures"
FileUtils.mkdir "#{name}/output"
create_main_tex
create_sections "#{name}/contents/"
else
puts "Directory #{name} already exists. Please provide another name."
end
end
def load_template(template)
begin
file = "#{TEMPLATES_DIR}/#{template}.json"
parsed_template = JSON.parse(IO.read file)
@class = parsed_template["class"] || @class
@font_size = parsed_template["font_size"] || @font_size
@sections = parsed_template["sections"]
@language = parsed_template["language"]
rescue Errno::ENOENT
abort(HELP)
end
end
def show_templates
Dir.foreach(TEMPLATES_DIR) do |filename|
extension = File.extname filename
if extension.eql? ".json"
description = JSON.parse(IO.read "#{TEMPLATES_DIR}/#{filename}")["description"]
puts "#{filename.delete '.json'}\t#{description}"
end
end
end
def create_main_tex
File.open("#{@name}/main.tex", 'w') do |f|
f.puts "\\documentclass[#{@font_size}]{#{@class}}"
f.puts
f.puts "%%% PACKAGES"
write_packages(f)
f.puts
f.puts "%%% META"
write_meta(f)
f.puts
f.puts "%%% HYPERREF OPTIONS"
write_hypersetup(f)
f.puts
f.puts "\\begin{document}"
f.puts
f.puts "\\maketitle"
f.puts " %%% INPUT"
write_sections @sections, f unless @sections.nil?
f.puts("\\end{document}")
end
end
def create_sections(path)
@sections.each { |section| create_section section, path } unless @sections.nil?
end
def create_section(section, path)
section_name = parse_section_name(section.split("/")[0])
subsections = section.split("/")[1].split(",") unless section.split("/")[1].nil?
File.open("#{path}#{section_name}.tex", 'w') do |f|
f.puts "\\section{#{parse_heading(section_name)}}"
f.puts "\\label{sec:#{section_name}}"
f.puts
subsections.each { |subsection| create_subsection subsection, f } unless subsections.nil?
f.puts
f.puts "% section #{section_name} (end)"
end
end
def add_section(section, position)
create_section section, "contents/"
section_line = "\\input{contents/#{position[:section]}.tex}"
if position[:place] == :before
add_line_before section_line, " \\input{contents/#{section}.tex}", "main.tex"
else
add_line_after section_line, " \\input{contents/#{section}.tex}", "main.tex"
end
end
def write_sections(sections, file)
sections.each do |section|
section_name = parse_section_name(section.split("/")[0])
file.puts " \\input{contents/#{section_name}.tex}"
end
file.puts SECTIONS_END_MARKER
end
def create_subsection(subsection, file)
file.puts "\\subsection{#{parse_heading(subsection)}}"
file.puts "\\label{subsec:#{subsection}}"
file.puts
file.puts "% subsection #{subsection} (end)"
end
def write_meta(file)
file.puts "\\author{#{@author}}"
file.puts "\\title{#{@title}}"
file.puts "\\date{#{@date}}"
end
def write_hypersetup(file)
file.puts "\\makeatletter"
file.puts "\\hypersetup{"
file.puts " colorlinks, linkcolor=black, urlcolor=black,"
file.puts " pdftitle={\\@title},"
file.puts " pdfauthor={\\@author},"
file.puts "}\n\\makeatother"
end
def write_packages(file)
file.puts "\\usepackage[#{@language}]{babel}"
file.puts "\\usepackage{amsmath}"
file.puts "\\usepackage{amssymb}"
file.puts "\\usepackage{graphicx}"
file.puts "\\graphicspath{ {pictures/} }"
file.puts "\\usepackage{booktabs}"
file.puts "\\usepackage{tikz}"
file.puts "\\usepackage{hyperref}"
@packages.each { |pkg| file.puts "\\usepackage{#{pkg}}"} unless @packages.nil?
file.puts PACKAGES_END_MARKER
end
def add_package(package)
add_line_before PACKAGES_END_MARKER, "\\usepackage{#{package}}", "main.tex"
end
def compile(file = "main.tex")
if pdflatex_is_found?
system("pdflatex --output-directory=output #{file}")
else
puts "pdflatex not found."
end
end
def parse_section_name(section_name)
section_name.strip.gsub(/\s+/, "_")
end
def parse_heading(heading)
heading.tr('_', ' ').titlecase
end
def pdflatex_is_found?
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
exe = File.join(path, "pdflatex")
return true if File.executable? exe
end
return false
end
def add_line(line_to_be_found, line_to_be_inserted, path, position)
temp_file = Tempfile.new(TEMP_FILE)
begin
lines = File.readlines(path)
lines.each_cons(2) do |line, next_line|
temp_file.puts line
if position == :before
temp_file.puts line_to_be_inserted if next_line.strip == line_to_be_found
else
temp_file.puts line_to_be_inserted if line.strip == line_to_be_found
end
temp_file.puts next_line if next_line.equal? lines.last
end
temp_file.rewind
FileUtils.mv(temp_file.path, path)
rescue Errno::ENOENT
puts "File #{path} does not exist."
ensure
temp_file.close
temp_file.unlink
end
end
def add_line_after(line_to_be_found, line_to_be_inserted, path)
add_line line_to_be_found, line_to_be_inserted, path, :after
end
def add_line_before(line_to_be_found, line_to_be_inserted, path)
add_line line_to_be_found, line_to_be_inserted, path, :before
end
end
# Commands parsing
global_opts = Trollop::options do
version "#{VERSION} 2013 Edoardo Colombo"
banner "LaTeX scaffolding"
stop_on SUB_COMMANDS
end
cmd = ARGV.shift
abort(HELP) if cmd.nil?
creator = LatexCreator.new
options = case cmd
when "new"
Trollop::options do
opt :class, "Document class", :default => "article"
opt :sections, "Document sections", :type => :strings
opt :author, "Document author", :default => ""
opt :title, "Document title", :default => "Document Title"
opt :date, "Document date", :default => "\\today"
opt :font_size, "Font size", :default => "10pt"
opt :language, "Language", :default => "english"
opt :packages, "Extra packages", :type => :strings
opt :template, "Document template", :type => :string
end
when"add-section"
Trollop::options do
opt :after, "Insert section after specified section", :type => :string
opt :before, "Insert section before specified section", :type => :string
end
end
case cmd
when "new"
abort(HELP) if (name = ARGV.shift).nil?
creator.new_project(name,
options[:author],
options[:title],
options[:date],
options[:font_size],
options[:language],
options[:class],
options[:sections],
options[:packages],
options[:template])
when "compile"
file = ARGV.shift
if file.nil? then creator.compile else creator.compile file end
when "add-package"
abort(HELP) if (package = ARGV.shift).nil?
creator.add_package package
when "add-section"
abort(HELP) if (section = ARGV.shift).nil? || (options[:before].nil? && options[:after].nil?)
position = {}
if !options[:before].nil?
position[:section], position[:place] = options[:before], :before
else
position[:section], position[:place] = options[:after], :after
end
creator.add_section section, position
when "templates"
creator.show_templates
end