review 3.0.0 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (170) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby-win.yml +39 -0
  3. data/.github/workflows/ruby.yml +27 -0
  4. data/.rubocop.yml +50 -12
  5. data/.travis.yml +17 -8
  6. data/Dockerfile +21 -5
  7. data/NEWS.ja.md +358 -0
  8. data/NEWS.md +358 -1
  9. data/README.md +11 -8
  10. data/appveyor.yml +1 -3
  11. data/bin/review-catalog-converter +4 -4
  12. data/bin/review-check +8 -8
  13. data/bin/review-checkdep +1 -1
  14. data/bin/review-compile +12 -12
  15. data/bin/review-epubmaker +3 -35
  16. data/bin/review-idgxmlmaker +16 -0
  17. data/bin/review-index +2 -89
  18. data/bin/review-preproc +14 -19
  19. data/bin/review-validate +3 -3
  20. data/bin/review-vol +4 -78
  21. data/doc/LICENSE +1 -1
  22. data/doc/config.yml.sample +46 -12
  23. data/doc/config.yml.sample-simple +4 -2
  24. data/doc/format.ja.md +37 -13
  25. data/doc/format.md +35 -20
  26. data/doc/pdfmaker.ja.md +43 -1
  27. data/doc/pdfmaker.md +42 -1
  28. data/doc/quickstart.ja.md +46 -26
  29. data/doc/quickstart.md +38 -17
  30. data/lib/epubmaker/epubcommon.rb +10 -5
  31. data/lib/epubmaker/epubv2.rb +1 -1
  32. data/lib/epubmaker/epubv3.rb +1 -0
  33. data/lib/epubmaker/producer.rb +4 -2
  34. data/lib/review/book.rb +1 -1
  35. data/lib/review/book/base.rb +38 -79
  36. data/lib/review/book/chapter.rb +18 -3
  37. data/lib/review/book/compilable.rb +6 -5
  38. data/lib/review/book/index.rb +69 -101
  39. data/lib/review/book/index/item.rb +40 -0
  40. data/lib/review/book/page_metric.rb +7 -7
  41. data/lib/review/book/part.rb +28 -5
  42. data/lib/review/book/volume.rb +3 -4
  43. data/lib/review/builder.rb +105 -44
  44. data/lib/review/catalog.rb +13 -16
  45. data/lib/review/compiler.rb +84 -72
  46. data/lib/review/configure.rb +19 -8
  47. data/lib/review/epub2html.rb +37 -4
  48. data/lib/review/epubmaker.rb +62 -7
  49. data/lib/review/extentions/string.rb +0 -4
  50. data/lib/review/htmlbuilder.rb +102 -115
  51. data/lib/review/htmlutils.rb +2 -3
  52. data/lib/review/i18n.rb +2 -2
  53. data/lib/review/i18n.yml +9 -0
  54. data/lib/review/idgxmlbuilder.rb +153 -74
  55. data/lib/review/idgxmlmaker.rb +191 -0
  56. data/lib/review/init-web/finish.html +10 -0
  57. data/lib/review/init-web/index.html +190 -0
  58. data/lib/review/init-web/review-layout-design.js +691 -0
  59. data/lib/review/init.rb +125 -34
  60. data/lib/review/latexbuilder.rb +199 -88
  61. data/lib/review/lineinput.rb +1 -1
  62. data/lib/review/location.rb +32 -0
  63. data/lib/review/logger.rb +4 -8
  64. data/lib/review/makerhelper.rb +24 -5
  65. data/lib/review/markdownbuilder.rb +31 -37
  66. data/lib/review/md2inaobuilder.rb +3 -5
  67. data/lib/review/pdfmaker.rb +44 -22
  68. data/lib/review/plaintextbuilder.rb +106 -85
  69. data/lib/review/preprocessor.rb +32 -41
  70. data/lib/review/rstbuilder.rb +33 -33
  71. data/lib/review/textmaker.rb +19 -3
  72. data/lib/review/textutils.rb +76 -2
  73. data/lib/review/tocprinter.rb +231 -102
  74. data/lib/review/topbuilder.rb +114 -61
  75. data/lib/review/update.rb +19 -19
  76. data/lib/review/version.rb +1 -1
  77. data/lib/review/volumeprinter.rb +99 -0
  78. data/lib/review/webmaker.rb +11 -4
  79. data/lib/review/webtocprinter.rb +38 -35
  80. data/lib/review/yamlloader.rb +26 -16
  81. data/review.gemspec +6 -4
  82. data/samples/sample-book/README.md +7 -2
  83. data/samples/sample-book/src/.gitignore +154 -0
  84. data/samples/sample-book/src/config-ebook.yml +4 -0
  85. data/samples/sample-book/src/config-jlreq-ebook.yml +4 -0
  86. data/samples/sample-book/src/config-jlreq.yml +6 -0
  87. data/samples/sample-book/src/config.yml +2 -2
  88. data/samples/sample-book/src/lib/tasks/review.rake +29 -14
  89. data/samples/sample-book/src/lib/tasks/z01_copy_sty.rake +14 -8
  90. data/samples/syntax-book/ch01.re +4 -2
  91. data/samples/syntax-book/ch02.re +8 -16
  92. data/samples/syntax-book/ch03.re +3 -6
  93. data/samples/syntax-book/config-jlreq-lualatex.yml +4 -0
  94. data/samples/syntax-book/config-jlreq.yml +5 -0
  95. data/samples/syntax-book/config-print.yml +3 -0
  96. data/samples/syntax-book/config.yml +1 -1
  97. data/samples/syntax-book/lib/tasks/review.rake +30 -15
  98. data/samples/syntax-book/lib/tasks/z01_copy_sty.rake +14 -8
  99. data/templates/latex/config.erb +16 -0
  100. data/templates/latex/layout.tex.erb +4 -0
  101. data/templates/latex/review-jlreq/review-base.sty +150 -61
  102. data/templates/latex/review-jlreq/review-jlreq.cls +74 -8
  103. data/templates/latex/review-jlreq/review-style.sty +4 -1
  104. data/templates/latex/review-jsbook/README.md +39 -0
  105. data/templates/latex/review-jsbook/review-base.sty +101 -23
  106. data/templates/latex/review-jsbook/review-jsbook.cls +28 -5
  107. data/templates/latex/review-jsbook/review-style.sty +5 -2
  108. data/templates/opf/epubv3.opf.erb +1 -0
  109. data/templates/web/html/layout-html5.html.erb +2 -2
  110. data/test/assets/test_template.tex +24 -3
  111. data/test/assets/test_template_backmatter.tex +24 -3
  112. data/test/test_book.rb +75 -21
  113. data/test/test_book_chapter.rb +4 -2
  114. data/test/test_book_part.rb +3 -3
  115. data/test/test_builder.rb +16 -0
  116. data/test/test_catalog.rb +24 -42
  117. data/test/test_catalog_converter_cmd.rb +1 -1
  118. data/test/test_epubmaker_cmd.rb +14 -7
  119. data/test/test_helper.rb +15 -7
  120. data/test/test_htmlbuilder.rb +909 -159
  121. data/test/test_i18n.rb +25 -25
  122. data/test/test_idgxmlbuilder.rb +395 -38
  123. data/test/test_idgxmlmaker_cmd.rb +46 -0
  124. data/test/test_image_finder.rb +52 -70
  125. data/test/test_index.rb +50 -10
  126. data/test/test_latexbuilder.rb +1194 -106
  127. data/test/test_latexbuilder_v2.rb +628 -97
  128. data/test/test_logger.rb +14 -1
  129. data/test/test_makerhelper.rb +3 -3
  130. data/test/test_markdownbuilder.rb +134 -16
  131. data/test/test_md2inaobuilder.rb +32 -9
  132. data/test/test_pdfmaker.rb +18 -1
  133. data/test/test_pdfmaker_cmd.rb +100 -6
  134. data/test/test_plaintextbuilder.rb +371 -25
  135. data/test/test_preprocessor.rb +2 -16
  136. data/test/test_rstbuilder.rb +249 -26
  137. data/test/test_textmaker_cmd.rb +54 -0
  138. data/test/test_textutils.rb +109 -2
  139. data/test/test_topbuilder.rb +546 -31
  140. data/test/test_update.rb +17 -8
  141. data/test/test_webtocprinter.rb +66 -34
  142. data/test/test_yamlloader.rb +13 -0
  143. data/vendor/jsclasses/LICENSE +1 -1
  144. data/vendor/jsclasses/jis/jsarticle.cls +53 -14
  145. data/vendor/jsclasses/jis/jsbook.cls +53 -14
  146. data/vendor/jsclasses/jis/jsclasses.dtx +84 -25
  147. data/vendor/jsclasses/jis/jslogo.dtx +4 -4
  148. data/vendor/jsclasses/jis/jslogo.sty +3 -3
  149. data/vendor/jsclasses/jis/jspf.cls +52 -13
  150. data/vendor/jsclasses/jis/jsreport.cls +53 -14
  151. data/vendor/jsclasses/jis/kiyou.cls +53 -14
  152. data/vendor/jsclasses/jis/okumacro.dtx +4 -5
  153. data/vendor/jsclasses/jis/okumacro.sty +3 -4
  154. data/vendor/jsclasses/jsarticle.cls +53 -14
  155. data/vendor/jsclasses/jsbook.cls +53 -14
  156. data/vendor/jsclasses/jsclasses.dtx +84 -25
  157. data/vendor/jsclasses/jsclasses.pdf +0 -0
  158. data/vendor/jsclasses/jslogo.dtx +4 -4
  159. data/vendor/jsclasses/jslogo.pdf +0 -0
  160. data/vendor/jsclasses/jslogo.sty +3 -3
  161. data/vendor/jsclasses/jspf.cls +52 -13
  162. data/vendor/jsclasses/jsreport.cls +53 -14
  163. data/vendor/jsclasses/kiyou.cls +53 -14
  164. data/vendor/jsclasses/okumacro.dtx +4 -5
  165. data/vendor/jsclasses/okumacro.pdf +0 -0
  166. data/vendor/jsclasses/okumacro.sty +3 -4
  167. metadata +55 -10
  168. data/lib/review/tocparser.rb +0 -271
  169. data/samples/syntax-book/review-ext.rb +0 -14
  170. data/test/test_tocparser.rb +0 -25
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (c) 2018 Kenshi Muto
2
+ # Copyright (c) 2018-2019 Kenshi Muto
3
3
  #
