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,372 @@
|
|
1
|
+
# = epubcommon.rb -- super class for EPUBv2 and EPUBv3
|
2
|
+
#
|
3
|
+
# Copyright (c) 2010-2019 Kenshi Muto and Masayoshi Takahashi
|
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/i18n'
|
12
|
+
require 'review/template'
|
13
|
+
begin
|
14
|
+
require 'cgi/escape'
|
15
|
+
rescue LoadError
|
16
|
+
require 'cgi/util'
|
17
|
+
end
|
18
|
+
|
19
|
+
module ReVIEW
|
20
|
+
class EPUBMaker
|
21
|
+
# EPUBCommon is the common class for EPUB producer.
|
22
|
+
# Some methods of this class are overridden by subclasses
|
23
|
+
class EPUBCommon
|
24
|
+
# Construct object with parameter hash +config+ and message resource hash +res+.
|
25
|
+
def initialize(producer)
|
26
|
+
@config = producer.config
|
27
|
+
@contents = producer.contents
|
28
|
+
@body_ext = nil
|
29
|
+
@logger = ReVIEW.logger
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :config
|
33
|
+
attr_reader :contents
|
34
|
+
|
35
|
+
def h(str)
|
36
|
+
CGI.escapeHTML(str)
|
37
|
+
end
|
38
|
+
|
39
|
+
def produce(epubfile, basedir, tmpdir)
|
40
|
+
raise NotImplementedError # should be overridden
|
41
|
+
end
|
42
|
+
|
43
|
+
# Return mimetype content.
|
44
|
+
def mimetype
|
45
|
+
'application/epub+zip'
|
46
|
+
end
|
47
|
+
|
48
|
+
def opf
|
49
|
+
raise NotImplementedError # should be overridden
|
50
|
+
end
|
51
|
+
|
52
|
+
def opf_manifest
|
53
|
+
raise NotImplementedError # should be overridden
|
54
|
+
end
|
55
|
+
|
56
|
+
def opf_metainfo
|
57
|
+
raise NotImplementedError # should be overridden
|
58
|
+
end
|
59
|
+
|
60
|
+
def opf_tocx
|
61
|
+
raise NotImplementedError # should be overridden
|
62
|
+
end
|
63
|
+
|
64
|
+
def opf_path
|
65
|
+
"OEBPS/#{config['bookname']}.opf"
|
66
|
+
end
|
67
|
+
|
68
|
+
def opf_coverimage
|
69
|
+
if config['coverimage']
|
70
|
+
item = contents.find { |content| content.coverimage?(config['coverimage']) }
|
71
|
+
|
72
|
+
unless item
|
73
|
+
raise "coverimage #{config['coverimage']} not found. Abort."
|
74
|
+
end
|
75
|
+
|
76
|
+
%Q( <meta name="cover" content="#{item.id}"/>\n)
|
77
|
+
else
|
78
|
+
''
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def ncx(indentarray)
|
83
|
+
raise NotImplementedError # should be overridden
|
84
|
+
end
|
85
|
+
|
86
|
+
# Return container content.
|
87
|
+
def container
|
88
|
+
@opf_path = opf_path
|
89
|
+
ReVIEW::Template.generate(path: './xml/container.xml.erb', binding: binding)
|
90
|
+
end
|
91
|
+
|
92
|
+
def coverimage
|
93
|
+
return nil unless config['coverimage']
|
94
|
+
|
95
|
+
item = contents.find { |content| content.coverimage?(config['coverimage']) }
|
96
|
+
|
97
|
+
if item
|
98
|
+
item.file
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Return cover content.
|
103
|
+
# If Producer#config["coverimage"] is defined, it will be used for
|
104
|
+
# the cover image.
|
105
|
+
def cover
|
106
|
+
@body_ext = config['epubversion'] >= 3 ? %Q( epub:type="cover") : ''
|
107
|
+
|
108
|
+
if config['coverimage']
|
109
|
+
@coverimage_src = coverimage
|
110
|
+
raise "coverimage #{config['coverimage']} not found. Abort." unless @coverimage_src
|
111
|
+
end
|
112
|
+
@body = ReVIEW::Template.generate(path: './html/_cover.html.erb', binding: binding)
|
113
|
+
|
114
|
+
@title = h(config.name_of('title'))
|
115
|
+
@language = config['language']
|
116
|
+
@stylesheets = config['stylesheet']
|
117
|
+
template_path = if config['htmlversion'].to_i == 5
|
118
|
+
'./html/layout-html5.html.erb'
|
119
|
+
else
|
120
|
+
'./html/layout-xhtml1.html.erb'
|
121
|
+
end
|
122
|
+
ReVIEW::Template.generate(path: template_path, binding: binding)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Return title (copying) content.
|
126
|
+
# NOTE: this method is not used yet.
|
127
|
+
# see lib/review/epubmaker.rb#build_titlepage
|
128
|
+
def titlepage
|
129
|
+
@title = h(config.name_of('title'))
|
130
|
+
|
131
|
+
@title_str = config.name_of('title')
|
132
|
+
if config['subtitle']
|
133
|
+
@subtitle_str = config.name_of('subtitle')
|
134
|
+
end
|
135
|
+
if config['aut']
|
136
|
+
@author_str = join_with_separator(config.names_of('aut'), ReVIEW::I18n.t('names_splitter'))
|
137
|
+
end
|
138
|
+
if config.names_of('pbl')
|
139
|
+
@publisher_str = join_with_separator(config.names_of('pbl'), ReVIEW::I18n.t('names_splitter'))
|
140
|
+
end
|
141
|
+
@body = ReVIEW::Template.generate(path: './html/_titlepage.html.erb', binding: binding)
|
142
|
+
|
143
|
+
@language = config['language']
|
144
|
+
@stylesheets = config['stylesheet']
|
145
|
+
template_path = if config['htmlversion'].to_i == 5
|
146
|
+
'./html/layout-html5.html.erb'
|
147
|
+
else
|
148
|
+
'./html/layout-xhtml1.html.erb'
|
149
|
+
end
|
150
|
+
ReVIEW::Template.generate(path: template_path, binding: binding)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Return colophon content.
|
154
|
+
def colophon
|
155
|
+
@title = h(ReVIEW::I18n.t('colophontitle'))
|
156
|
+
@isbn_hyphen = isbn_hyphen
|
157
|
+
|
158
|
+
@body = ReVIEW::Template.generate(path: './html/_colophon.html.erb', binding: binding)
|
159
|
+
|
160
|
+
@language = config['language']
|
161
|
+
@stylesheets = config['stylesheet']
|
162
|
+
template_path = if config['htmlversion'].to_i == 5
|
163
|
+
'./html/layout-html5.html.erb'
|
164
|
+
else
|
165
|
+
'./html/layout-xhtml1.html.erb'
|
166
|
+
end
|
167
|
+
ReVIEW::Template.generate(path: template_path, binding: binding)
|
168
|
+
end
|
169
|
+
|
170
|
+
def isbn_hyphen
|
171
|
+
str = config['isbn'].to_s
|
172
|
+
|
173
|
+
if str =~ /\A\d{10}\Z/
|
174
|
+
"#{str[0..0]}-#{str[1..5]}-#{str[6..8]}-#{str[9..9]}"
|
175
|
+
elsif str =~ /\A\d{13}\Z/
|
176
|
+
"#{str[0..2]}-#{str[3..3]}-#{str[4..8]}-#{str[9..11]}-#{str[12..12]}"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def colophon_history
|
181
|
+
@col_history = []
|
182
|
+
if config['history']
|
183
|
+
config['history'].each_with_index do |items, edit|
|
184
|
+
items.each_with_index do |item, rev|
|
185
|
+
editstr = edit == 0 ? ReVIEW::I18n.t('first_edition') : ReVIEW::I18n.t('nth_edition', (edit + 1).to_s)
|
186
|
+
revstr = ReVIEW::I18n.t('nth_impression', (rev + 1).to_s)
|
187
|
+
if item =~ /\A\d+-\d+-\d+\Z/
|
188
|
+
@col_history << ReVIEW::I18n.t('published_by1', [date_to_s(item), editstr + revstr])
|
189
|
+
elsif item =~ /\A(\d+-\d+-\d+)[\s ](.+)/
|
190
|
+
# custom date with string
|
191
|
+
item.match(/\A(\d+-\d+-\d+)[\s ](.+)/) do |m|
|
192
|
+
@col_history << ReVIEW::I18n.t('published_by3', [date_to_s(m[1]), m[2]])
|
193
|
+
end
|
194
|
+
else
|
195
|
+
# free format
|
196
|
+
@col_history << item
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
ReVIEW::Template.generate(path: './html/_colophon_history.html.erb', binding: binding)
|
203
|
+
end
|
204
|
+
|
205
|
+
def date_to_s(date)
|
206
|
+
require 'date'
|
207
|
+
d = Date.parse(date)
|
208
|
+
d.strftime(ReVIEW::I18n.t('date_format'))
|
209
|
+
end
|
210
|
+
|
211
|
+
# Return own toc content.
|
212
|
+
def mytoc
|
213
|
+
@title = h(ReVIEW::I18n.t('toctitle'))
|
214
|
+
|
215
|
+
@body = %Q( <h1 class="toc-title">#{h(ReVIEW::I18n.t('toctitle'))}</h1>\n)
|
216
|
+
if config['epubmaker']['flattoc'].nil?
|
217
|
+
@body << hierarchy_ncx('ul')
|
218
|
+
else
|
219
|
+
@body << flat_ncx('ul', config['epubmaker']['flattocindent'])
|
220
|
+
end
|
221
|
+
|
222
|
+
@language = config['language']
|
223
|
+
@stylesheets = config['stylesheet']
|
224
|
+
template_path = if config['htmlversion'].to_i == 5
|
225
|
+
'./html/layout-html5.html.erb'
|
226
|
+
else
|
227
|
+
'./html/layout-xhtml1.html.erb'
|
228
|
+
end
|
229
|
+
ReVIEW::Template.generate(path: template_path, binding: binding)
|
230
|
+
end
|
231
|
+
|
232
|
+
def hierarchy_ncx(type)
|
233
|
+
require 'rexml/document'
|
234
|
+
level = 1
|
235
|
+
find_jump = nil
|
236
|
+
has_part = nil
|
237
|
+
toclevel = config['toclevel'].to_i
|
238
|
+
|
239
|
+
# check part existance
|
240
|
+
contents.each do |item|
|
241
|
+
next if item.notoc || item.chaptype != 'part'
|
242
|
+
|
243
|
+
has_part = true
|
244
|
+
break
|
245
|
+
end
|
246
|
+
|
247
|
+
if has_part
|
248
|
+
contents.each do |item|
|
249
|
+
if item.chaptype == 'part' && item.level > 0
|
250
|
+
# sections in part
|
251
|
+
item.level -= 1
|
252
|
+
end
|
253
|
+
# down level for part and chaps. pre, appendix, post are preserved
|
254
|
+
if item.chaptype == 'part' || item.chaptype == 'body'
|
255
|
+
item.level += 1
|
256
|
+
end
|
257
|
+
end
|
258
|
+
toclevel += 1
|
259
|
+
end
|
260
|
+
|
261
|
+
doc = REXML::Document.new(%Q(<#{type} class="toc-h#{level}"><li /></#{type}>))
|
262
|
+
doc.context[:attribute_quote] = :quote
|
263
|
+
|
264
|
+
e = doc.root.elements[1] # first <li/>
|
265
|
+
contents.each do |item|
|
266
|
+
next if !item.notoc.nil? || item.level.nil? || item.file.nil? || item.title.nil? || item.level > toclevel
|
267
|
+
|
268
|
+
if item.level == level
|
269
|
+
e2 = e.parent.add_element('li')
|
270
|
+
e = e2
|
271
|
+
elsif item.level > level
|
272
|
+
find_jump = true if (item.level - level) > 1
|
273
|
+
# deeper
|
274
|
+
(level + 1).upto(item.level) do |n|
|
275
|
+
if e.size == 0
|
276
|
+
# empty span for epubcheck
|
277
|
+
e.attributes['style'] = 'list-style-type: none;'
|
278
|
+
es = e.add_element('span', 'style' => 'display:none;')
|
279
|
+
es.add_text(REXML::Text.new(' ', false, nil, true))
|
280
|
+
end
|
281
|
+
|
282
|
+
e2 = e.add_element(type, 'class' => "toc-h#{n}")
|
283
|
+
e3 = e2.add_element('li')
|
284
|
+
e = e3
|
285
|
+
end
|
286
|
+
level = item.level
|
287
|
+
elsif item.level < level
|
288
|
+
# shallower
|
289
|
+
(level - 1).downto(item.level) { e = e.parent.parent }
|
290
|
+
e2 = e.parent.add_element('li')
|
291
|
+
e = e2
|
292
|
+
level = item.level
|
293
|
+
end
|
294
|
+
e2 = e.add_element('a', 'href' => item.file)
|
295
|
+
e2.add_text(REXML::Text.new(item.title, true))
|
296
|
+
end
|
297
|
+
|
298
|
+
warn %Q(found level jumping in table of contents. consider to use 'epubmaker:flattoc: true' for strict ePUB validator.) unless find_jump.nil?
|
299
|
+
|
300
|
+
doc.to_s.gsub('<li/>', '').gsub('</li>', "</li>\n").gsub("<#{type} ", "\n" + '\&') # ugly
|
301
|
+
end
|
302
|
+
|
303
|
+
def flat_ncx(type, indent = nil)
|
304
|
+
s = %Q(<#{type} class="toc-h1">\n)
|
305
|
+
contents.each do |item|
|
306
|
+
next if !item.notoc.nil? || item.level.nil? || item.file.nil? || item.title.nil? || item.level > config['toclevel'].to_i
|
307
|
+
|
308
|
+
is = indent == true ? ' ' * item.level : ''
|
309
|
+
s << %Q(<li><a href="#{item.file}">#{is}#{h(item.title)}</a></li>\n)
|
310
|
+
end
|
311
|
+
s << %Q(</#{type}>\n)
|
312
|
+
|
313
|
+
s
|
314
|
+
end
|
315
|
+
|
316
|
+
def produce_write_common(basedir, tmpdir)
|
317
|
+
File.write("#{tmpdir}/mimetype", mimetype)
|
318
|
+
|
319
|
+
FileUtils.mkdir_p("#{tmpdir}/META-INF")
|
320
|
+
File.write("#{tmpdir}/META-INF/container.xml", container)
|
321
|
+
|
322
|
+
FileUtils.mkdir_p("#{tmpdir}/OEBPS")
|
323
|
+
File.write(File.join(tmpdir, opf_path), opf)
|
324
|
+
|
325
|
+
if File.exist?("#{basedir}/#{config['cover']}")
|
326
|
+
FileUtils.cp("#{basedir}/#{config['cover']}", "#{tmpdir}/OEBPS")
|
327
|
+
else
|
328
|
+
File.write("#{tmpdir}/OEBPS/#{config['cover']}", cover)
|
329
|
+
end
|
330
|
+
|
331
|
+
if config['colophon'] && !config['colophon'].is_a?(String)
|
332
|
+
filename = File.join(basedir, "colophon.#{config['htmlext']}")
|
333
|
+
File.write(filename, colophon)
|
334
|
+
end
|
335
|
+
|
336
|
+
contents.each do |item|
|
337
|
+
next if item.file =~ /#/ # skip subgroup
|
338
|
+
|
339
|
+
fname = "#{basedir}/#{item.file}"
|
340
|
+
unless File.exist?(fname)
|
341
|
+
raise "#{fname} is not found."
|
342
|
+
end
|
343
|
+
|
344
|
+
FileUtils.mkdir_p(File.dirname("#{tmpdir}/OEBPS/#{item.file}"))
|
345
|
+
FileUtils.cp(fname, "#{tmpdir}/OEBPS/#{item.file}")
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
def call_hook(filename, *params)
|
350
|
+
return if !filename.present? || !File.exist?(filename) || !FileTest.executable?(filename)
|
351
|
+
|
352
|
+
if ENV['REVIEW_SAFE_MODE'].to_i & 1 > 0
|
353
|
+
warn 'hook is prohibited in safe mode. ignored.'
|
354
|
+
else
|
355
|
+
system(filename, *params)
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
def legacy_cover_and_title_file(loadfile, writefile)
|
360
|
+
FileUtils.cp(loadfile, writefile)
|
361
|
+
end
|
362
|
+
|
363
|
+
def join_with_separator(value, sep)
|
364
|
+
if value.is_a?(Array)
|
365
|
+
value.join(sep)
|
366
|
+
else
|
367
|
+
value
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
# = epubv2.rb -- EPUB version 2 producer.
|
2
|
+
#
|
3
|
+
# Copyright (c) 2010-2017 Kenshi Muto and Masayoshi Takahashi
|
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
|
+
# EPUBv2 is EPUB version 2 producer.
|
18
|
+
class EPUBv2 < 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[aut 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-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]
|
23
|
+
CONTRIBUTER_ATTRIBUTES = %w[adp ann arr art asn aqt aft aui ant bkp clb cmm dsr edt ill lyr mdc mus nrt oth 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) # rubocop:disable Lint/UselessMethodDefinition
|
27
|
+
super
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return opf file content.
|
31
|
+
def opf
|
32
|
+
@opf_metainfo = opf_metainfo
|
33
|
+
@opf_coverimage = opf_coverimage
|
34
|
+
@opf_manifest = opf_manifest
|
35
|
+
@opf_toc = opf_tocx
|
36
|
+
|
37
|
+
ReVIEW::Template.generate(path: './opf/epubv2.opf.erb', binding: binding)
|
38
|
+
end
|
39
|
+
|
40
|
+
def opf_metainfo
|
41
|
+
@dc_items = DC_ITEMS.map do |item|
|
42
|
+
if config[item]
|
43
|
+
if config[item].is_a?(Array)
|
44
|
+
config.names_of(item).map { |_v| { tag: "dc:#{item}", val: item_sub } }
|
45
|
+
else
|
46
|
+
{ tag: "dc:#{item}", val: config.name_of(item).to_s }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end.flatten.compact
|
50
|
+
|
51
|
+
# creator (should be array)
|
52
|
+
@creators = CREATOR_ATTRIBUTES.map do |role|
|
53
|
+
if config[role]
|
54
|
+
config.names_of(role).map { |v| { role: role, val: v } }
|
55
|
+
end
|
56
|
+
end.flatten.compact
|
57
|
+
|
58
|
+
# contributor (should be array)
|
59
|
+
@contributers = CONTRIBUTER_ATTRIBUTES.map do |role|
|
60
|
+
if config[role]
|
61
|
+
config.names_of(role).map { |v| { role: role, val: v } }
|
62
|
+
end
|
63
|
+
end.flatten.compact
|
64
|
+
|
65
|
+
ReVIEW::Template.generate(path: './opf/opf_metainfo_epubv2.opf.erb', binding: binding)
|
66
|
+
end
|
67
|
+
|
68
|
+
def opf_manifest
|
69
|
+
@items = contents.find_all { |item| item.file !~ /#/ } # skip subgroup
|
70
|
+
|
71
|
+
ReVIEW::Template.generate(path: './opf/opf_manifest_epubv2.opf.erb', binding: binding)
|
72
|
+
end
|
73
|
+
|
74
|
+
def opf_tocx
|
75
|
+
@cover_linear = if config['epubmaker']['cover_linear'] && config['epubmaker']['cover_linear'] != 'no'
|
76
|
+
'yes'
|
77
|
+
else
|
78
|
+
'no'
|
79
|
+
end
|
80
|
+
@ncx_contents = contents.find_all { |content| content.media =~ /xhtml\+xml/ } # skip non XHTML
|
81
|
+
|
82
|
+
ReVIEW::Template.generate(path: './opf/opf_tocx_epubv2.opf.erb', binding: binding)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Return ncx content. +indentarray+ has prefix marks for each level.
|
86
|
+
def ncx(indentarray)
|
87
|
+
@ncx_isbn = ncx_isbn
|
88
|
+
@ncx_doctitle = ncx_doctitle
|
89
|
+
@ncx_navmap = ncx_navmap(indentarray)
|
90
|
+
|
91
|
+
ReVIEW::Template.generate(path: './ncx/epubv2.ncx.erb', binding: binding)
|
92
|
+
end
|
93
|
+
|
94
|
+
def ncx_isbn
|
95
|
+
uid = config['isbn'] || config['urnid']
|
96
|
+
%Q( <meta name="dtb:uid" content="#{uid}"/>\n)
|
97
|
+
end
|
98
|
+
|
99
|
+
def ncx_doctitle
|
100
|
+
<<-EOT
|
101
|
+
<docTitle>
|
102
|
+
<text>#{h(config['title'])}</text>
|
103
|
+
</docTitle>
|
104
|
+
<docAuthor>
|
105
|
+
<text>#{config['aut'].nil? ? '' : h(join_with_separator(config['aut'], ReVIEW::I18n.t('names_splitter')))}</text>
|
106
|
+
</docAuthor>
|
107
|
+
EOT
|
108
|
+
end
|
109
|
+
|
110
|
+
def ncx_navmap(indentarray)
|
111
|
+
s = <<EOT
|
112
|
+
<navMap>
|
113
|
+
<navPoint id="top" playOrder="1">
|
114
|
+
<navLabel>
|
115
|
+
<text>#{h(config['title'])}</text>
|
116
|
+
</navLabel>
|
117
|
+
<content src="#{config['cover']}"/>
|
118
|
+
</navPoint>
|
119
|
+
EOT
|
120
|
+
|
121
|
+
nav_count = 2
|
122
|
+
|
123
|
+
unless config['mytoc'].nil?
|
124
|
+
s << <<EOT
|
125
|
+
<navPoint id="toc" playOrder="#{nav_count}">
|
126
|
+
<navLabel>
|
127
|
+
<text>#{h(ReVIEW::I18n.t('toctitle'))}</text>
|
128
|
+
</navLabel>
|
129
|
+
<content src="#{config['bookname']}-toc.#{config['htmlext']}"/>
|
130
|
+
</navPoint>
|
131
|
+
EOT
|
132
|
+
nav_count += 1
|
133
|
+
end
|
134
|
+
|
135
|
+
contents.each do |item|
|
136
|
+
next if item.title.nil?
|
137
|
+
|
138
|
+
indent = indentarray.nil? ? [''] : indentarray
|
139
|
+
level = item.level.nil? ? 0 : (item.level - 1)
|
140
|
+
level = indent.size - 1 if level >= indent.size
|
141
|
+
s << <<EOT
|
142
|
+
<navPoint id="nav-#{nav_count}" playOrder="#{nav_count}">
|
143
|
+
<navLabel>
|
144
|
+
<text>#{indent[level]}#{h(item.title)}</text>
|
145
|
+
</navLabel>
|
146
|
+
<content src="#{item.file}"/>
|
147
|
+
</navPoint>
|
148
|
+
EOT
|
149
|
+
nav_count += 1
|
150
|
+
end
|
151
|
+
|
152
|
+
s << <<EOT
|
153
|
+
</navMap>
|
154
|
+
EOT
|
155
|
+
s
|
156
|
+
end
|
157
|
+
|
158
|
+
# Produce EPUB file +epubfile+.
|
159
|
+
# +basedir+ points the directory has contents.
|
160
|
+
# +tmpdir+ defines temporary directory.
|
161
|
+
def produce(epubfile, work_dir, tmpdir, base_dir:)
|
162
|
+
produce_write_common(work_dir, tmpdir)
|
163
|
+
|
164
|
+
ncx_file = "#{tmpdir}/OEBPS/#{config['bookname']}.ncx"
|
165
|
+
File.write(ncx_file, ncx(config['epubmaker']['ncxindent']))
|
166
|
+
|
167
|
+
if config['mytoc']
|
168
|
+
toc_file = "#{tmpdir}/OEBPS/#{config['bookname']}-toc.#{config['htmlext']}"
|
169
|
+
File.write(toc_file, mytoc)
|
170
|
+
end
|
171
|
+
|
172
|
+
call_hook('hook_prepack', tmpdir, base_dir: base_dir)
|
173
|
+
expoter = ReVIEW::EPUBMaker::ZipExporter.new(tmpdir, config)
|
174
|
+
expoter.export_zip(epubfile)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|