review-retrovert 0.9.7 → 0.9.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +9 -0
  3. data/.editorconfig +3 -0
  4. data/.github/workflows/retrovert.yml +20 -5
  5. data/.gitignore +1 -0
  6. data/.vscode/launch.json +48 -0
  7. data/Gemfile.lock +13 -4
  8. data/Makefile +24 -0
  9. data/docker/review.Dockerfile +10 -0
  10. data/lib/review/retrovert/converter.rb +374 -111
  11. data/lib/review/retrovert/reviewcompat.rb +57 -0
  12. data/lib/review/retrovert/reviewdef.rb +70 -0
  13. data/lib/review/retrovert/sty/{ird.sty → ird.sty.erb} +9 -1
  14. data/lib/review/retrovert/sty/review-retrovert-custom.sty.erb +6 -0
  15. data/lib/review/retrovert/utils.rb +35 -0
  16. data/lib/review/retrovert/version.rb +1 -1
  17. data/lib/review/retrovert/yamlconfig.rb +0 -1
  18. data/package-lock.json +6 -0
  19. data/package.json +1 -0
  20. data/review-retrovert.gemspec +5 -1
  21. data/testdata/mybook/.gitignore +3 -1
  22. data/testdata/mybook/.textlintrc +11 -0
  23. data/testdata/mybook/Rakefile +8 -0
  24. data/testdata/mybook/catalog.yml +36 -10
  25. data/testdata/mybook/config-starter.yml +134 -9
  26. data/testdata/mybook/config.yml +44 -15
  27. data/testdata/mybook/contents/00-preface.re +48 -2
  28. data/testdata/mybook/contents/01-install.re +38 -5
  29. data/testdata/mybook/contents/02-tutorial.re +333 -113
  30. data/testdata/mybook/contents/03-syntax.re +2370 -373
  31. data/testdata/mybook/contents/04-customize.re +424 -88
  32. data/testdata/mybook/contents/05-faq.re +288 -10
  33. data/testdata/mybook/contents/06-bestpractice.re +431 -59
  34. data/testdata/mybook/contents/91-compare.re +402 -2
  35. data/testdata/mybook/contents/92-filelist.re +14 -8
  36. data/testdata/mybook/contents/93-background.re +10 -10
  37. data/testdata/mybook/contents/99-postface.re +2 -1
  38. data/testdata/mybook/contents/r0-root.re +1 -1
  39. data/testdata/mybook/css/webstyle.css +180 -2
  40. data/testdata/mybook/data/terms.txt +3 -0
  41. data/testdata/mybook/data/words.txt +15 -0
  42. data/testdata/mybook/images/03-syntax/index-page.png +0 -0
  43. data/testdata/mybook/images/03-syntax/order-detail.png +0 -0
  44. data/testdata/mybook/images/04-customize/section_decoration_samples.png +0 -0
  45. data/testdata/mybook/images/05-faq/dummy-image.png +0 -0
  46. data/testdata/mybook/images/06-bestpractice/section_title_wlines.png +0 -0
  47. data/testdata/mybook/images/avatar-b.png +0 -0
  48. data/testdata/mybook/images/avatar-g.png +0 -0
  49. data/testdata/mybook/images/avatar-r.png +0 -0
  50. data/testdata/mybook/images/caution-icon.png +0 -0
  51. data/testdata/mybook/images/info-icon.png +0 -0
  52. data/testdata/mybook/images/warning-icon.png +0 -0
  53. data/testdata/mybook/layouts/layout.html5.erb +3 -1
  54. data/testdata/mybook/layouts/layout.tex.erb +265 -379
  55. data/testdata/mybook/layouts/layout.tex.erb.orig +386 -0
  56. data/testdata/mybook/lib/ruby/review-book.rb +64 -0
  57. data/testdata/mybook/lib/ruby/review-builder.rb +902 -63
  58. data/testdata/mybook/lib/ruby/review-compiler.rb +675 -22
  59. data/testdata/mybook/lib/ruby/review-epubbuilder.rb +33 -0
  60. data/testdata/mybook/lib/ruby/review-epubmaker.rb +10 -7
  61. data/testdata/mybook/lib/ruby/review-htmlbuilder.rb +354 -66
  62. data/testdata/mybook/lib/ruby/review-latexbuilder.rb +429 -146
  63. data/testdata/mybook/lib/ruby/review-maker.rb +117 -6
  64. data/testdata/mybook/lib/ruby/review-markdownbuilder.rb +945 -0
  65. data/testdata/mybook/lib/ruby/review-markdownmaker.rb +91 -0
  66. data/testdata/mybook/lib/ruby/review-monkeypatch.rb +2 -0
  67. data/testdata/mybook/lib/ruby/review-pdfmaker.rb +160 -82
  68. data/testdata/mybook/lib/ruby/review-webmaker.rb +20 -5
  69. data/testdata/mybook/lib/tasks/review.rake +14 -0
  70. data/testdata/mybook/lib/tasks/starter.rake +148 -4
  71. data/testdata/mybook/sty/indexstyle.ist +25 -0
  72. data/testdata/mybook/sty/mytextsize.sty +29 -0
  73. data/testdata/mybook/sty/mytitlepage.sty +34 -11
  74. data/testdata/mybook/sty/review-base.sty +276 -0
  75. data/testdata/mybook/sty/starter-codeblock.sty +237 -106
  76. data/testdata/mybook/sty/starter-color.sty +72 -17
  77. data/testdata/mybook/sty/starter-heading.sty +60 -13
  78. data/testdata/mybook/sty/starter-misc.sty +894 -0
  79. data/testdata/mybook/sty/starter-note.sty +67 -14
  80. data/testdata/mybook/sty/starter-talklist.sty +105 -0
  81. data/testdata/mybook/sty/starter-util.sty +35 -0
  82. data/testdata/mybook/sty/starter.sty +8 -526
  83. metadata +80 -4
