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) 2009-2020 Minero Aoki, Kenshi Muto
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
- @builder.previous_list_type = nil
292
+ @previous_list_type = nil
273
293
  when /\A\s+\*/
274
294
  compile_ulist(f)
275
- @builder.previous_list_type = 'ul'
295
+ @previous_list_type = 'ul'
276
296
  when /\A\s+\d+\./
277
297
  compile_olist(f)
278
- @builder.previous_list_type = 'ol'
298
+ @previous_list_type = 'ol'
279
299
  when /\A\s+:\s/
280
300
  compile_dlist(f)
281
- @builder.previous_list_type = 'dl'
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
- @builder.previous_list_type = 'dl'
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
- @builder.previous_list_type = nil
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
- @builder.previous_list_type = nil
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
- @builder.previous_list_type = nil
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 !~ %r{\A/}
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 = f.break(/\A(\S|\s*:|\s+\d+\.\s|\s+\*\s)/).map { |line| text(line.strip) }
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.gsub('@', "\x01").gsub('\\', "\x02").gsub('{', "\x03").gsub('}', "\x04")
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.gsub("\x01", '@').gsub("\x02", '\\').gsub("\x03", '{').gsub("\x04", '}')
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 warn(msg)
685
- @builder.warn msg
712
+ def ignore_errors?
713
+ @ignore_errors
686
714
  end
687
715
 
688
- def error(msg)
689
- @builder.error msg
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
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (c) 2012-2020 Masanori Kado, Masayoshi Takahashi, Kenshi Muto
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:uid:#{SecureRandom.uuid}", # Identifier
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
- 'mathml' => nil, # for HTML
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
- error "yaml error #{e.message}"
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