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,661 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ ###
4
+ ### ReVIEW::HTMLBuilderクラスを拡張する
5
+ ###
6
+
7
+ require 'review/htmlbuilder'
8
+
9
+
10
+ module ReVIEW
11
+
12
+ defined?(HTMLBuilder) or raise "internal error: HTMLBuilder not found."
13
+
14
+
15
+ class HTMLBuilder
16
+
17
+ attr_accessor :starter_config
18
+
19
+ def layoutfile
20
+ ## 'rake web' のときに使うレイアウトファイルを 'layout.html5.erb' へ変更
21
+ if @book.config.maker == 'webmaker'
22
+ htmldir = 'web/html'
23
+ #localfilename = 'layout-web.html.erb'
24
+ localfilename = 'layout.html5.erb'
25
+ else
26
+ htmldir = 'html'
27
+ localfilename = 'layout.html.erb'
28
+ end
29
+ ## 以下はリファクタリングした結果
30
+ basename = @book.htmlversion == 5 ? 'layout-html5.html.erb' : 'layout-xhtml1.html.erb'
31
+ htmlfilename = File.join(htmldir, basename)
32
+ #
33
+ layout_file = File.join(@book.basedir, 'layouts', localfilename)
34
+ if ! File.exist?(layout_file)
35
+ ! File.exist?(File.join(@book.basedir, 'layouts', 'layout.erb')) or
36
+ raise ReVIEW::ConfigError, 'layout.erb is obsoleted. Please use layout.html.erb.'
37
+ layout_file = nil
38
+ elsif ENV['REVIEW_SAFE_MODE'].to_i & 4 > 0
39
+ warn "user's layout is prohibited in safe mode. ignored."
40
+ layout_file = nil
41
+ end
42
+ layout_file ||= File.expand_path(htmlfilename, ReVIEW::Template::TEMPLATE_DIR)
43
+ return layout_file
44
+ end
45
+
46
+ def result
47
+ # default XHTML header/footer
48
+ @title = strip_html(compile_inline(@chapter.title))
49
+ @body = @output.string
50
+ @language = @book.config['language']
51
+ @stylesheets = @book.config['stylesheet']
52
+ @next = @chapter.next_chapter
53
+ @prev = @chapter.prev_chapter
54
+ @next_title = @next ? compile_inline(@next.title) : ''
55
+ @prev_title = @prev ? compile_inline(@prev.title) : ''
56
+
57
+ if @book.config.maker == 'webmaker'
58
+ #@toc = ReVIEW::WEBTOCPrinter.book_to_string(@book) #-
59
+ @toc = ReVIEW::WEBTOCPrinter.book_to_html(@book, @chapter) #+
60
+ end
61
+
62
+ ReVIEW::Template.load(layoutfile).result(binding)
63
+ end
64
+
65
+ def headline(level, label, caption)
66
+ prefix, anchor = headline_prefix(level)
67
+ prefix = "<span class=\"secno\">#{prefix}</span> " if prefix
68
+ puts "" if level > 1
69
+ a_id = ""
70
+ a_id = "<a id=\"h#{anchor}\"></a>" if anchor
71
+ #
72
+ if caption.empty?
73
+ puts a_id if label
74
+ else
75
+ br = ""
76
+ attr = label ? " id=\"#{normalize_id(label)}\"" : ""
77
+ conf = @starter_config
78
+ case level
79
+ when 1 ; attr << " class=\"#{conf['chapter_decoration']} #{conf['chapter_align']} #{conf['chapter_oneline'] ? 'oneline' : 'twolines'}\"" if prefix
80
+ attr << " class=\"none #{conf['chapter_align']}\"" unless prefix
81
+ br = "<br/>" unless conf['chapter_oneline']
82
+ when 2 ; attr << " class=\"#{conf['section_decoration']}\""
83
+ when 3 ; attr << " class=\"#{conf['subsection_decoration']}\""
84
+ end
85
+ puts "<h#{level}#{attr}>#{a_id}#{prefix}#{br}#{compile_inline(caption)}</h#{level}>"
86
+ end
87
+ end
88
+
89
+ def result_metric(array)
90
+ attrs = {}
91
+ array.each do |item|
92
+ k = item.keys[0]
93
+ v = item[k]
94
+ if k == 'border' && v == 'on'
95
+ k = 'class'; v = 'border-on'
96
+ end
97
+ (attrs[k] ||= []) << v
98
+ end
99
+ attrs.map {|k, arr| " #{k}=\"#{arr.join(' ')}\"" }.join()
100
+ end
101
+
102
+ def image_image(id, caption, metric)
103
+ src = @chapter.image(id).path.sub(%r{\A\./}, '')
104
+ alt = escape_html(compile_inline(caption))
105
+ metrics = parse_metric('html', metric)
106
+ metrics = " class=\"img\"" unless metrics.present?
107
+ puts "<div id=\"#{normalize_id(id)}\" class=\"image\">"
108
+ puts "<img src=\"#{src}\" alt=\"#{alt}\"#{metrics} />"
109
+ image_header(id, caption)
110
+ puts "</div>"
111
+ end
112
+
113
+ ## コードブロック(//program, //terminal)
114
+
115
+ def program(lines, id=nil, caption=nil, optionstr=nil)
116
+ _codeblock('program', 'code', lines, id, caption, optionstr)
117
+ end
118
+
119
+ def terminal(lines, id=nil, caption=nil, optionstr=nil)
120
+ _codeblock('terminal', 'cmd-code', lines, id, caption, optionstr)
121
+ end
122
+
123
+ protected
124
+
125
+ def _codeblock(blockname, classname, lines, id, caption, optionstr)
126
+ ## ブロックコマンドのオプション引数はCompilerクラスでパースすべき。
127
+ ## しかしCompilerクラスがそのような設計になってないので、
128
+ ## 仕方ないのでBuilderクラスでパースする。
129
+ opts = _parse_codeblock_optionstr(optionstr, blockname)
130
+ CODEBLOCK_OPTIONS.each {|k, v| opts[k] = v unless opts.key?(k) }
131
+ #
132
+ if opts['eolmark']
133
+ lines = lines.map {|line| "#{detab(line)}<small class=\"startereolmark\"></small>" }
134
+ else
135
+ lines = lines.map {|line| detab(line) }
136
+ end
137
+ #
138
+ indent_w = opts['indentwidth']
139
+ if indent_w && indent_w > 0
140
+ indent_str = " " * (indent_w - 1) + '<span class="indentbar"> </span>'
141
+ lines = lines.map {|line|
142
+ line.sub(/\A( +)/) {
143
+ m, n = ($1.length - 1).divmod indent_w
144
+ " " << indent_str * m << " " * n
145
+ }
146
+ }
147
+ end
148
+ #
149
+ puts "<div id=\"#{normalize_id(id)}\" class=\"#{classname}\">" if id.present?
150
+ puts "<div class=\"#{classname}\">" unless id.present?
151
+ #
152
+ if id.present? || caption.present?
153
+ str = _build_caption_str(id, caption)
154
+ print "<span class=\"caption\">#{str}</span>\n"
155
+ classattr = "list"
156
+ else
157
+ classattr = "emlist"
158
+ end
159
+ #
160
+ lang = opts['lang']
161
+ lang = File.extname(id || "").gsub(".", "") if lang.blank?
162
+ classattr << " language-#{lang}" unless lang.blank?
163
+ classattr << " highlight" if highlight?
164
+ print "<pre class=\"#{classattr}\">"
165
+ #
166
+ gen = opts['lineno'] ? LineNumberGenerator.new(opts['lineno']).each : nil
167
+ if gen
168
+ width = opts['linenowidth']
169
+ if width < 0
170
+ format = "%s"
171
+ elsif width == 0
172
+ last_lineno = gen.each.take(lines.length).compact.last
173
+ width = last_lineno.to_s.length
174
+ format = "%#{width}s"
175
+ else
176
+ format = "%#{width}s"
177
+ end
178
+ end
179
+ buf = []
180
+ start_tag = opts['linenowidth'] >= 0 ? "<em class=\"linenowidth\">" : "<em class=\"lineno\">"
181
+ lines.each_with_index do |line, i|
182
+ buf << start_tag << (format % gen.next) << ": </em>" if gen
183
+ buf << line << "\n"
184
+ end
185
+ puts highlight(body: buf.join(), lexer: lang,
186
+ format: "html", linenum: !!gen,
187
+ #options: {linenostart: start}
188
+ )
189
+ #
190
+ print "</pre>\n"
191
+ print "</div>\n"
192
+ end
193
+
194
+ public
195
+
196
+ ## コードリスト(//list, //emlist, //listnum, //emlistnum, //cmd, //source)
197
+ def list(lines, id=nil, caption=nil, lang=nil)
198
+ _codeblock("list", "caption-code", lines, id, caption, _codeblock_optstr(lang, false))
199
+ end
200
+ def listnum(lines, id=nil, caption=nil, lang=nil)
201
+ _codeblock("listnum", "code", lines, id, caption, _codeblock_optstr(lang, true))
202
+ end
203
+ def emlist(lines, caption=nil, lang=nil)
204
+ _codeblock("emlist", "emlist-code", lines, nil, caption, _codeblock_optstr(lang, false))
205
+ end
206
+ def emlistnum(lines, caption=nil, lang=nil)
207
+ _codeblock("emlistnum", "emlistnum-code", lines, nil, caption, _codeblock_optstr(lang, true))
208
+ end
209
+ def source(lines, caption=nil, lang=nil)
210
+ _codeblock("source", "source-code", lines, nil, caption, _codeblock_optstr(lang, false))
211
+ end
212
+ def cmd(lines, caption=nil, lang=nil)
213
+ lang ||= "shell-session"
214
+ _codeblock("cmd", "cmd-code", lines, nil, caption, _codeblock_optstr(lang, false))
215
+ end
216
+ def _codeblock_optstr(lang, lineno_flag)
217
+ arr = []
218
+ arr << lang if lang
219
+ if lineno_flag
220
+ first_line_num = line_num()
221
+ arr << "lineno=#{first_line_num}"
222
+ arr << "linenowidth=0"
223
+ end
224
+ return arr.join(",")
225
+ end
226
+ private :_codeblock_optstr
227
+
228
+ protected
229
+
230
+ ## @<secref>{}
231
+
232
+ def _build_secref(chap, num, title, parent_title)
233
+ s = ""
234
+ ## 親セクションのタイトルがあれば使う
235
+ if parent_title
236
+ s << "「%s」内の" % parent_title # TODO: I18n化
237
+ end
238
+ ## 対象セクションへのリンクを作成する
239
+ if @book.config['chapterlink']
240
+ filename = "#{chap.id}#{extname()}"
241
+ dom_id = 'h' + num.gsub('.', '-')
242
+ s << "「<a href=\"#{filename}##{dom_id}\">#{title}</a>」"
243
+ else
244
+ s << "「#{title}」"
245
+ end
246
+ return s
247
+ end
248
+
249
+ public
250
+
251
+ ## 順序つきリスト
252
+
253
+ def ol_begin(start_num=nil)
254
+ @_ol_types ||= [] # stack
255
+ case start_num
256
+ when nil
257
+ type = "1"; start = 1
258
+ when /\A(\d+)\.\z/
259
+ type = "1"; start = $1.to_i
260
+ when /\A([A-Z])\.\z/
261
+ type = "A"; start = $1.ord - 'A'.ord + 1
262
+ when /\A([a-z])\.\z/
263
+ type = "a"; start = $1.ord - 'a'.ord + 1
264
+ else
265
+ type = nil; start = nil
266
+ end
267
+ if type
268
+ puts "<ol start=\"#{start}\" type=\"#{type}\">"
269
+ else
270
+ puts "<ol class=\"ol-none\">"
271
+ end
272
+ @_ol_types.push(type)
273
+ end
274
+
275
+ def ol_end()
276
+ ol = !! @_ol_types.pop()
277
+ if ol
278
+ puts "</ol>"
279
+ else
280
+ puts "</ol>"
281
+ end
282
+ end
283
+
284
+ def ol_item_begin(lines, num)
285
+ ol = !! @_ol_types[-1]
286
+ if ol
287
+ print "<li>#{lines.join}"
288
+ else
289
+ n = escape_html(num)
290
+ print "<li data-mark=\"#{n}\"><span class=\"li-mark\">#{n} </span>#{lines.join}"
291
+ end
292
+ end
293
+
294
+ def ol_item_end()
295
+ puts "</li>"
296
+ end
297
+
298
+ ## 入れ子可能なブロック命令
299
+
300
+ def on_minicolumn(type, caption, &b)
301
+ puts "<div class=\"#{type}\">"
302
+ puts "<p class=\"caption\">#{compile_inline(caption)}</p>" if caption.present?
303
+ yield
304
+ puts '</div>'
305
+ end
306
+ protected :on_minicolumn
307
+
308
+ def on_sideimage_block(imagefile, imagewidth, option_str=nil, &b)
309
+ imagefile, imagewidth, opts = validate_sideimage_args(imagefile, imagewidth, option_str)
310
+ filepath = find_image_filepath(imagefile)
311
+ side = (opts['side'] || 'L') == 'L' ? 'left' : 'right'
312
+ imgclass = opts['border'] ? "image-bordered" : nil
313
+ normalize = proc {|s| s =~ /^\A(\d+(\.\d+))%\z/ ? "#{$1.to_f/100.0}\\textwidth" : s }
314
+ imgwidth = normalize.call(imagewidth)
315
+ boxwidth = normalize.call(opts['boxwidth']) || imgwidth
316
+ sepwidth = normalize.call(opts['sep'] || "0pt")
317
+ #
318
+ puts "<div class=\"sideimage\">\n"
319
+ puts " <div class=\"sideimage-image\" style=\"float:#{side};text-align:center;width:#{boxwidth}\">\n"
320
+ puts " <img src=\"#{filepath}\" class=\"#{imgclass}\" style=\"width:#{imgwidth}\"/>\n"
321
+ puts " </div>\n"
322
+ puts " <div class=\"sideimage-text\" style=\"margin-#{side}:#{boxwidth}\">\n"
323
+ puts " <div style=\"padding-#{side}:#{sepwidth}\">\n"
324
+ yield
325
+ puts " </div>\n"
326
+ puts " </div>\n"
327
+ puts "</div>\n"
328
+ end
329
+
330
+ #### ブロック命令
331
+
332
+ def footnote(id, str)
333
+ id_val = "fn-#{normalize_id(id)}"
334
+ href = "#fnb-#{normalize_id(id)}"
335
+ text = compile_inline(str)
336
+ chap_num = @chapter.footnote(id).number
337
+ if @book.config['epubversion'].to_i == 3
338
+ attr = " epub:type=\"footnote\""
339
+ mark = "[*#{chap_num}]"
340
+ else
341
+ attr = ""
342
+ mark = "[<a href=\"#{href}\">*#{chap_num}</a>]"
343
+ end
344
+ #
345
+ if truncate_if_endwith?("</div><!--/.footnote-list-->\n")
346
+ else
347
+ puts "<div class=\"footnote-list\">"
348
+ end
349
+ print "<div class=\"footnote\" id=\"#{id_val}\"#{attr}>"
350
+ print "<p class=\"footnote\">"
351
+ print "<span class=\"footnote-mark\">#{mark} </span>"
352
+ print text
353
+ print "</p>"
354
+ puts "</div>"
355
+ puts "</div><!--/.footnote-list-->\n"
356
+ end
357
+
358
+ alias __original_texequation texequation
359
+ def texequation(lines, label=nil, caption=nil)
360
+ if label.present?
361
+ chap = get_chap()
362
+ if chap.nil?
363
+ key = "format_number_header_without_chapter"
364
+ args = [@chapter.equation(label).number]
365
+ else
366
+ key = "format_number_header"
367
+ args = [chap, @chapter.equation(label).number]
368
+ end
369
+ s1 = I18n.t("equation")
370
+ s2 = I18n.t(key, args)
371
+ s3 = I18n.t("caption_prefix")
372
+ s4 = compile_inline(caption)
373
+ puts "<span class=\"caption\">#{s1}#{s2}#{s3}#{s4}</span>"
374
+ has_caption_line = true
375
+ elsif caption.present?
376
+ s3 = I18n.t("caption_prefix")
377
+ s4 = compile_inline(caption)
378
+ puts "<span class=\"caption\">#{s3}#{s4}</span>"
379
+ has_caption_line = true
380
+ else
381
+ has_caption_line = false
382
+ end
383
+ #
384
+ __original_texequation(lines)
385
+ end
386
+
387
+ ## 章 (Chapter) の概要
388
+ def abstract(lines)
389
+ puts '<div class="abstract">'
390
+ puts lines
391
+ puts '</div>'
392
+ end
393
+
394
+ ## 章タイトルを独立したページに
395
+ def makechaptitlepage(option=nil)
396
+ puts '' # HTMLでは特に何もしない
397
+ end
398
+
399
+ ## 縦方向のスペースがなければ改ページ
400
+ def needvspace(builder_name, height)
401
+ if builder_name == 'html' || builder_name == 'epub'
402
+ puts "<div style=\"height:#{height}\"></div>"
403
+ end
404
+ end
405
+
406
+ ## 段(Paragraph)の終わりにスペースを入れる
407
+ def paragraphend()
408
+ puts '' # HTMLでは特に何もしない
409
+ end
410
+
411
+ ## 小段(Subparagraph)の終わりにスペースを入れる(あれば)
412
+ def subparagraphend()
413
+ puts '' # HTMLでは特に何もしない
414
+ end
415
+
416
+ ## 引用(複数段落に対応)
417
+ def blockquote(lines)
418
+ puts '<blockquote class="blockquote">'
419
+ puts lines
420
+ puts '</blockquote>'
421
+ end
422
+
423
+ ## 引用(//quote{ ... //})
424
+ ## (入れ子対応なので、中に箇条書きや別のブロックを入れられる)
425
+ def on_quote_block()
426
+ puts '<blockquote class="blockquote">'
427
+ yield
428
+ puts '</blockquote>'
429
+ end
430
+ def quote(lines)
431
+ on_quote_block() do
432
+ puts lines
433
+ end
434
+ end
435
+
436
+ ## 引用 (====[quote] ... ====[/quote])
437
+ ## (ブロック構文ではないので、中に別のブロックや箇条書きを入れられる)
438
+ def quote_begin(level, label, caption)
439
+ puts '<blockquote class="blockquote">'
440
+ end
441
+ def quote_end(level)
442
+ puts '</blockquote>'
443
+ end
444
+
445
+ ## ノート(//note{ ... //})
446
+ ## (入れ子対応なので、中に箇条書きや別のブロックを入れられる)
447
+ def on_note_block(label=nil, caption=nil)
448
+ caption, label = label, nil if caption.nil?
449
+ if label
450
+ puts "<div class=\"note\" id=\"#{label}\">"
451
+ else
452
+ puts "<div class=\"note\">"
453
+ end
454
+ puts "<h5>#{compile_inline(caption)}</h5>" if caption.present?
455
+ yield
456
+ puts "</div>"
457
+ end
458
+ def note(lines, label=nil, caption=nil)
459
+ on_quote_block(label, caption) do
460
+ puts lines
461
+ end
462
+ end
463
+
464
+ ## ノート (====[note] ... ====[/note])
465
+ ## (ブロック構文ではないので、中に別のブロックや箇条書きを入れられる)
466
+ def note_begin(level, label, caption)
467
+ s = compile_inline(caption || "")
468
+ puts "<div class=\"note\">"
469
+ puts "<h5>#{s}</h5>" if s.present?
470
+ end
471
+ def note_end(level)
472
+ puts "</div>"
473
+ end
474
+
475
+ #### インライン命令
476
+
477
+ def inline_fn(id)
478
+ if @book.config['epubversion'].to_i == 3
479
+ type = " epub:type=\"noteref\""
480
+ else
481
+ type = ""
482
+ end
483
+ return "<sup><a id=\"fnb-#{normalize_id(id)}\" href=\"#fn-#{normalize_id(id)}\" class=\"noteref\"#{type}>*#{@chapter.footnote(id).number}</a></sup>"
484
+ rescue KeyError
485
+ error "unknown footnote: #{id}"
486
+ end
487
+
488
+ ## ファイル名
489
+ def inline_file(str)
490
+ on_inline_file { escape(str) }
491
+ end
492
+ def on_inline_file
493
+ "<span class=\"file\">#{yield}</span>"
494
+ end
495
+
496
+ ## ユーザ入力
497
+ def inline_userinput(str)
498
+ on_inline_input { escape(str) }
499
+ end
500
+ def on_inline_userinput
501
+ "<span class=\"userinput\">#{yield}</span>"
502
+ end
503
+
504
+ ## 引数をそのまま表示 (No Operation)
505
+ def inline_nop(str)
506
+ escape_html(str || "")
507
+ end
508
+ alias inline_letitgo inline_nop
509
+
510
+ ## 目立たせない(@<strong>{} の反対)
511
+ def inline_weak(str)
512
+ on_inline_weak { escape(str) }
513
+ end
514
+ def on_inline_weak
515
+ "<span class=\"weak\">#{yield}</span>"
516
+ end
517
+
518
+ ## 文字を小さくする
519
+ def inline_small(str) ; on_inline_small { escape(str) } ; end
520
+ def inline_xsmall(str) ; on_inline_xsmall { escape(str) } ; end
521
+ def inline_xxsmall(str) ; on_inline_xxsmall { escape(str) } ; end
522
+
523
+ def on_inline_small ; "<small style=\"font-size:small\">#{yield}</small>" ; end
524
+ def on_inline_xsmall ; "<small style=\"font-size:x-small\">#{yield}</small>" ; end
525
+ def on_inline_xxsmall ; "<small style=\"font-size:xx-small\">#{yield}</small>"; end
526
+
527
+ ## 文字を大きくする
528
+ def inline_large(str) ; on_inline_large { escape(str) } ; end
529
+ def inline_xlarge(str) ; on_inline_xlarge { escape(str) } ; end
530
+ def inline_xxlarge(str) ; on_inline_xxlarge { escape(str) } ; end
531
+ def on_inline_large ; "<span style=\"font-size:large\">#{yield}</span>" ; end
532
+ def on_inline_xlarge ; "<span style=\"font-size:x-large\">#{yield}</span>" ; end
533
+ def on_inline_xxlarge ; "<span style=\"font-size:xx-large\">#{yield}</span>"; end
534
+
535
+ ## 文字を大きくした@<strong>{}
536
+ def inline_xstrong(str) ; on_inline_xstrong { escape(str) }; end
537
+ def inline_xxstrong(str); on_inline_xxstrong { escape(str) }; end
538
+ def on_inline_xstrong ; "<span style=\"font-size:x-large\">#{yield}</span>"; end
539
+ def on_inline_xxstrong; "<span style=\"font-size:xx-large\">#{yield}</span>"; end
540
+
541
+ ## コードブロック中で折り返し箇所を手動で指定する
542
+ def inline_foldhere(arg)
543
+ '<br>'
544
+ end
545
+
546
+ ## ターミナルでのカーソル(背景が白、文字が黒)
547
+ def inline_cursor(str)
548
+ "<span class=\"cursor\">#{escape_html(str)}</span>"
549
+ end
550
+
551
+ ## nestable inline commands
552
+
553
+ def on_inline_i() ; "<i>#{yield}</i>" ; end
554
+ def on_inline_b() ; "<b>#{yield}</b>" ; end
555
+ def on_inline_code() ; "<code>#{yield}</code>" ; end
556
+ def on_inline_tt() ; "<tt>#{yield}</tt>" ; end
557
+ def on_inline_del() ; "<s>#{yield}</s>" ; end
558
+ def on_inline_sub() ; "<sub>#{yield}</sub>" ; end
559
+ def on_inline_sup() ; "<sup>#{yield}</sup>" ; end
560
+ def on_inline_em() ; "<em>#{yield}</em>" ; end
561
+ def on_inline_strong(); "<strong>#{yield}</strong>" ; end
562
+ def on_inline_u() ; "<u>#{yield}</u>" ; end
563
+ def on_inline_ami() ; "<span class=\"ami\">#{yield}</span>"; end
564
+ def on_inline_balloon(); "<span class=\"balloon\">← #{yield}</span>"; end
565
+
566
+ def build_inline_href(url, escaped_label) # compile_href()をベースに改造
567
+ flag_link = @book.config['externallink']
568
+ return _inline_hyperlink(url, escaped_label, flag_link)
569
+ end
570
+
571
+ def inline_hlink(str)
572
+ url, label = str.split(/, /, 2)
573
+ flag_link = @book.config['externallink']
574
+ return _inline_hyperlink(url, escape(label), flag_link)
575
+ end
576
+
577
+ def _inline_hyperlink(url, escaped_label, flag_link)
578
+ if flag_link
579
+ label = escaped_label || escape_html(url)
580
+ "<a href=\"#{escape_html(url)}\" class=\"link\">#{label}</a>"
581
+ elsif escaped_label
582
+ I18n.t('external_link', [escaped_label, escape_html(url)])
583
+ else
584
+ escape_html(url)
585
+ end
586
+ end
587
+ private :_inline_hyperlink
588
+
589
+ def build_inline_ruby(escaped_word, escaped_yomi) # compile_ruby()をベースに改造
590
+ pre = I18n.t('ruby_prefix'); post = I18n.t('ruby_postfix')
591
+ if @book.htmlversion == 5
592
+ "<ruby>#{escaped_word}<rp>#{pre}</rp><rt>#{escaped_yomi}</rt><rp>#{post}</rp></ruby>"
593
+ else
594
+ "<ruby><rb>#{escaped_word}</rb><rp>#{pre}</rp><rt>#{escaped_yomi}</rt><rp>#{post}</rp></ruby>"
595
+ end
596
+ end
597
+
598
+ protected
599
+
600
+ ## ノートを参照する
601
+ def build_noteref(chapter, label, caption)
602
+ href = chapter ? "#{chapter.id}#{extname()}##{label}" : "##{label}"
603
+ %Q`ノート「<a href="#{href}">#{escape(caption)}</a>」`
604
+ end
605
+
606
+ ## 数式を参照する
607
+ def build_eq(chapter, label, number)
608
+ s = "#{I18n.t('equation')}#{chapter.number}.#{number}"
609
+ "<a>#{escape(s)}</a>"
610
+ end
611
+
612
+ end
613
+
614
+
615
+ class TEXTBuilder
616
+
617
+ ## nestable inline commands
618
+
619
+ def on_inline_i() ; "<i>#{yield}</i>" ; end
620
+ def on_inline_b() ; "<b>#{yield}</b>" ; end
621
+ def on_inline_code() ; "<code>#{yield}</code>" ; end
622
+ def on_inline_tt() ; "<tt>#{yield}</tt>" ; end
623
+ def on_inline_del() ; "<s>#{yield}</s>" ; end
624
+ def on_inline_sub() ; "<sub>#{yield}</sub>" ; end
625
+ def on_inline_sup() ; "<sup>#{yield}</sup>" ; end
626
+ def on_inline_em() ; "<em>#{yield}</em>" ; end
627
+ def on_inline_strong(); "<strong>#{yield}</strong>" ; end
628
+ def on_inline_u() ; "<u>#{yield}</u>" ; end
629
+ def on_inline_ami() ; "<span class=\"ami\">#{yield}</span>"; end
630
+
631
+ def build_inline_href(url, escaped_label) # compile_href()をベースに改造
632
+ if @book.config['externallink']
633
+ label = escaped_label || escape_html(url)
634
+ "<a href=\"#{escape_html(url)}\" class=\"link\">#{label}</a>"
635
+ elsif escaped_label
636
+ I18n.t('external_link', [escaped_label, escape_html(url)])
637
+ else
638
+ escape_html(url)
639
+ end
640
+ end
641
+
642
+ def build_inline_ruby(escaped_word, escaped_yomi) # compile_ruby()をベースに改造
643
+ pre = I18n.t('ruby_prefix'); post = I18n.t('ruby_postfix')
644
+ if @book.htmlversion == 5
645
+ "<ruby>#{escaped_word}<rp>#{pre}</rp><rt>#{escaped_yomi}</rt><rp>#{post}</rp></ruby>"
646
+ else
647
+ "<ruby><rb>#{escaped_word}</rb><rp>#{pre}</rp><rt>#{escaped_yomi}</rt><rp>#{post}</rp></ruby>"
648
+ end
649
+ end
650
+
651
+ ## その他
652
+
653
+ def inline_nop(str)
654
+ str || ''
655
+ end
656
+ alias inline_letitgo inline_nop
657
+
658
+ end
659
+
660
+
661
+ end