@@ -14,13 +14,19 @@ module ReVIEW
14
14
 
15
15
  class LATEXBuilder
16
16
 
17
+ public :print, :puts
18
+
19
+ def target_name
20
+ "latex"
21
+ end
22
+
17
23
  ## 章や節や項や目のタイトル
18
24
  def headline(level, label, caption)
19
25
  with_context(:headline) do
20
26
  _, anchor = headline_prefix(level)
21
27
  headname = _headline_name(level) # 'chapter', 'section', 'subsection', ...
22
28
  headstar = _headline_star(level) # '*' or nil
23
- blank unless @output.pos == 0
29
+ blank() unless @output.pos == 0
24
30
  with_context(:caption) do
25
31
  print macro("#{headname}#{headstar}", compile_inline(caption))
26
32
  print "\n" unless level >= 5 # \paragraphと\subpararaphでは改行しない
@@ -31,7 +37,12 @@ module ReVIEW
31
37
  puts "\\ifx\\Chapter\\undefined{\\addcontentsline{toc}{#{headname}}{#{compile_inline(caption)}}}\\fi"
32
38
  end
33
39
  if _headline_chapter?(level)
34
- puts macro('label', chapter_label)
40
+ ## \Chapter直後の\addvspaceが効くように、
41
+ ## \lastskipをいったん保存し、\labelのあとで復元する。
42
+ puts "\\keeplastskip{"
43
+ puts " \\label{#{chapter_label()}}"
44
+ puts " \\par\\nobreak"
45
+ puts "}"
35
46
  elsif level >= 5 # 段(Paragraph)と小段(Subparagraph)では
36
47
  nil # 何もしない、\labelもつけない
37
48
  else
@@ -72,7 +83,7 @@ module ReVIEW
72
83
  public
73
84
 
74
85
  def nonum_begin(level, _label, caption)
75
- blank unless @output.pos == 0
86
+ blank() unless @output.pos == 0
76
87
  with_context(:headline) do
77
88
  with_context(:caption) do
78
89
  puts macro(HEADLINE[level] + '*', compile_inline(caption))
@@ -84,7 +95,7 @@ module ReVIEW
84
95
  end
85
96
 
86
97
  def notoc_begin(level, _label, caption)
87
- blank unless @output.pos == 0
98
+ blank() unless @output.pos == 0
88
99
  with_context(:headline) do
89
100
  with_context(:caption) do
90
101
  puts "\\ifx\\Chapter\\undefined"
@@ -96,18 +107,51 @@ module ReVIEW
96
107
  end
97
108
  end
98
109
 
99
- ## テーブルヘッダー
100
- ## (TODO: 第3引数にpos=htbpを指定)
101
- def table_header(id, caption)
102
- if caption.present?
110
+ ## テーブル
111
+ def table(lines, id=nil, caption=nil, option=nil)
112
+ super
113
+ end
114
+
115
+ def table_header(id, caption, options)
116
+ if id.present? || caption.present?
103
117
  @table_caption = true
118
+ pos = options[:pos] || 'h'
104
119
  star = id.present? ? '' : '*'
105
- s = with_context(:caption) { compile_inline(caption) }
106
- puts "\\begin{table}[h]%%#{id}"
120
+ s = with_context(:caption) { compile_inline(caption || '') }
121
+ puts "\\begin{table}[#{pos}]%%#{id}"
107
122
  puts "\\centering%"
108
123
  puts macro("reviewtablecaption#{star}", s)
109
124
  end
110
- puts macro('label', table_label(id)) if id.present?
125
+ begin
126
+ puts macro('label', table_label(id)) if id.present?
127
+ rescue KeyError
128
+ error "no such table: #{id}"
129
+ end
130
+ end
131
+
132
+ alias __original_table_begin table_begin
133
+
134
+ def table_begin(ncols, fontsize: nil)
135
+ if fontsize
136
+ font = FONTSIZES[fontsize]
137
+ puts "\\def\\startertablefont{\\#{font}}" if font
138
+ end
139
+ __original_table_begin(ncols)
140
+ end
141
+
142
+ ## CSVテーブル
143
+ def _table_hline()
144
+ "\\hline"
145
+ end
146
+
147
+ def _table_bottom(hline: false)
148
+ puts "\\hline" if hline
149
+ end
150
+
151
+ def _table_tr(cells, hline: false)
152
+ s = "#{cells.join(' & ')} \\\\"
153
+ s << " \\hline" if hline
154
+ return s
111
155
  end
112
156
 
113
157
  ## 改行命令「\\」のあとに改行文字「\n」を置かない。
@@ -131,18 +175,10 @@ module ReVIEW
131
175
  "\\\\[0pt]" # これなら後続行の先頭に1/4空白が入らない