4
4
  # This program is free software.
5
5
  # You can distribute or modify this program under the terms of
@@ -9,6 +9,8 @@
9
9
  require 'zip'
10
10
  require 'rexml/document'
11
11
  require 'cgi'
12
+ require 'optparse'
13
+ require 'review/version'
12
14
 
13
15
  module ReVIEW
14
16
  class Epub2Html
@@ -17,13 +19,26 @@ module ReVIEW
17
19
  end
18
20
 
19
21
  def execute(*args)
20
- if args[0].nil? || !File.exist?(args[0])
21
- STDERR.puts <<EOT
22
- Usage: #{File.basename($PROGRAM_NAME)} EPUBfile [file_for_head_and_foot] > HTMLfile
22
+ opts = OptionParser.new
23
+
24
+ opts.banner = <<EOT
25
+ Usage: review-epub2html [options] EPUBfile [file_for_head_and_foot] > HTMLfile
23
26
  file_for_head_and_foot: HTML file to extract header and footer area.
24
27
  This file must be contained in the EPUB.
25
28
  If omitted, the first found file is used.
29
+
26
30
  EOT
31
+ opts.version = ReVIEW::VERSION
32
+ opts.on('--help', 'Prints this message and quit.') do
33
+ puts opts.help
34
+ exit 0
35
+ end
36
+ opts.on('--inline-footnote', 'Embed footnote blocks in paragraph.') { @inline_footnote = true }
37
+
38
+ opts.parse!(args)
39
+
40
+ if args[0].nil? || !File.exist?(args[0])
41
+ puts opts.help
27
42
  exit 1
