review 5.2.0 → 5.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby-tex.yml +1 -1
  3. data/.github/workflows/ruby-win.yml +1 -1
  4. data/.github/workflows/ruby.yml +1 -1
  5. data/.rubocop.yml +1 -319
  6. data/NEWS.ja.md +116 -0
  7. data/NEWS.md +117 -0
  8. data/README.md +9 -8
  9. data/bin/review +1 -1
  10. data/bin/review-catalog-converter +15 -15
  11. data/bin/review-check +7 -7
  12. data/bin/review-compile +6 -8
  13. data/bin/review-index +1 -1
  14. data/bin/review-preproc +1 -1
  15. data/bin/review-validate +2 -2
  16. data/doc/config.yml.sample +7 -1
  17. data/doc/config.yml.sample-simple +1 -1
  18. data/doc/format.ja.md +34 -4
  19. data/doc/format.md +32 -3
  20. data/lib/review/book/base.rb +3 -3
  21. data/lib/review/book/book_unit.rb +13 -3
  22. data/lib/review/book/chapter.rb +1 -1
  23. data/lib/review/book/index.rb +7 -4
  24. data/lib/review/book/part.rb +12 -13
  25. data/lib/review/book/volume.rb +1 -1
  26. data/lib/review/builder.rb +82 -28
  27. data/lib/review/catalog.rb +6 -5
  28. data/lib/review/compiler.rb +20 -14
  29. data/lib/review/configure.rb +5 -2
  30. data/lib/review/epub2html.rb +12 -12
  31. data/lib/review/epubmaker/content.rb +1 -1
  32. data/lib/review/epubmaker/epubcommon.rb +47 -45
  33. data/lib/review/epubmaker/epubv2.rb +2 -1
  34. data/lib/review/epubmaker/epubv3.rb +5 -4
  35. data/lib/review/epubmaker/producer.rb +3 -3
  36. data/lib/review/epubmaker/reviewheaderlistener.rb +1 -1
  37. data/lib/review/epubmaker.rb +35 -32
  38. data/lib/review/extentions/string.rb +1 -1
  39. data/lib/review/htmlbuilder.rb +65 -15
  40. data/lib/review/htmlutils.rb +17 -17
  41. data/lib/review/i18n.rb +3 -3
  42. data/lib/review/i18n.yml +6 -0
  43. data/lib/review/idgxmlbuilder.rb +42 -21
  44. data/lib/review/idgxmlmaker.rb +15 -13
  45. data/lib/review/img_math.rb +1 -0
  46. data/lib/review/index_builder.rb +100 -38
  47. data/lib/review/init.rb +4 -4
  48. data/lib/review/latexbuilder.rb +69 -34
  49. data/lib/review/lineinput.rb +3 -3
  50. data/lib/review/location.rb +1 -1
  51. data/lib/review/logger.rb +21 -21
  52. data/lib/review/makerhelper.rb +3 -3
  53. data/lib/review/markdownbuilder.rb +16 -8
  54. data/lib/review/pdfmaker.rb +40 -21
  55. data/lib/review/plaintextbuilder.rb +8 -7
  56. data/lib/review/preprocessor/repository.rb +1 -1
  57. data/lib/review/preprocessor.rb +5 -5
  58. data/lib/review/rstbuilder.rb +11 -2
  59. data/lib/review/textmaker.rb +20 -18
  60. data/lib/review/textutils.rb +5 -6
  61. data/lib/review/tocprinter.rb +11 -6
  62. data/lib/review/topbuilder.rb +89 -12
  63. data/lib/review/update.rb +16 -8
  64. data/lib/review/version.rb +1 -1
  65. data/lib/review/volumeprinter.rb +9 -9
  66. data/lib/review/webmaker.rb +32 -32
  67. data/lib/review/webtocprinter.rb +10 -10
  68. data/lib/review/yamlloader.rb +36 -2
  69. data/review.gemspec +2 -0
  70. data/samples/sample-book/src/config.yml +0 -1
  71. data/samples/syntax-book/ch02.re +16 -1
  72. data/templates/html/_titlepage.html.erb +9 -17
  73. data/templates/latex/config.erb +3 -0
  74. data/templates/latex/review-jlreq/review-base.sty +2 -1
  75. data/templates/latex/review-jlreq/review-jlreq.cls +36 -3
  76. data/templates/latex/review-jsbook/review-base.sty +7 -1
  77. data/templates/latex/review-jsbook/review-jsbook.cls +31 -4
  78. data/templates/opf/opf_manifest_epubv2.opf.erb +1 -1
  79. data/templates/opf/opf_manifest_epubv3.opf.erb +1 -1
  80. data/test/assets/syntax_book_index_detail.txt +10 -8
  81. data/test/assets/test_template.tex +4 -1
  82. data/test/assets/test_template_backmatter.tex +4 -1
  83. data/test/book_test_helper.rb +10 -10
  84. data/test/test_book_chapter.rb +25 -2
  85. data/test/test_builder.rb +5 -3
  86. data/test/test_epub3maker.rb +3 -3
  87. data/test/test_epubmaker.rb +14 -29
  88. data/test/test_epubmaker_cmd.rb +2 -2
  89. data/test/test_htmlbuilder.rb +80 -8
  90. data/test/test_idgxmlbuilder.rb +13 -13
  91. data/test/test_idgxmlmaker_cmd.rb +1 -1
  92. data/test/test_img_math.rb +11 -2
  93. data/test/test_index.rb +30 -4
  94. data/test/test_latexbuilder.rb +53 -6
  95. data/test/test_markdownbuilder.rb +45 -0
  96. data/test/test_pdfmaker.rb +19 -0
  97. data/test/test_pdfmaker_cmd.rb +10 -10
  98. data/test/test_plaintextbuilder.rb +45 -4
  99. data/test/test_rstbuilder.rb +13 -0
  100. data/test/test_textmaker_cmd.rb +1 -1
  101. data/test/test_topbuilder.rb +169 -11
  102. data/test/test_yamlloader.rb +28 -42
  103. metadata +19 -4
