review 5.0.0 → 5.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (173) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby-tex.yml +35 -0
  3. data/.github/workflows/ruby-win.yml +8 -4
  4. data/.github/workflows/ruby.yml +6 -2
  5. data/.rubocop.yml +24 -9
  6. data/NEWS.ja.md +215 -0
  7. data/NEWS.md +215 -1
  8. data/README.md +7 -6
  9. data/Rakefile +7 -2
  10. data/bin/review +2 -4
  11. data/bin/review-catalog-converter +3 -3
  12. data/bin/review-check +6 -8
  13. data/bin/review-checkdep +1 -4
  14. data/bin/review-compile +10 -20
  15. data/bin/review-epub2html +1 -4
  16. data/bin/review-epubmaker +3 -4
  17. data/bin/review-idgxmlmaker +1 -3
  18. data/bin/review-index +11 -5
  19. data/bin/review-init +1 -4
  20. data/bin/review-pdfmaker +1 -3
  21. data/bin/review-preproc +30 -38
  22. data/bin/review-textmaker +1 -3
  23. data/bin/review-update +1 -4
  24. data/bin/review-validate +3 -3
  25. data/bin/review-vol +1 -4
  26. data/bin/review-webmaker +1 -3
  27. data/doc/config.yml.sample +23 -5
  28. data/doc/config.yml.sample-simple +1 -1
  29. data/doc/format.ja.md +49 -12
  30. data/doc/format.md +52 -12
  31. data/doc/quickstart.ja.md +11 -1
  32. data/doc/quickstart.md +11 -2
  33. data/doc/writing_vertical.ja.md +6 -0
  34. data/lib/review/book/base.rb +4 -0
  35. data/lib/review/book/book_unit.rb +15 -2
  36. data/lib/review/book/chapter.rb +3 -0
  37. data/lib/review/book/index.rb +5 -1
  38. data/lib/review/book/volume.rb +1 -0
  39. data/lib/review/builder.rb +90 -54
  40. data/lib/review/call_hook.rb +20 -0
  41. data/lib/review/catalog.rb +2 -0
  42. data/lib/review/compiler.rb +88 -52
  43. data/lib/review/configure.rb +64 -7
  44. data/lib/review/epubmaker/content.rb +113 -0
  45. data/lib/review/epubmaker/epubcommon.rb +372 -0
  46. data/lib/review/epubmaker/epubv2.rb +178 -0
  47. data/lib/review/epubmaker/epubv3.rb +231 -0
  48. data/lib/review/epubmaker/producer.rb +167 -0
  49. data/lib/review/epubmaker/reviewheaderlistener.rb +12 -2
  50. data/lib/review/epubmaker/zip_exporter.rb +84 -0
  51. data/lib/review/epubmaker.rb +114 -129
  52. data/lib/review/exception.rb +13 -0
  53. data/lib/review/htmlbuilder.rb +109 -67
  54. data/lib/review/htmlutils.rb +1 -1
  55. data/lib/review/i18n.rb +1 -0
  56. data/lib/review/i18n.yml +6 -0
  57. data/lib/review/idgxmlbuilder.rb +72 -48
  58. data/lib/review/idgxmlmaker.rb +15 -14
  59. data/lib/review/img_math.rb +239 -0
  60. data/lib/review/index_builder.rb +90 -32
  61. data/lib/review/init.rb +4 -4
  62. data/lib/review/latexbox.rb +58 -0
  63. data/lib/review/latexbuilder.rb +79 -58
  64. data/lib/review/latexutils.rb +9 -1
  65. data/lib/review/lineinput.rb +112 -2
  66. data/lib/review/loggable.rb +27 -0
  67. data/lib/review/logger.rb +89 -2
  68. data/lib/review/makerhelper.rb +7 -206
  69. data/lib/review/markdownbuilder.rb +44 -4
  70. data/lib/review/pdfmaker.rb +70 -51
  71. data/lib/review/plaintextbuilder.rb +20 -11
  72. data/lib/review/preprocessor/directive.rb +35 -0
  73. data/lib/review/preprocessor/line.rb +34 -0
  74. data/lib/review/preprocessor/repository.rb +177 -0
  75. data/lib/review/preprocessor.rb +105 -301
  76. data/lib/review/rstbuilder.rb +13 -4
  77. data/lib/review/sec_counter.rb +1 -0
  78. data/lib/review/template.rb +11 -1
  79. data/lib/review/textmaker.rb +23 -20
  80. data/lib/review/textutils.rb +10 -17
  81. data/lib/review/tocprinter.rb +93 -71
  82. data/lib/review/topbuilder.rb +44 -19
  83. data/lib/review/update.rb +5 -6
  84. data/lib/review/version.rb +1 -1
  85. data/lib/review/volumeprinter.rb +11 -12
  86. data/lib/review/webmaker.rb +31 -27
  87. data/lib/review/webtocprinter.rb +10 -9
  88. data/lib/review/yamlloader.rb +2 -1
  89. data/lib/review.rb +1 -1
  90. data/review.gemspec +5 -3
  91. data/samples/sample-book/src/config-epub2.yml +1 -1
  92. data/samples/sample-book/src/config.yml +1 -1
  93. data/samples/sample-book/src/lib/tasks/review.rake +19 -1
  94. data/samples/sample-book/src/lib/tasks/z01_copy_sty.rake +2 -1
  95. data/samples/syntax-book/ch01.re +1 -1
  96. data/samples/syntax-book/ch02.re +30 -6
  97. data/samples/syntax-book/ch03.re +1 -1
  98. data/samples/syntax-book/images/img3-2.png +0 -0
  99. data/samples/syntax-book/lib/tasks/z01_copy_sty.rake +2 -1
  100. data/templates/html/_colophon.html.erb +23 -0
  101. data/templates/html/_colophon_history.html.erb +9 -0
  102. data/templates/html/_cover.html.erb +10 -0
  103. data/templates/html/_part_body.html.erb +6 -0
  104. data/templates/html/_titlepage.html.erb +20 -0
  105. data/templates/html/layout-html5.html.erb +6 -0
  106. data/templates/html/layout-xhtml1.html.erb +6 -0
  107. data/templates/latex/config.erb +11 -0
  108. data/templates/latex/review-jlreq/review-base.sty +7 -9
  109. data/templates/latex/review-jlreq/review-jlreq.cls +48 -6
  110. data/templates/latex/review-jlreq/review-style.sty +6 -1
  111. data/templates/latex/review-jlreq/review-tcbox.sty +348 -0
  112. data/templates/latex/review-jlreq/reviewmacro.sty +5 -0
  113. data/templates/latex/review-jsbook/review-base.sty +13 -9
  114. data/templates/latex/review-jsbook/review-jsbook.cls +41 -6
  115. data/templates/latex/review-jsbook/review-style.sty +6 -1
  116. data/templates/latex/review-jsbook/review-tcbox.sty +348 -0
  117. data/templates/latex/review-jsbook/reviewmacro.sty +5 -0
  118. data/templates/opf/epubv2.opf.erb +7 -7
  119. data/templates/opf/epubv3.opf.erb +7 -7
  120. data/templates/opf/opf_manifest_epubv2.opf.erb +10 -0
  121. data/templates/opf/opf_manifest_epubv3.opf.erb +10 -0
  122. data/templates/opf/opf_metainfo_epubv2.opf.erb +17 -0
  123. data/templates/opf/opf_metainfo_epubv3.opf.erb +49 -0
  124. data/templates/opf/opf_tocx_epubv2.opf.erb +9 -0
  125. data/templates/opf/opf_tocx_epubv3.opf.erb +17 -0
  126. data/templates/web/html/layout-html5.html.erb +6 -5
  127. data/templates/web/html/layout-xhtml1.html.erb +6 -0
  128. data/test/assets/header_listener.html +35 -0
  129. data/test/assets/img_math/img1.png +0 -0
  130. data/test/assets/img_math/img2.png +0 -0
  131. data/test/assets/img_math/img3.png +0 -0
  132. data/test/assets/syntax_book_index_detail.txt +60 -0
  133. data/test/assets/test_template.tex +7 -1
  134. data/test/assets/test_template_backmatter.tex +7 -1
  135. data/test/run_test.rb +1 -1
  136. data/test/test_book_chapter.rb +27 -4
  137. data/test/test_builder.rb +10 -8
  138. data/test/test_catalog_converter_cmd.rb +1 -1
  139. data/test/test_epub3maker.rb +168 -124
  140. data/test/test_epubmaker.rb +248 -131
  141. data/test/test_epubmaker_cmd.rb +15 -4
  142. data/test/test_helper.rb +5 -4
  143. data/test/test_htmlbuilder.rb +170 -31
  144. data/test/test_idgxmlbuilder.rb +44 -23
  145. data/test/test_idgxmlmaker_cmd.rb +7 -3
  146. data/test/test_img_math.rb +111 -0
  147. data/test/test_index.rb +30 -4
  148. data/test/test_indexbuilder.rb +5 -5
  149. data/test/test_latexbuilder.rb +151 -26
  150. data/test/test_latexbuilder_v2.rb +18 -10
  151. data/test/test_lineinput.rb +20 -93
  152. data/test/test_markdownbuilder.rb +42 -0
  153. data/test/test_pdfmaker.rb +90 -0
  154. data/test/test_pdfmaker_cmd.rb +2 -2
  155. data/test/test_plaintextbuilder.rb +56 -40
  156. data/test/test_preprocessor.rb +188 -1
  157. data/test/test_reviewheaderlistener.rb +49 -0
  158. data/test/test_rstbuilder.rb +13 -0
  159. data/test/test_template.rb +12 -2
  160. data/test/test_textmaker_cmd.rb +5 -1
  161. data/test/test_tocprinter.rb +46 -0
  162. data/test/test_topbuilder.rb +50 -19
  163. data/test/test_update.rb +34 -34
  164. data/test/test_zip_exporter.rb +5 -6
  165. metadata +95 -17
  166. data/lib/epubmaker/content.rb +0 -111
  167. data/lib/epubmaker/epubcommon.rb +0 -449
  168. data/lib/epubmaker/epubv2.rb +0 -142
  169. data/lib/epubmaker/epubv3.rb +0 -235
  170. data/lib/epubmaker/producer.rb +0 -375
  171. data/lib/epubmaker/zip_exporter.rb +0 -81
  172. data/lib/epubmaker.rb +0 -23
  173. data/lib/lineinput.rb +0 -155
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2010-2020 Kenshi Muto and Masayoshi Takahashi
1
+ # Copyright (c) 2010-2021 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
@@ -13,47 +13,35 @@ require 'review/book'
13
13
  require 'review/configure'
