review 5.0.0 → 5.1.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-tex.yml +31 -0
- data/.github/workflows/ruby-win.yml +3 -3
- data/.github/workflows/ruby.yml +1 -1
- data/.rubocop.yml +15 -7
- data/NEWS.ja.md +108 -0
- data/NEWS.md +108 -0
- data/README.md +7 -6
- data/Rakefile +7 -2
- data/bin/review +2 -4
- data/bin/review-catalog-converter +3 -3
- data/bin/review-check +6 -8
- data/bin/review-checkdep +1 -4
- data/bin/review-compile +2 -5
- data/bin/review-epub2html +1 -4
- data/bin/review-epubmaker +3 -4
- data/bin/review-idgxmlmaker +1 -3
- data/bin/review-index +11 -5
- data/bin/review-init +1 -4
- data/bin/review-pdfmaker +1 -3
- data/bin/review-preproc +2 -4
- data/bin/review-textmaker +1 -3
- data/bin/review-update +1 -4
- data/bin/review-validate +3 -3
- data/bin/review-vol +1 -4
- data/bin/review-webmaker +1 -3
- data/doc/config.yml.sample +21 -5
- data/doc/config.yml.sample-simple +1 -1
- data/doc/format.ja.md +21 -10
- data/doc/format.md +21 -10
- data/doc/quickstart.ja.md +11 -1
- data/doc/quickstart.md +11 -2
- data/lib/review.rb +1 -1
- data/lib/review/book/base.rb +4 -0
- data/lib/review/book/book_unit.rb +3 -0
- data/lib/review/book/chapter.rb +3 -0
- data/lib/review/book/index.rb +1 -0
- data/lib/review/book/volume.rb +1 -0
- data/lib/review/builder.rb +8 -1
- data/lib/review/call_hook.rb +20 -0
- data/lib/review/catalog.rb +1 -0
- data/lib/review/compiler.rb +27 -10
- data/lib/review/configure.rb +64 -7
- data/lib/review/epubmaker.rb +93 -96
- data/lib/review/epubmaker/content.rb +113 -0
- data/lib/review/epubmaker/epubcommon.rb +372 -0
- data/lib/review/epubmaker/epubv2.rb +178 -0
- data/lib/review/epubmaker/epubv3.rb +231 -0
- data/lib/review/epubmaker/producer.rb +168 -0
- data/lib/review/epubmaker/reviewheaderlistener.rb +12 -2
- data/lib/review/epubmaker/zip_exporter.rb +84 -0
- data/lib/review/exception.rb +6 -0
- data/lib/review/htmlbuilder.rb +36 -49
- data/lib/review/htmlutils.rb +1 -1
- data/lib/review/i18n.rb +1 -0
- data/lib/review/idgxmlbuilder.rb +33 -30
- data/lib/review/idgxmlmaker.rb +3 -1
- data/lib/review/img_math.rb +245 -0
- data/lib/review/index_builder.rb +1 -0
- data/lib/review/init.rb +4 -4
- data/lib/review/latexbox.rb +58 -0
- data/lib/review/latexbuilder.rb +30 -19
- data/lib/review/latexutils.rb +9 -1
- data/lib/review/lineinput.rb +112 -2
- data/lib/review/logger.rb +41 -2
- data/lib/review/makerhelper.rb +2 -205
- data/lib/review/markdownbuilder.rb +32 -1
- data/lib/review/pdfmaker.rb +31 -29
- data/lib/review/plaintextbuilder.rb +9 -1
- data/lib/review/preprocessor.rb +12 -6
- data/lib/review/rstbuilder.rb +1 -1
- data/lib/review/sec_counter.rb +1 -0
- data/lib/review/template.rb +6 -0
- data/lib/review/textmaker.rb +11 -7
- data/lib/review/textutils.rb +2 -10
- data/lib/review/tocprinter.rb +85 -68
- data/lib/review/topbuilder.rb +18 -11
- data/lib/review/update.rb +5 -6
- data/lib/review/version.rb +1 -1
- data/lib/review/volumeprinter.rb +4 -5
- data/lib/review/webmaker.rb +18 -13
- data/lib/review/webtocprinter.rb +10 -9
- data/lib/review/yamlloader.rb +2 -1
- data/review.gemspec +5 -3
- data/samples/sample-book/src/config-epub2.yml +1 -1
- data/samples/sample-book/src/config.yml +1 -1
- data/samples/sample-book/src/lib/tasks/review.rake +17 -1
- data/samples/syntax-book/ch01.re +1 -1
- data/samples/syntax-book/ch02.re +21 -6
- data/samples/syntax-book/ch03.re +1 -1
- data/samples/syntax-book/images/img3-2.png +0 -0
- data/templates/html/_colophon.html.erb +23 -0
- data/templates/html/_colophon_history.html.erb +9 -0
- data/templates/html/_cover.html.erb +10 -0
- data/templates/html/_part_body.html.erb +6 -0
- data/templates/html/_titlepage.html.erb +20 -0
- data/templates/html/layout-html5.html.erb +6 -0
- data/templates/html/layout-xhtml1.html.erb +6 -0
- data/templates/latex/config.erb +8 -0
- data/templates/latex/review-jlreq/review-base.sty +4 -5
- data/templates/latex/review-jlreq/review-jlreq.cls +10 -2
- data/templates/latex/review-jlreq/review-style.sty +6 -1
- data/templates/latex/review-jlreq/review-tcbox.sty +348 -0
- data/templates/latex/review-jlreq/reviewmacro.sty +5 -0
- data/templates/latex/review-jsbook/review-base.sty +5 -7
- data/templates/latex/review-jsbook/review-jsbook.cls +10 -2
- data/templates/latex/review-jsbook/review-style.sty +6 -1
- data/templates/latex/review-jsbook/review-tcbox.sty +348 -0
- data/templates/latex/review-jsbook/reviewmacro.sty +5 -0
- data/templates/opf/epubv2.opf.erb +7 -7
- data/templates/opf/epubv3.opf.erb +7 -7
- data/templates/opf/opf_manifest_epubv2.opf.erb +10 -0
- data/templates/opf/opf_manifest_epubv3.opf.erb +10 -0
- data/templates/opf/opf_metainfo_epubv2.opf.erb +17 -0
- data/templates/opf/opf_metainfo_epubv3.opf.erb +49 -0
- data/templates/opf/opf_tocx_epubv2.opf.erb +9 -0
- data/templates/opf/opf_tocx_epubv3.opf.erb +17 -0
- data/templates/web/html/layout-html5.html.erb +6 -5
- data/templates/web/html/layout-xhtml1.html.erb +6 -0
- data/test/assets/header_listener.html +35 -0
- data/test/assets/img_math/img1.png +0 -0
- data/test/assets/img_math/img2.png +0 -0
- data/test/assets/img_math/img3.png +0 -0
- data/test/assets/syntax_book_index_detail.txt +58 -0
- data/test/assets/test_template.tex +4 -1
- data/test/assets/test_template_backmatter.tex +4 -1
- data/test/run_test.rb +1 -1
- data/test/test_book_chapter.rb +2 -2
- data/test/test_catalog_converter_cmd.rb +1 -1
- data/test/test_epub3maker.rb +168 -124
- data/test/test_epubmaker.rb +243 -131
- data/test/test_epubmaker_cmd.rb +2 -2
- data/test/test_helper.rb +5 -4
- data/test/test_htmlbuilder.rb +64 -6
- data/test/test_idgxmlbuilder.rb +13 -0
- data/test/test_idgxmlmaker_cmd.rb +7 -3
- data/test/test_img_math.rb +111 -0
- data/test/test_indexbuilder.rb +5 -5
- data/test/test_latexbuilder.rb +107 -4
- data/test/test_lineinput.rb +20 -93
- data/test/test_markdownbuilder.rb +29 -0
- data/test/test_pdfmaker.rb +71 -0
- data/test/test_pdfmaker_cmd.rb +2 -2
- data/test/test_plaintextbuilder.rb +10 -18
- data/test/test_reviewheaderlistener.rb +49 -0
- data/test/test_template.rb +12 -2
- data/test/test_textmaker_cmd.rb +5 -1
- data/test/test_tocprinter.rb +46 -0
- data/test/test_topbuilder.rb +6 -1
- data/test/test_update.rb +34 -34
- data/test/test_zip_exporter.rb +5 -6
- metadata +91 -17
- data/lib/epubmaker.rb +0 -23
- data/lib/epubmaker/content.rb +0 -111
- data/lib/epubmaker/epubcommon.rb +0 -449
- data/lib/epubmaker/epubv2.rb +0 -142
- data/lib/epubmaker/epubv3.rb +0 -235
- data/lib/epubmaker/producer.rb +0 -375
- data/lib/epubmaker/zip_exporter.rb +0 -81
- data/lib/lineinput.rb +0 -155
@@ -0,0 +1,231 @@
|
|
1
|
+
# = epubv3.rb -- EPUB version 3 producer.
|
2
|
+
#
|
3
|
+
# Copyright (c) 2010-2017 Kenshi Muto
|
4
|
+
#
|
5
|
+
# This program is free software.
|
6
|
+
# You can distribute or modify this program under the terms of
|
7
|
+
# the GNU LGPL, Lesser General Public License version 2.1.
|
8
|
+
# For details of the GNU LGPL, see the file "COPYING".
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'review/epubmaker/epubcommon'
|
12
|
+
require 'review/epubmaker/zip_exporter'
|
13
|
+
require 'review/call_hook'
|
14
|
+
|
15
|
+
module ReVIEW
|
16
|
+
class EPUBMaker
|
17
|
+
# EPUBv3 is EPUB version 3 producer.
|
18
|
+
class EPUBv3 < EPUBCommon
|
19
|
+
include ReVIEW::CallHook
|
20
|
+
|
21
|
+
DC_ITEMS = %w[title language date type format source description relation coverage subject rights]
|
22
|
+
CREATOR_ATTRIBUTES = %w[a-adp a-ann a-arr a-art a-asn a-aqt a-aft a-aui a-ant a-bkp a-clb a-cmm a-csl a-dsr a-edt a-ill a-lyr a-mdc a-mus a-nrt a-oth a-pht a-prt a-red a-rev a-spn a-ths a-trc a-trl aut]
|
23
|
+
CONTRIBUTER_ATTRIBUTES = %w[adp ann arr art asn aqt aft aui ant bkp clb cmm csl dsr edt ill lyr mdc mus nrt oth pbd pbl pht prt red rev spn ths trc trl]
|
24
|
+
|
25
|
+
# Construct object with parameter hash +config+ and message resource hash +res+.
|
26
|
+
def initialize(producer)
|
27
|
+
super
|
28
|
+
@opf_prefix = {}
|
29
|
+
if config['opf_prefix'].present?
|
30
|
+
config['opf_prefix'].each { |k, v| @opf_prefix[k] = v }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Return opf file content.
|
35
|
+
def opf
|
36
|
+
@opf_metainfo = opf_metainfo
|
37
|
+
@opf_coverimage = opf_coverimage
|
38
|
+
@opf_manifest = opf_manifest
|
39
|
+
@opf_toc = opf_tocx
|
40
|
+
@package_attrs = ''
|
41
|
+
|
42
|
+
if @opf_prefix && @opf_prefix.size > 0
|
43
|
+
prefixes_str = @opf_prefix.map { |k, v| %Q(#{k}: #{v}) }.join(' ')
|
44
|
+
@package_attrs << %Q( prefix="#{prefixes_str}")
|
45
|
+
end
|
46
|
+
|
47
|
+
ReVIEW::Template.generate(path: './opf/epubv3.opf.erb', binding: binding)
|
48
|
+
end
|
49
|
+
|
50
|
+
def opf_metainfo
|
51
|
+
@dc_items = opf_dc_items
|
52
|
+
|
53
|
+
# creator (should be array)
|
54
|
+
@creators = opf_creators
|
55
|
+
|
56
|
+
# contributor (should be array)
|
57
|
+
@contributers = opf_contributers
|
58
|
+
|
59
|
+
ReVIEW::Template.generate(path: './opf/opf_metainfo_epubv3.opf.erb', binding: binding)
|
60
|
+
end
|
61
|
+
|
62
|
+
def opf_dc_items
|
63
|
+
DC_ITEMS.map do |item|
|
64
|
+
next unless config[item]
|
65
|
+
|
66
|
+
case config[item]
|
67
|
+
when Array
|
68
|
+
config[item].map.with_index do |v, i|
|
69
|
+
if v.is_a?(Hash)
|
70
|
+
{ tag: "dc:#{item}",
|
71
|
+
id: "#{item}-#{i}",
|
72
|
+
val: v['name'],
|
73
|
+
refines: v.map { |name, val| { name: name, val: val } }.delete_if { |h| h[:name] == 'name' } }
|
74
|
+
else
|
75
|
+
{ tag: "dc:#{item}", id: "#{item}-#{i}", val: v.to_s, refines: [] }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
when Hash
|
79
|
+
{ tag: "dc:#{item}",
|
80
|
+
id: item.to_s,
|
81
|
+
val: config[item]['name'],
|
82
|
+
refines: config[item].map { |name, val| { name: name, val: val } }.delete_if { |h| h[:name] == 'name' } }
|
83
|
+
else
|
84
|
+
{ tag: "dc:#{item}",
|
85
|
+
id: item.to_s,
|
86
|
+
val: config[item].to_s,
|
87
|
+
refines: [] }
|
88
|
+
end
|
89
|
+
end.flatten.compact
|
90
|
+
end
|
91
|
+
|
92
|
+
def opf_creators
|
93
|
+
CREATOR_ATTRIBUTES.map do |role|
|
94
|
+
next unless config[role]
|
95
|
+
|
96
|
+
config[role].map.with_index do |v, i|
|
97
|
+
case v
|
98
|
+
when Hash
|
99
|
+
refines = v.map { |name, val| { id: "#{role.sub('a-', '')}-#{i}", property: name.to_s, scheme: nil, val: val } }.delete_if { |h| h[:property] == 'name' }
|
100
|
+
|
101
|
+
{
|
102
|
+
id: "#{role}-#{i}",
|
103
|
+
val: v['name'],
|
104
|
+
refines: [
|
105
|
+
{ id: "#{role}-#{i}", property: 'role', scheme: 'marc:relators', val: role.sub('a-', '') }
|
106
|
+
].concat(refines)
|
107
|
+
}
|
108
|
+
else
|
109
|
+
{
|
110
|
+
id: "#{role}-#{i}",
|
111
|
+
val: v,
|
112
|
+
refines: [
|
113
|
+
{ id: "#{role}-#{i}", property: 'role', scheme: 'marc:relators', val: role.sub('a-', '') }
|
114
|
+
]
|
115
|
+
}
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end.flatten.compact
|
119
|
+
end
|
120
|
+
|
121
|
+
def opf_contributers
|
122
|
+
CONTRIBUTER_ATTRIBUTES.map do |role|
|
123
|
+
next unless config[role]
|
124
|
+
|
125
|
+
config[role].map.with_index do |v, i|
|
126
|
+
case v
|
127
|
+
when Hash
|
128
|
+
refines = v.map { |name, val| { id: "#{role}-#{i}", property: name, scheme: nil, val: val } }.delete_if { |h| h[:property] == 'name' }
|
129
|
+
contributer = {
|
130
|
+
id: "#{role}-#{i}",
|
131
|
+
val: v['name'],
|
132
|
+
refines: [
|
133
|
+
{ id: "#{role}-#{i}", property: 'role', scheme: 'marc:relators', val: role }
|
134
|
+
].concat(refines)
|
135
|
+
}
|
136
|
+
else
|
137
|
+
contributer = {
|
138
|
+
id: "#{role}-#{i}",
|
139
|
+
val: v,
|
140
|
+
refines: [
|
141
|
+
{ id: "#{role}-#{i}", property: 'role', scheme: 'marc:relators', val: role }
|
142
|
+
]
|
143
|
+
}
|
144
|
+
end
|
145
|
+
if %w[prt pbl].include?(role)
|
146
|
+
contributer[:pub_id] = "pub-#{role}-#{i}"
|
147
|
+
case v
|
148
|
+
when Hash
|
149
|
+
contributer[:pub_val] = v['name']
|
150
|
+
pub_refines = v.map { |name, val| { id: "pub-#{role}-#{i}", property: name, scheme: nil, val: val } }.delete_if { |h| h[:property] == 'name' }
|
151
|
+
contributer[:pub_refines] = [
|
152
|
+
{ id: "pub-#{role}-#{i}", property: 'role', scheme: 'marc:relators', val: role }
|
153
|
+
].concat(pub_refines)
|
154
|
+
else
|
155
|
+
contributer[:pub_val] = v
|
156
|
+
contributer[:pub_refines] = [
|
157
|
+
{ id: "pub-#{role}-#{i}", property: 'role', scheme: 'marc:relators', val: 'prt' }
|
158
|
+
]
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
contributer
|
163
|
+
end
|
164
|
+
end.flatten.compact
|
165
|
+
end
|
166
|
+
|
167
|
+
def opf_manifest
|
168
|
+
if config['coverimage']
|
169
|
+
@coverimage = contents.find { |content| content.coverimage?(config['coverimage']) } # @coverimage can be nil
|
170
|
+
end
|
171
|
+
@items = if @coverimage
|
172
|
+
contents.find_all { |content| content.file !~ /#/ && content.id != @coverimage.id } # skip subgroup, or @coverimage
|
173
|
+
else
|
174
|
+
contents.find_all { |content| content.file !~ /#/ }
|
175
|
+
end
|
176
|
+
|
177
|
+
ReVIEW::Template.generate(path: './opf/opf_manifest_epubv3.opf.erb', binding: binding)
|
178
|
+
end
|
179
|
+
|
180
|
+
def opf_tocx
|
181
|
+
@cover_linear = if config['epubmaker']['cover_linear'] && config['epubmaker']['cover_linear'] != 'no'
|
182
|
+
'yes'
|
183
|
+
else
|
184
|
+
'no'
|
185
|
+
end
|
186
|
+
@tocx_contents = []
|
187
|
+
toc = nil
|
188
|
+
contents.each do |item|
|
189
|
+
next if item.media !~ /xhtml\+xml/ # skip non XHTML
|
190
|
+
|
191
|
+
@tocx_contents << item
|
192
|
+
end
|
193
|
+
|
194
|
+
ReVIEW::Template.generate(path: './opf/opf_tocx_epubv3.opf.erb', binding: binding)
|
195
|
+
end
|
196
|
+
|
197
|
+
def ncx(indentarray)
|
198
|
+
ncx_main = if config['epubmaker']['flattoc'].nil?
|
199
|
+
hierarchy_ncx('ol')
|
200
|
+
else
|
201
|
+
flat_ncx('ol', config['epubmaker']['flattocindent'])
|
202
|
+
end
|
203
|
+
|
204
|
+
@body = <<-EOT
|
205
|
+
<nav xmlns:epub="http://www.idpf.org/2007/ops" epub:type="toc" id="toc">
|
206
|
+
<h1 class="toc-title">#{h(ReVIEW::I18n.t('toctitle'))}</h1>
|
207
|
+
#{ncx_main} </nav>
|
208
|
+
EOT
|
209
|
+
|
210
|
+
@title = h(ReVIEW::I18n.t('toctitle'))
|
211
|
+
@language = config['language']
|
212
|
+
@stylesheets = config['stylesheet']
|
213
|
+
ReVIEW::Template.generate(path: './html/layout-html5.html.erb', binding: binding)
|
214
|
+
end
|
215
|
+
|
216
|
+
# Produce EPUB file +epubfile+.
|
217
|
+
# +work_dir+ points the directory has contents.
|
218
|
+
# +tmpdir+ defines temporary directory.
|
219
|
+
def produce(epubfile, work_dir, tmpdir, base_dir:)
|
220
|
+
produce_write_common(work_dir, tmpdir)
|
221
|
+
|
222
|
+
toc_file = "#{tmpdir}/OEBPS/#{config['bookname']}-toc.#{config['htmlext']}"
|
223
|
+
File.write(toc_file, ncx(config['epubmaker']['ncxindent']))
|
224
|
+
|
225
|
+
call_hook('hook_prepack', tmpdir, base_dir: base_dir)
|
226
|
+
expoter = ReVIEW::EPUBMaker::ZipExporter.new(tmpdir, config)
|
227
|
+
expoter.export_zip(epubfile)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
# = producer.rb -- EPUB producer.
|
2
|
+
#
|
3
|
+
# Copyright (c) 2010-2021 Kenshi Muto
|
4
|
+
#
|
5
|
+
# This program is free software.
|
6
|
+
# You can distribute or modify this program under the terms of
|
7
|
+
# the GNU LGPL, Lesser General Public License version 2.1.
|
8
|
+
# For details of the GNU LGPL, see the file "COPYING".
|
9
|
+
#
|
10
|
+
# == Quick usage
|
11
|
+
# require 'review/epubmaker'
|
12
|
+
# producer = ReVIEW::EPUBMaker::Producer.new(config)
|
13
|
+
# producer.contents.push(ReVIEW::EPUBMaker::Content.new(file: 'ch01.xhtml'))
|
14
|
+
# producer.contents.push(ReVIEW::EPUBMaker::Content.new(file: 'ch02.xhtml'))
|
15
|
+
# ...
|
16
|
+
# producer.import_imageinfo('images')
|
17
|
+
# producer.produce(epub_filename)
|
18
|
+
|
19
|
+
require 'tmpdir'
|
20
|
+
require 'fileutils'
|
21
|
+
require 'review/yamlloader'
|
22
|
+
require 'review/epubmaker/content'
|
23
|
+
require 'review/epubmaker/epubv2'
|
24
|
+
require 'review/epubmaker/epubv3'
|
25
|
+
require 'review/i18n'
|
26
|
+
require 'review/configure'
|
27
|
+
require 'review/extentions/hash'
|
28
|
+
|
29
|
+
module ReVIEW
|
30
|
+
class EPUBMaker
|
31
|
+
# EPUBMaker produces EPUB file.
|
32
|
+
class Producer
|
33
|
+
# Array of content objects.
|
34
|
+
attr_accessor :contents
|
35
|
+
# Parameter hash.
|
36
|
+
attr_accessor :config
|
37
|
+
# Message resource object.
|
38
|
+
attr_reader :res
|
39
|
+
|
40
|
+
def warn(msg)
|
41
|
+
@logger.warn(msg)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Construct producer object.
|
45
|
+
# +config+ takes initial parameter hash.
|
46
|
+
def initialize(config)
|
47
|
+
@contents = []
|
48
|
+
@config = config
|
49
|
+
@config.maker = 'epubmaker'
|
50
|
+
@epub = nil
|
51
|
+
@res = ReVIEW::I18n
|
52
|
+
@logger = ReVIEW.logger
|
53
|
+
modify_config
|
54
|
+
end
|
55
|
+
|
56
|
+
# Modify parameters for EPUB specific.
|
57
|
+
def modify_config
|
58
|
+
if @config['epubversion'] >= 3
|
59
|
+
@config['htmlversion'] = 5
|
60
|
+
end
|
61
|
+
|
62
|
+
@config['title'] ||= @config['booktitle']
|
63
|
+
@config['cover'] ||= "#{@config['bookname']}.#{@config['htmlext']}"
|
64
|
+
|
65
|
+
%w[bookname title].each do |k|
|
66
|
+
unless @config[k]
|
67
|
+
raise "Key #{k} must have a value. Abort."
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
case @config['epubversion'].to_i
|
72
|
+
when 2
|
73
|
+
@epub = ReVIEW::EPUBMaker::EPUBv2.new(self)
|
74
|
+
when 3
|
75
|
+
@epub = ReVIEW::EPUBMaker::EPUBv3.new(self)
|
76
|
+
else
|
77
|
+
raise "Invalid EPUB version (#{@config['epubversion']}.)"
|
78
|
+
end
|
79
|
+
|
80
|
+
ReVIEW::I18n.locale = @config['language']
|
81
|
+
support_legacy_maker
|
82
|
+
end
|
83
|
+
|
84
|
+
# Add informations of figure files in +path+ to contents array.
|
85
|
+
# +base+ defines a string to remove from path name.
|
86
|
+
def import_imageinfo(path, base = nil, allow_exts = nil)
|
87
|
+
return nil unless File.exist?(path)
|
88
|
+
|
89
|
+
allow_exts ||= @config['image_ext']
|
90
|
+
Dir.foreach(path) do |f|
|
91
|
+
next if f.start_with?('.')
|
92
|
+
|
93
|
+
if f =~ /\.(#{allow_exts.join('|')})\Z/i
|
94
|
+
path.chop! if path =~ %r{/\Z}
|
95
|
+
if base.nil?
|
96
|
+
@contents.push(ReVIEW::EPUBMaker::Content.new(file: "#{path}/#{f}"))
|
97
|
+
else
|
98
|
+
@contents.push(ReVIEW::EPUBMaker::Content.new(file: "#{path.sub(base + '/', '')}/#{f}"))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
if FileTest.directory?("#{path}/#{f}")
|
102
|
+
import_imageinfo("#{path}/#{f}", base)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
alias_method :importImageInfo, :import_imageinfo
|
108
|
+
|
109
|
+
# Produce EPUB file +epubfile+.
|
110
|
+
# +work_dir+ points the directory has contents (default: current directory.)
|
111
|
+
# +tmpdir+ defines temporary directory.
|
112
|
+
# +base_dir+ is original root dir.
|
113
|
+
def produce(epubfile, work_dir, tmpdir = nil, base_dir: nil)
|
114
|
+
current = Dir.pwd
|
115
|
+
base_dir ||= current
|
116
|
+
|
117
|
+
# use Dir to solve a path for Windows (see #1011)
|
118
|
+
new_tmpdir = Dir[File.join(tmpdir.nil? ? Dir.mktmpdir : tmpdir)][0]
|
119
|
+
if epubfile !~ %r{\A/}
|
120
|
+
epubfile = "#{current}/#{epubfile}"
|
121
|
+
end
|
122
|
+
|
123
|
+
# FIXME: error check
|
124
|
+
File.unlink(epubfile) if File.exist?(epubfile)
|
125
|
+
|
126
|
+
begin
|
127
|
+
@epub.produce(epubfile, work_dir, new_tmpdir, base_dir: base_dir)
|
128
|
+
ensure
|
129
|
+
FileUtils.rm_r(new_tmpdir) if tmpdir.nil?
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def support_legacy_maker
|
136
|
+
# legacy review-epubmaker support
|
137
|
+
if @config['flag_legacy_coverfile'].nil? && !@config['coverfile'].nil? && File.exist?(@config['coverfile'])
|
138
|
+
@config['cover'] = "#{@config['bookname']}-cover.#{@config['htmlext']}"
|
139
|
+
@epub.legacy_cover_and_title_file(@config['coverfile'], @config['cover'])
|
140
|
+
@config['flag_legacy_coverfile'] = true
|
141
|
+
warn %Q(Parameter 'coverfile' is obsolete. Please use 'cover' and make complete html file with header and footer.)
|
142
|
+
end
|
143
|
+
|
144
|
+
if @config['flag_legacy_titlepagefile'].nil? && !@config['titlepagefile'].nil? && File.exist?(@config['titlepagefile'])
|
145
|
+
@config['titlefile'] = "#{@config['bookname']}-title.#{@config['htmlext']}"
|
146
|
+
@config['titlepage'] = true
|
147
|
+
@epub.legacy_cover_and_title_file(@config['titlepagefile'], @config['titlefile'])
|
148
|
+
@config['flag_legacy_titlepagefile'] = true
|
149
|
+
warn %Q(Parameter 'titlepagefile' is obsolete. Please use 'titlefile' and make complete html file with header and footer.)
|
150
|
+
end
|
151
|
+
|
152
|
+
if @config['flag_legacy_backcoverfile'].nil? && !@config['backcoverfile'].nil? && File.exist?(@config['backcoverfile'])
|
153
|
+
@config['backcover'] = "#{@config['bookname']}-backcover.#{@config['htmlext']}"
|
154
|
+
@epub.legacy_cover_and_title_file(@config['backcoverfile'], @config['backcover'])
|
155
|
+
@config['flag_legacy_backcoverfile'] = true
|
156
|
+
warn %Q(Parameter 'backcoverfile' is obsolete. Please use 'backcover' and make complete html file with header and footer.)
|
157
|
+
end
|
158
|
+
|
159
|
+
if @config['flag_legacy_pubhistory'].nil? && @config['pubhistory']
|
160
|
+
@config['history'] = [[]]
|
161
|
+
@config['pubhistory'].split("\n").each { |date| @config['history'][0].push(date.sub(/(\d+)年(\d+)月(\d+)日/, '\1-\2-\3')) }
|
162
|
+
@config['flag_legacy_pubhistory'] = true
|
163
|
+
warn %Q(Parameter 'pubhistory' is obsolete. Please use 'history' array.)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
@@ -7,6 +7,15 @@
|
|
7
7
|
#
|
8
8
|
module ReVIEW
|
9
9
|
class EPUBMaker
|
10
|
+
# Listener class to scan HTML and get heading information
|
11
|
+
#
|
12
|
+
# The heading information this listener will retrieve is as follows:
|
13
|
+
#
|
14
|
+
# * level: Heading level (1..6)
|
15
|
+
# * id: HTMl ID attribute. Basically the `id` attribute of the h(1-6) element, but if there is an `a` element within the h(1-6) element, it will be its `id` attribute.
|
16
|
+
# * title: The title string of the headline. Usually, it is the text within the h(1-6) element, but if there is an `img` element, it will be the text with its `alt` attribute.
|
17
|
+
# * notoc: The `notoc` attribute of the headline element.
|
18
|
+
#
|
10
19
|
class ReVIEWHeaderListener
|
11
20
|
include REXML::StreamListener
|
12
21
|
def initialize(headlines)
|
@@ -18,10 +27,11 @@ module ReVIEW
|
|
18
27
|
def tag_start(name, attrs)
|
19
28
|
if name =~ /\Ah(\d+)/
|
20
29
|
raise "#{name}, #{attrs}" if @level.present?
|
30
|
+
|
21
31
|
@level = $1.to_i
|
22
32
|
@id = attrs['id'] if attrs['id'].present?
|
23
33
|
@notoc = attrs['notoc'] if attrs['notoc'].present?
|
24
|
-
elsif
|
34
|
+
elsif @level.present? # if in <hN> tag
|
25
35
|
if name == 'img' && attrs['alt'].present?
|
26
36
|
@content << attrs['alt']
|
27
37
|
elsif name == 'a' && attrs['id'].present?
|
@@ -49,7 +59,7 @@ module ReVIEW
|
|
49
59
|
|
50
60
|
def text(text)
|
51
61
|
if @level.present?
|
52
|
-
@content << text.
|
62
|
+
@content << text.tr("\t", ' ')
|
53
63
|
end
|
54
64
|
end
|
55
65
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# Copyright (c) 2010-2017 Kenshi Muto and Masayoshi Takahashi
|
2
|
+
#
|
3
|
+
# This program is free software.
|
4
|
+
# You can distribute or modify this program under the terms of
|
5
|
+
# the GNU LGPL, Lesser General Public License version 2.1.
|
6
|
+
# For details of the GNU LGPL, see the file "COPYING".
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
begin
|
11
|
+
require 'zip'
|
12
|
+
rescue LoadError
|
13
|
+
## I cannot find rubyzip library, so I use external zip command.
|
14
|
+
warn 'rubyzip not found, so use external zip command'
|
15
|
+
end
|
16
|
+
|
17
|
+
module ReVIEW
|
18
|
+
class EPUBMaker
|
19
|
+
##
|
20
|
+
# Export into zip file for EPUB producer.
|
21
|
+
#
|
22
|
+
class ZipExporter
|
23
|
+
attr_reader :tmpdir
|
24
|
+
|
25
|
+
def initialize(tmpdir, config)
|
26
|
+
@tmpdir = tmpdir
|
27
|
+
@config = config
|
28
|
+
end
|
29
|
+
|
30
|
+
def export_zip(epubfile)
|
31
|
+
if defined?(Zip)
|
32
|
+
export_zip_rubyzip(epubfile)
|
33
|
+
else
|
34
|
+
export_zip_extcmd(epubfile)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def export_zip_extcmd(epubfile)
|
39
|
+
stage1 = @config['epubmaker']['zip_stage1'].to_s.split
|
40
|
+
path1 = stage1[0] || 'zip'
|
41
|
+
opt1 = stage1[1] || '-0Xq'
|
42
|
+
stage2 = @config['epubmaker']['zip_stage2'].to_s.split
|
43
|
+
path2 = stage2[0] || 'zip'
|
44
|
+
opt2 = stage2[1] || '-Xr9Dq'
|
45
|
+
|
46
|
+
Dir.chdir(tmpdir) do
|
47
|
+
system(path1, opt1, epubfile, 'mimetype')
|
48
|
+
addpath = @config['epubmaker']['zip_addpath']
|
49
|
+
if addpath
|
50
|
+
system(path2, opt2, epubfile, 'META-INF', 'OEBPS', addpath)
|
51
|
+
else
|
52
|
+
system(path2, opt2, epubfile, 'META-INF', 'OEBPS')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def export_zip_rubyzip(epubfile)
|
58
|
+
Dir.chdir(tmpdir) do
|
59
|
+
Zip::OutputStream.open(epubfile) do |epub|
|
60
|
+
root_pathname = Pathname.new(tmpdir)
|
61
|
+
epub.put_next_entry('mimetype', nil, nil, Zip::Entry::STORED)
|
62
|
+
epub << 'application/epub+zip'
|
63
|
+
|
64
|
+
export_zip_rubyzip_addpath(epub, File.join(tmpdir, 'META-INF'), root_pathname)
|
65
|
+
export_zip_rubyzip_addpath(epub, File.join(tmpdir, 'OEBPS'), root_pathname)
|
66
|
+
if @config['zip_addpath'].present?
|
67
|
+
export_zip_rubyzip_addpath(epub, File.join(tmpdir, @config['zip_addpath']), root_pathname)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def export_zip_rubyzip_addpath(epub, dirname, rootdir)
|
74
|
+
Dir[File.join(dirname, '**', '**')].each do |path|
|
75
|
+
next if File.directory?(path)
|
76
|
+
|
77
|
+
relpath = Pathname.new(path).relative_path_from(rootdir)
|
78
|
+
epub.put_next_entry(relpath)
|
79
|
+
epub << File.binread(path)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|