review 2.4.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +20 -5
  3. data/.travis.yml +2 -1
  4. data/NEWS.ja.md +93 -0
  5. data/NEWS.md +77 -0
  6. data/README.md +1 -1
  7. data/bin/review +38 -12
  8. data/bin/review-compile +106 -88
  9. data/bin/review-epubmaker +6 -1
  10. data/bin/review-init +21 -1
  11. data/bin/review-textmaker +16 -0
  12. data/doc/config.yml.sample +6 -1
  13. data/doc/format.ja.md +23 -0
  14. data/doc/format.md +20 -2
  15. data/doc/quickstart.ja.md +8 -4
  16. data/doc/quickstart.md +11 -8
  17. data/lib/review/book/base.rb +29 -18
  18. data/lib/review/book/index.rb +10 -5
  19. data/lib/review/builder.rb +58 -33
  20. data/lib/review/catalog.rb +30 -0
  21. data/lib/review/compiler.rb +53 -19
  22. data/lib/review/configure.rb +15 -14
  23. data/lib/review/epubmaker.rb +15 -4
  24. data/lib/review/htmlbuilder.rb +56 -24
  25. data/lib/review/idgxmlbuilder.rb +17 -7
  26. data/lib/review/latexbuilder.rb +113 -38
  27. data/lib/review/markdownbuilder.rb +12 -5
  28. data/lib/review/md2inaobuilder.rb +3 -1
  29. data/lib/review/pdfmaker.rb +23 -9
  30. data/lib/review/plaintextbuilder.rb +683 -0
  31. data/lib/review/rstbuilder.rb +30 -10
  32. data/lib/review/textmaker.rb +158 -0
  33. data/lib/review/textutils.rb +10 -1
  34. data/lib/review/topbuilder.rb +32 -417
  35. data/lib/review/version.rb +1 -1
  36. data/lib/review/webmaker.rb +29 -8
  37. data/review.gemspec +3 -4
  38. data/templates/html/layout-xhtml1.html.erb +0 -2
  39. data/templates/latex/layout.tex.erb +6 -4
  40. data/templates/web/html/layout-xhtml1.html.erb +0 -2
  41. data/test/book_test_helper.rb +1 -0
  42. data/test/run_test.rb +1 -1
  43. data/test/sample-book/src/Rakefile +19 -3
  44. data/test/syntax-book/Rakefile +19 -3
  45. data/test/test_catalog.rb +45 -0
  46. data/test/test_compiler.rb +8 -2
  47. data/test/test_htmlbuilder.rb +22 -0
  48. data/test/test_idgxmlbuilder.rb +22 -0
  49. data/test/test_index.rb +31 -0
  50. data/test/test_latexbuilder.rb +48 -16
  51. data/test/test_plaintextbuilder.rb +390 -0
  52. data/test/test_textutils.rb +2 -0
  53. data/test/test_topbuilder.rb +23 -1
  54. metadata +13 -7
@@ -33,11 +33,16 @@ rescue OptionParser::ParseError => err
33
33
  exit 1
34
34
  end
35
35
 
36
- if ARGV.size < 1 || !File.exist?(ARGV[0])
36
+ if ARGV.size < 1
37
37
  puts opts.help
38
38
  exit 1
39
39
  end
40
40
 
41
+ unless File.exist?(ARGV[0])
42
+ @logger.error "#{File.basename($PROGRAM_NAME, '.*')}: #{ARGV[0]} not found."
43
+ exit 1
44
+ end
45
+
41
46
  yaml_file = ARGV[0]
42
47
  bookname = ARGV[1]
43
48
  rv.produce(yaml_file, bookname)
@@ -29,6 +29,7 @@ def main
29
29
  opts.on('-f', '--force', 'generate files (except *.re) if directory has already existed.') { @force = true }
30
30
  opts.on('-l', '--locale', 'generate locale.yml file.') { @locale = true }
31
31
  opts.on('', '--epub-version VERSION', 'define EPUB version') { |version| @epub_version = version }
32
+ opts.on('', '--without-doc', "don't generate doc files") { @without_doc = true }
32
33
  begin
33
34
  opts.parse!
34
35
  rescue OptionParser::ParseError => err
@@ -57,6 +58,7 @@ def main
57
58
  generate_locale(dir) if @locale
58
59
  generate_rakefile(dir)
59
60
  generate_gemfile(dir)
61
+ generate_doc(dir) unless @without_doc
60
62
  end
61
63
  end
62
64
 
@@ -130,7 +132,18 @@ def generate_texmacro(dir)
130
132
  end
131
133
 