132
176
  end
133
177
 
178
+ protected
134
179
 
135
- ## コードブロック(//program, //terminal)
136
-
137
- def program(lines, id=nil, caption=nil, optionstr=nil)
138
- _codeblock('program', lines, id, caption, optionstr)
139
- end
140
-
141
- def terminal(lines, id=nil, caption=nil, optionstr=nil)
142
- _codeblock('terminal', lines, id, caption, optionstr)
143
- end
144
180
 
145
- protected
181
+ ## コードブロック(//program, //terminal, //output)
146
182
 
147
183
  FONTSIZES = {
148
184
  "small" => "small",
@@ -151,125 +187,226 @@ module ReVIEW
151
187
  "large" => "large",
152
188
  "x-large" => "Large",
153
189
  "xx-large" => "LARGE",
190
+ "medium" => "normalsize",
154
191
  }
155
192
 
156
- ## コードブロック(//program, //terminal)
157
- def _codeblock(blockname, lines, id, caption, optionstr)
158
- ## ブロックコマンドのオプション引数はCompilerクラスでパースすべき。
159
- ## しかしCompilerクラスがそのような設計になってないので、
160
- ## 仕方ないのでBuilderクラスでパースする。
161
- opts = _parse_codeblock_optionstr(optionstr, blockname)
162
- CODEBLOCK_OPTIONS.each {|k, v| opts[k] = v unless opts.key?(k) }
163
- #
193
+ def _codeblock_eolmark()
194
+ "{\\startereolmark}"
195
+ end
196
+
197
+ def _codeblock_indentmark()
198
+ "{\\starterindentchar}"
199
+ end
200
+
201
+ LATEX_ESCAPE_TABLE = {
202
+ '{' => '\{',
203
+ '}' => '\}',
204
+ '%' => '\%',
205
+ '$' => '\$',
206
+ '#' => '\#',
207
+ '&' => '\&',
208
+ '_' => '\_',
209
+ '\\' => '{\textbackslash}',
210
+ '^' => '{\textasciicircum}',
211
+ '~' => '{\textasciitilde}',
212
+ }
213
+
214
+ def _render_codeblock(blockname, lines, id, caption_str, opts)
164
215
  if opts['eolmark']
165
- lines = lines.map {|line| "#{detab(line)}\\startereolmark{}" }
216
+ eolmark = _codeblock_eolmark() # ex: '{\startereolmark}'
166
217
  else
167
- lines = lines.map {|line| detab(line) }
218
+ eolmark = nil
168
219
  end
169
220
  #
