review 5.1.1 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby-tex.yml +5 -1
  3. data/.github/workflows/ruby-win.yml +5 -1
  4. data/.github/workflows/ruby.yml +5 -1
  5. data/.rubocop.yml +6 -2
  6. data/NEWS.ja.md +48 -0
  7. data/NEWS.md +48 -1
  8. data/bin/review-compile +8 -15
  9. data/bin/review-preproc +28 -34
  10. data/doc/config.yml.sample +2 -0
  11. data/doc/writing_vertical.ja.md +6 -0
  12. data/lib/review/builder.rb +34 -40
  13. data/lib/review/compiler.rb +59 -43
  14. data/lib/review/epubmaker.rb +24 -38
  15. data/lib/review/epubmaker/producer.rb +3 -4
  16. data/lib/review/exception.rb +7 -0
  17. data/lib/review/htmlbuilder.rb +51 -20
  18. data/lib/review/idgxmlbuilder.rb +19 -18
  19. data/lib/review/idgxmlmaker.rb +12 -13
  20. data/lib/review/img_math.rb +11 -18
  21. data/lib/review/index_builder.rb +6 -15
  22. data/lib/review/latexbuilder.rb +38 -43
  23. data/lib/review/loggable.rb +27 -0
  24. data/lib/review/logger.rb +48 -0
  25. data/lib/review/makerhelper.rb +5 -1
  26. data/lib/review/markdownbuilder.rb +5 -4
  27. data/lib/review/pdfmaker.rb +23 -21
  28. data/lib/review/plaintextbuilder.rb +8 -8
  29. data/lib/review/preprocessor.rb +94 -296
  30. data/lib/review/preprocessor/directive.rb +35 -0
  31. data/lib/review/preprocessor/line.rb +34 -0
  32. data/lib/review/preprocessor/repository.rb +177 -0
  33. data/lib/review/rstbuilder.rb +1 -1
  34. data/lib/review/template.rb +5 -1
  35. data/lib/review/textmaker.rb +12 -13
  36. data/lib/review/tocprinter.rb +1 -1
  37. data/lib/review/topbuilder.rb +7 -7
  38. data/lib/review/version.rb +1 -1
  39. data/lib/review/webmaker.rb +13 -14
  40. data/review.gemspec +1 -1
  41. data/samples/sample-book/src/lib/tasks/review.rake +3 -1
  42. data/samples/sample-book/src/lib/tasks/z01_copy_sty.rake +2 -1
  43. data/samples/syntax-book/lib/tasks/z01_copy_sty.rake +2 -1
  44. data/templates/latex/review-jlreq/review-base.sty +3 -5
  45. data/templates/latex/review-jlreq/review-jlreq.cls +4 -3
  46. data/templates/latex/review-jsbook/review-base.sty +3 -3
  47. data/templates/latex/review-jsbook/review-jsbook.cls +1 -1
  48. data/test/test_builder.rb +8 -8
  49. data/test/test_epubmaker.rb +13 -8
  50. data/test/test_epubmaker_cmd.rb +13 -2
  51. data/test/test_htmlbuilder.rb +68 -26
  52. data/test/test_idgxmlbuilder.rb +31 -23
  53. data/test/test_latexbuilder.rb +30 -22
  54. data/test/test_latexbuilder_v2.rb +18 -10
  55. data/test/test_plaintextbuilder.rb +30 -22
  56. data/test/test_preprocessor.rb +188 -1
  57. data/test/test_topbuilder.rb +26 -18
  58. metadata +12 -8
@@ -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)
@@ -22,4 +22,11 @@ module ReVIEW
22
22
  class FileNotFound < ApplicationError; end
23
23
 
24
24
  class KeyError < CompileError; end
25
+
26
+ class BuildError < ApplicationError
27
+ def initialize(msg, location: nil)
28
+ @location = location
29
+ super(msg)
30
+ end
31
+ end
25
32
  end
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2008-2020 Minero Aoki, Kenshi Muto, Masayoshi Takahashi,
1
+ # Copyright (c) 2008-2021 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
 
@@ -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,33 @@ 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
+ # flush all `</section>`
115
+ if use_section?
116
+ print close_sections
117
+ end
118
+
91
119
  # default XHTML header/footer
