review 5.0.0 → 5.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (173) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby-tex.yml +35 -0
  3. data/.github/workflows/ruby-win.yml +8 -4
  4. data/.github/workflows/ruby.yml +6 -2
  5. data/.rubocop.yml +24 -9
  6. data/NEWS.ja.md +215 -0
  7. data/NEWS.md +215 -1
  8. data/README.md +7 -6
  9. data/Rakefile +7 -2
  10. data/bin/review +2 -4
  11. data/bin/review-catalog-converter +3 -3
  12. data/bin/review-check +6 -8
  13. data/bin/review-checkdep +1 -4
  14. data/bin/review-compile +10 -20
  15. data/bin/review-epub2html +1 -4
  16. data/bin/review-epubmaker +3 -4
  17. data/bin/review-idgxmlmaker +1 -3
  18. data/bin/review-index +11 -5
  19. data/bin/review-init +1 -4
  20. data/bin/review-pdfmaker +1 -3
  21. data/bin/review-preproc +30 -38
  22. data/bin/review-textmaker +1 -3
  23. data/bin/review-update +1 -4
  24. data/bin/review-validate +3 -3
  25. data/bin/review-vol +1 -4
  26. data/bin/review-webmaker +1 -3
  27. data/doc/config.yml.sample +23 -5
  28. data/doc/config.yml.sample-simple +1 -1
  29. data/doc/format.ja.md +49 -12
  30. data/doc/format.md +52 -12
  31. data/doc/quickstart.ja.md +11 -1
  32. data/doc/quickstart.md +11 -2
  33. data/doc/writing_vertical.ja.md +6 -0
  34. data/lib/review/book/base.rb +4 -0
  35. data/lib/review/book/book_unit.rb +15 -2
  36. data/lib/review/book/chapter.rb +3 -0
  37. data/lib/review/book/index.rb +5 -1
  38. data/lib/review/book/volume.rb +1 -0
  39. data/lib/review/builder.rb +90 -54
  40. data/lib/review/call_hook.rb +20 -0
  41. data/lib/review/catalog.rb +2 -0
  42. data/lib/review/compiler.rb +88 -52
  43. data/lib/review/configure.rb +64 -7
  44. data/lib/review/epubmaker/content.rb +113 -0
  45. data/lib/review/epubmaker/epubcommon.rb +372 -0
  46. data/lib/review/epubmaker/epubv2.rb +178 -0
  47. data/lib/review/epubmaker/epubv3.rb +231 -0
  48. data/lib/review/epubmaker/producer.rb +167 -0
  49. data/lib/review/epubmaker/reviewheaderlistener.rb +12 -2
  50. data/lib/review/epubmaker/zip_exporter.rb +84 -0
  51. data/lib/review/epubmaker.rb +114 -129
  52. data/lib/review/exception.rb +13 -0
  53. data/lib/review/htmlbuilder.rb +109 -67
  54. data/lib/review/htmlutils.rb +1 -1
  55. data/lib/review/i18n.rb +1 -0
  56. data/lib/review/i18n.yml +6 -0
  57. data/lib/review/idgxmlbuilder.rb +72 -48
  58. data/lib/review/idgxmlmaker.rb +15 -14
  59. data/lib/review/img_math.rb +239 -0
  60. data/lib/review/index_builder.rb +90 -32
  61. data/lib/review/init.rb +4 -4
  62. data/lib/review/latexbox.rb +58 -0
  63. data/lib/review/latexbuilder.rb +79 -58
  64. data/lib/review/latexutils.rb +9 -1
  65. data/lib/review/lineinput.rb +112 -2
  66. data/lib/review/loggable.rb +27 -0
  67. data/lib/review/logger.rb +89 -2
  68. data/lib/review/makerhelper.rb +7 -206
  69. data/lib/review/markdownbuilder.rb +44 -4
  70. data/lib/review/pdfmaker.rb +70 -51
  71. data/lib/review/plaintextbuilder.rb +20 -11
  72. data/lib/review/preprocessor/directive.rb +35 -0
  73. data/lib/review/preprocessor/line.rb +34 -0
  74. data/lib/review/preprocessor/repository.rb +177 -0
  75. data/lib/review/preprocessor.rb +105 -301
  76. data/lib/review/rstbuilder.rb +13 -4
  77. data/lib/review/sec_counter.rb +1 -0
  78. data/lib/review/template.rb +11 -1
  79. data/lib/review/textmaker.rb +23 -20
  80. data/lib/review/textutils.rb +10 -17
  81. data/lib/review/tocprinter.rb +93 -71
  82. data/lib/review/topbuilder.rb +44 -19
  83. data/lib/review/update.rb +5 -6
  84. data/lib/review/version.rb +1 -1
  85. data/lib/review/volumeprinter.rb +11 -12
  86. data/lib/review/webmaker.rb +31 -27
  87. data/lib/review/webtocprinter.rb +10 -9
  88. data/lib/review/yamlloader.rb +2 -1
  89. data/lib/review.rb +1 -1
  90. data/review.gemspec +5 -3
  91. data/samples/sample-book/src/config-epub2.yml +1 -1
  92. data/samples/sample-book/src/config.yml +1 -1
  93. data/samples/sample-book/src/lib/tasks/review.rake +19 -1
  94. data/samples/sample-book/src/lib/tasks/z01_copy_sty.rake +2 -1
  95. data/samples/syntax-book/ch01.re +1 -1
  96. data/samples/syntax-book/ch02.re +30 -6
  97. data/samples/syntax-book/ch03.re +1 -1
  98. data/samples/syntax-book/images/img3-2.png +0 -0
  99. data/samples/syntax-book/lib/tasks/z01_copy_sty.rake +2 -1
  100. data/templates/html/_colophon.html.erb +23 -0
  101. data/templates/html/_colophon_history.html.erb +9 -0
  102. data/templates/html/_cover.html.erb +10 -0
  103. data/templates/html/_part_body.html.erb +6 -0
  104. data/templates/html/_titlepage.html.erb +20 -0
  105. data/templates/html/layout-html5.html.erb +6 -0
  106. data/templates/html/layout-xhtml1.html.erb +6 -0
  107. data/templates/latex/config.erb +11 -0
  108. data/templates/latex/review-jlreq/review-base.sty +7 -9
  109. data/templates/latex/review-jlreq/review-jlreq.cls +48 -6
  110. data/templates/latex/review-jlreq/review-style.sty +6 -1
  111. data/templates/latex/review-jlreq/review-tcbox.sty +348 -0
  112. data/templates/latex/review-jlreq/reviewmacro.sty +5 -0
  113. data/templates/latex/review-jsbook/review-base.sty +13 -9
  114. data/templates/latex/review-jsbook/review-jsbook.cls +41 -6
  115. data/templates/latex/review-jsbook/review-style.sty +6 -1
  116. data/templates/latex/review-jsbook/review-tcbox.sty +348 -0
  117. data/templates/latex/review-jsbook/reviewmacro.sty +5 -0
  118. data/templates/opf/epubv2.opf.erb +7 -7
  119. data/templates/opf/epubv3.opf.erb +7 -7
  120. data/templates/opf/opf_manifest_epubv2.opf.erb +10 -0
  121. data/templates/opf/opf_manifest_epubv3.opf.erb +10 -0
  122. data/templates/opf/opf_metainfo_epubv2.opf.erb +17 -0
  123. data/templates/opf/opf_metainfo_epubv3.opf.erb +49 -0
  124. data/templates/opf/opf_tocx_epubv2.opf.erb +9 -0
  125. data/templates/opf/opf_tocx_epubv3.opf.erb +17 -0
  126. data/templates/web/html/layout-html5.html.erb +6 -5
  127. data/templates/web/html/layout-xhtml1.html.erb +6 -0
  128. data/test/assets/header_listener.html +35 -0
  129. data/test/assets/img_math/img1.png +0 -0
  130. data/test/assets/img_math/img2.png +0 -0
  131. data/test/assets/img_math/img3.png +0 -0
  132. data/test/assets/syntax_book_index_detail.txt +60 -0
  133. data/test/assets/test_template.tex +7 -1
  134. data/test/assets/test_template_backmatter.tex +7 -1
  135. data/test/run_test.rb +1 -1
  136. data/test/test_book_chapter.rb +27 -4
  137. data/test/test_builder.rb +10 -8
  138. data/test/test_catalog_converter_cmd.rb +1 -1
  139. data/test/test_epub3maker.rb +168 -124
  140. data/test/test_epubmaker.rb +248 -131
  141. data/test/test_epubmaker_cmd.rb +15 -4
  142. data/test/test_helper.rb +5 -4
  143. data/test/test_htmlbuilder.rb +170 -31
  144. data/test/test_idgxmlbuilder.rb +44 -23
  145. data/test/test_idgxmlmaker_cmd.rb +7 -3
  146. data/test/test_img_math.rb +111 -0
  147. data/test/test_index.rb +30 -4
  148. data/test/test_indexbuilder.rb +5 -5
  149. data/test/test_latexbuilder.rb +151 -26
  150. data/test/test_latexbuilder_v2.rb +18 -10
  151. data/test/test_lineinput.rb +20 -93
  152. data/test/test_markdownbuilder.rb +42 -0
  153. data/test/test_pdfmaker.rb +90 -0
  154. data/test/test_pdfmaker_cmd.rb +2 -2
  155. data/test/test_plaintextbuilder.rb +56 -40
  156. data/test/test_preprocessor.rb +188 -1
  157. data/test/test_reviewheaderlistener.rb +49 -0
  158. data/test/test_rstbuilder.rb +13 -0
  159. data/test/test_template.rb +12 -2
  160. data/test/test_textmaker_cmd.rb +5 -1
  161. data/test/test_tocprinter.rb +46 -0
  162. data/test/test_topbuilder.rb +50 -19
  163. data/test/test_update.rb +34 -34
  164. data/test/test_zip_exporter.rb +5 -6
  165. metadata +95 -17
  166. data/lib/epubmaker/content.rb +0 -111
  167. data/lib/epubmaker/epubcommon.rb +0 -449
  168. data/lib/epubmaker/epubv2.rb +0 -142
  169. data/lib/epubmaker/epubv3.rb +0 -235
  170. data/lib/epubmaker/producer.rb +0 -375
  171. data/lib/epubmaker/zip_exporter.rb +0 -81
  172. data/lib/epubmaker.rb +0 -23
  173. 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,167 @@
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
+ require 'review/loggable'
29
+
30
+ module ReVIEW
31
+ class EPUBMaker
32
+ # EPUBMaker produces EPUB file.
33
+ class Producer
34
+ include Loggable
35
+
36
+ # Array of content objects.
37
+ attr_accessor :contents
38
+ # Parameter hash.
39
+ attr_accessor :config
40
+ # Message resource object.
41
+ attr_reader :res
42
+
43
+ # Construct producer object.
44
+ # +config+ takes initial parameter hash.
45
+ def initialize(config)
46
+ @contents = []
47
+ @config = config
48
+ @config.maker = 'epubmaker'
49
+ @epub = nil
50
+ @res = ReVIEW::I18n
51
+ @logger = ReVIEW.logger
52
+ modify_config
53
+ end
54
+
55
+ # Modify parameters for EPUB specific.
56
+ def modify_config
57
+ if @config['epubversion'] >= 3
58
+ @config['htmlversion'] = 5
59
+ end
60
+
61
+ @config['title'] ||= @config['booktitle']
62
+ @config['cover'] ||= "#{@config['bookname']}.#{@config['htmlext']}"
63
+
64
+ %w[bookname title].each do |k|
65
+ unless @config[k]
66
+ raise "Key #{k} must have a value. Abort."
67
+ end
68
+ end
69
+
70
+ case @config['epubversion'].to_i
71
+ when 2
72
+ @epub = ReVIEW::EPUBMaker::EPUBv2.new(self)
73
+ when 3
74
+ @epub = ReVIEW::EPUBMaker::EPUBv3.new(self)
75
+ else
76
+ raise "Invalid EPUB version (#{@config['epubversion']}.)"
77
+ end
78
+
79
+ ReVIEW::I18n.locale = @config['language']
80
+ support_legacy_maker
81
+ end
82
+
83
+ # Add informations of figure files in +path+ to contents array.
84
+ # +base+ defines a string to remove from path name.
85
+ def import_imageinfo(path, base = nil, allow_exts = nil)
86
+ return nil unless File.exist?(path)
87
+
88
+ allow_exts ||= @config['image_ext']
89
+ Dir.foreach(path) do |f|
90
+ next if f.start_with?('.')
91
+
92
+ if f =~ /\.(#{allow_exts.join('|')})\Z/i
93
+ path.chop! if path =~ %r{/\Z}
94
+ if base.nil?
95
+ @contents.push(ReVIEW::EPUBMaker::Content.new(file: "#{path}/#{f}"))
96
+ else
97
+ @contents.push(ReVIEW::EPUBMaker::Content.new(file: "#{path.sub(base + '/', '')}/#{f}"))
98
+ end
99
+ end
100
+ if FileTest.directory?("#{path}/#{f}")
101
+ import_imageinfo("#{path}/#{f}", base)
102
+ end
103
+ end
104
+ end
105
+
106
+ alias_method :importImageInfo, :import_imageinfo
107
+
108
+ # Produce EPUB file +epubfile+.
109
+ # +work_dir+ points the directory has contents (default: current directory.)
110
+ # +tmpdir+ defines temporary directory.
111
+ # +base_dir+ is original root dir.
112
+ def produce(epubfile, work_dir, tmpdir = nil, base_dir: nil)
113
+ current = Dir.pwd
114
+ base_dir ||= current
115
+
116
+ # use Dir to solve a path for Windows (see #1011)
117
+ new_tmpdir = Dir[File.join(tmpdir.nil? ? Dir.mktmpdir : tmpdir)][0]
118
+ if epubfile !~ %r{\A/}
119
+ epubfile = "#{current}/#{epubfile}"
120
+ end
121
+
122
+ # FIXME: error check
123
+ File.unlink(epubfile) if File.exist?(epubfile)
124
+
125
+ begin
126
+ @epub.produce(epubfile, work_dir, new_tmpdir, base_dir: base_dir)
127
+ ensure
128
+ FileUtils.rm_r(new_tmpdir) if tmpdir.nil?
129
+ end
130
+ end
131
+
132
+ private
133
+
134
+ def support_legacy_maker
135
+ # legacy review-epubmaker support
136
+ if @config['flag_legacy_coverfile'].nil? && !@config['coverfile'].nil? && File.exist?(@config['coverfile'])
137
+ @config['cover'] = "#{@config['bookname']}-cover.#{@config['htmlext']}"
138
+ @epub.legacy_cover_and_title_file(@config['coverfile'], @config['cover'])
139
+ @config['flag_legacy_coverfile'] = true
140
+ warn %Q(Parameter 'coverfile' is obsolete. Please use 'cover' and make complete html file with header and footer.)
141
+ end
142
+
143
+ if @config['flag_legacy_titlepagefile'].nil? && !@config['titlepagefile'].nil? && File.exist?(@config['titlepagefile'])
144
+ @config['titlefile'] = "#{@config['bookname']}-title.#{@config['htmlext']}"
145
+ @config['titlepage'] = true
146
+ @epub.legacy_cover_and_title_file(@config['titlepagefile'], @config['titlefile'])
147
+ @config['flag_legacy_titlepagefile'] = true
148
+ warn %Q(Parameter 'titlepagefile' is obsolete. Please use 'titlefile' and make complete html file with header and footer.)
149
+ end
150
+
151
+ if @config['flag_legacy_backcoverfile'].nil? && !@config['backcoverfile'].nil? && File.exist?(@config['backcoverfile'])
152
+ @config['backcover'] = "#{@config['bookname']}-backcover.#{@config['htmlext']}"
153
+ @epub.legacy_cover_and_title_file(@config['backcoverfile'], @config['backcover'])
154
+ @config['flag_legacy_backcoverfile'] = true
155
+ warn %Q(Parameter 'backcoverfile' is obsolete. Please use 'backcover' and make complete html file with header and footer.)
156
+ end
157
+
158
+ if @config['flag_legacy_pubhistory'].nil? && @config['pubhistory']
159
+ @config['history'] = [[]]
160
+ @config['pubhistory'].split("\n").each { |date| @config['history'][0].push(date.sub(/(\d+)年(\d+)月(\d+)日/, '\1-\2-\3')) }
161
+ @config['flag_legacy_pubhistory'] = true
162
+ warn %Q(Parameter 'pubhistory' is obsolete. Please use 'history' array.)
163
+ end
164
+ end
165
+ end
166
+ end
167
+ 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 !@level.nil?
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.gsub("\t", ' ')
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