review-retrovert 0.9.7 → 0.9.8

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -5,6 +5,7 @@
5
5
  ##
6
6
 
7
7
  require 'pathname'
8
+ require 'open3'
8
9
 
9
10
  require 'review/logger'
10
11
  require 'review/version'
@@ -34,9 +35,10 @@ module ReVIEW
34
35
  @config.maker = @maker_name
35
36
  @config.check_version(ReVIEW::VERSION) # may raise ReVIEW::ConfigError
36
37
  #
37
- yamlfile = File.join(@basedir, 'config-starter.yml')
38
- yamldata = File.open(yamlfile) {|f| YAML.safe_load(f) }
39
- @starter_config = yamldata['starter']
38
+ #yamlfile = File.join(@basedir, 'config-starter.yml')
39
+ #yamldata = File.open(yamlfile) {|f| YAML.safe_load(f) }
40
+ #@starter_config = yamldata['starter']
41
+ @starter_config = @config['starter']
40
42
  end
41
43
 
42
44
  def self.execute(*args)
@@ -57,7 +59,7 @@ module ReVIEW
57
59
  maker = nil
58
60
  begin
59
61
  maker = self.new(config_filename, cmdopts)
60
- maker.generate()
62
+ return maker.generate()
61
63
  rescue ApplicationError, ReVIEW::ConfigError => ex
62
64
  raise if maker && maker.config['debug']
63
65
  error(ex.message)
@@ -87,6 +89,8 @@ module ReVIEW
87
89
  config.deep_merge!(additionals)
88
90
  I18n.setup(config['language'])
89
91
  #
92
+ validate_config(config)
93
+ #
90
94
  if ENV['STARTER_CHAPTER'].present?
91
95
  modify_config(config)
92
96
  end
@@ -116,6 +120,76 @@ module ReVIEW
116
120
  return book
117
121
  end
118
122
 
123
+ def validate_config(config)
124
+ _validate_obsoletes(config)
125
+ _validate_pdffiles(config)
126
+ end
127
+
128
+ private
129
+
130
+ def _validate_obsoletes(config)
131
+ d = config['pdfmaker']
132
+ return unless d
133
+ obsolete_keys = ['coverpdf_files', 'backcoverpdf_files', 'coverpdf_option']
134
+ if obsolete_keys.any? {|key| d.key?(key) }
135
+ errmsg = CONFIG_PDFFILES_ERRMSG
136
+ $stderr.puts "\033[0;31m\n#{errmsg}\033[0m"
137
+ error "obsolete config entry exists."
138
+ end
139
+ end
140
+
141
+ CONFIG_PDFFILES_ERRMSG = <<END
142
+ ***** ERROR *****
143
+ 'config.yml' contains old entries.
144
+
145
+ Please move the followings from 'config.yml' ...
146
+
147
+ * coverpdf_files:
148
+ * backcoverpdf_files:
149
+ * coverpdf_option:
150
+
151
+ into 'config-starter.yml' with renaming to ...
152
+
153
+ * frontcover_pdffile:
154
+ * backcover_pdffile:
155
+ * includepdf_option:
156
+ END
157
+
158
+ def _validate_pdffiles(config)
159
+ d = config['starter']
160
+ return unless d
161
+ imagedir = config['imagedir']
162
+ pdffile_keys = ['frontcover_pdffile', 'backcover_pdffile', 'titlepage_pdffile', 'colophon_pdffile']
163
+ pdffile_keys.each do |key|
164
+ [d[key]].flatten.compact.each do |pdffile|
165
+ pdffile = pdffile.gsub(/(<.*?>)?\*?\z/, '')
166
+ errmsg = _validate_pdffile(File.join(imagedir, pdffile))
167
+ error "(config-starter.yml) '#{key}: #{pdffile}': #{errmsg}" if errmsg
168
+ end
169
+ end
170
+ #
171
+ key = 'includepdf_option'; val = d[key]
172
+ if val.present?
173
+ val =~ /\Aoffset=/ or
174
+ error "(config-starter.yml) '#{key}: #{val}': unexpected value. only 'offset=...' available here."
175
+ end
176
+ end
177
+
178
+ def _validate_pdffile(filepath)
179
+ filepath =~ /\.pdf\z/i or
180
+ return "expected pdf file ('*.pdf'), but not."
181
+ return _validate_file(filepath)
182
+ end
183
+
184
+ def _validate_file(filepath)
185
+ File.exist?(filepath) or return "file not found."
186
+ File.file?(filepath) or return "not a file."
187
+ File.readable?(filepath) or return "cannot read (permission denied?)."
188
+ return nil
189
+ end
190
+
191
+ protected
192
+
119
193
  def system_or_raise(*args)