14
14
  require 'review/converter'
15
15
  require 'review/latexbuilder'
16
- require 'review/yamlloader'
17
16
  require 'review/version'
18
17
  require 'review/htmltoc'
19
18
  require 'review/htmlbuilder'
19
+ require 'review/img_math'
20
20
 
21
21
  require 'rexml/document'
22
22
  require 'rexml/streamlistener'
23
- require 'epubmaker'
23
+ require 'review/call_hook'
24
+ require 'review/epubmaker/producer'
25
+ require 'review/epubmaker/content'
26
+ require 'review/epubmaker/epubv2'
27
+ require 'review/epubmaker/epubv3'
24
28
  require 'review/epubmaker/reviewheaderlistener'
25
29
  require 'review/makerhelper'
30
+ require 'review/loggable'
26
31
 
27
32
  module ReVIEW
28
33
  class EPUBMaker
29
- include ::EPUBMaker
30
- include REXML
31
34
  include MakerHelper
35
+ include Loggable
36
+ include ReVIEW::CallHook
32
37
 
33
38
  def initialize
34
39
  @producer = nil
35
40
  @htmltoc = nil
36
41
  @buildlogtxt = 'build-log.txt'
37
42
  @logger = ReVIEW.logger
38
- end
39
-
40
- def error(msg)
41
- @logger.error msg
42
- exit 1
43
- end
44
-
45
- def warn(msg)
46
- @logger.warn msg
47
- end
48
-
49
- def log(msg)
50
- @logger.debug(msg)
51
- end
52
-
53
- def load_yaml(yamlfile)
54
- @producer = Producer.new(@config)
55
- @producer.load(yamlfile)
56
- @config = @producer.config
43
+ @img_math = nil
44
+ @basedir = nil
57
45
  end
