review 5.1.1 → 5.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby-tex.yml +6 -2
  3. data/.github/workflows/ruby-win.yml +6 -2
  4. data/.github/workflows/ruby.yml +6 -2
  5. data/.rubocop.yml +5 -319
  6. data/NEWS.ja.md +149 -0
  7. data/NEWS.md +149 -1
  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 +14 -23
  13. data/bin/review-index +1 -1
  14. data/bin/review-preproc +29 -35
  15. data/bin/review-validate +2 -2
  16. data/doc/config.yml.sample +9 -1
  17. data/doc/config.yml.sample-simple +1 -1
  18. data/doc/format.ja.md +29 -3
  19. data/doc/format.md +32 -3
  20. data/doc/writing_vertical.ja.md +6 -0
  21. data/lib/review/book/base.rb +3 -3
  22. data/lib/review/book/book_unit.rb +13 -3
  23. data/lib/review/book/chapter.rb +1 -1
  24. data/lib/review/book/index.rb +7 -4
  25. data/lib/review/book/part.rb +12 -13
  26. data/lib/review/book/volume.rb +1 -1
  27. data/lib/review/builder.rb +92 -65
  28. data/lib/review/catalog.rb +6 -5
  29. data/lib/review/compiler.rb +76 -57
  30. data/lib/review/configure.rb +5 -2
  31. data/lib/review/epub2html.rb +12 -12
  32. data/lib/review/epubmaker/content.rb +1 -1
  33. data/lib/review/epubmaker/epubcommon.rb +47 -45
  34. data/lib/review/epubmaker/epubv2.rb +2 -1
  35. data/lib/review/epubmaker/epubv3.rb +5 -4
  36. data/lib/review/epubmaker/producer.rb +6 -7
  37. data/lib/review/epubmaker/reviewheaderlistener.rb +1 -1
  38. data/lib/review/epubmaker.rb +56 -67
  39. data/lib/review/exception.rb +7 -0
  40. data/lib/review/extentions/string.rb +1 -1
  41. data/lib/review/htmlbuilder.rb +90 -34
  42. data/lib/review/htmlutils.rb +17 -17
  43. data/lib/review/i18n.rb +3 -3
  44. data/lib/review/i18n.yml +6 -0
  45. data/lib/review/idgxmlbuilder.rb +61 -39
  46. data/lib/review/idgxmlmaker.rb +27 -26
  47. data/lib/review/img_math.rb +12 -18
  48. data/lib/review/index_builder.rb +94 -53
  49. data/lib/review/init.rb +4 -4
  50. data/lib/review/latexbuilder.rb +84 -76
  51. data/lib/review/lineinput.rb +3 -3
  52. data/lib/review/location.rb +1 -1
  53. data/lib/review/loggable.rb +27 -0
  54. data/lib/review/logger.rb +69 -21
  55. data/lib/review/makerhelper.rb +8 -4
  56. data/lib/review/markdownbuilder.rb +21 -12
  57. data/lib/review/pdfmaker.rb +63 -42
  58. data/lib/review/plaintextbuilder.rb +16 -15
  59. data/lib/review/preprocessor/directive.rb +35 -0
  60. data/lib/review/preprocessor/line.rb +34 -0
  61. data/lib/review/preprocessor/repository.rb +177 -0
  62. data/lib/review/preprocessor.rb +94 -296
  63. data/lib/review/rstbuilder.rb +12 -3
  64. data/lib/review/template.rb +5 -1
  65. data/lib/review/textmaker.rb +32 -31
  66. data/lib/review/textutils.rb +5 -6
  67. data/lib/review/tocprinter.rb +12 -7
  68. data/lib/review/topbuilder.rb +96 -19
  69. data/lib/review/update.rb +16 -8
  70. data/lib/review/version.rb +1 -1
  71. data/lib/review/volumeprinter.rb +9 -9
  72. data/lib/review/webmaker.rb +45 -46
  73. data/lib/review/webtocprinter.rb +10 -10
  74. data/lib/review/yamlloader.rb +35 -2
  75. data/review.gemspec +2 -1
  76. data/samples/sample-book/src/config.yml +0 -1
  77. data/samples/sample-book/src/lib/tasks/review.rake +3 -1
  78. data/samples/sample-book/src/lib/tasks/z01_copy_sty.rake +2 -1
  79. data/samples/syntax-book/ch02.re +9 -0
  80. data/samples/syntax-book/lib/tasks/z01_copy_sty.rake +2 -1
  81. data/templates/html/_titlepage.html.erb +9 -17
  82. data/templates/latex/config.erb +3 -0
  83. data/templates/latex/review-jlreq/review-base.sty +4 -5
  84. data/templates/latex/review-jlreq/review-jlreq.cls +39 -5
  85. data/templates/latex/review-jsbook/review-base.sty +9 -3
  86. data/templates/latex/review-jsbook/review-jsbook.cls +32 -5
  87. data/templates/opf/opf_manifest_epubv2.opf.erb +1 -1
  88. data/templates/opf/opf_manifest_epubv3.opf.erb +1 -1
  89. data/test/assets/syntax_book_index_detail.txt +10 -8
  90. data/test/assets/test_template.tex +4 -1
  91. data/test/assets/test_template_backmatter.tex +4 -1
  92. data/test/book_test_helper.rb +10 -10
  93. data/test/test_book_chapter.rb +25 -2
  94. data/test/test_builder.rb +10 -8
  95. data/test/test_epub3maker.rb +3 -3
  96. data/test/test_epubmaker.rb +27 -37
  97. data/test/test_epubmaker_cmd.rb +14 -3
  98. data/test/test_htmlbuilder.rb +111 -31
  99. data/test/test_idgxmlbuilder.rb +41 -33
  100. data/test/test_idgxmlmaker_cmd.rb +1 -1
  101. data/test/test_img_math.rb +11 -2
  102. data/test/test_index.rb +30 -4
  103. data/test/test_latexbuilder.rb +46 -25
  104. data/test/test_latexbuilder_v2.rb +18 -10
  105. data/test/test_markdownbuilder.rb +13 -0
  106. data/test/test_pdfmaker.rb +19 -0
  107. data/test/test_pdfmaker_cmd.rb +10 -10
  108. data/test/test_plaintextbuilder.rb +46 -22
  109. data/test/test_preprocessor.rb +188 -1
  110. data/test/test_rstbuilder.rb +13 -0
  111. data/test/test_textmaker_cmd.rb +1 -1
  112. data/test/test_topbuilder.rb +195 -29
  113. data/test/test_yamlloader.rb +28 -42
  114. metadata +11 -6