120
194
  Kernel.system(*args) or raise("failed to run command: #{args.join(' ')}")
121
195
  end
@@ -145,16 +219,48 @@ module ReVIEW
145
219
  path
146
220
  end
147
221
 
148
- def run_cmd(cmd)
222
+ def echoback(cmd)
149
223
  puts ""
150
224
  puts "[#{@maker_name}]$ #{cmd}"
151
- return Kernel.system(cmd)
225
+ end
226
+
227
+ def run_cmd(cmd)
228
+ time = @_usr_bin_time ? "time " : ""
229
+ echoback("#{time}#{cmd}")
230
+ return Kernel::system(cmd)
152
231
  end
153
232
 
154
233
  def run_cmd!(cmd)
155
234
  run_cmd(cmd) or raise("failed to run command: #{cmd}")
156
235
  end
157
236
 
237
+ def run_cmd_capturing_output(cmd, append=nil)
238
+ time = @_usr_bin_time ? "time " : ""
239
+ echoback("#{time}#{cmd}#{append}")
240
+ output, status = Open3.capture2(cmd)
241
+ return output, status.success?
242
+ end
243
+
244
+ def usr_bin_time(&b)
245
+ result = nil
246
+ @_usr_bin_time = true
247
+ start_t = Time.now
248
+ ptime1 = Process.times
249
+ result = yield
250
+ end_t = Time.now
251
+ ptime2 = Process.times
252
+ @_usr_bin_time = false
253
+ #
254
+ real_time = end_t - start_t
255
+ user_time = ptime2.cutime - ptime1.cutime
256
+ sys_time = ptime2.cstime - ptime1.cstime
257
+ format = "%12.2f real %12.2f user %12.2f sys"
258
+ $stderr.puts format % [real_time, user_time, sys_time]
259
+ #
260
+ return result
261
+ end
262
+ protected :usr_bin_time
263
+
158
264
  def call_hook(hookname)
159
265
  d = @config[@maker_name]
160
266
  return unless d.is_a?(Hash)
@@ -183,6 +289,11 @@ module ReVIEW
183
289
  return File.join(c['bindir'], c['ruby_install_name']) + c['EXEEXT'].to_s
184
290
  end
185
291
 
292
+ def colored_errmsg(msg)
293
+ #return msg unless $stderr.tty?
294
+ return "\033[0;31m#{msg}\033[0m"
295
+ end
296
+
186
297
  end
187
298
 
188
299
 
