review-retrovert 0.2.2 → 0.9.1

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 (93) 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 +68 -2
  7. data/README.md +6 -3
  8. data/lib/review/retrovert/cli.rb +2 -0
  9. data/lib/review/retrovert/converter.rb +255 -16
  10. data/lib/review/retrovert/version.rb +1 -1
  11. data/review-retrovert.gemspec +1 -1
  12. data/testdata/mybook/.gitignore +7 -0
  13. data/testdata/mybook/README.md +43 -0
  14. data/testdata/mybook/Rakefile +16 -0
  15. data/testdata/mybook/catalog.yml +33 -0
  16. data/testdata/mybook/config-starter.yml +139 -0
  17. data/testdata/mybook/config.yml +375 -0
  18. data/testdata/mybook/contents/00-preface.re +83 -0
  19. data/testdata/mybook/contents/01-install.re +272 -0
  20. data/testdata/mybook/contents/02-tutorial.re +1008 -0
  21. data/testdata/mybook/contents/03-syntax.re +2613 -0
  22. data/testdata/mybook/contents/04-customize.re +728 -0
  23. data/testdata/mybook/contents/05-faq.re +328 -0
  24. data/testdata/mybook/contents/06-bestpractice.re +971 -0
  25. data/testdata/mybook/contents/91-compare.re +18 -0
  26. data/testdata/mybook/contents/92-filelist.re +119 -0
  27. data/testdata/mybook/contents/93-background.re +267 -0
  28. data/testdata/mybook/contents/99-postface.re +38 -0
  29. data/testdata/mybook/css/normalize.css +349 -0
  30. data/testdata/mybook/css/webstyle.css +514 -0
  31. data/testdata/mybook/images/03-syntax/favicon-16x16.png +0 -0
  32. data/testdata/mybook/images/03-syntax/figure_heretop.png +0 -0
  33. data/testdata/mybook/images/03-syntax/tw-icon1.jpg +0 -0
  34. data/testdata/mybook/images/03-syntax/tw-icon2.jpg +0 -0
  35. data/testdata/mybook/images/03-syntax/tw-icon3.jpg +0 -0
  36. data/testdata/mybook/images/03-syntax/tw-icon4.jpg +0 -0
  37. data/testdata/mybook/images/04-customize/caption_pagebreak.png +0 -0
  38. data/testdata/mybook/images/04-customize/chaptitlepage_sample.png +0 -0
  39. data/testdata/mybook/images/05-faq/codeblock_rpadding1.png +0 -0
  40. data/testdata/mybook/images/05-faq/codeblock_rpadding2.png +0 -0
  41. data/testdata/mybook/images/06-bestpractice/figure_heretop.png +0 -0
  42. data/testdata/mybook/images/06-bestpractice/font_beramono.png +0 -0
  43. data/testdata/mybook/images/06-bestpractice/heading_design1.png +0 -0
  44. data/testdata/mybook/images/06-bestpractice/heading_design2.png +0 -0
  45. data/testdata/mybook/images/06-bestpractice/margin_book.png +0 -0
  46. data/testdata/mybook/images/06-bestpractice/multiline-title.png +0 -0
  47. data/testdata/mybook/images/06-bestpractice/preface_numbered.png +0 -0
  48. data/testdata/mybook/images/06-bestpractice/program_border.png +0 -0
  49. data/testdata/mybook/images/06-bestpractice/sechead_design_4.png +0 -0
  50. data/testdata/mybook/images/06-bestpractice/titlepage-samples.png +0 -0
  51. data/testdata/mybook/images/93-background/bug913.png +0 -0
  52. data/testdata/mybook/images/93-background/slide2.png +0 -0
  53. data/testdata/mybook/images/cover_a5.pdf +0 -0
  54. data/testdata/mybook/images/cover_b5.pdf +0 -0
  55. data/testdata/mybook/images/tw-icon.jpg +0 -0
  56. data/testdata/mybook/layouts/layout.epub.erb +29 -0
  57. data/testdata/mybook/layouts/layout.html5.erb +106 -0
  58. data/testdata/mybook/layouts/layout.tex.erb +546 -0
  59. data/testdata/mybook/lib/hooks/beforetexcompile.rb +55 -0
  60. data/testdata/mybook/lib/ruby/review-builder.rb +503 -0
  61. data/testdata/mybook/lib/ruby/review-cli.rb +58 -0
  62. data/testdata/mybook/lib/ruby/review-compiler.rb +523 -0
  63. data/testdata/mybook/lib/ruby/review-epubmaker.rb +606 -0
  64. data/testdata/mybook/lib/ruby/review-htmlbuilder.rb +661 -0
  65. data/testdata/mybook/lib/ruby/review-latexbuilder.rb +782 -0
  66. data/testdata/mybook/lib/ruby/review-maker.rb +235 -0
  67. data/testdata/mybook/lib/ruby/review-monkeypatch.rb +91 -0
  68. data/testdata/mybook/lib/ruby/review-pdfmaker.rb +468 -0
  69. data/testdata/mybook/lib/ruby/review-textbuilder.rb +36 -0
  70. data/testdata/mybook/lib/ruby/review-tocparser.rb +285 -0
  71. data/testdata/mybook/lib/ruby/review-webmaker.rb +433 -0
  72. data/testdata/mybook/lib/tasks/mytasks.rake +31 -0
  73. data/testdata/mybook/lib/tasks/review.rake +142 -0
  74. data/testdata/mybook/lib/tasks/review.rake.orig +72 -0
  75. data/testdata/mybook/lib/tasks/starter.rake +326 -0
  76. data/testdata/mybook/locale.yml +6 -0
  77. data/testdata/mybook/review-ext.rb +206 -0
  78. data/testdata/mybook/sty/jumoline.sty +310 -0
  79. data/testdata/mybook/sty/mycolophon.sty +81 -0
  80. data/testdata/mybook/sty/mystyle.sty +8 -0
  81. data/testdata/mybook/sty/mytextsize.sty +61 -0
  82. data/testdata/mybook/sty/mytitlepage.sty +103 -0
  83. data/testdata/mybook/sty/reviewmacro.sty +60 -0
  84. data/testdata/mybook/sty/starter-codeblock.sty +332 -0
  85. data/testdata/mybook/sty/starter-color.sty +79 -0
  86. data/testdata/mybook/sty/starter-font.sty +112 -0
  87. data/testdata/mybook/sty/starter-heading.sty +514 -0
  88. data/testdata/mybook/sty/starter-note.sty +127 -0
  89. data/testdata/mybook/sty/starter-section.sty +262 -0
  90. data/testdata/mybook/sty/starter-toc.sty +72 -0
  91. data/testdata/mybook/sty/starter.sty +554 -0
  92. data/testdata/mybook/style.css +597 -0
  93. 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