58
46
 
59
47
  def self.execute(*args)
@@ -85,22 +73,28 @@ module ReVIEW
85
73
 
86
74
  def execute(*args)
87
75
  cmd_config, yamlfile, exportfile = parse_opts(args)
88
- error "#{yamlfile} not found." unless File.exist?(yamlfile)
76
+ error! "#{yamlfile} not found." unless File.exist?(yamlfile)
89
77
 
90
78
  @config = ReVIEW::Configure.create(maker: 'epubmaker',
91
79
  yamlfile: yamlfile,
92
80
  config: cmd_config)
93
- load_yaml(yamlfile)
81
+ @producer = ReVIEW::EPUBMaker::Producer.new(@config)
94
82
  update_log_level
95
- log("Loaded yaml file (#{yamlfile}).")
83
+ debug("Loaded yaml file (#{yamlfile}).")
84
+ @basedir = File.absolute_path(File.dirname(yamlfile))
96
85
 
97
86
  produce(yamlfile, exportfile)
98
87
  end
99
88
 
100
89
  def update_log_level
101
90
  if @config['debug']
102
- @logger.level = Logger::DEBUG
103
- else
91
+ if @logger.ttylogger?
92
+ ReVIEW.logger = nil
93
+ @logger = ReVIEW.logger(level: 'debug')
94
+ else
95
+ @logger.level = Logger::DEBUG
96
+ end
97
+ elsif !@logger.ttylogger?
104
98
  @logger.level = Logger::INFO
