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.
- checksums.yaml +4 -4
- data/.github/workflows/retrovert.yml +39 -0
- data/.gitignore +2 -2
- data/.ruby-version +1 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +68 -2
- data/README.md +6 -3
- data/lib/review/retrovert/cli.rb +2 -0
- data/lib/review/retrovert/converter.rb +255 -16
- data/lib/review/retrovert/version.rb +1 -1
- data/review-retrovert.gemspec +1 -1
- data/testdata/mybook/.gitignore +7 -0
- data/testdata/mybook/README.md +43 -0
- data/testdata/mybook/Rakefile +16 -0
- data/testdata/mybook/catalog.yml +33 -0
- data/testdata/mybook/config-starter.yml +139 -0
- data/testdata/mybook/config.yml +375 -0
- data/testdata/mybook/contents/00-preface.re +83 -0
- data/testdata/mybook/contents/01-install.re +272 -0
- data/testdata/mybook/contents/02-tutorial.re +1008 -0
- data/testdata/mybook/contents/03-syntax.re +2613 -0
- data/testdata/mybook/contents/04-customize.re +728 -0
- data/testdata/mybook/contents/05-faq.re +328 -0
- data/testdata/mybook/contents/06-bestpractice.re +971 -0
- data/testdata/mybook/contents/91-compare.re +18 -0
- data/testdata/mybook/contents/92-filelist.re +119 -0
- data/testdata/mybook/contents/93-background.re +267 -0
- data/testdata/mybook/contents/99-postface.re +38 -0
- data/testdata/mybook/css/normalize.css +349 -0
- data/testdata/mybook/css/webstyle.css +514 -0
- data/testdata/mybook/images/03-syntax/favicon-16x16.png +0 -0
- data/testdata/mybook/images/03-syntax/figure_heretop.png +0 -0
- data/testdata/mybook/images/03-syntax/tw-icon1.jpg +0 -0
- data/testdata/mybook/images/03-syntax/tw-icon2.jpg +0 -0
- data/testdata/mybook/images/03-syntax/tw-icon3.jpg +0 -0
- data/testdata/mybook/images/03-syntax/tw-icon4.jpg +0 -0
- data/testdata/mybook/images/04-customize/caption_pagebreak.png +0 -0
- data/testdata/mybook/images/04-customize/chaptitlepage_sample.png +0 -0
- data/testdata/mybook/images/05-faq/codeblock_rpadding1.png +0 -0
- data/testdata/mybook/images/05-faq/codeblock_rpadding2.png +0 -0
- data/testdata/mybook/images/06-bestpractice/figure_heretop.png +0 -0
- data/testdata/mybook/images/06-bestpractice/font_beramono.png +0 -0
- data/testdata/mybook/images/06-bestpractice/heading_design1.png +0 -0
- data/testdata/mybook/images/06-bestpractice/heading_design2.png +0 -0
- data/testdata/mybook/images/06-bestpractice/margin_book.png +0 -0
- data/testdata/mybook/images/06-bestpractice/multiline-title.png +0 -0
- data/testdata/mybook/images/06-bestpractice/preface_numbered.png +0 -0
- data/testdata/mybook/images/06-bestpractice/program_border.png +0 -0
- data/testdata/mybook/images/06-bestpractice/sechead_design_4.png +0 -0
- data/testdata/mybook/images/06-bestpractice/titlepage-samples.png +0 -0
- data/testdata/mybook/images/93-background/bug913.png +0 -0
- data/testdata/mybook/images/93-background/slide2.png +0 -0
- data/testdata/mybook/images/cover_a5.pdf +0 -0
- data/testdata/mybook/images/cover_b5.pdf +0 -0
- data/testdata/mybook/images/tw-icon.jpg +0 -0
- data/testdata/mybook/layouts/layout.epub.erb +29 -0
- data/testdata/mybook/layouts/layout.html5.erb +106 -0
- data/testdata/mybook/layouts/layout.tex.erb +546 -0
- data/testdata/mybook/lib/hooks/beforetexcompile.rb +55 -0
- data/testdata/mybook/lib/ruby/review-builder.rb +503 -0
- data/testdata/mybook/lib/ruby/review-cli.rb +58 -0
- data/testdata/mybook/lib/ruby/review-compiler.rb +523 -0
- data/testdata/mybook/lib/ruby/review-epubmaker.rb +606 -0
- data/testdata/mybook/lib/ruby/review-htmlbuilder.rb +661 -0
- data/testdata/mybook/lib/ruby/review-latexbuilder.rb +782 -0
- data/testdata/mybook/lib/ruby/review-maker.rb +235 -0
- data/testdata/mybook/lib/ruby/review-monkeypatch.rb +91 -0
- data/testdata/mybook/lib/ruby/review-pdfmaker.rb +468 -0
- data/testdata/mybook/lib/ruby/review-textbuilder.rb +36 -0
- data/testdata/mybook/lib/ruby/review-tocparser.rb +285 -0
- data/testdata/mybook/lib/ruby/review-webmaker.rb +433 -0
- data/testdata/mybook/lib/tasks/mytasks.rake +31 -0
- data/testdata/mybook/lib/tasks/review.rake +142 -0
- data/testdata/mybook/lib/tasks/review.rake.orig +72 -0
- data/testdata/mybook/lib/tasks/starter.rake +326 -0
- data/testdata/mybook/locale.yml +6 -0
- data/testdata/mybook/review-ext.rb +206 -0
- data/testdata/mybook/sty/jumoline.sty +310 -0
- data/testdata/mybook/sty/mycolophon.sty +81 -0
- data/testdata/mybook/sty/mystyle.sty +8 -0
- data/testdata/mybook/sty/mytextsize.sty +61 -0
- data/testdata/mybook/sty/mytitlepage.sty +103 -0
- data/testdata/mybook/sty/reviewmacro.sty +60 -0
- data/testdata/mybook/sty/starter-codeblock.sty +332 -0
- data/testdata/mybook/sty/starter-color.sty +79 -0
- data/testdata/mybook/sty/starter-font.sty +112 -0
- data/testdata/mybook/sty/starter-heading.sty +514 -0
- data/testdata/mybook/sty/starter-note.sty +127 -0
- data/testdata/mybook/sty/starter-section.sty +262 -0
- data/testdata/mybook/sty/starter-toc.sty +72 -0
- data/testdata/mybook/sty/starter.sty +554 -0
- data/testdata/mybook/style.css +597 -0
- 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
|