review-retrovert 0.3.3 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/retrovert.yml +39 -0
  3. data/.gitignore +2 -2
  4. data/.ruby-version +1 -0
  5. data/Gemfile +1 -0
  6. data/Gemfile.lock +67 -1
  7. data/README.md +6 -3
  8. data/lib/review/retrovert/converter.rb +231 -6
  9. data/lib/review/retrovert/version.rb +1 -1
  10. data/review-retrovert.gemspec +1 -1
  11. data/testdata/mybook/.gitignore +7 -0
  12. data/testdata/mybook/README.md +43 -0
  13. data/testdata/mybook/Rakefile +16 -0
  14. data/testdata/mybook/catalog.yml +33 -0
  15. data/testdata/mybook/config-starter.yml +139 -0
  16. data/testdata/mybook/config.yml +375 -0
  17. data/testdata/mybook/contents/00-preface.re +83 -0
  18. data/testdata/mybook/contents/01-install.re +272 -0
  19. data/testdata/mybook/contents/02-tutorial.re +1008 -0
  20. data/testdata/mybook/contents/03-syntax.re +2613 -0
  21. data/testdata/mybook/contents/04-customize.re +728 -0
  22. data/testdata/mybook/contents/05-faq.re +328 -0
  23. data/testdata/mybook/contents/06-bestpractice.re +971 -0
  24. data/testdata/mybook/contents/91-compare.re +18 -0
  25. data/testdata/mybook/contents/92-filelist.re +119 -0
  26. data/testdata/mybook/contents/93-background.re +267 -0
  27. data/testdata/mybook/contents/99-postface.re +38 -0
  28. data/testdata/mybook/css/normalize.css +349 -0
  29. data/testdata/mybook/css/webstyle.css +514 -0
  30. data/testdata/mybook/images/03-syntax/favicon-16x16.png +0 -0
  31. data/testdata/mybook/images/03-syntax/figure_heretop.png +0 -0
  32. data/testdata/mybook/images/03-syntax/tw-icon1.jpg +0 -0
  33. data/testdata/mybook/images/03-syntax/tw-icon2.jpg +0 -0
  34. data/testdata/mybook/images/03-syntax/tw-icon3.jpg +0 -0
  35. data/testdata/mybook/images/03-syntax/tw-icon4.jpg +0 -0
  36. data/testdata/mybook/images/04-customize/caption_pagebreak.png +0 -0
  37. data/testdata/mybook/images/04-customize/chaptitlepage_sample.png +0 -0
  38. data/testdata/mybook/images/05-faq/codeblock_rpadding1.png +0 -0
  39. data/testdata/mybook/images/05-faq/codeblock_rpadding2.png +0 -0
  40. data/testdata/mybook/images/06-bestpractice/figure_heretop.png +0 -0
  41. data/testdata/mybook/images/06-bestpractice/font_beramono.png +0 -0
  42. data/testdata/mybook/images/06-bestpractice/heading_design1.png +0 -0
  43. data/testdata/mybook/images/06-bestpractice/heading_design2.png +0 -0
  44. data/testdata/mybook/images/06-bestpractice/margin_book.png +0 -0
  45. data/testdata/mybook/images/06-bestpractice/multiline-title.png +0 -0
  46. data/testdata/mybook/images/06-bestpractice/preface_numbered.png +0 -0
  47. data/testdata/mybook/images/06-bestpractice/program_border.png +0 -0
  48. data/testdata/mybook/images/06-bestpractice/sechead_design_4.png +0 -0
  49. data/testdata/mybook/images/06-bestpractice/titlepage-samples.png +0 -0
  50. data/testdata/mybook/images/93-background/bug913.png +0 -0
  51. data/testdata/mybook/images/93-background/slide2.png +0 -0
  52. data/testdata/mybook/images/cover_a5.pdf +0 -0
  53. data/testdata/mybook/images/cover_b5.pdf +0 -0
  54. data/testdata/mybook/images/tw-icon.jpg +0 -0
  55. data/testdata/mybook/layouts/layout.epub.erb +29 -0
  56. data/testdata/mybook/layouts/layout.html5.erb +106 -0
  57. data/testdata/mybook/layouts/layout.tex.erb +546 -0
  58. data/testdata/mybook/lib/hooks/beforetexcompile.rb +55 -0
  59. data/testdata/mybook/lib/ruby/review-builder.rb +503 -0
  60. data/testdata/mybook/lib/ruby/review-cli.rb +58 -0
  61. data/testdata/mybook/lib/ruby/review-compiler.rb +523 -0
  62. data/testdata/mybook/lib/ruby/review-epubmaker.rb +606 -0
  63. data/testdata/mybook/lib/ruby/review-htmlbuilder.rb +661 -0
  64. data/testdata/mybook/lib/ruby/review-latexbuilder.rb +782 -0
  65. data/testdata/mybook/lib/ruby/review-maker.rb +235 -0
  66. data/testdata/mybook/lib/ruby/review-monkeypatch.rb +91 -0
  67. data/testdata/mybook/lib/ruby/review-pdfmaker.rb +468 -0
  68. data/testdata/mybook/lib/ruby/review-textbuilder.rb +36 -0
  69. data/testdata/mybook/lib/ruby/review-tocparser.rb +285 -0
  70. data/testdata/mybook/lib/ruby/review-webmaker.rb +433 -0
  71. data/testdata/mybook/lib/tasks/mytasks.rake +31 -0
  72. data/testdata/mybook/lib/tasks/review.rake +142 -0
  73. data/testdata/mybook/lib/tasks/review.rake.orig +72 -0
  74. data/testdata/mybook/lib/tasks/starter.rake +326 -0
  75. data/testdata/mybook/locale.yml +6 -0
  76. data/testdata/mybook/review-ext.rb +206 -0
  77. data/testdata/mybook/sty/jumoline.sty +310 -0
  78. data/testdata/mybook/sty/mycolophon.sty +81 -0
  79. data/testdata/mybook/sty/mystyle.sty +8 -0
  80. data/testdata/mybook/sty/mytextsize.sty +61 -0
  81. data/testdata/mybook/sty/mytitlepage.sty +103 -0
  82. data/testdata/mybook/sty/reviewmacro.sty +60 -0
  83. data/testdata/mybook/sty/starter-codeblock.sty +332 -0
  84. data/testdata/mybook/sty/starter-color.sty +79 -0
  85. data/testdata/mybook/sty/starter-font.sty +112 -0
  86. data/testdata/mybook/sty/starter-heading.sty +514 -0
  87. data/testdata/mybook/sty/starter-note.sty +127 -0
  88. data/testdata/mybook/sty/starter-section.sty +262 -0
  89. data/testdata/mybook/sty/starter-toc.sty +72 -0
  90. data/testdata/mybook/sty/starter.sty +554 -0
  91. data/testdata/mybook/style.css +597 -0
  92. metadata +100 -3