132
134
  def generate_rakefile(dir)
133
- FileUtils.cp @review_dir + '/test/sample-book/src/Rakefile', dir
135
+ FileUtils.mkdir_p dir + '/lib/tasks'
136
+
137
+ File.open(dir + '/Rakefile', 'w') do |file|
138
+ file.write <<-EOS
139
+ Dir.glob('lib/tasks/*.rake').each do |file|
140
+ load(file)
141
+ end
142
+ EOS
143
+ end
144
+
145
+ FileUtils.cp @review_dir + '/test/sample-book/src/Rakefile',
146
+ dir + '/lib/tasks/review.rake'
134
147
  end
135
148
 
136
149
  def generate_locale(dir)
@@ -148,4 +161,11 @@ EOS
148
161
  end
149
162
  end
150
163
 
164
+ def generate_doc(dir)
165
+ docdir = dir + '/doc'
166
+ FileUtils.mkdir_p docdir
167
+ md_files = Dir.glob(@review_dir + '/doc/*.md').map.to_a
168
+ FileUtils.cp md_files, docdir
169
+ end
170
+
151
171
  main if File.basename($PROGRAM_NAME) == File.basename(__FILE__)
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ # Copyright (c) 2018 Kenshi Muto
3
+ #
4
+ # This program is free software.
5
+ # You can distribute or modify this program under the terms of
6
+ # the GNU LGPL, Lesser General Public License version 2.1.
7
+ # For details of the GNU LGPL, see the file "COPYING".
8
+ #
9
+
10
+ require 'pathname'
11
+ bindir = Pathname.new(__FILE__).realpath.dirname
12
+ $LOAD_PATH.unshift((bindir + '../lib').realpath)
13
+
14
+ require 'review/textmaker'
15
+
16
+ ReVIEW::TEXTMaker.execute(*ARGV)
@@ -314,9 +314,14 @@ pdfmaker:
314
314
  # 渡される引数1=作業用展開ディレクトリ、引数2=呼び出しを実行したディレクトリ
315
315
  # hook_afterdvipdf: null
316
316
  #
317
- # 画像のscale=X.Xという指定を画像拡大縮小率からページ最大幅の相対倍率に変換します。
317
+ # 画像のscale=X.Xという指定を画像拡大縮小率からページ最大幅の相対倍率に変換する
318
318
  # image_scale2width: true
319
319
  #
320
+ # PDFやIllustratorファイル(.ai)の画像のBoudingBoxの抽出に指定のボックスを採用する
321
+ # cropbox(デフォルト), mediabox, artbox, trimbox, bleedboxから選択する。
322
+ # Illustrator CC以降のIllustratorファイルに対してはmediaboxを指定する必要がある
323
+ # bbox: mediabox
324
+ #
320
325
  # 奥付を作成するか。trueを指定するとデフォルトの奥付、ファイル名を指定するとそれがcolophon.htmlとしてコピーされる
321
326
  colophon: true
322
327
  # pdfmaker:階層を使うものはここまで
@@ -337,6 +337,12 @@ V1 --> V6 --|
337
337
  * `<id>` は //image[〜] の最初に入れた「〜」のことです(つまり、ID に日本語や空白交じりの文字を使ってしまうと、後で画像ファイル名の名前付けに苦労することになります!)。
338
338
  * `<ext>` は Re:VIEW が自動で判別する拡張子です。ビルダによってサポートおよび優先する拡張子は異なります。
339
339
 
340
+ 各ビルダでは、以下の拡張子から最初に発見した画像ファイルが使われます。
341
+
342
+ * HTMLBuilder (EPUBMaker、WEBMaker)、MARKDOWNBuilder: .png、.jpg、.jpeg、.gif、.svg
343
+ * LATEXBuilder (PDFMaker): .ai、.eps、.pdf、.tif、.tiff、.png、.bmp、.jpg、.jpeg、.gif
344
+ * それ以外のビルダ: .ai、.psd、.eps、.pdf、.tif、.tiff、.png、.bmp、.jpg、.jpeg、.gif、.svg
345
+
340
346
  ### インラインの画像挿入
341
347
 
342
348
  段落途中などに画像を貼り込むには、インライン命令の `@<icon>{識別子}` を使います。ファイルの探索ルールは同じです。
@@ -569,6 +575,23 @@ LaTeX の数式が正常に整形されるかどうかは処理系に依存し
569
575
 
570
576
  段落の行頭字下げを制御するタグとして、`//noindent` があります。HTML では `noindent` が `class` 属性に設定されます。
571
577
 