28
43
  end
29
44
 
@@ -36,6 +51,7 @@ EOT
36
51
  @htmls = {}
37
52
  @head = nil
38
53
  @tail = nil
54
+ @inline_footnote = nil
39
55
  end
40
56
 
41
57
  def parse_epub(epubname)
@@ -99,6 +115,23 @@ EOT
99
115
  e.attributes['href'] = "##{anc}"
100
116
  end
101
117
 
118
+ if @inline_footnote
119
+ # move footnotes to inline as same as LaTeX.
120
+ footnotes = {}
121
+
122
+ doc.each_element("//div[@class='footnote']") do |e|
123
+ e.name = 'span'
124
+ e.attributes.delete('epub:type')
125
+ footnotes[e.attributes['id']] = e
126
+ e.remove
127
+ end
128
+
129
+ doc.each_element("//a[@class='noteref']") do |e|
130
+ e.parent.insert_after(e, footnotes[e.attributes['href'].sub('#', '')])
131
+ e.remove
132
+ end
133
+ end
134
+
102
135
  doc.to_s.
103
136
  sub(/.*(<body.*?>)/m, %Q(<section id="#{sanitize(fname)}">)).
104
137
  sub(%r{(</body>).*}m, '</section>')
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2010-2018 Kenshi Muto and Masayoshi Takahashi
1
+ # Copyright (c) 2010-2020 Kenshi Muto and Masayoshi Takahashi
2
2
  #
3
3
  # This program is free software.
4
4
  # You can distribute or modify this program under the terms of
@@ -39,12 +39,12 @@ module ReVIEW
39
39
  end
40
40
 
41
41
  def error(msg)
42
- @logger.error "#{File.basename($PROGRAM_NAME, '.*')}: #{msg}"
42
+ @logger.error msg
43
43
  exit 1
44
44
  end
45
45
 
46
46
  def warn(msg)
47
- @logger.warn "#{File.basename($PROGRAM_NAME, '.*')}: #{msg}"
47
+ @logger.warn msg
48
48
  end
49
49
 
50
50
  def log(msg)
@@ -64,7 +64,47 @@ module ReVIEW
64
64
  @producer.load(yamlfile)
65
65
  @config = @producer.config
66
66
  @config.maker = 'epubmaker'
67
+ end
68
+
69
+ def self.execute(*args)
70
+ self.new.execute(*args)
71
+ end
72
+
73
+ def parse_opts(args)
74
+ cmd_config = {}
75
+ opts = OptionParser.new
76
+ @buildonly = nil
77
+
78
+ opts.banner = 'Usage: review-epubmaker [options] configfile [export_filename]'
79
+ opts.version = ReVIEW::VERSION
80
+ opts.on('--help', 'Prints this message and quit.') do
81
+ puts opts.help
82
+ exit 0
83
+ end
84
+ opts.on('--[no-]debug', 'Keep temporary files.') { |debug| cmd_config['debug'] = debug }
85
+ opts.on('-y', '--only file1,file2,...', 'Build only specified files.') { |v| @buildonly = v.split(/\s*,\s*/).map { |m| m.strip.sub(/\.re\Z/, '') } }
86
+
87
+ opts.parse!(args)
88
+ if args.size < 1 || args.size > 2
89
+ puts opts.help
90
+ exit 0
91
+ end
92
+
93
+ [cmd_config, args[0], args[1]]
94
+ end
95
+
96
+ def execute(*args)
97
+ @config = ReVIEW::Configure.values
98
+ @config.maker = 'epubmaker'
99
+ cmd_config, yamlfile, exportfile = parse_opts(args)
100
+ error "#{yamlfile} not found." unless File.exist?(yamlfile)
101
+
102
+ load_yaml(yamlfile)
103
+ @config.deep_merge!(cmd_config)
67
104
  update_log_level