@@ -1,6 +1,6 @@
1
1
  # = epubv3.rb -- EPUB version 3 producer.
2
2
  #
3
- # Copyright (c) 2010-2017 Kenshi Muto
3
+ # Copyright (c) 2010-2022 Kenshi Muto
4
4
  #
5
5
  # This program is free software.
6
6
  # You can distribute or modify this program under the terms of
@@ -186,7 +186,7 @@ module ReVIEW
186
186
  @tocx_contents = []
187
187
  toc = nil
188
188
  contents.each do |item|
189
- next if item.media !~ /xhtml\+xml/ # skip non XHTML
189
+ next unless /xhtml\+xml/.match?(item.media) # skip non XHTML
190
190
 
191
191
  @tocx_contents << item
192
192
  end
@@ -210,19 +210,20 @@ module ReVIEW
210
210
  @title = h(ReVIEW::I18n.t('toctitle'))
211
211
  @language = config['language']
212
212
  @stylesheets = config['stylesheet']
213
- ReVIEW::Template.generate(path: './html/layout-html5.html.erb', binding: binding)
213
+ ReVIEW::Template.generate(path: template_name, binding: binding)
214
214
  end
215
215
 
216
216
  # Produce EPUB file +epubfile+.
217
217
  # +work_dir+ points the directory has contents.
218
218
  # +tmpdir+ defines temporary directory.
219
219
  def produce(epubfile, work_dir, tmpdir, base_dir:)
220
+ @workdir = base_dir
220
221
  produce_write_common(work_dir, tmpdir)
221
222
 
222
223
  toc_file = "#{tmpdir}/OEBPS/#{config['bookname']}-toc.#{config['htmlext']}"
223
224
  File.write(toc_file, ncx(config['epubmaker']['ncxindent']))
224
225
 
225
- call_hook('hook_prepack', tmpdir, base_dir: base_dir)
226
+ call_hook('hook_prepack', tmpdir, base_dir: @workdir)
226
227
  expoter = ReVIEW::EPUBMaker::ZipExporter.new(tmpdir, config)
227
228
  expoter.export_zip(epubfile)
228
229
  end
@@ -89,8 +89,8 @@ module ReVIEW
89
89
  Dir.foreach(path) do |f|
90
90
  next if f.start_with?('.')
91
91
 
