review 2.3.0 → 2.4.0
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/.rubocop.yml +90 -66
- data/.travis.yml +1 -1
- data/Gemfile +0 -1
- data/NEWS.ja.md +82 -0
- data/NEWS.md +83 -0
- data/README.md +5 -3
- data/Rakefile +8 -8
- data/bin/review +1 -5
- data/bin/review-catalog-converter +22 -27
- data/bin/review-check +36 -43
- data/bin/review-checkdep +10 -15
- data/bin/review-compile +37 -55
- data/bin/review-epubmaker +4 -5
- data/bin/review-index +21 -29
- data/bin/review-init +26 -37
- data/bin/review-pdfmaker +0 -2
- data/bin/review-preproc +25 -45
- data/bin/review-validate +19 -18
- data/bin/review-vol +15 -27
- data/doc/config.yml.sample +5 -2
- data/doc/format.ja.md +20 -1
- data/doc/format.md +21 -5
- data/doc/images/review-generate.png +0 -0
- data/lib/epubmaker.rb +1 -3
- data/lib/epubmaker/content.rb +24 -27
- data/lib/epubmaker/epubcommon.rb +135 -148
- data/lib/epubmaker/epubv2.rb +39 -46
- data/lib/epubmaker/epubv3.rb +93 -103
- data/lib/epubmaker/producer.rb +138 -151
- data/lib/epubmaker/zip_exporter.rb +21 -26
- data/lib/review/book.rb +3 -6
- data/lib/review/book/base.rb +78 -103
- data/lib/review/book/chapter.rb +36 -40
- data/lib/review/book/compilable.rb +28 -31
- data/lib/review/book/image_finder.rb +6 -13
- data/lib/review/book/index.rb +100 -121
- data/lib/review/book/page_metric.rb +2 -7
- data/lib/review/book/part.rb +18 -20
- data/lib/review/book/volume.rb +9 -13
- data/lib/review/builder.rb +81 -116
- data/lib/review/catalog.rb +15 -19
- data/lib/review/compiler.rb +64 -83
- data/lib/review/configure.rb +87 -97
- data/lib/review/converter.rb +2 -7
- data/lib/review/epubbuilder.rb +1 -3
- data/lib/review/epubmaker.rb +213 -205
- data/lib/review/exception.rb +2 -4
- data/lib/review/extentions.rb +0 -1
- data/lib/review/extentions/hash.rb +2 -2
- data/lib/review/extentions/string.rb +5 -30
- data/lib/review/htmlbuilder.rb +320 -375
- data/lib/review/htmltoc.rb +4 -7
- data/lib/review/htmlutils.rb +29 -32
- data/lib/review/i18n.rb +33 -44
- data/lib/review/i18n.yml +3 -3
- data/lib/review/idgxmlbuilder.rb +309 -345
- data/lib/review/latexbuilder.rb +175 -212
- data/lib/review/latexindex.rb +2 -8
- data/lib/review/latexutils.rb +33 -43
- data/lib/review/lineinput.rb +1 -1
- data/lib/review/logger.rb +21 -0
- data/lib/review/makerhelper.rb +1 -4
- data/lib/review/markdownbuilder.rb +44 -53
- data/lib/review/md2inaobuilder.rb +6 -12
- data/lib/review/pdfmaker.rb +143 -173
- data/lib/review/preprocessor.rb +64 -101
- data/lib/review/rstbuilder.rb +126 -158
- data/lib/review/sec_counter.rb +18 -34
- data/lib/review/template.rb +4 -5
- data/lib/review/textbuilder.rb +2 -3
- data/lib/review/textutils.rb +7 -13
- data/lib/review/tocparser.rb +31 -56
- data/lib/review/tocprinter.rb +26 -52
- data/lib/review/topbuilder.rb +219 -247
- data/lib/review/unfold.rb +15 -24
- data/lib/review/version.rb +1 -1
- data/lib/review/webmaker.rb +75 -99
- data/lib/review/webtocprinter.rb +15 -20
- data/lib/review/yamlloader.rb +13 -15
- data/review.gemspec +20 -22
- data/templates/latex/layout.tex.erb +2 -2
- data/templates/opf/epubv2.opf.erb +7 -7
- data/templates/opf/epubv3.opf.erb +7 -7
- data/templates/web/html/layout-html5.html.erb +2 -2
- data/test/assets/black.eps +280 -0
- data/test/assets/fit.png +0 -0
- data/test/assets/large.gif +0 -0
- data/test/assets/large.jpg +0 -0
- data/test/assets/large.png +0 -0
- data/test/assets/large.svg +65 -0
- data/test/assets/test_template.tex +1 -1
- data/test/book_test_helper.rb +2 -2
- data/test/run_test.rb +4 -4
- data/test/sample-book/src/Rakefile +21 -22
- data/test/syntax-book/Gemfile +4 -0
- data/test/syntax-book/Rakefile +72 -0
- data/test/syntax-book/appA.re +22 -0
- data/test/syntax-book/bib.re +6 -0
- data/test/syntax-book/catalog.yml +15 -0
- data/test/syntax-book/ch01.re +136 -0
- data/test/syntax-book/ch02.re +351 -0
- data/test/syntax-book/ch03.re +82 -0
- data/test/syntax-book/config.yml +35 -0
- data/test/syntax-book/images/ball.png +0 -0
- data/test/syntax-book/images/cover.jpg +0 -0
- data/test/syntax-book/images/fractal.png +0 -0
- data/test/syntax-book/images/img3-1.png +0 -0
- data/test/syntax-book/images/inlineicon.jpg +0 -0
- data/test/syntax-book/images/logic.png +0 -0
- data/test/syntax-book/images/logic2.png +0 -0
- data/test/syntax-book/images/puzzle.jpg +0 -0
- data/test/syntax-book/images/table.jpg +0 -0
- data/test/syntax-book/part2.re +6 -0
- data/test/syntax-book/pre01.re +26 -0
- data/test/syntax-book/review-ext.rb +14 -0
- data/test/syntax-book/sty/jumoline.sty +310 -0
- data/test/syntax-book/sty/reviewmacro.sty +39 -0
- data/test/syntax-book/style.css +494 -0
- data/test/syntax-book/syntax.dic +2 -0
- data/test/test_book.rb +106 -111
- data/test/test_book_chapter.rb +21 -22
- data/test/test_book_part.rb +3 -5
- data/test/test_builder.rb +11 -22
- data/test/test_catalog.rb +17 -18
- data/test/test_catalog_converter_cmd.rb +5 -5
- data/test/test_compiler.rb +18 -16
- data/test/test_configure.rb +35 -38
- data/test/test_converter.rb +3 -4
- data/test/test_epub3maker.rb +136 -117
- data/test/test_epubmaker.rb +107 -114
- data/test/test_epubmaker_cmd.rb +2 -4
- data/test/test_extentions_hash.rb +32 -33
- data/test/test_helper.rb +9 -11
- data/test/test_htmlbuilder.rb +454 -420
- data/test/test_htmltoc.rb +8 -12
- data/test/test_htmlutils.rb +0 -2
- data/test/test_i18n.rb +159 -150
- data/test/test_idgxmlbuilder.rb +190 -197
- data/test/test_image_finder.rb +21 -22
- data/test/test_index.rb +24 -29
- data/test/test_latexbuilder.rb +274 -264
- data/test/test_lineinput.rb +7 -10
- data/test/test_location.rb +7 -7
- data/test/test_makerhelper.rb +13 -25
- data/test/test_markdownbuilder.rb +23 -26
- data/test/test_md2inaobuilder.rb +8 -11
- data/test/test_pdfmaker.rb +114 -123
- data/test/test_pdfmaker_cmd.rb +1 -3
- data/test/test_review_ext.rb +3 -5
- data/test/test_rstbuilder.rb +92 -97
- data/test/test_template.rb +3 -7
- data/test/test_textutils.rb +27 -27
- data/test/test_tocparser.rb +2 -2
- data/test/test_topbuilder.rb +98 -103
- data/test/test_webtocprinter.rb +5 -6
- data/test/test_yamlloader.rb +42 -42
- data/test/test_zip_exporter.rb +12 -18
- metadata +86 -9
- data/lib/review/ewbbuilder.rb +0 -382
data/lib/review/converter.rb
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
|
|
3
1
|
# This program is free software.
|
|
4
2
|
# You can distribute or modify this program under the terms of
|
|
5
3
|
# the GNU LGPL, Lesser General Public License version 2.1.
|
|
@@ -11,7 +9,7 @@ module ReVIEW
|
|
|
11
9
|
|
|
12
10
|
def initialize(book, builder)
|
|
13
11
|
@book = book
|
|
14
|
-
@book.config[
|
|
12
|
+
@book.config['builder'] = builder.target_name
|
|
15
13
|
@compiler = ReVIEW::Compiler.new(builder)
|
|
16
14
|
end
|
|
17
15
|
|
|
@@ -19,10 +17,7 @@ module ReVIEW
|
|
|
19
17
|
chap_name = File.basename(file, '.*')
|
|
20
18
|
chap = @book.chapter(chap_name)
|
|
21
19
|
result = @compiler.compile(chap)
|
|
22
|
-
File.open(output_path, 'w')
|
|
23
|
-
f.puts result
|
|
24
|
-
end
|
|
20
|
+
File.open(output_path, 'w') { |f| f.puts result }
|
|
25
21
|
end
|
|
26
22
|
end
|
|
27
23
|
end
|
|
28
|
-
|
data/lib/review/epubbuilder.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# epubbuilder.rb
|
|
2
2
|
# just for compatibility
|
|
3
3
|
#
|
|
4
|
-
# Copyright (c) 2010 Kenshi Muto
|
|
4
|
+
# Copyright (c) 2010-2017 Kenshi Muto
|
|
5
5
|
#
|
|
6
6
|
# This program is free software.
|
|
7
7
|
# You can distribute or modify this program under the terms of
|
|
@@ -11,8 +11,6 @@
|
|
|
11
11
|
require 'review/htmlbuilder'
|
|
12
12
|
|
|
13
13
|
module ReVIEW
|
|
14
|
-
|
|
15
14
|
class EPUBBuilder < HTMLBuilder
|
|
16
15
|
end
|
|
17
|
-
|
|
18
16
|
end # module ReVIEW
|
data/lib/review/epubmaker.rb
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
|
|
3
1
|
# Copyright (c) 2010-2017 Kenshi Muto and Masayoshi Takahashi
|
|
4
2
|
#
|
|
5
3
|
# This program is free software.
|
|
@@ -33,110 +31,120 @@ module ReVIEW
|
|
|
33
31
|
def initialize
|
|
34
32
|
@producer = nil
|
|
35
33
|
@htmltoc = nil
|
|
36
|
-
@buildlogtxt =
|
|
34
|
+
@buildlogtxt = 'build-log.txt'
|
|
35
|
+
@logger = ReVIEW.logger
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def error(msg)
|
|
39
|
+
@logger.error "#{File.basename($PROGRAM_NAME, '.*')}: #{msg}"
|
|
40
|
+
exit 1
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def warn(msg)
|
|
44
|
+
@logger.warn "#{File.basename($PROGRAM_NAME, '.*')}: #{msg}"
|
|
37
45
|
end
|
|
38
46
|
|
|
39
47
|
def log(s)
|
|
40
|
-
puts s if @
|
|
48
|
+
puts s if @config['debug'].present?
|
|
41
49
|
end
|
|
42
50
|
|
|
43
51
|
def load_yaml(yamlfile)
|
|
44
52
|
loader = ReVIEW::YAMLLoader.new
|
|
45
|
-
@
|
|
46
|
-
@producer = Producer.new(@
|
|
53
|
+
@config = ReVIEW::Configure.values.deep_merge(loader.load_file(yamlfile))
|
|
54
|
+
@producer = Producer.new(@config)
|
|
47
55
|
@producer.load(yamlfile)
|
|
48
|
-
@
|
|
49
|
-
@
|
|
56
|
+
@config = @producer.config
|
|
57
|
+
@config.maker = 'epubmaker'
|
|
50
58
|
end
|
|
51
59
|
|
|
52
60
|
def build_path
|
|
53
|
-
if @
|
|
54
|
-
path = File.expand_path("#{@
|
|
55
|
-
if File.exist?(path)
|
|
56
|
-
FileUtils.rm_rf(path, :secure => true)
|
|
57
|
-
end
|
|
61
|
+
if @config['debug']
|
|
62
|
+
path = File.expand_path("#{@config['bookname']}-epub", Dir.pwd)
|
|
63
|
+
FileUtils.rm_rf(path, secure: true) if File.exist?(path)
|
|
58
64
|
Dir.mkdir(path)
|
|
59
|
-
|
|
65
|
+
path
|
|
60
66
|
else
|
|
61
|
-
|
|
67
|
+
Dir.mktmpdir("#{@config['bookname']}-epub-")
|
|
62
68
|
end
|
|
63
69
|
end
|
|
64
70
|
|
|
65
|
-
def produce(yamlfile, bookname=nil)
|
|
71
|
+
def produce(yamlfile, bookname = nil)
|
|
66
72
|
load_yaml(yamlfile)
|
|
67
|
-
I18n.setup(@
|
|
68
|
-
bookname = @
|
|
73
|
+
I18n.setup(@config['language'])
|
|
74
|
+
bookname = @config['bookname'] if bookname.nil?
|
|
69
75
|
booktmpname = "#{bookname}-epub"
|
|
70
76
|
|
|
71
77
|
begin
|
|
72
|
-
@
|
|
78
|
+
@config.check_version(ReVIEW::VERSION)
|
|
73
79
|
rescue ReVIEW::ConfigError => e
|
|
74
80
|
warn e.message
|
|
75
81
|
end
|
|
76
82
|
log("Loaded yaml file (#{yamlfile}). I will produce #{bookname}.epub.")
|
|
77
83
|
|
|
78
84
|
FileUtils.rm_f("#{bookname}.epub")
|
|
79
|
-
FileUtils.rm_rf(booktmpname) if @
|
|
85
|
+
FileUtils.rm_rf(booktmpname) if @config['debug']
|
|
86
|
+
math_dir = "./#{@config['imagedir']}/_review_math"
|
|
87
|
+
FileUtils.rm_rf(math_dir) if @config['imgmath'] && Dir.exist?(math_dir)
|
|
80
88
|
|
|
81
|
-
basetmpdir = build_path
|
|
89
|
+
basetmpdir = build_path
|
|
82
90
|
begin
|
|
83
91
|
log("Created first temporary directory as #{basetmpdir}.")
|
|
84
92
|
|
|
85
|
-
call_hook(
|
|
93
|
+
call_hook('hook_beforeprocess', basetmpdir)
|
|
86
94
|
|
|
87
95
|
@htmltoc = ReVIEW::HTMLToc.new(basetmpdir)
|
|
88
96
|
## copy all files into basetmpdir
|
|
89
97
|
copy_stylesheet(basetmpdir)
|
|
90
98
|
|
|
91
99
|
copy_frontmatter(basetmpdir)
|
|
92
|
-
call_hook(
|
|
100
|
+
call_hook('hook_afterfrontmatter', basetmpdir)
|
|
93
101
|
|
|
94
102
|
build_body(basetmpdir, yamlfile)
|
|
95
|
-
call_hook(
|
|
103
|
+
call_hook('hook_afterbody', basetmpdir)
|
|
96
104
|
|
|
97
105
|
copy_backmatter(basetmpdir)
|
|
98
|
-
call_hook(
|
|
106
|
+
call_hook('hook_afterbackmatter', basetmpdir)
|
|
99
107
|
|
|
100
108
|
## push contents in basetmpdir into @producer
|
|
101
109
|
push_contents(basetmpdir)
|
|
102
110
|
|
|
103
|
-
if @
|
|
111
|
+
if @config['epubmaker']['verify_target_images'].present?
|
|
104
112
|
verify_target_images(basetmpdir)
|
|
105
|
-
copy_images(@
|
|
113
|
+
copy_images(@config['imagedir'], basetmpdir)
|
|
106
114
|
else
|
|
107
|
-
copy_images(@
|
|
115
|
+
copy_images(@config['imagedir'], "#{basetmpdir}/#{@config['imagedir']}")
|
|
108
116
|
end
|
|
109
117
|
|
|
110
|
-
copy_resources(
|
|
111
|
-
copy_resources(
|
|
112
|
-
copy_resources(@
|
|
118
|
+
copy_resources('covers', "#{basetmpdir}/#{@config['imagedir']}")
|
|
119
|
+
copy_resources('adv', "#{basetmpdir}/#{@config['imagedir']}")
|
|
120
|
+
copy_resources(@config['fontdir'], "#{basetmpdir}/fonts", @config['font_ext'])
|
|
121
|
+
|
|
122
|
+
call_hook('hook_aftercopyimage', basetmpdir)
|
|
113
123
|
|
|
114
|
-
|
|
124
|
+
@producer.import_imageinfo("#{basetmpdir}/#{@config['imagedir']}", basetmpdir)
|
|
125
|
+
@producer.import_imageinfo("#{basetmpdir}/fonts", basetmpdir, @config['font_ext'])
|
|
115
126
|
|
|
116
|
-
|
|
117
|
-
@producer.import_imageinfo("#{basetmpdir}/fonts", basetmpdir, @params["font_ext"])
|
|
127
|
+
check_image_size(basetmpdir, @config['image_maxpixels'], @config['image_ext'])
|
|
118
128
|
|
|
119
129
|
epubtmpdir = nil
|
|
120
|
-
if @
|
|
130
|
+
if @config['debug'].present?
|
|
121
131
|
epubtmpdir = "#{basetmpdir}/#{booktmpname}"
|
|
122
132
|
Dir.mkdir(epubtmpdir)
|
|
123
133
|
end
|
|
124
|
-
log(
|
|
134
|
+
log('Call ePUB producer.')
|
|
125
135
|
@producer.produce("#{bookname}.epub", basetmpdir, epubtmpdir)
|
|
126
|
-
log(
|
|
136
|
+
log('Finished.')
|
|
127
137
|
ensure
|
|
128
|
-
unless @
|
|
129
|
-
FileUtils.remove_entry_secure basetmpdir
|
|
130
|
-
end
|
|
138
|
+
FileUtils.remove_entry_secure basetmpdir unless @config['debug']
|
|
131
139
|
end
|
|
132
140
|
end
|
|
133
141
|
|
|
134
142
|
def call_hook(hook_name, *params)
|
|
135
|
-
filename = @
|
|
143
|
+
filename = @config['epubmaker'][hook_name]
|
|
136
144
|
log("Call #{hook_name}. (#{filename})")
|
|
137
145
|
if filename.present? && File.exist?(filename) && FileTest.executable?(filename)
|
|
138
|
-
if ENV[
|
|
139
|
-
warn
|
|
146
|
+
if ENV['REVIEW_SAFE_MODE'].to_i & 1 > 0
|
|
147
|
+
warn 'hook is prohibited in safe mode. ignored.'
|
|
140
148
|
else
|
|
141
149
|
system(filename, *params)
|
|
142
150
|
end
|
|
@@ -145,35 +153,30 @@ module ReVIEW
|
|
|
145
153
|
|
|
146
154
|
def verify_target_images(basetmpdir)
|
|
147
155
|
@producer.contents.each do |content|
|
|
148
|
-
if content.media ==
|
|
149
|
-
|
|
156
|
+
if content.media == 'application/xhtml+xml'
|
|
150
157
|
File.open("#{basetmpdir}/#{content.file}") do |f|
|
|
151
|
-
Document.new(File.new(f)).each_element(
|
|
152
|
-
@
|
|
153
|
-
if e.attributes[
|
|
154
|
-
content.properties.push("svg")
|
|
155
|
-
end
|
|
158
|
+
Document.new(File.new(f)).each_element('//img') do |e|
|
|
159
|
+
@config['epubmaker']['force_include_images'].push(e.attributes['src'])
|
|
160
|
+
content.properties.push('svg') if e.attributes['src'] =~ /svg\Z/i
|
|
156
161
|
end
|
|
157
162
|
end
|
|
158
|
-
elsif content.media ==
|
|
163
|
+
elsif content.media == 'text/css'
|
|
159
164
|
File.open("#{basetmpdir}/#{content.file}") do |f|
|
|
160
165
|
f.each_line do |l|
|
|
161
|
-
l.scan(/url\((.+?)\)/)
|
|
162
|
-
@params["epubmaker"]["force_include_images"].push($1.strip)
|
|
163
|
-
end
|
|
166
|
+
l.scan(/url\((.+?)\)/) { |_m| @config['epubmaker']['force_include_images'].push($1.strip) }
|
|
164
167
|
end
|
|
165
168
|
end
|
|
166
169
|
end
|
|
167
170
|
end
|
|
168
|
-
@
|
|
171
|
+
@config['epubmaker']['force_include_images'] = @config['epubmaker']['force_include_images'].compact.sort.uniq
|
|
169
172
|
end
|
|
170
173
|
|
|
171
|
-
def copy_images(resdir, destdir, allow_exts=nil)
|
|
174
|
+
def copy_images(resdir, destdir, allow_exts = nil)
|
|
172
175
|
return nil unless File.exist?(resdir)
|
|
173
|
-
allow_exts = @
|
|
176
|
+
allow_exts = @config['image_ext'] if allow_exts.nil?
|
|
174
177
|
FileUtils.mkdir_p(destdir)
|
|
175
|
-
if @
|
|
176
|
-
@
|
|
178
|
+
if @config['epubmaker']['verify_target_images'].present?
|
|
179
|
+
@config['epubmaker']['force_include_images'].each do |file|
|
|
177
180
|
unless File.exist?(file)
|
|
178
181
|
warn "#{file} is not found, skip." if file !~ /\Ahttp[s]?:/
|
|
179
182
|
next
|
|
@@ -188,9 +191,9 @@ module ReVIEW
|
|
|
188
191
|
end
|
|
189
192
|
end
|
|
190
193
|
|
|
191
|
-
def copy_resources(resdir, destdir, allow_exts=nil)
|
|
194
|
+
def copy_resources(resdir, destdir, allow_exts = nil)
|
|
192
195
|
return nil unless File.exist?(resdir)
|
|
193
|
-
allow_exts = @
|
|
196
|
+
allow_exts = @config['image_ext'] if allow_exts.nil?
|
|
194
197
|
FileUtils.mkdir_p(destdir)
|
|
195
198
|
recursive_copy_files(resdir, destdir, allow_exts)
|
|
196
199
|
end
|
|
@@ -201,12 +204,10 @@ module ReVIEW
|
|
|
201
204
|
next if fname.start_with?('.')
|
|
202
205
|
if FileTest.directory?("#{resdir}/#{fname}")
|
|
203
206
|
recursive_copy_files("#{resdir}/#{fname}", "#{destdir}/#{fname}", allow_exts)
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
FileUtils.cp("#{resdir}/#{fname}", destdir)
|
|
209
|
-
end
|
|
207
|
+
elsif fname =~ /\.(#{allow_exts.join('|')})\Z/i
|
|
208
|
+
FileUtils.mkdir_p(destdir)
|
|
209
|
+
log("Copy #{resdir}/#{fname} to the temporary directory.")
|
|
210
|
+
FileUtils.cp("#{resdir}/#{fname}", destdir)
|
|
210
211
|
end
|
|
211
212
|
end
|
|
212
213
|
end
|
|
@@ -215,7 +216,7 @@ module ReVIEW
|
|
|
215
216
|
def check_compile_status
|
|
216
217
|
return unless @compile_errors
|
|
217
218
|
|
|
218
|
-
$stderr.puts
|
|
219
|
+
$stderr.puts 'compile error, No EPUB file output.'
|
|
219
220
|
exit 1
|
|
220
221
|
end
|
|
221
222
|
|
|
@@ -224,28 +225,27 @@ module ReVIEW
|
|
|
224
225
|
@bodycount = 0
|
|
225
226
|
@postcount = 0
|
|
226
227
|
|
|
227
|
-
@manifeststr =
|
|
228
|
-
@ncxstr =
|
|
228
|
+
@manifeststr = ''
|
|
229
|
+
@ncxstr = ''
|
|
229
230
|
@tocdesc = []
|
|
230
231
|
|
|
231
232
|
basedir = File.dirname(yamlfile)
|
|
232
233
|
base_path = Pathname.new(basedir)
|
|
233
234
|
book = ReVIEW::Book.load(basedir)
|
|
234
|
-
book.config = @
|
|
235
|
+
book.config = @config
|
|
235
236
|
@converter = ReVIEW::Converter.new(book, ReVIEW::HTMLBuilder.new)
|
|
236
237
|
@compile_errors = nil
|
|
237
238
|
book.parts.each do |part|
|
|
238
|
-
htmlfile = nil
|
|
239
239
|
if part.name.present?
|
|
240
240
|
if part.file?
|
|
241
241
|
build_chap(part, base_path, basetmpdir, true)
|
|
242
242
|
else
|
|
243
|
-
htmlfile = "part_#{part.number}.#{@
|
|
243
|
+
htmlfile = "part_#{part.number}.#{@config['htmlext']}"
|
|
244
244
|
build_part(part, basetmpdir, htmlfile)
|
|
245
|
-
title = ReVIEW::I18n.t(
|
|
246
|
-
title += ReVIEW::I18n.t(
|
|
247
|
-
@htmltoc.add_item(0, htmlfile, title,
|
|
248
|
-
write_buildlogtxt(basetmpdir, htmlfile,
|
|
245
|
+
title = ReVIEW::I18n.t('part', part.number)
|
|
246
|
+
title += ReVIEW::I18n.t('chapter_postfix') + part.name.strip if part.name.strip.present?
|
|
247
|
+
@htmltoc.add_item(0, htmlfile, title, chaptype: 'part')
|
|
248
|
+
write_buildlogtxt(basetmpdir, htmlfile, '')
|
|
249
249
|
end
|
|
250
250
|
end
|
|
251
251
|
|
|
@@ -253,22 +253,22 @@ module ReVIEW
|
|
|
253
253
|
build_chap(chap, base_path, basetmpdir, false)
|
|
254
254
|
end
|
|
255
255
|
end
|
|
256
|
-
check_compile_status
|
|
256
|
+
check_compile_status
|
|
257
257
|
end
|
|
258
258
|
|
|
259
259
|
def build_part(part, basetmpdir, htmlfile)
|
|
260
260
|
log("Create #{htmlfile} from a template.")
|
|
261
|
-
File.open("#{basetmpdir}/#{htmlfile}",
|
|
262
|
-
@body =
|
|
263
|
-
@body <<
|
|
264
|
-
@body <<
|
|
261
|
+
File.open("#{basetmpdir}/#{htmlfile}", 'w') do |f|
|
|
262
|
+
@body = ''
|
|
263
|
+
@body << %Q(<div class="part">\n)
|
|
264
|
+
@body << %Q(<h1 class="part-number">#{CGI.escapeHTML(ReVIEW::I18n.t('part', part.number))}</h1>\n)
|
|
265
265
|
if part.name.strip.present?
|
|
266
|
-
@body <<
|
|
266
|
+
@body << %Q(<h2 class="part-title">#{CGI.escapeHTML(part.name.strip)}</h2>\n)
|
|
267
267
|
end
|
|
268
|
-
@body <<
|
|
268
|
+
@body << %Q(</div>\n)
|
|
269
269
|
|
|
270
|
-
@language = @producer.
|
|
271
|
-
@stylesheets = @producer.
|
|
270
|
+
@language = @producer.config['language']
|
|
271
|
+
@stylesheets = @producer.config['stylesheet']
|
|
272
272
|
tmplfile = File.expand_path(template_name, ReVIEW::Template::TEMPLATE_DIR)
|
|
273
273
|
tmpl = ReVIEW::Template.load(tmplfile)
|
|
274
274
|
f.write tmpl.result(binding)
|
|
@@ -276,7 +276,7 @@ module ReVIEW
|
|
|
276
276
|
end
|
|
277
277
|
|
|
278
278
|
def template_name
|
|
279
|
-
if @producer.
|
|
279
|
+
if @producer.config['htmlversion'].to_i == 5
|
|
280
280
|
'./html/layout-html5.html.erb'
|
|
281
281
|
else
|
|
282
282
|
'./html/layout-xhtml1.html.erb'
|
|
@@ -284,45 +284,45 @@ module ReVIEW
|
|
|
284
284
|
end
|
|
285
285
|
|
|
286
286
|
def build_chap(chap, base_path, basetmpdir, ispart)
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
chaptype = "body"
|
|
287
|
+
chaptype = 'body'
|
|
290
288
|
if ispart
|
|
291
|
-
chaptype =
|
|
292
|
-
elsif chap.
|
|
293
|
-
chaptype =
|
|
294
|
-
elsif chap.
|
|
295
|
-
chaptype =
|
|
289
|
+
chaptype = 'part'
|
|
290
|
+
elsif chap.on_predef?
|
|
291
|
+
chaptype = 'pre'
|
|
292
|
+
elsif chap.on_appendix?
|
|
293
|
+
chaptype = 'post'
|
|
296
294
|
end
|
|
297
295
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
296
|
+
filename =
|
|
297
|
+
if ispart.present?
|
|
298
|
+
chap.path
|
|
299
|
+
else
|
|
300
|
+
Pathname.new(chap.path).relative_path_from(base_path).to_s
|
|
301
|
+
end
|
|
304
302
|
|
|
305
|
-
|
|
306
|
-
|
|
303
|
+
id = filename.sub(/\.re\Z/, '')
|
|
304
|
+
|
|
305
|
+
if @config['epubmaker']['rename_for_legacy'] && ispart.nil?
|
|
306
|
+
if chap.on_predef?
|
|
307
307
|
@precount += 1
|
|
308
|
-
id = sprintf(
|
|
309
|
-
elsif chap.
|
|
308
|
+
id = sprintf('pre%02d', @precount)
|
|
309
|
+
elsif chap.on_appendix?
|
|
310
310
|
@postcount += 1
|
|
311
|
-
id = sprintf(
|
|
311
|
+
id = sprintf('post%02d', @postcount)
|
|
312
312
|
else
|
|
313
313
|
@bodycount += 1
|
|
314
|
-
id = sprintf(
|
|
314
|
+
id = sprintf('chap%02d', @bodycount)
|
|
315
315
|
end
|
|
316
316
|
end
|
|
317
317
|
|
|
318
|
-
htmlfile = "#{id}.#{@
|
|
318
|
+
htmlfile = "#{id}.#{@config['htmlext']}"
|
|
319
319
|
write_buildlogtxt(basetmpdir, htmlfile, filename)
|
|
320
320
|
log("Create #{htmlfile} from #{filename}.")
|
|
321
321
|
|
|
322
|
-
if @
|
|
323
|
-
warn
|
|
324
|
-
if @
|
|
325
|
-
warn
|
|
322
|
+
if @config['params'].present?
|
|
323
|
+
warn %Q('params:' in config.yml is obsoleted.)
|
|
324
|
+
if @config['params'] =~ /stylesheet=/
|
|
325
|
+
warn %Q(stylesheets should be defined in 'stylesheet:', not in 'params:')
|
|
326
326
|
end
|
|
327
327
|
end
|
|
328
328
|
begin
|
|
@@ -337,10 +337,10 @@ module ReVIEW
|
|
|
337
337
|
end
|
|
338
338
|
|
|
339
339
|
def remove_hidden_title(basetmpdir, htmlfile)
|
|
340
|
-
File.open("#{basetmpdir}/#{htmlfile}",
|
|
340
|
+
File.open("#{basetmpdir}/#{htmlfile}", 'r+') do |f|
|
|
341
341
|
body = f.read.
|
|
342
|
-
gsub(
|
|
343
|
-
gsub(
|
|
342
|
+
gsub(%r{<h\d .*?hidden=['"]true['"].*?>.*?</h\d>\n}, '').
|
|
343
|
+
gsub(%r{(<h\d .*?)\s*notoc=['"]true['"]\s*(.*?>.*?</h\d>\n)}, '\1\2')
|
|
344
344
|
f.rewind
|
|
345
345
|
f.print body
|
|
346
346
|
f.truncate(f.tell)
|
|
@@ -351,108 +351,95 @@ module ReVIEW
|
|
|
351
351
|
properties = []
|
|
352
352
|
File.open(path) do |f|
|
|
353
353
|
doc = REXML::Document.new(f)
|
|
354
|
-
if REXML::XPath.first(doc,
|
|
355
|
-
properties<<
|
|
354
|
+
if REXML::XPath.first(doc, '//m:math', 'm' => 'http://www.w3.org/1998/Math/MathML')
|
|
355
|
+
properties << 'mathml'
|
|
356
356
|
end
|
|
357
|
-
if REXML::XPath.first(doc,
|
|
358
|
-
properties<<
|
|
357
|
+
if REXML::XPath.first(doc, '//s:svg', 's' => 'http://www.w3.org/2000/svg')
|
|
358
|
+
properties << 'svg'
|
|
359
359
|
end
|
|
360
360
|
end
|
|
361
361
|
properties
|
|
362
362
|
end
|
|
363
363
|
|
|
364
|
-
def write_info_body(basetmpdir,
|
|
364
|
+
def write_info_body(basetmpdir, _id, filename, ispart = nil, chaptype = nil)
|
|
365
365
|
headlines = []
|
|
366
366
|
path = File.join(basetmpdir, filename)
|
|
367
367
|
Document.parse_stream(File.new(path), ReVIEWHeaderListener.new(headlines))
|
|
368
368
|
properties = detect_properties(path)
|
|
369
|
-
prop_str =
|
|
370
|
-
if properties.present?
|
|
371
|
-
prop_str = ",properties="+properties.join(" ")
|
|
372
|
-
end
|
|
369
|
+
prop_str = ''
|
|
370
|
+
prop_str = ',properties=' + properties.join(' ') if properties.present?
|
|
373
371
|
first = true
|
|
374
372
|
headlines.each do |headline|
|
|
375
|
-
headline[
|
|
373
|
+
headline['level'] = 0 if ispart.present? && headline['level'] == 1
|
|
376
374
|
if first.nil?
|
|
377
|
-
@htmltoc.add_item(headline[
|
|
375
|
+
@htmltoc.add_item(headline['level'], filename + '#' + headline['id'], headline['title'], { chaptype: chaptype, notoc: headline['notoc'] })
|
|
378
376
|
else
|
|
379
|
-
@htmltoc.add_item(headline[
|
|
377
|
+
@htmltoc.add_item(headline['level'], filename, headline['title'], { force_include: true, chaptype: chaptype + prop_str, notoc: headline['notoc'] })
|
|
380
378
|
first = nil
|
|
381
379
|
end
|
|
382
380
|
end
|
|
383
381
|
end
|
|
384
382
|
|
|
385
|
-
def push_contents(
|
|
383
|
+
def push_contents(_basetmpdir)
|
|
386
384
|
@htmltoc.each_item do |level, file, title, args|
|
|
387
|
-
next if level.to_i > @
|
|
385
|
+
next if level.to_i > @config['toclevel'] && args[:force_include].nil?
|
|
388
386
|
log("Push #{file} to ePUB contents.")
|
|
389
387
|
|
|
390
|
-
hash = {
|
|
391
|
-
if args[:id].present?
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
if args[:properties].present?
|
|
395
|
-
hash["properties"] = args[:properties].split(" ")
|
|
396
|
-
end
|
|
397
|
-
if args[:notoc].present?
|
|
398
|
-
hash["notoc"] = args[:notoc]
|
|
399
|
-
end
|
|
388
|
+
hash = { 'file' => file, 'level' => level.to_i, 'title' => title, 'chaptype' => args[:chaptype] }
|
|
389
|
+
hash['id'] = args[:id] if args[:id].present?
|
|
390
|
+
hash['properties'] = args[:properties].split(' ') if args[:properties].present?
|
|
391
|
+
hash['notoc'] = args[:notoc] if args[:notoc].present?
|
|
400
392
|
@producer.contents.push(Content.new(hash))
|
|
401
393
|
end
|
|
402
394
|
end
|
|
403
395
|
|
|
404
396
|
def copy_stylesheet(basetmpdir)
|
|
405
|
-
if @
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
end
|
|
397
|
+
return if @config['stylesheet'].empty?
|
|
398
|
+
@config['stylesheet'].each do |sfile|
|
|
399
|
+
FileUtils.cp(sfile, basetmpdir)
|
|
400
|
+
@producer.contents.push(Content.new('file' => sfile))
|
|
410
401
|
end
|
|
411
402
|
end
|
|
412
403
|
|
|
413
404
|
def copy_frontmatter(basetmpdir)
|
|
414
|
-
FileUtils.cp(@
|
|
405
|
+
FileUtils.cp(@config['cover'], "#{basetmpdir}/#{File.basename(@config['cover'])}") if @config['cover'].present? && File.exist?(@config['cover'])
|
|
415
406
|
|
|
416
|
-
if @
|
|
417
|
-
if @
|
|
418
|
-
build_titlepage(basetmpdir, "titlepage.#{@
|
|
407
|
+
if @config['titlepage']
|
|
408
|
+
if @config['titlefile'].nil?
|
|
409
|
+
build_titlepage(basetmpdir, "titlepage.#{@config['htmlext']}")
|
|
419
410
|
else
|
|
420
|
-
FileUtils.cp(@
|
|
411
|
+
FileUtils.cp(@config['titlefile'], "#{basetmpdir}/titlepage.#{@config['htmlext']}")
|
|
421
412
|
end
|
|
422
|
-
@htmltoc.add_item(1, "titlepage.#{@
|
|
413
|
+
@htmltoc.add_item(1, "titlepage.#{@config['htmlext']}", @producer.res.v('titlepagetitle'), chaptype: 'pre')
|
|
423
414
|
end
|
|
424
415
|
|
|
425
|
-
if @
|
|
426
|
-
FileUtils.cp(@
|
|
427
|
-
@htmltoc.add_item(1, File.basename(@
|
|
416
|
+
if @config['originaltitlefile'].present? && File.exist?(@config['originaltitlefile'])
|
|
417
|
+
FileUtils.cp(@config['originaltitlefile'], "#{basetmpdir}/#{File.basename(@config['originaltitlefile'])}")
|
|
418
|
+
@htmltoc.add_item(1, File.basename(@config['originaltitlefile']), @producer.res.v('originaltitle'), chaptype: 'pre')
|
|
428
419
|
end
|
|
429
420
|
|
|
430
|
-
if @
|
|
431
|
-
FileUtils.cp(@
|
|
432
|
-
@htmltoc.add_item(1, File.basename(@
|
|
421
|
+
if @config['creditfile'].present? && File.exist?(@config['creditfile'])
|
|
422
|
+
FileUtils.cp(@config['creditfile'], "#{basetmpdir}/#{File.basename(@config['creditfile'])}")
|
|
423
|
+
@htmltoc.add_item(1, File.basename(@config['creditfile']), @producer.res.v('credittitle'), chaptype: 'pre')
|
|
433
424
|
end
|
|
425
|
+
|
|
426
|
+
true
|
|
434
427
|
end
|
|
435
428
|
|
|
436
429
|
def build_titlepage(basetmpdir, htmlfile)
|
|
437
430
|
# TODO: should be created via epubcommon
|
|
438
|
-
@title = CGI.escapeHTML(@
|
|
439
|
-
File.open("#{basetmpdir}/#{htmlfile}",
|
|
440
|
-
@body =
|
|
441
|
-
@body <<
|
|
442
|
-
@body <<
|
|
443
|
-
if @
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
@body << "<h3 class=\"tp-publisher\">#{CGI.escapeHTML(@params.names_of("prt").join(ReVIEW::I18n.t("names_splitter")))}</h3>\n"
|
|
451
|
-
end
|
|
452
|
-
@body << "</div>"
|
|
453
|
-
|
|
454
|
-
@language = @producer.params['language']
|
|
455
|
-
@stylesheets = @producer.params["stylesheet"]
|
|
431
|
+
@title = CGI.escapeHTML(@config.name_of('booktitle'))
|
|
432
|
+
File.open("#{basetmpdir}/#{htmlfile}", 'w') do |f|
|
|
433
|
+
@body = ''
|
|
434
|
+
@body << %Q(<div class="titlepage">\n)
|
|
435
|
+
@body << %Q(<h1 class="tp-title">#{CGI.escapeHTML(@config.name_of('booktitle'))}</h1>\n)
|
|
436
|
+
@body << %Q(<h2 class="tp-subtitle">#{CGI.escapeHTML(@config.name_of('subtitle'))}</h2>\n) if @config['subtitle']
|
|
437
|
+
@body << %Q(<h2 class="tp-author">#{CGI.escapeHTML(@config.names_of('aut').join(ReVIEW::I18n.t('names_splitter')))}</h2>\n) if @config['aut']
|
|
438
|
+
@body << %Q(<h3 class="tp-publisher">#{CGI.escapeHTML(@config.names_of('prt').join(ReVIEW::I18n.t('names_splitter')))}</h3>\n) if @config['prt']
|
|
439
|
+
@body << '</div>'
|
|
440
|
+
|
|
441
|
+
@language = @producer.config['language']
|
|
442
|
+
@stylesheets = @producer.config['stylesheet']
|
|
456
443
|
tmplfile = File.expand_path(template_name, ReVIEW::Template::TEMPLATE_DIR)
|
|
457
444
|
tmpl = ReVIEW::Template.load(tmplfile)
|
|
458
445
|
f.write tmpl.result(binding)
|
|
@@ -460,76 +447,97 @@ module ReVIEW
|
|
|
460
447
|
end
|
|
461
448
|
|
|
462
449
|
def copy_backmatter(basetmpdir)
|
|
463
|
-
if @
|
|
464
|
-
FileUtils.cp(@
|
|
465
|
-
@htmltoc.add_item(1, File.basename(@
|
|
450
|
+
if @config['profile']
|
|
451
|
+
FileUtils.cp(@config['profile'], "#{basetmpdir}/#{File.basename(@config['profile'])}")
|
|
452
|
+
@htmltoc.add_item(1, File.basename(@config['profile']), @producer.res.v('profiletitle'), chaptype: 'post')
|
|
466
453
|
end
|
|
467
454
|
|
|
468
|
-
if @
|
|
469
|
-
FileUtils.cp(@
|
|
470
|
-
@htmltoc.add_item(1, File.basename(@
|
|
455
|
+
if @config['advfile']
|
|
456
|
+
FileUtils.cp(@config['advfile'], "#{basetmpdir}/#{File.basename(@config['advfile'])}")
|
|
457
|
+
@htmltoc.add_item(1, File.basename(@config['advfile']), @producer.res.v('advtitle'), chaptype: 'post')
|
|
471
458
|
end
|
|
472
459
|
|
|
473
|
-
if @
|
|
474
|
-
if @
|
|
475
|
-
FileUtils.cp(@
|
|
460
|
+
if @config['colophon']
|
|
461
|
+
if @config['colophon'].is_a?(String) # FIXME: should let obsolete this style?
|
|
462
|
+
FileUtils.cp(@config['colophon'], "#{basetmpdir}/colophon.#{@config['htmlext']}")
|
|
476
463
|
else
|
|
477
|
-
File.open("#{basetmpdir}/colophon.#{@
|
|
464
|
+
File.open("#{basetmpdir}/colophon.#{@config['htmlext']}", 'w') { |f| @producer.colophon(f) }
|
|
478
465
|
end
|
|
479
|
-
@htmltoc.add_item(1, "colophon.#{@
|
|
466
|
+
@htmltoc.add_item(1, "colophon.#{@config['htmlext']}", @producer.res.v('colophontitle'), chaptype: 'post')
|
|
480
467
|
end
|
|
481
468
|
|
|
482
|
-
if @
|
|
483
|
-
FileUtils.cp(@
|
|
484
|
-
@htmltoc.add_item(1, File.basename(@
|
|
469
|
+
if @config['backcover']
|
|
470
|
+
FileUtils.cp(@config['backcover'], "#{basetmpdir}/#{File.basename(@config['backcover'])}")
|
|
471
|
+
@htmltoc.add_item(1, File.basename(@config['backcover']), @producer.res.v('backcovertitle'), chaptype: 'post')
|
|
485
472
|
end
|
|
473
|
+
|
|
474
|
+
true
|
|
486
475
|
end
|
|
487
476
|
|
|
488
477
|
def write_buildlogtxt(basetmpdir, htmlfile, reviewfile)
|
|
489
|
-
File.open("#{basetmpdir}/#{@buildlogtxt}",
|
|
490
|
-
|
|
478
|
+
File.open("#{basetmpdir}/#{@buildlogtxt}", 'a') { |f| f.puts "#{htmlfile},#{reviewfile}" }
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
def check_image_size(basetmpdir, maxpixels, allow_exts = nil)
|
|
482
|
+
begin
|
|
483
|
+
require 'image_size'
|
|
484
|
+
rescue LoadError
|
|
485
|
+
return nil
|
|
486
|
+
end
|
|
487
|
+
require 'find'
|
|
488
|
+
allow_exts ||= @config['image_ext']
|
|
489
|
+
|
|
490
|
+
extre = Regexp.new('\\.(' + allow_exts.delete_if { |t| %w[ttf woff otf].include?(t) }.join('|') + ')', Regexp::IGNORECASE)
|
|
491
|
+
Find.find(basetmpdir) do |fname|
|
|
492
|
+
next unless fname.match(extre)
|
|
493
|
+
img = ImageSize.path(fname)
|
|
494
|
+
next if img.width.nil? || img.width * img.height <= maxpixels
|
|
495
|
+
h = Math.sqrt(img.height * maxpixels / img.width)
|
|
496
|
+
w = maxpixels / h
|
|
497
|
+
fname.sub!("#{basetmpdir}/", '')
|
|
498
|
+
warn "#{fname}: #{img.width}x#{img.height} exceeds a limit. suggeted value is #{w.to_i}x#{h.to_i}"
|
|
491
499
|
end
|
|
500
|
+
|
|
501
|
+
true
|
|
492
502
|
end
|
|
493
503
|
|
|
494
504
|
class ReVIEWHeaderListener
|
|
495
505
|
include REXML::StreamListener
|
|
496
506
|
def initialize(headlines)
|
|
497
507
|
@level = nil
|
|
498
|
-
@content =
|
|
508
|
+
@content = ''
|
|
499
509
|
@headlines = headlines
|
|
500
510
|
end
|
|
501
511
|
|
|
502
512
|
def tag_start(name, attrs)
|
|
503
513
|
if name =~ /\Ah(\d+)/
|
|
504
|
-
if @level.present?
|
|
505
|
-
raise "#{name}, #{attrs}"
|
|
506
|
-
end
|
|
514
|
+
raise "#{name}, #{attrs}" if @level.present?
|
|
507
515
|
@level = $1.to_i
|
|
508
|
-
@id = attrs[
|
|
509
|
-
@notoc = attrs[
|
|
516
|
+
@id = attrs['id'] if attrs['id'].present?
|
|
517
|
+
@notoc = attrs['notoc'] if attrs['notoc'].present?
|
|
510
518
|
elsif !@level.nil?
|
|
511
|
-
if name ==
|
|
512
|
-
@content << attrs[
|
|
513
|
-
elsif name ==
|
|
514
|
-
@id = attrs[
|
|
519
|
+
if name == 'img' && attrs['alt'].present?
|
|
520
|
+
@content << attrs['alt']
|
|
521
|
+
elsif name == 'a' && attrs['id'].present?
|
|
522
|
+
@id = attrs['id']
|
|
515
523
|
end
|
|
516
524
|
end
|
|
517
525
|
end
|
|
518
526
|
|
|
519
527
|
def tag_end(name)
|
|
520
528
|
if name =~ /\Ah\d+/
|
|
521
|
-
@headlines.push({
|
|
522
|
-
@content =
|
|
529
|
+
@headlines.push({ 'level' => @level, 'id' => @id, 'title' => @content, 'notoc' => @notoc }) if @id.present?
|
|
530
|
+
@content = ''
|
|
523
531
|
@level = nil
|
|
524
532
|
@id = nil
|
|
525
533
|
@notoc = nil
|
|
526
534
|
end
|
|
535
|
+
|
|
536
|
+
true
|
|
527
537
|
end
|
|
528
538
|
|
|
529
539
|
def text(text)
|
|
530
|
-
if @level.present?
|
|
531
|
-
@content << text.gsub("\t", " ") # FIXME: 区切り文字
|
|
532
|
-
end
|
|
540
|
+
@content << text.gsub("\t", ' ') if @level.present?
|
|
533
541
|
end
|
|
534
542
|
end
|
|
535
543
|
end
|