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) 2008-2020 Minero Aoki, Kenshi Muto
1
+ # Copyright (c) 2008-2021 Minero Aoki, Kenshi Muto
2
2
  # 2002-2007 Minero Aoki
3
3
  #
4
4
  # This program is free software.
@@ -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
@@ -77,6 +78,8 @@ module ReVIEW
77
78
  end
78
79
 
79
80
  def result
81
+ check_printendnotes
82
+
80
83
  s = ''
81
84
  if @secttags
82
85
  s += '</sect4>' if @subsubsubsection > 0
@@ -103,15 +106,9 @@ module ReVIEW
103
106
  end
104
107
 
105
108
  def headline(level, label, caption)
109
+ output_close_sect_tags(level)
106
110
  case level
107
111
  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
112
  print %Q(<chapter id="chap:#{@chapter.number}">) if @secttags
116
113
 
117
114
  @section = 0
@@ -119,12 +116,6 @@ module ReVIEW
119
116
  @subsubsection = 0
120
117
  @subsubsubsection = 0
121
118
  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
119
  @section += 1
129
120
  print %Q(<sect id="sect:#{@chapter.number}.#{@section}">) if @secttags
130
121
 
@@ -132,30 +123,17 @@ module ReVIEW
132
123
  @subsubsection = 0
133
124
  @subsubsubsection = 0
134
125
  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
126
  @subsection += 1
142
127
  print %Q(<sect2 id="sect:#{@chapter.number}.#{@section}.#{@subsection}">) if @secttags
143
128
 
144
129
  @subsubsection = 0
145
130
  @subsubsubsection = 0
146
131
  when 4
147
- if @secttags
148
- print '</sect4>' if @subsubsubsection > 0
149
- print '</sect3>' if @subsubsection > 0
150
- end
151
-
152
132
  @subsubsection += 1
153
133
  print %Q(<sect3 id="sect:#{@chapter.number}.#{@section}.#{@subsection}.#{@subsubsection}">) if @secttags
154
134
 
155
135
  @subsubsubsection = 0
156
136
  when 5
157
- print '</sect4>' if @secttags && @subsubsubsection > 0
158
-
159
137
  @subsubsubsection += 1
160
138
  print %Q(<sect4 id="sect:#{@chapter.number}.#{@section}.#{@subsection}.#{@subsubsection}.#{@subsubsubsection}">) if @secttags
161
139
  when 6 # rubocop:disable Lint/EmptyWhen