105
99
  end
106
100
  end
@@ -123,43 +117,43 @@ module ReVIEW
123
117
  bookname ||= @config['bookname']
124
118
  booktmpname = "#{bookname}-epub"
125
119
 
120
+ @img_math = ReVIEW::ImgMath.new(@config)
126
121
  begin
127
- @config.check_version(ReVIEW::VERSION)
122
+ @config.check_version(ReVIEW::VERSION, exception: true)
128
123
  rescue ReVIEW::ConfigError => e
129
124
  warn e.message
130
125
  end
131
- log("#{bookname}.epub will be created.")
126
+ debug("#{bookname}.epub will be created.")
132
127
 
133
128
  FileUtils.rm_f("#{bookname}.epub")
134
129
  if @config['debug']
135
130
  FileUtils.rm_rf(booktmpname)
136
131
  end
137
132
 
138
- cleanup_mathimg
133
+ @img_math.cleanup_mathimg
139
134
 
140
135
  basetmpdir = build_path
141
136
  begin
142
- log("Created first temporary directory as #{basetmpdir}.")
137
+ debug("Created first temporary directory as #{basetmpdir}.")
143
138
 
144
- call_hook('hook_beforeprocess', basetmpdir)
139
+ call_hook('hook_beforeprocess', basetmpdir, base_dir: @basedir)
145
140
 
146
141
  @htmltoc = ReVIEW::HTMLToc.new(basetmpdir)
147
142
  ## copy all files into basetmpdir
148
143
  copy_stylesheet(basetmpdir)
149
144
 
150
145
  copy_frontmatter(basetmpdir)
151
- call_hook('hook_afterfrontmatter', basetmpdir)
146
+ call_hook('hook_afterfrontmatter', basetmpdir, base_dir: @basedir)
152
147
 
153
148
  build_body(basetmpdir, yamlfile)
154
- call_hook('hook_afterbody', basetmpdir)
149
+ call_hook('hook_afterbody', basetmpdir, base_dir: @basedir)
155
150
 
156
151
  copy_backmatter(basetmpdir)
157
152
 
158
- math_dir = "./#{@config['imagedir']}/_review_math"
159
- if @config['imgmath'] && File.exist?(File.join(math_dir, '__IMGMATH_BODY__.map'))
160
- make_math_images(math_dir)
153
+ if @config['math_format'] == 'imgmath'
154
+ @img_math.make_math_images
161
155
  end
162
- call_hook('hook_afterbackmatter', basetmpdir)
156
+ call_hook('hook_afterbackmatter', basetmpdir, base_dir: @basedir)
163
157
 
164
158
  ## push contents in basetmpdir into @producer
165
159
  push_contents(basetmpdir)
@@ -175,7 +169,7 @@ module ReVIEW
175
169
  copy_resources('adv', File.join(basetmpdir, @config['imagedir']))
176
170
  copy_resources(@config['fontdir'], File.join(basetmpdir, 'fonts'), @config['font_ext'])
177
171
 
178
- call_hook('hook_aftercopyimage', basetmpdir)
172
+ call_hook('hook_aftercopyimage', basetmpdir, base_dir: @basedir)
179
173
 
180
174
  @producer.import_imageinfo(File.join(basetmpdir, @config['imagedir']), basetmpdir)
181
175
  @producer.import_imageinfo(File.join(basetmpdir, 'fonts'), basetmpdir, @config['font_ext'])