578
+ ## 空行
579
+
580
+ 1行ぶんの空行を明示して入れるには、`//blankline` を使います。
581
+
582
+ 例:
583
+
584
+ ```
585
+ この下に1行の空行が入る
586
+
587
+ //blankline
588
+
589
+ この下に2行の空行が入る
590
+
591
+ //blankline
592
+ //blankline
593
+ ```
594
+
572
595
  ## 見出し参照
573
596
  章に対する参照は、次の3つのインライン命令を利用できます。章 ID は、各章のファイル名から拡張子を除いたものです。たとえば `advanced.re` であれば `advanced` が章 ID です。
574
597
 
@@ -380,6 +380,12 @@ The order of finding image is as follows. The first matched one is used.
380
380
  * ``<id>`` is the ID of the first argument of `//image`. You should use only printable ASCII characters as ID.
381
381
  * ``<ext>`` is file extensions of Re:VIEW. They are different by the builder you use.
382
382
 
383
+ For each builder, image files are searched in order of the following extensions, and the first hit file is adopted.
384
+
385
+ * HTMLBuilder (EPUBMaker, WEBMaker), MARKDOWNBuilder: .png, .jpg, .jpeg, .gif, .svg
386
+ * LATEXBuilder (PDFMaker): .ai, .eps, .pdf, .tif, .tiff, .png, .bmp, .jpg, .jpeg, .gif
387
+ * Other builders: .ai, .psd, .eps, .pdf, .tif, .tiff, .png, .bmp, .jpg, .jpeg, .gif, .svg
388
+
383
389
  ### Inline Images
384
390
 
385
391
  When you want to use images in paragraph, you can use the inline command `@<icon>{ID}`. The order of finding images are same as `//image`.
@@ -610,12 +616,24 @@ Usage:
610
616
 
611
617
  `//noindent` is a tag for spacing.
612
618
 
613
-
614
619
  * `//noindent` : ingore indentation immediately following line. (in HTML, add `noindent` class)
615
620
 
621
+ ## Blank line
622
+
623
+ `//blankline` put an empty line.
624
+
625
+ Usage:
626
+
627
+ ```
628
+ Insert one blank line below.
616
629
 
617
- `//linebreak` and `//pagebreak` will be obsoleted.
630
+ //blankline
618
631
 
632
+ Insert two blank line below.
633
+
634
+ //blankline
635
+ //blankline
636
+ ```
619
637
 
620
638
  ## Referring headings
621
639
 
@@ -113,17 +113,19 @@ review-init コマンドによらず、独自に作業フォルダを作成し
113
113
 
114
114
  テキストファイルの文字エンコーディングには、UTF-8 を使用してください。
115
115
 
116
- ### PDF 化と EPUB
116
+ ### PDF 化と EPUB 化、プレインテキスト化
117
117
 
118
- review-pdfmaker コマンドで PDF ブックの作成、review-epubmaker コマンドで EPUB ファイルの作成ができます。
118
+ review-pdfmaker コマンドで PDF ブックの作成、review-epubmaker コマンドで EPUB ファイルの作成、review-textmaker コマンドでプレインテキストの作成ができます。
119
119
 
120
120
  PDF を作成するには、TeXLive2012 以上の環境が必要です。EPUB を作成するには、rubyzip gem あるいは zip コマンドが必要です(MathML も使いたいときには、 [MathML ライブラリ](http://www.hinet.mydns.jp/?mathml.rb)も必要です)。
121
121
 
122
122
  いずれのコマンドも、必要な設定情報を記した YAML 形式ファイルを引数に指定して実行します。review-init コマンドで作成した環境には、デフォルトで config.yml として用意されているので、これを利用します。
123
123
 
124
124
  ```bash
125
- $ review-pdfmaker config.yml ←PDFの作成
126
- $ review-epubmaker config.yml ←EPUBの作成
125
+ $ review-pdfmaker config.yml ←PDFの作成
126
+ $ review-epubmaker config.yml ←EPUBの作成
127
+ $ review-textmaker config.yml ←テキストの作成(装飾情報あり)
128
+ $ review-textmaker -n config.yml ←テキストの作成(装飾情報なし)
127
129
  ```
128
130
 
129
131
  rake コマンドを利用できるなら、次のように実行することもできます。
@@ -131,6 +133,8 @@ rake コマンドを利用できるなら、次のように実行することも
131
133
  ```bash
132
134
  $ rake pdf ←PDFの作成
133
135
  $ rake epub ←EPUBの作成
