review 2.1.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +5 -0
- data/.rubocop.yml +293 -6
- data/.rubocop_todo.yml +3 -608
- data/.travis.yml +6 -13
- data/README.md +5 -3
- data/Rakefile +6 -6
- data/bin/review-catalog-converter +2 -2
- data/bin/review-check +1 -1
- data/bin/review-compile +1 -2
- data/bin/review-init +6 -3
- data/bin/review-validate +3 -3
- data/bin/review-vol +2 -1
- data/doc/NEWS.ja.md +138 -25
- data/doc/NEWS.md +137 -25
- data/doc/config.yml.sample +2 -2
- data/doc/config.yml.sample-simple +1 -1
- data/doc/format.ja.md +86 -5
- data/doc/format.md +67 -2
- data/doc/makeindex.ja.md +95 -0
- data/doc/makeindex.md +97 -0
- data/doc/sample.css +214 -0
- data/lib/epubmaker.rb +6 -6
- data/lib/epubmaker/epubcommon.rb +19 -47
- data/lib/epubmaker/epubv2.rb +3 -1
- data/lib/epubmaker/epubv3.rb +4 -26
- data/lib/epubmaker/producer.rb +46 -46
- data/lib/epubmaker/zip_exporter.rb +86 -0
- data/lib/review/book/base.rb +13 -15
- data/lib/review/book/chapter.rb +2 -1
- data/lib/review/book/compilable.rb +9 -9
- data/lib/review/book/image_finder.rb +13 -13
- data/lib/review/book/index.rb +2 -2
- data/lib/review/book/volume.rb +2 -2
- data/lib/review/builder.rb +57 -1
- data/lib/review/catalog.rb +2 -2
- data/lib/review/compiler.rb +15 -7
- data/lib/review/configure.rb +11 -0
- data/lib/review/epubmaker.rb +403 -401
- data/lib/review/ewbbuilder.rb +16 -16
- data/lib/review/htmlbuilder.rb +42 -58
- data/lib/review/htmltoc.rb +1 -1
- data/lib/review/htmlutils.rb +50 -4
- data/lib/review/i18n.rb +2 -2
- data/lib/review/idgxmlbuilder.rb +30 -47
- data/lib/review/latexbuilder.rb +86 -41
- data/lib/review/latexutils.rb +19 -19
- data/lib/review/markdownbuilder.rb +16 -4
- data/lib/review/md2inaobuilder.rb +0 -9
- data/lib/review/pdfmaker.rb +91 -48
- data/lib/review/preprocessor.rb +1 -1
- data/lib/review/rstbuilder.rb +763 -0
- data/lib/review/sec_counter.rb +7 -9
- data/lib/review/tocparser.rb +3 -3
- data/lib/review/tocprinter.rb +5 -5
- data/lib/review/topbuilder.rb +48 -56
- data/lib/review/version.rb +1 -1
- data/lib/review/webmaker.rb +6 -7
- data/review.gemspec +1 -0
- data/templates/latex/layout.tex.erb +27 -2
- data/test/assets/test_template.tex +10 -1
- data/test/book_test_helper.rb +1 -2
- data/test/run_test.rb +10 -0
- data/test/sample-book/src/style.css +215 -0
- data/test/sample-book/src/vendor/jumoline/lppl.txt +416 -0
- data/test/test_book.rb +0 -1
- data/test/test_catalog.rb +1 -0
- data/test/test_converter.rb +1 -1
- data/test/test_epub3maker.rb +44 -51
- data/test/test_epubmaker.rb +82 -38
- data/test/test_epubmaker_cmd.rb +1 -1
- data/test/test_extentions_hash.rb +8 -1
- data/test/test_htmlbuilder.rb +411 -18
- data/test/test_i18n.rb +17 -0
- data/test/test_idgxmlbuilder.rb +88 -3
- data/test/test_image_finder.rb +18 -0
- data/test/test_index.rb +2 -0
- data/test/test_latexbuilder.rb +96 -8
- data/test/test_makerhelper.rb +2 -2
- data/test/test_markdownbuilder.rb +22 -1
- data/test/test_md2inaobuilder.rb +0 -5
- data/test/test_pdfmaker.rb +54 -36
- data/test/test_pdfmaker_cmd.rb +1 -1
- data/test/test_rstbuilder.rb +356 -0
- data/test/test_textutils.rb +14 -4
- data/test/test_topbuilder.rb +23 -4
- data/test/test_zip_exporter.rb +113 -0
- metadata +28 -2
data/lib/review/configure.rb
CHANGED
@@ -65,11 +65,22 @@ module ReVIEW
|
|
65
65
|
"chapref" => nil, # for IDGXML
|
66
66
|
"structuredxml" => nil, # for IDGXML
|
67
67
|
"pt_to_mm_unit" => 0.3528, # for IDGXML (DTP: 1pt = 0.3528mm, JIS: 1pt = 0.3514mm)
|
68
|
+
|
68
69
|
"footnotetext" => nil, # for LaTeX
|
69
70
|
"texcommand" => "uplatex", # for LaTeX
|
70
71
|
"texdocumentclass" => ["jsbook", "uplatex,oneside"], # for LaTeX
|
71
72
|
"dvicommand" => "dvipdfmx", # for LaTeX
|
72
73
|
"dvioptions" => "-d 5", # for LaTeX
|
74
|
+
|
75
|
+
"pdfmaker" => {
|
76
|
+
"makeindex" => nil, # Make index page
|
77
|
+
"makeindex_command" => "mendex", # works only when makeindex is true
|
78
|
+
"makeindex_options" => "-f -r -I utf8",
|
79
|
+
"makeindex_sty" => nil,
|
80
|
+
"makeindex_dic" => nil,
|
81
|
+
"makeindex_mecab" => true,
|
82
|
+
"makeindex_mecab_opts" => "-Oyomi",
|
83
|
+
},
|
73
84
|
]
|
74
85
|
conf.maker = nil
|
75
86
|
conf
|
data/lib/review/epubmaker.rb
CHANGED
@@ -26,509 +26,511 @@ require 'rexml/streamlistener'
|
|
26
26
|
require 'epubmaker'
|
27
27
|
|
28
28
|
module ReVIEW
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
def initialize
|
34
|
-
@producer = nil
|
35
|
-
@htmltoc = nil
|
36
|
-
@buildlogtxt = "build-log.txt"
|
37
|
-
end
|
29
|
+
class EPUBMaker
|
30
|
+
include ::EPUBMaker
|
31
|
+
include REXML
|
38
32
|
|
39
|
-
|
40
|
-
|
41
|
-
|
33
|
+
def initialize
|
34
|
+
@producer = nil
|
35
|
+
@htmltoc = nil
|
36
|
+
@buildlogtxt = "build-log.txt"
|
37
|
+
end
|
42
38
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
39
|
+
def log(s)
|
40
|
+
puts s if @params["debug"].present?
|
41
|
+
end
|
42
|
+
|
43
|
+
def load_yaml(yamlfile)
|
44
|
+
loader = ReVIEW::YAMLLoader.new
|
45
|
+
@params = ReVIEW::Configure.values.deep_merge(loader.load_file(yamlfile))
|
46
|
+
@producer = Producer.new(@params)
|
47
|
+
@producer.load(yamlfile)
|
48
|
+
@params = @producer.params
|
49
|
+
@params.maker = "epubmaker"
|
50
|
+
end
|
51
51
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
52
|
+
def build_path
|
53
|
+
if @params["debug"]
|
54
|
+
path = File.expand_path("#{@params["bookname"]}-epub", Dir.pwd)
|
55
|
+
if File.exist?(path)
|
56
|
+
FileUtils.rm_rf(path, :secure => true)
|
57
|
+
end
|
58
|
+
Dir.mkdir(path)
|
59
|
+
return path
|
60
|
+
else
|
61
|
+
return Dir.mktmpdir("#{@params["bookname"]}-epub-")
|
57
62
|
end
|
58
|
-
Dir.mkdir(path)
|
59
|
-
return path
|
60
|
-
else
|
61
|
-
return Dir.mktmpdir("#{@params["bookname"]}-epub-")
|
62
63
|
end
|
63
|
-
end
|
64
64
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
65
|
+
def produce(yamlfile, bookname=nil)
|
66
|
+
load_yaml(yamlfile)
|
67
|
+
I18n.setup(@params["language"])
|
68
|
+
bookname = @params["bookname"] if bookname.nil?
|
69
|
+
booktmpname = "#{bookname}-epub"
|
70
70
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
71
|
+
begin
|
72
|
+
@params.check_version(ReVIEW::VERSION)
|
73
|
+
rescue ReVIEW::ConfigError => e
|
74
|
+
warn e.message
|
75
|
+
end
|
76
|
+
log("Loaded yaml file (#{yamlfile}). I will produce #{bookname}.epub.")
|
77
77
|
|
78
|
-
|
79
|
-
|
78
|
+
FileUtils.rm_f("#{bookname}.epub")
|
79
|
+
FileUtils.rm_rf(booktmpname) if @params["debug"]
|
80
80
|
|
81
|
-
|
82
|
-
|
83
|
-
|
81
|
+
basetmpdir = build_path()
|
82
|
+
begin
|
83
|
+
log("Created first temporary directory as #{basetmpdir}.")
|
84
84
|
|
85
|
-
|
85
|
+
call_hook("hook_beforeprocess", basetmpdir)
|
86
86
|
|
87
|
-
|
88
|
-
|
89
|
-
|
87
|
+
@htmltoc = ReVIEW::HTMLToc.new(basetmpdir)
|
88
|
+
## copy all files into basetmpdir
|
89
|
+
copy_stylesheet(basetmpdir)
|
90
90
|
|
91
|
-
|
92
|
-
|
91
|
+
copy_frontmatter(basetmpdir)
|
92
|
+
call_hook("hook_afterfrontmatter", basetmpdir)
|
93
93
|
|
94
|
-
|
95
|
-
|
94
|
+
build_body(basetmpdir, yamlfile)
|
95
|
+
call_hook("hook_afterbody", basetmpdir)
|
96
96
|
|
97
|
-
|
98
|
-
|
97
|
+
copy_backmatter(basetmpdir)
|
98
|
+
call_hook("hook_afterbackmatter", basetmpdir)
|
99
99
|
|
100
|
-
|
101
|
-
|
100
|
+
## push contents in basetmpdir into @producer
|
101
|
+
push_contents(basetmpdir)
|
102
102
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
103
|
+
if @params["epubmaker"]["verify_target_images"].present?
|
104
|
+
verify_target_images(basetmpdir)
|
105
|
+
copy_images(@params["imagedir"], basetmpdir)
|
106
|
+
else
|
107
|
+
copy_images(@params["imagedir"], "#{basetmpdir}/#{@params["imagedir"]}")
|
108
|
+
end
|
109
109
|
|
110
|
-
|
111
|
-
|
112
|
-
|
110
|
+
copy_resources("covers", "#{basetmpdir}/#{@params["imagedir"]}")
|
111
|
+
copy_resources("adv", "#{basetmpdir}/#{@params["imagedir"]}")
|
112
|
+
copy_resources(@params["fontdir"], "#{basetmpdir}/fonts", @params["font_ext"])
|
113
113
|
|
114
|
-
|
114
|
+
call_hook("hook_aftercopyimage", basetmpdir)
|
115
115
|
|
116
|
-
|
117
|
-
|
116
|
+
@producer.import_imageinfo("#{basetmpdir}/#{@params["imagedir"]}", basetmpdir)
|
117
|
+
@producer.import_imageinfo("#{basetmpdir}/fonts", basetmpdir, @params["font_ext"])
|
118
118
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
119
|
+
epubtmpdir = nil
|
120
|
+
if @params["debug"].present?
|
121
|
+
epubtmpdir = "#{basetmpdir}/#{booktmpname}"
|
122
|
+
Dir.mkdir(epubtmpdir)
|
123
|
+
end
|
124
|
+
log("Call ePUB producer.")
|
125
|
+
@producer.produce("#{bookname}.epub", basetmpdir, epubtmpdir)
|
126
|
+
log("Finished.")
|
127
|
+
ensure
|
128
|
+
unless @params["debug"]
|
129
|
+
FileUtils.remove_entry_secure basetmpdir
|
130
|
+
end
|
130
131
|
end
|
131
132
|
end
|
132
|
-
end
|
133
133
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
134
|
+
def call_hook(hook_name, *params)
|
135
|
+
filename = @params["epubmaker"][hook_name]
|
136
|
+
log("Call #{hook_name}. (#{filename})")
|
137
|
+
if filename.present? && File.exist?(filename) && FileTest.executable?(filename)
|
138
|
+
if ENV["REVIEW_SAFE_MODE"].to_i & 1 > 0
|
139
|
+
warn "hook is prohibited in safe mode. ignored."
|
140
|
+
else
|
141
|
+
system(filename, *params)
|
142
|
+
end
|
142
143
|
end
|
143
144
|
end
|
144
|
-
end
|
145
145
|
|
146
|
-
|
147
|
-
|
148
|
-
|
146
|
+
def verify_target_images(basetmpdir)
|
147
|
+
@producer.contents.each do |content|
|
148
|
+
if content.media == "application/xhtml+xml"
|
149
149
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
150
|
+
File.open("#{basetmpdir}/#{content.file}") do |f|
|
151
|
+
Document.new(File.new(f)).each_element("//img") do |e|
|
152
|
+
@params["epubmaker"]["force_include_images"].push(e.attributes["src"])
|
153
|
+
if e.attributes["src"] =~ /svg\Z/i
|
154
|
+
content.properties.push("svg")
|
155
|
+
end
|
155
156
|
end
|
156
157
|
end
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
158
|
+
elsif content.media == "text/css"
|
159
|
+
File.open("#{basetmpdir}/#{content.file}") do |f|
|
160
|
+
f.each_line do |l|
|
161
|
+
l.scan(/url\((.+?)\)/) do |m|
|
162
|
+
@params["epubmaker"]["force_include_images"].push($1.strip)
|
163
|
+
end
|
163
164
|
end
|
164
165
|
end
|
165
166
|
end
|
166
167
|
end
|
168
|
+
@params["epubmaker"]["force_include_images"] = @params["epubmaker"]["force_include_images"].sort.uniq
|
167
169
|
end
|
168
|
-
@params["epubmaker"]["force_include_images"] = @params["epubmaker"]["force_include_images"].sort.uniq
|
169
|
-
end
|
170
170
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
171
|
+
def copy_images(resdir, destdir, allow_exts=nil)
|
172
|
+
return nil unless File.exist?(resdir)
|
173
|
+
allow_exts = @params["image_ext"] if allow_exts.nil?
|
174
|
+
FileUtils.mkdir_p(destdir)
|
175
|
+
if @params["epubmaker"]["verify_target_images"].present?
|
176
|
+
@params["epubmaker"]["force_include_images"].each do |file|
|
177
|
+
unless File.exist?(file)
|
178
|
+
warn "#{file} is not found, skip." if file !~ /\Ahttp[s]?:/
|
179
|
+
next
|
180
|
+
end
|
181
|
+
basedir = File.dirname(file)
|
182
|
+
FileUtils.mkdir_p("#{destdir}/#{basedir}")
|
183
|
+
log("Copy #{file} to the temporary directory.")
|
184
|
+
FileUtils.cp(file, "#{destdir}/#{basedir}")
|
180
185
|
end
|
181
|
-
|
182
|
-
|
183
|
-
log("Copy #{file} to the temporary directory.")
|
184
|
-
FileUtils.cp(file, "#{destdir}/#{basedir}")
|
186
|
+
else
|
187
|
+
recursive_copy_files(resdir, destdir, allow_exts)
|
185
188
|
end
|
186
|
-
else
|
187
|
-
recursive_copy_files(resdir, destdir, allow_exts)
|
188
189
|
end
|
189
|
-
end
|
190
190
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
191
|
+
def copy_resources(resdir, destdir, allow_exts=nil)
|
192
|
+
return nil unless File.exist?(resdir)
|
193
|
+
allow_exts = @params["image_ext"] if allow_exts.nil?
|
194
|
+
FileUtils.mkdir_p(destdir)
|
195
|
+
recursive_copy_files(resdir, destdir, allow_exts)
|
196
|
+
end
|
197
197
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
198
|
+
def recursive_copy_files(resdir, destdir, allow_exts)
|
199
|
+
Dir.open(resdir) do |dir|
|
200
|
+
dir.each do |fname|
|
201
|
+
next if fname.start_with?('.')
|
202
|
+
if FileTest.directory?("#{resdir}/#{fname}")
|
203
|
+
recursive_copy_files("#{resdir}/#{fname}", "#{destdir}/#{fname}", allow_exts)
|
204
|
+
else
|
205
|
+
if fname =~ /\.(#{allow_exts.join("|")})\Z/i
|
206
|
+
FileUtils.mkdir_p(destdir)
|
207
|
+
log("Copy #{resdir}/#{fname} to the temporary directory.")
|
208
|
+
FileUtils.cp("#{resdir}/#{fname}", destdir)
|
209
|
+
end
|
209
210
|
end
|
210
211
|
end
|
211
212
|
end
|
212
213
|
end
|
213
|
-
end
|
214
|
-
|
215
|
-
def check_compile_status
|
216
|
-
return unless @compile_errors
|
217
|
-
|
218
|
-
$stderr.puts "compile error, No EPUB file output."
|
219
|
-
exit 1
|
220
|
-
end
|
221
214
|
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
215
|
+
def check_compile_status
|
216
|
+
return unless @compile_errors
|
217
|
+
|
218
|
+
$stderr.puts "compile error, No EPUB file output."
|
219
|
+
exit 1
|
220
|
+
end
|
221
|
+
|
222
|
+
def build_body(basetmpdir, yamlfile)
|
223
|
+
@precount = 0
|
224
|
+
@bodycount = 0
|
225
|
+
@postcount = 0
|
226
|
+
|
227
|
+
@manifeststr = ""
|
228
|
+
@ncxstr = ""
|
229
|
+
@tocdesc = []
|
230
|
+
|
231
|
+
basedir = File.dirname(yamlfile)
|
232
|
+
base_path = Pathname.new(basedir)
|
233
|
+
book = ReVIEW::Book.load(basedir)
|
234
|
+
book.config = @params
|
235
|
+
@converter = ReVIEW::Converter.new(book, ReVIEW::HTMLBuilder.new)
|
236
|
+
@compile_errors = nil
|
237
|
+
book.parts.each do |part|
|
238
|
+
htmlfile = nil
|
239
|
+
if part.name.present?
|
240
|
+
if part.file?
|
241
|
+
build_chap(part, base_path, basetmpdir, true)
|
242
|
+
else
|
243
|
+
htmlfile = "part_#{part.number}.#{@params["htmlext"]}"
|
244
|
+
build_part(part, 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
|
+
end
|
249
250
|
end
|
250
|
-
end
|
251
251
|
|
252
|
-
|
253
|
-
|
252
|
+
part.chapters.each do |chap|
|
253
|
+
build_chap(chap, base_path, basetmpdir, false)
|
254
|
+
end
|
254
255
|
end
|
255
|
-
|
256
|
+
check_compile_status()
|
256
257
|
end
|
257
|
-
check_compile_status()
|
258
|
-
end
|
259
258
|
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
@language = @producer.params['language']
|
272
|
-
@stylesheets = @producer.params["stylesheet"]
|
273
|
-
tmplfile = File.expand_path(template_name, ReVIEW::Template::TEMPLATE_DIR)
|
274
|
-
tmpl = ReVIEW::Template.load(tmplfile)
|
275
|
-
f.write tmpl.result(binding)
|
276
|
-
end
|
277
|
-
end
|
259
|
+
def build_part(part, basetmpdir, htmlfile)
|
260
|
+
log("Create #{htmlfile} from a template.")
|
261
|
+
File.open("#{basetmpdir}/#{htmlfile}", "w") do |f|
|
262
|
+
@body = ""
|
263
|
+
@body << "<div class=\"part\">\n"
|
264
|
+
@body << "<h1 class=\"part-number\">#{CGI.escapeHTML(ReVIEW::I18n.t("part", part.number))}</h1>\n"
|
265
|
+
if part.name.strip.present?
|
266
|
+
@body << "<h2 class=\"part-title\">#{CGI.escapeHTML(part.name.strip)}</h2>\n"
|
267
|
+
end
|
268
|
+
@body << "</div>\n"
|
278
269
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
270
|
+
@language = @producer.params['language']
|
271
|
+
@stylesheets = @producer.params["stylesheet"]
|
272
|
+
tmplfile = File.expand_path(template_name, ReVIEW::Template::TEMPLATE_DIR)
|
273
|
+
tmpl = ReVIEW::Template.load(tmplfile)
|
274
|
+
f.write tmpl.result(binding)
|
275
|
+
end
|
284
276
|
end
|
285
|
-
end
|
286
277
|
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
elsif chap.on_PREDEF?
|
294
|
-
chaptype = "pre"
|
295
|
-
elsif chap.on_APPENDIX?
|
296
|
-
chaptype = "post"
|
278
|
+
def template_name
|
279
|
+
if @producer.params["htmlversion"].to_i == 5
|
280
|
+
'./html/layout-html5.html.erb'
|
281
|
+
else
|
282
|
+
'./html/layout-xhtml1.html.erb'
|
283
|
+
end
|
297
284
|
end
|
298
285
|
|
299
|
-
|
300
|
-
filename =
|
301
|
-
else
|
302
|
-
filename = Pathname.new(chap.path).relative_path_from(base_path).to_s
|
303
|
-
end
|
304
|
-
id = filename.sub(/\.re\Z/, "")
|
286
|
+
def build_chap(chap, base_path, basetmpdir, ispart)
|
287
|
+
filename = ""
|
305
288
|
|
306
|
-
|
307
|
-
if
|
308
|
-
|
309
|
-
|
289
|
+
chaptype = "body"
|
290
|
+
if ispart
|
291
|
+
chaptype = "part"
|
292
|
+
elsif chap.on_PREDEF?
|
293
|
+
chaptype = "pre"
|
310
294
|
elsif chap.on_APPENDIX?
|
311
|
-
|
312
|
-
|
295
|
+
chaptype = "post"
|
296
|
+
end
|
297
|
+
|
298
|
+
if ispart.present?
|
299
|
+
filename = chap.path
|
313
300
|
else
|
314
|
-
|
315
|
-
|
301
|
+
filename = Pathname.new(chap.path).relative_path_from(base_path).to_s
|
302
|
+
end
|
303
|
+
id = filename.sub(/\.re\Z/, "")
|
304
|
+
|
305
|
+
if @params["epubmaker"]["rename_for_legacy"] && ispart.nil?
|
306
|
+
if chap.on_PREDEF?
|
307
|
+
@precount += 1
|
308
|
+
id = sprintf("pre%02d", @precount)
|
309
|
+
elsif chap.on_APPENDIX?
|
310
|
+
@postcount += 1
|
311
|
+
id = sprintf("post%02d", @postcount)
|
312
|
+
else
|
313
|
+
@bodycount += 1
|
314
|
+
id = sprintf("chap%02d", @bodycount)
|
315
|
+
end
|
316
316
|
end
|
317
|
-
end
|
318
317
|
|
319
|
-
|
320
|
-
|
321
|
-
|
318
|
+
htmlfile = "#{id}.#{@params["htmlext"]}"
|
319
|
+
write_buildlogtxt(basetmpdir, htmlfile, filename)
|
320
|
+
log("Create #{htmlfile} from #{filename}.")
|
322
321
|
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
322
|
+
if @params["params"].present?
|
323
|
+
warn "'params:' in config.yml is obsoleted."
|
324
|
+
if @params["params"] =~ /stylesheet=/
|
325
|
+
warn "stylesheets should be defined in 'stylesheet:', not in 'params:'"
|
326
|
+
end
|
327
|
+
end
|
328
|
+
begin
|
329
|
+
@converter.convert(filename, File.join(basetmpdir, htmlfile))
|
330
|
+
write_info_body(basetmpdir, id, htmlfile, ispart, chaptype)
|
331
|
+
remove_hidden_title(basetmpdir, htmlfile)
|
332
|
+
rescue => e
|
333
|
+
@compile_errors = true
|
334
|
+
warn "compile error in #{filename} (#{e.class})"
|
335
|
+
warn e.message
|
327
336
|
end
|
328
337
|
end
|
329
|
-
begin
|
330
|
-
@converter.convert(filename, File.join(basetmpdir, htmlfile))
|
331
|
-
write_info_body(basetmpdir, id, htmlfile, ispart, chaptype)
|
332
|
-
remove_hidden_title(basetmpdir, htmlfile)
|
333
|
-
rescue => e
|
334
|
-
@compile_errors = true
|
335
|
-
warn "compile error in #{filename} (#{e.class})"
|
336
|
-
warn e.message
|
337
|
-
end
|
338
|
-
end
|
339
338
|
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
339
|
+
def remove_hidden_title(basetmpdir, htmlfile)
|
340
|
+
File.open("#{basetmpdir}/#{htmlfile}", "r+") do |f|
|
341
|
+
body = f.read.
|
342
|
+
gsub(/<h\d .*?hidden=['"]true['"].*?>.*?<\/h\d>\n/, '').
|
343
|
+
gsub(/(<h\d .*?)\s*notoc=['"]true['"]\s*(.*?>.*?<\/h\d>\n)/, '\1\2')
|
344
|
+
f.rewind
|
345
|
+
f.print body
|
346
|
+
f.truncate(f.tell)
|
347
|
+
end
|
348
348
|
end
|
349
|
-
end
|
350
349
|
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
350
|
+
def detect_properties(path)
|
351
|
+
properties = []
|
352
|
+
File.open(path) do |f|
|
353
|
+
doc = REXML::Document.new(f)
|
354
|
+
if REXML::XPath.first(doc, "//m:math", {'m' => 'http://www.w3.org/1998/Math/MathML'})
|
355
|
+
properties<< "mathml"
|
356
|
+
end
|
357
|
+
if REXML::XPath.first(doc, "//s:svg", {'s' => 'http://www.w3.org/2000/svg'})
|
358
|
+
properties<< "svg"
|
359
|
+
end
|
360
360
|
end
|
361
|
+
properties
|
361
362
|
end
|
362
|
-
properties
|
363
|
-
end
|
364
363
|
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
364
|
+
def write_info_body(basetmpdir, id, filename, ispart=nil, chaptype=nil)
|
365
|
+
headlines = []
|
366
|
+
path = File.join(basetmpdir, filename)
|
367
|
+
Document.parse_stream(File.new(path), ReVIEWHeaderListener.new(headlines))
|
368
|
+
properties = detect_properties(path)
|
369
|
+
prop_str = ""
|
370
|
+
if properties.present?
|
371
|
+
prop_str = ",properties="+properties.join(" ")
|
372
|
+
end
|
373
|
+
first = true
|
374
|
+
headlines.each do |headline|
|
375
|
+
headline["level"] = 0 if ispart.present? && headline["level"] == 1
|
376
|
+
if first.nil?
|
377
|
+
@htmltoc.add_item(headline["level"], filename+"#"+headline["id"], headline["title"], {:chaptype => chaptype, :notoc => headline["notoc"]})
|
378
|
+
else
|
379
|
+
@htmltoc.add_item(headline["level"], filename, headline["title"], {:force_include => true, :chaptype => chaptype+prop_str, :notoc => headline["notoc"]})
|
380
|
+
first = nil
|
381
|
+
end
|
382
382
|
end
|
383
383
|
end
|
384
|
-
end
|
385
384
|
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
385
|
+
def push_contents(basetmpdir)
|
386
|
+
@htmltoc.each_item do |level, file, title, args|
|
387
|
+
next if level.to_i > @params["toclevel"] && args[:force_include].nil?
|
388
|
+
log("Push #{file} to ePUB contents.")
|
390
389
|
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
390
|
+
hash = {"file" => file, "level" => level.to_i, "title" => title, "chaptype" => args[:chaptype]}
|
391
|
+
if args[:id].present?
|
392
|
+
hash["id"] = args[:id]
|
393
|
+
end
|
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
|
400
|
+
@producer.contents.push(Content.new(hash))
|
400
401
|
end
|
401
|
-
@producer.contents.push(Content.new(hash))
|
402
402
|
end
|
403
|
-
end
|
404
403
|
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
404
|
+
def copy_stylesheet(basetmpdir)
|
405
|
+
if @params["stylesheet"].size > 0
|
406
|
+
@params["stylesheet"].each do |sfile|
|
407
|
+
FileUtils.cp(sfile, basetmpdir)
|
408
|
+
@producer.contents.push(Content.new("file" => sfile))
|
409
|
+
end
|
410
410
|
end
|
411
411
|
end
|
412
|
-
end
|
413
412
|
|
414
|
-
|
415
|
-
|
413
|
+
def copy_frontmatter(basetmpdir)
|
414
|
+
FileUtils.cp(@params["cover"], "#{basetmpdir}/#{File.basename(@params["cover"])}") if @params["cover"].present? && File.exist?(@params["cover"])
|
416
415
|
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
416
|
+
if @params["titlepage"]
|
417
|
+
if @params["titlefile"].nil?
|
418
|
+
build_titlepage(basetmpdir, "titlepage.#{@params["htmlext"]}")
|
419
|
+
else
|
420
|
+
FileUtils.cp(@params["titlefile"], "#{basetmpdir}/titlepage.#{@params["htmlext"]}")
|
421
|
+
end
|
422
|
+
@htmltoc.add_item(1, "titlepage.#{@params['htmlext']}", @producer.res.v("titlepagetitle"), {:chaptype => "pre"})
|
422
423
|
end
|
423
|
-
@htmltoc.add_item(1, "titlepage.#{@params['htmlext']}", @producer.res.v("titlepagetitle"), {:chaptype => "pre"})
|
424
|
-
end
|
425
424
|
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
425
|
+
if @params["originaltitlefile"].present? && File.exist?(@params["originaltitlefile"])
|
426
|
+
FileUtils.cp(@params["originaltitlefile"], "#{basetmpdir}/#{File.basename(@params["originaltitlefile"])}")
|
427
|
+
@htmltoc.add_item(1, File.basename(@params["originaltitlefile"]), @producer.res.v("originaltitle"), {:chaptype => "pre"})
|
428
|
+
end
|
430
429
|
|
431
|
-
|
432
|
-
|
433
|
-
|
430
|
+
if @params["creditfile"].present? && File.exist?(@params["creditfile"])
|
431
|
+
FileUtils.cp(@params["creditfile"], "#{basetmpdir}/#{File.basename(@params["creditfile"])}")
|
432
|
+
@htmltoc.add_item(1, File.basename(@params["creditfile"]), @producer.res.v("credittitle"), {:chaptype => "pre"})
|
433
|
+
end
|
434
434
|
end
|
435
|
-
end
|
436
435
|
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
tmplfile = File.expand_path(template_name, ReVIEW::Template::TEMPLATE_DIR)
|
455
|
-
tmpl = ReVIEW::Template.load(tmplfile)
|
456
|
-
f.write tmpl.result(binding)
|
457
|
-
end
|
458
|
-
end
|
436
|
+
def build_titlepage(basetmpdir, htmlfile)
|
437
|
+
# TODO: should be created via epubcommon
|
438
|
+
@title = CGI.escapeHTML(@params.name_of("booktitle"))
|
439
|
+
File.open("#{basetmpdir}/#{htmlfile}", "w") do |f|
|
440
|
+
@body = ""
|
441
|
+
@body << "<div class=\"titlepage\">\n"
|
442
|
+
@body << "<h1 class=\"tp-title\">#{CGI.escapeHTML(@params.name_of("booktitle"))}</h1>\n"
|
443
|
+
if @params["subtitle"]
|
444
|
+
@body << "<h2 class=\"tp-subtitle\">#{CGI.escapeHTML(@params.name_of("subtitle"))}</h2>\n"
|
445
|
+
end
|
446
|
+
if @params["aut"]
|
447
|
+
@body << "<h2 class=\"tp-author\">#{CGI.escapeHTML(@params.names_of("aut").join(ReVIEW::I18n.t("names_splitter")))}</h2>\n"
|
448
|
+
end
|
449
|
+
if @params["prt"]
|
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>"
|
459
453
|
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
454
|
+
@language = @producer.params['language']
|
455
|
+
@stylesheets = @producer.params["stylesheet"]
|
456
|
+
tmplfile = File.expand_path(template_name, ReVIEW::Template::TEMPLATE_DIR)
|
457
|
+
tmpl = ReVIEW::Template.load(tmplfile)
|
458
|
+
f.write tmpl.result(binding)
|
459
|
+
end
|
464
460
|
end
|
465
461
|
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
462
|
+
def copy_backmatter(basetmpdir)
|
463
|
+
if @params["profile"]
|
464
|
+
FileUtils.cp(@params["profile"], "#{basetmpdir}/#{File.basename(@params["profile"])}")
|
465
|
+
@htmltoc.add_item(1, File.basename(@params["profile"]), @producer.res.v("profiletitle"), {:chaptype => "post"})
|
466
|
+
end
|
470
467
|
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
else
|
475
|
-
File.open("#{basetmpdir}/colophon.#{@params["htmlext"]}", "w") {|f| @producer.colophon(f) }
|
468
|
+
if @params["advfile"]
|
469
|
+
FileUtils.cp(@params["advfile"], "#{basetmpdir}/#{File.basename(@params["advfile"])}")
|
470
|
+
@htmltoc.add_item(1, File.basename(@params["advfile"]), @producer.res.v("advtitle"), {:chaptype => "post"})
|
476
471
|
end
|
477
|
-
@htmltoc.add_item(1, "colophon.#{@params["htmlext"]}", @producer.res.v("colophontitle"), {:chaptype => "post"})
|
478
|
-
end
|
479
472
|
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
473
|
+
if @params["colophon"]
|
474
|
+
if @params["colophon"].kind_of?(String) # FIXME: このやり方はやめる?
|
475
|
+
FileUtils.cp(@params["colophon"], "#{basetmpdir}/colophon.#{@params["htmlext"]}")
|
476
|
+
else
|
477
|
+
File.open("#{basetmpdir}/colophon.#{@params["htmlext"]}", "w") {|f| @producer.colophon(f) }
|
478
|
+
end
|
479
|
+
@htmltoc.add_item(1, "colophon.#{@params["htmlext"]}", @producer.res.v("colophontitle"), {:chaptype => "post"})
|
480
|
+
end
|
485
481
|
|
486
|
-
|
487
|
-
|
488
|
-
|
482
|
+
if @params["backcover"]
|
483
|
+
FileUtils.cp(@params["backcover"], "#{basetmpdir}/#{File.basename(@params["backcover"])}")
|
484
|
+
@htmltoc.add_item(1, File.basename(@params["backcover"]), @producer.res.v("backcovertitle"), {:chaptype => "post"})
|
485
|
+
end
|
489
486
|
end
|
490
|
-
end
|
491
487
|
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
@content = ""
|
497
|
-
@headlines = headlines
|
488
|
+
def write_buildlogtxt(basetmpdir, htmlfile, reviewfile)
|
489
|
+
File.open("#{basetmpdir}/#{@buildlogtxt}", "a") do |f|
|
490
|
+
f.puts "#{htmlfile},#{reviewfile}"
|
491
|
+
end
|
498
492
|
end
|
499
493
|
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
@
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
if name
|
510
|
-
@
|
511
|
-
|
512
|
-
|
494
|
+
class ReVIEWHeaderListener
|
495
|
+
include REXML::StreamListener
|
496
|
+
def initialize(headlines)
|
497
|
+
@level = nil
|
498
|
+
@content = ""
|
499
|
+
@headlines = headlines
|
500
|
+
end
|
501
|
+
|
502
|
+
def tag_start(name, attrs)
|
503
|
+
if name =~ /\Ah(\d+)/
|
504
|
+
if @level.present?
|
505
|
+
raise "#{name}, #{attrs}"
|
506
|
+
end
|
507
|
+
@level = $1.to_i
|
508
|
+
@id = attrs["id"] if attrs["id"].present?
|
509
|
+
@notoc = attrs["notoc"] if attrs["notoc"].present?
|
510
|
+
elsif !@level.nil?
|
511
|
+
if name == "img" && attrs["alt"].present?
|
512
|
+
@content << attrs["alt"]
|
513
|
+
elsif name == "a" && attrs["id"].present?
|
514
|
+
@id = attrs["id"]
|
515
|
+
end
|
513
516
|
end
|
514
517
|
end
|
515
|
-
end
|
516
518
|
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
519
|
+
def tag_end(name)
|
520
|
+
if name =~ /\Ah\d+/
|
521
|
+
@headlines.push({"level" => @level, "id" => @id, "title" => @content, "notoc" => @notoc}) if @id.present?
|
522
|
+
@content = ""
|
523
|
+
@level = nil
|
524
|
+
@id = nil
|
525
|
+
@notoc = nil
|
526
|
+
end
|
524
527
|
end
|
525
|
-
end
|
526
528
|
|
527
|
-
|
528
|
-
|
529
|
-
|
529
|
+
def text(text)
|
530
|
+
if @level.present?
|
531
|
+
@content << text.gsub("\t", " ") # FIXME: 区切り文字
|
532
|
+
end
|
530
533
|
end
|
531
534
|
end
|
532
535
|
end
|
533
|
-
end
|
534
536
|
end
|