92
120
  @title = strip_html(compile_inline(@chapter.title))
93
121
  @body = solve_nest(@output.string)
@@ -133,6 +161,9 @@ module ReVIEW
133
161
  end
134
162
 
135
163
  def headline(level, label, caption)
164
+ if use_section?
165
+ print open_section(level)
166
+ end
136
167
  prefix, anchor = headline_prefix(level)
137
168
  if prefix
138
169
  prefix = %Q(<span class="secno">#{prefix}</span>)
@@ -618,7 +649,7 @@ module ReVIEW
618
649
  require 'math_ml'
619
650
  require 'math_ml/symbol/character_reference'
620
651
  rescue LoadError
621
- error 'not found math_ml'
652
+ app_error 'not found math_ml'
622
653
  end
623
654
  p = MathML::LaTeX::Parser.new(symbol: MathML::Symbol::CharacterReference)
624
655
  print p.parse(lines.join("\n") + "\n", true)
@@ -676,7 +707,7 @@ module ReVIEW
676
707
  end
677
708
 
678
709
  def image_dummy(id, caption, lines)
679
- warn "image not bound: #{id}"
710
+ warn "image not bound: #{id}", location: location
680
711
  puts %Q(<div id="#{normalize_id(id)}" class="image">)
681
712
  image_header(id, caption) if caption_top?('image')
682
713
  puts %Q(<pre class="dummyimage">)
@@ -740,7 +771,7 @@ module ReVIEW
740
771
 
741
772
  def imgtable(lines, id, caption = nil, metric = nil)
742
773
  unless @chapter.image_bound?(id)
743
- warn "image not bound: #{id}"
774
+ warn "image not bound: #{id}", location: location
744
775
  image_dummy(id, caption, lines)
745
776
  return
746
777
  end
@@ -757,7 +788,7 @@ module ReVIEW
757
788
  table_header(id, caption)
758
789
  end
759
790
  rescue KeyError
760
- error "no such table: #{id}"
791
+ app_error "no such table: #{id}"
761
792
  end
762
793
 
763
794
  puts '</div>'
@@ -813,7 +844,7 @@ EOS
813
844
  begin
814
845
  puts %Q(<img src="#{@chapter.image(id).path.sub(%r{\A\./}, '')}" alt="#{escape(compile_inline(caption))}"#{metrics} />)
815
846
  rescue
816
- warn "image not bound: #{id}"
847
+ warn "image not bound: #{id}", location: location
817
848
  if lines
818
849
  puts %Q(<pre class="dummyimage">)
819
850
  lines.each do |line|
@@ -866,7 +897,7 @@ EOS
866
897
  alias_method :inline_ref, :inline_labelref
867
898
 
868
899
  def inline_pageref(id)
869
- error "pageref op is unsupported on this builder: #{id}"
900
+ app_error "pageref op is unsupported on this builder: #{id}"
870
901
  end
871
902
 
872
903
  def inline_chapref(id)
@@ -877,7 +908,7 @@ EOS
877
908
  title
878
909
  end
879
910
  rescue KeyError
880
- error "unknown chapter: #{id}"
911
+ app_error "unknown chapter: #{id}"
881
912
  end
882
913
 
883
914
  def inline_chap(id)
@@ -887,7 +918,7 @@ EOS
887
918
  @book.chapter_index.number(id)
888
919
  end
889
920
  rescue KeyError
890
- error "unknown chapter: #{id}"
921
+ app_error "unknown chapter: #{id}"
891
922
  end
892
923
 
893
924
  def inline_title(id)
@@ -898,7 +929,7 @@ EOS
898
929
  title
899
930
  end
900
931
  rescue KeyError
901
- error "unknown chapter: #{id}"
932
+ app_error "unknown chapter: #{id}"
902
933
  end
903
934
 
904
935
  def inline_fn(id)
@@ -908,7 +939,7 @@ EOS
908
939
  %Q(<a id="fnb-#{normalize_id(id)}" href="#fn-#{normalize_id(id)}" class="noteref">*#{@chapter.footnote(id).number}</a>)
909
940
  end
910
941
  rescue KeyError
911
- error "unknown footnote: #{id}"
942
+ app_error "unknown footnote: #{id}"
912
943
  end
913
944
 
914
945
  def compile_ruby(base, ruby)
@@ -990,7 +1021,7 @@ EOS
990
1021
  require 'math_ml'
991
1022
  require 'math_ml/symbol/character_reference'
992
1023
  rescue LoadError
993
- error 'not found math_ml'
1024
+ app_error 'not found math_ml'
994
1025
  end
995
1026
  parser = MathML::LaTeX::Parser.new(symbol: MathML::Symbol::CharacterReference)
996
1027
  %Q(<span class="equation">#{parser.parse(str, nil)}</span>)
@@ -1036,7 +1067,7 @@ EOS
1036
1067
  def inline_bib(id)
1037
1068
  %Q(<a href="#{@book.bib_file.gsub(/\.re\Z/, ".#{@book.config['htmlext']}")}#bib-#{normalize_id(id)}">[#{@chapter.bibpaper(id).number}]</a>)
1038
1069
  rescue KeyError
1039
- error "unknown bib: #{id}"
1070
+ app_error "unknown bib: #{id}"
1040
1071
  end
1041
1072
 
1042
1073
  def inline_hd_chap(chap, id)
@@ -1053,7 +1084,7 @@ EOS
1053
1084
  str
1054
1085
  end
1055
1086
  rescue KeyError
1056
- error "unknown headline: #{id}"
1087
+ app_error "unknown headline: #{id}"
1057
1088
  end
1058
1089
 
1059
1090
  def column_label(id, chapter = @chapter)
@@ -1069,7 +1100,7 @@ EOS
1069
1100
  I18n.t('column', compile_inline(chapter.column(id).caption))
1070
1101
  end
1071
1102
  rescue KeyError
1072
- error "unknown column: #{id}"
1103
+ app_error "unknown column: #{id}"
1073
1104
  end
1074
1105
 
1075
1106
  def inline_list(id)
@@ -1196,7 +1227,7 @@ EOS
1196
1227
  begin
1197
1228
  %Q(<img src="#{@chapter.image(id).path.sub(%r{\A\./}, '')}" alt="[#{id}]" />)
1198
1229
  rescue
1199
- warn "image not bound: #{id}"
1230
+ warn "image not bound: #{id}", location: location
1200
1231
  %Q(<pre>missing image: #{id}</pre>)
1201
1232
  end
1202
1233
  end
@@ -47,6 +47,7 @@ module ReVIEW
47
47
  end
48
48
 
49
49
  def builder_init_file
50
+ super
50
51
  @warns = []
51
52
  @errors = []
52
53
  @section = 0
@@ -200,7 +201,7 @@ module ReVIEW
200
201
 
201
202
  def ol_begin
202
203
  puts '<ol>'
203
- @ol_num ||= 1
204
+ @ol_num ||= 1 # rubocop:disable Naming/MemoizedInstanceVariableName
204
205
  end
205
206
 
206
207
  def ol_item(lines, num)
@@ -265,7 +266,7 @@ module ReVIEW
265
266
  I18n.t('column', compile_inline(chapter.column(id).caption))
266
267
  end
267
268
  rescue KeyError
268
- error "unknown column: #{id}"
269
+ app_error "unknown column: #{id}"
269
270
  end
270
271
 
271
272
  def inline_list(id)
@@ -436,7 +437,7 @@ module ReVIEW
436
437
  print '</pre>'
437
438
  image_header(id, caption) unless caption_top?('image')
438
439
  puts '</img>'
439
- warn "image not bound: #{id}"
440
+ warn "image not bound: #{id}", location: location
440
441
  end
441
442
 
442
443
  def image_header(id, caption)
@@ -502,7 +503,7 @@ module ReVIEW
502
503
  table_header(id, caption)
503
504
  end
504
505
  rescue KeyError
505
- error "no such table: #{id}"
506
+ app_error "no such table: #{id}"
506
507
  end
507
508
  puts '</table>'
508
509
  @tsize = nil
@@ -524,7 +525,7 @@ module ReVIEW
524
525
  col2 = rows[rows.length - 1].split(table_row_separator_regexp).length
525
526
  @col = col2 if col2 > @col
526
527
  end
527
- error 'no rows in the table' if rows.empty?
528
+ app_error 'no rows in the table' if rows.empty?
528
529
  [sepidx, rows]
529
530
  end
530
531
 
@@ -539,11 +540,11 @@ module ReVIEW
539
540
  cellwidth.size.times do |n|
540
541
  cellwidth[n] = cellwidth[n].to_f / @book.config['pt_to_mm_unit'].to_f
541
542
  totallength += cellwidth[n]
542
- warn "total length exceeds limit for table: #{@table_id}" if totallength > @tablewidth
543
+ warn "total length exceeds limit for table: #{@table_id}", location: location if totallength > @tablewidth
543
544
  end
544
545
  if cellwidth.size < @col
545
546
  cw = (@tablewidth - totallength) / (@col - cellwidth.size)
546
- warn "auto cell sizing exceeds limit for table: #{@table_id}" if cw <= 0
547
+ warn "auto cell sizing exceeds limit for table: #{@table_id}", location: location if cw <= 0
547
548
  (cellwidth.size..(@col - 1)).each { |i| cellwidth[i] = cw }
548
549
  end
549
550
  end
@@ -627,7 +628,7 @@ module ReVIEW
627
628
  end
628
629
  puts '</table>'
629
630
  else
630
- warn "image not bound: #{id}" if @strict
631
+ warn "image not bound: #{id}", location: location if @strict
631
632
  image_dummy(id, caption, lines)
632
633
  end
633
634
  end
@@ -656,7 +657,7 @@ module ReVIEW
656
657
  def inline_fn(id)
657
658
  %Q(<footnote>#{compile_inline(@chapter.footnote(id).content.strip)}</footnote>)
658
659
  rescue KeyError
659
- error "unknown footnote: #{id}"
660
+ app_error "unknown footnote: #{id}"
660
661
  end
661
662
 
662
663
  def compile_ruby(base, ruby)
@@ -718,7 +719,7 @@ module ReVIEW
718
719
  sprintf('&#x%x;', 9392 + str[0] - 65)
719
720
  end
720
721
  else
721
- error "can't parse maru: #{str}"
722
+ app_error "can't parse maru: #{str}"
722
723
  end
723
724
  end
724
725
 
@@ -780,7 +781,7 @@ module ReVIEW
780
781
  begin
781
782
  %Q(<Image href="file://#{@chapter.image(id).path.sub(%r{\A\./}, '')}" type="inline" />)
782
783
  rescue
783
- warn "image not bound: #{id}"
784
+ warn "image not bound: #{id}", location: location
784
785
  ''
785
786
  end
786
787
  end
@@ -1127,7 +1128,7 @@ module ReVIEW
1127
1128
  begin
1128
1129
  puts %Q(<Image href="file://#{@chapter.image(id).path.sub(%r{\A\./}, '')}"#{metrics} />)
1129
1130
  rescue
1130
- warn %Q(image not bound: #{id})
1131
+ warn %Q(image not bound: #{id}), location: location
1131
1132
  end
1132
1133
  unless caption_top?('image')
1133
1134
  puts %Q(<caption>#{compile_inline(caption)}</caption>) if caption.present?
@@ -1188,7 +1189,7 @@ module ReVIEW
1188
1189
  if chs2.size == 3
1189
1190
  chs = chs2
1190
1191
  else
1191
- error '--chapsplitter must have exactly 3 parameters with comma.'
1192
+ app_error '--chapsplitter must have exactly 3 parameters with comma.'
1192
1193
  end
1193
1194
  end
1194
1195
  s = "#{chs[0]}#{@book.chapter_index.number(id)}#{chs[1]}#{@book.chapter_index.title(id)}#{chs[2]}"
@@ -1206,7 +1207,7 @@ module ReVIEW
1206
1207
  end
1207
1208
  end
1208
1209
  rescue KeyError
1209
- error "unknown chapter: #{id}"
1210
+ app_error "unknown chapter: #{id}"
1210
1211
  end
1211
1212
 
1212
1213
  def inline_chap(id)
@@ -1216,7 +1217,7 @@ module ReVIEW
1216
1217
  @book.chapter_index.number(id)
1217
1218
  end
1218
1219
  rescue KeyError
1219
- error "unknown chapter: #{id}"
1220
+ app_error "unknown chapter: #{id}"
1220
1221
  end
1221
1222
 
1222
1223
  def inline_title(id)
@@ -1227,7 +1228,7 @@ module ReVIEW
1227
1228
  title
1228
1229
  end
1229
1230
  rescue KeyError
1230
- error "unknown chapter: #{id}"
1231
+ app_error "unknown chapter: #{id}"
1231
1232
  end
1232
1233
 
1233
1234
  def source(lines, caption = nil, lang = nil)
@@ -1270,7 +1271,7 @@ module ReVIEW
1270
1271
  def inline_bib(id)
1271
1272
  %Q(<span type='bibref' idref='#{id}'>[#{@chapter.bibpaper(id).number}]</span>)
1272
1273
  rescue KeyError
1273
- error "unknown bib: #{id}"
1274
+ app_error "unknown bib: #{id}"
1274
1275
  end
1275
1276
 
1276
1277
  def inline_hd_chap(chap, id)
@@ -1281,7 +1282,7 @@ module ReVIEW
1281
1282
  I18n.t('hd_quote_without_number', compile_inline(chap.headline(id).caption))
1282
1283
  end
1283
1284
  rescue KeyError
1284
- error "unknown headline: #{id}"
1285
+ app_error "unknown headline: #{id}"
1285
1286
  end
1286
1287
 
1287
1288
  def inline_recipe(id)
@@ -16,10 +16,12 @@ require 'review/yamlloader'
16
16
  require 'review/idgxmlbuilder'
17
17
  require 'review/version'
18
18
  require 'review/makerhelper'
19
+ require 'review/loggable'
19
20
 
20
21
  module ReVIEW
21
22
  class IDGXMLMaker
22
23
  include MakerHelper
24
+ include Loggable
23
25
 
24
26
  attr_accessor :config, :basedir
25
27
 
@@ -27,15 +29,7 @@ module ReVIEW
27
29
  @basedir = nil
28
30
  @logger = ReVIEW.logger
29
31
  @plaintext = nil
30
- end
31
-
32
- def error(msg)
33
- @logger.error "#{File.basename($PROGRAM_NAME, '.*')}: #{msg}"
34
- exit 1
35
- end
36
-
37
- def warn(msg)
38
- @logger.warn "#{File.basename($PROGRAM_NAME, '.*')}: #{msg}"
32
+ @compile_errors = nil
39
33
  end
40
34
 
41
35
  def self.execute(*args)
@@ -78,7 +72,7 @@ module ReVIEW
78
72
 
79
73
  def execute(*args)
80
74
  cmd_config, yamlfile = parse_opts(args)
81
- error "#{yamlfile} not found." unless File.exist?(yamlfile)
75
+ error! "#{yamlfile} not found." unless File.exist?(yamlfile)
82
76
 
83
77
  @config = ReVIEW::Configure.create(maker: 'idgxmlmaker',
84
78
  yamlfile: yamlfile,
@@ -90,7 +84,7 @@ module ReVIEW
90
84
  rescue ApplicationError => e
91
85
  raise if @config['debug']
92
86
 
93
- error(e.message)
87
+ error! e.message
94
88
  end
95
89
  end
96
90
 
@@ -106,6 +100,10 @@ module ReVIEW
106
100
  end
107
101
 
108
102
  build_body(@path, yamlfile)
103
+
104
+ if @compile_errors
105
+ app_error 'compile error, No IDGXML file output.'
106
+ end
109
107
  end
110
108
 
111
109
  def apply_filter(xmlfile)
@@ -180,8 +178,9 @@ module ReVIEW
180
178
  @converter.convert(filename, File.join(basetmpdir, xmlfile))
181
179
  apply_filter(File.join(basetmpdir, xmlfile))
182
180
  rescue => e
183
- warn "compile error in #{filename} (#{e.class})"
184
- warn e.message
181
+ @compile_errors = true
182
+ error "compile error in #{filename} (#{e.class})"
183
+ error e.message
185
184
  end
186
185
  end
187
186
  end