review 4.1.0 → 4.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +1 -1
- data/.rubocop.yml +4 -1
- data/NEWS.ja.md +29 -0
- data/NEWS.md +29 -0
- data/bin/review-index +2 -89
- data/bin/review-vol +4 -78
- data/doc/config.yml.sample +18 -5
- data/doc/config.yml.sample-simple +1 -1
- data/doc/pdfmaker.ja.md +42 -0
- data/doc/pdfmaker.md +41 -0
- data/doc/quickstart.ja.md +8 -5
- data/doc/quickstart.md +7 -4
- data/lib/review/book/base.rb +2 -4
- data/lib/review/book/compilable.rb +1 -5
- data/lib/review/book/page_metric.rb +7 -7
- data/lib/review/book/part.rb +6 -3
- data/lib/review/book/volume.rb +3 -4
- data/lib/review/builder.rb +23 -10
- data/lib/review/compiler.rb +9 -9
- data/lib/review/configure.rb +6 -0
- data/lib/review/epubmaker.rb +1 -1
- data/lib/review/htmlbuilder.rb +56 -16
- data/lib/review/idgxmlbuilder.rb +63 -22
- data/lib/review/latexbuilder.rb +70 -19
- data/lib/review/makerhelper.rb +18 -1
- data/lib/review/pdfmaker.rb +8 -1
- data/lib/review/plaintextbuilder.rb +41 -11
- data/lib/review/textmaker.rb +1 -1
- data/lib/review/textutils.rb +2 -3
- data/lib/review/tocprinter.rb +231 -102
- data/lib/review/topbuilder.rb +47 -13
- data/lib/review/version.rb +1 -1
- data/lib/review/volumeprinter.rb +99 -0
- data/lib/review/webmaker.rb +1 -1
- data/lib/review/webtocprinter.rb +38 -35
- data/review.gemspec +1 -1
- data/samples/sample-book/src/config.yml +1 -1
- data/templates/web/html/layout-html5.html.erb +2 -2
- data/test/test_book.rb +1 -1
- data/test/test_book_part.rb +3 -3
- data/test/test_helper.rb +4 -1
- data/test/test_htmlbuilder.rb +179 -0
- data/test/test_idgxmlbuilder.rb +143 -0
- data/test/test_latexbuilder.rb +223 -0
- data/test/test_pdfmaker.rb +17 -0
- data/test/test_plaintextbuilder.rb +99 -0
- data/test/test_topbuilder.rb +116 -2
- data/test/test_webtocprinter.rb +66 -34
- metadata +3 -5
- data/lib/review/tocparser.rb +0 -275
- data/test/test_tocparser.rb +0 -25
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2018-
|
1
|
+
# Copyright (c) 2018-2020 Kenshi Muto
|
2
2
|
#
|
3
3
|
# This program is free software.
|
4
4
|
# You can distribute or modify this program under the terms of
|
@@ -140,13 +140,19 @@ module ReVIEW
|
|
140
140
|
def list(lines, id, caption, lang = nil)
|
141
141
|
blank
|
142
142
|
begin
|
143
|
-
|
143
|
+
if caption_top?('list')
|
144
|
+
list_header(id, caption, lang)
|
145
|
+
blank
|
146
|
+
end
|
147
|
+
list_body(id, lines, lang)
|
148
|
+
unless caption_top?('list')
|
149
|
+
blank
|
150
|
+
list_header(id, caption, lang)
|
151
|
+
end
|
144
152
|
rescue KeyError
|
145
153
|
error "no such list: #{id}"
|
146
154
|
end
|
147
155
|
blank
|
148
|
-
list_body(id, lines, lang)
|
149
|
-
blank
|
150
156
|
end
|
151
157
|
|
152
158
|
def list_header(id, caption, _lang)
|
@@ -165,8 +171,13 @@ module ReVIEW
|
|
165
171
|
|
166
172
|
def base_block(_type, lines, caption = nil)
|
167
173
|
blank
|
168
|
-
|
174
|
+
if caption_top?('list') && caption.present?
|
175
|
+
puts compile_inline(caption)
|
176
|
+
end
|
169
177
|
puts lines.join("\n")
|
178
|
+
if !caption_top?('list') && caption.present?
|
179
|
+
puts compile_inline(caption)
|
180
|
+
end
|
170
181
|
blank
|
171
182
|
end
|
172
183
|
|
@@ -183,23 +194,34 @@ module ReVIEW
|
|
183
194
|
|
184
195
|
def emlistnum(lines, caption = nil, _lang = nil)
|
185
196
|
blank
|
186
|
-
|
197
|
+
if caption_top?('list')
|
198
|
+
puts compile_inline(caption) if caption.present?
|
199
|
+
end
|
187
200
|
lines.each_with_index do |line, i|
|
188
201
|
puts((i + 1).to_s.rjust(2) + ": #{line}")
|
189
202
|
end
|
203
|
+
unless caption_top?('list')
|
204
|
+
puts compile_inline(caption) if caption.present?
|
205
|
+
end
|
190
206
|
blank
|
191
207
|
end
|
192
208
|
|
193
209
|
def listnum(lines, id, caption, lang = nil)
|
194
210
|
blank
|
195
211
|
begin
|
196
|
-
|
212
|
+
if caption_top?('list')
|
213
|
+
list_header(id, caption, lang)
|
214
|
+
blank
|
215
|
+
end
|
216
|
+
listnum_body(lines, lang)
|
217
|
+
unless caption_top?('list')
|
218
|
+
blank
|
219
|
+
list_header(id, caption, lang)
|
220
|
+
end
|
197
221
|
rescue KeyError
|
198
222
|
error "no such list: #{id}"
|
199
223
|
end
|
200
224
|
blank
|
201
|
-
listnum_body(lines, lang)
|
202
|
-
blank
|
203
225
|
end
|
204
226
|
|
205
227
|
def listnum_body(lines, _lang)
|
@@ -228,8 +250,9 @@ module ReVIEW
|
|
228
250
|
|
229
251
|
def texequation(lines, id = nil, caption = '')
|
230
252
|
blank
|
231
|
-
texequation_header(id, caption)
|
253
|
+
texequation_header(id, caption) if caption_top?('equation')
|
232
254
|
puts lines.join("\n")
|
255
|
+
texequation_header(id, caption) unless caption_top?('equation')
|
233
256
|
blank
|
234
257
|
end
|
235
258
|
|
@@ -251,6 +274,10 @@ module ReVIEW
|
|
251
274
|
end
|
252
275
|
|
253
276
|
def table_header(id, caption)
|
277
|
+
unless caption_top?('table')
|
278
|
+
blank
|
279
|
+
end
|
280
|
+
|
254
281
|
if id.nil?
|
255
282
|
puts compile_inline(caption)
|
256
283
|
elsif get_chap
|
@@ -258,7 +285,10 @@ module ReVIEW
|
|
258
285
|
else
|
259
286
|
puts "#{I18n.t('table')}#{I18n.t('format_number_without_chapter', [@chapter.table(id).number])}#{I18n.t('caption_prefix_idgxml')}#{compile_inline(caption)}"
|
260
287
|
end
|
261
|
-
|
288
|
+
|
289
|
+
if caption_top?('table')
|
290
|
+
blank
|
291
|
+
end
|
262
292
|
end
|
263
293
|
|
264
294
|
def table_begin(_ncols)
|
data/lib/review/textmaker.rb
CHANGED
@@ -98,7 +98,7 @@ module ReVIEW
|
|
98
98
|
end
|
99
99
|
|
100
100
|
math_dir = "./#{@config['imagedir']}/_review_math_text"
|
101
|
-
if @config['imgmath'] && File.exist?(File.join(math_dir, '__IMGMATH_BODY__.
|
101
|
+
if @config['imgmath'] && File.exist?(File.join(math_dir, '__IMGMATH_BODY__.map'))
|
102
102
|
make_math_images(math_dir)
|
103
103
|
end
|
104
104
|
end
|
data/lib/review/textutils.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2008-
|
1
|
+
# Copyright (c) 2008-2020 Minero Aoki, Kenshi Muto, Masayoshi Takahashi,
|
2
2
|
# KADO Masanori
|
3
3
|
# 2002-2007 Minero Aoki
|
4
4
|
#
|
@@ -98,9 +98,8 @@ module ReVIEW
|
|
98
98
|
|
99
99
|
def defer_math_image(str, path, key)
|
100
100
|
# for Re:VIEW >3
|
101
|
-
File.open(File.join(File.dirname(path),
|
101
|
+
File.open(File.join(File.dirname(path), "__IMGMATH_BODY__#{key}.tex"), 'w') do |f|
|
102
102
|
f.puts str
|
103
|
-
f.puts '\\clearpage'
|
104
103
|
end
|
105
104
|
File.open(File.join(File.dirname(path), '__IMGMATH_BODY__.map'), 'a+') do |f|
|
106
105
|
f.puts key
|
data/lib/review/tocprinter.rb
CHANGED
@@ -1,148 +1,277 @@
|
|
1
|
-
# Copyright (c) 2008-
|
2
|
-
#
|
1
|
+
# Copyright (c) 2008-2020 Minero Aoki, Kenshi Muto
|
2
|
+
# 1999-2007 Minero Aoki
|
3
3
|
#
|
4
4
|
# This program is free software.
|
5
5
|
# You can distribute or modify this program under the terms of
|
6
6
|
# the GNU LGPL, Lesser General Public License version 2.1.
|
7
|
-
# For details of LGPL, see the file "COPYING".
|
7
|
+
# For details of the GNU LGPL, see the file "COPYING".
|
8
8
|
#
|
9
9
|
|
10
|
-
require 'review/
|
11
|
-
require 'review/
|
10
|
+
require 'review/book'
|
11
|
+
require 'review/version'
|
12
|
+
require 'optparse'
|
13
|
+
require 'review/plaintextbuilder'
|
12
14
|
|
13
15
|
module ReVIEW
|
14
|
-
class
|
15
|
-
def
|
16
|
-
|
16
|
+
class PLAINTEXTTocBuilder < PLAINTEXTBuilder
|
17
|
+
def headline(level, label, caption)
|
18
|
+
if @chapter.is_a?(ReVIEW::Book::Part)
|
19
|
+
print "\x01H0\x01" # XXX: don't modify level value. level value will be handled in sec_counter#prefix()
|
20
|
+
else
|
21
|
+
print "\x01H#{level}\x01"
|
22
|
+
end
|
23
|
+
# embed header information for tocparser
|
24
|
+
super(level, label, caption)
|
17
25
|
end
|
18
26
|
|
19
|
-
def
|
20
|
-
|
21
|
-
|
22
|
-
|
27
|
+
def base_block(type, lines, caption = nil)
|
28
|
+
puts "\x01STARTLIST\x01"
|
29
|
+
super(type, lines, caption)
|
30
|
+
puts "\x01ENDLIST\x01"
|
23
31
|
end
|
24
32
|
|
25
|
-
def
|
26
|
-
|
33
|
+
def blank
|
34
|
+
@blank_seen = true
|
27
35
|
end
|
36
|
+
end
|
28
37
|
|
29
|
-
|
30
|
-
|
38
|
+
class TOCPrinter
|
39
|
+
def self.execute(*args)
|
40
|
+
Signal.trap(:INT) { exit 1 }
|
41
|
+
if RUBY_PLATFORM !~ /mswin(?!ce)|mingw|cygwin|bccwin/
|
42
|
+
Signal.trap(:PIPE, 'IGNORE')
|
43
|
+
end
|
44
|
+
new.execute(*args)
|
45
|
+
rescue Errno::EPIPE
|
46
|
+
exit 0
|
31
47
|
end
|
32
48
|
|
33
|
-
def
|
34
|
-
|
35
|
-
|
36
|
-
|
49
|
+
def initialize
|
50
|
+
@logger = ReVIEW.logger
|
51
|
+
@config = ReVIEW::Configure.values
|
52
|
+
@yamlfile = 'config.yml'
|
53
|
+
@book = ReVIEW::Book::Base.load
|
54
|
+
@upper = 4
|
55
|
+
@indent = true
|
56
|
+
@buildonly = nil
|
57
|
+
@detail = nil
|
37
58
|
end
|
38
59
|
|
39
|
-
def
|
40
|
-
|
60
|
+
def execute(*args)
|
61
|
+
parse_options(args)
|
62
|
+
@book.config = ReVIEW::Configure.values
|
63
|
+
unless File.readable?(@yamlfile)
|
64
|
+
@logger.error("No such fiile or can't open #{@yamlfile}.")
|
65
|
+
exit 1
|
66
|
+
end
|
67
|
+
@book.load_config(@yamlfile)
|
68
|
+
I18n.setup(@config['language'])
|
69
|
+
|
70
|
+
if @detail
|
71
|
+
begin
|
72
|
+
require 'unicode/eaw'
|
73
|
+
@calc_char_width = true
|
74
|
+
rescue LoadError
|
75
|
+
@logger.warn('not found unicode/eaw library. page volume may be unreliable.')
|
76
|
+
@calc_char_width = nil
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
print_result(build_result_array)
|
41
81
|
end
|
42
|
-
end
|
43
82
|
|
44
|
-
|
45
|
-
|
83
|
+
def build_result_array
|
84
|
+
result_array = []
|
85
|
+
begin
|
86
|
+
@book.parts.each do |part|
|
87
|
+
if part.name.present? && (@buildonly.nil? || @buildonly.include?(part.name))
|
88
|
+
result_array.push({ part: 'start' })
|
89
|
+
if part.file?
|
90
|
+
result = build_chap(part)
|
91
|
+
result_array += parse_contents(part.name, @upper, result)
|
92
|
+
else
|
93
|
+
title = part.format_number + I18n.t('chapter_postfix') + part.title
|
94
|
+
result_array += [
|
95
|
+
{ name: '', lines: 1, chars: title.size, list_lines: 0, text_lines: 1 },
|
96
|
+
{ level: 0, headline: title, lines: 1, chars: title.size, list_lines: 0, text_lines: 1 }
|
97
|
+
]
|
98
|
+
end
|
99
|
+
end
|
46
100
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
101
|
+
part.chapters.each do |chap|
|
102
|
+
if @buildonly.nil? || @buildonly.include?(chap.name)
|
103
|
+
result = build_chap(chap)
|
104
|
+
result_array += parse_contents(chap.name, @upper, result)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
if part.name.present? && (@buildonly.nil? || @buildonly.include?(part.name))
|
108
|
+
result_array.push({ part: 'end' })
|
109
|
+
end
|
53
110
|
end
|
111
|
+
rescue ReVIEW::FileNotFound => e
|
112
|
+
@logger.error e
|
113
|
+
exit 1
|
54
114
|
end
|
115
|
+
|
116
|
+
result_array
|
55
117
|
end
|
56
118
|
|
57
|
-
def
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
119
|
+
def print_result(result_array)
|
120
|
+
result_array.each do |result|
|
121
|
+
if result[:part]
|
122
|
+
next
|
123
|
+
end
|
124
|
+
|
125
|
+
if result[:name]
|
126
|
+
# file information
|
127
|
+
if @detail
|
128
|
+
puts '============================='
|
129
|
+
printf("%6dC %5dL %5dP %s\n", result[:chars], result[:lines], calc_pages(result).ceil, result[:name])
|
130
|
+
puts '-----------------------------'
|
131
|
+
end
|
132
|
+
next
|
133
|
+
end
|
134
|
+
|
135
|
+
# section information
|
136
|
+
if @detail
|
137
|
+
printf('%6dC %5dL %5.1fP ', result[:chars], result[:lines], calc_pages(result))
|
138
|
+
end
|
139
|
+
if @indent && result[:level]
|
140
|
+
print ' ' * (result[:level] == 0 ? 0 : result[:level] - 1)
|
141
|
+
end
|
142
|
+
puts result[:headline]
|
68
143
|
end
|
69
144
|
end
|
70
145
|
|
71
|
-
def
|
72
|
-
|
146
|
+
def calc_pages(result)
|
147
|
+
p = 0
|
148
|
+
p += result[:list_lines].to_f / @book.page_metric.list.n_lines
|
149
|
+
p += result[:text_lines].to_f / @book.page_metric.text.n_lines
|
150
|
+
p
|
73
151
|
end
|
74
152
|
|
75
|
-
def
|
76
|
-
|
77
|
-
|
78
|
-
|
153
|
+
def calc_linesize(l)
|
154
|
+
return l.size unless @calc_char_width
|
155
|
+
w = 0
|
156
|
+
l.split('').each do |c|
|
157
|
+
# XXX: should include A also?
|
158
|
+
if %i[Na H N].include?(Unicode::Eaw.property(c))
|
159
|
+
w += 0.5 # halfwidth
|
160
|
+
else
|
161
|
+
w += 1
|
162
|
+
end
|
163
|
+
end
|
164
|
+
w
|
79
165
|
end
|
80
|
-
end
|
81
166
|
|
82
|
-
|
83
|
-
|
167
|
+
def parse_contents(name, upper, content)
|
168
|
+
headline_array = []
|
169
|
+
counter = { lines: 0, chars: 0, list_lines: 0, text_lines: 0 }
|
170
|
+
listmode = nil
|
84
171
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
172
|
+
content.split("\n").each do |l|
|
173
|
+
if l.start_with?("\x01STARTLIST\x01")
|
174
|
+
listmode = true
|
175
|
+
next
|
176
|
+
elsif l.start_with?("\x01ENDLIST\x01")
|
177
|
+
listmode = nil
|
178
|
+
next
|
179
|
+
elsif l =~ /\A\x01H(\d)\x01/
|
180
|
+
# headline
|
181
|
+
level = $1.to_i
|
182
|
+
l = $'
|
183
|
+
if level <= upper
|
184
|
+
if counter[:chars] > 0
|
185
|
+
headline_array.push(counter)
|
186
|
+
end
|
187
|
+
headline = l
|
188
|
+
counter = {
|
189
|
+
level: level,
|
190
|
+
headline: headline,
|
191
|
+
lines: 1,
|
192
|
+
chars: headline.size,
|
193
|
+
list_lines: 0,
|
194
|
+
text_lines: 1
|
195
|
+
}
|
196
|
+
next
|
197
|
+
end
|
198
|
+
end
|
90
199
|
|
91
|
-
|
92
|
-
|
93
|
-
@out.puts li(part.title)
|
94
|
-
end
|
95
|
-
super
|
96
|
-
end
|
200
|
+
counter[:lines] += 1
|
201
|
+
counter[:chars] += l.size
|
97
202
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
203
|
+
if listmode
|
204
|
+
# code list: calculate line wrapping
|
205
|
+
if l.size == 0
|
206
|
+
counter[:list_lines] += 1
|
207
|
+
else
|
208
|
+
counter[:list_lines] += (calc_linesize(l) - 1) / @book.page_metric.list.n_columns + 1
|
209
|
+
end
|
210
|
+
else
|
211
|
+
# normal paragraph: calculate line wrapping
|
212
|
+
if l.size == 0
|
213
|
+
counter[:text_lines] += 1
|
214
|
+
else
|
215
|
+
counter[:text_lines] += (calc_linesize(l) - 1) / @book.page_metric.text.n_columns + 1
|
216
|
+
end
|
217
|
+
end
|
113
218
|
end
|
114
|
-
|
219
|
+
headline_array.push(counter)
|
115
220
|
|
116
|
-
|
221
|
+
total_lines = 0
|
222
|
+
total_chars = 0
|
223
|
+
total_list_lines = 0
|
224
|
+
total_text_lines = 0
|
117
225
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
end
|
226
|
+
headline_array.each do |h|
|
227
|
+
next unless h[:lines]
|
228
|
+
total_lines += h[:lines]
|
229
|
+
total_chars += h[:chars]
|
230
|
+
total_list_lines += h[:list_lines]
|
231
|
+
total_text_lines += h[:text_lines]
|
232
|
+
end
|
126
233
|
|
127
|
-
|
128
|
-
|
129
|
-
chap.each_section do |sec|
|
130
|
-
res << li(escape_html(sec.label))
|
131
|
-
next unless print?(4)
|
132
|
-
next unless sec.section_size > 0
|
133
|
-
res << '<ul>'
|
134
|
-
sec.each_section { |node| res << li(escape_html(node.label)) }
|
135
|
-
res << '</ul>'
|
136
|
-
end
|
137
|
-
res.join("\n")
|
234
|
+
headline_array.delete_if(&:empty?).
|
235
|
+
unshift({ name: name, lines: total_lines, chars: total_chars, list_lines: total_list_lines, text_lines: total_text_lines })
|
138
236
|
end
|
139
237
|
|
140
|
-
def
|
141
|
-
|
238
|
+
def build_chap(chap)
|
239
|
+
compiler = ReVIEW::Compiler.new(ReVIEW::PLAINTEXTTocBuilder.new)
|
240
|
+
begin
|
241
|
+
compiler.compile(@book.chapter(chap.name))
|
242
|
+
rescue ReVIEW::ApplicationError => e
|
243
|
+
@logger.error e
|
244
|
+
exit 1
|
245
|
+
end
|
142
246
|
end
|
143
247
|
|
144
|
-
def
|
145
|
-
|
248
|
+
def parse_options(args)
|
249
|
+
opts = OptionParser.new
|
250
|
+
opts.version = ReVIEW::VERSION
|
251
|
+
opts.on('--yaml=YAML', 'Read configurations from YAML file.') { |yaml| @yamlfile = yaml }
|
252
|
+
opts.on('-y', '--only file1,file2,...', 'list only specified files.') do |v|
|
253
|
+
@buildonly = v.split(/\s*,\s*/).map { |m| m.strip.sub(/\.re\Z/, '') }
|
254
|
+
end
|
255
|
+
opts.on('-l', '--level N', 'list upto N level (default=4)') do |n|
|
256
|
+
@upper = n.to_i
|
257
|
+
end
|
258
|
+
opts.on('-d', '--detail', 'show characters and lines of each section.') do
|
259
|
+
@detail = true
|
260
|
+
end
|
261
|
+
opts.on('--noindent', "don't indent headlines.") do
|
262
|
+
@indent = nil
|
263
|
+
end
|
264
|
+
opts.on('--help', 'print this message and quit.') do
|
265
|
+
puts opts.help
|
266
|
+
exit 0
|
267
|
+
end
|
268
|
+
begin
|
269
|
+
opts.parse!(args)
|
270
|
+
rescue OptionParser::ParseError => e
|
271
|
+
@logger.error e.message
|
272
|
+
$stderr.puts opts.help
|
273
|
+
exit 1
|
274
|
+
end
|
146
275
|
end
|
147
276
|
end
|
148
277
|
end
|