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
@@ -14,11 +14,11 @@ module ReVIEW
14
14
  def self.mkpart_from_namelistfile(book, path)
15
15
  chaps = []
16
16
  File.read(path, mode: 'rt:BOM|utf-8').split.each_with_index do |name, number|
17
- if path =~ /PREDEF/
18
- chaps << Chapter.mkchap(book, name)
19
- else
20
- chaps << Chapter.mkchap(book, name, number + 1)
21
- end
17
+ chaps << if /PREDEF/.match?(path)
18
+ Chapter.mkchap(book, name)
19
+ else
20
+ Chapter.mkchap(book, name, number + 1)
21
+ end
22
22
  end
23
23
  Part.mkpart(chaps)
24
24
  end
@@ -47,11 +47,11 @@ module ReVIEW
47
47
  else
48
48
  @content = ''
49
49
  end
50
- if file?
51
- @title = nil
52
- else
53
- @title = name
54
- end
50
+ @title = if file?
51
+ nil
52
+ else
53
+ name
54
+ end
55
55
  @volume = nil
56
56
 
57
57
  super()
@@ -78,11 +78,10 @@ module ReVIEW
78
78
 
79
79
  def volume
80
80
  if @number && file?
81
- vol = Volume.count_file(File.join(@book.config['contentdir'], @path))
81
+ Volume.count_file(File.join(@book.config['contentdir'], @path))
82
82
  else
83
- vol = Volume.new(0, 0, 0)
83
+ Volume.new(0, 0, 0)
84
84
  end
85
- vol
86
85
  end
87
86
 
88
87
  def file?
@@ -12,7 +12,7 @@ module ReVIEW
12
12
  def self.count_file(path)
13
13
  b = c = l = 0
14
14
  File.foreach(path) do |line|
15
- next if /\A\#@/ =~ line
15
+ next if /\A\#@/.match?(line)
16
16
 
17
17
  text = line.gsub(/\s+/, '')
18
18
  b += text.bytesize
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2002-2020 Minero Aoki, Kenshi Muto
1
+ # Copyright (c) 2002-2022 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
@@ -42,6 +42,7 @@ module ReVIEW
42
42
  @doc_status = {}
43
43
  @dictionary = {}
44
44
  @img_math = img_math
45
+ @shown_endnotes = true
45
46
  end
46
47
 
47
48
  def bind(compiler, chapter, location)
@@ -61,11 +62,11 @@ module ReVIEW
61
62
  if @book && @book.config
62
63
  @img_math ||= ReVIEW::ImgMath.new(@book.config)
63
64
  if words_file_path = @book.config['words_file']
64
- if words_file_path.is_a?(String)
65
- words_files = [words_file_path]
66
- else
67
- words_files = words_file_path
68
- end
65
+ words_files = if words_file_path.is_a?(String)
66
+ [words_file_path]
67
+ else
68
+ words_file_path
69
+ end
69
70
  words_files.each do |f|
70
71
  load_words(f)
71
72
  end
@@ -103,11 +104,18 @@ module ReVIEW
103
104
 
104
105
  def check_nest
105
106
  if @children && !@children.empty?
106
- app_error "//beginchild of #{@children.reverse.join(',')} misses //endchild"
107
+ app_error "#{@location}: //beginchild of #{@children.reverse.join(',')} misses //endchild"
108
+ end
109
+ end
110
+
111
+ def check_printendnotes
112
+ if @shown_endnotes.nil?
113
+ app_error "#{@location}: //endnote is found but //printendnotes is not found."
107
114
  end
108
115
  end
109
116
 
110
117
  def result
118
+ check_printendnotes
111
119
  solve_nest(@output.string)
112
120
  end
113
121
 
@@ -126,11 +134,9 @@ module ReVIEW
126
134
  end
127
135
 
128
136
  def load_words(file)
129
- if File.exist?(file)
130
- if file =~ /\.csv\Z/i
131
- CSV.foreach(file) do |row|
132
- @dictionary[row[0]] = row[1]
133
- end
137
+ if File.exist?(file) && /\.csv\Z/i.match?(file)
138
+ CSV.foreach(file) do |row|
139
+ @dictionary[row[0]] = row[1]
134
140
  end