136
+ $ rake text ←テキストの作成(装飾情報あり)
137
+ $ rake plaintext ←テキストの作成(装飾情報なし)
134
138
  ```
135
139
 
136
140
  config.yml のサンプルについては以下を参照してください。
@@ -115,28 +115,31 @@ The web site of Re:VIEW is @<tt>{https://reviewml.org/}.
115
115
 
116
116
  You should use UTF-8 as encodings in text files.
117
117
 
118
- ### generating PDF and EPUB
118
+ ### generating PDF, EPUB, and plain-text
119
119
 
120
- You can generate a PDF file with `review-pdfmaker` command. Also you can generate an EPUB file with `review-epubmaker` command.
120
+ You can generate a PDF file with `review-pdfmaker` command. Also you can generate an EPUB file with `review-epubmaker` command, and can generate an plain-text file with 'review-textmaker' command.
121
121
 
122
122
  To generate PDF, you should install TeXLive 2012 or later. To generate EPUB, you should install zip command.
123
123
  When you want to use MathML, you should install [MathML library](http://www.hinet.mydns.jp/?mathml.rb)
124
124
 
125
- `review-pdfmaker` and `review-epubmaker` need `config.yml`, configuration YAML files. `review-init` command generates `config.yml` in default.
125
+ Each maker need `config.yml`, configuration YAML files. `review-init` command generates `config.yml` in default.
126
126
 
127
127
  ```bash
128
- $ review-pdfmaker config.yml ## generate PDF
129
- $ review-epubmaker config.yml ## generate EPUB
128
+ $ review-pdfmaker config.yml ## generate PDF
129
+ $ review-epubmaker config.yml ## generate EPUB
130
+ $ review-textmaker config.yml ## generate text with decoration
131
+ $ review-textmaker -n config.yml ## generate text without decoration
130
132
  ```
131
133
 
132
134
  You also can generate them with Rake.
133
135
 
134
136
  ```bash
