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
data/lib/review/compiler.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2009-
|
1
|
+
# Copyright (c) 2009-2021 Minero Aoki, Kenshi Muto
|
2
2
|
# Copyright (c) 2002-2007 Minero Aoki
|
3
3
|
#
|
4
4
|
# This program is free software.
|
@@ -10,10 +10,13 @@ require 'review/extentions'
|
|
10
10
|
require 'review/preprocessor'
|
11
11
|
require 'review/exception'
|
12
12
|
require 'review/location'
|
13
|
+
require 'review/loggable'
|
13
14
|
require 'strscan'
|
14
15
|
|
15
16
|
module ReVIEW
|
16
17
|
class Compiler
|
18
|
+
include Loggable
|
19
|
+
|
17
20
|
MAX_HEADLINE_LEVEL = 6
|
18
21
|
|
19
22
|
def initialize(builder)
|
@@ -24,6 +27,12 @@ module ReVIEW
|
|
24
27
|
|
25
28
|
## to decide escaping/non-escaping for text
|
26
29
|
@command_name_stack = []
|
30
|
+
|
31
|
+
@logger = ReVIEW.logger
|
32
|
+
|
33
|
+
@ignore_errors = builder.is_a?(ReVIEW::IndexBuilder)
|
34
|
+
|
35
|
+
@compile_errors = nil
|
27
36
|
end
|
28
37
|
|
29
38
|
attr_reader :builder, :previous_list_type
|
@@ -35,7 +44,7 @@ module ReVIEW
|
|
35
44
|
|
36
45
|
def non_escaped_commands
|
37
46
|
if @builder.highlight?
|
38
|
-
%i[list emlist listnum emlistnum cmd]
|
47
|
+
%i[list emlist listnum emlistnum cmd source]
|
39
48
|
else
|
40
49
|
[]
|
41
50
|
end
|
@@ -44,6 +53,10 @@ module ReVIEW
|
|
44
53
|
def compile(chap)
|
45
54
|
@chapter = chap
|
46
55
|
do_compile
|
56
|
+
if @compile_errors
|
57
|
+
raise ApplicationError, "#{location.filename} cannot be compiled."
|
58
|
+
end
|
59
|
+
|
47
60
|
@builder.result
|
48
61
|
end
|
49
62
|
|
@@ -58,7 +71,7 @@ module ReVIEW
|
|
58
71
|
attr_reader :name
|
59
72
|
|
60
73
|
def check_args(args)
|
61
|
-
unless @argc_spec === args.size
|
74
|
+
unless @argc_spec === args.size # rubocop:disable Style/CaseEquality
|
62
75
|
raise CompileError, "wrong # of parameters (block command //#{@name}, expect #{@argc_spec} but #{args.size})"
|
63
76
|
end
|
64
77
|
|
@@ -183,6 +196,8 @@ module ReVIEW
|
|
183
196
|
defminicolumn :notice, 0..1
|
184
197
|
|
185
198
|
defsingle :footnote, 2
|
199
|
+
defsingle :endnote, 2
|
200
|
+
defsingle :printendnotes, 0
|
186
201
|
defsingle :noindent, 0
|
187
202
|
defsingle :blankline, 0
|
188
203
|
defsingle :pagebreak, 0
|
@@ -207,6 +222,7 @@ module ReVIEW
|
|
207
222
|
definline :table
|
208
223
|
definline :eq
|
209
224
|
definline :fn
|
225
|
+
definline :endnote
|
210
226
|
definline :kw
|
211
227
|
definline :ruby
|
212
228
|
definline :bou
|
@@ -261,6 +277,7 @@ module ReVIEW
|
|
261
277
|
def do_compile
|
262
278
|
f = LineInput.new(StringIO.new(@chapter.content))
|
263
279
|
@builder.bind(self, @chapter, Location.new(@chapter.basename, f))
|
280
|
+
@previous_list_type = nil
|
264
281
|
|
265
282
|
## in minicolumn, such as note/info/alert...
|
266
283
|
@minicolumn_name = nil
|
@@ -272,27 +289,27 @@ module ReVIEW
|
|
272
289
|
f.gets # Nothing to do
|
273
290
|
when /\A=+[\[\s{]/
|
274
291
|
compile_headline(f.gets)
|
275
|
-
@
|
292
|
+
@previous_list_type = nil
|
276
293
|
when /\A\s+\*/
|
277
294
|
compile_ulist(f)
|
278
|
-
@
|
295
|
+
@previous_list_type = 'ul'
|
279
296
|
when /\A\s+\d+\./
|
280
297
|
compile_olist(f)
|
281
|
-
@
|
298
|
+
@previous_list_type = 'ol'
|
282
299
|
when /\A\s+:\s/
|
283
300
|
compile_dlist(f)
|
284
|
-
@
|
301
|
+
@previous_list_type = 'dl'
|
285
302
|
when /\A\s*:\s/
|
286
|
-
warn 'Definition list starting with `:` is deprecated. It should start with ` : `.'
|
303
|
+
warn 'Definition list starting with `:` is deprecated. It should start with ` : `.', location: location
|
287
304
|
compile_dlist(f)
|
288
|
-
@
|
305
|
+
@previous_list_type = 'dl'
|
289
306
|
when %r{\A//\}}
|
290
307
|
if in_minicolumn?
|
291
308
|
_line = f.gets
|
292
309
|
compile_minicolumn_end
|
293
310
|
else
|
294
311
|
f.gets
|
295
|
-
error 'block end seen but not opened'
|
312
|
+
error 'block end seen but not opened', location: location
|
296
313
|
end
|
297
314
|
when %r{\A//[a-z]+}
|
298
315
|
line = f.peek
|
@@ -307,45 +324,44 @@ module ReVIEW
|
|
307
324
|
name, args, lines = read_command(f)
|
308
325
|
syntax = syntax_descriptor(name)
|
309
326
|
unless syntax
|
310
|
-
error "unknown command: //#{name}"
|
311
|
-
compile_unknown_command(args, lines)
|
327
|
+
error "unknown command: //#{name}", location: location
|
312
328
|
@command_name_stack.pop
|
313
329
|
next
|
314
330
|
end
|
315
331
|
compile_command(syntax, args, lines)
|
316
332
|
@command_name_stack.pop
|
317
333
|
end
|
318
|
-
@
|
334
|
+
@previous_list_type = nil
|
319
335
|
when %r{\A//}
|
320
336
|
line = f.gets
|
321
|
-
warn "`//' seen but is not valid command: #{line.strip.inspect}"
|
337
|
+
warn "`//' seen but is not valid command: #{line.strip.inspect}", location: location
|
322
338
|
if block_open?(line)
|
323
|
-
warn 'skipping block...'
|
339
|
+
warn 'skipping block...', location: location
|
324
340
|
read_block(f, false)
|
325
341
|
end
|
326
|
-
@
|
342
|
+
@previous_list_type = nil
|
327
343
|
else
|
328
344
|
if f.peek.strip.empty?
|
329
345
|
f.gets
|
330
346
|
next
|
331
347
|
end
|
332
348
|
compile_paragraph(f)
|
333
|
-
@
|
349
|
+
@previous_list_type = nil
|
334
350
|
end
|
335
351
|
end
|
336
352
|
close_all_tagged_section
|
337
353
|
rescue SyntaxError => e
|
338
|
-
error e
|
354
|
+
error e, location: location
|
339
355
|
end
|
340
356
|
|
341
357
|
def compile_minicolumn_begin(name, caption = nil)
|
342
358
|
mid = "#{name}_begin"
|
343
359
|
unless @builder.respond_to?(mid)
|
344
|
-
error "strategy does not support minicolumn: #{name}"
|
360
|
+
error "strategy does not support minicolumn: #{name}", location: location
|
345
361
|
end
|
346
362
|
|
347
363
|
if @minicolumn_name
|
348
|
-
error "minicolumn cannot be nested: #{name}"
|
364
|
+
error "minicolumn cannot be nested: #{name}", location: location
|
349
365
|
return
|
350
366
|
end
|
351
367
|
@minicolumn_name = name
|
@@ -355,7 +371,7 @@ module ReVIEW
|
|
355
371
|
|
356
372
|
def compile_minicolumn_end
|
357
373
|
unless @minicolumn_name
|
358
|
-
error "minicolumn is not used: #{name}"
|
374
|
+
error "minicolumn is not used: #{name}", location: location
|
359
375
|
return
|
360
376
|
end
|
361
377
|
name = @minicolumn_name
|
@@ -382,19 +398,19 @@ module ReVIEW
|
|
382
398
|
open_tag = tag[1..-1]
|
383
399
|
prev_tag_info = @tagged_section.pop
|
384
400
|
if prev_tag_info.nil? || prev_tag_info.first != open_tag
|
385
|
-
error "#{open_tag} is not opened."
|
401
|
+
error "#{open_tag} is not opened.", location: location
|
386
402
|
end
|
387
403
|
close_tagged_section(*prev_tag_info)
|
388
404
|
else
|
389
405
|
if caption.empty?
|
390
|
-
warn 'headline is empty.'
|
406
|
+
warn 'headline is empty.', location: location
|
391
407
|
end
|
392
408
|
close_current_tagged_section(level)
|
393
409
|
open_tagged_section(tag, level, label, caption)
|
394
410
|
end
|
395
411
|
else
|
396
412
|
if caption.empty?
|
397
|
-
warn 'headline is empty.'
|
413
|
+
warn 'headline is empty.', location: location
|
398
414
|
end
|
399
415
|
if @headline_indexs.size > (index + 1)
|
400
416
|
@headline_indexs = @headline_indexs[0..index]
|
@@ -425,7 +441,7 @@ module ReVIEW
|
|
425
441
|
def open_tagged_section(tag, level, label, caption)
|
426
442
|
mid = "#{tag}_begin"
|
427
443
|
unless @builder.respond_to?(mid)
|
428
|
-
error "builder does not support tagged section: #{tag}"
|
444
|
+
error "builder does not support tagged section: #{tag}", location: location
|
429
445
|
headline(level, label, caption)
|
430
446
|
return
|
431
447
|
end
|
@@ -438,7 +454,7 @@ module ReVIEW
|
|
438
454
|
if @builder.respond_to?(mid)
|
439
455
|
@builder.__send__(mid, level)
|
440
456
|
else
|
441
|
-
error "builder does not support block op: #{mid}"
|
457
|
+
error "builder does not support block op: #{mid}", location: location
|
442
458
|
end
|
443
459
|
end
|
444
460
|
|
@@ -451,7 +467,7 @@ module ReVIEW
|
|
451
467
|
def compile_ulist(f)
|
452
468
|
level = 0
|
453
469
|
f.while_match(/\A\s+\*|\A\#@/) do |line|
|
454
|
-
next if
|
470
|
+
next if /\A\#@/.match?(line)
|
455
471
|
|
456
472
|
buf = [text(line.sub(/\*+/, '').strip)]
|
457
473
|
f.while_match(/\A\s+(?!\*)\S/) do |cont|
|
@@ -467,7 +483,7 @@ module ReVIEW
|
|
467
483
|
elsif level < current_level # down
|
468
484
|
level_diff = current_level - level
|
469
485
|
if level_diff != 1
|
470
|
-
error 'too many *.'
|
486
|
+
error 'too many *.', location: location
|
471
487
|
end
|
472
488
|
level = current_level
|
473
489
|
@builder.ul_begin { level }
|
@@ -494,7 +510,7 @@ module ReVIEW
|
|
494
510
|
def compile_olist(f)
|
495
511
|
@builder.ol_begin
|
496
512
|
f.while_match(/\A\s+\d+\.|\A\#@/) do |line|
|
497
|
-
next if
|
513
|
+
next if /\A\#@/.match?(line)
|
498
514
|
|
499
515
|
num = line.match(/(\d+)\./)[1]
|
500
516
|
buf = [text(line.sub(/\d+\./, '').strip)]
|
@@ -556,12 +572,12 @@ module ReVIEW
|
|
556
572
|
f.until_match(%r{\A//\}}) do |line|
|
557
573
|
if ignore_inline
|
558
574
|
buf.push(line.chomp)
|
559
|
-
elsif line
|
575
|
+
elsif !/\A\#@/.match?(line)
|
560
576
|
buf.push(text(line.rstrip, true))
|
561
577
|
end
|
562
578
|
end
|
563
579
|
unless f.peek.to_s.start_with?('//}')
|
564
|
-
error "unexpected EOF (block begins at: #{head})"
|
580
|
+
error "unexpected EOF (block begins at: #{head})", location: location
|
565
581
|
return buf
|
566
582
|
end
|
567
583
|
f.gets # discard terminator
|
@@ -581,7 +597,7 @@ module ReVIEW
|
|
581
597
|
words << w2
|
582
598
|
end
|
583
599
|
unless scanner.eos?
|
584
|
-
error "argument syntax error: #{scanner.rest} in #{str.inspect}"
|
600
|
+
error "argument syntax error: #{scanner.rest} in #{str.inspect}", location: location
|
585
601
|
return []
|
586
602
|
end
|
587
603
|
words
|
@@ -589,37 +605,32 @@ module ReVIEW
|
|
589
605
|
|
590
606
|
def compile_command(syntax, args, lines)
|
591
607
|
unless @builder.respond_to?(syntax.name)
|
592
|
-
error "builder does not support command: //#{syntax.name}"
|
593
|
-
compile_unknown_command(args, lines)
|
608
|
+
error "builder does not support command: //#{syntax.name}", location: location
|
594
609
|
return
|
595
610
|
end
|
596
611
|
begin
|
597
612
|
syntax.check_args(args)
|
598
613
|
rescue CompileError => e
|
599
|
-
error e.message
|
614
|
+
error e.message, location: location
|
600
615
|
args = ['(NoArgument)'] * syntax.min_argc
|
601
616
|
end
|
602
617
|
if syntax.block_allowed?
|
603
618
|
compile_block(syntax, args, lines)
|
604
619
|
else
|
605
620
|
if lines
|
606
|
-
error "block is not allowed for command //#{syntax.name}; ignore"
|
621
|
+
error "block is not allowed for command //#{syntax.name}; ignore", location: location
|
607
622
|
end
|
608
623
|
compile_single(syntax, args)
|
609
624
|
end
|
610
625
|
end
|
611
626
|
|
612
|
-
def compile_unknown_command(args, lines)
|
613
|
-
@builder.unknown_command(args, lines)
|
614
|
-
end
|
615
|
-
|
616
627
|
def compile_block(syntax, args, lines)
|
617
628
|
@builder.__send__(syntax.name, (lines || default_block(syntax)), *args)
|
618
629
|
end
|
619
630
|
|
620
631
|
def default_block(syntax)
|
621
632
|
if syntax.block_required?
|
622
|
-
error "block is required for //#{syntax.name}; use empty block"
|
633
|
+
error "block is required for //#{syntax.name}; use empty block", location: location
|
623
634
|
end
|
624
635
|
[]
|
625
636
|
end
|
@@ -632,8 +643,8 @@ module ReVIEW
|
|
632
643
|
str.gsub(/@<(\w+)>([$|])(.+?)(\2)/) do
|
633
644
|
op = $1
|
634
645
|
arg = $3
|
635
|
-
if
|
636
|
-
error "invalid character in '#{str}'"
|
646
|
+
if /[\x01\x02\x03\x04]/.match?(arg)
|
647
|
+
error "invalid character in '#{str}'", location: location
|
637
648
|
end
|
638
649
|
replaced = arg.tr('@', "\x01").tr('\\', "\x02").tr('{', "\x03").tr('}', "\x04")
|
639
650
|
"@<#{op}>{#{replaced}}"
|
@@ -655,23 +666,23 @@ module ReVIEW
|
|
655
666
|
words = replace_fence(str).split(/(@<\w+>\{(?:[^}\\]|\\.)*?\})/, -1)
|
656
667
|
words.each do |w|
|
657
668
|
if w.scan(/@<\w+>/).size > 1 && !/\A@<raw>/.match(w)
|
658
|
-
error "`@<xxx>' seen but is not valid inline op: #{w}"
|
669
|
+
error "`@<xxx>' seen but is not valid inline op: #{w}", location: location
|
659
670
|
end
|
660
671
|
end
|
661
672
|
result = ''
|
662
673
|
until words.empty?
|
663
|
-
if in_non_escaped_command? && block_mode
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
674
|
+
result << if in_non_escaped_command? && block_mode
|
675
|
+
revert_replace_fence(words.shift)
|
676
|
+
else
|
677
|
+
@builder.nofunc_text(revert_replace_fence(words.shift))
|
678
|
+
end
|
668
679
|
break if words.empty?
|
669
680
|
|
670
681
|
result << compile_inline(revert_replace_fence(words.shift.gsub(/\\\}/, '}').gsub(/\\\\/, '\\')))
|
671
682
|
end
|
672
683
|
result
|
673
|
-
rescue => e
|
674
|
-
error e.message
|
684
|
+
rescue StandardError => e
|
685
|
+
error e.message, location: location
|
675
686
|
end
|
676
687
|
public :text # called from builder
|
677
688
|
|
@@ -685,8 +696,8 @@ module ReVIEW
|
|
685
696
|
end
|
686
697
|
|
687
698
|
@builder.__send__("inline_#{op}", arg)
|
688
|
-
rescue => e
|
689
|
-
error e.message
|
699
|
+
rescue StandardError => e
|
700
|
+
error e.message, location: location
|
690
701
|
@builder.nofunc_text(str)
|
691
702
|
end
|
692
703
|
|
@@ -698,12 +709,20 @@ module ReVIEW
|
|
698
709
|
@builder.minicolumn_block_name?(name)
|
699
710
|
end
|
700
711
|
|
701
|
-
def
|
702
|
-
@
|
712
|
+
def ignore_errors?
|
713
|
+
@ignore_errors
|
703
714
|
end
|
704
715
|
|
705
|
-
def
|
706
|
-
@builder.
|
716
|
+
def location
|
717
|
+
@builder.location
|
718
|
+
end
|
719
|
+
|
720
|
+
## override
|
721
|
+
def error(msg, location: nil)
|
722
|
+
return if ignore_errors? # for IndexBuilder
|
723
|
+
|
724
|
+
@compile_errors = true
|
725
|
+
super
|
707
726
|
end
|
708
727
|
end
|
709
728
|
end # module ReVIEW
|
data/lib/review/configure.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (c) 2012-
|
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
|
data/lib/review/epub2html.rb
CHANGED
@@ -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
|
65
|
+
if /.+\.opf\Z/.match?(entry.name)
|
66
66
|
opf = entry.get_input_stream.read
|
67
67
|
@opfxml = REXML::Document.new(opf)
|
68
|
-
elsif
|
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
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
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
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# = epubcommon.rb -- super class for EPUBv2 and EPUBv3
|
2
2
|
#
|
3
|
-
# Copyright (c) 2010-
|
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(
|
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: '
|
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
|
-
|
118
|
-
|
119
|
-
|
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
|
-
|
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
|
-
|
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
|
181
|
+
if /\A\d{10}\Z/.match?(str)
|
174
182
|
"#{str[0..0]}-#{str[1..5]}-#{str[6..8]}-#{str[9..9]}"
|
175
|
-
elsif
|
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
|
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
|
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
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
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
|
-
|
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
|
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-
|
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"
|