@@ -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
@@ -25,11 +25,14 @@ require 'review/epubmaker/epubv3'
25
25
  require 'review/i18n'
26
26
  require 'review/configure'
27
27
  require 'review/extentions/hash'
28
+ require 'review/loggable'
28
29
 
29
30
  module ReVIEW
30
31
  class EPUBMaker
31
32
  # EPUBMaker produces EPUB file.
32
33
  class Producer
34
+ include Loggable
35
+
33
36
  # Array of content objects.
34
37
  attr_accessor :contents
35
38
  # Parameter hash.
@@ -37,10 +40,6 @@ module ReVIEW
37
40
  # Message resource object.
38
41
  attr_reader :res
39
42
 
40
- def warn(msg)
41
- @logger.warn(msg)
42
- end
43
-
44
43
  # Construct producer object.
45
44
  # +config+ takes initial parameter hash.
46
45
  def initialize(config)
@@ -90,8 +89,8 @@ module ReVIEW
90
89
  Dir.foreach(path) do |f|
91
90
  next if f.start_with?('.')
92
91
 
93
- if f =~ /\.(#{allow_exts.join('|')})\Z/i
94
- path.chop! if path =~ %r{/\Z}
92
+ if /\.(#{allow_exts.join('|')})\Z/i.match?(f)
93
+ path.chop! if %r{/\Z}.match?(path)
95
94
  if base.nil?
96
95
  @contents.push(ReVIEW::EPUBMaker::Content.new(file: "#{path}/#{f}"))
97
96
  else
@@ -116,7 +115,7 @@ module ReVIEW
116
115
 
117
116
  # use Dir to solve a path for Windows (see #1011)
118
117
  new_tmpdir = Dir[File.join(tmpdir.nil? ? Dir.mktmpdir : tmpdir)][0]
119
- if epubfile !~ %r{\A/}
118
+ unless epubfile.start_with?('/')
120
119
  epubfile = "#{current}/#{epubfile}"
121
120
  end
122
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
@@ -27,10 +27,12 @@ require 'review/epubmaker/epubv2'
27
27
  require 'review/epubmaker/epubv3'
28
28
  require 'review/epubmaker/reviewheaderlistener'
29
29
  require 'review/makerhelper'
30
+ require 'review/loggable'
30
31
 
31
32
  module ReVIEW
32
33
  class EPUBMaker
33
34
  include MakerHelper
35
+ include Loggable
34
36
  include ReVIEW::CallHook
35
37
 
36
38
  def initialize
@@ -42,19 +44,6 @@ module ReVIEW
42
44
  @basedir = nil
43
45
  end
44
46
 
45
- def error(msg)
46
- @logger.error msg
47
- exit 1
48
- end
49
-
50
- def warn(msg)
51
- @logger.warn msg
52
- end
53
-
54
- def log(msg)
55
- @logger.debug(msg)
56
- end
57
-
58
47
  def self.execute(*args)
59
48
  self.new.execute(*args)
60
49
  end
@@ -84,14 +73,18 @@ module ReVIEW
84
73
 
85
74
  def execute(*args)
86
75
  cmd_config, yamlfile, exportfile = parse_opts(args)
87
- error "#{yamlfile} not found." unless File.exist?(yamlfile)
76
+ error! "#{yamlfile} not found." unless File.exist?(yamlfile)
88
77
 
89
- @config = ReVIEW::Configure.create(maker: 'epubmaker',
90
- yamlfile: yamlfile,
91
- 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
92
85
  @producer = ReVIEW::EPUBMaker::Producer.new(@config)
93
86
  update_log_level
94
- log("Loaded yaml file (#{yamlfile}).")
87
+ debug("Loaded yaml file (#{yamlfile}).")
95
88
  @basedir = File.absolute_path(File.dirname(yamlfile))
96
89
 
97
90
  produce(yamlfile, exportfile)
@@ -130,11 +123,11 @@ module ReVIEW
130
123
 
131
124
  @img_math = ReVIEW::ImgMath.new(@config)
132
125
  begin
133
- @config.check_version(ReVIEW::VERSION)
126
+ @config.check_version(ReVIEW::VERSION, exception: true)
134
127
  rescue ReVIEW::ConfigError => e
135
128
  warn e.message
136
129
  end
137
- log("#{bookname}.epub will be created.")
130
+ debug("#{bookname}.epub will be created.")
138
131
 
139
132
  FileUtils.rm_f("#{bookname}.epub")
140
133
  if @config['debug']
@@ -145,7 +138,7 @@ module ReVIEW
145
138
 
146
139
  basetmpdir = build_path
147
140
  begin
148
- log("Created first temporary directory as #{basetmpdir}.")
141
+ debug("Created first temporary directory as #{basetmpdir}.")
149
142
 
150
143
  call_hook('hook_beforeprocess', basetmpdir, base_dir: @basedir)
151
144
 
@@ -192,14 +185,14 @@ module ReVIEW
192
185
  epubtmpdir = File.join(basetmpdir, booktmpname)
193
186
  Dir.mkdir(epubtmpdir)
194
187
  end
195
- log('Call ePUB producer.')
188
+ debug('Call ePUB producer.')
196
189
  @producer.produce("#{bookname}.epub", basetmpdir, epubtmpdir, base_dir: @basedir)
197
- log('Finished.')
190
+ debug('Finished.')
198
191
  @logger.success("built #{bookname}.epub")
199
192
  rescue ApplicationError => e
200
193
  raise if @config['debug']
201
194
 
202
- error(e.message)
195
+ error! e.message
203
196
  ensure
204
197
  FileUtils.remove_entry_secure(basetmpdir) unless @config['debug']
205
198
  end
@@ -238,15 +231,15 @@ module ReVIEW
238
231
  if @config['epubmaker']['verify_target_images'].present?
239
232
  @config['epubmaker']['force_include_images'].each do |file|
240
233
  unless File.exist?(file)
241
- if file !~ /\Ahttps?:/
234
+ unless /\Ahttps?:/.match?(file)
242
235
  warn "#{file} is not found, skip."
243
236
  end
244
237
  next
245
238
  end
246
239
  basedir = File.dirname(file)
247
240
  FileUtils.mkdir_p(File.join(destdir, basedir))
248
- log("Copy #{file} to the temporary directory.")
249
- FileUtils.cp(file, File.join(destdir, basedir))
241
+ debug("Copy #{file} to the temporary directory.")
242
+ FileUtils.cp(file, File.join(destdir, basedir), preserve: true)
250
243
  end
251
244
  else
252
245
  recursive_copy_files(resdir, destdir, allow_exts)
@@ -268,10 +261,10 @@ module ReVIEW
268
261
 
269
262
  if FileTest.directory?(File.join(resdir, fname))
270
263
  recursive_copy_files(File.join(resdir, fname), File.join(destdir, fname), allow_exts)
271
- elsif fname =~ /\.(#{allow_exts.join('|')})\Z/i
264
+ elsif /\.(#{allow_exts.join('|')})\Z/i.match?(fname)
272
265
  FileUtils.mkdir_p(destdir)
273
- log("Copy #{resdir}/#{fname} to the temporary directory.")
274
- FileUtils.cp(File.join(resdir, fname), destdir)
266
+ debug("Copy #{resdir}/#{fname} to the temporary directory.")
267
+ FileUtils.cp(File.join(resdir, fname), destdir, preserve: true)
275
268
  end
276
269
  end
277
270
  end
@@ -280,8 +273,7 @@ module ReVIEW
280
273
  def check_compile_status
281
274
  return unless @compile_errors
282
275
 
283
- $stderr.puts 'compile error, No EPUB file output.'
284
- exit 1
276
+ error! 'compile error, No EPUB file output.'
285
277
  end
286
278
 
287
279
  def build_body(basetmpdir, yamlfile)
@@ -323,19 +315,29 @@ module ReVIEW
323
315
  end
324
316
 
325
317
  def build_part(part, basetmpdir, htmlfile)
326
- log("Create #{htmlfile} from a template.")
318
+ debug("Create #{htmlfile} from a template.")
327
319
  File.open(File.join(basetmpdir, htmlfile), 'w') do |f|
328
320
  @part_number = part.number
329
321
  @part_title = part.name.strip
330
- @body = ReVIEW::Template.generate(path: 'html/_part_body.html.erb', binding: binding)
331
-
322
+ @body = ReVIEW::Template.generate(path: template_name(localfile: '_part_body.html.erb', systemfile: 'html/_part_body.html.erb'), binding: binding)
332
323
  @language = @producer.config['language']
333
324
  @stylesheets = @producer.config['stylesheet']
334
325
  f.write ReVIEW::Template.generate(path: template_name, binding: binding)
335
326
  end
336
327
  end
337
328
 
338
- 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
+
339
341
  if @producer.config['htmlversion'].to_i == 5
340
342
  './html/layout-html5.html.erb'
341
343
  else
@@ -384,11 +386,11 @@ module ReVIEW
384
386
 
385
387
  htmlfile = "#{id}.#{@config['htmlext']}"
386
388
  write_buildlogtxt(basetmpdir, htmlfile, filename)
387
- log("Create #{htmlfile} from #{filename}.")
389
+ debug("Create #{htmlfile} from #{filename}.")
388
390
 
389
391
  if @config['params'].present?
390
392
  warn %Q('params:' in config.yml is obsoleted.)
391
- if @config['params'] =~ /stylesheet=/
393
+ if /stylesheet=/.match?(@config['params'])
392
394
  warn %Q(stylesheets should be defined in 'stylesheet:', not in 'params:')
393
395
  end
394
396
  end
@@ -396,10 +398,10 @@ module ReVIEW
396
398
  @converter.convert(filename, File.join(basetmpdir, htmlfile))
397
399
  write_info_body(basetmpdir, id, htmlfile, ispart, chaptype)
398
400
  remove_hidden_title(basetmpdir, htmlfile)
399
- rescue => e
401
+ rescue StandardError => e
400
402
  @compile_errors = true
401
- warn "compile error in #{filename} (#{e.class})"
402
- warn e.message
403
+ error "compile error in #{filename} (#{e.class})"
404
+ error e.message
403
405
  end
404
406
  end
405
407
 
@@ -448,11 +450,11 @@ module ReVIEW
448
450
  end
449
451
 
450
452
  properties = detect_properties(path)
451
- if properties.present?
452
- prop_str = ',properties=' + properties.join(' ')
453
- else
454
- prop_str = ''
455
- end
453
+ prop_str = if properties.present?
454
+ ',properties=' + properties.join(' ')
455
+ else
456
+ ''
457
+ end
456
458
  first = true
457
459
  headlines.each do |headline|
458
460
  if ispart.present? && headline['level'] == 1
@@ -480,7 +482,7 @@ module ReVIEW
480
482
  @htmltoc.each_item do |level, file, title, args|
481
483
  next if level.to_i > @config['toclevel'] && args[:force_include].nil?
482
484
 
483
- log("Push #{file} to ePUB contents.")
485
+ debug("Push #{file} to ePUB contents.")
484
486
 
485
487
  params = { file: file,
486
488
  level: level.to_i,
@@ -504,9 +506,9 @@ module ReVIEW
504
506
 
505
507
  @config['stylesheet'].each do |sfile|
506
508
  unless File.exist?(sfile)
507
- error "stylesheet: #{sfile} is not found."
509
+ error! "stylesheet: #{sfile} is not found."
508
510
  end
509
- FileUtils.cp(sfile, basetmpdir)
511
+ FileUtils.cp(sfile, basetmpdir, preserve: true)
510
512
  @producer.contents.push(ReVIEW::EPUBMaker::Content.new(file: sfile))
511
513
  end
512
514
  end
@@ -514,10 +516,10 @@ module ReVIEW
514
516
  def copy_static_file(configname, destdir, destfilename: nil)
515
517
  destfilename ||= @config[configname]
516
518
  unless File.exist?(@config[configname])
517
- error "#{configname}: #{@config[configname]} is not found."
519
+ error! "#{configname}: #{@config[configname]} is not found."
518
520
  end
519
521
  FileUtils.cp(@config[configname],
520
- File.join(destdir, destfilename))
522
+ File.join(destdir, destfilename), preserve: true)
521
523
  end
522
524
 
523
525
  def copy_frontmatter(basetmpdir)
@@ -557,25 +559,12 @@ module ReVIEW
557
559
  end
558
560
 
559
561
  def build_titlepage(basetmpdir, htmlfile)
560
- # TODO: should be created via epubcommon
561
562
  @title = h(@config.name_of('booktitle'))
562
563
  File.open(File.join(basetmpdir, htmlfile), 'w') do |f|
563
- @body = ''
564
- @body << %Q(<div class="titlepage">\n)
565
- @body << %Q(<h1 class="tp-title">#{h(@config.name_of('booktitle'))}</h1>\n)
566
- if @config['subtitle']
567
- @body << %Q(<h2 class="tp-subtitle">#{h(@config.name_of('subtitle'))}</h2>\n)
568
- end
569
- if @config['aut']
570
- @body << %Q(<h2 class="tp-author">#{h(@config.names_of('aut').join(ReVIEW::I18n.t('names_splitter')))}</h2>\n)
571
- end
572
- if @config['pbl']
573
- @body << %Q(<h3 class="tp-publisher">#{h(@config.names_of('pbl').join(ReVIEW::I18n.t('names_splitter')))}</h3>\n)
574
- end
575
- @body << '</div>'
576
-
564
+ @body = ReVIEW::Template.generate(path: template_name(localfile: '_titlepage.html.erb', systemfile: 'html/_titlepage.html.erb'), binding: binding)
577
565
  @language = @producer.config['language']
578
566
  @stylesheets = @producer.config['stylesheet']
567
+
579
568
  f.write ReVIEW::Template.generate(path: template_name, binding: binding)
580
569
  end
581
570
  end
@@ -22,4 +22,11 @@ module ReVIEW
22
22
  class FileNotFound < ApplicationError; end
23
23
 
24
24
  class KeyError < CompileError; end
25
+
26
+ class BuildError < ApplicationError
27
+ def initialize(msg, location: nil)
28
+ @location = location
29
+ super(msg)
30
+ end
31
+ end
25
32
  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-2020 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
  #
@@ -42,18 +42,20 @@ module ReVIEW
42
42
  end
43
43
 
44
44
  def builder_init_file
45
+ super
45
46
  @noindent = nil
46
47
  @ol_num = nil
47
- @warns = []
48
- @errors = []
49
48
  @chapter.book.image_types = %w[.png .jpg .jpeg .gif .svg]
50
49
  @column = 0
51
- @sec_counter = SecCounter.new(5, @chapter)
52
50
  @nonum_counter = 0
53
51
  @first_line_num = nil
54
52
  @body_ext = nil
55
53
  @toc = nil
56
54
  @javascripts = []
55
+ @section_stack = []
56
+
57
+ maker = @book.config.maker || 'epubmaker' # for review-compile
58
+ @use_section = @book.config[maker] && @book.config[maker]['use_section']
57
59
  end
58
60
  private :builder_init_file
59
61
 
@@ -65,11 +67,11 @@ module ReVIEW
65
67
  htmldir = 'html'
66
68
  localfilename = 'layout.html.erb'
67
69
  end
68
- if @book.htmlversion == 5
69
- htmlfilename = File.join(htmldir, 'layout-html5.html.erb')
70
- else
71
- htmlfilename = File.join(htmldir, 'layout-xhtml1.html.erb')
72
- 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
73
75
 
74
76
  layout_file = File.join(@book.basedir, 'layouts', localfilename)
75
77
  if !File.exist?(layout_file) && File.exist?(File.join(@book.basedir, 'layouts', 'layout.erb'))
@@ -78,7 +80,7 @@ module ReVIEW
78
80
 
79
81
  if File.exist?(layout_file)
80
82
  if ENV['REVIEW_SAFE_MODE'].to_i & 4 > 0
81
- warn %Q(user's layout is prohibited in safe mode. ignored.)
83
+ warn %Q(user's layout is prohibited in safe mode. ignored.), location: location
82
84
  layout_file = File.expand_path(htmlfilename, ReVIEW::Template::TEMPLATE_DIR)
83
85
  end
84
86
  else
@@ -87,7 +89,35 @@ module ReVIEW
87
89
  layout_file
88
90
  end
89
91
 
92
+ def use_section?
93
+ @use_section
94
+ end
95
+
96
+ def open_section(level)
97
+ result = []
98
+
99
+ while @section_stack.size > 0 && level <= @section_stack[-1]
100
+ result << '</section>'
101
+ @section_stack.pop
102
+ end
103
+ @section_stack.push(level)
104
+ result << %Q(<section class="level#{level}">)
105
+
106
+ return result.join("\n")
107
+ end
108
+
109
+ def close_sections
110
+ "</section>\n" * @section_stack.size
111
+ end
112
+
90
113
  def result
114
+ check_printendnotes
115
+
116
+ # flush all `</section>`
117
+ if use_section?
118
+ print close_sections
119
+ end
120
+
91
121
  # default XHTML header/footer
92
122
  @title = strip_html(compile_inline(@chapter.title))
93
123
  @body = solve_nest(@output.string)
@@ -133,6 +163,9 @@ module ReVIEW
133
163
  end
134
164
 
135
165
  def headline(level, label, caption)
166
+ if use_section?
167
+ print open_section(level)
168
+ end
136
169
  prefix, anchor = headline_prefix(level)
137
170
  if prefix
138
171
  prefix = %Q(<span class="secno">#{prefix}</span>)
@@ -618,7 +651,7 @@ module ReVIEW
618
651
  require 'math_ml'
619
652
  require 'math_ml/symbol/character_reference'
620
653
  rescue LoadError
621
- error 'not found math_ml'
654
+ app_error 'not found math_ml'
622
655
  end
623
656
  p = MathML::LaTeX::Parser.new(symbol: MathML::Symbol::CharacterReference)
624
657
  print p.parse(lines.join("\n") + "\n", true)
@@ -676,7 +709,7 @@ module ReVIEW
676
709
  end
677
710
 
678
711
  def image_dummy(id, caption, lines)
679
- warn "image not bound: #{id}"
712
+ warn "image not bound: #{id}", location: location
680
713
  puts %Q(<div id="#{normalize_id(id)}" class="image">)
681
714
  image_header(id, caption) if caption_top?('image')
682
715
  puts %Q(<pre class="dummyimage">)
@@ -740,7 +773,7 @@ module ReVIEW
740
773
 
741
774
  def imgtable(lines, id, caption = nil, metric = nil)
742
775
  unless @chapter.image_bound?(id)
743
- warn "image not bound: #{id}"
776
+ warn "image not bound: #{id}", location: location
744
777
  image_dummy(id, caption, lines)
745
778
  return
746
779
  end
@@ -757,7 +790,7 @@ module ReVIEW
757
790
  table_header(id, caption)
758
791
  end
759
792
  rescue KeyError
760
- error "no such table: #{id}"
793
+ app_error "no such table: #{id}"
761
794
  end
762
795
 
763
796
  puts '</div>'
@@ -794,6 +827,22 @@ module ReVIEW
794
827
  end
795
828
  end
796
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
+
797
846
  def indepimage(lines, id, caption = '', metric = nil)
798
847
  metrics = parse_metric('html', metric)
799
848
  caption = '' unless caption.present?
@@ -812,8 +861,8 @@ EOS
812
861
  end
813
862
  begin
814
863
  puts %Q(<img src="#{@chapter.image(id).path.sub(%r{\A\./}, '')}" alt="#{escape(compile_inline(caption))}"#{metrics} />)
815
- rescue
816
- warn "image not bound: #{id}"
864
+ rescue StandardError
865
+ warn "image not bound: #{id}", location: location
817
866
  if lines
818
867
  puts %Q(<pre class="dummyimage">)
819
868
  lines.each do |line|
@@ -866,7 +915,7 @@ EOS
866
915
  alias_method :inline_ref, :inline_labelref
867
916
 
868
917
  def inline_pageref(id)
869
- error "pageref op is unsupported on this builder: #{id}"
918
+ app_error "pageref op is unsupported on this builder: #{id}"
870
919
  end
871
920
 
872
921
  def inline_chapref(id)
@@ -877,7 +926,7 @@ EOS
877
926
  title
878
927
  end
879
928
  rescue KeyError
880
- error "unknown chapter: #{id}"
929
+ app_error "unknown chapter: #{id}"
881
930
  end
882
931
 
883
932
  def inline_chap(id)
@@ -887,7 +936,7 @@ EOS
887
936
  @book.chapter_index.number(id)
888
937
  end
889
938
  rescue KeyError
890
- error "unknown chapter: #{id}"
939
+ app_error "unknown chapter: #{id}"
891
940
  end
892
941
 
893
942
  def inline_title(id)
@@ -898,7 +947,7 @@ EOS
898
947
  title
899
948
  end
900
949
  rescue KeyError
901
- error "unknown chapter: #{id}"
950
+ app_error "unknown chapter: #{id}"
902
951
  end
903
952
 
904
953
  def inline_fn(id)
@@ -908,7 +957,13 @@ EOS
908
957
  %Q(<a id="fnb-#{normalize_id(id)}" href="#fn-#{normalize_id(id)}" class="noteref">*#{@chapter.footnote(id).number}</a>)
909
958
  end
910
959
  rescue KeyError
911
- error "unknown footnote: #{id}"
960
+ app_error "unknown footnote: #{id}"
961
+ end
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}"
912
967
  end
913
968
 
914
969
  def compile_ruby(base, ruby)
@@ -922,8 +977,9 @@ EOS
922
977
  def compile_kw(word, alt)
923
978
  %Q(<b class="kw">) +
924
979
  if alt
925
- then escape(word + " (#{alt.strip})")
926
- else escape(word)
980
+ escape(word + " (#{alt.strip})")
981
+ else
982
+ escape(word)
927
983
  end +
928
984
  "</b><!-- IDX:#{escape_comment(escape(word))} -->"
929
985
  end
@@ -990,7 +1046,7 @@ EOS
990
1046
  require 'math_ml'
991
1047
  require 'math_ml/symbol/character_reference'
992
1048
  rescue LoadError
993
- error 'not found math_ml'
1049
+ app_error 'not found math_ml'
994
1050
  end
995
1051
  parser = MathML::LaTeX::Parser.new(symbol: MathML::Symbol::CharacterReference)
996
1052
  %Q(<span class="equation">#{parser.parse(str, nil)}</span>)
@@ -1036,16 +1092,16 @@ EOS
1036
1092
  def inline_bib(id)
1037
1093
  %Q(<a href="#{@book.bib_file.gsub(/\.re\Z/, ".#{@book.config['htmlext']}")}#bib-#{normalize_id(id)}">[#{@chapter.bibpaper(id).number}]</a>)
1038
1094
  rescue KeyError
1039
- error "unknown bib: #{id}"
1095
+ app_error "unknown bib: #{id}"
1040
1096
  end
1041
1097
 
1042
1098
  def inline_hd_chap(chap, id)
1043
1099
  n = chap.headline_index.number(id)
1044
- if n.present? && chap.number && over_secnolevel?(n)
1045
- str = I18n.t('hd_quote', [n, compile_inline(chap.headline(id).caption)])
1046
- else
1047
- str = I18n.t('hd_quote_without_number', compile_inline(chap.headline(id).caption))
1048
- 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
1049
1105
  if @book.config['chapterlink']
1050
1106
  anchor = 'h' + n.tr('.', '-')
1051
1107
  %Q(<a href="#{chap.id}#{extname}##{anchor}">#{str}</a>)
@@ -1053,7 +1109,7 @@ EOS
1053
1109
  str
1054
1110
  end
1055
1111
  rescue KeyError
1056
- error "unknown headline: #{id}"
1112
+ app_error "unknown headline: #{id}"
1057
1113
  end
1058
1114
 
1059
1115
  def column_label(id, chapter = @chapter)
@@ -1069,7 +1125,7 @@ EOS
1069
1125
  I18n.t('column', compile_inline(chapter.column(id).caption))
1070
1126
  end
1071
1127
  rescue KeyError
1072
- error "unknown column: #{id}"
1128
+ app_error "unknown column: #{id}"
1073
1129
  end
1074
1130
 
1075
1131
  def inline_list(id)
@@ -1195,8 +1251,8 @@ EOS
1195
1251
  def inline_icon(id)
1196
1252
  begin
1197
1253
  %Q(<img src="#{@chapter.image(id).path.sub(%r{\A\./}, '')}" alt="[#{id}]" />)
1198
- rescue
1199
- warn "image not bound: #{id}"
1254
+ rescue StandardError
1255
+ warn "image not bound: #{id}", location: location
1200
1256
  %Q(<pre>missing image: #{id}</pre>)
1201
1257
  end
1202
1258
  end