135
- $ rake pdf ## generate PDF
136
- $ rake epub ## generate EPUB
137
+ $ rake pdf ## generate PDF
138
+ $ rake epub ## generate EPUB
139
+ $ rake text ## generate text with decoration
140
+ $ rake plaintext ## generate text without decoration
137
141
  ```
138
142
 
139
-
140
143
  There is a sample YAML file [config.yml.sample](https://github.com/kmuto/review/blob/master/doc/config.yml.sample) in the same directory of this document.
141
144
 
142
145
  ### add chapters and modify them
@@ -32,11 +32,11 @@ module ReVIEW
32
32
 
33
33
  def self.update_rubyenv(dir)
34
34
  return if @basedir_seen.key?(dir)
35
- if File.file?("#{dir}/review-ext.rb")
35
+ if File.file?(File.join(dir, 'review-ext.rb'))
36
36
  if ENV['REVIEW_SAFE_MODE'].to_i & 2 > 0
37
37
  ReVIEW.logger.warn 'review-ext.rb is prohibited in safe mode. ignored.'
38
38
  else
39
- Kernel.load File.expand_path("#{dir}/review-ext.rb")
39
+ Kernel.load File.expand_path(File.join(dir, 'review-ext.rb'))
40
40
  end
41
41
  end
42
42
  @basedir_seen[dir] = true
@@ -183,8 +183,11 @@ module ReVIEW
183
183
  def catalog
184
184
  return @catalog if @catalog.present?
185
185
 
186
- catalogfile_path = "#{basedir}/#{config['catalogfile']}"
187
- @catalog = File.open(catalogfile_path) { |f| Catalog.new(f) } if File.file? catalogfile_path
186
+ catalogfile_path = filename_join(@basedir, config['catalogfile'])
187
+ @catalog = File.open(catalogfile_path, 'r:BOM|utf-8') { |f| Catalog.new(f) } if File.file? catalogfile_path
188
+ if @catalog
189
+ @catalog.validate!(basedir)
190
+ end
188
191
  @catalog
189
192
  end
190
193
 
@@ -226,7 +229,7 @@ module ReVIEW
226
229
  if catalog
227
230
  @read_part = catalog.parts
228
231
  else
229
- @read_part = File.read("#{@basedir}/#{config['part_file']}")
232
+ @read_part = File.read(File.join(@basedir, config['part_file']))
230
233
  end
231
234
  end
232
235
 
@@ -234,23 +237,26 @@ module ReVIEW
234
237
  if catalog
235
238
  catalog.parts.present?
236
239
  else
237
- File.exist?("#{@basedir}/#{config['part_file']}")
240
+ File.exist?(File.join(@basedir, config['part_file']))
238
241
  end
239
242
  end
240
243
 
241
244
  def read_bib
242
- File.read("#{@basedir}/#{bib_file}")
245
+ File.read(File.join(@basedir, bib_file))
243
246
  end
244
247
 
245
248
  def bib_exist?
246
- File.exist?("#{@basedir}/#{bib_file}")
249
+ File.exist?(File.join(@basedir, bib_file))
247
250
  end
248
251
 
249
252
  def prefaces
250
- return mkpart_from_namelist(catalog.predef.split("\n")) if catalog
253
+ if catalog
254
+ return mkpart_from_namelist(catalog.predef.split("\n"))
255
+ end
251
256
 
252
257
  begin
253
- mkpart_from_namelistfile("#{@basedir}/#{config['predef_file']}") if File.file?("#{@basedir}/#{config['predef_file']}")
258
+ predef_file = filename_join(@basedir, config['predef_file'])
259
+ mkpart_from_namelistfile(predef_file) if File.file?(predef_file)
254
260
  rescue FileNotFound => err
255
261
  raise FileNotFound, "preface #{err.message}"
256
262
  end
@@ -264,7 +270,8 @@ module ReVIEW
264
270
  end
265
271
 
266
272
  begin
267
- mkpart_from_namelistfile("#{@basedir}/#{config['postdef_file']}") if File.file?("#{@basedir}/#{config['postdef_file']}")
273
+ postdef_file = filename_join(@basedir, config['postdef_file'])
274
+ mkpart_from_namelistfile(postdef_file) if File.file?(postdef_file)
268
275
  rescue FileNotFound => err
269
276
  raise FileNotFound, "postscript #{err.message}"
270
277
  end
@@ -301,12 +308,12 @@ module ReVIEW
301
308
  return catalog.parts_with_chaps.map do |entry|
302
309
  if entry.is_a?(Hash)
303
310
  chaps = entry.values.first.map do |chap|
304
- chap = Chapter.new(self, num += 1, chap, "#{@basedir}/#{chap}")
311
+ chap = Chapter.new(self, num += 1, chap, File.join(@basedir, chap))
305
312
  chap
306
313
  end
307
314
  Part.new(self, part += 1, chaps, read_part.split("\n")[part - 1])
308
315
  else
309
- chap = Chapter.new(self, num += 1, entry, "#{@basedir}/#{entry}")
316
+ chap = Chapter.new(self, num += 1, entry, File.join(@basedir, entry))
310
317
  if chap.number
311
318
  num = chap.number
312
319
  else
@@ -320,7 +327,7 @@ module ReVIEW
320
327
  chap = read_chaps.
321
328
  strip.lines.map(&:strip).join("\n").split(/\n{2,}/).
322
329
  map do |part_chunk|
323
- chaps = part_chunk.split.map { |chapid| Chapter.new(self, num += 1, chapid, "#{@basedir}/#{chapid}") }
330
+ chaps = part_chunk.split.map { |chapid| Chapter.new(self, num += 1, chapid, File.join(@basedir, chapid)) }
324
331
  if part_exist? && read_part.split("\n").size > part
325
332
  Part.new(self, part += 1, chaps, read_part.split("\n")[part - 1])
326
333
  else
@@ -352,14 +359,14 @@ module ReVIEW
352
359
 
353
360
  def mkchap(name, number = nil)
354
361
  name += ext if File.extname(name).empty?
355
- path = "#{@basedir}/#{name}"
362
+ path = File.join(@basedir, name)
356
363
  raise FileNotFound, "file not exist: #{path}" unless File.file?(path)
357
364
  Chapter.new(self, number, name, path)
358
365
  end
359
366
 
360
367
  def mkchap_ifexist(name, idx = nil)
361
368
  name += ext if File.extname(name).empty?
362
- path = "#{@basedir}/#{name}"
369
+ path = File.join(@basedir, name)
363
370
  if File.file?(path)
364
371
  idx += 1 if idx
365
372
  Chapter.new(self, idx, name, path)
@@ -369,10 +376,10 @@ module ReVIEW
369
376
  def read_file(filename)
370
377
  unless @warn_old_files[filename]
371
378
  @warn_old_files[filename] = true
372
- warn "!!! #{filename} is obsoleted. please use catalog.yml." if caller.none? { |item| item =~ %r{/review/test/test_} }
379
+ ReVIEW.logger.warn "!!! #{filename} is obsoleted. please use catalog.yml." if caller.none? { |item| item =~ %r{/review/test/test_} }
373
380
  end
374
381
  res = ''
375
- File.open("#{@basedir}/#{filename}", 'r:BOM|utf-8') do |f|
382
+ File.open(filename_join(@basedir, filename), 'r:BOM|utf-8') do |f|
376
383
  f.each_line do |line|
377
384
  next if /\A#/ =~ line
378
385
  line.gsub!(/#.*\Z/, '')
@@ -385,6 +392,10 @@ module ReVIEW
385
392
  rescue Errno::EISDIR
386
393
  ''
387
394
  end
395
+
396
+ def filename_join(*args)
397
+ File.join(args.reject(&:nil?))
398
+ end
388
399
  end
389
400
  end
390
401
  end
@@ -88,9 +88,11 @@ module ReVIEW
88
88
 
89
89
  def number(id)
90
90
  chapter = @index.fetch(id)
91
- chapter.format_number
92
- rescue # part
93
- I18n.t('part', chapter.number)
91
+ begin
92
+ chapter.format_number
93
+ rescue # part
94
+ I18n.t('part', chapter.number)
95
+ end
94
96
  end
95
97
 
96
98
  def title(id)
@@ -280,6 +282,7 @@ module ReVIEW
280
282
  headlines = []
281
283
  inside_column = false
282
284
  inside_block = nil
285
+ column_level = -1
283
286
  src.each do |line|
284
287
  if line =~ %r{\A//[a-z]+.*\{\Z}
285
288
  inside_block = true
@@ -293,19 +296,20 @@ module ReVIEW
293
296
 
294
297
  m = HEADLINE_PATTERN.match(line)
295
298
  next if m.nil? || m[1].size > 10 # Ignore too deep index
296
- next if m[4].strip.empty? # no title
297
299
  index = m[1].size - 2
298
300
 
299
301
  # column
300
302
  if m[2] == 'column'
301
303
  inside_column = true
304
+ column_level = index
302
305
  next
303
306
  elsif m[2] == '/column'
304
307
  inside_column = false
305
308
  next
306
309
  end
307
- inside_column = false if indexs.blank? || index <= indexs[-1]
310
+ inside_column = false if indexs.blank? || index <= column_level
308
311
  next if inside_column
312
+ next if m[4].strip.empty? # no title
309
313
 
310
314
  next unless index >= 0
311
315
  if indexs.size > (index + 1)
@@ -324,6 +328,7 @@ module ReVIEW
324
328
  @items = items
325
329
  @chap = chap
326
330
  @index = {}
331
+ @logger = ReVIEW.logger
327
332
  items.each do |i|
328
333
  @logger.warn "warning: duplicate ID: #{i.id}" if @index[i.id]
329
334
  @index[i.id] = i
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2002-2017 Minero Aoki, Kenshi Muto
1
+ # Copyright (c) 2002-2018 Minero Aoki, Kenshi Muto
2
2
  #
3
3
  # This program is free software.
4
4
  # You can distribute or modify this program under the terms of
@@ -12,6 +12,7 @@ require 'review/compiler'
12
12
  require 'review/sec_counter'
13
13
  require 'stringio'
14
14
  require 'cgi'
15
+ require 'fileutils'
15
16
 
16
17
  module ReVIEW
17
18
  class Builder
@@ -27,10 +28,13 @@ module ReVIEW
27
28
  nil
28
29
  end
29
30
 
31
+ attr_accessor :doc_status
32
+
30
33
  def initialize(strict = false, *args)
31
34
  @strict = strict
32
35
  @output = nil
33
36
  @logger = ReVIEW.logger
37
+ @doc_status = {}
34
38
  builder_init(*args)
35
39
  end
36
40
 
@@ -148,8 +152,12 @@ module ReVIEW
148
152
  return if rows.empty?
149
153
  table_begin rows.first.size
150
154
  if sepidx
151
- sepidx.times { tr(rows.shift.map { |s| th(s) }) }
152
- rows.each { |cols| tr(cols.map { |s| td(s) }) }
155
+ sepidx.times do
156
+ tr(rows.shift.map { |s| th(s) })
157
+ end
158
+ rows.each do |cols|
159
+ tr(cols.map { |s| td(s) })
160
+ end
153
161
  else
154
162
  rows.each do |cols|
155
163
  h, *cs = *cols
@@ -160,9 +168,15 @@ module ReVIEW
160
168
  end
161
169
 
162
170
  def adjust_n_cols(rows)
163
- rows.each { |cols| cols.pop while cols.last and cols.last.strip.empty? }
171
+ rows.each do |cols|
172
+ while cols.last and cols.last.strip.empty?
173
+ cols.pop
174
+ end
175
+ end
164
176
  n_maxcols = rows.map(&:size).max
165
- rows.each { |cols| cols.concat [''] * (n_maxcols - cols.size) }
177
+ rows.each do |cols|
178
+ cols.concat [''] * (n_maxcols - cols.size)
179
+ end
166
180
  rows
167
181
  end
168
182
  private :adjust_n_cols
@@ -183,6 +197,10 @@ module ReVIEW
183
197
  # footnote_end
184
198
  # end
185
199
 
200
+ def blankline
201
+ puts ''
202
+ end
203
+
186
204
  def compile_inline(s)
187
205
  @compiler.text(s)
188
206
  end
@@ -191,35 +209,30 @@ module ReVIEW
191
209
  compile_inline @book.chapter_index.display_string(id)
192
210
  rescue KeyError
193
211
  error "unknown chapter: #{id}"
194
- nofunc_text("[UnknownChapter:#{id}]")
195
212
  end
196
213
 
197
214
  def inline_chap(id)
198
215
  @book.chapter_index.number(id)
199
216
  rescue KeyError
200
217
  error "unknown chapter: #{id}"
201
- nofunc_text("[UnknownChapter:#{id}]")
202
218
  end
203
219
 
204
220
  def inline_title(id)
205
221
  compile_inline @book.chapter_index.title(id)
206
222
  rescue KeyError
207
223
  error "unknown chapter: #{id}"
208
- nofunc_text("[UnknownChapter:#{id}]")
209
224
  end
210
225
 
211
226
  def inline_list(id)
212
227
  "#{I18n.t('list')}#{@chapter.list(id).number}"
213
228
  rescue KeyError
214
229
  error "unknown list: #{id}"
215
- nofunc_text("[UnknownList:#{id}]")
216
230
  end
217
231
 
218
232
  def inline_img(id)
219
233
  "#{I18n.t('image')}#{@chapter.image(id).number}"
220
234
  rescue KeyError
221
235
  error "unknown image: #{id}"
222
- nofunc_text("[UnknownImage:#{id}]")
223
236
  end
224
237
 
225
238
  def inline_imgref(id)
@@ -236,14 +249,12 @@ module ReVIEW
236
249
  "#{I18n.t('table')}#{@chapter.table(id).number}"
237
250
  rescue KeyError
238
251
  error "unknown table: #{id}"
239
- nofunc_text("[UnknownTable:#{id}]")
240
252
  end
241
253
 
242
254
  def inline_fn(id)
243
255
  @chapter.footnote(id).content
244
256
  rescue KeyError
245
257
  error "unknown footnote: #{id}"
246
- nofunc_text("[UnknownFootnote:#{id}]")
247
258
  end
248
259
 
249
260
  def inline_bou(str)
@@ -252,8 +263,12 @@ module ReVIEW
252
263
 
253
264
  def inline_ruby(arg)
254
265
  base, *ruby = *arg.scan(/(?:(?:(?:\\\\)*\\,)|[^,\\]+)+/)
255
- base = base.gsub(/\\,/, ',') if base
256
- ruby = ruby.join(',').gsub(/\\,/, ',') if ruby
266
+ if base
267
+ base = base.gsub(/\\,/, ',')
268
+ end
269
+ if ruby
270
+ ruby = ruby.join(',').gsub(/\\,/, ',')
271
+ end
257
272
  compile_ruby(base, ruby)
258
273
  end
259
274
 
@@ -265,7 +280,9 @@ module ReVIEW
265
280
  def inline_href(arg)
266
281
  url, label = *arg.scan(/(?:(?:(?:\\\\)*\\,)|[^,\\]+)+/).map(&:lstrip)
267
282
  url = url.gsub(/\\,/, ',').strip
268
- label = label.gsub(/\\,/, ',').strip if label
283
+ if label
284
+ label = label.gsub(/\\,/, ',').strip
285
+ end
269
286
  compile_href(url, label)
270
287
  end
271
288
 
@@ -284,20 +301,23 @@ module ReVIEW
284
301
 
285
302
  def inline_hd(id)
286
303
  m = /\A([^|]+)\|(.+)/.match(id)
287
- chapter = @book.contents.detect { |chap| chap.id == m[1] } if m && m[1]
304
+ if m && m[1]
305
+ chapter = @book.contents.detect { |chap| chap.id == m[1] }
306
+ end
288
307
  if chapter
289
308
  inline_hd_chap(chapter, m[2])
290
309
  else
291
310
  inline_hd_chap(@chapter, id)
292
311
  end
293
312
  rescue KeyError
294
- error "unknown hd: #{id}"
295
- nofunc_text("[UnknownHeader:#{id}]")
313
+ error "unknown headline: #{id}"
296
314
  end
297
315
 
298
316
  def inline_column(id)
299
317
  m = /\A([^|]+)\|(.+)/.match(id)
300
- chapter = @book.chapters.detect { |chap| chap.id == m[1] } if m && m[1]
318
+ if m && m[1]
319
+ chapter = @book.chapters.detect { |chap| chap.id == m[1] }
320
+ end
301
321
  if chapter
302
322
  inline_column_chap(chapter, m[2])
303
323
  else
@@ -305,7 +325,6 @@ module ReVIEW
305
325
  end
306
326
  rescue KeyError
307
327
  error "unknown column: #{id}"
308
- nofunc_text("[UnknownColumn:#{id}]")
309
328
  end
310
329
 
311
330
  def inline_column_chap(chapter, id)
@@ -324,7 +343,9 @@ module ReVIEW
324
343
  if matched = str.match(/\|(.*?)\|(.*)/)
325
344
  builders = matched[1].split(',').map { |i| i.gsub(/\s/, '') }
326
345
  c = target_name
327
- print matched[2].gsub('\\n', "\n") if builders.include?(c)
346
+ if builders.include?(c)
347
+ print matched[2].gsub('\\n', "\n")
348
+ end
328
349
  else
329
350
  print str.gsub('\\n', "\n")
330
351
  end
@@ -345,8 +366,11 @@ module ReVIEW
345
366
  end
346
367
 
347
368
  def error(msg)
348
- raise ApplicationError, msg if msg =~ /:\d+: error: /
349
- raise ApplicationError, "#{@location}: error: #{msg}"
369
+ if msg =~ /:\d+: error: /
370
+ raise ApplicationError, msg
371
+ else
372
+ raise ApplicationError, "#{@location}: error: #{msg}"
373
+ end
350
374
  end
351
375
 
352
376
  def handle_metric(str)
@@ -374,15 +398,20 @@ module ReVIEW
374
398
 
375
399
  def get_chap(chapter = @chapter)
376
400
  if @book.config['secnolevel'] > 0 && !chapter.number.nil? && !chapter.number.to_s.empty?
377
- return I18n.t('part_short', chapter.number) if chapter.is_a?(ReVIEW::Book::Part)
378
- return chapter.format_number(nil)
401
+ if chapter.is_a?(ReVIEW::Book::Part)
402
+ return I18n.t('part_short', chapter.number)
403
+ else
404
+ return chapter.format_number(nil)
405
+ end
379
406
  end
380
407
  nil
381
408
  end
382
409
 
383
410
  def extract_chapter_id(chap_ref)
384
411
  m = /\A([\w+-]+)\|(.+)/.match(chap_ref)
385
- return [@book.contents.detect { |chap| chap.id == m[1] }, m[2]] if m
412
+ if m
413
+ return [@book.contents.detect { |chap| chap.id == m[1] }, m[2]]
414
+ end
386
415
  [@chapter, chap_ref]
387
416
  end
388
417
 
@@ -401,7 +430,7 @@ module ReVIEW
401
430
  def graph(lines, id, command, caption = nil)
402
431
  c = target_name
403
432
  dir = File.join(@book.basedir, @book.image_dir, c)
404
- Dir.mkdir(dir) unless File.exist?(dir)
433
+ FileUtils.mkdir_p(dir)
405
434
  file = "#{id}.#{image_ext}"
406
435
  file_path = File.join(dir, file)
407
436
 
@@ -410,7 +439,7 @@ module ReVIEW
410
439
  graphviz: "echo '#{line}' | dot -T#{image_ext} -o#{file_path}",
411
440
  gnuplot: %Q(echo 'set terminal ) +
412
441
  "#{image_ext == 'eps' ? 'postscript eps' : image_ext}\n" +
413
- %Q(" set output "#{file_path}"\n#{line}' | gnuplot),
442
+ %Q( set output "#{file_path}"\n#{line}' | gnuplot),
414
443
  blockdiag: "echo '#{line}' " +
415
444
  "| blockdiag -a -T #{image_ext} -o #{file_path} /dev/stdin",
416
445
  aafigure: "echo '#{line}' | aafigure -t#{image_ext} -o#{file_path}"
@@ -428,11 +457,7 @@ module ReVIEW
428
457
  end
429
458
 
430
459
  def inline_include(file_name)
431
- compile_inline File.read(file_name)
432
- end
433
-
434
- def include(file_name)
435
- File.foreach(file_name) { |line| paragraph([line]) }
460
+ compile_inline File.read(file_name, mode: 'rt:BOM|utf-8').chomp
436
461
  end
437
462
 
438
463
  def ul_item_begin(lines)