review 5.0.0 → 5.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (160) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby-tex.yml +31 -0
  3. data/.github/workflows/ruby-win.yml +3 -3
  4. data/.github/workflows/ruby.yml +1 -1
  5. data/.rubocop.yml +15 -7
  6. data/NEWS.ja.md +108 -0
  7. data/NEWS.md +108 -0
  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 +2 -5
  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 +2 -4
  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 +21 -5
  28. data/doc/config.yml.sample-simple +1 -1
  29. data/doc/format.ja.md +21 -10
  30. data/doc/format.md +21 -10
  31. data/doc/quickstart.ja.md +11 -1
  32. data/doc/quickstart.md +11 -2
  33. data/lib/review.rb +1 -1
  34. data/lib/review/book/base.rb +4 -0
  35. data/lib/review/book/book_unit.rb +3 -0
  36. data/lib/review/book/chapter.rb +3 -0
  37. data/lib/review/book/index.rb +1 -0
  38. data/lib/review/book/volume.rb +1 -0
  39. data/lib/review/builder.rb +8 -1
  40. data/lib/review/call_hook.rb +20 -0
  41. data/lib/review/catalog.rb +1 -0
  42. data/lib/review/compiler.rb +27 -10
  43. data/lib/review/configure.rb +64 -7
  44. data/lib/review/epubmaker.rb +93 -96
  45. data/lib/review/epubmaker/content.rb +113 -0
  46. data/lib/review/epubmaker/epubcommon.rb +372 -0
  47. data/lib/review/epubmaker/epubv2.rb +178 -0
  48. data/lib/review/epubmaker/epubv3.rb +231 -0
  49. data/lib/review/epubmaker/producer.rb +168 -0
  50. data/lib/review/epubmaker/reviewheaderlistener.rb +12 -2
  51. data/lib/review/epubmaker/zip_exporter.rb +84 -0
  52. data/lib/review/exception.rb +6 -0
  53. data/lib/review/htmlbuilder.rb +36 -49
  54. data/lib/review/htmlutils.rb +1 -1
  55. data/lib/review/i18n.rb +1 -0
  56. data/lib/review/idgxmlbuilder.rb +33 -30
  57. data/lib/review/idgxmlmaker.rb +3 -1
  58. data/lib/review/img_math.rb +245 -0
  59. data/lib/review/index_builder.rb +1 -0
  60. data/lib/review/init.rb +4 -4
  61. data/lib/review/latexbox.rb +58 -0
  62. data/lib/review/latexbuilder.rb +30 -19
  63. data/lib/review/latexutils.rb +9 -1
  64. data/lib/review/lineinput.rb +112 -2
  65. data/lib/review/logger.rb +41 -2
  66. data/lib/review/makerhelper.rb +2 -205
  67. data/lib/review/markdownbuilder.rb +32 -1
  68. data/lib/review/pdfmaker.rb +31 -29
  69. data/lib/review/plaintextbuilder.rb +9 -1
  70. data/lib/review/preprocessor.rb +12 -6
  71. data/lib/review/rstbuilder.rb +1 -1
  72. data/lib/review/sec_counter.rb +1 -0
  73. data/lib/review/template.rb +6 -0
  74. data/lib/review/textmaker.rb +11 -7
  75. data/lib/review/textutils.rb +2 -10
  76. data/lib/review/tocprinter.rb +85 -68
  77. data/lib/review/topbuilder.rb +18 -11
  78. data/lib/review/update.rb +5 -6
  79. data/lib/review/version.rb +1 -1
  80. data/lib/review/volumeprinter.rb +4 -5
  81. data/lib/review/webmaker.rb +18 -13
  82. data/lib/review/webtocprinter.rb +10 -9
  83. data/lib/review/yamlloader.rb +2 -1
  84. data/review.gemspec +5 -3
  85. data/samples/sample-book/src/config-epub2.yml +1 -1
  86. data/samples/sample-book/src/config.yml +1 -1
  87. data/samples/sample-book/src/lib/tasks/review.rake +17 -1
  88. data/samples/syntax-book/ch01.re +1 -1
  89. data/samples/syntax-book/ch02.re +21 -6
  90. data/samples/syntax-book/ch03.re +1 -1
  91. data/samples/syntax-book/images/img3-2.png +0 -0
  92. data/templates/html/_colophon.html.erb +23 -0
  93. data/templates/html/_colophon_history.html.erb +9 -0
  94. data/templates/html/_cover.html.erb +10 -0
  95. data/templates/html/_part_body.html.erb +6 -0
  96. data/templates/html/_titlepage.html.erb +20 -0
  97. data/templates/html/layout-html5.html.erb +6 -0
  98. data/templates/html/layout-xhtml1.html.erb +6 -0
  99. data/templates/latex/config.erb +8 -0
  100. data/templates/latex/review-jlreq/review-base.sty +4 -5
  101. data/templates/latex/review-jlreq/review-jlreq.cls +10 -2
  102. data/templates/latex/review-jlreq/review-style.sty +6 -1
  103. data/templates/latex/review-jlreq/review-tcbox.sty +348 -0
  104. data/templates/latex/review-jlreq/reviewmacro.sty +5 -0
  105. data/templates/latex/review-jsbook/review-base.sty +5 -7
  106. data/templates/latex/review-jsbook/review-jsbook.cls +10 -2
  107. data/templates/latex/review-jsbook/review-style.sty +6 -1
  108. data/templates/latex/review-jsbook/review-tcbox.sty +348 -0
  109. data/templates/latex/review-jsbook/reviewmacro.sty +5 -0
  110. data/templates/opf/epubv2.opf.erb +7 -7
  111. data/templates/opf/epubv3.opf.erb +7 -7
  112. data/templates/opf/opf_manifest_epubv2.opf.erb +10 -0
  113. data/templates/opf/opf_manifest_epubv3.opf.erb +10 -0
  114. data/templates/opf/opf_metainfo_epubv2.opf.erb +17 -0
  115. data/templates/opf/opf_metainfo_epubv3.opf.erb +49 -0
  116. data/templates/opf/opf_tocx_epubv2.opf.erb +9 -0
  117. data/templates/opf/opf_tocx_epubv3.opf.erb +17 -0
  118. data/templates/web/html/layout-html5.html.erb +6 -5
  119. data/templates/web/html/layout-xhtml1.html.erb +6 -0
  120. data/test/assets/header_listener.html +35 -0
  121. data/test/assets/img_math/img1.png +0 -0
  122. data/test/assets/img_math/img2.png +0 -0
  123. data/test/assets/img_math/img3.png +0 -0
  124. data/test/assets/syntax_book_index_detail.txt +58 -0
  125. data/test/assets/test_template.tex +4 -1
  126. data/test/assets/test_template_backmatter.tex +4 -1
  127. data/test/run_test.rb +1 -1
  128. data/test/test_book_chapter.rb +2 -2
  129. data/test/test_catalog_converter_cmd.rb +1 -1
  130. data/test/test_epub3maker.rb +168 -124
  131. data/test/test_epubmaker.rb +243 -131
  132. data/test/test_epubmaker_cmd.rb +2 -2
  133. data/test/test_helper.rb +5 -4
  134. data/test/test_htmlbuilder.rb +64 -6
  135. data/test/test_idgxmlbuilder.rb +13 -0
  136. data/test/test_idgxmlmaker_cmd.rb +7 -3
  137. data/test/test_img_math.rb +111 -0
  138. data/test/test_indexbuilder.rb +5 -5
  139. data/test/test_latexbuilder.rb +107 -4
  140. data/test/test_lineinput.rb +20 -93
  141. data/test/test_markdownbuilder.rb +29 -0
  142. data/test/test_pdfmaker.rb +71 -0
  143. data/test/test_pdfmaker_cmd.rb +2 -2
  144. data/test/test_plaintextbuilder.rb +10 -18
  145. data/test/test_reviewheaderlistener.rb +49 -0
  146. data/test/test_template.rb +12 -2
  147. data/test/test_textmaker_cmd.rb +5 -1
  148. data/test/test_tocprinter.rb +46 -0
  149. data/test/test_topbuilder.rb +6 -1
  150. data/test/test_update.rb +34 -34
  151. data/test/test_zip_exporter.rb +5 -6
  152. metadata +91 -17
  153. data/lib/epubmaker.rb +0 -23
  154. data/lib/epubmaker/content.rb +0 -111
  155. data/lib/epubmaker/epubcommon.rb +0 -449
  156. data/lib/epubmaker/epubv2.rb +0 -142
  157. data/lib/epubmaker/epubv3.rb +0 -235
  158. data/lib/epubmaker/producer.rb +0 -375
  159. data/lib/epubmaker/zip_exporter.rb +0 -81
  160. data/lib/lineinput.rb +0 -155