135
141
  end
136
142
  end
@@ -273,17 +279,28 @@ module ReVIEW
273
279
  table(lines, nil, caption)
274
280
  end
275
281
 
276
- # def footnote(id, str)
277
- # @footnotes.push [id, str]
278
- # end
279
- #
280
- # def flush_footnote
281
- # footnote_begin
282
- # @footnotes.each do |id, str|
283
- # footnote_item(id, str)
284
- # end
285
- # footnote_end
286
- # end
282
+ def printendnotes
283
+ @shown_endnotes = true
284
+ endnote_begin
285
+ @chapter.endnotes.each do |en|
286
+ endnote_item(en.id)
287
+ end
288
+ endnote_end
289
+ end
290
+
291
+ def endnote(_id, _str)
292
+ @shown_endnotes = nil
293
+ end
294
+
295
+ def endnote_begin
296
+ end
297
+
298
+ def endnote_end
299
+ end
300
+
301
+ def endnote_item(id)
302
+ puts "(#{@chapter.endnote(id).number}) #{compile_inline(@chapter.endnote(id).content)}"
303
+ end
287
304
 
288
305
  def blankline
289
306
  puts ''
@@ -371,6 +388,12 @@ module ReVIEW
371
388
  app_error "unknown footnote: #{id}"
372
389
  end
373
390
 
391
+ def inline_endnote(id)
392
+ "(#{@chapter.endnote(id).number})"
393
+ rescue KeyError
394
+ app_error "unknown endnote: #{id}"
395
+ end
396
+
374
397
  def inline_bou(str)
375
398
  text(str)
376
399
  end
@@ -427,6 +450,27 @@ module ReVIEW
427
450
  app_error "unknown headline: #{id}"
428
451
  end
429
452
 
453
+ alias_method :inline_secref, :inline_hd
454
+
455
+ def inline_sec(id)
456
+ chapter, id = extract_chapter_id(id)
457
+ n = chapter.headline_index.number(id)
458
+ if n.present? && chapter.number && over_secnolevel?(n)
459
+ n
460
+ else
461
+ app_error "the target headline doesn't have a number: #{id}"
462
+ end
463
+ rescue KeyError
464
+ app_error "unknown headline: #{id}"
465
+ end
466
+
467
+ def inline_sectitle(id)
468
+ chapter, id = extract_chapter_id(id)
469
+ compile_inline(chapter.headline(id).caption)
470
+ rescue KeyError
471
+ app_error "unknown headline: #{id}"
472
+ end
473
+
430
474
  def inline_column(id)
431
475
  m = /\A([^|]+)\|(.+)/.match(id)
432
476
  if m && m[1]
@@ -512,8 +556,8 @@ module ReVIEW
512
556
  params = metric.split(/,\s*/)
513
557
  results = []
514
558
  params.each do |param|
515
- if param =~ /\A.+?::/
516
- next unless param =~ /\A#{type}::/
559
+ if /\A.+?::/.match?(param)
560
+ next unless /\A#{type}::/.match?(param)
517
561
 