@@ -187,35 +181,25 @@ module ReVIEW
187
181
  epubtmpdir = File.join(basetmpdir, booktmpname)
188
182
  Dir.mkdir(epubtmpdir)
189
183
  end
190
- log('Call ePUB producer.')
191
- @producer.produce("#{bookname}.epub", basetmpdir, epubtmpdir)
192
- log('Finished.')
184
+ debug('Call ePUB producer.')
185
+ @producer.produce("#{bookname}.epub", basetmpdir, epubtmpdir, base_dir: @basedir)
186
+ debug('Finished.')
187
+ @logger.success("built #{bookname}.epub")
193
188
  rescue ApplicationError => e
194
189
  raise if @config['debug']
195
- error(e.message)
190
+
191
+ error! e.message
196
192
  ensure
197
193
  FileUtils.remove_entry_secure(basetmpdir) unless @config['debug']
198
194
  end
199
195
  end
200
196
 
201
- def call_hook(hook_name, *params)
202
- filename = @config['epubmaker'][hook_name]
203
- log("Call #{hook_name}. (#{filename})")
204
- if filename.present? && File.exist?(filename) && FileTest.executable?(filename)
205
- if ENV['REVIEW_SAFE_MODE'].to_i & 1 > 0
206
- warn 'hook is prohibited in safe mode. ignored.'
207
- else
208
- system(filename, *params)
209
- end
210
- end
211
- end
212
-
213
197
  def verify_target_images(basetmpdir)
214
198
  @producer.contents.each do |content|
215
199
  case content.media
216
200
  when 'application/xhtml+xml'
217
201
  File.open("#{basetmpdir}/#{content.file}") do |f|
218
- Document.new(File.new(f)).each_element('//img') do |e|
202
+ REXML::Document.new(File.new(f)).each_element('//img') do |e|
219
203
  @config['epubmaker']['force_include_images'].push(e.attributes['src'])
220
204
  if e.attributes['src'] =~ /svg\Z/i
221
205
  content.properties.push('svg')
@@ -237,6 +221,7 @@ module ReVIEW
237
221
 
238
222
  def copy_images(resdir, destdir, allow_exts = nil)
239
223
  return nil unless File.exist?(resdir)
224
+
240
225
  allow_exts ||= @config['image_ext']
241
226
  FileUtils.mkdir_p(destdir)
242
227
  if @config['epubmaker']['verify_target_images'].present?
@@ -249,8 +234,8 @@ module ReVIEW
249
234
  end
250
235
  basedir = File.dirname(file)
251
236
  FileUtils.mkdir_p(File.join(destdir, basedir))
252
- log("Copy #{file} to the temporary directory.")
253
- FileUtils.cp(file, File.join(destdir, basedir))
237
+ debug("Copy #{file} to the temporary directory.")
238
+ FileUtils.cp(file, File.join(destdir, basedir), preserve: true)
254
239
  end
255
240
  else
256
241
  recursive_copy_files(resdir, destdir, allow_exts)
@@ -259,6 +244,7 @@ module ReVIEW
259
244
 
260
245
  def copy_resources(resdir, destdir, allow_exts = nil)
261
246
  return nil unless File.exist?(resdir)
247
+
262
248
  allow_exts ||= @config['image_ext']
263
249
  FileUtils.mkdir_p(destdir)
264
250
  recursive_copy_files(resdir, destdir, allow_exts)
@@ -268,12 +254,13 @@ module ReVIEW
268
254
  Dir.open(resdir) do |dir|
269
255
  dir.each do |fname|
270
256
  next if fname.start_with?('.')
257
+
271
258
  if FileTest.directory?(File.join(resdir, fname))
272
259
  recursive_copy_files(File.join(resdir, fname), File.join(destdir, fname), allow_exts)