@@ -170,6 +148,23 @@ module ReVIEW
170
148
  puts %Q(<title#{label} aid:pstyle="h#{level}">#{prefix}#{compile_inline(caption)}</title><?dtp level="#{level}" section="#{prefix}#{toccaption}"?>)
171
149
  end
172
150
 
151
+ def output_close_sect_tags(level)
152
+ if @secttags
153
+ if level <= 5 && @subsubsubsection > 0
154
+ print '</sect4>'
155
+ end
156
+ if level <= 4 && @subsubsection > 0
157
+ print '</sect3>'
158
+ end
159
+ if level <= 3 && @subsection > 0
160
+ print '</sect2>'
161
+ end
162
+ if level <= 2 && @section > 0
163
+ print '</sect>'
164
+ end
165
+ end
166
+ end
167
+
173
168
  def ul_begin
174
169
  level = block_given? ? yield : ''
175
170
  level = nil if level == 1
@@ -208,7 +203,7 @@ module ReVIEW
208
203
 
209
204
  def ol_begin
210
205
  puts '<ol>'
211
- @ol_num ||= 1
206
+ @ol_num ||= 1 # rubocop:disable Naming/MemoizedInstanceVariableName
212
207
  end
213
208
 
214
209
  def ol_item(lines, num)
@@ -273,7 +268,7 @@ module ReVIEW
273
268
  I18n.t('column', compile_inline(chapter.column(id).caption))
274
269
  end
275
270
  rescue KeyError
276
- error "unknown column: #{id}"
271
+ app_error "unknown column: #{id}"
277
272
  end
278
273
 
279
274
  def inline_list(id)
@@ -282,6 +277,7 @@ module ReVIEW
282
277
 
283
278
  def list_header(id, caption, _lang)
284
279
  return true unless caption.present?
280
+
285
281
  if get_chap.nil?
286
282
  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
283
  else
@@ -443,11 +439,12 @@ module ReVIEW
443
439
  print '</pre>'
444
440
  image_header(id, caption) unless caption_top?('image')
445
441
  puts '</img>'
446
- warn "image not bound: #{id}"
442
+ warn "image not bound: #{id}", location: location
447
443
  end
448
444
 
449
445
  def image_header(id, caption)
450
446
  return true unless caption.present?
447
+
451
448
  if get_chap.nil?
452
449
  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
450
  else
@@ -508,7 +505,7 @@ module ReVIEW
508
505
  table_header(id, caption)
509
506
  end
510
507
  rescue KeyError
511
- error "no such table: #{id}"
508
+ app_error "no such table: #{id}"
512
509
  end
513
510
  puts '</table>'
514
511
  @tsize = nil
@@ -530,7 +527,7 @@ module ReVIEW
530
527
  col2 = rows[rows.length - 1].split(table_row_separator_regexp).length
531
528
  @col = col2 if col2 > @col
532
529
  end
533
- error 'no rows in the table' if rows.empty?
530
+ app_error 'no rows in the table' if rows.empty?
534
531
  [sepidx, rows]
535
532
  end
536
533
 
@@ -545,11 +542,11 @@ module ReVIEW
545
542
  cellwidth.size.times do |n|
546
543
  cellwidth[n] = cellwidth[n].to_f / @book.config['pt_to_mm_unit'].to_f
547
544
  totallength += cellwidth[n]
548
- warn "total length exceeds limit for table: #{@table_id}" if totallength > @tablewidth
545
+ warn "total length exceeds limit for table: #{@table_id}", location: location if totallength > @tablewidth
549
546
  end
550
547
  if cellwidth.size < @col
551
548
  cw = (@tablewidth - totallength) / (@col - cellwidth.size)
552
- warn "auto cell sizing exceeds limit for table: #{@table_id}" if cw <= 0
549
+ warn "auto cell sizing exceeds limit for table: #{@table_id}", location: location if cw <= 0
553
550
  (cellwidth.size..(@col - 1)).each { |i| cellwidth[i] = cw }
554
551
  end
555
552
  end
@@ -633,13 +630,14 @@ module ReVIEW
633
630
  end
634
631
  puts '</table>'
635
632
  else
636
- warn "image not bound: #{id}" if @strict
633
+ warn "image not bound: #{id}", location: location if @strict
637
634
  image_dummy(id, caption, lines)
638
635
  end
639
636
  end
640
637
 
641
638
  def comment(lines, comment = nil)
642
639
  return unless @book.config['draft']
640
+
643
641
  lines ||= []
644
642
  lines.unshift(escape(comment)) unless comment.blank?
645
643
  str = lines.join("\n")
@@ -661,7 +659,25 @@ module ReVIEW
661
659
  def inline_fn(id)
662
660
  %Q(<footnote>#{compile_inline(@chapter.footnote(id).content.strip)}</footnote>)
663
661
  rescue KeyError
664
- error "unknown footnote: #{id}"
662
+ app_error "unknown footnote: #{id}"
663
+ end
664
+
665
+ def inline_endnote(id)
666
+ %Q(<span type='endnoteref' idref='endnoteb-#{normalize_id(id)}'>(#{@chapter.endnote(id).number})</span>)
667
+ rescue KeyError
668
+ app_error "unknown endnote: #{id}"
669
+ end
670
+
671
+ def endnote_begin
672
+ puts '<endnotes>'
673
+ end
674
+
675
+ def endnote_end
676
+ puts '</endnotes>'
677
+ end
678
+
679
+ def endnote_item(id)
680
+ puts %Q(<endnote id='endnoteb-#{normalize_id(id)}'><span type='endnotenumber'>(#{@chapter.endnote(id).number})</span>\t#{compile_inline(@chapter.endnote(id).content)}</endnote>)
665
681
  end
666
682
 
667
683
  def compile_ruby(base, ruby)
@@ -674,7 +690,7 @@ module ReVIEW
674
690
  then escape("#{word}(#{alt.strip})")
675
691
  else escape(word)
676
692
  end +
677
- '</keyword>' +
693
+ '</keyword>' +
678
694
  %Q(<index value="#{escape(word)}" />) +
679
695
  if alt
680
696
  alt.split(/\s*,\s*/).collect! { |e| %Q(<index value="#{escape(e.strip)}" />) }.join
@@ -723,7 +739,7 @@ module ReVIEW
723
739
  sprintf('&#x%x;', 9392 + str[0] - 65)
724
740
  end
725
741
  else
726
- error "can't parse maru: #{str}"
742
+ app_error "can't parse maru: #{str}"
727
743
  end
728
744
  end
729
745
 
@@ -773,11 +789,19 @@ module ReVIEW
773
789
  %Q(<underline>#{escape(str)}</underline>)
774
790
  end
775
791
 
792
+ def inline_ins(str)
793
+ %Q(<ins>#{escape(str)}</ins>)
794
+ end
795
+
796
+ def inline_del(str)
797
+ %Q(<del>#{escape(str)}</del>)
798
+ end
799
+
776
800
  def inline_icon(id)
777
801
  begin
778
802
  %Q(<Image href="file://#{@chapter.image(id).path.sub(%r{\A\./}, '')}" type="inline" />)
779
803
  rescue
780
- warn "image not bound: #{id}"
804
+ warn "image not bound: #{id}", location: location
781
805
  ''
782
806
  end
783
807
  end
@@ -1124,7 +1148,7 @@ module ReVIEW
1124
1148
  begin
1125
1149
  puts %Q(<Image href="file://#{@chapter.image(id).path.sub(%r{\A\./}, '')}"#{metrics} />)
1126
1150
  rescue
1127
- warn %Q(image not bound: #{id})
1151
+ warn %Q(image not bound: #{id}), location: location
1128
1152
  end
1129
1153
  unless caption_top?('image')
1130
1154
  puts %Q(<caption>#{compile_inline(caption)}</caption>) if caption.present?
@@ -1182,10 +1206,10 @@ module ReVIEW
1182
1206
  chs = ['', '「', '」']
1183
1207
  if @book.config['chapref']
1184
1208
  chs2 = @book.config['chapref'].split(',')
1185
- if chs2.size != 3
1186
- error '--chapsplitter must have exactly 3 parameters with comma.'
1187
- else
1209
+ if chs2.size == 3
1188
1210
  chs = chs2
1211
+ else
1212
+ app_error '--chapsplitter must have exactly 3 parameters with comma.'
1189
1213
  end
1190
1214
  end
1191
1215
  s = "#{chs[0]}#{@book.chapter_index.number(id)}#{chs[1]}#{@book.chapter_index.title(id)}#{chs[2]}"
@@ -1203,7 +1227,7 @@ module ReVIEW
1203
1227
  end
1204
1228
  end
1205
1229
  rescue KeyError
1206
- error "unknown chapter: #{id}"
1230
+ app_error "unknown chapter: #{id}"
1207
1231
  end
1208
1232
 
1209
1233
  def inline_chap(id)
@@ -1213,7 +1237,7 @@ module ReVIEW
1213
1237
  @book.chapter_index.number(id)
1214
1238
  end
1215
1239
  rescue KeyError
1216
- error "unknown chapter: #{id}"
1240
+ app_error "unknown chapter: #{id}"
1217
1241
  end
1218
1242
 
1219
1243
  def inline_title(id)
@@ -1224,7 +1248,7 @@ module ReVIEW
1224
1248
  title
1225
1249
  end
1226
1250
  rescue KeyError
1227
- error "unknown chapter: #{id}"
1251
+ app_error "unknown chapter: #{id}"
1228
1252
  end
1229
1253
 
1230
1254
  def source(lines, caption = nil, lang = nil)
@@ -1267,7 +1291,7 @@ module ReVIEW
1267
1291
  def inline_bib(id)
1268
1292
  %Q(<span type='bibref' idref='#{id}'>[#{@chapter.bibpaper(id).number}]</span>)
1269
1293
  rescue KeyError
1270
- error "unknown bib: #{id}"
1294
+ app_error "unknown bib: #{id}"
1271
1295
  end
1272
1296
 
1273
1297
  def inline_hd_chap(chap, id)
@@ -1278,7 +1302,7 @@ module ReVIEW
1278
1302
  I18n.t('hd_quote_without_number', compile_inline(chap.headline(id).caption))
1279
1303
  end
1280
1304
  rescue KeyError
1281
- error "unknown headline: #{id}"
1305
+ app_error "unknown headline: #{id}"
1282
1306
  end
1283
1307
 
1284
1308
  def inline_recipe(id)
@@ -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
@@ -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,
@@ -86,9 +80,11 @@ module ReVIEW
86
80
  I18n.setup(@config['language'])
87
81
  begin
88
82
  generate_idgxml_files(yamlfile)
83
+ @logger.success("built #{build_path}")
89
84
  rescue ApplicationError => e
90
85
  raise if @config['debug']
91
- error(e.message)
86
+
87
+ error! e.message
92
88
  end
93
89
  end
94
90
 
@@ -104,6 +100,10 @@ module ReVIEW
104
100
  end
105
101
 
106
102
  build_body(@path, yamlfile)
103
+
104
+ if @compile_errors
105
+ app_error 'compile error, No IDGXML file output.'
106
+ end
107
107
  end
108
108
 
109
109
  def apply_filter(xmlfile)
@@ -178,8 +178,9 @@ module ReVIEW
178
178
  @converter.convert(filename, File.join(basetmpdir, xmlfile))
179
179
  apply_filter(File.join(basetmpdir, xmlfile))
180
180
  rescue => e
181
- warn "compile error in #{filename} (#{e.class})"
182
- warn e.message
181
+ @compile_errors = true
182
+ error "compile error in #{filename} (#{e.class})"
183
+ error e.message
183
184
  end
184
185
  end
185
186
  end
@@ -0,0 +1,239 @@
1
+ require 'fileutils'
2
+ require 'shellwords'
3
+ require 'review/loggable'
4
+
5
+ module ReVIEW
6
+ class ImgMath
7
+ include Loggable
8
+
9
+ def initialize(config, path_name: '_review_math')
10
+ @config = config
11
+ @logger = ReVIEW.logger
12
+ @math_dir = File.join(@config['imagedir'], path_name)
13
+ @math_maps = {}
14
+ end
15
+
16
+ def cleanup_mathimg
17
+ if @config['math_format'] == 'imgmath' && Dir.exist?(@math_dir)
18
+ FileUtils.rm_rf(@math_dir)
19
+ end
20
+ end
21
+
22
+ def defer_math_image(str, key)
23
+ # for Re:VIEW >3
24
+ @math_maps[key] = str
25
+ File.join(@math_dir, "_gen_#{key}.#{@config['imgmath_options']['format']}")
26
+ end
27
+
28
+ def make_math_image(str, key, fontsize = 12)
29
+ # Re:VIEW 2 compatibility
30
+
31
+ img_path = File.join(@math_dir, "_gen_#{key}.#{@config['imgmath_options']['format']}")
32
+ Dir.mkdir(@math_dir) unless Dir.exist?(@math_dir)
33
+ fontsize2 = (fontsize * 1.2).round.to_i
34
+ texsrc = <<-EOB
35
+ \\documentclass[12pt]{article}
36
+ \\usepackage[utf8]{inputenc}
37
+ \\usepackage{amsmath}
38
+ \\usepackage{amsthm}
39
+ \\usepackage{amssymb}
40
+ \\usepackage{amsfonts}
41
+ \\usepackage{anyfontsize}
42
+ \\usepackage{bm}
43
+ \\pagestyle{empty}
44
+
45
+ \\begin{document}
46
+ \\fontsize{#{fontsize}}{#{fontsize2}}\\selectfont #{str}
47
+ \\end{document}
48
+ EOB
49
+ Dir.mktmpdir do |tmpdir|
50
+ tex_path = File.join(tmpdir, 'tmpmath.tex')
51
+ dvi_path = File.join(tmpdir, 'tmpmath.dvi')
52
+ File.write(tex_path, texsrc)
53
+ cmd = "latex --interaction=nonstopmode --output-directory=#{tmpdir} #{tex_path} && dvipng -T tight -z9 -o #{img_path} #{dvi_path}"
54
+ out, status = Open3.capture2e(cmd)
55
+ unless status.success?
56
+ raise ApplicationError, "latex compile error\n\nError log:\n#{out}"
57
+ end
58
+
59
+ img_path
60
+ end
61
+ end
62
+
63
+ def make_math_images
64
+ return if @math_maps.empty?
65
+
66
+ Dir.mkdir(@math_dir) unless Dir.exist?(@math_dir)
67
+ fontsize = @config['imgmath_options']['fontsize'].to_f
68
+ lineheight = @config['imgmath_options']['lineheight'].to_f
69
+
70
+ texsrc = default_imgmath_preamble
71
+ if @config['imgmath_options']['preamble_file'] && File.readable?(@config['imgmath_options']['preamble_file'])
72
+ texsrc = File.read(@config['imgmath_options']['preamble_file'])
73
+ end
74
+
75
+ texsrc << <<-EOB
76
+ \\begin{document}
77
+ \\fontsize{#{fontsize}}{#{lineheight}}\\selectfont
78
+ \\input{__IMGMATH_BODY__}
79
+ \\end{document}
80
+ EOB
81
+
82
+ math_real_dir = File.realpath(@math_dir)
83
+ Dir.mktmpdir do |tmpdir|
84
+ File.open(File.join(tmpdir, '__IMGMATH_BODY__.tex'), 'w') do |f|
85
+ @math_maps.keys.sort.each do |key|
86
+ f.puts "% #{key}"
87
+ f.puts @math_maps[key]
88
+ f.puts '\\clearpage'
89
+ f.puts
90
+ end
91
+ end
92
+
93
+ tex_path = File.join(tmpdir, '__IMGMATH__.tex')
94
+ File.write(tex_path, texsrc)
95
+
96
+ begin
97
+ case @config['imgmath_options']['converter']
98
+ when 'pdfcrop'
99
+ make_math_images_pdfcrop(tmpdir, tex_path, math_real_dir)
100
+ when 'dvipng'
101
+ make_math_images_dvipng(tmpdir, tex_path, math_real_dir)
102
+ else
103
+ error! "unknown math converter error. imgmath_options/converter parameter should be 'pdfcrop' or 'dvipng'."
104
+ end
105
+ rescue CompileError
106
+ FileUtils.cp([tex_path,
107
+ File.join(File.dirname(tex_path), '__IMGMATH_BODY__.tex'),
108
+ File.join(File.dirname(tex_path), '__IMGMATH__.log')],
109
+ math_real_dir)
110
+ error! "LaTeX math compile error. See #{math_real_dir}/__IMGMATH__.log for details."
111
+ end
112
+ end
113
+ @math_maps.clear
114
+ end
115
+
116
+ private
117
+
118
+ def default_imgmath_preamble
119
+ <<-EOB
120
+ \\documentclass[uplatex,a3paper,landscape]{jsarticle}
121
+ \\usepackage[deluxe,uplatex]{otf}
122
+ \\usepackage[T1]{fontenc}
123
+ \\usepackage{textcomp}
124
+ \\usepackage{lmodern}
125
+ \\usepackage[dvipdfmx]{graphicx}
126
+ \\usepackage[dvipdfmx,table]{xcolor}
127
+ \\usepackage[utf8]{inputenc}
128
+ \\usepackage{ascmac}
129
+ \\usepackage{float}
130
+ \\usepackage{alltt}
131
+ \\usepackage{amsmath}
132
+ \\usepackage{amssymb}
133
+ \\usepackage{amsfonts}
134
+ \\usepackage{anyfontsize}
135
+ \\usepackage{bm}
136
+ \\pagestyle{empty}
137
+ % \\setpaperwidth{1000mm}
138
+ EOB
139
+ end
140
+
141
+ def make_math_images_pdfcrop(dir, tex_path, math_real_dir)
142
+ Dir.chdir(dir) do
143
+ dvi_path = '__IMGMATH__.dvi'
144
+ pdf_path = '__IMGMATH__.pdf'
145
+ out, status = Open3.capture2e(*[@config['texcommand'], @config['texoptions'].shellsplit, tex_path].flatten.compact)
146
+ if !status.success? || (!File.exist?(dvi_path) && !File.exist?(pdf_path))
147
+ raise CompileError
148
+ end
149
+
150
+ if File.exist?(dvi_path)
151
+ out, status = Open3.capture2e(*[@config['dvicommand'], @config['dvioptions'].shellsplit, dvi_path].flatten.compact)
152
+ if !status.success? || !File.exist?(pdf_path)
153
+ @logger.error "error in #{@config['dvicommand']}. Error log:\n#{out}"
154
+ raise CompileError
155
+ end
156
+ end
157
+ args = @config['imgmath_options']['pdfcrop_cmd'].shellsplit
158
+ args.map! do |m|
159
+ m.sub('%i', pdf_path).
160
+ sub('%o', '__IMGMATH__pdfcrop.pdf')
161
+ end
162
+ out, status = Open3.capture2e(*args)
163
+ unless status.success?
164
+ @logger.error "error in pdfcrop. Error log:\n#{out}"
165
+ raise CompileError
166
+ end
167
+ pdf_path = '__IMGMATH__pdfcrop.pdf'
168
+ pdf_path2 = pdf_path
169
+
170
+ @math_maps.keys.sort.each_with_index do |key, idx|
171
+ page = idx + 1
172
+ if File.exist?(File.join(math_real_dir, "_gen_#{key}.#{@config['imgmath_options']['format']}"))
173
+ # made already
174
+ next
175
+ end
176
+
177
+ if @config['imgmath_options']['extract_singlepage']
178
+ # if extract_singlepage = true, split each page
179
+ args = @config['imgmath_options']['pdfextract_cmd'].shellsplit
180
+
181
+ args.map! do |m|
182
+ m.sub('%i', pdf_path).
183
+ sub('%o', "__IMGMATH__pdfcrop_p#{page}.pdf").
184
+ sub('%O', "__IMGMATH__pdfcrop_p#{page}").
185
+ sub('%p', page.to_s)
186
+ end
187
+ out, status = Open3.capture2e(*args)
188
+ unless status.success?
189
+ @logger.error "error in pdf extracting. Error log:\n#{out}"
190
+ raise CompileError
191
+ end
192
+
193
+ pdf_path2 = "__IMGMATH__pdfcrop_p#{page}.pdf"
194
+ end
195
+
196
+ args = @config['imgmath_options']['pdfcrop_pixelize_cmd'].shellsplit
197
+ args.map! do |m|
198
+ m.sub('%i', pdf_path2).
199
+ sub('%t', @config['imgmath_options']['format']).
200
+ sub('%o', File.join(math_real_dir, "_gen_#{key}.#{@config['imgmath_options']['format']}")).
201
+ sub('%O', File.join(math_real_dir, "_gen_#{key}")).
202
+ sub('%p', page.to_s)
203
+ end
204
+ out, status = Open3.capture2e(*args)
205
+ unless status.success?
206
+ @logger.error "error in pdf pixelizing. Error log:\n#{out}"
207
+ raise CompileError
208
+ end
209
+ end
210
+ end
211
+ end
212
+
213
+ def make_math_images_dvipng(dir, tex_path, math_real_dir)
214
+ Dir.chdir(dir) do
215
+ dvi_path = '__IMGMATH__.dvi'
216
+ out, status = Open3.capture2e(*[@config['texcommand'], @config['texoptions'].shellsplit, tex_path].flatten.compact)
217
+ if !status.success? || !File.exist?(dvi_path)
218
+ raise CompileError
219
+ end
220
+
221
+ @math_maps.keys.sort.each_with_index do |key, idx|
222
+ page = idx + 1
223
+ args = @config['imgmath_options']['dvipng_cmd'].shellsplit
224
+ args.map! do |m|
225
+ m.sub('%i', dvi_path).
226
+ sub('%o', File.join(math_real_dir, "_gen_#{key}.#{@config['imgmath_options']['format']}")).
227
+ sub('%O', File.join(math_real_dir, "_gen_#{key}")).
228
+ sub('%p', page.to_s)
229
+ end
230
+ out, status = Open3.capture2e(*args)
231
+ unless status.success?
232
+ @logger.error "error in dvipng. Error log:\n#{out}"
233
+ raise CompileError
234
+ end
235
+ end
236
+ end
237
+ end
238
+ end
239
+ end