518
562
  param.sub!(/\A#{type}::/, '')
519
563
  end
@@ -644,7 +688,17 @@ EOTGNUPLOT
644
688
  ext = 'eps'
645
689
  file_path.sub!(/\.pdf\Z/, '.eps')
646
690
  end
647
- system_graph(id, 'java', '-jar', 'plantuml.jar', "-t#{ext}", '-charset', 'UTF-8', tf_path)
691
+ plant_path = nil
692
+ if File.exist?('plantuml.jar')
693
+ plant_path = 'plantuml.jar'
694
+ elsif File.exist?('/usr/share/plantuml/plantuml.jar')
695
+ plant_path = '/usr/share/plantuml/plantuml.jar'
696
+ elsif File.exist?('/usr/share/java/plantuml.jar')
697
+ plant_path = '/usr/share/java/plantuml.jar'
698
+ else
699
+ error!('missing plantuml.jar. Please put plantuml.jar at the working folder.')
700
+ end
701
+ system_graph(id, 'java', '-jar', plant_path, "-t#{ext}", '-charset', 'UTF-8', tf_path)
648
702
  FileUtils.mv("#{tf_path}.#{ext}", file_path)
649
703
  file_path
650
704
  end
@@ -729,7 +783,7 @@ EOTGNUPLOT
729
783
  def beginchild
730
784
  @children ||= []
731
785
  unless previous_list_type
732
- app_error "//beginchild is shown, but previous element isn't ul, ol, or dl"
786
+ app_error "#{@location}: //beginchild is shown, but previous element isn't ul, ol, or dl"
733
787
  end
734
788
  puts "\x01→#{previous_list_type}←\x01"
735
789
  @children.push(previous_list_type)
@@ -737,7 +791,7 @@ EOTGNUPLOT
737
791
 
738
792
  def endchild
739
793
  if @children.nil? || @children.empty?
740
- app_error "//endchild is shown, but any opened //beginchild doesn't exist"
794
+ app_error "#{@location}: //endchild is shown, but any opened //beginchild doesn't exist"
741
795
  else
742
796
  puts "\x01→/#{@children.pop}←\x01"
743
797
  end
@@ -1,13 +1,14 @@
1
1
  require 'yaml'
2
+ require 'date'
2
3
 
3
4
  module ReVIEW
4
5
  class Catalog
5
6
  def initialize(file)
6
- if file.respond_to?(:read)
7
- @yaml = YAML.safe_load(file.read, [Date])
8
- else ## as Object
9
- @yaml = file
10
- end
7
+ @yaml = if file.respond_to?(:read)
8
+ YAMLLoader.safe_load(file.read)
9
+ else ## as Object
10
+ file
11
+ end
11
12
  @yaml ||= {}
12
13
  end
13
14
 
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2009-2020 Minero Aoki, Kenshi Muto
1
+ # Copyright (c) 2009-2022 Minero Aoki, Kenshi Muto
2
2
  # Copyright (c) 2002-2007 Minero Aoki
3
3
  #
4
4
  # This program is free software.
@@ -44,7 +44,7 @@ module ReVIEW
44
44
 
45
45
  def non_escaped_commands
46
46
  if @builder.highlight?
47
- %i[list emlist listnum emlistnum cmd]
47
+ %i[list emlist listnum emlistnum cmd source]
48
48
  else
49
49
  []
50
50
  end
@@ -71,7 +71,7 @@ module ReVIEW
71
71
  attr_reader :name
72
72
 
73
73
  def check_args(args)
74
- unless @argc_spec === args.size
74
+ unless @argc_spec === args.size # rubocop:disable Style/CaseEquality
75
75
  raise CompileError, "wrong # of parameters (block command //#{@name}, expect #{@argc_spec} but #{args.size})"
76
76
  end
77
77
 
@@ -196,6 +196,8 @@ module ReVIEW
196
196
  defminicolumn :notice, 0..1
197
197
 
198
198
  defsingle :footnote, 2
199
+ defsingle :endnote, 2
200
+ defsingle :printendnotes, 0
199
201
  defsingle :noindent, 0
200
202
  defsingle :blankline, 0
201
203
  defsingle :pagebreak, 0
@@ -220,6 +222,7 @@ module ReVIEW
220
222
  definline :table
221
223
  definline :eq
222
224
  definline :fn
225
+ definline :endnote
223
226
  definline :kw
224
227
  definline :ruby
225
228
  definline :bou
@@ -229,6 +232,9 @@ module ReVIEW
229
232
  definline :code
230
233
  definline :bib
231
234
  definline :hd
235
+ definline :secref
236
+ definline :sec
237
+ definline :sectitle
232
238
  definline :href
233
239
  definline :recipe
234
240
  definline :column
@@ -464,7 +470,7 @@ module ReVIEW
464
470
  def compile_ulist(f)
465
471
  level = 0
466
472
  f.while_match(/\A\s+\*|\A\#@/) do |line|
467
- next if line =~ /\A\#@/
473
+ next if /\A\#@/.match?(line)
468
474
 
469
475
  buf = [text(line.sub(/\*+/, '').strip)]
470
476
  f.while_match(/\A\s+(?!\*)\S/) do |cont|
@@ -507,7 +513,7 @@ module ReVIEW
507
513
  def compile_olist(f)
508
514
  @builder.ol_begin
509
515
  f.while_match(/\A\s+\d+\.|\A\#@/) do |line|
510
- next if line =~ /\A\#@/
516
+ next if /\A\#@/.match?(line)
511
517
 
512
518
  num = line.match(/(\d+)\./)[1]
513
519
  buf = [text(line.sub(/\d+\./, '').strip)]
@@ -569,7 +575,7 @@ module ReVIEW
569
575
  f.until_match(%r{\A//\}}) do |line|
570
576
  if ignore_inline
571
577
  buf.push(line.chomp)
572
- elsif line !~ /\A\#@/
578
+ elsif !/\A\#@/.match?(line)
573
579
  buf.push(text(line.rstrip, true))
574
580
  end
575
581
  end
@@ -640,7 +646,7 @@ module ReVIEW
640
646
  str.gsub(/@<(\w+)>([$|])(.+?)(\2)/) do
641
647
  op = $1
642
648
  arg = $3
643
- if arg =~ /[\x01\x02\x03\x04]/
649
+ if /[\x01\x02\x03\x04]/.match?(arg)
644
650
  error "invalid character in '#{str}'", location: location
645
651
  end
646
652
  replaced = arg.tr('@', "\x01").tr('\\', "\x02").tr('{', "\x03").tr('}', "\x04")
@@ -668,17 +674,17 @@ module ReVIEW
668
674
  end
669
675
  result = ''
670
676
  until words.empty?
671
- if in_non_escaped_command? && block_mode
672
- result << revert_replace_fence(words.shift)
673
- else
674
- result << @builder.nofunc_text(revert_replace_fence(words.shift))
675
- end
677
+ result << if in_non_escaped_command? && block_mode
678
+ revert_replace_fence(words.shift)
679
+ else
680
+ @builder.nofunc_text(revert_replace_fence(words.shift))
681
+ end
676
682
  break if words.empty?
677
683
 
678
684
  result << compile_inline(revert_replace_fence(words.shift.gsub(/\\\}/, '}').gsub(/\\\\/, '\\')))
679
685
  end
680
686
  result
681
- rescue => e
687
+ rescue StandardError => e
682
688
  error e.message, location: location
683
689
  end
684
690
  public :text # called from builder
@@ -693,7 +699,7 @@ module ReVIEW
693
699
  end
694
700
 
695
701
  @builder.__send__("inline_#{op}", arg)
696
- rescue => e
702
+ rescue StandardError => e
697
703
  error e.message, location: location
698
704
  @builder.nofunc_text(str)
699
705
  end
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (c) 2012-2021 Masanori Kado, Masayoshi Takahashi, Kenshi Muto
2
+ # Copyright (c) 2012-2022 Masanori Kado, Masayoshi Takahashi, Kenshi Muto
3
3
  #
4
4
  # This program is free software.
5
5
  # You can distribute or modify this program under the terms of
@@ -141,6 +141,9 @@ module ReVIEW
141
141
  'force_include_images' => [],
142
142
  'cover_linear' => nil,
143
143
  'back_footnote' => nil
144
+ },
145
+ 'textmaker' => {
146
+ 'th_bold' => nil
144
147
  }
145
148
  ]
146
149
  conf.maker = nil
@@ -155,7 +158,7 @@ module ReVIEW
155
158
  begin
156
159
  loader = ReVIEW::YAMLLoader.new
157
160
  conf.deep_merge!(loader.load_file(yamlfile))
158
- rescue => e
161
+ rescue StandardError => e
159
162
  raise ReVIEW::ConfigError, "yaml error #{e.message}"
160
163
  end
161
164
  end
@@ -13,7 +13,7 @@ require 'review/version'
13
13
 
14
14
  begin
15
15
  require 'cgi/escape'
16
- rescue
16
+ rescue StandardError
17
17
  require 'cgi/util'
18
18
  end
19
19
 
@@ -62,10 +62,10 @@ EOT
62
62
  def parse_epub(epubname)
63
63
  Zip::File.open(epubname) do |zio|
64
64
  zio.each do |entry|
65
- if entry.name =~ /.+\.opf\Z/
65
+ if /.+\.opf\Z/.match?(entry.name)
66
66
  opf = entry.get_input_stream.read
67
67
  @opfxml = REXML::Document.new(opf)
68
- elsif entry.name =~ /.+\.x?html\Z/
68
+ elsif /.+\.x?html\Z/.match?(entry.name)
69
69
  @htmls[entry.name.sub('OEBPS/', '')] = entry.get_input_stream.read.force_encoding('utf-8')
70
70
  end
71
71
  end
@@ -107,15 +107,15 @@ EOT
107
107
  end
108
108
 
109
109
  file, anc = href.split('#', 2)
110
- if anc
111
- if file.empty?
112
- anc = "#{sanitize(fname)}_#{sanitize(anc)}"
113
- else
114
- anc = "#{sanitize(file)}_#{sanitize(anc)}"
115
- end
116
- else
117
- anc = sanitize(file)
118
- end
110
+ anc = if anc
111
+ if file.empty?
112
+ "#{sanitize(fname)}_#{sanitize(anc)}"
113
+ else
114
+ "#{sanitize(file)}_#{sanitize(anc)}"
115
+ end
116
+ else
117
+ sanitize(file)
118
+ end
119
119
 
120
120
  e.attributes['href'] = "##{anc}"
121
121
  end
@@ -76,7 +76,7 @@ module ReVIEW
76
76
  if @id.nil?
77
77
  @id = @file.gsub(%r{[\\/. ]}, '-')
78
78
  end
79
- if @id =~ /\A[^a-z]/i
79
+ if /\A[^a-z]/i.match?(@id)
80
80
  @id = "rv-#{@id}"
81
81
  end
82
82
  @id = CGI.escape(@id).gsub('%', '_25_')
@@ -1,6 +1,6 @@
1
1
  # = epubcommon.rb -- super class for EPUBv2 and EPUBv3
2
2
  #
3
- # Copyright (c) 2010-2019 Kenshi Muto and Masayoshi Takahashi
3
+ # Copyright (c) 2010-2022 Kenshi Muto and Masayoshi Takahashi
4
4
  #
5
5
  # This program is free software.
6
6
  # You can distribute or modify this program under the terms of
@@ -27,6 +27,7 @@ module ReVIEW
27
27
  @contents = producer.contents
28
28
  @body_ext = nil
29
29
  @logger = ReVIEW.logger
30
+ @workdir = nil
30
31
  end
31
32
 
32
33
  attr_reader :config
@@ -36,7 +37,8 @@ module ReVIEW
36
37
  CGI.escapeHTML(str)
37
38
  end
38
39
 
39
- def produce(epubfile, basedir, tmpdir)
40
+ def produce(_epubfile, _basedir, _tmpdir, base_dir:)
41
+ @workdir = base_dir
40
42
  raise NotImplementedError # should be overridden
41
43
  end
42
44
 
@@ -70,7 +72,7 @@ module ReVIEW
70
72
  item = contents.find { |content| content.coverimage?(config['coverimage']) }
71
73
 
72
74
  unless item
73
- raise "coverimage #{config['coverimage']} not found. Abort."
75
+ raise ApplicationError, "coverimage #{config['coverimage']} not found. Abort."
74
76
  end
75
77
 
76
78
  %Q( <meta name="cover" content="#{item.id}"/>\n)
@@ -99,27 +101,43 @@ module ReVIEW
99
101
  end
100
102
  end
101
103
 
104
+ def template_name(localfile: 'layout.html.erb', systemfile: nil)
105
+ if @workdir
106
+ layoutfile = File.join(@workdir, 'layouts', localfile)
107
+ if File.exist?(layoutfile)
108
+ return layoutfile
109
+ end
110
+ end
111
+
112
+ if systemfile
113
+ return systemfile
114
+ end
115
+
116
+ if config['htmlversion'].to_i == 5
117
+ './html/layout-html5.html.erb'
118
+ else
119
+ './html/layout-xhtml1.html.erb'
120
+ end
121
+ end
122
+
102
123
  # Return cover content.
103
124
  # If Producer#config["coverimage"] is defined, it will be used for
104
125
  # the cover image.
105
126
  def cover
106
- @body_ext = config['epubversion'] >= 3 ? %Q( epub:type="cover") : ''
127
+ @body_ext = config['epubversion'] >= 3 ? %Q( epub:type="cover") : nil
107
128
 
108
129
  if config['coverimage']
109
130
  @coverimage_src = coverimage
110
- raise "coverimage #{config['coverimage']} not found. Abort." unless @coverimage_src
131
+ raise ApplicationError, "coverimage #{config['coverimage']} not found. Abort." unless @coverimage_src
111
132
  end
112
- @body = ReVIEW::Template.generate(path: './html/_cover.html.erb', binding: binding)
133
+ @body = ReVIEW::Template.generate(path: template_name(localfile: '_cover.html.erb', systemfile: 'html/_cover.html.erb'), binding: binding)
113
134
 
114
135
  @title = h(config.name_of('title'))
115
136
  @language = config['language']
116
137
  @stylesheets = config['stylesheet']
117
- template_path = if config['htmlversion'].to_i == 5
118
- './html/layout-html5.html.erb'
119
- else
120
- './html/layout-xhtml1.html.erb'
121
- end
122
- ReVIEW::Template.generate(path: template_path, binding: binding)
138
+ ret = ReVIEW::Template.generate(path: template_name, binding: binding)
139
+ @body_ext = nil
140
+ ret
123
141
  end
124
142
 
125
143
  # Return title (copying) content.
@@ -138,16 +156,11 @@ module ReVIEW
138
156
  if config.names_of('pbl')
139
157
  @publisher_str = join_with_separator(config.names_of('pbl'), ReVIEW::I18n.t('names_splitter'))
140
158
  end
141
- @body = ReVIEW::Template.generate(path: './html/_titlepage.html.erb', binding: binding)
159
+ @body = ReVIEW::Template.generate(path: template_name(localfile: '_titlepage.html.erb', systemfile: './html/_titlepage.html.erb'), binding: binding)
142
160
 
143
161
  @language = config['language']
144
162
  @stylesheets = config['stylesheet']
145
- template_path = if config['htmlversion'].to_i == 5
146
- './html/layout-html5.html.erb'
147
- else
148
- './html/layout-xhtml1.html.erb'
149
- end
150
- ReVIEW::Template.generate(path: template_path, binding: binding)
163
+ ReVIEW::Template.generate(path: template_name, binding: binding)
151
164
  end
152
165
 
153
166
  # Return colophon content.
@@ -155,24 +168,19 @@ module ReVIEW
155
168
  @title = h(ReVIEW::I18n.t('colophontitle'))
156
169
  @isbn_hyphen = isbn_hyphen
157
170
 
158
- @body = ReVIEW::Template.generate(path: './html/_colophon.html.erb', binding: binding)
171
+ @body = ReVIEW::Template.generate(path: template_name(localfile: '_colophon.html.erb', systemfile: './html/_colophon.html.erb'), binding: binding)
159
172
 
160
173
  @language = config['language']
161
174
  @stylesheets = config['stylesheet']
162
- template_path = if config['htmlversion'].to_i == 5
163
- './html/layout-html5.html.erb'
164
- else
165
- './html/layout-xhtml1.html.erb'
166
- end
167
- ReVIEW::Template.generate(path: template_path, binding: binding)
175
+ ReVIEW::Template.generate(path: template_name, binding: binding)
168
176
  end
169
177
 
170
178
  def isbn_hyphen
171
179
  str = config['isbn'].to_s
172
180
 
173
- if str =~ /\A\d{10}\Z/
181
+ if /\A\d{10}\Z/.match?(str)
174
182
  "#{str[0..0]}-#{str[1..5]}-#{str[6..8]}-#{str[9..9]}"
175
- elsif str =~ /\A\d{13}\Z/
183
+ elsif /\A\d{13}\Z/.match?(str)
176
184
  "#{str[0..2]}-#{str[3..3]}-#{str[4..8]}-#{str[9..11]}-#{str[12..12]}"
177
185
  end
178
186
  end
@@ -184,9 +192,9 @@ module ReVIEW
184
192
  items.each_with_index do |item, rev|
185
193
  editstr = edit == 0 ? ReVIEW::I18n.t('first_edition') : ReVIEW::I18n.t('nth_edition', (edit + 1).to_s)
186
194
  revstr = ReVIEW::I18n.t('nth_impression', (rev + 1).to_s)
187
- if item =~ /\A\d+-\d+-\d+\Z/
195
+ if /\A\d+-\d+-\d+\Z/.match?(item)
188
196
  @col_history << ReVIEW::I18n.t('published_by1', [date_to_s(item), editstr + revstr])
189
- elsif item =~ /\A(\d+-\d+-\d+)[\s ](.+)/
197
+ elsif /\A(\d+-\d+-\d+)[\s ](.+)/.match?(item)
190
198
  # custom date with string
191
199
  item.match(/\A(\d+-\d+-\d+)[\s ](.+)/) do |m|
192
200
  @col_history << ReVIEW::I18n.t('published_by3', [date_to_s(m[1]), m[2]])
@@ -199,7 +207,7 @@ module ReVIEW
199
207
  end
200
208
  end
201
209
 
202
- ReVIEW::Template.generate(path: './html/_colophon_history.html.erb', binding: binding)
210
+ ReVIEW::Template.generate(path: template_name(localfile: '_colophon_history.html.erb', systemfile: './html/_colophon_history.html.erb'), binding: binding)
203
211
  end
204
212
 
205
213
  def date_to_s(date)
@@ -211,22 +219,16 @@ module ReVIEW
211
219
  # Return own toc content.
212
220
  def mytoc
213
221
  @title = h(ReVIEW::I18n.t('toctitle'))
214
-
215
222
  @body = %Q( <h1 class="toc-title">#{h(ReVIEW::I18n.t('toctitle'))}</h1>\n)
216
- if config['epubmaker']['flattoc'].nil?
217
- @body << hierarchy_ncx('ul')
218
- else
219
- @body << flat_ncx('ul', config['epubmaker']['flattocindent'])
220
- end
223
+ @body << if config['epubmaker']['flattoc'].nil?
224
+ hierarchy_ncx('ul')
225
+ else
226
+ flat_ncx('ul', config['epubmaker']['flattocindent'])
227
+ end
221
228
 
222
229
  @language = config['language']
223
230
  @stylesheets = config['stylesheet']
224
- template_path = if config['htmlversion'].to_i == 5
225
- './html/layout-html5.html.erb'
226
- else
227
- './html/layout-xhtml1.html.erb'
228
- end
229
- ReVIEW::Template.generate(path: template_path, binding: binding)
231
+ ReVIEW::Template.generate(path: template_name, binding: binding)
230
232
  end
231
233
 
232
234
  def hierarchy_ncx(type)
@@ -334,11 +336,11 @@ module ReVIEW
334
336
  end
335
337
 
336
338
  contents.each do |item|
337
- next if item.file =~ /#/ # skip subgroup
339
+ next if /#/.match?(item.file) # skip subgroup
338
340
 
339
341
  fname = "#{basedir}/#{item.file}"
340
342
  unless File.exist?(fname)
341
- raise "#{fname} is not found."
343
+ raise ApplicationError, "#{fname} is not found."
342
344
  end
343
345
 
344
346
  FileUtils.mkdir_p(File.dirname("#{tmpdir}/OEBPS/#{item.file}"))
@@ -1,6 +1,6 @@
1
1
  # = epubv2.rb -- EPUB version 2 producer.
2
2
  #
3
- # Copyright (c) 2010-2017 Kenshi Muto and Masayoshi Takahashi
3
+ # Copyright (c) 2010-2022 Kenshi Muto and Masayoshi Takahashi
4
4
  #
5
5
  # This program is free software.
6
6
  # You can distribute or modify this program under the terms of
@@ -159,6 +159,7 @@ EOT
159
159
  # +basedir+ points the directory has contents.
160
160
  # +tmpdir+ defines temporary directory.
161
161
  def produce(epubfile, work_dir, tmpdir, base_dir:)
162
+ @workdir = base_dir
162
163
  produce_write_common(work_dir, tmpdir)
163
164
 
164
165
  ncx_file = "#{tmpdir}/OEBPS/#{config['bookname']}.ncx"