review 5.1.1 → 5.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby-tex.yml +6 -2
- data/.github/workflows/ruby-win.yml +6 -2
- data/.github/workflows/ruby.yml +6 -2
- data/.rubocop.yml +5 -319
- data/NEWS.ja.md +149 -0
- data/NEWS.md +149 -1
- data/README.md +9 -8
- data/bin/review +1 -1
- data/bin/review-catalog-converter +15 -15
- data/bin/review-check +7 -7
- data/bin/review-compile +14 -23
- data/bin/review-index +1 -1
- data/bin/review-preproc +29 -35
- data/bin/review-validate +2 -2
- data/doc/config.yml.sample +9 -1
- data/doc/config.yml.sample-simple +1 -1
- data/doc/format.ja.md +29 -3
- data/doc/format.md +32 -3
- data/doc/writing_vertical.ja.md +6 -0
- data/lib/review/book/base.rb +3 -3
- data/lib/review/book/book_unit.rb +13 -3
- data/lib/review/book/chapter.rb +1 -1
- data/lib/review/book/index.rb +7 -4
- data/lib/review/book/part.rb +12 -13
- data/lib/review/book/volume.rb +1 -1
- data/lib/review/builder.rb +92 -65
- data/lib/review/catalog.rb +6 -5
- data/lib/review/compiler.rb +76 -57
- data/lib/review/configure.rb +5 -2
- data/lib/review/epub2html.rb +12 -12
- data/lib/review/epubmaker/content.rb +1 -1
- data/lib/review/epubmaker/epubcommon.rb +47 -45
- data/lib/review/epubmaker/epubv2.rb +2 -1
- data/lib/review/epubmaker/epubv3.rb +5 -4
- data/lib/review/epubmaker/producer.rb +6 -7
- data/lib/review/epubmaker/reviewheaderlistener.rb +1 -1
- data/lib/review/epubmaker.rb +56 -67
- data/lib/review/exception.rb +7 -0
- data/lib/review/extentions/string.rb +1 -1
- data/lib/review/htmlbuilder.rb +90 -34
- data/lib/review/htmlutils.rb +17 -17
- data/lib/review/i18n.rb +3 -3
- data/lib/review/i18n.yml +6 -0
- data/lib/review/idgxmlbuilder.rb +61 -39
- data/lib/review/idgxmlmaker.rb +27 -26
- data/lib/review/img_math.rb +12 -18
- data/lib/review/index_builder.rb +94 -53
- data/lib/review/init.rb +4 -4
- data/lib/review/latexbuilder.rb +84 -76
- data/lib/review/lineinput.rb +3 -3
- data/lib/review/location.rb +1 -1
- data/lib/review/loggable.rb +27 -0
- data/lib/review/logger.rb +69 -21
- data/lib/review/makerhelper.rb +8 -4
- data/lib/review/markdownbuilder.rb +21 -12
- data/lib/review/pdfmaker.rb +63 -42
- data/lib/review/plaintextbuilder.rb +16 -15
- data/lib/review/preprocessor/directive.rb +35 -0
- data/lib/review/preprocessor/line.rb +34 -0
- data/lib/review/preprocessor/repository.rb +177 -0
- data/lib/review/preprocessor.rb +94 -296
- data/lib/review/rstbuilder.rb +12 -3
- data/lib/review/template.rb +5 -1
- data/lib/review/textmaker.rb +32 -31
- data/lib/review/textutils.rb +5 -6
- data/lib/review/tocprinter.rb +12 -7
- data/lib/review/topbuilder.rb +96 -19
- data/lib/review/update.rb +16 -8
- data/lib/review/version.rb +1 -1
- data/lib/review/volumeprinter.rb +9 -9
- data/lib/review/webmaker.rb +45 -46
- data/lib/review/webtocprinter.rb +10 -10
- data/lib/review/yamlloader.rb +35 -2
- data/review.gemspec +2 -1
- data/samples/sample-book/src/config.yml +0 -1
- data/samples/sample-book/src/lib/tasks/review.rake +3 -1
- data/samples/sample-book/src/lib/tasks/z01_copy_sty.rake +2 -1
- data/samples/syntax-book/ch02.re +9 -0
- data/samples/syntax-book/lib/tasks/z01_copy_sty.rake +2 -1
- data/templates/html/_titlepage.html.erb +9 -17
- data/templates/latex/config.erb +3 -0
- data/templates/latex/review-jlreq/review-base.sty +4 -5
- data/templates/latex/review-jlreq/review-jlreq.cls +39 -5
- data/templates/latex/review-jsbook/review-base.sty +9 -3
- data/templates/latex/review-jsbook/review-jsbook.cls +32 -5
- data/templates/opf/opf_manifest_epubv2.opf.erb +1 -1
- data/templates/opf/opf_manifest_epubv3.opf.erb +1 -1
- data/test/assets/syntax_book_index_detail.txt +10 -8
- data/test/assets/test_template.tex +4 -1
- data/test/assets/test_template_backmatter.tex +4 -1
- data/test/book_test_helper.rb +10 -10
- data/test/test_book_chapter.rb +25 -2
- data/test/test_builder.rb +10 -8
- data/test/test_epub3maker.rb +3 -3
- data/test/test_epubmaker.rb +27 -37
- data/test/test_epubmaker_cmd.rb +14 -3
- data/test/test_htmlbuilder.rb +111 -31
- data/test/test_idgxmlbuilder.rb +41 -33
- data/test/test_idgxmlmaker_cmd.rb +1 -1
- data/test/test_img_math.rb +11 -2
- data/test/test_index.rb +30 -4
- data/test/test_latexbuilder.rb +46 -25
- data/test/test_latexbuilder_v2.rb +18 -10
- data/test/test_markdownbuilder.rb +13 -0
- data/test/test_pdfmaker.rb +19 -0
- data/test/test_pdfmaker_cmd.rb +10 -10
- data/test/test_plaintextbuilder.rb +46 -22
- data/test/test_preprocessor.rb +188 -1
- data/test/test_rstbuilder.rb +13 -0
- data/test/test_textmaker_cmd.rb +1 -1
- data/test/test_topbuilder.rb +195 -29
- data/test/test_yamlloader.rb +28 -42
- metadata +11 -6
@@ -1,6 +1,6 @@
|
|
1
1
|
# = epubv3.rb -- EPUB version 3 producer.
|
2
2
|
#
|
3
|
-
# Copyright (c) 2010-
|
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
|
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:
|
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:
|
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
|
94
|
-
path.chop! if
|
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
|
-
|
118
|
+
unless epubfile.start_with?('/')
|
120
119
|
epubfile = "#{current}/#{epubfile}"
|
121
120
|
end
|
122
121
|
|
data/lib/review/epubmaker.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2010-
|
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
|
-
|
90
|
-
|
91
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
188
|
+
debug('Call ePUB producer.')
|
196
189
|
@producer.produce("#{bookname}.epub", basetmpdir, epubtmpdir, base_dir: @basedir)
|
197
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
264
|
+
elsif /\.(#{allow_exts.join('|')})\Z/i.match?(fname)
|
272
265
|
FileUtils.mkdir_p(destdir)
|
273
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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']
|
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
|
-
|
402
|
-
|
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
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
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
|
-
|
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
|
data/lib/review/exception.rb
CHANGED
data/lib/review/htmlbuilder.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2008-
|
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
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
926
|
-
else
|
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
|
-
|
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
|
-
|
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
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
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
|
-
|
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
|
-
|
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
|