92
- if f =~ /\.(#{allow_exts.join('|')})\Z/i
93
- path.chop! if path =~ %r{/\Z}
92
+ if /\.(#{allow_exts.join('|')})\Z/i.match?(f)
93
+ path.chop! if %r{/\Z}.match?(path)
94
94
  if base.nil?
95
95
  @contents.push(ReVIEW::EPUBMaker::Content.new(file: "#{path}/#{f}"))
96
96
  else
@@ -115,7 +115,7 @@ module ReVIEW
115
115
 
116
116
  # use Dir to solve a path for Windows (see #1011)
117
117
  new_tmpdir = Dir[File.join(tmpdir.nil? ? Dir.mktmpdir : tmpdir)][0]
118
- if epubfile !~ %r{\A/}
118
+ unless epubfile.start_with?('/')
119
119
  epubfile = "#{current}/#{epubfile}"
120
120
  end
121
121
 
@@ -41,7 +41,7 @@ module ReVIEW
41
41
  end
42
42
 
43
43
  def tag_end(name)
44
- if name =~ /\Ah\d+/
44
+ if /\Ah\d+/.match?(name)
45
45
  if @id.present?
46
46
  @headlines.push({ 'level' => @level,
47
47
  'id' => @id,
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2010-2021 Kenshi Muto and Masayoshi Takahashi
1
+ # Copyright (c) 2010-2022 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
@@ -75,9 +75,13 @@ module ReVIEW
75
75
  cmd_config, yamlfile, exportfile = parse_opts(args)
76
76
  error! "#{yamlfile} not found." unless File.exist?(yamlfile)
77
77
 
78
- @config = ReVIEW::Configure.create(maker: 'epubmaker',
79
- yamlfile: yamlfile,
80
- config: cmd_config)
78
+ begin
79
+ @config = ReVIEW::Configure.create(maker: 'epubmaker',
80
+ yamlfile: yamlfile,
81
+ config: cmd_config)
82
+ rescue ReVIEW::ConfigError => e
83
+ error! e.message
84
+ end
81
85
  @producer = ReVIEW::EPUBMaker::Producer.new(@config)
82
86
  update_log_level
83
87
  debug("Loaded yaml file (#{yamlfile}).")
@@ -118,7 +122,9 @@ module ReVIEW
118
122
  booktmpname = "#{bookname}-epub"
119
123
 
120
124
  @img_math = ReVIEW::ImgMath.new(@config)
121
- unless @config.check_version(ReVIEW::VERSION, exception: false)
125
+ begin
126
+ @config.check_version(ReVIEW::VERSION, exception: true)
127
+ rescue ReVIEW::ConfigError => e
122
128
  warn e.message
123
129
  end
124
130
  debug("#{bookname}.epub will be created.")
@@ -225,7 +231,7 @@ module ReVIEW
225
231
  if @config['epubmaker']['verify_target_images'].present?
226
232
  @config['epubmaker']['force_include_images'].each do |file|
227
233
  unless File.exist?(file)
228
- if file !~ /\Ahttps?:/
234
+ unless /\Ahttps?:/.match?(file)
229
235
  warn "#{file} is not found, skip."
230
236
  end
231
237
  next
@@ -255,7 +261,7 @@ module ReVIEW
255
261
 
256
262
  if FileTest.directory?(File.join(resdir, fname))
257
263
  recursive_copy_files(File.join(resdir, fname), File.join(destdir, fname), allow_exts)
258
- elsif fname =~ /\.(#{allow_exts.join('|')})\Z/i
264
+ elsif /\.(#{allow_exts.join('|')})\Z/i.match?(fname)
259
265
  FileUtils.mkdir_p(destdir)
260
266
  debug("Copy #{resdir}/#{fname} to the temporary directory.")
261
267
  FileUtils.cp(File.join(resdir, fname), destdir, preserve: true)
@@ -313,15 +319,25 @@ module ReVIEW
313
319
  File.open(File.join(basetmpdir, htmlfile), 'w') do |f|
314
320
  @part_number = part.number
315
321
  @part_title = part.name.strip
316
- @body = ReVIEW::Template.generate(path: 'html/_part_body.html.erb', binding: binding)
317
-
322
+ @body = ReVIEW::Template.generate(path: template_name(localfile: '_part_body.html.erb', systemfile: 'html/_part_body.html.erb'), binding: binding)
318
323
  @language = @producer.config['language']
319
324
  @stylesheets = @producer.config['stylesheet']
320
325
  f.write ReVIEW::Template.generate(path: template_name, binding: binding)
321
326
  end
322
327
  end
323
328
 
324
- def template_name
329
+ def template_name(localfile: 'layout.html.erb', systemfile: nil)
330
+ if @basedir
331
+ layoutfile = File.join(@basedir, 'layouts', localfile)
332
+ if File.exist?(layoutfile)
333
+ return layoutfile
334
+ end
335
+ end
336
+
337
+ if systemfile
338
+ return systemfile
339
+ end
340
+
325
341
  if @producer.config['htmlversion'].to_i == 5
326
342
  './html/layout-html5.html.erb'
327
343
  else
@@ -374,7 +390,7 @@ module ReVIEW
374
390
 
375
391
  if @config['params'].present?
376
392
  warn %Q('params:' in config.yml is obsoleted.)
377
- if @config['params'] =~ /stylesheet=/
393
+ if /stylesheet=/.match?(@config['params'])
378
394
  warn %Q(stylesheets should be defined in 'stylesheet:', not in 'params:')
379
395
  end
380
396
  end
@@ -382,7 +398,7 @@ module ReVIEW
382
398
  @converter.convert(filename, File.join(basetmpdir, htmlfile))
383
399
  write_info_body(basetmpdir, id, htmlfile, ispart, chaptype)
384
400
  remove_hidden_title(basetmpdir, htmlfile)
385
- rescue => e
401
+ rescue StandardError => e
386
402
  @compile_errors = true
387
403
  error "compile error in #{filename} (#{e.class})"
388
404
  error e.message
@@ -434,11 +450,11 @@ module ReVIEW
434
450
  end
435
451
 
436
452
  properties = detect_properties(path)
437
- if properties.present?
438
- prop_str = ',properties=' + properties.join(' ')
439
- else
440
- prop_str = ''
441
- end
453
+ prop_str = if properties.present?
454
+ ',properties=' + properties.join(' ')
455
+ else
456
+ ''
457
+ end
442
458
  first = true
443
459
  headlines.each do |headline|
444
460
  if ispart.present? && headline['level'] == 1
@@ -543,25 +559,12 @@ module ReVIEW
543
559
  end
544
560
 
545
561
  def build_titlepage(basetmpdir, htmlfile)
546
- # TODO: should be created via epubcommon
547
562
  @title = h(@config.name_of('booktitle'))
548
563
  File.open(File.join(basetmpdir, htmlfile), 'w') do |f|
549
- @body = ''
550
- @body << %Q(<div class="titlepage">\n)
551
- @body << %Q(<h1 class="tp-title">#{h(@config.name_of('booktitle'))}</h1>\n)
552
- if @config['subtitle']
553
- @body << %Q(<h2 class="tp-subtitle">#{h(@config.name_of('subtitle'))}</h2>\n)
554
- end
555
- if @config['aut']
556
- @body << %Q(<h2 class="tp-author">#{h(@config.names_of('aut').join(ReVIEW::I18n.t('names_splitter')))}</h2>\n)
557
- end
558
- if @config['pbl']
559
- @body << %Q(<h3 class="tp-publisher">#{h(@config.names_of('pbl').join(ReVIEW::I18n.t('names_splitter')))}</h3>\n)
560
- end
561
- @body << '</div>'
562
-
564
+ @body = ReVIEW::Template.generate(path: template_name(localfile: '_titlepage.html.erb', systemfile: 'html/_titlepage.html.erb'), binding: binding)
563
565
  @language = @producer.config['language']
564
566
  @stylesheets = @producer.config['stylesheet']
567
+
565
568
  f.write ReVIEW::Template.generate(path: template_name, binding: binding)
566
569
  end
567
570
  end
@@ -1,4 +1,4 @@
1
- if defined?(Encoding) && Encoding.respond_to?('default_external') &&
1
+ 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
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2008-2021 Minero Aoki, Kenshi Muto, Masayoshi Takahashi,
1
+ # Copyright (c) 2008-2022 Minero Aoki, Kenshi Muto, Masayoshi Takahashi,
2
2
  # KADO Masanori
3
3
  # 2002-2007 Minero Aoki
4
4
  #
@@ -67,11 +67,11 @@ module ReVIEW
67
67
  htmldir = 'html'
68
68
  localfilename = 'layout.html.erb'
69
69
  end
70
- if @book.htmlversion == 5
71
- htmlfilename = File.join(htmldir, 'layout-html5.html.erb')
72
- else
73
- htmlfilename = File.join(htmldir, 'layout-xhtml1.html.erb')
74
- end
70
+ htmlfilename = if @book.htmlversion == 5
71
+ File.join(htmldir, 'layout-html5.html.erb')
72
+ else
73
+ File.join(htmldir, 'layout-xhtml1.html.erb')
74
+ end
75
75
 
76
76
  layout_file = File.join(@book.basedir, 'layouts', localfilename)
77
77
  if !File.exist?(layout_file) && File.exist?(File.join(@book.basedir, 'layouts', 'layout.erb'))
@@ -111,6 +111,8 @@ module ReVIEW
111
111
  end
112
112
 
113
113
  def result
114
+ check_printendnotes
115
+
114
116
  # flush all `</section>`
115
117
  if use_section?
116
118
  print close_sections
@@ -825,6 +827,22 @@ module ReVIEW
825
827
  end
826
828
  end
827
829
 
830
+ def endnote_begin
831
+ puts %Q(<div class="endnotes">)
832
+ end
833
+
834
+ def endnote_end
835
+ puts %Q(</div>)
836
+ end
837
+
838
+ def endnote_item(id)
839
+ back = ''
840
+ if @book.config['epubmaker'] && @book.config['epubmaker']['back_footnote']
841
+ back = %Q(<a href="#endnoteb-#{normalize_id(id)}">#{I18n.t('html_footnote_backmark')}</a>)
842
+ end
843
+ puts %Q(<div class="endnote" id="endnote-#{normalize_id(id)}"><p class="endnote">#{back}#{I18n.t('html_endnote_textmark', @chapter.endnote(id).number)}#{compile_inline(@chapter.endnote(id).content)}</p></div>)
844
+ end
845
+
828
846
  def indepimage(lines, id, caption = '', metric = nil)
829
847
  metrics = parse_metric('html', metric)
830
848
  caption = '' unless caption.present?
@@ -843,7 +861,7 @@ EOS
843
861
  end
844
862
  begin
845
863
  puts %Q(<img src="#{@chapter.image(id).path.sub(%r{\A\./}, '')}" alt="#{escape(compile_inline(caption))}"#{metrics} />)
846
- rescue
864
+ rescue StandardError
847
865
  warn "image not bound: #{id}", location: location
848
866
  if lines
849
867
  puts %Q(<pre class="dummyimage">)
@@ -942,6 +960,12 @@ EOS
942
960
  app_error "unknown footnote: #{id}"
943
961
  end
944
962
 
963
+ def inline_endnote(id)
964
+ %Q(<a id="endnoteb-#{normalize_id(id)}" href="#endnote-#{normalize_id(id)}" class="noteref" epub:type="noteref">#{I18n.t('html_endnote_refmark', @chapter.endnote(id).number)}</a>)
965
+ rescue KeyError
966
+ app_error "unknown endnote: #{id}"
967
+ end
968
+
945
969
  def compile_ruby(base, ruby)
946
970
  if @book.htmlversion == 5
947
971
  %Q(<ruby>#{escape(base)}<rp>#{I18n.t('ruby_prefix')}</rp><rt>#{escape(ruby)}</rt><rp>#{I18n.t('ruby_postfix')}</rp></ruby>)
@@ -953,8 +977,9 @@ EOS
953
977
  def compile_kw(word, alt)
954
978
  %Q(<b class="kw">) +
955
979
  if alt
956
- then escape(word + " (#{alt.strip})")
957
- else escape(word)
980
+ escape(word + " (#{alt.strip})")
981
+ else
982
+ escape(word)
958
983
  end +
959
984
  "</b><!-- IDX:#{escape_comment(escape(word))} -->"
960
985
  end
@@ -1072,11 +1097,11 @@ EOS
1072
1097
 
1073
1098
  def inline_hd_chap(chap, id)
1074
1099
  n = chap.headline_index.number(id)
1075
- if n.present? && chap.number && over_secnolevel?(n)
1076
- str = I18n.t('hd_quote', [n, compile_inline(chap.headline(id).caption)])
1077
- else
1078
- str = I18n.t('hd_quote_without_number', compile_inline(chap.headline(id).caption))
1079
- end
1100
+ str = if n.present? && chap.number && over_secnolevel?(n)
1101
+ I18n.t('hd_quote', [n, compile_inline(chap.headline(id).caption)])
1102
+ else
1103
+ I18n.t('hd_quote_without_number', compile_inline(chap.headline(id).caption))
1104
+ end
1080
1105
  if @book.config['chapterlink']
1081
1106
  anchor = 'h' + n.tr('.', '-')
1082
1107
  %Q(<a href="#{chap.id}#{extname}##{anchor}">#{str}</a>)
@@ -1087,6 +1112,31 @@ EOS
1087
1112
  app_error "unknown headline: #{id}"
1088
1113
  end
1089
1114
 
1115
+ def inline_sec(id)
1116
+ if @book.config['chapterlink']
1117
+ chap, id2 = extract_chapter_id(id)
1118
+ n = chap.headline_index.number(id2)
1119
+ anchor = 'h' + n.tr('.', '-')
1120
+ %Q(<a href="#{chap.id}#{extname}##{anchor}">#{super(id)}</a>)
1121
+ else
1122
+ super(id)
1123
+ end
1124
+ rescue KeyError
1125
+ app_error "unknown headline: #{id}"
1126
+ end
1127
+
1128
+ def inline_sectitle(id)
1129
+ if @book.config['chapterlink']
1130
+ chap, id2 = extract_chapter_id(id)
1131
+ anchor = 'h' + chap.headline_index.number(id2).tr('.', '-')
1132
+ %Q(<a href="#{chap.id}#{extname}##{anchor}">#{super(id)}</a>)
1133
+ else
1134
+ super(id)
1135
+ end
1136
+ rescue KeyError
1137
+ app_error "unknown headline: #{id}"
1138
+ end
1139
+
1090
1140
  def column_label(id, chapter = @chapter)
1091
1141
  num = chapter.column(id).number
1092
1142
  "column-#{num}"
@@ -1226,7 +1276,7 @@ EOS
1226
1276
  def inline_icon(id)
1227
1277
  begin
1228
1278
  %Q(<img src="#{@chapter.image(id).path.sub(%r{\A\./}, '')}" alt="[#{id}]" />)
1229
- rescue
1279
+ rescue StandardError
1230
1280
  warn "image not bound: #{id}", location: location
1231
1281
  %Q(<pre>missing image: #{id}</pre>)
1232
1282
  end
@@ -9,7 +9,7 @@
9
9
 
10
10
  begin
11
11
  require 'cgi/escape'
12
- rescue
12
+ rescue StandardError
13
13
  require 'cgi/util'
14
14
  end
15
15
 
@@ -60,13 +60,13 @@ module ReVIEW
60
60
  def highlight_pygments(ops)
61
61
  body = ops[:body] || ''
62
62
  format = ops[:format] || ''
63
- if ops[:lexer].present?
64
- lexer = ops[:lexer]
65
- elsif @book.config['highlight'] && @book.config['highlight']['lang']
66
- lexer = @book.config['highlight']['lang'] # default setting
67
- else
68
- lexer = 'text'
69
- end
63
+ lexer = if ops[:lexer].present?
64
+ ops[:lexer]
65
+ elsif @book.config['highlight'] && @book.config['highlight']['lang']
66
+ @book.config['highlight']['lang'] # default setting
67
+ else
68
+ 'text'
69
+ end
70
70
  options = { nowrap: true, noclasses: true }
71
71
  if ops[:linenum]
72
72
  options[:nowrap] = false
@@ -93,13 +93,13 @@ module ReVIEW
93
93
 
94
94
  def highlight_rouge(ops)
95
95
  body = ops[:body] || ''
96
- if ops[:lexer].present?
97
- lexer = ops[:lexer]
98
- elsif @book.config['highlight'] && @book.config['highlight']['lang']
99
- lexer = @book.config['highlight']['lang'] # default setting
100
- else
101
- lexer = 'text'
102
- end
96
+ lexer = if ops[:lexer].present?
97
+ ops[:lexer]
98
+ elsif @book.config['highlight'] && @book.config['highlight']['lang']
99
+ @book.config['highlight']['lang'] # default setting
100
+ else
101
+ 'text'
102
+ end
103
103
  # format = ops[:format] || ''
104
104
 
105
105
  first_line_num = 1 ## default
@@ -129,9 +129,9 @@ module ReVIEW
129
129
  end
130
130
 
131
131
  def normalize_id(id)
132
- if id =~ /\A[a-z][a-z0-9_.-]*\Z/i
132
+ if /\A[a-z][a-z0-9_.-]*\Z/i.match?(id)
133
133
  id
134
- elsif id =~ /\A[0-9_.-][a-z0-9_.-]*\Z/i
134
+ elsif /\A[0-9_.-][a-z0-9_.-]*\Z/i.match?(id)
135
135
  "id_#{id}" # dummy prefix
136
136
  else
137
137
  "id_#{CGI.escape(id.gsub('_', '__')).tr('%', '_').tr('+', '-')}" # escape all
data/lib/review/i18n.rb CHANGED
@@ -75,11 +75,11 @@ module ReVIEW
75
75
  end
76
76
 
77
77
  def load_file(path)
78
- @store = YAML.load_file(path)
78
+ @store = YAMLLoader.safe_load_file(path)
79
79
  end
80
80
 
81
81
  def update_localefile(path)
82
- user_i18n = YAML.load_file(path)
82
+ user_i18n = YAMLLoader.safe_load_file(path)
83
83
  locale = user_i18n['locale']
84
84
  if locale
85
85
  user_i18n.delete('locale')
@@ -171,7 +171,7 @@ module ReVIEW
171
171
  args_matched = (frmt.count('%') <= args.size)
172
172
  frmt.gsub!('##', '%%')
173
173
  args_matched ? (frmt % args) : frmt
174
- rescue
174
+ rescue StandardError
175
175
  str
176
176
  end
177
177
  end
data/lib/review/i18n.yml CHANGED
@@ -32,6 +32,8 @@ ja:
32
32
  html_footnote_refmark: "*%s"
33
33
  html_footnote_textmark: "[*%s] "
34
34
  html_footnote_backmark: "⏎"
35
+ html_endnote_refmark: "(%s)"
36
+ html_endnote_textmark: "(%s) "
35
37
  aut: "著 者"
36
38
  csl: "監 修"
37
39
  dsr: "デザイン"
@@ -120,6 +122,8 @@ en:
120
122
  html_footnote_refmark: "*%s"
121
123
  html_footnote_textmark: "[*%s] "
122
124
  html_footnote_backmark: "⏎"
125
+ html_endnote_refmark: "(%s)"
126
+ html_endnote_textmark: "(%s) "
123
127
  aut: "Author"
124
128
  csl: "Consultant"
125
129
  dsr: "Design"
@@ -188,6 +192,8 @@ zh-TW:
188
192
  html_footnote_refmark: "*%s"
189
193
  html_footnote_textmark: "[*%s] "
190
194
  html_footnote_backmark: "⏎"
195
+ html_endnote_refmark: "(%s)"
196
+ html_endnote_textmark: "(%s) "
191
197
  aut: "著作人"
192
198
  csl: "監 修"
193
199
  dsr: "美術編輯"
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2008-2020 Minero Aoki, Kenshi Muto
1
+ # Copyright (c) 2008-2022 Minero Aoki, Kenshi Muto
2
2
  # 2002-2007 Minero Aoki
3
3
  #
4
4
  # This program is free software.
@@ -78,13 +78,15 @@ module ReVIEW
78
78
  end
79
79
 
80
80
  def result
81
+ check_printendnotes
82
+
81
83
  s = ''
82
84
  if @secttags
83
85
  s += '</sect4>' if @subsubsubsection > 0
84
86
  s += '</sect3>' if @subsubsection > 0
85
87
  s += '</sect2>' if @subsection > 0
86
88
  s += '</sect>' if @section > 0
87
- s += '</chapter>' if @chapter.number > 0
89
+ s += '</chapter>'
88
90
  end
89
91
  solve_nest(@output.string) + s + "</#{@rootelement}>\n"
90
92
  end
@@ -455,11 +457,11 @@ module ReVIEW
455
457
  caption_str = nil
456
458
  if id
457
459
  puts '<equationblock>'
458
- if get_chap.nil?
459
- caption_str = %Q(<caption>#{I18n.t('equation')}#{I18n.t('format_number_without_chapter', [@chapter.equation(id).number])}#{I18n.t('caption_prefix_idgxml')}#{compile_inline(caption)}</caption>)
460
- else
461
- caption_str = %Q(<caption>#{I18n.t('equation')}#{I18n.t('format_number', [get_chap, @chapter.equation(id).number])}#{I18n.t('caption_prefix_idgxml')}#{compile_inline(caption)}</caption>)
462
- end
460
+ caption_str = if get_chap.nil?
461
+ %Q(<caption>#{I18n.t('equation')}#{I18n.t('format_number_without_chapter', [@chapter.equation(id).number])}#{I18n.t('caption_prefix_idgxml')}#{compile_inline(caption)}</caption>)
462
+ else
463
+ %Q(<caption>#{I18n.t('equation')}#{I18n.t('format_number', [get_chap, @chapter.equation(id).number])}#{I18n.t('caption_prefix_idgxml')}#{compile_inline(caption)}</caption>)
464
+ end
463
465
  puts caption_str if caption_top?('equation')
464
466
  end
465
467
 
@@ -478,7 +480,7 @@ module ReVIEW
478
480
  def table(lines, id = nil, caption = nil)
479
481
  @tablewidth = nil
480
482
  if @book.config['tableopt']
481
- @tablewidth = @book.config['tableopt'].split(',')[0].to_f / @book.config['pt_to_mm_unit'].to_f
483
+ @tablewidth = @book.config['tableopt'].split(',')[0].to_f / @book.config['pt_to_mm_unit'].to_f # rubocop:disable Style/FloatDivision
482
484
  end
483
485
  @col = 0
484
486
 
@@ -513,7 +515,7 @@ module ReVIEW
513
515
  sepidx = nil
514
516
  rows = []
515
517
  lines.each_with_index do |line, idx|
516
- if /\A[=\-]{12}/ =~ line
518
+ if /\A[=\-]{12}/.match?(line)
517
519
  sepidx ||= idx
518
520
  next
519
521
  end
@@ -538,7 +540,7 @@ module ReVIEW
538
540
  cellwidth = @tsize.split(/\s*,\s*/)
539
541
  totallength = 0
540
542
  cellwidth.size.times do |n|
541
- cellwidth[n] = cellwidth[n].to_f / @book.config['pt_to_mm_unit'].to_f
543
+ cellwidth[n] = cellwidth[n].to_f / @book.config['pt_to_mm_unit'].to_f # rubocop:disable Style/FloatDivision
542
544
  totallength += cellwidth[n]
543
545
  warn "total length exceeds limit for table: #{@table_id}", location: location if totallength > @tablewidth
544
546
  end
@@ -660,6 +662,24 @@ module ReVIEW
660
662
  app_error "unknown footnote: #{id}"
661
663
  end
662
664
 
665
+ def inline_endnote(id)
666
+ %Q(<span type='endnoteref' idref='endnoteb-#{normalize_id(id)}'>(#{@chapter.endnote(id).number})</span>)
667
+ rescue KeyError
668
+ app_error "unknown endnote: #{id}"
669
+ end
670
+
671
+ def endnote_begin
672
+ puts '<endnotes>'
673
+ end
674
+
675
+ def endnote_end
676
+ puts '</endnotes>'
677
+ end
678
+
679
+ def endnote_item(id)
680
+ puts %Q(<endnote id='endnoteb-#{normalize_id(id)}'><span type='endnotenumber'>(#{@chapter.endnote(id).number})</span>\t#{compile_inline(@chapter.endnote(id).content)}</endnote>)
681
+ end
682
+
663
683
  def compile_ruby(base, ruby)
664
684
  %Q(<GroupRuby><aid:ruby xmlns:aid="http://ns.adobe.com/AdobeInDesign/3.0/"><aid:rb>#{escape(base.strip)}</aid:rb><aid:rt>#{escape(ruby.strip)}</aid:rt></aid:ruby></GroupRuby>)
665
685
  end
@@ -667,8 +687,9 @@ module ReVIEW
667
687
  def compile_kw(word, alt)
668
688
  '<keyword>' +
669
689
  if alt
670
- then escape("#{word}(#{alt.strip})")
671
- else escape(word)
690
+ escape("#{word}(#{alt.strip})")
691
+ else
692
+ escape(word)
672
693
  end +
673
694
  '</keyword>' +
674
695
  %Q(<index value="#{escape(word)}" />) +
@@ -704,15 +725,15 @@ module ReVIEW
704
725
  end
705
726
 
706
727
  def inline_maru(str)
707
- if str =~ /\A\d+\Z/
728
+ if /\A\d+\Z/.match?(str)
708
729
  sprintf('&#x%x;', 9311 + str.to_i)
709
- elsif str =~ /\A[A-Z]\Z/
730
+ elsif /\A[A-Z]\Z/.match?(str)
710
731
  begin
711
732
  sprintf('&#x%x;', 9398 + str.codepoints.to_a[0] - 65)
712
733
  rescue NoMethodError
713
734
  sprintf('&#x%x;', 9398 + str[0] - 65)
714
735
  end
715
- elsif str =~ /\A[a-z]\Z/
736
+ elsif /\A[a-z]\Z/.match?(str)
716
737
  begin
717
738
  sprintf('&#x%x;', 9392 + str.codepoints.to_a[0] - 65)
718
739
  rescue NoMethodError
@@ -780,7 +801,7 @@ module ReVIEW
780
801
  def inline_icon(id)
781
802
  begin
782
803
  %Q(<Image href="file://#{@chapter.image(id).path.sub(%r{\A\./}, '')}" type="inline" />)
783
- rescue
804
+ rescue StandardError
784
805
  warn "image not bound: #{id}", location: location
785
806
  ''
786
807
  end
@@ -1122,16 +1143,16 @@ module ReVIEW
1122
1143
  def indepimage(_lines, id, caption = nil, metric = nil)
1123
1144
  metrics = parse_metric('idgxml', metric)
1124
1145
  puts '<img>'
1125
- if caption_top?('image')
1126
- puts %Q(<caption>#{compile_inline(caption)}</caption>) if caption.present?
1146
+ if caption_top?('image') && caption.present?
1147
+ puts %Q(<caption>#{compile_inline(caption)}</caption>)
1127
1148
  end
1128
1149
  begin
1129
1150
  puts %Q(<Image href="file://#{@chapter.image(id).path.sub(%r{\A\./}, '')}"#{metrics} />)
1130
- rescue
1151
+ rescue StandardError
1131
1152
  warn %Q(image not bound: #{id}), location: location
1132
1153
  end
1133
- unless caption_top?('image')
1134
- puts %Q(<caption>#{compile_inline(caption)}</caption>) if caption.present?
1154
+ if !caption_top?('image') && caption.present?
1155
+ puts %Q(<caption>#{compile_inline(caption)}</caption>)
1135
1156
  end
1136
1157
  puts '</img>'
1137
1158
  end