review 5.0.0 → 5.3.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 +35 -0
- data/.github/workflows/ruby-win.yml +8 -4
- data/.github/workflows/ruby.yml +6 -2
- data/.rubocop.yml +24 -9
- data/NEWS.ja.md +215 -0
- data/NEWS.md +215 -1
- data/README.md +7 -6
- data/Rakefile +7 -2
- data/bin/review +2 -4
- data/bin/review-catalog-converter +3 -3
- data/bin/review-check +6 -8
- data/bin/review-checkdep +1 -4
- data/bin/review-compile +10 -20
- data/bin/review-epub2html +1 -4
- data/bin/review-epubmaker +3 -4
- data/bin/review-idgxmlmaker +1 -3
- data/bin/review-index +11 -5
- data/bin/review-init +1 -4
- data/bin/review-pdfmaker +1 -3
- data/bin/review-preproc +30 -38
- data/bin/review-textmaker +1 -3
- data/bin/review-update +1 -4
- data/bin/review-validate +3 -3
- data/bin/review-vol +1 -4
- data/bin/review-webmaker +1 -3
- data/doc/config.yml.sample +23 -5
- data/doc/config.yml.sample-simple +1 -1
- data/doc/format.ja.md +49 -12
- data/doc/format.md +52 -12
- data/doc/quickstart.ja.md +11 -1
- data/doc/quickstart.md +11 -2
- data/doc/writing_vertical.ja.md +6 -0
- data/lib/review/book/base.rb +4 -0
- data/lib/review/book/book_unit.rb +15 -2
- data/lib/review/book/chapter.rb +3 -0
- data/lib/review/book/index.rb +5 -1
- data/lib/review/book/volume.rb +1 -0
- data/lib/review/builder.rb +90 -54
- data/lib/review/call_hook.rb +20 -0
- data/lib/review/catalog.rb +2 -0
- data/lib/review/compiler.rb +88 -52
- data/lib/review/configure.rb +64 -7
- data/lib/review/epubmaker/content.rb +113 -0
- data/lib/review/epubmaker/epubcommon.rb +372 -0
- data/lib/review/epubmaker/epubv2.rb +178 -0
- data/lib/review/epubmaker/epubv3.rb +231 -0
- data/lib/review/epubmaker/producer.rb +167 -0
- data/lib/review/epubmaker/reviewheaderlistener.rb +12 -2
- data/lib/review/epubmaker/zip_exporter.rb +84 -0
- data/lib/review/epubmaker.rb +114 -129
- data/lib/review/exception.rb +13 -0
- data/lib/review/htmlbuilder.rb +109 -67
- data/lib/review/htmlutils.rb +1 -1
- data/lib/review/i18n.rb +1 -0
- data/lib/review/i18n.yml +6 -0
- data/lib/review/idgxmlbuilder.rb +72 -48
- data/lib/review/idgxmlmaker.rb +15 -14
- data/lib/review/img_math.rb +239 -0
- data/lib/review/index_builder.rb +90 -32
- data/lib/review/init.rb +4 -4
- data/lib/review/latexbox.rb +58 -0
- data/lib/review/latexbuilder.rb +79 -58
- data/lib/review/latexutils.rb +9 -1
- data/lib/review/lineinput.rb +112 -2
- data/lib/review/loggable.rb +27 -0
- data/lib/review/logger.rb +89 -2
- data/lib/review/makerhelper.rb +7 -206
- data/lib/review/markdownbuilder.rb +44 -4
- data/lib/review/pdfmaker.rb +70 -51
- data/lib/review/plaintextbuilder.rb +20 -11
- 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 +105 -301
- data/lib/review/rstbuilder.rb +13 -4
- data/lib/review/sec_counter.rb +1 -0
- data/lib/review/template.rb +11 -1
- data/lib/review/textmaker.rb +23 -20
- data/lib/review/textutils.rb +10 -17
- data/lib/review/tocprinter.rb +93 -71
- data/lib/review/topbuilder.rb +44 -19
- data/lib/review/update.rb +5 -6
- data/lib/review/version.rb +1 -1
- data/lib/review/volumeprinter.rb +11 -12
- data/lib/review/webmaker.rb +31 -27
- data/lib/review/webtocprinter.rb +10 -9
- data/lib/review/yamlloader.rb +2 -1
- data/lib/review.rb +1 -1
- data/review.gemspec +5 -3
- data/samples/sample-book/src/config-epub2.yml +1 -1
- data/samples/sample-book/src/config.yml +1 -1
- data/samples/sample-book/src/lib/tasks/review.rake +19 -1
- data/samples/sample-book/src/lib/tasks/z01_copy_sty.rake +2 -1
- data/samples/syntax-book/ch01.re +1 -1
- data/samples/syntax-book/ch02.re +30 -6
- data/samples/syntax-book/ch03.re +1 -1
- data/samples/syntax-book/images/img3-2.png +0 -0
- data/samples/syntax-book/lib/tasks/z01_copy_sty.rake +2 -1
- data/templates/html/_colophon.html.erb +23 -0
- data/templates/html/_colophon_history.html.erb +9 -0
- data/templates/html/_cover.html.erb +10 -0
- data/templates/html/_part_body.html.erb +6 -0
- data/templates/html/_titlepage.html.erb +20 -0
- data/templates/html/layout-html5.html.erb +6 -0
- data/templates/html/layout-xhtml1.html.erb +6 -0
- data/templates/latex/config.erb +11 -0
- data/templates/latex/review-jlreq/review-base.sty +7 -9
- data/templates/latex/review-jlreq/review-jlreq.cls +48 -6
- data/templates/latex/review-jlreq/review-style.sty +6 -1
- data/templates/latex/review-jlreq/review-tcbox.sty +348 -0
- data/templates/latex/review-jlreq/reviewmacro.sty +5 -0
- data/templates/latex/review-jsbook/review-base.sty +13 -9
- data/templates/latex/review-jsbook/review-jsbook.cls +41 -6
- data/templates/latex/review-jsbook/review-style.sty +6 -1
- data/templates/latex/review-jsbook/review-tcbox.sty +348 -0
- data/templates/latex/review-jsbook/reviewmacro.sty +5 -0
- data/templates/opf/epubv2.opf.erb +7 -7
- data/templates/opf/epubv3.opf.erb +7 -7
- data/templates/opf/opf_manifest_epubv2.opf.erb +10 -0
- data/templates/opf/opf_manifest_epubv3.opf.erb +10 -0
- data/templates/opf/opf_metainfo_epubv2.opf.erb +17 -0
- data/templates/opf/opf_metainfo_epubv3.opf.erb +49 -0
- data/templates/opf/opf_tocx_epubv2.opf.erb +9 -0
- data/templates/opf/opf_tocx_epubv3.opf.erb +17 -0
- data/templates/web/html/layout-html5.html.erb +6 -5
- data/templates/web/html/layout-xhtml1.html.erb +6 -0
- data/test/assets/header_listener.html +35 -0
- data/test/assets/img_math/img1.png +0 -0
- data/test/assets/img_math/img2.png +0 -0
- data/test/assets/img_math/img3.png +0 -0
- data/test/assets/syntax_book_index_detail.txt +60 -0
- data/test/assets/test_template.tex +7 -1
- data/test/assets/test_template_backmatter.tex +7 -1
- data/test/run_test.rb +1 -1
- data/test/test_book_chapter.rb +27 -4
- data/test/test_builder.rb +10 -8
- data/test/test_catalog_converter_cmd.rb +1 -1
- data/test/test_epub3maker.rb +168 -124
- data/test/test_epubmaker.rb +248 -131
- data/test/test_epubmaker_cmd.rb +15 -4
- data/test/test_helper.rb +5 -4
- data/test/test_htmlbuilder.rb +170 -31
- data/test/test_idgxmlbuilder.rb +44 -23
- data/test/test_idgxmlmaker_cmd.rb +7 -3
- data/test/test_img_math.rb +111 -0
- data/test/test_index.rb +30 -4
- data/test/test_indexbuilder.rb +5 -5
- data/test/test_latexbuilder.rb +151 -26
- data/test/test_latexbuilder_v2.rb +18 -10
- data/test/test_lineinput.rb +20 -93
- data/test/test_markdownbuilder.rb +42 -0
- data/test/test_pdfmaker.rb +90 -0
- data/test/test_pdfmaker_cmd.rb +2 -2
- data/test/test_plaintextbuilder.rb +56 -40
- data/test/test_preprocessor.rb +188 -1
- data/test/test_reviewheaderlistener.rb +49 -0
- data/test/test_rstbuilder.rb +13 -0
- data/test/test_template.rb +12 -2
- data/test/test_textmaker_cmd.rb +5 -1
- data/test/test_tocprinter.rb +46 -0
- data/test/test_topbuilder.rb +50 -19
- data/test/test_update.rb +34 -34
- data/test/test_zip_exporter.rb +5 -6
- metadata +95 -17
- data/lib/epubmaker/content.rb +0 -111
- data/lib/epubmaker/epubcommon.rb +0 -449
- data/lib/epubmaker/epubv2.rb +0 -142
- data/lib/epubmaker/epubv3.rb +0 -235
- data/lib/epubmaker/producer.rb +0 -375
- data/lib/epubmaker/zip_exporter.rb +0 -81
- data/lib/epubmaker.rb +0 -23
- data/lib/lineinput.rb +0 -155
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,15 @@ 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
|
+
|
20
|
+
MAX_HEADLINE_LEVEL = 6
|
21
|
+
|
17
22
|
def initialize(builder)
|
18
23
|
@builder = builder
|
19
24
|
|
@@ -22,6 +27,12 @@ module ReVIEW
|
|
22
27
|
|
23
28
|
## to decide escaping/non-escaping for text
|
24
29
|
@command_name_stack = []
|
30
|
+
|
31
|
+
@logger = ReVIEW.logger
|
32
|
+
|
33
|
+
@ignore_errors = builder.is_a?(ReVIEW::IndexBuilder)
|
34
|
+
|
35
|
+
@compile_errors = nil
|
25
36
|
end
|
26
37
|
|
27
38
|
attr_reader :builder, :previous_list_type
|
@@ -42,6 +53,10 @@ module ReVIEW
|
|
42
53
|
def compile(chap)
|
43
54
|
@chapter = chap
|
44
55
|
do_compile
|
56
|
+
if @compile_errors
|
57
|
+
raise ApplicationError, "#{location.filename} cannot be compiled."
|
58
|
+
end
|
59
|
+
|
45
60
|
@builder.result
|
46
61
|
end
|
47
62
|
|
@@ -59,6 +74,7 @@ module ReVIEW
|
|
59
74
|
unless @argc_spec === args.size
|
60
75
|
raise CompileError, "wrong # of parameters (block command //#{@name}, expect #{@argc_spec} but #{args.size})"
|
61
76
|
end
|
77
|
+
|
62
78
|
if @checker
|
63
79
|
@checker.call(*args)
|
64
80
|
end
|
@@ -180,6 +196,8 @@ module ReVIEW
|
|
180
196
|
defminicolumn :notice, 0..1
|
181
197
|
|
182
198
|
defsingle :footnote, 2
|
199
|
+
defsingle :endnote, 2
|
200
|
+
defsingle :printendnotes, 0
|
183
201
|
defsingle :noindent, 0
|
184
202
|
defsingle :blankline, 0
|
185
203
|
defsingle :pagebreak, 0
|
@@ -204,6 +222,7 @@ module ReVIEW
|
|
204
222
|
definline :table
|
205
223
|
definline :eq
|
206
224
|
definline :fn
|
225
|
+
definline :endnote
|
207
226
|
definline :kw
|
208
227
|
definline :ruby
|
209
228
|
definline :bou
|
@@ -258,6 +277,7 @@ module ReVIEW
|
|
258
277
|
def do_compile
|
259
278
|
f = LineInput.new(StringIO.new(@chapter.content))
|
260
279
|
@builder.bind(self, @chapter, Location.new(@chapter.basename, f))
|
280
|
+
@previous_list_type = nil
|
261
281
|
|
262
282
|
## in minicolumn, such as note/info/alert...
|
263
283
|
@minicolumn_name = nil
|
@@ -269,27 +289,27 @@ module ReVIEW
|
|
269
289
|
f.gets # Nothing to do
|
270
290
|
when /\A=+[\[\s{]/
|
271
291
|
compile_headline(f.gets)
|
272
|
-
@
|
292
|
+
@previous_list_type = nil
|
273
293
|
when /\A\s+\*/
|
274
294
|
compile_ulist(f)
|
275
|
-
@
|
295
|
+
@previous_list_type = 'ul'
|
276
296
|
when /\A\s+\d+\./
|
277
297
|
compile_olist(f)
|
278
|
-
@
|
298
|
+
@previous_list_type = 'ol'
|
279
299
|
when /\A\s+:\s/
|
280
300
|
compile_dlist(f)
|
281
|
-
@
|
301
|
+
@previous_list_type = 'dl'
|
282
302
|
when /\A\s*:\s/
|
283
|
-
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
|
284
304
|
compile_dlist(f)
|
285
|
-
@
|
305
|
+
@previous_list_type = 'dl'
|
286
306
|
when %r{\A//\}}
|
287
307
|
if in_minicolumn?
|
288
308
|
_line = f.gets
|
289
309
|
compile_minicolumn_end
|
290
310
|
else
|
291
311
|
f.gets
|
292
|
-
error 'block end seen but not opened'
|
312
|
+
error 'block end seen but not opened', location: location
|
293
313
|
end
|
294
314
|
when %r{\A//[a-z]+}
|
295
315
|
line = f.peek
|
@@ -304,43 +324,44 @@ module ReVIEW
|
|
304
324
|
name, args, lines = read_command(f)
|
305
325
|
syntax = syntax_descriptor(name)
|
306
326
|
unless syntax
|
307
|
-
error "unknown command: //#{name}"
|
308
|
-
compile_unknown_command(args, lines)
|
327
|
+
error "unknown command: //#{name}", location: location
|
309
328
|
@command_name_stack.pop
|
310
329
|
next
|
311
330
|
end
|
312
331
|
compile_command(syntax, args, lines)
|
313
332
|
@command_name_stack.pop
|
314
333
|
end
|
315
|
-
@
|
334
|
+
@previous_list_type = nil
|
316
335
|
when %r{\A//}
|
317
336
|
line = f.gets
|
318
|
-
warn "`//' seen but is not valid command: #{line.strip.inspect}"
|
337
|
+
warn "`//' seen but is not valid command: #{line.strip.inspect}", location: location
|
319
338
|
if block_open?(line)
|
320
|
-
warn 'skipping block...'
|
339
|
+
warn 'skipping block...', location: location
|
321
340
|
read_block(f, false)
|
322
341
|
end
|
323
|
-
@
|
342
|
+
@previous_list_type = nil
|
324
343
|
else
|
325
344
|
if f.peek.strip.empty?
|
326
345
|
f.gets
|
327
346
|
next
|
328
347
|
end
|
329
348
|
compile_paragraph(f)
|
330
|
-
@
|
349
|
+
@previous_list_type = nil
|
331
350
|
end
|
332
351
|
end
|
333
352
|
close_all_tagged_section
|
353
|
+
rescue SyntaxError => e
|
354
|
+
error e, location: location
|
334
355
|
end
|
335
356
|
|
336
357
|
def compile_minicolumn_begin(name, caption = nil)
|
337
358
|
mid = "#{name}_begin"
|
338
359
|
unless @builder.respond_to?(mid)
|
339
|
-
error "strategy does not support minicolumn: #{name}"
|
360
|
+
error "strategy does not support minicolumn: #{name}", location: location
|
340
361
|
end
|
341
362
|
|
342
363
|
if @minicolumn_name
|
343
|
-
error "minicolumn cannot be nested: #{name}"
|
364
|
+
error "minicolumn cannot be nested: #{name}", location: location
|
344
365
|
return
|
345
366
|
end
|
346
367
|
@minicolumn_name = name
|
@@ -350,7 +371,7 @@ module ReVIEW
|
|
350
371
|
|
351
372
|
def compile_minicolumn_end
|
352
373
|
unless @minicolumn_name
|
353
|
-
error "minicolumn is not used: #{name}"
|
374
|
+
error "minicolumn is not used: #{name}", location: location
|
354
375
|
return
|
355
376
|
end
|
356
377
|
name = @minicolumn_name
|
@@ -364,28 +385,32 @@ module ReVIEW
|
|
364
385
|
@headline_indexs ||= [@chapter.number.to_i - 1]
|
365
386
|
m = /\A(=+)(?:\[(.+?)\])?(?:\{(.+?)\})?(.*)/.match(line)
|
366
387
|
level = m[1].size
|
388
|
+
if level > MAX_HEADLINE_LEVEL
|
389
|
+
raise CompileError, "Invalid header: max headline level is #{MAX_HEADLINE_LEVEL}"
|
390
|
+
end
|
391
|
+
|
367
392
|
tag = m[2]
|
368
393
|
label = m[3]
|
369
394
|
caption = m[4].strip
|
370
395
|
index = level - 1
|
371
396
|
if tag
|
372
|
-
if tag
|
373
|
-
if caption.empty?
|
374
|
-
warn 'headline is empty.'
|
375
|
-
end
|
376
|
-
close_current_tagged_section(level)
|
377
|
-
open_tagged_section(tag, level, label, caption)
|
378
|
-
else
|
397
|
+
if tag.start_with?('/')
|
379
398
|
open_tag = tag[1..-1]
|
380
399
|
prev_tag_info = @tagged_section.pop
|
381
400
|
if prev_tag_info.nil? || prev_tag_info.first != open_tag
|
382
|
-
error "#{open_tag} is not opened."
|
401
|
+
error "#{open_tag} is not opened.", location: location
|
383
402
|
end
|
384
403
|
close_tagged_section(*prev_tag_info)
|
404
|
+
else
|
405
|
+
if caption.empty?
|
406
|
+
warn 'headline is empty.', location: location
|
407
|
+
end
|
408
|
+
close_current_tagged_section(level)
|
409
|
+
open_tagged_section(tag, level, label, caption)
|
385
410
|
end
|
386
411
|
else
|
387
412
|
if caption.empty?
|
388
|
-
warn 'headline is empty.'
|
413
|
+
warn 'headline is empty.', location: location
|
389
414
|
end
|
390
415
|
if @headline_indexs.size > (index + 1)
|
391
416
|
@headline_indexs = @headline_indexs[0..index]
|
@@ -416,7 +441,7 @@ module ReVIEW
|
|
416
441
|
def open_tagged_section(tag, level, label, caption)
|
417
442
|
mid = "#{tag}_begin"
|
418
443
|
unless @builder.respond_to?(mid)
|
419
|
-
error "builder does not support tagged section: #{tag}"
|
444
|
+
error "builder does not support tagged section: #{tag}", location: location
|
420
445
|
headline(level, label, caption)
|
421
446
|
return
|
422
447
|
end
|
@@ -429,7 +454,7 @@ module ReVIEW
|
|
429
454
|
if @builder.respond_to?(mid)
|
430
455
|
@builder.__send__(mid, level)
|
431
456
|
else
|
432
|
-
error "builder does not support block op: #{mid}"
|
457
|
+
error "builder does not support block op: #{mid}", location: location
|
433
458
|
end
|
434
459
|
end
|
435
460
|
|
@@ -458,7 +483,7 @@ module ReVIEW
|
|
458
483
|
elsif level < current_level # down
|
459
484
|
level_diff = current_level - level
|
460
485
|
if level_diff != 1
|
461
|
-
error 'too many *.'
|
486
|
+
error 'too many *.', location: location
|
462
487
|
end
|
463
488
|
level = current_level
|
464
489
|
@builder.ul_begin { level }
|
@@ -504,7 +529,10 @@ module ReVIEW
|
|
504
529
|
@builder.doc_status[:dt] = true
|
505
530
|
@builder.dt(text(f.gets.sub(/\A\s*:/, '').strip))
|
506
531
|
@builder.doc_status[:dt] = nil
|
507
|
-
desc =
|
532
|
+
desc = []
|
533
|
+
f.until_match(/\A(\S|\s*:|\s+\d+\.\s|\s+\*\s)/) do |line|
|
534
|
+
desc << text(line.strip)
|
535
|
+
end
|
508
536
|
@builder.dd(desc)
|
509
537
|
f.skip_blank_lines
|
510
538
|
f.skip_comment_lines
|
@@ -516,6 +544,7 @@ module ReVIEW
|
|
516
544
|
buf = []
|
517
545
|
f.until_match(%r{\A//|\A\#@}) do |line|
|
518
546
|
break if line.strip.empty?
|
547
|
+
|
519
548
|
buf.push(text(line.sub(/^(\t+)\s*/) { |m| '<!ESCAPETAB!>' * m.size }.strip.gsub('<!ESCAPETAB!>', "\t")))
|
520
549
|
end
|
521
550
|
@builder.paragraph(buf)
|
@@ -548,7 +577,7 @@ module ReVIEW
|
|
548
577
|
end
|
549
578
|
end
|
550
579
|
unless f.peek.to_s.start_with?('//}')
|
551
|
-
error "unexpected EOF (block begins at: #{head})"
|
580
|
+
error "unexpected EOF (block begins at: #{head})", location: location
|
552
581
|
return buf
|
553
582
|
end
|
554
583
|
f.gets # discard terminator
|
@@ -557,6 +586,7 @@ module ReVIEW
|
|
557
586
|
|
558
587
|
def parse_args(str, _name = nil)
|
559
588
|
return [] if str.empty?
|
589
|
+
|
560
590
|
scanner = StringScanner.new(str)
|
561
591
|
words = []
|
562
592
|
while word = scanner.scan(/(\[\]|\[.*?[^\\]\])/)
|
@@ -567,7 +597,7 @@ module ReVIEW
|
|
567
597
|
words << w2
|
568
598
|
end
|
569
599
|
unless scanner.eos?
|
570
|
-
error "argument syntax error: #{scanner.rest} in #{str.inspect}"
|
600
|
+
error "argument syntax error: #{scanner.rest} in #{str.inspect}", location: location
|
571
601
|
return []
|
572
602
|
end
|
573
603
|
words
|
@@ -575,37 +605,32 @@ module ReVIEW
|
|
575
605
|
|
576
606
|
def compile_command(syntax, args, lines)
|
577
607
|
unless @builder.respond_to?(syntax.name)
|
578
|
-
error "builder does not support command: //#{syntax.name}"
|
579
|
-
compile_unknown_command(args, lines)
|
608
|
+
error "builder does not support command: //#{syntax.name}", location: location
|
580
609
|
return
|
581
610
|
end
|
582
611
|
begin
|
583
612
|
syntax.check_args(args)
|
584
613
|
rescue CompileError => e
|
585
|
-
error e.message
|
614
|
+
error e.message, location: location
|
586
615
|
args = ['(NoArgument)'] * syntax.min_argc
|
587
616
|
end
|
588
617
|
if syntax.block_allowed?
|
589
618
|
compile_block(syntax, args, lines)
|
590
619
|
else
|
591
620
|
if lines
|
592
|
-
error "block is not allowed for command //#{syntax.name}; ignore"
|
621
|
+
error "block is not allowed for command //#{syntax.name}; ignore", location: location
|
593
622
|
end
|
594
623
|
compile_single(syntax, args)
|
595
624
|
end
|
596
625
|
end
|
597
626
|
|
598
|
-
def compile_unknown_command(args, lines)
|
599
|
-
@builder.unknown_command(args, lines)
|
600
|
-
end
|
601
|
-
|
602
627
|
def compile_block(syntax, args, lines)
|
603
628
|
@builder.__send__(syntax.name, (lines || default_block(syntax)), *args)
|
604
629
|
end
|
605
630
|
|
606
631
|
def default_block(syntax)
|
607
632
|
if syntax.block_required?
|
608
|
-
error "block is required for //#{syntax.name}; use empty block"
|
633
|
+
error "block is required for //#{syntax.name}; use empty block", location: location
|
609
634
|
end
|
610
635
|
[]
|
611
636
|
end
|
@@ -619,15 +644,15 @@ module ReVIEW
|
|
619
644
|
op = $1
|
620
645
|
arg = $3
|
621
646
|
if arg =~ /[\x01\x02\x03\x04]/
|
622
|
-
error "invalid character in '#{str}'"
|
647
|
+
error "invalid character in '#{str}'", location: location
|
623
648
|
end
|
624
|
-
replaced = arg.
|
649
|
+
replaced = arg.tr('@', "\x01").tr('\\', "\x02").tr('{', "\x03").tr('}', "\x04")
|
625
650
|
"@<#{op}>{#{replaced}}"
|
626
651
|
end
|
627
652
|
end
|
628
653
|
|
629
654
|
def revert_replace_fence(str)
|
630
|
-
str.
|
655
|
+
str.tr("\x01", '@').tr("\x02", '\\').tr("\x03", '{').tr("\x04", '}')
|
631
656
|
end
|
632
657
|
|
633
658
|
def in_non_escaped_command?
|
@@ -637,10 +662,11 @@ module ReVIEW
|
|
637
662
|
|
638
663
|
def text(str, block_mode = false)
|
639
664
|
return '' if str.empty?
|
665
|
+
|
640
666
|
words = replace_fence(str).split(/(@<\w+>\{(?:[^}\\]|\\.)*?\})/, -1)
|
641
667
|
words.each do |w|
|
642
668
|
if w.scan(/@<\w+>/).size > 1 && !/\A@<raw>/.match(w)
|
643
|
-
error "`@<xxx>' seen but is not valid inline op: #{w}"
|
669
|
+
error "`@<xxx>' seen but is not valid inline op: #{w}", location: location
|
644
670
|
end
|
645
671
|
end
|
646
672
|
result = ''
|
@@ -651,11 +677,12 @@ module ReVIEW
|
|
651
677
|
result << @builder.nofunc_text(revert_replace_fence(words.shift))
|
652
678
|
end
|
653
679
|
break if words.empty?
|
680
|
+
|
654
681
|
result << compile_inline(revert_replace_fence(words.shift.gsub(/\\\}/, '}').gsub(/\\\\/, '\\')))
|
655
682
|
end
|
656
683
|
result
|
657
684
|
rescue => e
|
658
|
-
error e.message
|
685
|
+
error e.message, location: location
|
659
686
|
end
|
660
687
|
public :text # called from builder
|
661
688
|
|
@@ -667,9 +694,10 @@ module ReVIEW
|
|
667
694
|
unless @builder.respond_to?("inline_#{op}")
|
668
695
|
raise "builder does not support inline op: @<#{op}>"
|
669
696
|
end
|
697
|
+
|
670
698
|
@builder.__send__("inline_#{op}", arg)
|
671
699
|
rescue => e
|
672
|
-
error e.message
|
700
|
+
error e.message, location: location
|
673
701
|
@builder.nofunc_text(str)
|
674
702
|
end
|
675
703
|
|
@@ -681,12 +709,20 @@ module ReVIEW
|
|
681
709
|
@builder.minicolumn_block_name?(name)
|
682
710
|
end
|
683
711
|
|
684
|
-
def
|
685
|
-
@
|
712
|
+
def ignore_errors?
|
713
|
+
@ignore_errors
|
686
714
|
end
|
687
715
|
|
688
|
-
def
|
689
|
-
@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
|
690
726
|
end
|
691
727
|
end
|
692
728
|
end # module ReVIEW
|
data/lib/review/configure.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (c) 2012-
|
2
|
+
# Copyright (c) 2012-2021 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
|
@@ -7,12 +7,13 @@
|
|
7
7
|
# For details of the GNU LGPL, see the file "COPYING".
|
8
8
|
#
|
9
9
|
require 'securerandom'
|
10
|
+
require 'review/yamlloader'
|
10
11
|
|
11
12
|
module ReVIEW
|
12
13
|
class Configure < Hash
|
13
14
|
attr_accessor :maker
|
14
15
|
|
15
|
-
def self.values
|
16
|
+
def self.values # rubocop:disable Metrics/MethodLength
|
16
17
|
conf = Configure[
|
17
18
|
# These parameters can be overridden by YAML file.
|
18
19
|
'bookname' => 'book', # it defines epub file name also
|
@@ -31,7 +32,7 @@ module ReVIEW
|
|
31
32
|
'date' => Time.now.strftime('%Y-%m-%d'), # publishing date
|
32
33
|
'rights' => nil, # Copyright messages
|
33
34
|
'description' => nil, # Description
|
34
|
-
'urnid' => "urn:
|
35
|
+
'urnid' => "urn:uuid:#{SecureRandom.uuid}", # Identifier
|
35
36
|
'stylesheet' => [], # stylesheet file
|
36
37
|
'coverfile' => nil, # content file of body of cover page
|
37
38
|
'mytoc' => nil, # whether make own table of contents or not
|
@@ -45,8 +46,7 @@ module ReVIEW
|
|
45
46
|
'debug' => nil, # debug flag
|
46
47
|
'catalogfile' => 'catalog.yml',
|
47
48
|
'language' => 'ja', # XXX default language should be JA??
|
48
|
-
'
|
49
|
-
'imgmath' => nil, # for HTML
|
49
|
+
'math_format' => nil,
|
50
50
|
'htmlext' => 'html',
|
51
51
|
'htmlversion' => 5,
|
52
52
|
'contentdir' => '.',
|
@@ -63,7 +63,7 @@ module ReVIEW
|
|
63
63
|
'image_types' => %w[.ai .psd .eps .pdf .tif .tiff .png .bmp .jpg .jpeg .gif .svg],
|
64
64
|
'bib_file' => 'bib.re',
|
65
65
|
'words_file' => nil,
|
66
|
-
'colophon_order' => %w[aut csl trl dsr ill cov edt pbl contact prt],
|
66
|
+
'colophon_order' => %w[aut csl trl dsr ill cov edt pbl contact prt pht],
|
67
67
|
'chapterlink' => true,
|
68
68
|
'externallink' => true,
|
69
69
|
'join_lines_by_lang' => nil, # experimental. default should be nil
|
@@ -113,6 +113,34 @@ module ReVIEW
|
|
113
113
|
'image' => 'bottom',
|
114
114
|
'table' => 'top',
|
115
115
|
'equation' => 'top'
|
116
|
+
},
|
117
|
+
# for EPUBMaker
|
118
|
+
'modified' => Time.now.utc.strftime('%Y-%02m-%02dT%02H:%02M:%02SZ'),
|
119
|
+
'isbn' => nil,
|
120
|
+
'titlefile' => nil,
|
121
|
+
'originaltitlefile' => nil,
|
122
|
+
'profile' => nil,
|
123
|
+
'direction' => 'ltr',
|
124
|
+
'image_maxpixels' => 4_000_000,
|
125
|
+
'font_ext' => %w[ttf woff otf],
|
126
|
+
'epubmaker' => {
|
127
|
+
'flattoc' => nil,
|
128
|
+
'flattocindent' => true,
|
129
|
+
'ncx_indent' => [],
|
130
|
+
'zip_stage1' => 'zip -0Xq',
|
131
|
+
'zip_stage2' => 'zip -Xr9Dq',
|
132
|
+
'zip_addpath' => nil,
|
133
|
+
'hook_beforeprocess' => nil,
|
134
|
+
'hook_afterfrontmatter' => nil,
|
135
|
+
'hook_afterbody' => nil,
|
136
|
+
'hook_afterbackmatter' => nil,
|
137
|
+
'hook_aftercopyimage' => nil,
|
138
|
+
'hook_prepack' => nil,
|
139
|
+
'rename_for_legacy' => nil,
|
140
|
+
'verify_target_images' => nil,
|
141
|
+
'force_include_images' => [],
|
142
|
+
'cover_linear' => nil,
|
143
|
+
'back_footnote' => nil
|
116
144
|
}
|
117
145
|
]
|
118
146
|
conf.maker = nil
|
@@ -128,17 +156,45 @@ module ReVIEW
|
|
128
156
|
loader = ReVIEW::YAMLLoader.new
|
129
157
|
conf.deep_merge!(loader.load_file(yamlfile))
|
130
158
|
rescue => e
|
131
|
-
|
159
|
+
raise ReVIEW::ConfigError, "yaml error #{e.message}"
|
132
160
|
end
|
133
161
|
end
|
162
|
+
|
134
163
|
# YAML configs will be overridden by command line options.
|
135
164
|
if config
|
136
165
|
conf.deep_merge!(config)
|
137
166
|
end
|
138
167
|
|
168
|
+
conf.migrate_parameters
|
169
|
+
|
139
170
|
conf
|
140
171
|
end
|
141
172
|
|
173
|
+
def migrate_parameters
|
174
|
+
# string to array
|
175
|
+
%w[subject aut
|
176
|
+
a-adp a-ann a-arr a-art a-asn a-aqt a-aft a-aui a-ant a-bkp a-clb a-cmm a-dsr a-edt
|
177
|
+
a-ill a-lyr a-mdc a-mus a-nrt a-oth a-pht a-prt a-red a-rev a-spn a-ths a-trc a-trl
|
178
|
+
adp ann arr art asn aut aqt aft aui ant bkp clb cmm dsr edt
|
179
|
+
ill lyr mdc mus nrt oth pht pbl prt red rev spn ths trc trl
|
180
|
+
stylesheet rights].each do |item|
|
181
|
+
if self[item] && self[item].is_a?(String)
|
182
|
+
self[item] = [self[item]]
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# backward compatibility
|
187
|
+
if self['mathml']
|
188
|
+
warn '"mathml: true" is obsoleted. Please use "math_format: mathml"'
|
189
|
+
self['math_format'] = 'mathml'
|
190
|
+
end
|
191
|
+
|
192
|
+
if self['imgmath']
|
193
|
+
warn '"imgmath: true" is obsoleted. Please use "math_format: imgmath"'
|
194
|
+
self['math_format'] = 'imgmath'
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
142
198
|
def [](key)
|
143
199
|
maker = self.maker
|
144
200
|
if maker && self.key?(maker) && self.fetch(maker) && self.fetch(maker).key?(key)
|
@@ -147,6 +203,7 @@ module ReVIEW
|
|
147
203
|
if self.key?(key)
|
148
204
|
return self.fetch(key)
|
149
205
|
end
|
206
|
+
|
150
207
|
nil
|
151
208
|
end
|
152
209
|
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# = content.rb -- Content object for EPUBMaker.
|
2
|
+
#
|
3
|
+
# Copyright (c) 2010-2020 Kenshi Muto
|
4
|
+
#
|
5
|
+
# This program is free software.
|
6
|
+
# You can distribute or modify this program under the terms of
|
7
|
+
# the GNU LGPL, Lesser General Public License version 2.1.
|
8
|
+
# For details of the GNU LGPL, see the file "COPYING".
|
9
|
+
#
|
10
|
+
|
11
|
+
module ReVIEW
|
12
|
+
class EPUBMaker
|
13
|
+
# EPUBMaker::Content represents a content data for EPUBMaker.
|
14
|
+
# EPUBMaker#contents takes an array of Content.
|
15
|
+
class Content
|
16
|
+
# ID
|
17
|
+
attr_accessor :id
|
18
|
+
# File path (will accept #<anchor> suffix also)
|
19
|
+
attr_accessor :file
|
20
|
+
# MIME type
|
21
|
+
attr_accessor :media
|
22
|
+
# Title
|
23
|
+
attr_accessor :title
|
24
|
+
# Header level (from 1)
|
25
|
+
attr_accessor :level
|
26
|
+
# Show in TOC? nil:No.
|
27
|
+
attr_accessor :notoc
|
28
|
+
# Properties (EPUB3)
|
29
|
+
attr_accessor :properties
|
30
|
+
# Chapter type (pre/post/part/nil(body))
|
31
|
+
attr_accessor :chaptype
|
32
|
+
|
33
|
+
def inspect
|
34
|
+
"<Content id=#{@id}, file=#{@file}, media=#{@media}, title=#{@title}, level=#{@level}, notoc=#{@notoc}, properties=#{@properties}, chaptype=#{@chaptype}>"
|
35
|
+
end
|
36
|
+
|
37
|
+
# :call-seq:
|
38
|
+
# initialize(params)
|
39
|
+
# Construct Content object by passing named parameters.
|
40
|
+
# +params+[:file] is required. Others are optional.
|
41
|
+
def initialize(file:, id: nil, media: nil, title: nil, level: nil, notoc: nil, properties: nil, chaptype: nil)
|
42
|
+
@id = id
|
43
|
+
@file = file
|
44
|
+
@media = media
|
45
|
+
@title = title
|
46
|
+
@level = level
|
47
|
+
@notoc = notoc
|
48
|
+
@properties = properties || []
|
49
|
+
@chaptype = chaptype
|
50
|
+
complement
|
51
|
+
end
|
52
|
+
|
53
|
+
def ==(other)
|
54
|
+
return false unless self.class == other.class
|
55
|
+
|
56
|
+
[self.id, self.file, self.media, self.title, self.level, self.notoc, self.chaptype, self.properties] ==
|
57
|
+
[other.id, other.file, other.media, other.title, other.level, other.notoc, other.chaptype, other.properties]
|
58
|
+
end
|
59
|
+
|
60
|
+
def coverimage?(imagefile)
|
61
|
+
self.media.start_with?('image') && self.file =~ /#{imagefile}\Z/
|
62
|
+
end
|
63
|
+
|
64
|
+
def properties_attribute
|
65
|
+
if self.properties.size > 0
|
66
|
+
%Q( properties="#{self.properties.sort.uniq.join(' ')}")
|
67
|
+
else
|
68
|
+
''
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
# Complement other parameters by using file parameter.
|
75
|
+
def complement
|
76
|
+
if @id.nil?
|
77
|
+
@id = @file.gsub(%r{[\\/. ]}, '-')
|
78
|
+
end
|
79
|
+
if @id =~ /\A[^a-z]/i
|
80
|
+
@id = "rv-#{@id}"
|
81
|
+
end
|
82
|
+
@id = CGI.escape(@id).gsub('%', '_25_')
|
83
|
+
|
84
|
+
if !@file.nil? && @media.nil?
|
85
|
+
@media = @file.sub(/.+\./, '').downcase
|
86
|
+
end
|
87
|
+
|
88
|
+
case @media
|
89
|
+
when 'xhtml', 'xml', 'html'
|
90
|
+
@media = 'application/xhtml+xml'
|
91
|
+
when 'css'
|
92
|
+
@media = 'text/css'
|
93
|
+
when 'jpg', 'jpeg', 'image/jpg'
|
94
|
+
@media = 'image/jpeg'
|
95
|
+
when 'png'
|
96
|
+
@media = 'image/png'
|
97
|
+
when 'gif'
|
98
|
+
@media = 'image/gif'
|
99
|
+
when 'svg', 'image/svg'
|
100
|
+
@media = 'image/svg+xml'
|
101
|
+
when 'ttf', 'otf'
|
102
|
+
@media = 'application/vnd.ms-opentype'
|
103
|
+
when 'woff'
|
104
|
+
@media = 'application/font-woff'
|
105
|
+
end
|
106
|
+
|
107
|
+
if @id.nil? || @file.nil? || @media.nil?
|
108
|
+
raise "Type error: #{id}, #{file}, #{media}, #{title}, #{notoc}"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|