170
- if opts['indentwidth'] && opts['indentwidth'] > 0
171
- indent_w = opts['indentwidth']
172
- indent_str = " " * (indent_w - 1) + "{\\starterindentchar}"
173
- lines = lines.map {|line|
174
- line.sub(/\A( +)/) {
175
- m, n = ($1.length - 1).divmod indent_w
176
- " " << indent_str * m << " " * n
177
- }
221
+ eol = eolmark ? "#{eolmark}\\par\n" : "\\par\n"
222
+ lines = lines.map {|line|
223
+ line = _parse_inline(line) {|text|
224
+ _convert_and_escape_str(text)
178
225
  }
179
- end
226
+ if line =~ /\A\n/
227
+ "\\mbox{}#{eol}\n"
228
+ else
229
+ line.gsub(/(?:\\-)?\n/, eol)
230
+ end
231
+ }
180
232
  #
181
- if id.present? || caption.present?
182
- caption_str = _build_caption_str(id, caption)
183
- else
184
- caption_str = nil
233
+ indent_width = opts['indent']
234
+ if indent_width && indent_width > 0
235
+ lines = _add_indent_mark(lines, indent_width)
185
236
  end
186
237
  #
238
+ default_opts = _codeblock_default_options(blockname)
239
+ opts2 = {}
240
+ opts.each do |k, v|
241
+ opts2[k] = v unless v == default_opts[k]
242
+ end
187
243
  fontsize = FONTSIZES[opts['fontsize']]
188
- print "\\def\\startercodeblockfontsize{#{fontsize}}\n"
189
244
  #
190
245
  environ = "starter#{blockname}"
246
+ puts "\\begingroup"
247
+ puts " \\makeatletter" if fontsize
248
+ puts " \\def\\starter@#{blockname}@fontsize{#{fontsize}}" if fontsize
249
+ puts " \\makeatother" if fontsize
250
+ puts " \\makeatletter" unless opts2.empty?
251
+ opts2.each do |k, v|
252
+ x = k.gsub(/[^a-zA-Z]/, '_')
253
+ v = v == true ? 'Y' : v == false ? '' : v #escape(v.to_s)
254
+ puts " \\edef\\starter@#{blockname}@#{x}{#{v}}"
255
+ #puts " \\edef\\starter@codeblock@#{x}{#{v}}"
256
+ end
257
+ puts " \\makeatother" unless opts2.empty?
191
258
  print "\\begin{#{environ}}[#{id}]{#{caption_str}}"
192
259
  print "\\startersetfoldmark{}" unless opts['foldmark']
193
- if opts['eolmark']
194
- print "\\startereolmarkdark{}" if blockname == 'terminal'
195
- print "\\startereolmarklight{}" if blockname != 'terminal'
196
- end
197
260
  if opts['lineno']
198
261
  gen = LineNumberGenerator.new(opts['lineno'])
199
- width = opts['linenowidth']
262
+ width = opts['linenowidth'] || -1
200
263
  if width && width >= 0
201
264
  if width == 0
202
265
  last_lineno = gen.each.take(lines.length).compact.last
203
266
  width = last_lineno.to_s.length
204
267
  end
205
268
  print "\\startersetfoldindentwidth{#{'9'*(width+2)}}"
206
- format = "\\textcolor{gray}{%#{width}s:} "
269
+ format = "\\starterinnerlineno{%#{width}s:} "
207
270
  else
208
- format = "\\starterlineno{%s}"
271
+ format = "\\starterouterlineno{%s}"
209
272
  end
210
273
  buf = []
211
- opt_fold = opts['fold']
212
274
  lines.zip(gen).each do |x, n|
213
- buf << ( opt_fold \
214
- ? "#{format % n.to_s}\\seqsplit{#{x}}" \
215
- : "#{format % n.to_s}#{x}" )
275
+ buf << "#{(format % n.to_s).gsub(' ', '~')}#{x}"
216
276
  end
217
- print buf.join("\n")
277
+ print buf.join()
218
278
  else
219
- print "\\seqsplit{" if opts['fold']
220
- print lines.join("\n")
221
- print "}" if opts['fold']
279
+ print lines.join()
222
280
  end
223
281
  puts "\\end{#{environ}}"
282
+ puts "\\endgroup"
224
283
  nil
225
284
  end
226
285
 
286
+ def _convert_and_escape_str(str)
287
+ #str.gsub(/[\{\}\\\%\^\_\$\&\~]/, LATEX_ESCAPE_TABLE)
288
+ rexp = /[\{\}\\\#\%\^\_\$\&\~]/
289
+ cs = []
290
+ zenkaku_rexp = ZENKAKU_CHAR_REXP
291
+ str.each_char do |c|
292
+ cs << (
293
+ case c
294
+ when / / ; '~'
295
+ when /\n/ ; "\n"
296
+ when rexp ; LATEX_ESCAPE_TABLE[c]
297
+ when zenkaku_rexp ; "\\ZC{#{c}}" # 全角文字
298
+ else ; c # 半角文字
299
+ end
300
+ )
301
+ end
302
+ cs << "" unless cs[-1] == "\n"
303
+ return cs.join('\\-')
304
+ end
305
+
306
+ ZENKAKU_CHAR_REXP = /\A[^\000-\177]\z/
307
+
308
+ def _add_indent_mark(lines, indent_width)
309
+ space = "~\\-"
310
+ rexp = /\A((?:~\\-)+)/
311
+ #
312
+ width = indent_width
313
+ mark = _codeblock_indentmark() # ex: '{\starterindentmark}'
314
+ indent = space * (width - 1) + mark
315
+ nchar = space.length
316
+ return lines.map {|line|
317
+ line.sub(rexp) {
318
+ m, n = ($1.length / nchar - 1).divmod width
319
+ "#{space}#{indent * m}#{space * n}"
320
+ }
321
+ }
322
+ end
323
+
227
324
  public
228
325
 
229
326
  ## ・\caption{} のかわりに \reviewimagecaption{} を使うよう修正
230
327
  ## ・「scale=X」に加えて「pos=X」も受け付けるように拡張
231
- def image_image(id, caption, option_str)
232
- pos = nil; border = nil; arr = []
233
- _each_block_option(option_str) do |k, v|
234
- case k
235
- when 'pos'
236
- v =~ /\A[Hhtb]+\z/ or # H: Here, h: here, t: top, b: bottom
237
- raise "//image[][][pos=#{v}]: expected 'pos=H' or 'pos=h'."
238
- pos = v # detect 'pos=H' or 'pos=h'
239
- when 'border', 'draft'
240
- case v
241
- when nil ; flag = true
242
- when 'on' ; flag = true
243
- when 'off'; flag = false
244
- else
245
- raise "//image[][][#{k}=#{v}]: expected '#{k}=on' or '#{k}=off'"
246
- end
247
- border = flag if k == 'border'
248
- arr << "draft=#{flag}" if k == 'draft'
328
+ def _render_image(id, image_filepath, caption, opts)
329
+ width = "\\maxwidth"
330
+ if opts[:scale]
331
+ case (scale = opts[:scale])
332
+ when /\A\d+\z/, /\A\d\.\d*\z/, /\A\.\d+\z/
333
+ when /\A\d+(\.\d+)?%\z/
334
+ scale = scale.sub(/%\z/, '').to_f / 100.0
249
335
  else
250
- arr << (v.nil? ? k : "#{k}=#{v}")
336
+ error "scale=#{scale}: invalid scale value."
251
337
  end
338
+ width = "#{scale}\\maxwidth" # not '\textwidth'
252
339
  end
253
340
  #
254
- metrics = parse_metric('latex', arr.join(","))
255
- puts "\\begin{reviewimage}[#{pos}]%%#{id}" if pos
256
- puts "\\begin{reviewimage}%%#{id}" unless pos
257
- metrics = "width=\\maxwidth" unless metrics.present?
258
- puts "\\starterimageframe{%" if border
259
- puts "\\includegraphics[#{metrics}]{#{@chapter.image(id).path}}%"
260
- puts "}%" if border
341
+ if opts[:width]
342
+ case opts[:width]
343
+ when /\A\d+\z/, /\A\d\.\d*\z/, /\A\.\d+\z/
344
+ width = "#{opts[:width]}\\textwidth" # not '\maxwidth'
345
+ when /\A(\d+(\.\d+)?)%\z/
346
+ width = "#{$1.to_f/100.0}\\textwidth" # not '\maxwidth'
347
+ when /\A(\d+(?:\.\d*)?)(mm|cm)\z/
348
+ width = "#{$1}true#{$2}" # 'mm'->'truemm', 'cm'->'truecm'
349
+ else
350
+ width = opts[:width]
351
+ end
352
+ end
353
+ #
354
+ metrics = ["width=#{width}"]
355
+ metrics << "draft=#{opts[:draft]}" if opts[:draft] != nil
356
+ #
357
+ pos = opts[:pos] || config_starter()['image_position']
358
+ puts "\\begin{reviewimage}[#{pos}]%%#{id}"
359
+ puts "\\starterimageframe{%" if opts[:border]
360
+ puts "\\includegraphics[#{metrics.join(',')}]{#{image_filepath}}%"
361
+ puts "}%" if opts[:border]
261
362
  with_context(:caption) do
262
363
  #puts macro('caption', compile_inline(caption)) if caption.present? # original
263
- puts macro('reviewimagecaption', compile_inline(caption)) if caption.present?
364
+ puts "\\reviewimagecaption{#{compile_inline(caption)}}" if caption.present?
264
365
  end
265
- puts macro('label', image_label(id))
366
+ puts "\\label{#{image_label(id)}}"
266
367
  puts "\\end{reviewimage}"
267
368
  end
268
369
 
370
+ ## //imgtable
371
+ def imgtable(lines, id, caption=nil, option=nil)
372
+ super
373
+ end
374
+
375
+ def _render_imgtable(id, caption, opts)
376
+ pos = opts['pos'] || 'h'
377
+ puts "\\begin{table}[#{pos}]%%#{id}"
378
+ puts "\\centering"
379
+ yield
380
+ puts "\\end{table}"
381
+ blank()
382
+ end
383
+
384
+ def _render_imgtable_caption(caption)
385
+ puts macro('reviewimgtablecaption', compile_inline(caption))
386
+ end
387
+
388
+ def _render_imgtable_label(id)
389
+ puts macro('label', table_label(id))
390
+ rescue ReVIEW::KeyError
391
+ error "no such table: #{id}"
392
+ end
393
+
394
+ def imgtable_image(id, _caption, metric)
395
+ metrics = parse_metric('latex', metric)
396
+ # image is always bound here
397
+ #puts "\\begin{reviewimage}%%#{id}" #-
398
+ metrics = "width=\\maxwidth" unless metrics.present?
399
+ imagefile = @chapter.image(id).path
400
+ puts "\\includegraphics[#{metrics}]{#{imagefile}}"
401
+ #puts '\end{reviewimage}' #-
402
+ end
403
+
404
+ ##
405
+
269
406
  def _build_secref(chap, num, title, parent_title)
270
407
  s = ""
271
408
  ## 親セクションのタイトルがあれば使う
272
- if parent_title && @book.config['starter']['secref_parenttitle']
409
+ if parent_title && self.config_starter['secref_parenttitle']
273
410
  s << "「%s」内の" % parent_title # TODO: I18n化
274
411
  end
275
412
  ## 対象セクションへのリンクを作成する
@@ -294,17 +431,17 @@ module ReVIEW
294
431
  public
295
432
 
296
433
  def ul_begin
297
- blank
434
+ blank()
298
435
  puts '\begin{starteritemize}' # instead of 'itemize'
299
436
  end
300
437
 
301
438
  def ul_end
302
439
  puts '\end{starteritemize}' # instead of 'itemize'
303
- blank
440
+ blank()
304
441
  end
305
442
 
306
443
  def ol_begin(start_num=nil)
307
- blank
444
+ blank()
308
445
  puts '\begin{starterenumerate}' # instead of 'enumerate'
309
446
  if start_num.nil?
310
447
  return true unless @ol_num
@@ -315,7 +452,7 @@ module ReVIEW
315
452
 
316
453
  def ol_end
317
454
  puts '\end{starterenumerate}' # instead of 'enumerate'
318
- blank
455
+ blank()
319
456
  end
320
457
 
321
458
  def ol_item_begin(lines, num)
@@ -327,10 +464,20 @@ module ReVIEW
327
464
  def ol_item_end()
328
465
  end
329
466
 
467
+ def dt(str)
468
+ puts "\\starterdt{#{str}}%"
469
+ end
470
+
471
+ def dl_dd_begin()
472
+ end
473
+
474
+ def dl_dd_end()
475
+ end
476
+
330
477
  ## コラム
331
478
 
332
479
  def column_begin(level, label, caption)
333
- blank
480
+ blank()
334
481
  @doc_status[:column] = true
335
482
  puts "\\begin{reviewcolumn}\n"
336
483
  puts "\\phantomsection % for hyperref" #+
@@ -367,12 +514,85 @@ module ReVIEW
367
514
  ## (導入文 //lead{ ... //} と似ているが、導入文では詩や物語を
368
515
  ## 引用するのが普通らしく、概要 (abstract) とは違うみたいなので、
369
516
  ## 概要を表すブロックを用意した。)
370
- def abstract(lines)
517
+ def on_abstract_block()
371
518
  puts '\begin{starterabstract}'
372
- puts lines
519
+ yield
373
520
  puts '\end{starterabstract}'
374
521
  end
375
522
 
523
+ ## 章 (Chapter) の著者
524
+ def on_chapterauthor_block(name)
525
+ puts "\\starterchapterauthor{#{escape(name)}}"
526
+ end
527
+
528
+ ## 会話リスト
529
+ def _render_talklist(opts, &b)
530
+ puts '\begingroup'
531
+ puts ' \makeatletter' unless opts.empty?
532
+ opts.each do |k, v|
533
+ puts " \\def\\starter@talklist@#{k}{#{v}}"
534
+ end
535
+ puts ' \makeatother' unless opts.empty?
536
+ puts '\begin{startertalklist}'
537
+ yield
538
+ puts '\end{startertalklist}'
539
+ puts '\endgroup'
540
+ end
541
+
542
+ ## 会話項目
543
+ def _render_talk(image_filepath=nil, name=nil, &b)
544
+ s = name ? compile_inline(name) : nil
545
+ print "\\startertalk{#{image_filepath}}{#{s}}{%"
546
+ yield
547
+ @blank_needed = false
548
+ puts "}"
549
+ end
550
+
551
+ ## キーと説明文のリスト
552
+ def _render_desclist(opts, &b)
553
+ bkup = @_desclist_opts
554
+ @_desclist_opts = opts
555
+ star = opts[:compact] ? '*' : ''
556
+ puts "\\begingroup"
557
+ puts " \\makeatletter" unless opts.empty?
558
+ opts.each do |k, v|
559
+ k_ = k.to_s.gsub('_', '@')
560
+ v_ = v == true ? 'Y' : v == false ? '' : escape(v.to_s)
561
+ puts " \\def\\starter@desclist@#{k_}{#{v_}}"
562
+ end
563
+ puts " \\makeatother" unless opts.empty?
564
+ puts "\\begin{starterdesclist}"
565
+ yield
566
+ puts "\\end{starterdesclist}"
567
+ puts "\\endgroup"
568
+ @_desclist_opts = bkup
569
+ end
570
+
571
+ ## キーと説明文
572
+ def on_desc_block(key, text=nil, &b)
573
+ text = "\n" + text if text
574
+ super(key, text, &b)
575
+ end
576
+ def _render_desc(key, &b)
577
+ opts = @_desclist_opts
578
+ s = compile_inline(key)
579
+ #print "\\begin{starterdesc}{#{s}}"
580
+ #print "\\begin{starterdesc}{#{s}}%"
581
+ print "\\begin{starterdesc}{#{s}}\\ignorespaces "
582
+ yield
583
+ @blank_needed = false
584
+ puts "\\end{starterdesc}"
585
+ end
586
+
587
+ ## 縦方向の空きを入れる
588
+ def _render_vspace(size)
589
+ puts "\\vspace{#{size}}"
590
+ end
591
+
592
+ def _render_addvspace(size)
593
+ puts "\\addvspace{#{size}}"
594
+ end
595
+
376
596
  ## 章タイトルを独立したページに
377
597
  def makechaptitlepage(option=nil)
378
598
  case option
@@ -387,10 +607,8 @@ module ReVIEW
387
607
  end
388
608
 
389
609
  ## 縦方向のスペースがなければ改ページ
390
- def needvspace(builder_name, height)
391
- if builder_name == 'latex'
392
- puts "\\needvspace{#{height}}"
393
- end
610
+ def _render_needvspace(height)
611
+ puts "\\needvspace{#{height}}"
394
612
  end
395
613
 
396
614
  ## 段(Paragraph)の終わりにスペースを入れる
@@ -428,11 +646,13 @@ module ReVIEW
428
646
  ## ノート(//note[caption]{ ... //})
429
647
  ## (入れ子対応なので、中に箇条書きや別のブロックを入れられる)
430
648
  def on_note_block(label=nil, caption=nil)
431
- caption, label = label, nil if caption.nil?
432
- s = compile_inline(caption || "")
433
- puts "\\begin{starternote}[#{label}]{#{s}}"
434
- yield
435
- puts "\\end{starternote}"
649
+ with_context(:minicolumn) do
650
+ caption, label = label, nil if caption.nil?
651
+ s = compile_inline(caption || "")
652
+ puts "\\begin{starternote}[#{label}]{#{s}}"
653
+ yield
654
+ puts "\\end{starternote}"
655
+ end
436
656
  end
437
657
  def note(lines, label=nil, caption=nil)
438
658
  on_note_block(label, caption) do
@@ -476,7 +696,7 @@ module ReVIEW
476
696
  arr = []
477
697
  arr << lang if lang
478
698
  if lineno_flag
479
- first_line_num = line_num
699
+ first_line_num = line_num()
480
700
  arr << "lineno=#{first_line_num}"
481
701
  arr << "linenowidth=0"
482
702
  end
@@ -484,24 +704,12 @@ module ReVIEW
484
704
  end
485
705
  private :_codeblock_optstr
486
706
 
487
-
488
- ## 入れ子可能なブロック命令
489
-
490
- def on_minicolumn(type, caption, &b)
491
- s = with_context(:caption) { compile_inline(caption) }
492
- puts "\\begin{starter#{type}}{#{s}}"
493
- yield
494
- puts "\\end{starter#{type}}\n"
495
- end
496
- protected :on_minicolumn
497
-
498
- def on_sideimage_block(imagefile, imagewidth, option_str=nil, &b)
499
- imagefile, imagewidth, opts = validate_sideimage_args(imagefile, imagewidth, option_str)
500
- filepath = find_image_filepath(imagefile)
501
- side = opts['side'] || 'L'
707
+ ## 画像横に文章
708
+ def _render_sideimage(filepath, imagewidth, opts, &b)
709
+ side = opts['side'] || 'L'
502
710
  normalize = proc {|s|
503
- s =~ /\A(\d+(?:\.\d+)?)(%|mm|cm)\z/
504
- if $2.nil? ; s
711
+ rexp = /\A(\d+(?:\.\d+)?)(%|mm|cm)\z/
712
+ if s !~ rexp ; s
505
713
  elsif $2 == '%' ; "#{$1.to_f/100.0}\\textwidth"
506
714
  else ; "#{$1}true#{$2}"
507
715
  end
@@ -517,6 +725,20 @@ module ReVIEW
517
725
  puts "}\n"
518
726
  end
519
727
 
728
+ ## 入れ子可能なブロック命令
729
+
730
+ def on_minicolumn(type, caption=nil, &b)
731
+ with_context(:minicolumn) do
732
+ s = caption ? with_context(:caption) { compile_inline(caption) } : nil
733
+ puts "\\begin{starter#{type}}{#{s}}"
734
+ yield
735
+ puts "\\end{starter#{type}}\n"
736
+ end
737
+ end
738
+ protected :on_minicolumn
739
+
740
+ ## 数式
741
+
520
742
  def texequation(lines, label=nil, caption=nil)
521
743
  blank()
522
744
  #
@@ -561,6 +783,11 @@ module ReVIEW
561
783
 
562
784
  #### インライン命令
563
785
 
786
+ ## 改段落(箇条書き内では空行を入れられないため)
787
+ def inline_par(arg)
788
+ "\\starterpar{#{arg}}"
789
+ end
790
+
564
791
  ## ファイル名
565
792
  def inline_file(str)
566
793
  on_inline_file { escape(str) }
@@ -574,11 +801,11 @@ module ReVIEW
574
801
  on_inline_userinput { escape(str) }
575
802
  end
576
803
  def on_inline_userinput
577
- if within_codeblock?()
578
- "{\\starteruserinput{\\seqsplit{#{yield}}}}"
579
- else
804
+ #if within_codeblock?()
805
+ # "{\\starteruserinput{\\seqsplit{#{yield}}}}"
806
+ #else
580
807
  "\\starteruserinput{#{yield}}"
581
- end
808
+ #end
582
809
  end
583
810
 
584
811
  ## 引数をそのまま表示
@@ -597,11 +824,11 @@ module ReVIEW
597
824
  on_inline_weak { escape(str) }
598
825
  end
599
826
  def on_inline_weak
600
- if within_codeblock?()
601
- "{\\starterweak{\\seqsplit{#{yield}}}}"
602
- else
827
+ #if within_codeblock?()
828
+ # "{\\starterweak{\\seqsplit{#{yield}}}}"
829
+ #else
603
830
  "\\starterweak{#{yield}}"
604
- end
831
+ #end
605
832
  end
606
833
 
607
834
  ## 文字を小さくする
@@ -672,19 +899,17 @@ module ReVIEW
672
899
  def on_inline_balloon(); "{\\reviewballoon{#{yield}}}" ; end
673
900
 
674
901
  def on_inline_tt()
675
- ## LaTeXでは、'\texttt{}' 中の '!?:.' の直後の空白が2文字分で表示される。
676
- ## その問題を回避するために、' ' を '\ ' にする。
677
- s = yield
678
- s = s.gsub(/([!?:.]) /, '\\1\\ ')
679
- return "{\\reviewtt{#{s}}}"
902
+ return "{\\reviewtt{#{yield}}}"
680
903
  end
681
904
 
682
905
  def on_inline_code()
683
906
  with_context(:inline_code) {
684
- ## LaTeXでは、'\texttt{}' 中の '!?:.' の直後の空白が2文字分で表示される。
685
- ## その問題を回避するために、' ' を '\ ' にする。
907
+ ## 連続した空白が1つの空白として扱われるのを防ぐ
686
908
  s = yield
687
- s = s.gsub(/([!?:.]) /, '\\1\\ ')
909
+ s = s.gsub(/ /, '{\\starterspacechar}')
910
+ ## 連続した「`」や「'」はエスケープする必要がある
911
+ s = s.gsub(/\'\'/, "{'}{'}")
912
+ s = s.gsub(/\`\`/, '{`}{`}')
688
913
  ## コンテキストによって、背景色をつけないことがある
689
914
  if false
690
915
  elsif within_context?(:headline) # 章タイトルや節タイトルでは
@@ -719,35 +944,43 @@ module ReVIEW
719
944
  if within_codeblock?()
720
945
  #"\\reviewstrike{#{yield}}" # \seqsplit{} 内でエラーになる
721
946
  #"{\\reviewstrike{#{yield}}}" # \seqsplit{} 内でもエラーにならないが折り返しされない
722
- "{\\reviewstrike{\\seqsplit{#{yield}}}}" # エラーにならないし、折り返しもされる
947
+ #"{\\reviewstrike{\\seqsplit{#{yield}}}}" # エラーにならないし、折り返しもされる
948
+ "{\\reviewstrike{#{yield}}}"
723
949
  else
724
950
  macro('reviewstrike', yield)
725
951
  end
726
952
  end
727
953
 
728
954
  def build_inline_href(url, escaped_label) # compile_href()をベースに改造
729
- flag_footnote = @book.config['starter']['linkurl_footnote']
955
+ flag_footnote = self.config_starter['linkurl_footnote']
730
956
  return _inline_hyperlink(url, escaped_label, flag_footnote)
731
957
  end
732
958
 
733
959
  ## @<href>{} の代わり
734
960
  def inline_hlink(str)
735
961
  url, label = str.split(/, /, 2)
736
- flag_footnote = @book.config['starter']['hyperlink_footnote']
737
- return _inline_hyperlink(url, escape(label), flag_footnote)
962
+ flag_footnote = self.config_starter['hyperlink_footnote']
963
+ label_ = label.present? ? escape(label) : nil
964
+ return _inline_hyperlink(url, label_, flag_footnote)
738
965
  end
739
966
 
740
967
  def _inline_hyperlink(url, escaped_label, flag_footnote)
741
968
  if /\A[a-z]+:/ !~ url
742
969
  "\\ref{#{url}}"
743
970
  elsif ! escaped_label.present?
744
- "\\url{#{escape_url(url)}}"
971
+ #"\\url{#{escape_url(url)}}"
972
+ "\\starterurl{#{escape_url(url)}}{#{escape(url)}}"
745
973
  elsif ! flag_footnote
746
974
  "\\href{#{escape_url(url)}}{#{escaped_label}}"
747
975
  elsif within_context?(:footnote)
748
- "#{escaped_label}(\\url{#{escape_url(url)}})"
976
+ #"#{escaped_label}(\\url{#{escape_url(url)}})"
977
+ "#{escaped_label}(\\starterurl{#{escape_url(url)}}{#{escape(url)}})"
749
978
  else
750
- "#{escaped_label}\\footnote{\\url{#{escape_url(url)}}}"
979
+ #"#{escaped_label}\\footnote{\\url{#{escape_url(url)}}}"
980
+ #"#{escaped_label}\\footnote{\\starterurl{#{escape_url(url)}}{#{escape(url)}}}"
981
+ url1 = escape_url(url)
982
+ url2 = escape(url)
983
+ "\\href{#{url1}}{#{escaped_label}}\\footnote{\\starterurl{#{url1}}{#{url2}}}"
751
984
  end
752
985
  end
753
986
  private :_inline_hyperlink
@@ -776,6 +1009,56 @@ module ReVIEW
776
1009
  escape("#{I18n.t('equation')}#{chapter.number}.#{number}")
777
1010
  end
778
1011
 
1012
+ public
1013
+
1014
+ ## 数式を $...$ から \(...\) に変更する。
1015
+ ## これで //list の中でも @<m>$...$ が使えるようになる。
1016
+ ## ただし '^' や '_' がエスケープされるので、実用性はイマイチ。
1017
+ def inline_m(str)
1018
+ #" $#{str}$ " # original
1019
+ "\\(#{str}\\)"
1020
+ end
1021
+
1022
+ ## 「``」と「''」で囲む
1023
+ def on_inline_qq()
1024
+ "``#{yield}''"
1025
+ end
1026
+
1027
+ ## 索引に載せる語句 (@<idx>{}, @<term>{})
1028
+ def inline_idx(str)
1029
+ s1, s2 = _compile_term(str)
1030
+ "#{s1}\\index{#{s2}}"
1031
+ end
1032
+ def inline_hidx(str)
1033
+ _, s2 = _compile_term(str)
1034
+ "\\index{#{s2}}"
1035
+ end
1036
+ def inline_term(str)
1037
+ s1, s2 = _compile_term(str)
1038
+ "\\starterterm{#{s1}}\\index{#{s2}}"
1039
+ end
1040
+ def on_inline_termnoidx()
1041
+ "\\starterterm{#{yield}}"
1042
+ end
1043
+
1044
+ def _compile_term(str)
1045
+ arr = []
1046
+ placeholder = "\\starterindexplaceholder{}"
1047
+ display_str, see = parse_term(str, placeholder) do |term, term_e, yomi|
1048
+ if yomi
1049
+ arr << "#{escape_index(escape_latex(yomi))}@#{term_e}"
1050
+ elsif escape_index(term) != term_e
1051
+ arr << "#{escape_index(term)}@#{term_e}"
1052
+ else
1053
+ arr << term_e
1054
+ end
1055
+ end
1056
+ argstr = arr.join('!')
1057
+ argstr += "|see{#{escape_latex(see)}}" if see
1058
+ return display_str, argstr
1059
+ end
1060
+ private :_compile_term
1061
+
779
1062
  end
780
1063
 
781
1064