105
+ log("Loaded yaml file (#{yamlfile}).")
106
+
107
+ produce(yamlfile, exportfile)
68
108
  end
69
109
 
70
110
  def update_log_level
@@ -89,7 +129,6 @@ module ReVIEW
89
129
  end
90
130
 
91
131
  def produce(yamlfile, bookname = nil)
92
- load_yaml(yamlfile)
93
132
  I18n.setup(@config['language'])
94
133
  bookname ||= @config['bookname']
95
134
  booktmpname = "#{bookname}-epub"
@@ -99,7 +138,7 @@ module ReVIEW
99
138
  rescue ReVIEW::ConfigError => e
100
139
  warn e.message
101
140
  end
102
- log("Loaded yaml file (#{yamlfile}). I will produce #{bookname}.epub.")
141
+ log("#{bookname}.epub will be created.")
103
142
 
104
143
  FileUtils.rm_f("#{bookname}.epub")
105
144
  if @config['debug']
@@ -127,7 +166,7 @@ module ReVIEW
127
166
  copy_backmatter(basetmpdir)
128
167
 
129
168
  math_dir = "./#{@config['imagedir']}/_review_math"
130
- if @config['imgmath'] && File.exist?(File.join(math_dir, '__IMGMATH_BODY__.tex'))
169
+ if @config['imgmath'] && File.exist?(File.join(math_dir, '__IMGMATH_BODY__.map'))
131
170
  make_math_images(math_dir)
132
171
  end
133
172
  call_hook('hook_afterbackmatter', basetmpdir)
@@ -330,6 +369,8 @@ module ReVIEW
330
369
  elsif chap.on_predef?
331
370
  chaptype = 'pre'
332
371
  elsif chap.on_appendix?
372
+ chaptype = 'appendix'
373
+ elsif chap.on_postdef?
333
374
  chaptype = 'post'
334
375
  end
335
376
 
@@ -355,6 +396,11 @@ module ReVIEW
355
396
  end
356
397
  end
357
398
 
399
+ if @buildonly && !@buildonly.include?(id)
400
+ warn "skip #{id}.re"
401
+ return
402
+ end
403
+
358
404
  htmlfile = "#{id}.#{@config['htmlext']}"
359
405
  write_buildlogtxt(basetmpdir, htmlfile, filename)
360
406
  log("Create #{htmlfile} from #{filename}.")
@@ -406,6 +452,13 @@ module ReVIEW
406
452
  path = File.join(basetmpdir, filename)
407
453
  htmlio = File.new(path)
408
454
  Document.parse_stream(htmlio, ReVIEWHeaderListener.new(headlines))
455
+ htmlio.close
456
+
457
+ if headlines.empty?
458
+ warn "#{filename} is discarded because there is no heading. Use `=[notoc]' or `=[nodisp]' to exclude headlines from the table of contents."
459
+ return
460
+ end
461
+
409
462
  properties = detect_properties(path)
410
463
  if properties.present?
411
464
  prop_str = ',properties=' + properties.join(' ')
@@ -433,7 +486,6 @@ module ReVIEW
433
486
  first = nil
434
487
  end
435
488
  end
436
- htmlio.close
437
489
  end
438
490
 
439
491
  def push_contents(_basetmpdir)
@@ -461,6 +513,9 @@ module ReVIEW
461
513
  def copy_stylesheet(basetmpdir)
462
514
  return if @config['stylesheet'].empty?
463
515
  @config['stylesheet'].each do |sfile|
516
+ unless File.exist?(sfile)
517
+ error "#{sfile} is not found."
518
+ end
464
519
  FileUtils.cp(sfile, basetmpdir)
465
520
  @producer.contents.push(Content.new('file' => sfile))
466
521
  end
@@ -2,7 +2,3 @@ if defined?(Encoding) && Encoding.respond_to?('default_external') &&
2
2
  Encoding.default_external != Encoding::UTF_8
3
3
  Encoding.default_external = 'UTF-8'
4
4
  end
5
-
6
- class String
7
- alias_method :each, :each_line
8
- end
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2008-2018 Minero Aoki, Kenshi Muto, Masayoshi Takahashi,
1
+ # Copyright (c) 2008-2020 Minero Aoki, Kenshi Muto, Masayoshi Takahashi,
2
2
  # KADO Masanori
3
3
  # 2002-2007 Minero Aoki
4
4
  #
@@ -12,7 +12,6 @@ require 'review/htmlutils'
12
12
  require 'review/template'
13
13
  require 'review/textutils'
14
14
  require 'review/webtocprinter'
15
- require 'digest'
16
15
  require 'tmpdir'
17
16
  require 'open3'
18
17
 
@@ -287,15 +286,25 @@ module ReVIEW
287
286
  end
288
287
 
289
288
  def box(lines, caption = nil)
290
- puts %Q(<div class="syntax">)
289
+ captionstr = nil
291
290
  if caption.present?