@@ -0,0 +1,945 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ ##
4
+ ## Markdownへ変換する
5
+ ##
6
+
7
+ require_relative './review-builder'
8
+
9
+
10
+ module ReVIEW
11
+
12
+
13
+ class MarkdownBuilder < Builder
14
+
15
+ def target_name
16
+ "markdown"
17
+ end
18
+
19
+ def extname
20
+ ".md"
21
+ end
22
+
23
+ def headline(level, label, caption)
24
+ blank2() unless @output.pos == 0
25
+ print "#{'#' * level} "
26
+ print compile_inline(caption||'')
27
+ #print " {##{label}}" if label
28
+ puts ""
29
+ blank()
30
+ end
31
+
32
+ def notoc_begin(level, label, caption)
33
+ blank2() unless @output.pos == 0
34
+ print "#{'#' * level} "
35
+ print compile_inline(caption||'')
36
+ #print " {##{label}}" if label
37
+ puts ""
38
+ blank()
39
+ end
40
+
41
+ def notoc_end(level)
42
+ end
43
+
44
+ def column_begin(level, label, caption)
45
+ blank2()
46
+ puts "[column] #{compile_inline(caption||'')}"
47
+ end
48
+
49
+ def column_end(_level)
50
+ truncate_blank()
51
+ puts "[/column]"
52
+ blank2()
53
+ end
54
+
55
+ def blank()
56
+ puts "" unless @output.string.end_with?("\n\n")
57
+ end
58
+
59
+ def blank2()
60
+ if @output.string.end_with?("\n\n\n")
61
+ elsif @output.string.end_with?("\n\n")
62
+ puts ""
63
+ else
64
+ puts ""
65
+ puts ""
66
+ end
67
+ end
68
+
69
+ def twospaces()
70
+ if truncate_if_endwith?("\n")
71
+ puts " "
72
+ return true
73
+ else
74
+ false
75
+ end
76
+ end
77
+
78
+ def nofunc_text(str)
79
+ if within_codeblock?() || within_context?(:code)
80
+ str
81
+ else
82
+ escape(str)
83
+ end
84
+ end
85
+
86
+ def escape(str)
87
+ str.to_s.gsub(/[&<>"']/, HTML_ESCAPE_TABLE)
88
+ end
89
+
90
+ HTML_ESCAPE_TABLE = {
91
+ '&' => '&amp;',
92
+ '<' => '&lt;',
93
+ '>' => '&gt;',
94
+ '"' => '&quot;',
95
+ "'" => '&#039;',
96
+ }
97
+
98
+ ## 画像
99
+
100
+ def indepimage(lines, id, caption='', metric=nil)
101
+ image = @chapter.image(id)
102
+ image.bound? or
103
+ error "//image[#{id}]: image not found."
104
+ image_filepath = image.path
105
+ blank()
106
+ puts "![#{compile_inline(caption||'')}](#{image_filepath})"
107
+ if caption.present?
108
+ puts "<span class=\"caption\">▲#{compile_inline(caption)}</span>"
109
+ end
110
+ blank()
111
+ end
112
+
113
+ def imgtable_image(id, caption, metric)
114
+ metrics = parse_metric('html', metric)
115
+ puts "<img src=\"#{@chapter.image(id).path.sub(%r{\A\./}, '')}\"#{metrics} />"
116
+ end
117
+
118
+ protected
119
+
120
+ def _render_image(id, image_filepath, caption, opts)
121
+ puts "![#{compile_inline(caption||'')}](#{image_filepath})"
122
+ end
123
+
124
+ ## コードブロック(//program, //terminal, //output)
125
+
126
+ def _render_codeblock(blockname, lines, id, caption_str, opts)
127
+ blank()
128
+ if caption_str.present?
129
+ puts "<span class=\"caption\">▼#{compile_inline(caption_str)}</span>"
130
+ puts ""
131
+ end
132
+ lang = opts['lang']
133
+ lang ||= 'terminal' if blockname == 'terminal' || blockname == 'cmd'
134
+ puts "```#{lang}"
135
+ lines.each do |line|
136
+ puts compile_inline(line)
137
+ end
138
+ puts "```"
139
+ blank()
140
+ end
141
+
142
+ def _add_indent_mark(lines, indent_width)
143
+ space = " "
144
+ rexp = /\A( +)/
145
+ #
146
+ width = indent_width
147
+ mark = _codeblock_indentmark() # ex: '{\starterindentmark}'
148
+ indent = space * (width - 1) + mark
149
+ nchar = space.length
150
+ return lines.map {|line|
151
+ line.sub(rexp) {
152
+ m, n = ($1.length / nchar - 1).divmod width
153
+ "#{space}#{indent * m}#{space * n}"
154
+ }
155
+ }
156
+ end
157
+
158
+ public
159
+
160
+ def list(lines, id=nil, caption=nil, lang=nil)
161
+ _codeblock("list", lines, id, caption, _codeblock_optstr(lang, false))
162
+ end
163
+ def listnum(lines, id=nil, caption=nil, lang=nil)
164
+ _codeblock("listnum", lines, id, caption, _codeblock_optstr(lang, true))
165
+ end
166
+ def emlist(lines, caption=nil, lang=nil)
167
+ _codeblock("emlist", lines, nil, caption, _codeblock_optstr(lang, false))
168
+ end
169
+ def emlistnum(lines, caption=nil, lang=nil)
170
+ _codeblock("emlistnum", lines, nil, caption, _codeblock_optstr(lang, true))
171
+ end
172
+ def source(lines, caption=nil, lang=nil)
173
+ _codeblock("source", lines, nil, caption, _codeblock_optstr(lang, false))
174
+ end
175
+ def cmd(lines, caption=nil, lang=nil)
176
+ lang ||= "shell-session"
177
+ _codeblock("cmd", lines, nil, caption, _codeblock_optstr(lang, false))
178
+ end
179
+ def _codeblock_optstr(lang, lineno_flag)
180
+ arr = []
181
+ arr << lang if lang
182
+ if lineno_flag
183
+ first_line_num = line_num()
184
+ arr << "lineno=#{first_line_num}"
185
+ arr << "linenowidth=0"
186
+ end
187
+ return arr.join(",")
188
+ end
189
+ private :_codeblock_optstr
190
+
191
+ protected
192
+
193
+ ## @<secref>{}
194
+
195
+ def _build_secref(chap, num, title, parent_title)
196
+ s = ""
197
+ ## 親セクションのタイトルがあれば使う
198
+ if parent_title
199
+ s << "「%s」内の" % parent_title # TODO: I18n化
200
+ end
201
+ ## 対象セクションへのリンクを作成する
202
+ if @book.config['chapterlink']
203
+ filename = "#{chap.id}#{extname()}"
204
+ s << "[「#{title}」](#{filename})"
205
+ else
206
+ s << "「#{title}」"
207
+ end
208
+ return s
209
+ end
210
+
211
+ public
212
+
213
+ def ul_begin()
214
+ n = _enter_ul()
215
+ blank() if n == 1
216
+ end
217
+
218
+ def ul_end()
219
+ n = _exit_ul()
220
+ blank() if n == 1
221
+ end
222
+
223
+ def ul_item(lines)
224
+ n = _depth_ul()
225
+ sp = nil
226
+ lines.each do |line|
227
+ if !sp
228
+ puts "#{' ' * (n-1)}* #{line.strip}"
229
+ sp = ' ' * n
230
+ else
231
+ puts "#{sp}#{line.strip}"
232
+ end
233
+ end
234
+ end
235
+
236
+ def _enter_ul
237
+ @_ul_depth ||= 0
238
+ @_ul_depth += 1
239
+ return @_ul_depth
240
+ end
241
+
242
+ def _exit_ul
243
+ n = @_ul_depth
244
+ @_ul_depth -= 1
245
+ return n
246
+ end
247
+
248
+ def _depth_ul
249
+ return @_ul_depth
250
+ end
251
+
252
+ ## 順序つきリスト
253
+
254
+ def ol_begin(start_num=nil)
255
+ @_ol_types ||= [] # stack
256
+ case start_num
257
+ when nil
258
+ type = "1"; start = 1
259
+ when /\A(\d+)\.\z/
260
+ type = "1"; start = $1.to_i
261
+ when /\A([A-Z])\.\z/
262
+ type = "A"; start = $1.ord - 'A'.ord + 1
263
+ when /\A([a-z])\.\z/
264
+ type = "a"; start = $1.ord - 'a'.ord + 1
265
+ else
266
+ type = nil; start = nil
267
+ end
268
+ @_ol_types.push(type)
269
+ blank() if @_ol_types.length == 1
270
+ end
271
+
272
+ def ol_end()
273
+ blank() if @_ol_types.length == 1
274
+ ol = !! @_ol_types.pop()
275
+ _ = ol
276
+ end
277
+
278
+ def ol_item(lines, num)
279
+ n = @_ol_types.length
280
+ sp = nil
281
+ lines.each do |line|
282
+ if !sp
283
+ #s = num.length == 1 ? '. ' : '. '
284
+ #puts "#{num}#{s}#{line.strip}"
285
+ #sp = ' ' * n
286
+ puts "#{num}. #{line.strip}"
287
+ sp = ' ' * "#{num}. ".length
288
+ else
289
+ puts "#{sp}#{line.strip}"
290
+ end
291
+ end
292
+ end
293
+
294
+ def ol_item_begin(lines, num)
295
+ n = @_ol_types.length
296
+ sp = nil
297
+ lines.each do |line|
298
+ if !sp
299
+ puts "#{' ' * (n-1)}- #{num} #{line.strip}"
300
+ sp = ' ' * n
301
+ else
302
+ puts "#{sp}#{line.strip}"
303
+ end
304
+ end
305
+ end
306
+
307
+ def ol_item_end()
308
+ end
309
+
310
+ def dl_begin()
311
+ blank()
312
+ puts "<dl>"
313
+ end
314
+
315
+ def dl_end()
316
+ puts "</dl>"
317
+ blank()
318
+ end
319
+
320
+ def dt(word)
321
+ #puts word
322
+ #puts "#{word}::"
323
+ puts "<dt>#{word}</dt>"
324
+ end
325
+
326
+ def dl_dd_begin()
327
+ puts "<dd>"
328
+ end
329
+
330
+ def dl_dd_end()
331
+ puts "</dd>"
332
+ end
333
+
334
+ ## 画像横に文章
335
+ def _render_sideimage(filepath, imagewidth, opts, &b)
336
+ blank()
337
+ puts "<img src=\"#{filepath}\">"
338
+ blank()
339
+ yield
340
+ end
341
+
342
+ ## 入れ子可能なブロック命令
343
+
344
+ def on_minicolumn(type, caption=nil, &b)
345
+ ## ・タイトルと1つ目の文の間に空行がないと、
346
+ ## textlintにタイトルも含めた1つの文だと認識されてしまい、
347
+ ## 「一文が長すぎる」というエラーが頻発する。
348
+ ## ・タイトルには末尾に「。」がないので、そのままだと
349
+ ## 「文末に「。」がない」というエラーが頻発する。
350
+ ## ・タイトルを <b></b> で囲むとこのエラーだけが出なくなる。
351
+ ## (他のエラーは出るまま。)
352
+ with_context(:minicolumn) do
353
+ blank2()
354
+ puts "[#{type}] <b>#{compile_inline(caption||'')}</a>"
355
+ blank()
356
+ yield
357
+ blank()
358
+ puts "[/#{type}]"
359
+ blank2()
360
+ end
361
+ end
362
+ protected :on_minicolumn
363
+
364
+ def paragraph(lines)
365
+ lines.each do |line|
366
+ puts line
367
+ end
368
+ blank()
369
+ end
370
+
371
+ #### ブロック命令
372
+
373
+ def footnote(id, str)
374
+ @_fn_count ||= 0
375
+ @_fn_labels ||= {}
376
+ n = @_fn_labels[id]
377
+ blank() # necessary?
378
+ puts "[^#{n || 9999}]: #{compile_inline(str||'')}"
379
+ blank() # necessary?
380
+ end
381
+
382
+ def texequation(lines, label=nil, caption=nil)
383
+ blank()
384
+ puts "```math"
385
+ puts lines
386
+ puts "```"
387
+ blank()
388
+ end
389
+
390
+ ## 章 (Chapter) の概要
391
+ def on_abstract_block()
392
+ blank()
393
+ puts "[abstract]"; twospaces()
394
+ yield
395
+ truncate_blank(); twospaces()
396
+ puts "[/abstract]"
397
+ blank()
398
+ end
399
+
400
+ ## 章 (Chapter) の著者
401
+ def on_chapterauthor_block(name)
402
+ puts "(著:#{name})"
403
+ blank()
404
+ end
405
+
406
+ ## 会話リスト
407
+ def _render_talklist(opts, &b)
408
+ blank()
409
+ yield
410
+ blank()
411
+ end
412
+
413
+ ## 会話項目
414
+ def on_talk_block(imagefile, name=nil, text=nil, &b)
415
+ if imagefile.present?
416
+ find_image_filepath(imagefile) or
417
+ error "//talk[#{imagefile}]: image file not found."
418
+ print "* [#{imagefile}] "
419
+ elsif name
420
+ print "* [#{name}] "
421
+ else
422
+ print "* "
423
+ end
424
+ if text.nil?
425
+ yield
426
+ else
427
+ puts compile_inline(text||'')
428
+ end
429
+ end
430
+
431
+ ## キーと説明文のリスト
432
+ def _render_desclist(opts, &b)
433
+ blank()
434
+ bkup = @_desclist_opts
435
+ @_desclist_opts = opts
436
+ yield
437
+ @_desclist_opts = bkup
438
+ blank()
439
+ end
440
+
441
+ ## キーと説明文
442
+ def _render_desc(key, &b)
443
+ opts = @_desclist_opts
444
+ puts "#{compile_inline(key)}:: "
445
+ yield
446
+ end
447
+
448
+ ## 縦方向の空きを入れる
449
+ def _render_vspace(size)
450
+ blank()
451
+ end
452
+
453
+ ## 章タイトルを独立したページに
454
+ def makechaptitlepage(option=nil)
455
+ blank()
456
+ puts ''
457
+ end
458
+
459
+ ## 縦方向のスペースがなければ改ページ
460
+ def _render_needvspace(height)
461
+ blank()
462
+ puts ''
463
+ end
464
+
465
+ ## 段(Paragraph)の終わりにスペースを入れる
466
+ def paragraphend()
467
+ blank()
468
+ end
469
+
470
+ ## 小段(Subparagraph)の終わりにスペースを入れる(あれば)
471
+ def subparagraphend()
472
+ blank()
473
+ end
474
+
475
+ ## 引用(複数段落に対応)
476
+ def blockquote(lines)
477
+ blank()
478
+ puts "[quote]"; twospaces()
479
+ puts lines
480
+ truncate_blank(); twospaces()
481
+ puts "[/quote]"
482
+ blank()
483
+ end
484
+
485
+ ## 引用(//quote{ ... //})
486
+ ## (入れ子対応なので、中に箇条書きや別のブロックを入れられる)
487
+ def on_quote_block()
488
+ blank()
489
+ puts "[quote]"; twospaces()
490
+ yield
491
+ truncate_blank(); twospaces()
492
+ puts "[/quote]"
493
+ blank()
494
+ end
495
+ def quote(lines)
496
+ on_quote_block() do
497
+ puts lines
498
+ end
499
+ end
500
+
501
+ ## 引用 (====[quote] ... ====[/quote])
502
+ ## (ブロック構文ではないので、中に別のブロックや箇条書きを入れられる)
503
+ def quote_begin(level, label, caption)
504
+ blank()
505
+ end
506
+ def quote_end(level)
507
+ blank()
508
+ end
509
+
510
+ ## ノート(//note{ ... //})
511
+ ## (入れ子対応なので、中に箇条書きや別のブロックを入れられる)
512
+ def on_note_block(label=nil, caption=nil)
513
+ ## ・ノートのタイトルと1つ目の文の間に空行がないと、
514
+ ## textlintにタイトルも含めた1つの文だと認識されてしまい、
515
+ ## 「一文が長すぎる」というエラーが頻発する。
516
+ ## ・ノートのタイトルには末尾に「。」がないので、そのままだと
517
+ ## 「文末に「。」がない」というエラーが頻発する。
518
+ ## ・タイトルを <b></b> で囲むとこのエラーだけが出なくなる。
519
+ ## (他のエラーは出るまま。)
520
+ with_context(:minicolumn) do
521
+ blank2()
522
+ caption, label = label, nil if caption.nil?
523
+ puts "[note] <b>#{compile_inline(caption||'')}</a>"
524
+ blank()
525
+ yield
526
+ blank()
527
+ puts "[/note]"
528
+ blank2()
529
+ end
530
+ end
531
+ def note(lines, label=nil, caption=nil)
532
+ on_quote_block(label, caption) do
533
+ puts lines
534
+ end
535
+ end
536
+
537
+ ## ノート (====[note] ... ====[/note])
538
+ ## (ブロック構文ではないので、中に別のブロックや箇条書きを入れられる)
539
+ def note_begin(level, label, caption)
540
+ blank()
541
+ puts "[note] #{compile_inline(caption||'')}"; twospaces()
542
+ end
543
+ def note_end(level)
544
+ truncate_blank(); twospaces()
545
+ puts "[/note]"
546
+ blank()
547
+ end
548
+
549
+ ## テーブル
550
+ def table(lines, id=nil, caption=nil, option=nil)
551
+ super
552
+ end
553
+
554
+ def table_header(id, caption, options)
555
+ if id.present?
556
+ begin
557
+ num = @chapter.table(id).number
558
+ rescue KeyError
559
+ error "no such table: #{id}"
560
+ end
561
+ s1 = I18n.t('table')
562
+ s2 = get_chap() \
563
+ ? I18n.t('format_number_header', [get_chap(), num]) \
564
+ : I18n.t('format_number_header_without_chapter', [num])
565
+ s3 = I18n.t('caption_prefix')
566
+ puts "<p class=\"caption\">#{s1}#{s2}#{s3}#{compile_inline(caption||'')}</p>"
567
+ elsif caption.present?
568
+ puts "<p class=\"caption\">#{compile_inline(caption||'')}</p>"
569
+ end
570
+ end
571
+
572
+ def table_begin(_ncols, fontsize: nil)
573
+ if fontsize
574
+ puts "<table style=\"font-size:#{fontsize}\">"
575
+ else
576
+ puts "<table>"
577
+ end
578
+ end
579
+
580
+ def table_end
581
+ puts "</table>"
582
+ end
583
+
584
+ def th(s)
585
+ "<th>#{s}</th>"
586
+ end
587
+
588
+ def td(s)
589
+ "<td>#{s}</td>"
590
+ end
591
+
592
+ protected
593
+
594
+ def normalize_id(id)
595
+ id
596
+ end
597
+
598
+ def _table_before(id, caption, opts)
599
+ class_ = opts[:csv] ? "table table-nohline" : "table"
600
+ if id.present?
601
+ puts "<div id=\"#{normalize_id(id)}\" class=\"#{class_}\">"
602
+ else
603
+ puts "<div class=\"#{class_}\">"
604
+ end
605
+ end
606
+
607
+ def _table_after(id, caption, opts)
608
+ puts "</div>"
609
+ end
610
+
611
+ def _table_bottom(hline: false)
612
+ end
613
+
614
+ def _table_tr(cells, hline: false)
615
+ if hline
616
+ "<tr class=\"hline\">#{cells.join}</tr>"
617
+ else
618
+ "<tr>#{cells.join}</tr>"
619
+ end
620
+ end
621
+
622
+ public
623
+
624
+ ## //imgtable
625
+ def imgtable(lines, id, caption=nil, option=nil)
626
+ super
627
+ end
628
+
629
+ protected
630
+
631
+ def _render_imgtable(id, caption, opts)
632
+ puts "<div id=\"#{normalize_id(id)}\" class=\"imgtable image\">"
633
+ table_header(id, caption, opts)
634
+ puts " <div>"
635
+ yield
636
+ puts " </div>"
637
+ puts "</div>"
638
+ end
639
+
640
+ def _render_imgtable_caption(caption)
641
+ end
642
+
643
+ def _render_imgtable_label(id)
644
+ end
645
+
646
+ public
647
+
648
+ #### インライン命令
649
+
650
+ def inline_fn(id)
651
+ @_fn_count ||= 0
652
+ @_fn_labels ||= {}
653
+ n = (@_fn_count += 1)
654
+ @_fn_labels[id] = n
655
+ return "[^#{n}]"
656
+ end
657
+
658
+ ## 改段落(箇条書き内では空行を入れられないため)
659
+ def inline_par(arg)
660
+ #blank()
661
+ "<br>"
662
+ end
663
+
664
+ ## ファイル名
665
+ def inline_file(str)
666
+ "`#{str}`"
667
+ end
668
+ def on_inline_file
669
+ "`#{yield}`"
670
+ end
671
+
672
+ ## ユーザ入力
673
+ def inline_userinput(str)
674
+ str
675
+ end
676
+ def on_inline_userinput
677
+ yield
678
+ end
679
+
680
+ ## 引数をそのまま表示 (No Operation)
681
+ def inline_nop(str)
682
+ str || ""
683
+ end
684
+ alias inline_letitgo inline_nop
685
+
686
+ ## 目立たせない(@<strong>{} の反対)
687
+ def inline_weak(str)
688
+ str
689
+ end
690
+ def on_inline_weak
691
+ yield
692
+ end
693
+
694
+ ## 文字を小さくする
695
+ def inline_small(str); str; end
696
+ def inline_xsmall(str); str; end
697
+ def inline_xxsmall(str); str; end
698
+ def on_inline_small(); yield; end
699
+ def on_inline_xsmall(); yield; end
700
+ def on_inline_xxsmall(); yield; end
701
+
702
+ ## 文字を大きくする
703
+ def inline_large(str); str; end
704
+ def inline_xlarge(str); str; end
705
+ def inline_xxlarge(str); str; end
706
+ def on_inline_large(); yield; end
707
+ def on_inline_xlarge(); yield; end
708
+ def on_inline_xxlarge(); yield; end
709
+
710
+ ## 文字を大きくした@<strong>{}
711
+ def inline_strong(str); str; end
712
+ def inline_xstrong(str); str; end
713
+ def inline_xxstrong(str); str; end
714
+ def on_inline_strong(); yield; end
715
+ def on_inline_xstrong(); yield; end
716
+ def on_inline_xxstrong(); yield; end
717
+
718
+ ## コードブロック中で折り返し箇所を手動で指定する
719
+ def inline_foldhere(arg)
720
+ ""
721
+ end
722
+
723
+ ## ターミナルでのカーソル(背景が白、文字が黒)
724
+ def inline_cursor(str)
725
+ "[#{str}]"
726
+ end
727
+
728
+ ## nestable inline commands
729
+
730
+ def on_inline_i() ; "*#{yield}*" ; end
731
+ #def on_inline_b() ; "**#{yield}**" ; end
732
+ #def on_inline_code() ; "`#{yield}`" ; end
733
+ def on_inline_tt() ; "<tt>#{yield}</tt>" ; end
734
+ def on_inline_del() ; "~~#{yield}~~" ; end
735
+ def on_inline_sub() ; "<sub>#{yield}</sub>" ; end
736
+ def on_inline_sup() ; "<sup>#{yield}</sup>" ; end
737
+ def on_inline_em() ; "<em>#{yield}</em>" ; end
738
+ def on_inline_strong(); "**#{yield}**" ; end
739
+ def on_inline_u() ; "<u>#{yield}</u>" ; end
740
+ def on_inline_ami() ; "<span class=\"ami\">#{yield}</span>"; end
741
+ def on_inline_balloon(); "← #{yield}"; end
742
+
743
+ def on_inline_b()
744
+ if within_codeblock?()
745
+ #"<b>#{yield}</b>"
746
+ yield
747
+ else
748
+ "**#{yield}**"
749
+ end
750
+ end
751
+
752
+ def on_inline_code()
753
+ with_context(:code) do
754
+ return "`#{yield}`"
755
+ end
756
+ end
757
+
758
+ ## 「“」と「”」で囲む
759
+ def on_inline_qq()
760
+ "“#{yield}”"
761
+ end
762
+
763
+ def build_inline_href(url, escaped_label) # compile_href()をベースに改造
764
+ if escaped_label.present?
765
+ "[#{escaped_label}](#{url})"
766
+ else
767
+ url
768
+ end
769
+ end
770
+
771
+ def inline_hlink(str)
772
+ url, label = str.split(/, /, 2)
773
+ flag_link = @book.config['externallink']
774
+ return _inline_hyperlink(url, escape(label), flag_link)
775
+ end
776
+
777
+ def _inline_hyperlink(url, escaped_label, flag_link)
778
+ if flag_link
779
+ label = escaped_label || url
780
+ "<a href=\"#{url}\">#{label}</a>"
781
+ elsif escaped_label
782
+ I18n.t('external_link', [escaped_label, url])
783
+ else
784
+ escape_html(url)
785
+ end
786
+ end
787
+ private :_inline_hyperlink
788
+
789
+ def build_inline_ruby(escaped_word, escaped_yomi) # compile_ruby()をベースに改造
790
+ "#{escaped_word}(#{escaped_yomi})"
791
+ end
792
+
793
+ protected
794
+
795
+ ## ノートを参照する
796
+ def build_noteref(chapter, label, caption)
797
+ "ノート「#{compile_inline(caption||'')}」"
798
+ end
799
+
800
+ ## 数式を参照する
801
+ def build_eq(chapter, label, number)
802
+ s = "#{I18n.t('equation')}#{chapter.number}.#{number}"
803
+ "<a>#{escape(s)}</a>"
804
+ end
805
+
806
+ public
807
+
808
+ ##########
809
+
810
+ def noindent()
811
+ end
812
+
813
+ def clearpage
814
+ blank2()
815
+ end
816
+
817
+ def on_flushright_block()
818
+ blank()
819
+ puts "[flushright]"; twospaces()
820
+ yield
821
+ truncate_blank(); twospaces()
822
+ puts "[/flushright]"
823
+ blank()
824
+ end
825
+
826
+ def on_centering_block()
827
+ blank()
828
+ puts "[centering]"; twospaces()
829
+ yield
830
+ truncate_blank(); twospaces()
831
+ puts "[/centering]"
832
+ blank()
833
+ end
834
+
835
+ def on_center_block()
836
+ blank()
837
+ puts "[center]"; twospaces()
838
+ yield
839
+ truncate_blank(); twospaces()
840
+ puts "[/center]"
841
+ blank()
842
+ end
843
+
844
+ def on_textleft_block()
845
+ blank()
846
+ puts "[textleft]"; twospaces()
847
+ yield
848
+ truncate_blank(); twospaces()
849
+ puts "[/textleft]"
850
+ blank()
851
+ end
852
+
853
+ def on_textright_block()
854
+ blank()
855
+ puts "[textright]"; twospaces()
856
+ yield
857
+ truncate_blank(); twospaces()
858
+ puts "[/textright]"
859
+ blank()
860
+ end
861
+
862
+ def on_textcenter_block()
863
+ blank()
864
+ puts "[textcenter]"; twospaces()
865
+ yield
866
+ truncate_blank(); twospaces()
867
+ puts "[/textcenter]"
868
+ blank()
869
+ end
870
+
871
+ def sampleoutputbegin(caption=nil)
872
+ blank()
873
+ if caption.present?
874
+ puts "<span class=\"caption\">▽#{compile_inline(caption)}</span>"
875
+ blank()
876
+ end
877
+ puts "----------------------------------------"
878
+ blank()
879
+ end
880
+
881
+ def sampleoutputend()
882
+ blank()
883
+ puts "----------------------------------------"
884
+ blank()
885
+ end
886
+
887
+
888
+ def on_inline_B()
889
+ "**#{yield}**"
890
+ end
891
+
892
+ def inline_br(_)
893
+ "<br>"
894
+ end
895
+
896
+ def inline_clearpage(_)
897
+ blank2()
898
+ end
899
+
900
+ def inline_m(str)
901
+ "$#{str}$"
902
+ end
903
+
904
+ def inline_icon(imagefile)
905
+ filepath = find_image_filepath(imagefile)
906
+ "<img src=\"#{filepath}\">"
907
+ end
908
+
909
+ def inline_par(arg)
910
+ "\n\n"
911
+ end
912
+
913
+ def inline_hd_chap(chap, id)
914
+ n = chap.headline_index.number(id)
915
+ if chap.number and @book.config['secnolevel'] >= n.split('.').size
916
+ str = I18n.t('chapter_quote', "#{n} #{compile_inline(chap.headline(id).caption)}")
917
+ else
918
+ str = I18n.t('chapter_quote', compile_inline(chap.headline(id).caption))
919
+ end
920
+ if @book.config['chapterlink']
921
+ anchor = 'h' + n.gsub('.', '-')
922
+ %Q(<a href="#{chap.id}#{extname}##{anchor}">#{str}</a>)
923
+ else
924
+ str
925
+ end
926
+ rescue KeyError
927
+ error "unknown headline: #{id}"
928
+ end
929
+
930
+ def inline_TeX(_)
931
+ "TeX"
932
+ end
933
+
934
+ def inline_LaTeX(_)
935
+ "LaTeX"
936
+ end
937
+
938
+ def inline_hearts(_)
939
+ ":heart:"
940
+ end
941
+
942
+ end
943
+
944
+
945
+ end