273
260
  elsif fname =~ /\.(#{allow_exts.join('|')})\Z/i
274
261
  FileUtils.mkdir_p(destdir)
275
- log("Copy #{resdir}/#{fname} to the temporary directory.")
276
- FileUtils.cp(File.join(resdir, fname), destdir)
262
+ debug("Copy #{resdir}/#{fname} to the temporary directory.")
263
+ FileUtils.cp(File.join(resdir, fname), destdir, preserve: true)
277
264
  end
278
265
  end
279
266
  end
@@ -282,8 +269,7 @@ module ReVIEW
282
269
  def check_compile_status
283
270
  return unless @compile_errors
284
271
 
285
- $stderr.puts 'compile error, No EPUB file output.'
286
- exit 1
272
+ error! 'compile error, No EPUB file output.'
287
273
  end
288
274
 
289
275
  def build_body(basetmpdir, yamlfile)
@@ -298,7 +284,7 @@ module ReVIEW
298
284
  basedir = File.dirname(yamlfile)
299
285
  base_path = Pathname.new(basedir)
300
286
  book = ReVIEW::Book::Base.new(basedir, config: @config)
301
- @converter = ReVIEW::Converter.new(book, ReVIEW::HTMLBuilder.new)
287
+ @converter = ReVIEW::Converter.new(book, ReVIEW::HTMLBuilder.new(img_math: @img_math))
302
288
  @compile_errors = nil
303
289
 
304
290
  book.parts.each do |part|
@@ -325,21 +311,15 @@ module ReVIEW
325
311
  end
326
312
 
327
313
  def build_part(part, basetmpdir, htmlfile)
328
- log("Create #{htmlfile} from a template.")
314
+ debug("Create #{htmlfile} from a template.")
329
315
  File.open(File.join(basetmpdir, htmlfile), 'w') do |f|
330
- @body = ''
331
- @body << %Q(<div class="part">\n)
332
- @body << %Q(<h1 class="part-number">#{h(ReVIEW::I18n.t('part', part.number))}</h1>\n)
333
- if part.name.strip.present?
334
- @body << %Q(<h2 class="part-title">#{h(part.name.strip)}</h2>\n)
335
- end
336
- @body << %Q(</div>\n)
316
+ @part_number = part.number
317
+ @part_title = part.name.strip
318
+ @body = ReVIEW::Template.generate(path: 'html/_part_body.html.erb', binding: binding)
337
319
 
338
320
  @language = @producer.config['language']
339
321
  @stylesheets = @producer.config['stylesheet']
340
- tmplfile = File.expand_path(template_name, ReVIEW::Template::TEMPLATE_DIR)
341
- tmpl = ReVIEW::Template.load(tmplfile)
342
- f.write tmpl.result(binding)
322
+ f.write ReVIEW::Template.generate(path: template_name, binding: binding)
343
323
  end
344
324
  end
345
325
 
@@ -392,7 +372,7 @@ module ReVIEW
392
372
 
393
373
  htmlfile = "#{id}.#{@config['htmlext']}"
394
374
  write_buildlogtxt(basetmpdir, htmlfile, filename)
395
- log("Create #{htmlfile} from #{filename}.")
375
+ debug("Create #{htmlfile} from #{filename}.")
396
376
 
397
377
  if @config['params'].present?
398
378
  warn %Q('params:' in config.yml is obsoleted.)
@@ -406,8 +386,8 @@ module ReVIEW
406
386
  remove_hidden_title(basetmpdir, htmlfile)
407
387
  rescue => e
408
388
  @compile_errors = true
409
- warn "compile error in #{filename} (#{e.class})"
410
- warn e.message
389
+ error "compile error in #{filename} (#{e.class})"
390
+ error e.message
411
391
  end
412
392
  end
413
393
 
@@ -436,12 +416,19 @@ module ReVIEW
436
416
  properties
437
417
  end
438
418
 
439
- def write_info_body(basetmpdir, _id, filename, ispart = nil, chaptype = nil)
419
+ def parse_headlines(path)
440
420
  headlines = []
421
+
422
+ File.open(path) do |htmlio|
423
+ REXML::Document.parse_stream(htmlio, ReVIEWHeaderListener.new(headlines))
424
+ end
425
+
426
+ headlines
427
+ end
428
+
429
+ def write_info_body(basetmpdir, _id, filename, ispart = nil, chaptype = nil)
441
430
  path = File.join(basetmpdir, filename)
442
- htmlio = File.new(path)
443
- Document.parse_stream(htmlio, ReVIEWHeaderListener.new(headlines))
444
- htmlio.close
431
+ headlines = parse_headlines(path)
445
432
 
446
433
  if headlines.empty?
447
434
  warn "#{filename} is discarded because there is no heading. Use `=[notoc]' or `=[nodisp]' to exclude headlines from the table of contents."
@@ -480,70 +467,77 @@ module ReVIEW
480
467
  def push_contents(_basetmpdir)
481
468
  @htmltoc.each_item do |level, file, title, args|
482
469
  next if level.to_i > @config['toclevel'] && args[:force_include].nil?
483
- log("Push #{file} to ePUB contents.")
484
470
 
485
- hash = { 'file' => file,
486
- 'level' => level.to_i,
487
- 'title' => title,
488
- 'chaptype' => args[:chaptype] }
471
+ debug("Push #{file} to ePUB contents.")
472
+
473
+ params = { file: file,
474
+ level: level.to_i,
475
+ title: title,
476
+ chaptype: args[:chaptype] }
489
477
  if args[:id].present?
490
- hash['id'] = args[:id]
478
+ params[:id] = args[:id]
491
479
  end
492
480
  if args[:properties].present?
493
- hash['properties'] = args[:properties].split(' ')
481
+ params[:properties] = args[:properties].split(' ') # rubocop:disable Style/RedundantArgument
494
482
  end
495
483
  if args[:notoc].present?
496
- hash['notoc'] = args[:notoc]
484
+ params[:notoc] = args[:notoc]
497
485
  end
498
- @producer.contents.push(Content.new(hash))
486
+ @producer.contents.push(ReVIEW::EPUBMaker::Content.new(**params))
499
487
  end
500
488
  end
501
489
 
502
490
  def copy_stylesheet(basetmpdir)
503
491
  return if @config['stylesheet'].empty?
492
+
504
493
  @config['stylesheet'].each do |sfile|
505
494
  unless File.exist?(sfile)
506
- error "#{sfile} is not found."
495
+ error! "stylesheet: #{sfile} is not found."
507
496
  end
508
- FileUtils.cp(sfile, basetmpdir)
509
- @producer.contents.push(Content.new('file' => sfile))
497
+ FileUtils.cp(sfile, basetmpdir, preserve: true)
498
+ @producer.contents.push(ReVIEW::EPUBMaker::Content.new(file: sfile))
499
+ end
500
+ end
501
+
502
+ def copy_static_file(configname, destdir, destfilename: nil)
503
+ destfilename ||= @config[configname]
504
+ unless File.exist?(@config[configname])
505
+ error! "#{configname}: #{@config[configname]} is not found."
510
506
  end
507
+ FileUtils.cp(@config[configname],
508
+ File.join(destdir, destfilename), preserve: true)
511
509
  end
512
510
 
513
511
  def copy_frontmatter(basetmpdir)
514
512
  if @config['cover'].present? && File.exist?(@config['cover'])
515
- FileUtils.cp(@config['cover'],
516
- File.join(basetmpdir, File.basename(@config['cover'])))
513
+ copy_static_file('cover', basetmpdir)
517
514
  end
518
515
 
519
516
  if @config['titlepage']
520
517
  if @config['titlefile'].nil?
521
518
  build_titlepage(basetmpdir, "titlepage.#{@config['htmlext']}")
522
519
  else
523
- FileUtils.cp(@config['titlefile'],
524
- File.join(basetmpdir, "titlepage.#{@config['htmlext']}"))
520
+ copy_static_file('titlefile', basetmpdir, destfilename: "titlepage.#{@config['htmlext']}")
525
521
  end
526
522
  @htmltoc.add_item(1,
527
523
  "titlepage.#{@config['htmlext']}",
528
- @producer.res.v('titlepagetitle'),
524
+ ReVIEW::I18n.t('titlepagetitle'),
529
525
  chaptype: 'pre')
530
526
  end
531
527
 
532
- if @config['originaltitlefile'].present? && File.exist?(@config['originaltitlefile'])
533
- FileUtils.cp(@config['originaltitlefile'],
534
- File.join(basetmpdir, File.basename(@config['originaltitlefile'])))
528
+ if @config['originaltitlefile'].present?
529
+ copy_static_file('originaltitlefile', basetmpdir)
535
530
  @htmltoc.add_item(1,
536
531
  File.basename(@config['originaltitlefile']),
537
- @producer.res.v('originaltitle'),
532
+ ReVIEW::I18n.t('originaltitle'),
538
533
  chaptype: 'pre')
539
534
  end
540
535
 
541
- if @config['creditfile'].present? && File.exist?(@config['creditfile'])
542
- FileUtils.cp(@config['creditfile'],
543
- File.join(basetmpdir, File.basename(@config['creditfile'])))
536
+ if @config['creditfile'].present?
537
+ copy_static_file('creditfile', basetmpdir)
544
538
  @htmltoc.add_item(1,
545
539
  File.basename(@config['creditfile']),
546
- @producer.res.v('credittitle'),
540
+ ReVIEW::I18n.t('credittitle'),
547
541
  chaptype: 'pre')
548
542
  end
549
543
 
@@ -570,53 +564,42 @@ module ReVIEW
570
564
 
571
565
  @language = @producer.config['language']
572
566
  @stylesheets = @producer.config['stylesheet']
573
- tmplfile = File.expand_path(template_name, ReVIEW::Template::TEMPLATE_DIR)
574
- tmpl = ReVIEW::Template.load(tmplfile)
575
- f.write tmpl.result(binding)
567
+ f.write ReVIEW::Template.generate(path: template_name, binding: binding)
576
568
  end
577
569
  end
578
570
 
579
571
  def copy_backmatter(basetmpdir)
580
572
  if @config['profile']
581
- FileUtils.cp(@config['profile'],
582
- File.join(basetmpdir, File.basename(@config['profile'])))
573
+ copy_static_file('profile', basetmpdir)
583
574
  @htmltoc.add_item(1,
584
575
  File.basename(@config['profile']),
585
- @producer.res.v('profiletitle'),
576
+ ReVIEW::I18n.t('profiletitle'),
586
577
  chaptype: 'post')
587
578
  end
588
579
 
589
580
  if @config['advfile']
590
- FileUtils.cp(@config['advfile'],
591
- File.join(basetmpdir, File.basename(@config['advfile'])))
581
+ copy_static_file('advfile', basetmpdir)
592
582
  @htmltoc.add_item(1,
593
583
  File.basename(@config['advfile']),
594
- @producer.res.v('advtitle'),
584
+ ReVIEW::I18n.t('advtitle'),
595
585
  chaptype: 'post')
596
586
  end
597
587
 
598
588
  if @config['colophon']
599
- if @config['colophon'].is_a?(String) # FIXME: should let obsolete this style?
600
- FileUtils.cp(@config['colophon'],
601
- File.join(basetmpdir, "colophon.#{@config['htmlext']}"))
602
- else
603
- filename = File.join(basetmpdir, "colophon.#{@config['htmlext']}")
604
- File.open(filename, 'w') do |f|
605
- @producer.colophon(f)
606
- end
589
+ if @config['colophon'].is_a?(String)
590
+ copy_static_file('colophon', basetmpdir, destfilename: "colophon.#{@config['htmlext']}") # override pre-built colophon
607
591
  end
608
592
  @htmltoc.add_item(1,
609
593
  "colophon.#{@config['htmlext']}",
610
- @producer.res.v('colophontitle'),
594
+ ReVIEW::I18n.t('colophontitle'),
611
595
  chaptype: 'post')
612
596
  end
613
597
 
614
598
  if @config['backcover']
615
- FileUtils.cp(@config['backcover'],
616
- File.join(basetmpdir, File.basename(@config['backcover'])))
599
+ copy_static_file('backcover', basetmpdir)
617
600
  @htmltoc.add_item(1,
618
601
  File.basename(@config['backcover']),
619
- @producer.res.v('backcovertitle'),
602
+ ReVIEW::I18n.t('backcovertitle'),
620
603
  chaptype: 'post')
621
604
  end
622
605
 
@@ -642,8 +625,10 @@ module ReVIEW
642
625
  extre = Regexp.new(pat, Regexp::IGNORECASE)
643
626
  Find.find(basetmpdir) do |fname|
644
627
  next unless fname.match(extre)
628
+
645
629
  img = ImageSize.path(fname)
646
630
  next if img.width.nil? || img.width * img.height <= maxpixels
631
+
647
632
  h = Math.sqrt(img.height * maxpixels / img.width)
648
633
  w = maxpixels / h
649
634
  fname.sub!("#{basetmpdir}/", '')
@@ -10,10 +10,23 @@
10
10
 
11
11
  module ReVIEW
12
12
  class Error < ::StandardError; end
13
+
13
14
  class ApplicationError < Error; end
15
+
14
16
  class ConfigError < ApplicationError; end
17
+
15
18
  class CompileError < ApplicationError; end
19
+
16
20
  class SyntaxError < CompileError; end
21
+
17
22
  class FileNotFound < ApplicationError; end
23
+
18
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
19
32
  end