292
- puts %Q(<p class="caption">#{compile_inline(caption)}</p>)
291
+ captionstr = %Q(<p class="caption">#{compile_inline(caption)}</p>)
292
+ end
293
+ puts %Q(<div class="syntax">)
294
+
295
+ if caption_top?('list') && caption.present?
296
+ puts captionstr
293
297
  end
298
+
294
299
  print %Q(<pre class="syntax">)
295
300
  lines.each do |line|
296
301
  puts detab(line)
297
302
  end
298
303
  puts '</pre>'
304
+
305
+ if !caption_top?('list') && caption.present?
306
+ puts captionstr
307
+ end
299
308
  puts '</div>'
300
309
  end
301
310
 
@@ -308,7 +317,7 @@ module ReVIEW
308
317
  end
309
318
 
310
319
  def ul_item_begin(lines)
311
- print "<li>#{lines.join}"
320
+ print "<li>#{join_lines_to_paragraph(lines)}"
312
321
  end
313
322
 
314
323
  def ul_item_end
@@ -329,7 +338,7 @@ module ReVIEW
329
338
  end
330
339
 
331
340
  def ol_item(lines, _num)
332
- puts "<li>#{lines.join}</li>"
341
+ puts "<li>#{join_lines_to_paragraph(lines)}</li>"
333
342
  end
334
343
 
335
344
  def ol_end
@@ -345,7 +354,7 @@ module ReVIEW
345
354
  end
346
355
 
347
356
  def dd(lines)
348
- puts "<dd>#{lines.join}</dd>"
357
+ puts "<dd>#{join_lines_to_paragraph(lines)}</dd>"
349
358
  end
350
359
 
351
360
  def dl_end
@@ -354,10 +363,10 @@ module ReVIEW
354
363
 
355
364
  def paragraph(lines)
356
365
  if @noindent
357
- puts %Q(<p class="noindent">#{lines.join}</p>)
366
+ puts %Q(<p class="noindent">#{join_lines_to_paragraph(lines)}</p>)
358
367
  @noindent = nil
359
368
  else
360
- puts "<p>#{lines.join}</p>"
369
+ puts "<p>#{join_lines_to_paragraph(lines)}</p>"
361
370
  end
362
371
  end
363
372
 
@@ -374,12 +383,7 @@ module ReVIEW
374
383
 
375
384
  def list(lines, id, caption, lang = nil)
376
385
  puts %Q(<div id="#{normalize_id(id)}" class="caption-code">)
377
- begin
378
- list_header id, caption, lang
379
- rescue KeyError
380
- error "no such list: #{id}"
381
- end
382
- list_body id, lines, lang
386
+ super(lines, id, caption, lang)
383
387
  puts '</div>'
384
388
  end
385
389
 
@@ -404,8 +408,7 @@ module ReVIEW
404
408
 
405
409
  def source(lines, caption = nil, lang = nil)
406
410
  puts %Q(<div class="source-code">)
407
- source_header caption
408
- source_body caption, lines, lang
411
+ super(lines, caption, lang)
409
412
  puts '</div>'
410
413
  end
411
414
 
@@ -415,7 +418,7 @@ module ReVIEW
415
418
  end
416
419
  end
417
420
 
418
- def source_body(_id, lines, lang)
421
+ def source_body(lines, lang)
419
422
  print %Q(<pre class="source">)
420
423
  body = lines.inject('') { |i, j| i + detab(j) + "\n" }
421
424
  lexer = lang
@@ -425,30 +428,25 @@ module ReVIEW
425
428
 
426
429
  def listnum(lines, id, caption, lang = nil)
427
430
  puts %Q(<div id="#{normalize_id(id)}" class="code">)
428
- begin
429
- list_header id, caption, lang
430
- rescue KeyError
431
- error "no such list: #{id}"
432
- end
433
- listnum_body lines, lang
431
+ super(lines, id, caption, lang)
434
432
  puts '</div>'
435
433
  end
436
434
 
437
435
  def listnum_body(lines, lang)
436
+ body = lines.inject('') { |i, j| i + detab(j) + "\n" }
437
+ lexer = lang
438
+ first_line_number = line_num
439
+ hs = highlight(body: body, lexer: lexer, format: 'html', linenum: true,
440
+ options: { linenostart: first_line_number })
441
+
438
442
  if highlight?
439
- body = lines.inject('') { |i, j| i + detab(j) + "\n" }
440
- lexer = lang
441
- first_line_number = line_num
442
- puts highlight(body: body, lexer: lexer, format: 'html', linenum: true,
443
- options: { linenostart: first_line_number })
443
+ puts hs
444
444
  else
445
445
  class_names = ['list']
446
446
  class_names.push("language-#{lang}") unless lang.blank?
447
- class_names.push('highlight') if highlight?
448
447
  print %Q(<pre class="#{class_names.join(' ')}">)
449
- first_line_num = line_num
450
- lines.each_with_index do |line, i|
451
- puts detab((i + first_line_num).to_s.rjust(2) + ': ' + line)
448
+ hs.split("\n").each_with_index do |line, i|
449
+ puts detab((i + first_line_number).to_s.rjust(2) + ': ' + line)
452
450
  end
453
451
  puts '</pre>'
454
452
  end
@@ -456,7 +454,7 @@ module ReVIEW
456
454
 
457
455
  def emlist(lines, caption = nil, lang = nil)
458
456
  puts %Q(<div class="emlist-code">)
459
- if caption.present?
457
+ if caption_top?('list') && caption.present?
460
458
  puts %Q(<p class="caption">#{compile_inline(caption)}</p>)
461
459
  end
462
460
  class_names = ['emlist']
@@ -467,46 +465,59 @@ module ReVIEW
467
465
  lexer = lang
468
466
  puts highlight(body: body, lexer: lexer, format: 'html')
469
467
  puts '</pre>'
468
+ if !caption_top?('list') && caption.present?
469
+ puts %Q(<p class="caption">#{compile_inline(caption)}</p>)
470
+ end
470
471
  puts '</div>'
471
472
  end
472
473
 
473
474
  def emlistnum(lines, caption = nil, lang = nil)
474
475
  puts %Q(<div class="emlistnum-code">)
475
- if caption.present?
476
+ if caption_top?('list') && caption.present?
476
477
  puts %Q(<p class="caption">#{compile_inline(caption)}</p>)
477
478
  end
478
479
 
480
+ body = lines.inject('') { |i, j| i + detab(j) + "\n" }
481
+ lexer = lang
482
+ first_line_number = line_num
483
+ hs = highlight(body: body, lexer: lexer, format: 'html', linenum: true,
484
+ options: { linenostart: first_line_number })
479
485
  if highlight?
480
- body = lines.inject('') { |i, j| i + detab(j) + "\n" }
481
- lexer = lang
482
- first_line_number = line_num
483
- puts highlight(body: body, lexer: lexer, format: 'html', linenum: true,
484
- options: { linenostart: first_line_number })
486
+ puts hs
485
487
  else
486
488
  class_names = ['emlist']
487
489
  class_names.push("language-#{lang}") unless lang.blank?
488
490
  class_names.push('highlight') if highlight?
489
491
  print %Q(<pre class="#{class_names.join(' ')}">)
490
- first_line_num = line_num
491
- lines.each_with_index do |line, i|
492
- puts detab((i + first_line_num).to_s.rjust(2) + ': ' + line)
492
+ hs.split("\n").each_with_index do |line, i|
493
+ puts detab((i + first_line_number).to_s.rjust(2) + ': ' + line)
493
494
  end
494
495
  puts '</pre>'
495
496
  end
496
497
 
498
+ if !caption_top?('list') && caption.present?
499
+ puts %Q(<p class="caption">#{compile_inline(caption)}</p>)
500
+ end
501
+
497
502
  puts '</div>'
498
503
  end
499
504
 
500
505
  def cmd(lines, caption = nil)
501
506
  puts %Q(<div class="cmd-code">)
502
- if caption.present?
507
+ if caption_top?('list') && caption.present?
503
508
  puts %Q(<p class="caption">#{compile_inline(caption)}</p>)
504
509
  end
510
+
505
511
  print %Q(<pre class="cmd">)
506
512
  body = lines.inject('') { |i, j| i + detab(j) + "\n" }
507
513
  lexer = 'shell-session'
508
514
  puts highlight(body: body, lexer: lexer, format: 'html')
509
515
  puts '</pre>'
516
+
517
+ if !caption_top?('list') && caption.present?
518
+ puts %Q(<p class="caption">#{compile_inline(caption)}</p>)
519
+ end
520
+
510
521
  puts '</div>'
511
522
  end
512
523
 
@@ -541,18 +552,19 @@ module ReVIEW
541
552
 
542
553
  def texequation(lines, id = nil, caption = '')
543
554
  if id
544
- texequation_header id, caption
555
+ puts %Q(<div id="#{normalize_id(id)}" class="caption-equation">)
556
+ texequation_header(id, caption) if caption_top?('equation')
545
557
  end
546
558
 
547
559
  texequation_body(lines)
548
560
 
549
561
  if id
562
+ texequation_header(id, caption) unless caption_top?('equation')
550
563
  puts '</div>'
551
564
  end
552
565
  end
553
566
 
554
567
  def texequation_header(id, caption)
555
- puts %Q(<div id="#{normalize_id(id)}" class="caption-equation">)
556
568
  if get_chap
557
569
  puts %Q(<p class="caption">#{I18n.t('equation')}#{I18n.t('format_number_header', [get_chap, @chapter.equation(id).number])}#{I18n.t('caption_prefix')}#{compile_inline(caption)}</p>)
558
570
  else
@@ -566,11 +578,11 @@ module ReVIEW
566
578
  require 'math_ml'
567
579
  require 'math_ml/symbol/character_reference'
568
580
  p = MathML::LaTeX::Parser.new(symbol: MathML::Symbol::CharacterReference)
569
- puts p.parse(unescape(lines.join("\n")), true)
581
+ print p.parse(lines.join("\n") + "\n", true)
570
582
  elsif @book.config['imgmath']
571
583
  fontsize = @book.config['imgmath_options']['fontsize'].to_f
572
584
  lineheight = @book.config['imgmath_options']['lineheight'].to_f
573
- math_str = "\\begin{equation*}\n\\fontsize{#{fontsize}}{#{lineheight}}\\selectfont\n#{unescape(lines.join("\n"))}\n\\end{equation*}\n"
585
+ math_str = "\\begin{equation*}\n\\fontsize{#{fontsize}}{#{lineheight}}\\selectfont\n#{lines.join("\n")}\n\\end{equation*}\n"
574
586
  key = Digest::SHA256.hexdigest(math_str)
575
587
  math_dir = File.join(@book.config['imagedir'], '_review_math')
576
588
  Dir.mkdir(math_dir) unless Dir.exist?(math_dir)
@@ -615,20 +627,22 @@ module ReVIEW
615
627
  def image_image(id, caption, metric)
616
628
  metrics = parse_metric('html', metric)
617
629
  puts %Q(<div id="#{normalize_id(id)}" class="image">)
630
+ image_header(id, caption) if caption_top?('image')
618
631
  puts %Q(<img src="#{@chapter.image(id).path.sub(%r{\A\./}, '')}" alt="#{escape(compile_inline(caption))}"#{metrics} />)
619
- image_header id, caption
632
+ image_header(id, caption) unless caption_top?('image')
620
633
  puts '</div>'
621
634
  end
622
635
 
623
636
  def image_dummy(id, caption, lines)
624
637
  warn "image not bound: #{id}"
625
638
  puts %Q(<div id="#{normalize_id(id)}" class="image">)
639
+ image_header(id, caption) if caption_top?('image')
626
640
  puts %Q(<pre class="dummyimage">)
627
641
  lines.each do |line|
628
642
  puts detab(line)
629
643
  end
630
644
  puts '</pre>'
631
- image_header id, caption
645
+ image_header(id, caption) unless caption_top?('image')
632
646
  puts '</div>'
633
647
  end
634
648
 
@@ -643,47 +657,12 @@ module ReVIEW
643
657
  end
644
658
 
645
659
  def table(lines, id = nil, caption = nil)
646
- rows = []
647
- sepidx = nil
648
- lines.each_with_index do |line, idx|
649
- if /\A[\=\-]{12}/ =~ line
650
- # just ignore
651
- # error "too many table separator" if sepidx
652
- sepidx ||= idx
653
- next
654
- end
655
- rows.push(line.strip.split(/\t+/).map { |s| s.sub(/\A\./, '') })
656
- end
657
- rows = adjust_n_cols(rows)
658
-
659
660
  if id
660
661
  puts %Q(<div id="#{normalize_id(id)}" class="table">)
661
662
  else
662
663
  puts %Q(<div class="table">)
663
664
  end
664
- begin
665
- if caption.present?
666
- table_header id, caption
667
- end
668
- rescue KeyError
669
- error "no such table: #{id}"
670
- end
671
- table_begin rows.first.size
672
- return if rows.empty?
673
- if sepidx
674
- sepidx.times do
675
- tr(rows.shift.map { |s| th(s) })
676
- end
677
- rows.each do |cols|
678
- tr(cols.map { |s| td(s) })
679
- end
680
- else
681
- rows.each do |cols|
682
- h, *cs = *cols
683
- tr([th(h)] + cs.map { |s| td(s) })
684
- end
685
- end
686
- table_end
665
+ super(lines, id, caption)
687
666
  puts '</div>'
688
667
  end
689
668
 
@@ -718,23 +697,27 @@ module ReVIEW
718
697
  end
719
698
 
720
699
  def imgtable(lines, id, caption = nil, metric = nil)
721
- unless @chapter.image(id).bound?
700
+ unless @chapter.image_bound?(id)
722
701
  warn "image not bound: #{id}"
723
- image_dummy id, caption, lines
702
+ image_dummy(id, caption, lines)
724
703
  return
725
704
  end
726
705
 
727
706
  puts %Q(<div id="#{normalize_id(id)}" class="imgtable image">)
728
707
  begin
729
- if caption.present?
730
- table_header id, caption
708
+ if caption_top?('table') && caption.present?
709
+ table_header(id, caption)
710
+ end
711
+
712
+ imgtable_image(id, caption, metric)
713
+
714
+ if !caption_top?('table') && caption.present?
715
+ table_header(id, caption)
731
716
  end
732
717
  rescue KeyError
733
718
  error "no such table: #{id}"
734
719
  end
735
720
 
736
- imgtable_image(id, caption, metric)
737
-
738
721
  puts '</div>'
739
722
  end
740
723
 
@@ -748,16 +731,21 @@ module ReVIEW
748
731
  end
749
732
 
750
733
  def comment(lines, comment = nil)
751
- lines ||= []
752
- lines.unshift comment unless comment.blank?
753
734
  return unless @book.config['draft']
735
+ lines ||= []
736
+ lines.unshift(escape(comment)) unless comment.blank?
754
737
  str = lines.join('<br />')
755
- puts %Q(<div class="draft-comment">#{escape(str)}</div>)
738
+ puts %Q(<div class="draft-comment">#{str}</div>)
756
739
  end
757
740
 
758
741
  def footnote(id, str)
759
742
  if @book.config['epubversion'].to_i == 3
760
- puts %Q(<div class="footnote" epub:type="footnote" id="fn-#{normalize_id(id)}"><p class="footnote">[*#{@chapter.footnote(id).number}] #{compile_inline(str)}</p></div>)
743
+ back = ''
744
+ if @book.config['epubmaker'] && @book.config['epubmaker']['back_footnote']
745
+ back = %Q(<a href="#fnb-#{normalize_id(id)}">#{I18n.t('html_footnote_backmark')}</a>)
746
+ end
747
+ # XXX: back link must be located at first of p for Kindle.
748
+ puts %Q(<div class="footnote" epub:type="footnote" id="fn-#{normalize_id(id)}"><p class="footnote">#{back}#{I18n.t('html_footnote_textmark', @chapter.footnote(id).number)}#{compile_inline(str)}</p></div>)
761
749
  else
762
750
  puts %Q(<div class="footnote" id="fn-#{normalize_id(id)}"><p class="footnote">[<a href="#fnb-#{normalize_id(id)}">*#{@chapter.footnote(id).number}</a>] #{compile_inline(str)}</p></div>)
763
751
  end
@@ -766,7 +754,19 @@ module ReVIEW
766
754
  def indepimage(lines, id, caption = '', metric = nil)
767
755
  metrics = parse_metric('html', metric)
768
756
  caption = '' unless caption.present?
757
+ caption_str = nil
758
+ if caption.present?
759
+ caption_str = <<-EOS
760
+ <p class="caption">
761
+ #{I18n.t('numberless_image')}#{I18n.t('caption_prefix')}#{compile_inline(caption)}
762
+ </p>
763
+ EOS
764
+ end
765
+
769
766
  puts %Q(<div id="#{normalize_id(id)}" class="image">)
767
+ if caption_top?('image') && caption.present?
768
+ puts caption_str
769
+ end
770
770
  begin
771
771
  puts %Q(<img src="#{@chapter.image(id).path.sub(%r{\A\./}, '')}" alt="#{escape(compile_inline(caption))}"#{metrics} />)
772
772
  rescue
@@ -780,10 +780,8 @@ module ReVIEW
780
780
  end
781
781
  end
782
782
 
783
- if caption.present?
784
- puts %Q(<p class="caption">)
785
- puts %Q(#{I18n.t('numberless_image')}#{I18n.t('caption_prefix')}#{compile_inline(caption)})
786
- puts '</p>'
783
+ if !caption_top?('image') && caption.present?
784
+ puts caption_str
787
785
  end
788
786
  puts '</div>'
789
787
  end
@@ -862,7 +860,7 @@ module ReVIEW
862
860
 
863
861
  def inline_fn(id)
864
862
  if @book.config['epubversion'].to_i == 3
865
- %Q(<a id="fnb-#{normalize_id(id)}" href="#fn-#{normalize_id(id)}" class="noteref" epub:type="noteref">*#{@chapter.footnote(id).number}</a>)
863
+ %Q(<a id="fnb-#{normalize_id(id)}" href="#fn-#{normalize_id(id)}" class="noteref" epub:type="noteref">#{I18n.t('html_footnote_refmark', @chapter.footnote(id).number)}</a>)
866
864
  else
867
865
  %Q(<a id="fnb-#{normalize_id(id)}" href="#fn-#{normalize_id(id)}" class="noteref">*#{@chapter.footnote(id).number}</a>)
868
866
  end
@@ -973,8 +971,8 @@ module ReVIEW
973
971
 
974
972
  def bibpaper(lines, id, caption)
975
973
  puts %Q(<div class="bibpaper">)
976
- bibpaper_header id, caption
977
- bibpaper_bibpaper id, caption, lines unless lines.empty?
974
+ bibpaper_header(id, caption)
975
+ bibpaper_bibpaper(id, caption, lines) unless lines.empty?
978
976
  puts '</div>'
979
977
  end
980
978
 
@@ -997,7 +995,7 @@ module ReVIEW
997
995
 
998
996
  def inline_hd_chap(chap, id)
999
997
  n = chap.headline_index.number(id)
1000
- if chap.number and @book.config['secnolevel'] >= n.split('.').size
998
+ if n.present? && chap.number && over_secnolevel?(n)
1001
999
  str = I18n.t('hd_quote', [n, compile_inline(chap.headline(id).caption)])
1002
1000
  else
1003
1001
  str = I18n.t('hd_quote_without_number', compile_inline(chap.headline(id).caption))
@@ -1214,17 +1212,6 @@ module ReVIEW
1214
1212
  @ol_num = num.to_i
1215
1213
  end
1216
1214
 
1217
- def defer_math_image(str, path, key)
1218
- # for Re:VIEW >3
1219
- File.open(File.join(File.dirname(path), '__IMGMATH_BODY__.tex'), 'a+') do |f|
1220
- f.puts str
1221
- f.puts '\\clearpage'
1222
- end
1223
- File.open(File.join(File.dirname(path), '__IMGMATH_BODY__.map'), 'a+') do |f|
1224
- f.puts key
1225
- end
1226
- end
1227
-
1228
1215
  def make_math_image(str, path, fontsize = 12)
1229
1216
  # Re:VIEW 2 compatibility
1230
1217
  fontsize2 = (fontsize * 1.2).round.to_i