@@ -0,0 +1,782 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ ##
4
+ ## ReVIEW::LATEXBuilderクラスを拡張する
5
+ ##
6
+
7
+ require 'review/latexbuilder'
8
+
9
+
10
+ module ReVIEW
11
+
12
+ defined?(LATEXBuilder) or raise "internal error: LATEXBuilder not found."
13
+
14
+
15
+ class LATEXBuilder
16
+
17
+ ## 章や節や項や目のタイトル
18
+ def headline(level, label, caption)
19
+ with_context(:headline) do
20
+ _, anchor = headline_prefix(level)
21
+ headname = _headline_name(level) # 'chapter', 'section', 'subsection', ...
22
+ headstar = _headline_star(level) # '*' or nil
23
+ blank unless @output.pos == 0
24
+ with_context(:caption) do
25
+ print macro("#{headname}#{headstar}", compile_inline(caption))
26
+ print "\n" unless level >= 5 # \paragraphと\subpararaphでは改行しない
27
+ end
28
+ if headstar && _headline_toc?(level) # 番号はつかないけど目次には出す場合
29
+ ## starter-heading.sty を使っているときは \addcontentsline が必要ない
30
+ #puts "\\@ifundefined{Chapter}{\\addcontentsline{toc}{#{headname}}{#{compile_inline(caption)}}}{}"
31
+ puts "\\ifx\\Chapter\\undefined{\\addcontentsline{toc}{#{headname}}{#{compile_inline(caption)}}}\\fi"
32
+ end
33
+ if _headline_chapter?(level)
34
+ puts macro('label', chapter_label)
35
+ elsif level >= 5 # 段(Paragraph)と小段(Subparagraph)では
36
+ nil # 何もしない、\labelもつけない
37
+ else
38
+ ## \Section最後と\Subsection最初の\addvspaceが効くように、
39
+ ## \lastskipをいったん保存し、\labelのあとで復元する。
40
+ puts "\\keeplastskip{"
41
+ puts " \\label{#{sec_label(anchor)}}"
42
+ puts " \\label{#{label}}" if label
43
+ puts " \\par\\nobreak"
44
+ puts "}"
45
+ end
46
+ end
47
+ rescue
48
+ error "unknown level: #{level}"
49
+ end
50
+
51
+ private
52
+
53
+ def _headline_name(level)
54
+ return 'part' if @chapter.is_a?(ReVIEW::Book::Part)
55
+ return HEADLINE[level] # 1: 'chapter', 2: 'section', ...
56
+ end
57
+
58
+ def _headline_star(level)
59
+ return '*' if level > @book.config['secnolevel']
60
+ return '*' if @chapter.number.to_s.empty? && level > 1
61
+ return nil
62
+ end
63
+
64
+ def _headline_toc?(level)
65
+ return level <= @book.config['toclevel'].to_i
66
+ end
67
+
68
+ def _headline_chapter?(level)
69
+ return level == 1
70
+ end
71
+
72
+ public
73
+
74
+ def nonum_begin(level, _label, caption)
75
+ blank unless @output.pos == 0
76
+ with_context(:headline) do
77
+ with_context(:caption) do
78
+ puts macro(HEADLINE[level] + '*', compile_inline(caption))
79
+ puts "\\ifx\\Chapter\\undefined"
80
+ puts macro('addcontentsline', 'toc', HEADLINE[level], compile_inline(caption))
81
+ puts "\\fi"
82
+ end
83
+ end
84
+ end
85
+
86
+ def notoc_begin(level, _label, caption)
87
+ blank unless @output.pos == 0
88
+ with_context(:headline) do
89
+ with_context(:caption) do
90
+ puts "\\ifx\\Chapter\\undefined"
91
+ puts macro(HEADLINE[level] + '*', compile_inline(caption))
92
+ puts "\\else"
93
+ puts macro(HEADLINE[level] + '[]', compile_inline(caption))
94
+ puts "\\fi"
95
+ end
96
+ end
97
+ end
98
+
99
+ ## テーブルヘッダー
100
+ ## (TODO: 第3引数にpos=htbpを指定)
101
+ def table_header(id, caption)
102
+ if caption.present?
103
+ @table_caption = true
104
+ star = id.present? ? '' : '*'
105
+ s = with_context(:caption) { compile_inline(caption) }
106
+ puts "\\begin{table}[h]%%#{id}"
107
+ puts "\\centering%"
108
+ puts macro("reviewtablecaption#{star}", s)
109
+ end
110
+ puts macro('label', table_label(id)) if id.present?
111
+ end
112
+
113
+ ## 改行命令「\\」のあとに改行文字「\n」を置かない。
114
+ ##
115
+ ## 「\n」が置かれると、たとえば
116
+ ##
117
+ ## foo@<br>{}
118
+ ## bar
119
+ ##
120
+ ## が
121
+ ##
122
+ ## foo\\
123
+ ##
124
+ ## bar
125
+ ##
126
+ ## に展開されてしまう。
127
+ ## つまり改行のつもりが改段落になってしまう。
128
+ def inline_br(_str)
129
+ #"\\\\\n" # original
130
+ #"\\\\{}" # これだと後続行の先頭に1/4空白が入ってしまう
131
+ "\\\\[0pt]" # これなら後続行の先頭に1/4空白が入らない
132
+ end
133
+
134
+
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
+
145
+ protected
146
+
147
+ FONTSIZES = {
148
+ "small" => "small",
149
+ "x-small" => "footnotesize",
150
+ "xx-small" => "scriptsize",
151
+ "large" => "large",
152
+ "x-large" => "Large",
153
+ "xx-large" => "LARGE",
154
+ }
155
+
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
+ #
164
+ if opts['eolmark']
165
+ lines = lines.map {|line| "#{detab(line)}\\startereolmark{}" }
166
+ else
167
+ lines = lines.map {|line| detab(line) }
168
+ end
169
+ #
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
+ }
178
+ }
179
+ end
180
+ #
181
+ if id.present? || caption.present?
182
+ caption_str = _build_caption_str(id, caption)
183
+ else
184
+ caption_str = nil
185
+ end
186
+ #
187
+ fontsize = FONTSIZES[opts['fontsize']]
188
+ print "\\def\\startercodeblockfontsize{#{fontsize}}\n"
189
+ #
190
+ environ = "starter#{blockname}"
191
+ print "\\begin{#{environ}}[#{id}]{#{caption_str}}"
192
+ print "\\startersetfoldmark{}" unless opts['foldmark']
193
+ if opts['eolmark']
194
+ print "\\startereolmarkdark{}" if blockname == 'terminal'
195
+ print "\\startereolmarklight{}" if blockname != 'terminal'
196
+ end
197
+ if opts['lineno']
198
+ gen = LineNumberGenerator.new(opts['lineno'])
199
+ width = opts['linenowidth']
200
+ if width && width >= 0
201
+ if width == 0
202
+ last_lineno = gen.each.take(lines.length).compact.last
203
+ width = last_lineno.to_s.length
204
+ end
205
+ print "\\startersetfoldindentwidth{#{'9'*(width+2)}}"
206
+ format = "\\textcolor{gray}{%#{width}s:} "
207
+ else
208
+ format = "\\starterlineno{%s}"
209
+ end
210
+ buf = []
211
+ opt_fold = opts['fold']
212
+ lines.zip(gen).each do |x, n|
213
+ buf << ( opt_fold \
214
+ ? "#{format % n.to_s}\\seqsplit{#{x}}" \
215
+ : "#{format % n.to_s}#{x}" )
216
+ end
217
+ print buf.join("\n")
218
+ else
219
+ print "\\seqsplit{" if opts['fold']
220
+ print lines.join("\n")
221
+ print "}" if opts['fold']
222
+ end
223
+ puts "\\end{#{environ}}"
224
+ nil
225
+ end
226
+
227
+ public
228
+
229
+ ## ・\caption{} のかわりに \reviewimagecaption{} を使うよう修正
230
+ ## ・「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'
249
+ else
250
+ arr << (v.nil? ? k : "#{k}=#{v}")
251
+ end
252
+ end
253
+ #
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
261
+ with_context(:caption) do
262
+ #puts macro('caption', compile_inline(caption)) if caption.present? # original
263
+ puts macro('reviewimagecaption', compile_inline(caption)) if caption.present?
264
+ end
265
+ puts macro('label', image_label(id))
266
+ puts "\\end{reviewimage}"
267
+ end
268
+
269
+ def _build_secref(chap, num, title, parent_title)
270
+ s = ""
271
+ ## 親セクションのタイトルがあれば使う
272
+ if parent_title && @book.config['starter']['secref_parenttitle']
273
+ s << "「%s」内の" % parent_title # TODO: I18n化
274
+ end
275
+ ## 対象セクションへのリンクを作成する
276
+ if @book.config['chapterlink']
277
+ label = "sec:" + num.gsub('.', '-')
278
+ level = num.split('.').length
279
+ case level
280
+ when 2 ; s << "\\startersecref{#{title}}{#{label}}"
281
+ when 3 ; s << "\\startersubsecref{#{title}}{#{label}}"
282
+ when 4 ; s << "\\startersubsubsecref{#{title}}{#{label}}"
283
+ else
284
+ raise "#{num}: unexpected section level (expected: 2~4)."
285
+ end
286
+ else
287
+ s << title
288
+ end
289
+ return s
290
+ end
291
+
292
+ ###
293
+
294
+ public
295
+
296
+ def ul_begin
297
+ blank
298
+ puts '\begin{starteritemize}' # instead of 'itemize'
299
+ end
300
+
301
+ def ul_end
302
+ puts '\end{starteritemize}' # instead of 'itemize'
303
+ blank
304
+ end
305
+
306
+ def ol_begin(start_num=nil)
307
+ blank
308
+ puts '\begin{starterenumerate}' # instead of 'enumerate'
309
+ if start_num.nil?
310
+ return true unless @ol_num
311
+ puts "\\setcounter{enumi}{#{@ol_num - 1}}"
312
+ @ol_num = nil
313
+ end
314
+ end
315
+
316
+ def ol_end
317
+ puts '\end{starterenumerate}' # instead of 'enumerate'
318
+ blank
319
+ end
320
+
321
+ def ol_item_begin(lines, num)
322
+ str = lines.join
323
+ num = escape(num).sub(']', '\rbrack{}')
324
+ puts "\\item[#{num}] #{str}"
325
+ end
326
+
327
+ def ol_item_end()
328
+ end
329
+
330
+ ## コラム
331
+
332
+ def column_begin(level, label, caption)
333
+ blank
334
+ @doc_status[:column] = true
335
+ puts "\\begin{reviewcolumn}\n"
336
+ puts "\\phantomsection % for hyperref" #+
337
+ if label
338
+ puts "\\hypertarget{#{column_label(label)}}{}"
339
+ else
340
+ puts "\\hypertarget{#{column_label(caption)}}{}"
341
+ end
342
+ @doc_status[:caption] = true
343
+ puts macro('reviewcolumnhead', nil, compile_inline(caption))
344
+ @doc_status[:caption] = nil
345
+ if level <= @book.config['toclevel'].to_i
346
+ #puts "\\addcontentsline{toc}{#{HEADLINE[level]}}{#{compile_inline(caption)}}" #-
347
+ puts "\\addcontentsline{toc}{#{HEADLINE[level]}}{\\numberline{#{toc_column()}}#{compile_inline(caption)}}" #+
348
+ end
349
+ end
350
+
351
+ def toc_column
352
+ #escape('コラム:')
353
+ escape('[コラム]')
354
+ end
355
+
356
+ #### ブロック命令
357
+
358
+ ## 導入文(//lead{ ... //})のデザインをLaTeXのスタイルファイルで
359
+ ## 変更できるよう、マクロを使う。
360
+ def lead(lines)
361
+ puts '\begin{starterlead}' # オリジナルは \begin{quotation}
362
+ puts lines
363
+ puts '\end{starterlead}'
364
+ end
365
+
366
+ ## 章 (Chapter) の概要
367
+ ## (導入文 //lead{ ... //} と似ているが、導入文では詩や物語を
368
+ ## 引用するのが普通らしく、概要 (abstract) とは違うみたいなので、
369
+ ## 概要を表すブロックを用意した。)
370
+ def abstract(lines)
371
+ puts '\begin{starterabstract}'
372
+ puts lines
373
+ puts '\end{starterabstract}'
374
+ end
375
+
376
+ ## 章タイトルを独立したページに
377
+ def makechaptitlepage(option=nil)
378
+ case option
379
+ when nil, "" ;
380
+ when 'toc=section', 'toc=subsection' ;
381
+ when 'toc', 'toc=on'
382
+ option = "toc=on"
383
+ else
384
+ raise ArgumentError.new("//makechaptitlepage[#{option}]: unknown option (expected 'toc=section' or 'toc=subsection').")
385
+ end
386
+ puts "\\makechaptitlepage{#{option}}"
387
+ end
388
+
389
+ ## 縦方向のスペースがなければ改ページ
390
+ def needvspace(builder_name, height)
391
+ if builder_name == 'latex'
392
+ puts "\\needvspace{#{height}}"
393
+ end
394
+ end
395
+
396
+ ## 段(Paragraph)の終わりにスペースを入れる
397
+ def paragraphend()
398
+ puts "\\ParagraphEnd"
399
+ end
400
+
401
+ ## 小段(Subparagraph)の終わりにスペースを入れる(あれば)
402
+ def subparagraphend()
403
+ puts "\\SubparagraphEnd"
404
+ end
405
+
406
+ ## 引用(複数段落に対応)
407
+ ## (入れ子対応なので、中に箇条書きや別のブロックを入れられる)
408
+ def on_quote_block()
409
+ puts '\begin{starterquote}'
410
+ yield
411
+ puts '\end{starterquote}'
412
+ end
413
+ def quote(lines)
414
+ on_quote_block() do
415
+ puts lines
416
+ end
417
+ end
418
+
419
+ ## 引用 (====[quote] ... ====[/quote])
420
+ ## (ブロック構文ではないので、中に箇条書きや別のブロックを入れられる)
421
+ def quote_begin(level, label, caption)
422
+ puts '\begin{starterquote}'
423
+ end
424
+ def quote_end(level)
425
+ puts '\end{starterquote}'
426
+ end
427
+
428
+ ## ノート(//note[caption]{ ... //})
429
+ ## (入れ子対応なので、中に箇条書きや別のブロックを入れられる)
430
+ 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}"
436
+ end
437
+ def note(lines, label=nil, caption=nil)
438
+ on_note_block(label, caption) do
439
+ puts lines
440
+ end
441
+ end
442
+
443
+ ## ノート (====[note] ... ====[/note])
444
+ ## (ブロック構文ではないので、中に箇条書きや別のブロックを入れられる)
445
+ def note_begin(level, label, caption)
446
+ enter_context(:note)
447
+ s = compile_inline(caption || "")
448
+ puts "\\begin{starternote}[#{label}]{#{s}}"
449
+ end
450
+ def note_end(level)
451
+ puts "\\end{starternote}"
452
+ exit_context(:note)
453
+ end
454
+
455
+ ## コードリスト(//list, //emlist, //listnum, //emlistnum, //cmd, //source)
456
+ ## TODO: code highlight support
457
+ def list(lines, id=nil, caption=nil, lang=nil)
458
+ program(lines, id, caption, _codeblock_optstr(lang, false))
459
+ end
460
+ def listnum(lines, id=nil, caption=nil, lang=nil)
461
+ program(lines, id, caption, _codeblock_optstr(lang, true))
462
+ end
463
+ def emlist(lines, caption=nil, lang=nil)
464
+ program(lines, nil, caption, _codeblock_optstr(lang, false))
465
+ end
466
+ def emlistnum(lines, caption=nil, lang=nil)
467
+ program(lines, nil, caption, _codeblock_optstr(lang, true))
468
+ end
469
+ def source(lines, caption=nil, lang=nil)
470
+ program(lines, nil, caption, _codeblock_optstr(lang, false))
471
+ end
472
+ def cmd(lines, caption=nil, lang=nil)
473
+ terminal(lines, nil, caption, _codeblock_optstr(lang, false))
474
+ end
475
+ def _codeblock_optstr(lang, lineno_flag)
476
+ arr = []
477
+ arr << lang if lang
478
+ if lineno_flag
479
+ first_line_num = line_num
480
+ arr << "lineno=#{first_line_num}"
481
+ arr << "linenowidth=0"
482
+ end
483
+ return arr.join(",")
484
+ end
485
+ private :_codeblock_optstr
486
+
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'
502
+ normalize = proc {|s|
503
+ s =~ /\A(\d+(?:\.\d+)?)(%|mm|cm)\z/
504
+ if $2.nil? ; s
505
+ elsif $2 == '%' ; "#{$1.to_f/100.0}\\textwidth"
506
+ else ; "#{$1}true#{$2}"
507
+ end
508
+ }
509
+ imgwidth = normalize.call(imagewidth)
510
+ boxwidth = normalize.call(opts['boxwidth']) || imgwidth
511
+ sepwidth = normalize.call(opts['sep'] || "0pt")
512
+ puts "{\n"
513
+ puts " \\def\\starterminiimageframe{Y}\n" if opts['border']
514
+ puts " \\begin{startersideimage}{#{side}}{#{filepath}}{#{imgwidth}}{#{boxwidth}}{#{sepwidth}}{}\n"
515
+ yield
516
+ puts " \\end{startersideimage}\n"
517
+ puts "}\n"
518
+ end
519
+
520
+ def texequation(lines, label=nil, caption=nil)
521
+ blank()
522
+ #
523
+ if label.present?
524
+ puts macro("begin", "reviewequationblock")
525
+ chap = get_chap()
526
+ if chap.nil?
527
+ key = "format_number_header_without_chapter"
528
+ args = [@chapter.equation(label).number]
529
+ else
530
+ key = "format_number_header"
531
+ args = [chap, @chapter.equation(label).number]
532
+ end
533
+ s1 = I18n.t("equation")
534
+ s2 = I18n.t(key, args)
535
+ s3 = I18n.t("caption_prefix")
536
+ s4 = compile_inline(caption)
537
+ puts macro("reviewequationcaption", "#{s1}#{s2}#{s3}#{s4}")
538
+ has_caption_line = true
539
+ elsif caption.present?
540
+ puts macro("begin", "reviewequationblock")
541
+ s3 = I18n.t("caption_prefix")
542
+ s4 = compile_inline(caption)
543
+ puts macro("reviewequationcaption", "#{s3}#{s4}")
544
+ has_caption_line = true
545
+ else
546
+ has_caption_line = false
547
+ end
548
+ #
549
+ puts macro("begin", "equation*")
550
+ lines.each do |line|
551
+ puts unescape_latex(line)
552
+ end
553
+ puts macro("end", "equation*")
554
+ #
555
+ if has_caption_line
556
+ puts macro("end", "reviewequationblock")
557
+ end
558
+ #
559
+ blank()
560
+ end
561
+
562
+ #### インライン命令
563
+
564
+ ## ファイル名
565
+ def inline_file(str)
566
+ on_inline_file { escape(str) }
567
+ end
568
+ def on_inline_file
569
+ "\\starterfile{#{yield}}"
570
+ end
571
+
572
+ ## ユーザ入力
573
+ def inline_userinput(str)
574
+ on_inline_userinput { escape(str) }
575
+ end
576
+ def on_inline_userinput
577
+ if within_codeblock?()
578
+ "{\\starteruserinput{\\seqsplit{#{yield}}}}"
579
+ else
580
+ "\\starteruserinput{#{yield}}"
581
+ end
582
+ end
583
+
584
+ ## 引数をそのまま表示
585
+ ## 例:
586
+ ## //emlist{
587
+ ## @<b>{ABC} ← 太字の「ABC」が表示される
588
+ ## @<nop>$@<b>{ABC}$ ← 「@<b>{ABC}」がそのまま表示される
589
+ ## //}
590
+ def inline_nop(str)
591
+ escape(str || "")
592
+ end
593
+ alias inline_letitgo inline_nop
594
+
595
+ ## 目立たせない(@<strong>{} の反対)
596
+ def inline_weak(str)
597
+ on_inline_weak { escape(str) }
598
+ end
599
+ def on_inline_weak
600
+ if within_codeblock?()
601
+ "{\\starterweak{\\seqsplit{#{yield}}}}"
602
+ else
603
+ "\\starterweak{#{yield}}"
604
+ end
605
+ end
606
+
607
+ ## 文字を小さくする
608
+ def inline_small(str) ; on_inline_small { escape(str) } ; end
609
+ def inline_xsmall(str) ; on_inline_xsmall { escape(str) } ; end
610
+ def inline_xxsmall(str) ; on_inline_xxsmall { escape(str) }; end
611
+ def on_inline_small() ; "{\\small{}#{yield}}" ; end
612
+ def on_inline_xsmall() ; "{\\footnotesize{}#{yield}}"; end
613
+ def on_inline_xxsmall() ; "{\\scriptsize{}#{yield}}" ; end
614
+
615
+ ## 文字を大きくする
616
+ def inline_large(str) ; on_inline_large { escape(str) } ; end
617
+ def inline_xlarge(str) ; on_inline_xlarge { escape(str) } ; end
618
+ def inline_xxlarge(str) ; on_inline_xxlarge { escape(str) }; end
619
+ def on_inline_large() ; "{\\large{}#{yield}}" ; end
620
+ def on_inline_xlarge() ; "{\\Large{}#{yield}}" ; end
621
+ def on_inline_xxlarge() ; "{\\LARGE{}#{yield}}" ; end
622
+
623
+ ## 文字を大きくした@<strong>{}
624
+ def inline_xstrong(str) ; on_inline_xstring { escape(str) } ; end
625
+ def inline_xxstrong(str); on_inline_xxstring { escape(str) }; end
626
+ def on_inline_xstrong(&b) ; "{\\Large{}#{on_inline_strong(&b)}}" ; end
627
+ def on_inline_xxstrong(&b); "{\\LARGE{}#{on_inline_strong(&b)}}" ; end
628
+
629
+ ## コードブロック中で折り返し箇所を手動で指定する
630
+ ## (\seqsplit による自動折り返し機能が日本語には効かないので、
631
+ ## 長い行を日本語の箇所で折り返したいときは @<foldhere>{} を使う)
632
+ def inline_foldhere(arg)
633
+ return '\starterfoldhere{}'
634
+ end
635
+
636
+ ## ターミナルでのカーソル(背景が白、文字が黒)
637
+ def inline_cursor(str)
638
+ "{\\startercursor{#{escape(str)}}}"
639
+ end
640
+
641
+ ## 脚注(「//footnote」の脚注テキストを「@<fn>{}」でパースすることに注意)
642
+ def inline_fn(id)
643
+ if @book.config['footnotetext']
644
+ macro("footnotemark[#{@chapter.footnote(id).number}]", '')
645
+ elsif @doc_status[:caption] || @doc_status[:table] || @doc_status[:column]
646
+ @foottext[id] = @chapter.footnote(id).number
647
+ macro('protect\\footnotemark', '')
648
+ else
649
+ with_context(:footnote) { #+
650
+ macro('footnote', compile_inline(@chapter.footnote(id).content.strip))
651
+ } #+
652
+ end
653
+ rescue KeyError
654
+ error "unknown footnote: #{id}"
655
+ end
656
+
657
+ ## nestable inline commands
658
+
659
+ def on_inline_i() ; "{\\reviewit{#{yield}}}" ; end
660
+ #def on_inline_b() ; "{\\reviewbold{#{yield}}}" ; end
661
+ #def on_inline_tt() ; "{\\reviewtt{#{yield}}}" ; end
662
+ def on_inline_tti() ; "{\\reviewtti{#{yield}}}" ; end
663
+ def on_inline_ttb() ; "{\\reviewttb{#{yield}}}" ; end
664
+ #def on_inline_code() ; "{\\reviewcode{#{yield}}}" ; end
665
+ #def on_inline_del() ; "{\\reviewstrike{#{yield}}}" ; end
666
+ def on_inline_sub() ; "{\\textsubscript{#{yield}}}" ; end
667
+ def on_inline_sup() ; "{\\textsuperscript{#{yield}}}"; end
668
+ def on_inline_em() ; "{\\reviewem{#{yield}}}" ; end
669
+ def on_inline_strong(); "{\\reviewstrong{#{yield}}}" ; end
670
+ def on_inline_u() ; "{\\reviewunderline{#{yield}}}"; end
671
+ def on_inline_ami() ; "{\\reviewami{#{yield}}}" ; end
672
+ def on_inline_balloon(); "{\\reviewballoon{#{yield}}}" ; end
673
+
674
+ def on_inline_tt()
675
+ ## LaTeXでは、'\texttt{}' 中の '!?:.' の直後の空白が2文字分で表示される。
676
+ ## その問題を回避するために、' ' を '\ ' にする。
677
+ s = yield
678
+ s = s.gsub(/([!?:.]) /, '\\1\\ ')
679
+ return "{\\reviewtt{#{s}}}"
680
+ end
681
+
682
+ def on_inline_code()
683
+ with_context(:inline_code) {
684
+ ## LaTeXでは、'\texttt{}' 中の '!?:.' の直後の空白が2文字分で表示される。
685
+ ## その問題を回避するために、' ' を '\ ' にする。
686
+ s = yield
687
+ s = s.gsub(/([!?:.]) /, '\\1\\ ')
688
+ ## コンテキストによって、背景色をつけないことがある
689
+ if false
690
+ elsif within_context?(:headline) # 章タイトルや節タイトルでは
691
+ "{\\reviewcode[headline]{#{s}}}" # 背景色をつけない(かも)
692
+ elsif within_context?(:caption) # リストや画像のキャプションでも
693
+ "{\\reviewcode[caption]{#{s}}}" # 背景色をつけない(かも)
694
+ else # それ以外では
695
+ "{\\reviewcode{#{s}}}" # 背景色をつける(かも)
696
+ end
697
+ }
698
+ end
699
+
700
+ ## @<b>{} が //terminal{ ... //} で効くように上書き
701
+ def inline_b(str)
702
+ on_inline_b { escape(str) }
703
+ end
704
+ def on_inline_b() # nestable
705
+ if within_codeblock?()
706
+ #"{\\bfseries #{yield}}" # \seqsplit{} 内では余計な空白が入る
707
+ #"{\\bfseries{}#{yield}}" # \seqsplit{} 内では後続も太字化する
708
+ "\\bfseries{}#{yield}\\mdseries{}" # \seqsplit{} 内でうまく効く
709
+ else
710
+ macro('reviewbold', yield)
711
+ end
712
+ end
713
+
714
+ ## @<del>{} が //list や //terminal で効くように上書き
715
+ def inline_del(str)
716
+ on_inline_del { escape(str) }
717
+ end
718
+ def on_inline_del()
719
+ if within_codeblock?()
720
+ #"\\reviewstrike{#{yield}}" # \seqsplit{} 内でエラーになる
721
+ #"{\\reviewstrike{#{yield}}}" # \seqsplit{} 内でもエラーにならないが折り返しされない
722
+ "{\\reviewstrike{\\seqsplit{#{yield}}}}" # エラーにならないし、折り返しもされる
723
+ else
724
+ macro('reviewstrike', yield)
725
+ end
726
+ end
727
+
728
+ def build_inline_href(url, escaped_label) # compile_href()をベースに改造
729
+ flag_footnote = @book.config['starter']['linkurl_footnote']
730
+ return _inline_hyperlink(url, escaped_label, flag_footnote)
731
+ end
732
+
733
+ ## @<href>{} の代わり
734
+ def inline_hlink(str)
735
+ url, label = str.split(/, /, 2)
736
+ flag_footnote = @book.config['starter']['hyperlink_footnote']
737
+ return _inline_hyperlink(url, escape(label), flag_footnote)
738
+ end
739
+
740
+ def _inline_hyperlink(url, escaped_label, flag_footnote)
741
+ if /\A[a-z]+:/ !~ url
742
+ "\\ref{#{url}}"
743
+ elsif ! escaped_label.present?
744
+ "\\url{#{escape_url(url)}}"
745
+ elsif ! flag_footnote
746
+ "\\href{#{escape_url(url)}}{#{escaped_label}}"
747
+ elsif within_context?(:footnote)
748
+ "#{escaped_label}(\\url{#{escape_url(url)}})"
749
+ else
750
+ "#{escaped_label}\\footnote{\\url{#{escape_url(url)}}}"
751
+ end
752
+ end
753
+ private :_inline_hyperlink
754
+
755
+ def build_inline_ruby(escaped_word, escaped_yomi) # compile_ruby()をベースに改造
756
+ "\\ruby{#{escaped_word}}{#{escaped_yomi}}"
757
+ end
758
+
759
+ def inline_bou(str)
760
+ ## original
761
+ #str.split(//).map { |c| macro('ruby', escape(c), macro('textgt', BOUTEN)) }.join('\allowbreak')
762
+ ## work well with XeLaTeX as well as upLaTeX
763
+ str.split(//).map {|c| "\\ruby{#{escape(c)}}{#{BOUTEN}}" }.join('\allowbreak')
764
+ end
765
+
766
+ protected
767
+
768
+ ## ノートを参照する
769
+ def build_noteref(chapter, label, caption)
770
+ "\\starternoteref{#{label}}{#{escape(caption)}}"
771
+ end
772
+
773
+ ## 数式を参照する
774
+ def build_eq(chapter, label, number)
775
+ #"\\reviewequationref{#{chapter.number}.#{number}}"
776
+ escape("#{I18n.t('equation')}#{chapter.number}.#{number}")
777
+ end
778
+
779
+ end
780
+
781
+
782
+ end