@@ -10,10 +10,16 @@
10
10
 
11
11
  module ReVIEW
12
12
  class Error < ::StandardError; end
13
+
13
14
  class ApplicationError < Error; end
15
+
14
16
  class ConfigError < ApplicationError; end
17
+
15
18
  class CompileError < ApplicationError; end
19
+
16
20
  class SyntaxError < CompileError; end
21
+
17
22
  class FileNotFound < ApplicationError; end
23
+
18
24
  class KeyError < CompileError; end
19
25
  end
@@ -53,6 +53,7 @@ module ReVIEW
53
53
  @first_line_num = nil
54
54
  @body_ext = nil
55
55
  @toc = nil
56
+ @javascripts = []
56
57
  end
57
58
  private :builder_init_file
58
59
 
@@ -74,6 +75,7 @@ module ReVIEW
74
75
  if !File.exist?(layout_file) && File.exist?(File.join(@book.basedir, 'layouts', 'layout.erb'))
75
76
  raise ReVIEW::ConfigError, 'layout.erb is obsoleted. Please use layout.html.erb.'
76
77
  end
78
+
77
79
  if File.exist?(layout_file)
78
80
  if ENV['REVIEW_SAFE_MODE'].to_i & 4 > 0
79
81
  warn %Q(user's layout is prohibited in safe mode. ignored.)
@@ -100,6 +102,11 @@ module ReVIEW
100
102
  @toc = ReVIEW::WEBTOCPrinter.book_to_string(@book)
101
103
  end
102
104
 
105
+ if @book.config['math_format'] == 'mathjax'
106
+ @javascripts.push(%Q(<script>MathJax = { tex: { inlineMath: [['\\\\(', '\\\\)']] }, svg: { fontCache: 'global' } };</script>))
107
+ @javascripts.push(%Q(<script type="text/javascript" id="MathJax-script" async="true" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>))
108
+ end
109
+
103
110
  ReVIEW::Template.load(layoutfile).result(binding)
104
111
  end
105
112
 
@@ -149,6 +156,7 @@ module ReVIEW
149
156
  @nonum_counter += 1
150
157
  puts if level > 1
151
158
  return unless caption.present?
159
+
152
160
  if label
153
161
  puts %Q(<h#{level} id="#{normalize_id(label)}">#{compile_inline(caption)}</h#{level}>)
154
162
  else
@@ -164,6 +172,7 @@ module ReVIEW
164
172
  @nonum_counter += 1
165
173
  puts if level > 1
166
174
  return unless caption.present?
175
+
167
176
  if label
168
177
  puts %Q(<h#{level} id="#{normalize_id(label)}" notoc="true">#{compile_inline(caption)}</h#{level}>)
169
178
  else
@@ -179,6 +188,7 @@ module ReVIEW
179
188
  @nonum_counter += 1
180
189
  puts '' if level > 1
181
190
  return unless caption.present?
191
+
182
192
  if label
183
193
  puts %Q(<a id="#{normalize_id(label)}" /><h#{level} id="#{normalize_id(label)}" hidden="true">#{compile_inline(caption)}</h#{level}>)
184
194
  else
@@ -603,24 +613,27 @@ module ReVIEW
603
613
 
604
614
  def texequation_body(lines)
605
615
  puts %Q(<div class="equation">)
606
- if @book.config['mathml']
607
- require 'math_ml'
608
- require 'math_ml/symbol/character_reference'
616
+ if @book.config['math_format'] == 'mathml'
617
+ begin
618
+ require 'math_ml'
619
+ require 'math_ml/symbol/character_reference'
620
+ rescue LoadError
621
+ error 'not found math_ml'
622
+ end
609
623
  p = MathML::LaTeX::Parser.new(symbol: MathML::Symbol::CharacterReference)
610
624
  print p.parse(lines.join("\n") + "\n", true)
611
- elsif @book.config['imgmath']
625
+ elsif @book.config['math_format'] == 'mathjax'
626
+ puts "$$#{lines.join("\n")}$$"
627
+ elsif @book.config['math_format'] == 'imgmath'
612
628
  fontsize = @book.config['imgmath_options']['fontsize'].to_f
613
629
  lineheight = @book.config['imgmath_options']['lineheight'].to_f
614
630
  math_str = "\\begin{equation*}\n\\fontsize{#{fontsize}}{#{lineheight}}\\selectfont\n#{lines.join("\n")}\n\\end{equation*}\n"
615
631
  key = Digest::SHA256.hexdigest(math_str)
616
- math_dir = File.join(@book.config['imagedir'], '_review_math')
617
- Dir.mkdir(math_dir) unless Dir.exist?(math_dir)
618
- img_path = File.join(math_dir, "_gen_#{key}.#{@book.config['imgmath_options']['format']}")
619
632
  if @book.config.check_version('2', exception: false)
620
- make_math_image(math_str, img_path)
633
+ img_path = @img_math.make_math_image(math_str, key)
621
634
  puts %Q(<img src="#{img_path}" />)
622
635
  else
623
- defer_math_image(math_str, img_path, key)
636
+ img_path = @img_math.defer_math_image(math_str, key)
624
637
  puts %Q(<img src="#{img_path}" class="math_gen_#{key}" alt="#{escape(lines.join(' '))}" />)
625
638
  end
626
639
  else
@@ -761,6 +774,7 @@ module ReVIEW
761
774
 
762
775
  def comment(lines, comment = nil)
763
776
  return unless @book.config['draft']
777
+
764
778
  lines ||= []
765
779
  lines.unshift(escape(comment)) unless comment.blank?
766
780
  str = lines.join('<br />')
@@ -971,22 +985,25 @@ EOS
971
985
  end
972
986
 
973
987
  def inline_m(str)
974
- if @book.config['mathml']
975
- require 'math_ml'
976
- require 'math_ml/symbol/character_reference'
988
+ if @book.config['math_format'] == 'mathml'
989
+ begin
990
+ require 'math_ml'
991
+ require 'math_ml/symbol/character_reference'
992
+ rescue LoadError
993
+ error 'not found math_ml'
994
+ end
977
995
  parser = MathML::LaTeX::Parser.new(symbol: MathML::Symbol::CharacterReference)
978
996
  %Q(<span class="equation">#{parser.parse(str, nil)}</span>)
979
- elsif @book.config['imgmath']
997
+ elsif @book.config['math_format'] == 'mathjax'
998
+ %Q(<span class="equation">\\( #{str} \\)</span>)
999
+ elsif @book.config['math_format'] == 'imgmath'
980
1000
  math_str = '$' + str + '$'
981
1001
  key = Digest::SHA256.hexdigest(str)
982
- math_dir = File.join(@book.config['imagedir'], '_review_math')
983
- Dir.mkdir(math_dir) unless Dir.exist?(math_dir)
984
- img_path = File.join(math_dir, "_gen_#{key}.#{@book.config['imgmath_options']['format']}")
985
1002
  if @book.config.check_version('2', exception: false)
986
- make_math_image(math_str, img_path)
1003
+ img_path = @img_math.make_math_image(math_str, key)
987
1004
  %Q(<span class="equation"><img src="#{img_path}" /></span>)
988
1005
  else
989
- defer_math_image(math_str, img_path, key)
1006
+ img_path = @img_math.defer_math_image(math_str, key)
990
1007
  %Q(<span class="equation"><img src="#{img_path}" class="math_gen_#{key}" alt="#{escape(str)}" /></span>)
991
1008
  end
992
1009
  else
@@ -1030,7 +1047,7 @@ EOS
1030
1047
  str = I18n.t('hd_quote_without_number', compile_inline(chap.headline(id).caption))
1031
1048
  end
1032
1049
  if @book.config['chapterlink']
1033
- anchor = 'h' + n.gsub('.', '-')
1050
+ anchor = 'h' + n.tr('.', '-')
1034
1051
  %Q(<a href="#{chap.id}#{extname}##{anchor}">#{str}</a>)
1035
1052
  else
1036
1053
  str
@@ -1240,35 +1257,5 @@ EOS
1240
1257
  def olnum(num)
1241
1258
  @ol_num = num.to_i
1242
1259
  end
1243
-
1244
- def make_math_image(str, path, fontsize = 12)
1245
- # Re:VIEW 2 compatibility
1246
- fontsize2 = (fontsize * 1.2).round.to_i
1247
- texsrc = <<-EOB
1248
- \\documentclass[12pt]{article}
1249
- \\usepackage[utf8]{inputenc}
1250
- \\usepackage{amsmath}
1251
- \\usepackage{amsthm}
1252
- \\usepackage{amssymb}
1253
- \\usepackage{amsfonts}
1254
- \\usepackage{anyfontsize}
1255
- \\usepackage{bm}
1256
- \\pagestyle{empty}
1257
-
1258
- \\begin{document}
1259
- \\fontsize{#{fontsize}}{#{fontsize2}}\\selectfont #{str}
1260
- \\end{document}
1261
- EOB
1262
- Dir.mktmpdir do |tmpdir|
1263
- tex_path = File.join(tmpdir, 'tmpmath.tex')
1264
- dvi_path = File.join(tmpdir, 'tmpmath.dvi')
1265
- File.write(tex_path, texsrc)
1266
- cmd = "latex --interaction=nonstopmode --output-directory=#{tmpdir} #{tex_path} && dvipng -T tight -z9 -o #{path} #{dvi_path}"
1267
- out, status = Open3.capture2e(cmd)
1268
- unless status.success?
1269
- error "latex compile error\n\nError log:\n" + out
1270
- end
1271
- end
1272
- end
1273
1260
  end
1274
1261
  end # module ReVIEW
@@ -134,7 +134,7 @@ module ReVIEW
134
134
  elsif id =~ /\A[0-9_.-][a-z0-9_.-]*\Z/i
135
135
  "id_#{id}" # dummy prefix
136
136
  else
137
- "id_#{CGI.escape(id.gsub('_', '__')).gsub('%', '_').gsub('+', '-')}" # escape all
137
+ "id_#{CGI.escape(id.gsub('_', '__')).tr('%', '_').tr('+', '-')}" # escape all
138
138
  end
139
139
  end
140
140
  end
data/lib/review/i18n.rb CHANGED
@@ -91,6 +91,7 @@ module ReVIEW
91
91
  else
92
92
  user_i18n.each do |key, values|
93
93
  raise KeyError, "Invalid locale file: #{path}" unless values.is_a?(Hash)
94
+
94
95
  @store[key].merge!(values)
95
96
  end
96
97
  end
@@ -103,15 +103,9 @@ module ReVIEW
103
103
  end
104
104
 
105
105
  def headline(level, label, caption)
106
+ output_close_sect_tags(level)
106
107
  case level
107
108
  when 1
108
- if @secttags
109
- print '</sect4>' if @subsubsubsection > 0
110
- print '</sect3>' if @subsubsection > 0
111
- print '</sect2>' if @subsection > 0
112
- print '</sect>' if @section > 0
113
- end
114
-
115
109
  print %Q(<chapter id="chap:#{@chapter.number}">) if @secttags
116
110
 
117
111
  @section = 0
@@ -119,12 +113,6 @@ module ReVIEW
119
113
  @subsubsection = 0
120
114
  @subsubsubsection = 0
121
115
  when 2
122
- if @secttags
123
- print '</sect4>' if @subsubsubsection > 0
124
- print '</sect3>' if @subsubsection > 0
125
- print '</sect2>' if @subsection > 0
126
- print '</sect>' if @section > 0
127
- end
128
116
  @section += 1
129
117
  print %Q(<sect id="sect:#{@chapter.number}.#{@section}">) if @secttags
130
118
 
@@ -132,30 +120,17 @@ module ReVIEW
132
120
  @subsubsection = 0
133
121
  @subsubsubsection = 0
134
122
  when 3
135
- if @secttags
136
- print '</sect4>' if @subsubsubsection > 0
137
- print '</sect3>' if @subsubsection > 0
138
- print '</sect2>' if @subsection > 0
139
- end
140
-
141
123
  @subsection += 1
142
124
  print %Q(<sect2 id="sect:#{@chapter.number}.#{@section}.#{@subsection}">) if @secttags
143
125
 
144
126
  @subsubsection = 0
145
127
  @subsubsubsection = 0
146
128
  when 4
147
- if @secttags
148
- print '</sect4>' if @subsubsubsection > 0
149
- print '</sect3>' if @subsubsection > 0
150
- end
151
-
152
129
  @subsubsection += 1
153
130
  print %Q(<sect3 id="sect:#{@chapter.number}.#{@section}.#{@subsection}.#{@subsubsection}">) if @secttags
154
131
 
155
132
  @subsubsubsection = 0
156
133
  when 5
157
- print '</sect4>' if @secttags && @subsubsubsection > 0
158
-
159
134
  @subsubsubsection += 1
160
135
  print %Q(<sect4 id="sect:#{@chapter.number}.#{@section}.#{@subsection}.#{@subsubsection}.#{@subsubsubsection}">) if @secttags
161
136
  when 6 # rubocop:disable Lint/EmptyWhen
@@ -170,6 +145,23 @@ module ReVIEW
170
145
  puts %Q(<title#{label} aid:pstyle="h#{level}">#{prefix}#{compile_inline(caption)}</title><?dtp level="#{level}" section="#{prefix}#{toccaption}"?>)
171
146
  end
172
147
 
148
+ def output_close_sect_tags(level)
149
+ if @secttags
150
+ if level <= 5 && @subsubsubsection > 0
151
+ print '</sect4>'
152
+ end
153
+ if level <= 4 && @subsubsection > 0
154
+ print '</sect3>'
155
+ end
156
+ if level <= 3 && @subsection > 0
157
+ print '</sect2>'
158
+ end
159
+ if level <= 2 && @section > 0
160
+ print '</sect>'
161
+ end
162
+ end
163
+ end
164
+
173
165
  def ul_begin
174
166
  level = block_given? ? yield : ''
175
167
  level = nil if level == 1
@@ -282,6 +274,7 @@ module ReVIEW
282
274
 
283
275
  def list_header(id, caption, _lang)
284
276
  return true unless caption.present?
277
+
285
278
  if get_chap.nil?
286
279
  puts %Q(<caption>#{I18n.t('list')}#{I18n.t('format_number_without_chapter', [@chapter.list(id).number])}#{I18n.t('caption_prefix_idgxml')}#{compile_inline(caption)}</caption>)
287
280
  else
@@ -448,6 +441,7 @@ module ReVIEW
448
441
 
449
442
  def image_header(id, caption)
450
443
  return true unless caption.present?
444
+
451
445
  if get_chap.nil?
452
446
  puts %Q(<caption>#{I18n.t('image')}#{I18n.t('format_number_without_chapter', [@chapter.image(id).number])}#{I18n.t('caption_prefix_idgxml')}#{compile_inline(caption)}</caption>)
453
447
  else
@@ -640,6 +634,7 @@ module ReVIEW
640
634
 
641
635
  def comment(lines, comment = nil)
642
636
  return unless @book.config['draft']
637
+
643
638
  lines ||= []
644
639
  lines.unshift(escape(comment)) unless comment.blank?
645
640
  str = lines.join("\n")
@@ -674,7 +669,7 @@ module ReVIEW
674
669
  then escape("#{word}(#{alt.strip})")
675
670
  else escape(word)
676
671
  end +
677
- '</keyword>' +
672
+ '</keyword>' +
678
673
  %Q(<index value="#{escape(word)}" />) +
679
674
  if alt
680
675
  alt.split(/\s*,\s*/).collect! { |e| %Q(<index value="#{escape(e.strip)}" />) }.join
@@ -773,6 +768,14 @@ module ReVIEW
773
768
  %Q(<underline>#{escape(str)}</underline>)
774
769
  end
775
770
 
771
+ def inline_ins(str)
772
+ %Q(<ins>#{escape(str)}</ins>)
773
+ end
774
+
775
+ def inline_del(str)
776
+ %Q(<del>#{escape(str)}</del>)
777
+ end
778
+
776
779
  def inline_icon(id)
777
780
  begin
778
781
  %Q(<Image href="file://#{@chapter.image(id).path.sub(%r{\A\./}, '')}" type="inline" />)
@@ -1182,10 +1185,10 @@ module ReVIEW
1182
1185
  chs = ['', '「', '」']
1183
1186
  if @book.config['chapref']
1184
1187
  chs2 = @book.config['chapref'].split(',')
1185
- if chs2.size != 3
1186
- error '--chapsplitter must have exactly 3 parameters with comma.'
1187
- else
1188
+ if chs2.size == 3
1188
1189
  chs = chs2
1190
+ else
1191
+ error '--chapsplitter must have exactly 3 parameters with comma.'
1189
1192
  end
1190
1193
  end
1191
1194
  s = "#{chs[0]}#{@book.chapter_index.number(id)}#{chs[1]}#{@book.chapter_index.title(id)}#{chs[2]}"
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2019 Kenshi Muto
1
+ # Copyright (c) 2019-2021 Kenshi Muto
2
2
  #
3
3
  # This program is free software.
4
4
  # You can distribute or modify this program under the terms of
@@ -86,8 +86,10 @@ module ReVIEW
86
86
  I18n.setup(@config['language'])
87
87
  begin
88
88
  generate_idgxml_files(yamlfile)
89
+ @logger.success("built #{build_path}")
89
90
  rescue ApplicationError => e
90
91
  raise if @config['debug']
92
+
91
93
  error(e.message)
92
94
  end
93
95
  end
@@ -0,0 +1,245 @@
1
+ require 'fileutils'
2
+ require 'shellwords'
3
+
4
+ module ReVIEW
5
+ class ImgMath
6
+ def initialize(config, path_name: '_review_math')
7
+ @config = config
8
+ @logger = ReVIEW.logger
9
+ @math_dir = File.join(@config['imagedir'], path_name)
10
+ @math_maps = {}
11
+ end
12
+
13
+ def cleanup_mathimg
14
+ if @config['math_format'] == 'imgmath' && Dir.exist?(@math_dir)
15
+ FileUtils.rm_rf(@math_dir)
16
+ end
17
+ end
18
+
19
+ def defer_math_image(str, key)
20
+ # for Re:VIEW >3
21
+ @math_maps[key] = str
22
+ File.join(@math_dir, "_gen_#{key}.#{@config['imgmath_options']['format']}")
23
+ end
24
+
25
+ def make_math_image(str, key, fontsize = 12)
26
+ # Re:VIEW 2 compatibility
27
+
28
+ img_path = File.join(@math_dir, "_gen_#{key}.#{@config['imgmath_options']['format']}")
29
+ Dir.mkdir(@math_dir) unless Dir.exist?(@math_dir)
30
+ fontsize2 = (fontsize * 1.2).round.to_i
31
+ texsrc = <<-EOB
32
+ \\documentclass[12pt]{article}
33
+ \\usepackage[utf8]{inputenc}
34
+ \\usepackage{amsmath}
35
+ \\usepackage{amsthm}
36
+ \\usepackage{amssymb}
37
+ \\usepackage{amsfonts}
38
+ \\usepackage{anyfontsize}
39
+ \\usepackage{bm}
40
+ \\pagestyle{empty}
41
+
42
+ \\begin{document}
43
+ \\fontsize{#{fontsize}}{#{fontsize2}}\\selectfont #{str}
44
+ \\end{document}
45
+ EOB
46
+ Dir.mktmpdir do |tmpdir|
47
+ tex_path = File.join(tmpdir, 'tmpmath.tex')
48
+ dvi_path = File.join(tmpdir, 'tmpmath.dvi')
49
+ File.write(tex_path, texsrc)
50
+ cmd = "latex --interaction=nonstopmode --output-directory=#{tmpdir} #{tex_path} && dvipng -T tight -z9 -o #{img_path} #{dvi_path}"
51
+ out, status = Open3.capture2e(cmd)
52
+ unless status.success?
53
+ raise ApplicationError, "latex compile error\n\nError log:\n" + out
54
+ end
55
+
56
+ img_path
57
+ end
58
+ end
59
+
60
+ def make_math_images
61
+ return if @math_maps.empty?
62
+
63
+ Dir.mkdir(@math_dir) unless Dir.exist?(@math_dir)
64
+ fontsize = @config['imgmath_options']['fontsize'].to_f
65
+ lineheight = @config['imgmath_options']['lineheight'].to_f
66
+
67
+ texsrc = default_imgmath_preamble
68
+ if @config['imgmath_options']['preamble_file'] && File.readable?(@config['imgmath_options']['preamble_file'])
69
+ texsrc = File.read(@config['imgmath_options']['preamble_file'])
70
+ end
71
+
72
+ texsrc << <<-EOB
73
+ \\begin{document}
74
+ \\fontsize{#{fontsize}}{#{lineheight}}\\selectfont
75
+ \\input{__IMGMATH_BODY__}
76
+ \\end{document}
77
+ EOB
78
+
79
+ math_real_dir = File.realpath(@math_dir)
80
+ Dir.mktmpdir do |tmpdir|
81
+ File.open(File.join(tmpdir, '__IMGMATH_BODY__.tex'), 'w') do |f|
82
+ @math_maps.keys.sort.each do |key|
83
+ f.puts "% #{key}"
84
+ f.puts @math_maps[key]
85
+ f.puts '\\clearpage'
86
+ f.puts
87
+ end
88
+ end
89
+
90
+ tex_path = File.join(tmpdir, '__IMGMATH__.tex')
91
+ File.write(tex_path, texsrc)
92
+
93
+ begin
94
+ case @config['imgmath_options']['converter']
95
+ when 'pdfcrop'
96
+ make_math_images_pdfcrop(tmpdir, tex_path, math_real_dir)
97
+ when 'dvipng'
98
+ make_math_images_dvipng(tmpdir, tex_path, math_real_dir)
99
+ else
100
+ error "unknown math converter error. imgmath_options/converter parameter should be 'pdfcrop' or 'dvipng'."
101
+ exit 1
102
+ end
103
+ rescue CompileError
104
+ FileUtils.cp([tex_path,
105
+ File.join(File.dirname(tex_path), '__IMGMATH__.log')],
106
+ math_real_dir)
107
+ error "LaTeX math compile error. See #{math_real_dir}/__IMGMATH__.log for details."
108
+ exit 1
109
+ end
110
+ end
111
+ @math_maps.clear
112
+ end
113
+
114
+ private
115
+
116
+ def error(msg)
117
+ @logger.error msg
118
+ end
119
+
120
+ def warn(msg)
121
+ @logger.warn msg
122
+ end
123
+
124
+ def default_imgmath_preamble
125
+ <<-EOB
126
+ \\documentclass[uplatex,a3paper,landscape]{jsarticle}
127
+ \\usepackage[deluxe,uplatex]{otf}
128
+ \\usepackage[T1]{fontenc}
129
+ \\usepackage{textcomp}
130
+ \\usepackage{lmodern}
131
+ \\usepackage[dvipdfmx]{graphicx}
132
+ \\usepackage[dvipdfmx,table]{xcolor}
133
+ \\usepackage[utf8]{inputenc}
134
+ \\usepackage{ascmac}
135
+ \\usepackage{float}
136
+ \\usepackage{alltt}
137
+ \\usepackage{amsmath}
138
+ \\usepackage{amssymb}
139
+ \\usepackage{amsfonts}
140
+ \\usepackage{anyfontsize}
141
+ \\usepackage{bm}
142
+ \\pagestyle{empty}
143
+ % \\setpaperwidth{1000mm}
144
+ EOB
145
+ end
146
+
147
+ def make_math_images_pdfcrop(dir, tex_path, math_real_dir)
148
+ Dir.chdir(dir) do
149
+ dvi_path = '__IMGMATH__.dvi'
150
+ pdf_path = '__IMGMATH__.pdf'
151
+ out, status = Open3.capture2e(*[@config['texcommand'], @config['texoptions'].shellsplit, tex_path].flatten.compact)
152
+ if !status.success? || (!File.exist?(dvi_path) && !File.exist?(pdf_path))
153
+ raise CompileError
154
+ end
155
+
156
+ if File.exist?(dvi_path)
157
+ out, status = Open3.capture2e(*[@config['dvicommand'], @config['dvioptions'].shellsplit, dvi_path].flatten.compact)
158
+ if !status.success? || !File.exist?(pdf_path)
159
+ warn "error in #{@config['dvicommand']}. Error log:\n#{out}"
160
+ raise CompileError
161
+ end
162
+ end
163
+ args = @config['imgmath_options']['pdfcrop_cmd'].shellsplit
164
+ args.map! do |m|
165
+ m.sub('%i', pdf_path).
166
+ sub('%o', '__IMGMATH__pdfcrop.pdf')
167
+ end
168
+ out, status = Open3.capture2e(*args)
169
+ unless status.success?
170
+ warn "error in pdfcrop. Error log:\n#{out}"
171
+ raise CompileError
172
+ end
173
+ pdf_path = '__IMGMATH__pdfcrop.pdf'
174
+ pdf_path2 = pdf_path
175
+
176
+ @math_maps.keys.sort.each_with_index do |key, idx|
177
+ page = idx + 1
178
+ if File.exist?(File.join(math_real_dir, "_gen_#{key}.#{@config['imgmath_options']['format']}"))
179
+ # made already
180
+ next
181
+ end
182
+
183
+ if @config['imgmath_options']['extract_singlepage']
184
+ # if extract_singlepage = true, split each page
185
+ args = @config['imgmath_options']['pdfextract_cmd'].shellsplit
186
+
187
+ args.map! do |m|
188
+ m.sub('%i', pdf_path).
189
+ sub('%o', "__IMGMATH__pdfcrop_p#{page}.pdf").
190
+ sub('%O', "__IMGMATH__pdfcrop_p#{page}").
191
+ sub('%p', page.to_s)
192
+ end
193
+ out, status = Open3.capture2e(*args)
194
+ unless status.success?
195
+ warn "error in pdf extracting. Error log:\n#{out}"
196
+ raise CompileError
197
+ end
198
+
199
+ pdf_path2 = "__IMGMATH__pdfcrop_p#{page}.pdf"
200
+ end
201
+
202
+ args = @config['imgmath_options']['pdfcrop_pixelize_cmd'].shellsplit
203
+ args.map! do |m|
204
+ m.sub('%i', pdf_path2).
205
+ sub('%t', @config['imgmath_options']['format']).
206
+ sub('%o', File.join(math_real_dir, "_gen_#{key}.#{@config['imgmath_options']['format']}")).
207
+ sub('%O', File.join(math_real_dir, "_gen_#{key}")).
208
+ sub('%p', page.to_s)
209
+ end
210
+ out, status = Open3.capture2e(*args)
211
+ unless status.success?
212
+ warn "error in pdf pixelizing. Error log:\n#{out}"
213
+ raise CompileError
214
+ end
215
+ end
216
+ end
217
+ end
218
+
219
+ def make_math_images_dvipng(dir, tex_path, math_real_dir)
220
+ Dir.chdir(dir) do
221
+ dvi_path = '__IMGMATH__.dvi'
222
+ out, status = Open3.capture2e(*[@config['texcommand'], @config['texoptions'].shellsplit, tex_path].flatten.compact)
223
+ if !status.success? || !File.exist?(dvi_path)
224
+ raise CompileError
225
+ end
226
+
227
+ @math_maps.keys.sort.each_with_index do |key, idx|
228
+ page = idx + 1
229
+ args = @config['imgmath_options']['dvipng_cmd'].shellsplit
230
+ args.map! do |m|
231
+ m.sub('%i', dvi_path).
232
+ sub('%o', File.join(math_real_dir, "_gen_#{key}.#{@config['imgmath_options']['format']}")).
233
+ sub('%O', File.join(math_real_dir, "_gen_#{key}")).
234
+ sub('%p', page.to_s)
235
+ end
236
+ out, status = Open3.capture2e(*args)
237
+ unless status.success?
238
+ warn "error in dvipng. Error log:\n#{out}"
239
+ raise CompileError
240
+ end
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end