texzip 0.1.6 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/History.txt +3 -0
  2. data/lib/texzip/Project.rb +518 -512
  3. data/version.txt +1 -1
  4. metadata +1 -1
data/History.txt CHANGED
@@ -1,3 +1,6 @@
1
+ == 0.1.7 / 2012-10-25
2
+ * fix plain image path substitution in tex-files
3
+
1
4
  == 0.1.6 / 2012-10-25
2
5
  * add .png extension for images
3
6
  * add -p parameter for storing images without nested subdirectories
@@ -8,516 +8,522 @@ class TeXzip::Error < Exception; end
8
8
 
9
9
  class TeXzip::Project < HighLine
10
10
 
11
- PACKAGE_EXTENSIONS = %w(.sty .cls)
12
- IMAGE_EXTENSIONS = %w(.png .jpg .pdf .eps .pstex)
13
- TEXIMAGE_EXTENSIONS = %w(.pspdftex .pdf_t .pstex_t)
14
-
15
- class Quit < Exception; end
16
-
17
- attr_accessor :overwrite_all
18
-
19
- class FilePath
20
- def initialize(root_dir, file)
21
- @root_dir = Pathname.new(root_dir).expand_path
22
- @file = Pathname.new(file)
23
- @file = @root_dir.join(file).expand_path.relative_path_from(@root_dir)
24
- @out_file = @file
25
- end
26
-
27
- def set_output_directory dir
28
- @out_dir = Pathname.new(dir).expand_path
29
- end
30
-
31
- def set_plain_output_directory dir
32
- set_output_directory dir
33
- @out_file = @file.basename
34
- end
35
-
36
- def file
37
- @file
38
- end
39
-
40
- def out_file= file
41
- @out_file = Pathname.new(file)
42
- end
43
-
44
- def output_path
45
- @out_dir.join(@out_file).expand_path
46
- end
47
-
48
- def path
49
- @root_dir.join(@file).expand_path
50
- end
51
-
52
- def extname
53
- @file.extname
54
- end
55
-
56
- def hash
57
- path.to_s.hash
58
- end
59
-
60
- def eql?(file_path)
61
- path.to_s.eql? file_path.path.to_s
62
- end
63
- end
64
-
65
- def initialize( master_file )
66
- super()
67
- @tex_master_file = Pathname.new(master_file).expand_path
68
-
69
- # All possible include-paths for TeX
70
- @tex_dirs = [@tex_master_file.dirname]
71
- @tex_dirs.concat((ENV["TEXINPUTS"] || "").split(':').map{|d| Pathname.new(d)})
72
- @tex_dirs.map! &:expand_path
73
- @tex_dirs.uniq!
74
-
75
- @overwrite_all = false
76
-
77
- parse_files
78
- end
79
-
80
- def parse_files
81
- # The hash of all files, including the whole text.
82
- @tex_files = {}
83
- @image_files = Set.new
84
- @bib_files = Set.new
85
- @cites = Set.new
86
-
87
- # Read all files recursively
88
- unparsed_files = [@tex_master_file]
89
- until unparsed_files.empty?
90
- fname = unparsed_files.pop
91
- file = find_file( fname )
92
- if file.nil? then
93
- if PACKAGE_EXTENSIONS.include? File.extname(fname)
94
- next
95
- else
96
- raise TeXzip::Error, "Can't find file: #{fname}"
97
- end
98
- end
99
-
100
- unless @tex_files.has_key? file
101
- included_files = parse_file file
102
- unparsed_files.concat included_files
103
- end
104
- end
105
-
106
- unless @bib_files.empty?
107
- @bib = BibTeX::Bibliography.new
108
- @bib_files.each do |bib_file|
109
- bib = BibTeX.open(bib_file.path)
110
- bib.replace_strings
111
- @bib.add(bib.data)
112
- end
113
- else
114
- @bib = nil
115
- end
116
- end
117
-
118
- # Returns the master-file's path.
119
- def master_file
120
- @tex_master_file
121
- end
122
-
123
- # Returns a list of included tex and sty files.
124
- # @return [Array<Pathname>] Included tex files.
125
- def tex_files
126
- @tex_files.keys
127
- end
128
-
129
- # Returns a list of included image-files.
130
- # @return [Array<Pathname>] Included image files.
131
- def image_files
132
- @image_files.to_a
133
- end
134
-
135
- # Returns a list of included BibTeX-files.
136
- # @return [Array<Pathname>] Included BibTeX files.
137
- def bib_files
138
- @bib_files.to_a
139
- end
140
-
141
- # Returns a list of citations.
142
- # @return [Array<String>] Citations.
143
- def cites
144
- @cites.to_a
145
- end
146
-
147
- # Returns the full path for a certain file.
148
- #
149
- # The file is searched in the current directory as well as all
150
- # directories given by the environment variable +TEXINPUTS+
151
- #
152
- # @param [String] file The (relative) path of the file.
153
- # @param [Array<String>] extensions The (possible) file extensions.
154
- # @return [Pathname,nil] The path to the file if exist.
155
- def find_file( file, extensions = [] )
156
- extensions.unshift "" # the empty extension
157
- extensions.uniq!
158
-
159
- @tex_dirs.each do |d|
160
- extensions.each do |ext|
161
- file_path = d.join(file + ext).expand_path
162
- if File.file? file_path
163
- return FilePath.new(d, file + ext)
164
- end
165
- end
166
- end
167
- return nil
168
- end
169
-
170
- # Returns the full paths for all variants of a certain file.
171
- #
172
- # The files are searched in the current directory as well as all
173
- # directories given by the environment variable +TEXINPUTS+
174
- #
175
- # @param [String] file The base file-name.
176
- # @param [Array<String>] extensions The possible file-extensions.
177
- # @return [Array<Pathname>] All found files.
178
- def find_files( file, extensions )
179
- extensions.uniq!
180
-
181
- files = []
182
-
183
- extensions.each do |ext|
184
- @tex_dirs.each do |d|
185
- file_path = d.join(file + ext).expand_path
186
- if file_path.file?
187
- files << FilePath.new(d, file + ext)
188
- break
189
- end
190
- end
191
- end
192
-
193
- files
194
- end
195
-
196
- # Load and parse a single tex-file.
197
- #
198
- # The file is parsed for commands including images, BibTeX-files
199
- # and citations. The command along with the command's argument is
200
- # passed to the block. The block is assumed to return a list of
201
- # further tex-files to be parsed.
202
- #
203
- # @param [Pathname,String] file_name The name of the TeX-file to parse
204
- # @return [Array<String>] A list of included TeX-files.
205
- def parse_file file_name, &block
206
- text = nil
207
- File.open(file_name.path, "rb") do |f|
208
- text = f.read
209
- end
210
- @tex_files[file_name] = text
211
-
212
- block = method(:handle_command) unless block
213
-
214
- included_files = []
215
- text_without_comments = ""
216
- text.each_line do |line|
217
- comment_match = line.match /(?:\\)*%/
218
- if comment_match and (comment_match.end(0) - comment_match.begin(0)).odd?
219
- line = line[0...comment_match.end(0)]
220
- end
221
- text_without_comments.concat line
222
- end
223
-
224
- text_without_comments.scan(/\\(documentclass|usepackage|include|input|includegraphics|bibliography|cite)(?:\[[^\]]+\])?\{([^}]+)\}/) do |cmd, arg|
225
- new_files = block.call cmd, arg
226
- included_files.concat new_files if new_files
227
- end
228
- included_files
229
- end
230
-
231
- # Handles parsed commands.
232
- def handle_command command, argument
233
- case command
234
- when "includegraphics"
235
- add_image argument
236
- when "bibliography"
237
- argument.split(',').uniq.each{|f| add_bib f.strip}
238
- when "usepackage"
239
- return [argument + ".sty"]
240
- when "documentclass"
241
- return [argument + ".cls"]
242
- when "cite"
243
- @cites.merge argument.split(',').map(&:strip)
244
- else
245
- ext = File.extname(argument)
246
- if TEXIMAGE_EXTENSIONS.include?(ext)
247
- file = find_file(argument)
248
- unless file
249
- puts "WARNING: Can't find tex-image file #{argument}"
250
- return nil
251
- end
252
- dir = File.dirname(argument)
253
- parse_file file do |command, arg|
254
- if command == "includegraphics"
255
- add_image File.join(dir, arg)
256
- else
257
- raise TeXzip::Error, "Unexpected command '\\#{command}' in tex-image file: \\#{argument}"
258
- end
259
- nil
260
- end
261
- elsif ext != ".tex"
262
- argument += ".tex"
263
- end
264
- return [argument]
265
- end
266
- return nil
267
- end
268
-
269
- # Adds an image to the list of included images.
270
- # @param [String] image_file_name The path of the image-file
271
- def add_image image_file_name
272
- ext = File.extname(image_file_name)
273
- if ext == ""
274
- image_files = find_files( image_file_name, IMAGE_EXTENSIONS )
275
- else
276
- image_files = [find_file( image_file_name )].compact
277
- end
278
-
279
- if image_files.empty?
280
- puts "WARNING: Can't find included image #{image_file_name}"
281
- else
282
- @image_files.merge image_files
283
- end
284
- end
285
-
286
- # Adds a BibTeX-file to the list of included BibTeX-files.
287
- # @param [String] image_file_name The path of the BibTeX-file.
288
- def add_bib bib_file_name
289
- bib_file = find_file( bib_file_name, [".bib"] )
290
-
291
- if bib_file.nil?
292
- puts "WARNING: Can't find included BibTeX file #{bib_file_name}"
293
- else
294
- @bib_files.add bib_file
295
- end
296
- end
297
-
298
- def modify_files outdir, image_dir, plain_img, bibtex_file
299
- @output_directory = Pathname.new(outdir).expand_path
300
- @output_image_directory = @output_directory.join(Pathname.new(image_dir)).expand_path
301
- @output_bibtex = @output_directory.join(Pathname.new(bibtex_file)).expand_path
302
- @modified_files = {}
303
-
304
- plain_img_files = Hash.new{|h,k| h[k] = []}
305
-
306
- @tex_files.each_key do |file|
307
- if TEXIMAGE_EXTENSIONS.include? file.extname
308
- if plain_img
309
- file.set_plain_output_directory @output_image_directory
310
- plain_img_files[file.file.to_s] << file
311
- else
312
- file.set_output_directory @output_image_directory
313
- end
314
- else
315
- file.set_output_directory @output_directory
316
- end
317
- end
318
- @tex_files.each_pair do |file, text|
319
- @modified_files[file] = update_file file, text
320
- end
321
-
322
- @image_files.each do |file|
323
- if plain_img
324
- file.set_plain_output_directory @output_image_directory
325
- plain_img_files[file.file.to_s] << file
326
- else
327
- file.set_output_directory @output_image_directory
328
- end
329
- end
330
-
331
- # maybe rename some files
332
- plain_img_files.each_pair do |fname, files|
333
- ext = File.extname(fname)
334
- next if files.size <= 1
335
- cnt = 2
336
- files.each do |file|
337
- loop do
338
- new_fname = fname.basename.sub_ext("#{cnt}#{ext}")
339
- if plain_img_files.include?(new_fname.to_s)
340
- cnt += 1
341
- else
342
- file.out_file = new_fname
343
- break
344
- end
345
- end
346
- end
347
- end
348
-
349
- filter_bibtex
350
- end
351
-
352
- def update_file tex_file, text
353
- ext = tex_file.path.extname
354
-
355
- new_text = ""
356
- text.each_line do |line|
357
- comment_match = line.match /(?:\\)*%/
358
- if comment_match and (comment_match.end(0) - comment_match.begin(0)).odd?
359
- comment = line[comment_match.end(0) .. -1]
360
- line = line[0...comment_match.end(0)]
361
- else
362
- comment = ""
363
- end
364
- new_line = line.gsub(/(\\(include|input|includegraphics|bibliography)(?:\[[^\]]+\])?)\{([^}]+)\}/) { |m|
365
- start = $1
366
- cmd = $2
367
- file = $3
368
- if cmd == "includegraphics"
369
- if TEXIMAGE_EXTENSIONS.include? ext
370
- file = File.join(tex_file.file.dirname, file)
371
- end
372
- new_file = @output_image_directory.join(Pathname.new(file)).relative_path_from(@output_directory)
373
- elsif cmd == "bibliography"
374
- new_file = @output_bibtex.basename.to_s.gsub(/\.bib$/, '')
375
- else
376
- if TEXIMAGE_EXTENSIONS.include? File.extname(file)
377
- new_file = @output_image_directory.join(Pathname.new(file)).relative_path_from(@output_directory)
378
- else
379
- new_file = @output_directory.join(Pathname.new(file)).relative_path_from(@output_directory)
380
- end
381
- end
382
- "#{start}{#{new_file}}"
383
- }
384
- new_text.concat new_line
385
- new_text.concat comment
386
- end
387
-
388
- return new_text
389
- end
390
-
391
- def filter_bibtex
392
- if @bib
393
- cites = @cites.to_a.map{|c| c.to_s}
394
- seen_cites = cites.to_set
395
- until cites.empty?
396
- cite = cites.pop
397
- entries = @bib[cite]
398
- if entries.nil?
399
- puts "WARNING: Can't find BibTeX-entry #{cite}"
400
- else
401
- entries = [entries] unless entries.kind_of? Array
402
- entries.each do |entry|
403
- crossref = entry["crossref"]
404
- if crossref
405
- crossref.split(',').map(&:strip).each do |ref|
406
- ref = ref.to_s
407
- cites << ref if seen_cites.add? ref
408
- end
409
- end
410
- end
411
- end
412
- end
413
-
414
- @bib = BibTeX::Bibliography.new.add(@bib.data.select{|entry| seen_cites.include? entry.key.to_s})
415
- end
416
- end
417
-
418
- def write_files( force = false )
419
- cwd = Pathname.getwd.expand_path
420
- write_data do |path, data|
421
- puts "Write file #{path.relative_path_from(cwd)}"
422
- FileUtils.mkdir_p path.dirname unless path.dirname.exist?
423
- if data.kind_of? Pathname
424
- FileUtils.copy data, path
425
- else
426
- File.open(path, "wb") do |f|
427
- f.write data
428
- end
429
- end
430
- end
431
- end
432
-
433
- def write_archive( archive_file, force = false )
434
- require 'ffi-libarchive'
435
-
436
- archive_file = Pathname.new(archive_file).expand_path
437
- return unless ask_overwrite(archive_file)
438
-
439
- compression = case File.basename(archive_file.to_s)
440
- when /\.tgz$/, /\.tar\.gz$/
441
- :gzip
442
- when /\.tbz2$/, /\.tar\.bz2$/
443
- :bzip2
444
- when /\.txz$/, /\.tar\.xz$/
445
- :xz
446
- when /\.tlzma$/, /\.tar\.lzma$/
447
- :lzma
448
- when /\.tZ$/, /\.tar\.Z$/
449
- :Z
450
- when /\.tar$/
451
- :none
452
- else
453
- raise TeXzip::Error, "Can't derive archive-type from file name #{archive_file}"
454
- end
455
-
456
- puts "Write archive #{archive_file.relative_path_from(Pathname.getwd)}"
457
- Archive.write_open_filename archive_file.to_s, compression, :tar do |ar|
458
- write_data true do |path, data|
459
- ar.add_entry do |e|
460
- e.pathname = path.relative_path_from(@output_directory).to_s
461
- if data.kind_of? Pathname
462
- e.copy_stat(data.to_s)
463
- File.open(data, "rb", &:read)
464
- else
465
- e.mode = 0644
466
- e.atime = Time.now
467
- e.mtime = Time.now
468
- e.filetype = :file
469
- data
470
- end
471
- end
472
- end
473
- end
474
- end
475
-
476
- def write_data( force = false, &block )
477
- raise ArgumentError, "Block required" unless block
478
-
479
- overwrite_all = force
480
- commands = []
481
-
482
- @modified_files.each_pair do |file, text|
483
- if force or ask_overwrite(file.output_path)
484
- commands << [file.output_path, text]
485
- end
486
- end
487
-
488
- @image_files.each do |file|
489
- if force or ask_overwrite(file.output_path)
490
- commands << [file.output_path, file.path]
491
- end
492
- end
493
-
494
- if @bib and (force or ask_overwrite(@output_bibtex))
495
- commands << [@output_bibtex, @bib.to_s]
496
- end
497
-
498
- commands.each do |path, data|
499
- block.call path, data
500
- end
501
- end
502
-
503
- def ask_overwrite file
504
- if !@overwrite_all and File.exist?(file)
505
- ask("File #{file.relative_path_from(Pathname.getwd)} exists. Overwrite? [Ynaq]") do |q|
506
- q.character = true
507
- q.validate = /[ynaq\r ]/
508
- q.case = :down
509
- q.overwrite = false
510
- q.answer_type = lambda{ |c|
511
- case c
512
- when "q"; raise Quit
513
- when "y"; true
514
- when "n"; false
515
- when "a"; @overwrite_all = true; true
516
- end
517
- }
518
- end
519
- else
520
- true
521
- end
522
- end
11
+ PACKAGE_EXTENSIONS = %w(.sty .cls)
12
+ IMAGE_EXTENSIONS = %w(.png .jpg .pdf .eps .pstex)
13
+ TEXIMAGE_EXTENSIONS = %w(.pspdftex .pdf_t .pstex_t)
14
+
15
+ class Quit < Exception; end
16
+
17
+ attr_accessor :overwrite_all
18
+
19
+ class FilePath
20
+ def initialize(root_dir, file)
21
+ @root_dir = Pathname.new(root_dir).expand_path
22
+ @file = Pathname.new(file)
23
+ @file = @root_dir.join(file).expand_path.relative_path_from(@root_dir)
24
+ @out_file = @file
25
+ end
26
+
27
+ def set_output_directory dir
28
+ @out_dir = Pathname.new(dir).expand_path
29
+ end
30
+
31
+ def set_plain_output_directory dir
32
+ set_output_directory dir
33
+ @out_file = @file.basename
34
+ end
35
+
36
+ def file
37
+ @file
38
+ end
39
+
40
+ def out_file= file
41
+ @out_file = Pathname.new(file)
42
+ end
43
+
44
+ def output_path
45
+ @out_dir.join(@out_file).expand_path
46
+ end
47
+
48
+ def path
49
+ @root_dir.join(@file).expand_path
50
+ end
51
+
52
+ def extname
53
+ @file.extname
54
+ end
55
+
56
+ def hash
57
+ path.to_s.hash
58
+ end
59
+
60
+ def eql?(file_path)
61
+ path.to_s.eql? file_path.path.to_s
62
+ end
63
+ end
64
+
65
+ def initialize( master_file )
66
+ super()
67
+ @tex_master_file = Pathname.new(master_file).expand_path
68
+
69
+ # All possible include-paths for TeX
70
+ @tex_dirs = [@tex_master_file.dirname]
71
+ @tex_dirs.concat((ENV["TEXINPUTS"] || "").split(':').map{|d| Pathname.new(d)})
72
+ @tex_dirs.map! &:expand_path
73
+ @tex_dirs.uniq!
74
+
75
+ @overwrite_all = false
76
+
77
+ parse_files
78
+ end
79
+
80
+ def parse_files
81
+ # The hash of all files, including the whole text.
82
+ @tex_files = {}
83
+ @image_files = Set.new
84
+ @bib_files = Set.new
85
+ @cites = Set.new
86
+
87
+ # Read all files recursively
88
+ unparsed_files = [@tex_master_file]
89
+ until unparsed_files.empty?
90
+ fname = unparsed_files.pop
91
+ file = find_file( fname )
92
+ if file.nil? then
93
+ if PACKAGE_EXTENSIONS.include? File.extname(fname)
94
+ next
95
+ else
96
+ raise TeXzip::Error, "Can't find file: #{fname}"
97
+ end
98
+ end
99
+
100
+ unless @tex_files.has_key? file
101
+ included_files = parse_file file
102
+ unparsed_files.concat included_files
103
+ end
104
+ end
105
+
106
+ unless @bib_files.empty?
107
+ @bib = BibTeX::Bibliography.new
108
+ @bib_files.each do |bib_file|
109
+ bib = BibTeX.open(bib_file.path)
110
+ bib.replace_strings
111
+ @bib.add(bib.data)
112
+ end
113
+ else
114
+ @bib = nil
115
+ end
116
+ end
117
+
118
+ # Returns the master-file's path.
119
+ def master_file
120
+ @tex_master_file
121
+ end
122
+
123
+ # Returns a list of included tex and sty files.
124
+ # @return [Array<Pathname>] Included tex files.
125
+ def tex_files
126
+ @tex_files.keys
127
+ end
128
+
129
+ # Returns a list of included image-files.
130
+ # @return [Array<Pathname>] Included image files.
131
+ def image_files
132
+ @image_files.to_a
133
+ end
134
+
135
+ # Returns a list of included BibTeX-files.
136
+ # @return [Array<Pathname>] Included BibTeX files.
137
+ def bib_files
138
+ @bib_files.to_a
139
+ end
140
+
141
+ # Returns a list of citations.
142
+ # @return [Array<String>] Citations.
143
+ def cites
144
+ @cites.to_a
145
+ end
146
+
147
+ # Returns the full path for a certain file.
148
+ #
149
+ # The file is searched in the current directory as well as all
150
+ # directories given by the environment variable +TEXINPUTS+
151
+ #
152
+ # @param [String] file The (relative) path of the file.
153
+ # @param [Array<String>] extensions The (possible) file extensions.
154
+ # @return [Pathname,nil] The path to the file if exist.
155
+ def find_file( file, extensions = [] )
156
+ extensions.unshift "" # the empty extension
157
+ extensions.uniq!
158
+
159
+ @tex_dirs.each do |d|
160
+ extensions.each do |ext|
161
+ file_path = d.join(file + ext).expand_path
162
+ if File.file? file_path
163
+ return FilePath.new(d, file + ext)
164
+ end
165
+ end
166
+ end
167
+ return nil
168
+ end
169
+
170
+ # Returns the full paths for all variants of a certain file.
171
+ #
172
+ # The files are searched in the current directory as well as all
173
+ # directories given by the environment variable +TEXINPUTS+
174
+ #
175
+ # @param [String] file The base file-name.
176
+ # @param [Array<String>] extensions The possible file-extensions.
177
+ # @return [Array<Pathname>] All found files.
178
+ def find_files( file, extensions )
179
+ extensions.uniq!
180
+
181
+ files = []
182
+
183
+ extensions.each do |ext|
184
+ @tex_dirs.each do |d|
185
+ file_path = d.join(file + ext).expand_path
186
+ if file_path.file?
187
+ files << FilePath.new(d, file + ext)
188
+ break
189
+ end
190
+ end
191
+ end
192
+
193
+ files
194
+ end
195
+
196
+ # Load and parse a single tex-file.
197
+ #
198
+ # The file is parsed for commands including images, BibTeX-files
199
+ # and citations. The command along with the command's argument is
200
+ # passed to the block. The block is assumed to return a list of
201
+ # further tex-files to be parsed.
202
+ #
203
+ # @param [Pathname,String] file_name The name of the TeX-file to parse
204
+ # @return [Array<String>] A list of included TeX-files.
205
+ def parse_file file_name, &block
206
+ text = nil
207
+ File.open(file_name.path, "rb") do |f|
208
+ text = f.read
209
+ end
210
+ @tex_files[file_name] = text
211
+
212
+ block = method(:handle_command) unless block
213
+
214
+ included_files = []
215
+ text_without_comments = ""
216
+ text.each_line do |line|
217
+ comment_match = line.match /(?:\\)*%/
218
+ if comment_match and (comment_match.end(0) - comment_match.begin(0)).odd?
219
+ line = line[0...comment_match.end(0)]
220
+ end
221
+ text_without_comments.concat line
222
+ end
223
+
224
+ text_without_comments.scan(/\\(documentclass|usepackage|include|input|includegraphics|bibliography|cite)(?:\[[^\]]+\])?\{([^}]+)\}/) do |cmd, arg|
225
+ new_files = block.call cmd, arg
226
+ included_files.concat new_files if new_files
227
+ end
228
+ included_files
229
+ end
230
+
231
+ # Handles parsed commands.
232
+ def handle_command command, argument
233
+ case command
234
+ when "includegraphics"
235
+ add_image argument
236
+ when "bibliography"
237
+ argument.split(',').uniq.each{|f| add_bib f.strip}
238
+ when "usepackage"
239
+ return [argument + ".sty"]
240
+ when "documentclass"
241
+ return [argument + ".cls"]
242
+ when "cite"
243
+ @cites.merge argument.split(',').map(&:strip)
244
+ else
245
+ ext = File.extname(argument)
246
+ if TEXIMAGE_EXTENSIONS.include?(ext)
247
+ file = find_file(argument)
248
+ unless file
249
+ puts "WARNING: Can't find tex-image file #{argument}"
250
+ return nil
251
+ end
252
+ dir = File.dirname(argument)
253
+ parse_file file do |command, arg|
254
+ if command == "includegraphics"
255
+ add_image File.join(dir, arg)
256
+ else
257
+ raise TeXzip::Error, "Unexpected command '\\#{command}' in tex-image file: \\#{argument}"
258
+ end
259
+ nil
260
+ end
261
+ elsif ext != ".tex"
262
+ argument += ".tex"
263
+ end
264
+ return [argument]
265
+ end
266
+ return nil
267
+ end
268
+
269
+ # Adds an image to the list of included images.
270
+ # @param [String] image_file_name The path of the image-file
271
+ def add_image image_file_name
272
+ ext = File.extname(image_file_name)
273
+ if ext == ""
274
+ image_files = find_files( image_file_name, IMAGE_EXTENSIONS )
275
+ else
276
+ image_files = [find_file( image_file_name )].compact
277
+ end
278
+
279
+ if image_files.empty?
280
+ puts "WARNING: Can't find included image #{image_file_name}"
281
+ else
282
+ @image_files.merge image_files
283
+ end
284
+ end
285
+
286
+ # Adds a BibTeX-file to the list of included BibTeX-files.
287
+ # @param [String] image_file_name The path of the BibTeX-file.
288
+ def add_bib bib_file_name
289
+ bib_file = find_file( bib_file_name, [".bib"] )
290
+
291
+ if bib_file.nil?
292
+ puts "WARNING: Can't find included BibTeX file #{bib_file_name}"
293
+ else
294
+ @bib_files.add bib_file
295
+ end
296
+ end
297
+
298
+ def modify_files outdir, image_dir, plain_img, bibtex_file
299
+ @output_directory = Pathname.new(outdir).expand_path
300
+ @output_image_directory = @output_directory.join(Pathname.new(image_dir)).expand_path
301
+ @output_bibtex = @output_directory.join(Pathname.new(bibtex_file)).expand_path
302
+ @modified_files = {}
303
+
304
+ plain_img_files = Hash.new{|h,k| h[k] = []}
305
+
306
+ @tex_files.each_key do |file|
307
+ if TEXIMAGE_EXTENSIONS.include? file.extname
308
+ if plain_img
309
+ file.set_plain_output_directory @output_image_directory
310
+ plain_img_files[file.file.to_s] << file
311
+ else
312
+ file.set_output_directory @output_image_directory
313
+ end
314
+ else
315
+ file.set_output_directory @output_directory
316
+ end
317
+ end
318
+ @tex_files.each_pair do |file, text|
319
+ @modified_files[file] = update_file file, text, plain_img
320
+ end
321
+
322
+ @image_files.each do |file|
323
+ if plain_img
324
+ file.set_plain_output_directory @output_image_directory
325
+ plain_img_files[file.file.to_s] << file
326
+ else
327
+ file.set_output_directory @output_image_directory
328
+ end
329
+ end
330
+
331
+ # maybe rename some files
332
+ plain_img_files.each_pair do |fname, files|
333
+ ext = File.extname(fname)
334
+ next if files.size <= 1
335
+ # TODO: not supported when updating references
336
+ raise "Multiple images with the same name but different directories"
337
+ cnt = 2
338
+ files.each do |file|
339
+ loop do
340
+ new_fname = fname.basename.sub_ext("#{cnt}#{ext}")
341
+ if plain_img_files.include?(new_fname.to_s)
342
+ cnt += 1
343
+ else
344
+ file.out_file = new_fname
345
+ break
346
+ end
347
+ end
348
+ end
349
+ end
350
+
351
+ filter_bibtex
352
+ end
353
+
354
+ def update_file tex_file, text, plain
355
+ ext = tex_file.path.extname
356
+
357
+ new_text = ""
358
+ text.each_line do |line|
359
+ comment_match = line.match /(?:\\)*%/
360
+ if comment_match and (comment_match.end(0) - comment_match.begin(0)).odd?
361
+ comment = line[comment_match.end(0) .. -1]
362
+ line = line[0...comment_match.end(0)]
363
+ else
364
+ comment = ""
365
+ end
366
+ new_line = line.gsub(/(\\(include|input|includegraphics|bibliography)(?:\[[^\]]+\])?)\{([^}]+)\}/) { |m|
367
+ start = $1
368
+ cmd = $2
369
+ file = $3
370
+ if cmd == "includegraphics"
371
+ if TEXIMAGE_EXTENSIONS.include? ext
372
+ file = File.join(tex_file.file.dirname, file)
373
+ end
374
+ new_file = @output_image_directory.join(Pathname.new(file)).relative_path_from(@output_directory)
375
+ # TODO: this does not support multiple files with same name
376
+ new_file = @output_image_directory.join(new_file.basename).relative_path_from(@output_directory) if plain
377
+ elsif cmd == "bibliography"
378
+ new_file = @output_bibtex.basename.to_s.gsub(/\.bib$/, '')
379
+ else
380
+ if TEXIMAGE_EXTENSIONS.include? File.extname(file)
381
+ new_file = @output_image_directory.join(Pathname.new(file)).relative_path_from(@output_directory)
382
+ # TODO: this does not support multiple files with same name
383
+ new_file = @output_image_directory.join(new_file.basename).relative_path_from(@output_directory) if plain
384
+ else
385
+ new_file = @output_directory.join(Pathname.new(file)).relative_path_from(@output_directory)
386
+ end
387
+ end
388
+ "#{start}{#{new_file}}"
389
+ }
390
+ new_text.concat new_line
391
+ new_text.concat comment
392
+ end
393
+
394
+ return new_text
395
+ end
396
+
397
+ def filter_bibtex
398
+ if @bib
399
+ cites = @cites.to_a.map{|c| c.to_s}
400
+ seen_cites = cites.to_set
401
+ until cites.empty?
402
+ cite = cites.pop
403
+ entries = @bib[cite]
404
+ if entries.nil?
405
+ puts "WARNING: Can't find BibTeX-entry #{cite}"
406
+ else
407
+ entries = [entries] unless entries.kind_of? Array
408
+ entries.each do |entry|
409
+ crossref = entry["crossref"]
410
+ if crossref
411
+ crossref.split(',').map(&:strip).each do |ref|
412
+ ref = ref.to_s
413
+ cites << ref if seen_cites.add? ref
414
+ end
415
+ end
416
+ end
417
+ end
418
+ end
419
+
420
+ @bib = BibTeX::Bibliography.new.add(@bib.data.select{|entry| seen_cites.include? entry.key.to_s})
421
+ end
422
+ end
423
+
424
+ def write_files( force = false )
425
+ cwd = Pathname.getwd.expand_path
426
+ write_data do |path, data|
427
+ puts "Write file #{path.relative_path_from(cwd)}"
428
+ FileUtils.mkdir_p path.dirname unless path.dirname.exist?
429
+ if data.kind_of? Pathname
430
+ FileUtils.copy data, path
431
+ else
432
+ File.open(path, "wb") do |f|
433
+ f.write data
434
+ end
435
+ end
436
+ end
437
+ end
438
+
439
+ def write_archive( archive_file, force = false )
440
+ require 'ffi-libarchive'
441
+
442
+ archive_file = Pathname.new(archive_file).expand_path
443
+ return unless ask_overwrite(archive_file)
444
+
445
+ compression = case File.basename(archive_file.to_s)
446
+ when /\.tgz$/, /\.tar\.gz$/
447
+ :gzip
448
+ when /\.tbz2$/, /\.tar\.bz2$/
449
+ :bzip2
450
+ when /\.txz$/, /\.tar\.xz$/
451
+ :xz
452
+ when /\.tlzma$/, /\.tar\.lzma$/
453
+ :lzma
454
+ when /\.tZ$/, /\.tar\.Z$/
455
+ :Z
456
+ when /\.tar$/
457
+ :none
458
+ else
459
+ raise TeXzip::Error, "Can't derive archive-type from file name #{archive_file}"
460
+ end
461
+
462
+ puts "Write archive #{archive_file.relative_path_from(Pathname.getwd)}"
463
+ Archive.write_open_filename archive_file.to_s, compression, :tar do |ar|
464
+ write_data true do |path, data|
465
+ ar.add_entry do |e|
466
+ e.pathname = path.relative_path_from(@output_directory).to_s
467
+ if data.kind_of? Pathname
468
+ e.copy_stat(data.to_s)
469
+ File.open(data, "rb", &:read)
470
+ else
471
+ e.mode = 0644
472
+ e.atime = Time.now
473
+ e.mtime = Time.now
474
+ e.filetype = :file
475
+ data
476
+ end
477
+ end
478
+ end
479
+ end
480
+ end
481
+
482
+ def write_data( force = false, &block )
483
+ raise ArgumentError, "Block required" unless block
484
+
485
+ overwrite_all = force
486
+ commands = []
487
+
488
+ @modified_files.each_pair do |file, text|
489
+ if force or ask_overwrite(file.output_path)
490
+ commands << [file.output_path, text]
491
+ end
492
+ end
493
+
494
+ @image_files.each do |file|
495
+ if force or ask_overwrite(file.output_path)
496
+ commands << [file.output_path, file.path]
497
+ end
498
+ end
499
+
500
+ if @bib and (force or ask_overwrite(@output_bibtex))
501
+ commands << [@output_bibtex, @bib.to_s]
502
+ end
503
+
504
+ commands.each do |path, data|
505
+ block.call path, data
506
+ end
507
+ end
508
+
509
+ def ask_overwrite file
510
+ if !@overwrite_all and File.exist?(file)
511
+ ask("File #{file.relative_path_from(Pathname.getwd)} exists. Overwrite? [Ynaq]") do |q|
512
+ q.character = true
513
+ q.validate = /[ynaq\r ]/
514
+ q.case = :down
515
+ q.overwrite = false
516
+ q.answer_type = lambda{ |c|
517
+ case c
518
+ when "q"; raise Quit
519
+ when "y"; true
520
+ when "n"; false
521
+ when "a"; @overwrite_all = true; true
522
+ end
523
+ }
524
+ end
525
+ else
526
+ true
527
+ end
528
+ end
523
529
  end
data/version.txt CHANGED
@@ -1 +1 @@
1
- 0.1.6
1
+ 0.1.7
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: texzip
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.1.6
5
+ version: 0.1.7
6
6
  platform: ruby
7
7
  authors:
8
8
  - Frank Fischer