review 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +1 -0
  3. data/ChangeLog +100 -0
  4. data/README.rdoc +11 -12
  5. data/Rakefile +6 -2
  6. data/bin/review-check +1 -1
  7. data/bin/review-compile +13 -4
  8. data/bin/review-epubmaker +172 -24
  9. data/bin/review-epubmaker-ng +8 -161
  10. data/bin/review-index +1 -1
  11. data/bin/review-init +17 -5
  12. data/bin/review-pdfmaker +40 -19
  13. data/bin/review-preproc +1 -1
  14. data/bin/review-validate +2 -2
  15. data/bin/review-vol +1 -1
  16. data/debian/control +2 -2
  17. data/doc/format.rdoc +23 -6
  18. data/doc/format_idg.rdoc +3 -3
  19. data/doc/libepubmaker/config.yaml +163 -0
  20. data/doc/quickstart.rdoc +27 -27
  21. data/doc/sample.yaml +10 -5
  22. data/lib/epubmaker.rb +2 -5
  23. data/lib/epubmaker/content.rb +9 -6
  24. data/lib/epubmaker/epubv2.rb +233 -109
  25. data/lib/epubmaker/epubv3.rb +83 -119
  26. data/lib/epubmaker/producer.rb +50 -20
  27. data/lib/epubmaker/resource.rb +22 -8
  28. data/lib/review/book/base.rb +1 -0
  29. data/lib/review/book/chapter.rb +2 -2
  30. data/lib/review/book/compilable.rb +1 -1
  31. data/lib/review/book/index.rb +33 -27
  32. data/lib/review/book/parameters.rb +3 -2
  33. data/lib/review/book/part.rb +1 -1
  34. data/lib/review/builder.rb +10 -42
  35. data/lib/review/compiler.rb +1 -1
  36. data/lib/review/configure.rb +3 -3
  37. data/lib/review/epubmaker.rb +428 -0
  38. data/lib/review/htmlbuilder.rb +45 -95
  39. data/lib/review/htmlutils.rb +2 -0
  40. data/lib/review/i18n.yaml +25 -0
  41. data/lib/review/idgxmlbuilder.rb +11 -9
  42. data/lib/review/inaobuilder.rb +1 -1
  43. data/lib/review/latexbuilder.rb +7 -6
  44. data/lib/review/latexutils.rb +6 -0
  45. data/lib/review/makerhelper.rb +4 -2
  46. data/lib/review/markdownbuilder.rb +8 -0
  47. data/lib/review/sec_counter.rb +71 -0
  48. data/lib/review/topbuilder.rb +0 -1
  49. data/lib/review/version.rb +2 -2
  50. data/review.gemspec +4 -4
  51. data/test/sample-book/README.md +2 -2
  52. data/test/sample-book/src/Rakefile +2 -2
  53. data/test/sample-book/src/config.yml +4 -4
  54. data/test/sample-book/src/images/cover.jpg +0 -0
  55. data/test/sample-book/src/sty/{samplemacro.sty → reviewmacro.sty} +1 -1
  56. data/test/test_book_parameter.rb +1 -1
  57. data/test/test_epubmaker.rb +77 -15
  58. data/test/test_epubmaker_cmd.rb +11 -7
  59. data/test/test_helper.rb +7 -0
  60. data/test/test_htmlbuilder.rb +39 -6
  61. data/test/test_idgxmlbuilder.rb +14 -2
  62. data/test/test_inaobuilder.rb +2 -1
  63. data/test/test_latexbuilder.rb +23 -2
  64. data/test/test_makerhelper.rb +19 -3
  65. data/test/test_markdownbuilder.rb +35 -0
  66. data/test/test_pdfmaker_cmd.rb +11 -7
  67. data/test/test_topbuilder.rb +36 -2
  68. metadata +18 -18
  69. data/VERSION +0 -1
  70. data/doc/libepubmaker/sample.yaml +0 -90
@@ -1,7 +1,7 @@
1
1
  # encoding: utf-8
2
2
  # = resource.rb -- Message resources for EPUBMaker.
3
3
  #
4
- # Copyright (c) 2010 Kenshi Muto
4
+ # Copyright (c) 2010-2013 Kenshi Muto
5
5
  #
6
6
  # This program is free software.
7
7
  # You can distribute or modify this program under the terms of
@@ -21,44 +21,58 @@ module EPUBMaker
21
21
  rescue
22
22
  @hash = __send__ :en
23
23
  end
24
-
24
+
25
25
  @hash.each_pair do |k, v|
26
26
  @hash[k] = params[k] unless params[k].nil?
27
27
  end
28
28
  end
29
-
29
+
30
30
  # Return message translation for +key+.
31
31
  def v(key)
32
32
  return @hash[key]
33
33
  end
34
-
34
+
35
35
  private
36
- # English messages.
36
+ # English message catalog
37
37
  def en
38
38
  {
39
39
  "toctitle" => "Table of Contents",
40
40
  "covertitle" => "Cover",
41
41
  "titlepagetitle" => "Title Page",
42
+ "originaltitle" => "Title Page of Original",
43
+ "credittitle" => "Credit",
42
44
  "colophontitle" => "Colophon",
45
+ "profiletitle" => "Profile",
46
+ "advtitle" => "Advertisement",
43
47
  "c-aut" => "Author",
48
+ "c-csl" => "Consultant",
44
49
  "c-dsr" => "Designer",
45
50
  "c-ill" => "Illustrator",
46
51
  "c-edt" => "Editor",
52
+ "c-pht" => "Director of Photography",
53
+ "c-trl" => "Translator",
47
54
  "c-prt" => "Publisher",
48
55
  }
49
56
  end
50
-
51
- # Japanese messages.
57
+
58
+ # Japanese message catalog
52
59
  def ja
53
60
  {
54
61
  "toctitle" => "目次",
55
62
  "covertitle" => "表紙",
56
- "titlepagetitle" => "権利表記",
63
+ "titlepagetitle" => "大扉",
64
+ "originaltitle" => "原書大扉",
65
+ "credittitle" => "クレジット",
57
66
  "colophontitle" => "奥付",
67
+ "advtitle" => "広告",
68
+ "profiletitle" => "著者紹介",
58
69
  "c-aut" => "著 者",
70
+ "c-csl" => "監 修",
59
71
  "c-dsr" => "デザイン",
60
72
  "c-ill" => "イラスト",
61
73
  "c-edt" => "編 集",
74
+ "c-pht" => "撮 影",
75
+ "c-trl" => "翻 訳",
62
76
  "c-prt" => "発行所",
63
77
  }
64
78
  end
@@ -64,6 +64,7 @@ module ReVIEW
64
64
  :ext,
65
65
  :image_dir,
66
66
  :image_types,
67
+ :image_types=,
67
68
  :page_metric
68
69
 
69
70
  def parts
@@ -22,8 +22,8 @@ module ReVIEW
22
22
  book = (books[File.expand_path(basedir)] ||= Book.load(basedir))
23
23
  begin
24
24
  book.chapter(File.basename(path, '.*'))
25
- rescue KeyError => err
26
- raise FileNotFound, "no such file: #{path}"
25
+ rescue KeyError
26
+ raise FileNotFound, "No such chapter in your book. Check if the catalog files contain the chapter. : #{path}"
27
27
  end
28
28
  }
29
29
  end
@@ -43,7 +43,7 @@ module ReVIEW
43
43
  f.each_line {|l|
44
44
  l = convert_inencoding(l, ReVIEW.book.param["inencoding"])
45
45
  if l =~ /\A=+/
46
- @title = l.sub(/\A=+/, '').strip
46
+ @title = l.sub(/\A=+(\[.+?\])?(\{.+?\})?/, '').strip
47
47
  break
48
48
  end
49
49
  }
@@ -132,6 +132,8 @@ module ReVIEW
132
132
 
133
133
  class ImageIndex < Index
134
134
  class Item
135
+ @@entries = nil
136
+
135
137
  def initialize(id, number)
136
138
  @id = id
137
139
  @number = number
@@ -167,39 +169,43 @@ module ReVIEW
167
169
  @chapid = chapid
168
170
  @basedir = basedir
169
171
  @types = types
172
+
173
+ @@entries ||= get_entries
174
+ end
175
+
176
+ def get_entries
177
+ Dir.glob(File.join(@basedir, "**/*.*"))
170
178
  end
171
179
 
172
180
  # internal use only
173
181
  def find_pathes(id)
174
- if ReVIEW.book.param["subdirmode"]
175
- re = /\A#{id}(?i:#{@types.join('|')})\z/x
176
- entries().select {|ent| re =~ ent }\
177
- .sort_by {|ent| @types.index(File.extname(ent).downcase) }\
178
- .map {|ent| "#{@basedir}/#{@chapid}/#{ent}" }
179
- elsif ReVIEW.book.param["singledirmode"]
180
- re = /\A#{id}(?i:#{@types.join('|')})\z/x
181
- entries().select {|ent| re =~ ent }\
182
- .sort_by {|ent| @types.index(File.extname(ent).downcase) }\
183
- .map {|ent| "#{@basedir}/#{ent}" }
184
- else
185
- re = /\A#{@chapid.gsub('+', '\\\+').gsub('-', '\\\-')}-#{id}(?i:#{@types.join('|')})\z/x
186
- entries().select {|ent| re =~ ent }\
187
- .sort_by {|ent| @types.index(File.extname(ent).downcase) }\
188
- .map {|ent| "#{@basedir}/#{ent}" }
189
- end
190
- end
182
+ pathes = []
191
183
 
192
- private
184
+ # 1. <basedir>/<builder>/<chapid>/<id>.<ext>
185
+ target = "#{@basedir}/#{ReVIEW.book.param['builder']}/#{@chapid}/#{id}"
186
+ @types.each {|ext| pathes.push("#{target}#{ext}") if @@entries.include?("#{target}#{ext}")}
193
187
 
194
- def entries
195
- # @entries: do not cache for graph
196
- if ReVIEW.book.param["subdirmode"]
197
- @entries = Dir.entries(File.join(@basedir, @chapid))
198
- else
199
- @entries = Dir.entries(@basedir)
200
- end
201
- rescue Errno::ENOENT
202
- @entries = []
188
+ # 2. <basedir>/<builder>/<chapid>-<id>.<ext>
189
+ target = "#{@basedir}/#{ReVIEW.book.param['builder']}/#{@chapid}-#{id}"
190
+ @types.each {|ext| pathes.push("#{target}#{ext}") if @@entries.include?("#{target}#{ext}")}
191
+
192
+ # 3. <basedir>/<builder>/<id>.<ext>
193
+ target = "#{@basedir}/#{ReVIEW.book.param['builder']}/#{id}"
194
+ @types.each {|ext| pathes.push("#{target}#{ext}") if @@entries.include?("#{target}#{ext}")}
195
+
196
+ # 4. <basedir>/<chapid>/<id>.<ext>
197
+ target = "#{@basedir}/#{@chapid}/#{id}"
198
+ @types.each {|ext| pathes.push("#{target}#{ext}") if @@entries.include?("#{target}#{ext}")}
199
+
200
+ # 5. <basedir>/<chapid>-<id>.<ext>
201
+ target = "#{@basedir}/#{@chapid}-#{id}"
202
+ @types.each {|ext| pathes.push("#{target}#{ext}") if @@entries.include?("#{target}#{ext}")}
203
+
204
+ # 6. <basedir>/<id>.<ext>
205
+ target = "#{@basedir}/#{id}"
206
+ @types.each {|ext| pathes.push("#{target}#{ext}") if @@entries.include?("#{target}#{ext}")}
207
+
208
+ return pathes
203
209
  end
204
210
  end
205
211
 
@@ -45,7 +45,8 @@ module ReVIEW
45
45
  PageMetric.new(const_get_safe(mod, :LINES_PER_PAGE_list) || 46,
46
46
  const_get_safe(mod, :COLUMNS_list) || 80,
47
47
  const_get_safe(mod, :LINES_PER_PAGE_text) || 30,
48
- const_get_safe(mod, :COLUMNS_text) || 74) # 37zw
48
+ const_get_safe(mod, :COLUMNS_text) || 74, # 37zw
49
+ const_get_safe(mod, :PAGE_PER_KBYTE) || 1) # 37zw
49
50
  end
50
51
 
51
52
  def Parameters.const_get_safe(mod, name)
@@ -89,7 +90,7 @@ module ReVIEW
89
90
  path_param :postdef_file
90
91
  attr_reader :ext
91
92
  path_param :image_dir
92
- attr_reader :image_types
93
+ attr_accessor :image_types
93
94
  attr_reader :page_metric
94
95
 
95
96
  end
@@ -20,7 +20,7 @@ module ReVIEW
20
20
  @number = number
21
21
  @chapters = chapters
22
22
  @path = name
23
- @name = name ? File.basename(name, '.*') : nil
23
+ @name = name ? File.basename(name, '.re') : nil
24
24
  end
25
25
 
26
26
  attr_reader :number
@@ -10,6 +10,7 @@
10
10
  require 'review/book/index'
11
11
  require 'review/exception'
12
12
  require 'review/textutils'
13
+ require 'review/compiler'
13
14
  require 'stringio'
14
15
  require 'cgi'
15
16
 
@@ -60,9 +61,9 @@ module ReVIEW
60
61
  alias :raw_result result
61
62
 
62
63
  def print(*s)
63
- @output.print *s.map{|i|
64
+ @output.print(*s.map{|i|
64
65
  convert_outencoding(i, ReVIEW.book.param["outencoding"])
65
- }
66
+ })
66
67
  end
67
68
 
68
69
  def puts(*s)
@@ -119,7 +120,7 @@ module ReVIEW
119
120
 
120
121
  begin
121
122
  table_header id, caption unless caption.nil?
122
- rescue KeyError => err
123
+ rescue KeyError
123
124
  error "no such table: #{id}"
124
125
  end
125
126
  return if rows.empty?
@@ -171,7 +172,7 @@ module ReVIEW
171
172
  end
172
173
 
173
174
  def inline_chapref(id)
174
- @chapter.env.chapter_index.display_string(id)
175
+ compile_inline @chapter.env.chapter_index.display_string(id)
175
176
  rescue KeyError
176
177
  error "unknown chapter: #{id}"
177
178
  nofunc_text("[UnknownChapter:#{id}]")
@@ -276,35 +277,6 @@ module ReVIEW
276
277
  end
277
278
  end
278
279
 
279
- def find_pathes(id)
280
- if ReVIEW.book.param["subdirmode"]
281
- re = /\A#{id}(?i:#{@chapter.name.join('|')})\z/x
282
- entries().select {|ent| re =~ ent }\
283
- .sort_by {|ent| @book.image_types.index(File.extname(ent).downcase) }\
284
- .map {|ent| "#{@book.basedir}/#{@chapter.name}/#{ent}" }
285
- elsif ReVIEW.book.param["singledirmode"]
286
- re = /\A#{id}(?i:#{@chapter.name.join('|')})\z/x
287
- entries().select {|ent| re =~ ent }\
288
- .sort_by {|ent| @book.image_types.index(File.extname(ent).downcase) }\
289
- .map {|ent| "#{@book.basedir}/#{ent}" }
290
- else
291
- re = /\A#{@chapter.name}-#{id}(?i:#{@book.image_types.join('|')})\z/x
292
- entries().select {|ent| re =~ ent }\
293
- .sort_by {|ent| @book.image_types.index(File.extname(ent).downcase) }\
294
- .map {|ent| "#{@book.basedir}/#{ent}" }
295
- end
296
- end
297
-
298
- def entries
299
- if ReVIEW.book.param["subdirmode"]
300
- @entries ||= Dir.entries(File.join(@book.basedir + @book.image_dir, @chapter.name))
301
- else
302
- @entries ||= Dir.entries(@book.basedir + @book.image_dir)
303
- end
304
- rescue Errno::ENOENT
305
- @entries = []
306
- end
307
-
308
280
  def warn(msg)
309
281
  $stderr.puts "#{@location}: warning: #{msg}"
310
282
  end
@@ -368,14 +340,10 @@ module ReVIEW
368
340
  end
369
341
 
370
342
  def graph(lines, id, command, caption = nil)
371
- dir = @book.basedir + @book.image_dir
372
- file = "#{@chapter.name}-#{id}.#{image_ext}"
373
- if ReVIEW.book.param["subdirmode"]
374
- dir = File.join(dir, @chapter.name)
375
- file = "#{id}.#{image_ext}"
376
- elsif ReVIEW.book.param["singledirmode"]
377
- file = "#{id}.#{image_ext}"
378
- end
343
+ c = self.class.to_s.gsub(/ReVIEW::/, '').gsub(/Builder/, '').downcase
344
+ dir = File.join(@book.basedir, @book.image_dir, c)
345
+ Dir.mkdir(dir) unless File.exist?(dir)
346
+ file = "#{id}.#{image_ext}"
379
347
  file_path = File.join(dir, file)
380
348
 
381
349
  line = CGI.unescapeHTML(lines.join("\n"))
@@ -392,7 +360,7 @@ module ReVIEW
392
360
  warn cmd
393
361
  system cmd
394
362
 
395
- image(lines, id, caption)
363
+ image(lines, id, caption ||= "")
396
364
  end
397
365
 
398
366
  def image_ext
@@ -412,7 +412,7 @@ module ReVIEW
412
412
 
413
413
  def compile_paragraph(f)
414
414
  buf = []
415
- f.until_match(%r<\A//|\A\#@|\A===+>) do |line|
415
+ f.until_match(%r<\A//|\A\#@>) do |line|
416
416
  break if line.strip.empty?
417
417
  buf.push text(line.sub(/^(\t+)\s*/) {|m| "<!ESCAPETAB!>" * m.size}.strip.gsub(/<!ESCAPETAB!>/, "\t"))
418
418
  end
@@ -4,8 +4,8 @@ module ReVIEW
4
4
  def self.values
5
5
  { # These parameters can be overridden by YAML file.
6
6
  "bookname"=> "example", # it defines epub file name also
7
- "booktitle" => "ReVIEW EPUBサンプル",
8
- "title" => "example",
7
+ "booktitle" => "Re:VIEW EPUBサンプル",
8
+ "title" => nil,
9
9
  "aut" => nil, # author
10
10
  "prt" => nil, # printer(publisher)
11
11
  "asn" => nil, # associated name
@@ -30,7 +30,7 @@ module ReVIEW
30
30
  "epubversion" => 2,
31
31
  "titlepage" => true, # Use title page
32
32
  "toc" => true, # Use table of contents
33
- "colophon" => false, # Use colophon
33
+ "colophon" => nil, # Use colophon
34
34
  "debug" => nil, # debug flag
35
35
  }
36
36
  end
@@ -0,0 +1,428 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Copyright (c) 2010-2014 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
+ require 'review'
11
+ require 'rexml/document'
12
+ require 'rexml/streamlistener'
13
+ require 'epubmaker'
14
+
15
+ module ReVIEW
16
+ class EPUBMaker
17
+ include ::EPUBMaker
18
+ include REXML
19
+
20
+ def initialize
21
+ @epub = nil
22
+ @tochtmltxt = "toc-html.txt"
23
+ end
24
+
25
+ def log(s)
26
+ puts s unless @params["debug"].nil?
27
+ end
28
+
29
+ def load_yaml(yamlfile)
30
+ @params = ReVIEW::Configure.values.merge(YAML.load_file(yamlfile)) # FIXME:設定がRe:VIEW側とepubmaker/producer.rb側の2つに分かれて面倒
31
+ @epub = Producer.new(@params)
32
+ @epub.load(yamlfile)
33
+ @params = @epub.params
34
+ end
35
+
36
+ def produce(yamlfile, bookname=nil)
37
+ load_yaml(yamlfile)
38
+ bookname = @params["bookname"] if bookname.nil?
39
+ log("Loaded yaml file (#{yamlfile}). I will produce #{bookname}.epub.")
40
+
41
+ File.unlink("#{bookname}.epub") if File.exist?("#{bookname}.epub")
42
+ FileUtils.rm_rf(bookname) if @params["debug"] && File.exist?(bookname)
43
+
44
+ Dir.mktmpdir(bookname, Dir.pwd) do |basetmpdir|
45
+ log("Created first temporary directory as #{basetmpdir}.")
46
+
47
+ log("Call hook_beforeprocess. (#{@params["hook_beforeprocess"]})")
48
+ call_hook(@params["hook_beforeprocess"], basetmpdir)
49
+
50
+ copy_stylesheet(basetmpdir)
51
+
52
+ copy_frontmatter(basetmpdir)
53
+ log("Call hook_afterfrontmatter. (#{@params["hook_afterfrontmatter"]})")
54
+ call_hook(@params["hook_afterfrontmatter"], basetmpdir)
55
+
56
+ build_body(basetmpdir, yamlfile)
57
+ log("Call hook_afterbody. (#{@params["hook_afterbody"]})")
58
+ call_hook(@params["hook_afterbody"], basetmpdir)
59
+
60
+ copy_backmatter(basetmpdir)
61
+ log("Call hook_afterbackmatter. (#{@params["hook_afterbackmatter"]})")
62
+ call_hook(@params["hook_afterbackmatter"], basetmpdir)
63
+
64
+ push_contents(basetmpdir)
65
+
66
+ copy_images(@params["imagedir"], "#{basetmpdir}/images")
67
+ copy_images("covers", "#{basetmpdir}/images")
68
+ copy_images("adv", "#{basetmpdir}/images")
69
+ copy_images(@params["fontdir"], "#{basetmpdir}/fonts", @params["font_ext"])
70
+ log("Call hook_aftercopyimage. (#{@params["hook_aftercopyimage"]})")
71
+ call_hook(@params["hook_aftercopyimage"], basetmpdir)
72
+
73
+ @epub.import_imageinfo("#{basetmpdir}/images", basetmpdir)
74
+ @epub.import_imageinfo("#{basetmpdir}/fonts", basetmpdir, @params["font_ext"])
75
+
76
+ epubtmpdir = @params["debug"].nil? ? nil : "#{Dir.pwd}/#{bookname}"
77
+ Dir.mkdir(bookname) unless @params["debug"].nil?
78
+ log("Call ePUB producer.")
79
+ @epub.produce("#{bookname}.epub", basetmpdir, epubtmpdir)
80
+ log("Finished.")
81
+
82
+ end
83
+ end
84
+
85
+ def call_hook(filename, *params)
86
+ if !filename.nil? && File.exist?(filename) && FileTest.executable?(filename)
87
+ system(filename, *params)
88
+ end
89
+ end
90
+
91
+ def copy_images(imagedir, destdir, allow_exts=nil)
92
+ return nil unless File.exist?(imagedir)
93
+ allow_exts = @params["image_ext"] if allow_exts.nil?
94
+ FileUtils.mkdir_p(destdir) unless FileTest.directory?(destdir)
95
+ recursive_copy_images(imagedir, destdir, allow_exts)
96
+ end
97
+
98
+ def recursive_copy_images(imagedir, destdir, allow_exts)
99
+ Dir.open(imagedir) do |dir|
100
+ dir.each do |fname|
101
+ next if fname =~ /\A\./
102
+ if FileTest.directory?("#{imagedir}/#{fname}")
103
+ recursive_copy_images("#{imagedir}/#{fname}", "#{destdir}/#{fname}", allow_exts)
104
+ else
105
+ if fname =~ /\.(#{allow_exts.join("|")})\Z/i
106
+ Dir.mkdir(destdir) unless File.exist?(destdir)
107
+ log("Copy #{imagedir}/#{fname} to the temporary directory.")
108
+ FileUtils.cp("#{imagedir}/#{fname}", destdir)
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ def build_body(basetmpdir, yamlfile)
116
+ @precount = 0
117
+ @bodycount = 0
118
+ @postcount = 0
119
+
120
+ @manifeststr = ""
121
+ @ncxstr = ""
122
+ @tocdesc = Array.new
123
+ # toccount = 2 ## not used
124
+
125
+ basedir = Dir.pwd
126
+ base_path = Pathname.new(basedir)
127
+ ReVIEW::Book.load(basedir).parts.each do |part|
128
+ htmlfile = nil
129
+ if part.name.present?
130
+ if part.file?
131
+ build_chap(part, base_path, basetmpdir, yamlfile, true)
132
+ else
133
+ htmlfile = "part_#{part.number}.#{@params["htmlext"]}"
134
+ build_part(part, basetmpdir, htmlfile)
135
+ title = ReVIEW::I18n.t("part", part.number)
136
+ title += ReVIEW::I18n.t("chapter_postfix") + part.name.strip if part.name.strip.present?
137
+ write_tochtmltxt(basetmpdir, "0\t#{htmlfile}\t#{title}")
138
+ end
139
+ end
140
+
141
+ part.chapters.each do |chap|
142
+ build_chap(chap, base_path, basetmpdir, yamlfile, nil)
143
+ end
144
+
145
+ end
146
+ end
147
+
148
+ def build_part(part, basetmpdir, htmlfile)
149
+ log("Create #{htmlfile} from a template.")
150
+ File.open("#{basetmpdir}/#{htmlfile}", "w") do |f|
151
+ f.puts header(CGI.escapeHTML(@params["booktitle"]))
152
+ f.puts <<EOT
153
+ <div class="part">
154
+ <h1 class="part-number">#{ReVIEW::I18n.t("part", part.number)}</h1>
155
+ EOT
156
+ if part.name.strip.present?
157
+ f.puts <<EOT
158
+ <h2 class="part-title">#{part.name.strip}</h2>
159
+ EOT
160
+ end
161
+
162
+ f.puts <<EOT
163
+ </div>
164
+ EOT
165
+ f.puts footer
166
+ end
167
+ end
168
+
169
+ def build_chap(chap, base_path, basetmpdir, yamlfile, ispart=nil)
170
+ filename = ""
171
+ if !ispart.nil?
172
+ filename = chap.path
173
+ else
174
+ filename = Pathname.new(chap.path).relative_path_from(base_path).to_s
175
+ end
176
+ id = filename.sub(/\.re\Z/, "")
177
+
178
+ if @params["rename_for_legacy"] && ispart.nil?
179
+ if chap.on_PREDEF?
180
+ @precount += 1
181
+ id = sprintf("pre%02d", @precount)
182
+ elsif chap.on_POSTDEF?
183
+ @postcount += 1
184
+ id = sprintf("post%02d", @postcount)
185
+ else
186
+ @bodycount += 1
187
+ id = sprintf("chap%02d", @bodycount)
188
+ end
189
+ end
190
+
191
+ htmlfile = "#{id}.#{@params["htmlext"]}"
192
+ log("Create #{htmlfile} from #{filename}.")
193
+
194
+ level = @params["secnolevel"]
195
+
196
+ if !ispart.nil?
197
+ level = @params["part_secnolevel"]
198
+ else
199
+ level = @params["pre_secnolevel"] if chap.on_PREDEF?
200
+ level = @params["post_secnolevel"] if chap.on_POSTDEF?
201
+ end
202
+
203
+ stylesheet = ""
204
+ if @params["stylesheet"].size > 0
205
+ stylesheet = "--stylesheet=#{@params["stylesheet"].join(",")}"
206
+ end
207
+
208
+ system("review-compile --yaml=#{yamlfile} --target=html --level=#{level} --htmlversion=#{@params["htmlversion"]} --epubversion=#{@params["epubversion"]} #{stylesheet} #{@params["params"]} #{filename} > \"#{basetmpdir}/#{htmlfile}\"")
209
+
210
+ write_info_body(basetmpdir, id, htmlfile, ispart)
211
+ end
212
+
213
+ def write_info_body(basetmpdir, id, filename, ispart=nil)
214
+ headlines = []
215
+ # FIXME:nonumを修正する必要あり
216
+ Document.parse_stream(File.new("#{basetmpdir}/#{filename}"), ReVIEWHeaderListener.new(headlines))
217
+ first = true
218
+ headlines.each do |headline|
219
+ headline["level"] = 0 if !ispart.nil? && headline["level"] == 1
220
+ if first.nil?
221
+ write_tochtmltxt(basetmpdir, "#{headline["level"]}\t#{filename}##{headline["id"]}\t#{headline["title"]}")
222
+ else
223
+ write_tochtmltxt(basetmpdir, "#{headline["level"]}\t#{filename}\t#{headline["title"]}\tforce_include=true")
224
+ first = nil
225
+ end
226
+ end
227
+ end
228
+
229
+ def push_contents(basetmpdir)
230
+ File.open("#{basetmpdir}/#{@tochtmltxt}") do |f|
231
+ f.each_line do |l|
232
+ force_include = nil
233
+ customid = nil
234
+ level, file, title, custom = l.chomp.split("\t")
235
+ unless custom.nil?
236
+ # custom setting
237
+ vars = custom.split(/,\s*/)
238
+ vars.each do |var|
239
+ k, v = var.split("=")
240
+ case k
241
+ when "id"
242
+ customid = v
243
+ when "force_include"
244
+ force_include = true
245
+ end
246
+ end
247
+ end
248
+ next if level.to_i > @params["toclevel"] && force_include.nil?
249
+ log("Push #{file} to ePUB contents.")
250
+
251
+ if customid.nil?
252
+ @epub.contents.push(Content.new("file" => file, "level" => level.to_i, "title" => title))
253
+ else
254
+ @epub.contents.push(Content.new("id" => customid, "file" => file, "level" => level.to_i, "title" => title))
255
+ end
256
+ end
257
+ end
258
+ end
259
+
260
+ def copy_stylesheet(basetmpdir)
261
+ if @params["stylesheet"].size > 0
262
+ @params["stylesheet"].each do |sfile|
263
+ FileUtils.cp(sfile, basetmpdir)
264
+ @epub.contents.push(Content.new("file" => sfile))
265
+ end
266
+ end
267
+ end
268
+
269
+ def copy_frontmatter(basetmpdir)
270
+ FileUtils.cp(@params["cover"], "#{basetmpdir}/#{File.basename(@params["cover"])}") if !@params["cover"].nil? && File.exist?(@params["cover"])
271
+
272
+ if @params["titlepage"]
273
+ if @params["titlepagefile"].nil?
274
+ build_titlepage(basetmpdir, "titlepage.#{@params["htmlext"]}")
275
+ else
276
+ FileUtils.cp(@params["titlepagefile"], "titlepage.#{@params["htmlext"]}")
277
+ end
278
+ write_tochtmltxt(basetmpdir, "1\ttitlepage.#{@params["htmlext"]}\t#{@epub.res.v("titlepagetitle")}")
279
+ end
280
+
281
+ if !@params["originaltitlefile"].nil? && File.exist?(@params["originaltitlefile"])
282
+ FileUtils.cp(@params["originaltitlefile"], "#{basetmpdir}/#{File.basename(@params["originaltitlefile"])}")
283
+ write_tochtmltxt(basetmpdir, "1\t#{File.basename(@params["originaltitlefile"])}\t#{@epub.res.v("originaltitle")}")
284
+ end
285
+
286
+ if !@params["creditfile"].nil? && File.exist?(@params["creditfile"])
287
+ FileUtils.cp(@params["creditfile"], "#{basetmpdir}/#{File.basename(@params["creditfile"])}")
288
+ write_tochtmltxt(basetmpdir, "1\t#{File.basename(@params["creditfile"])}\t#{@epub.res.v("credittitle")}")
289
+ end
290
+ end
291
+
292
+ def build_titlepage(basetmpdir, htmlfile)
293
+ File.open("#{basetmpdir}/#{htmlfile}", "w") do |f|
294
+ f.puts header(CGI.escapeHTML(@params["booktitle"]))
295
+ f.puts <<EOT
296
+ <div class="titlepage">
297
+ <h1 class="tp-title">#{CGI.escapeHTML(@params["booktitle"])}</h1>
298
+ EOT
299
+
300
+ if @params["aut"]
301
+ f.puts <<EOT
302
+ <h2 class="tp-author">#{@params["aut"].join(", ")}</h2>
303
+ EOT
304
+ end
305
+ if @params["prt"]
306
+ f.puts <<EOT
307
+ <h3 class="tp-publisher">#{@params["prt"].join(", ")}</h3>
308
+ EOT
309
+ end
310
+
311
+ f.puts <<EOT
312
+ </div>
313
+ EOT
314
+ f.puts footer
315
+ end
316
+ end
317
+
318
+ def copy_backmatter(basetmpdir)
319
+ if @params["profile"]
320
+ FileUtils.cp(@params["profile"], "#{basetmpdir}/#{File.basename(@params["profile"])}")
321
+ write_tochtmltxt(basetmpdir, "1\t#{File.basename(@params["profile"])}\t#{@epub.res.v("profiletitle")}")
322
+ end
323
+
324
+ if @params["advfile"]
325
+ FileUtils.cp(@params["advfile"], "#{basetmpdir}/#{File.basename(@params["advfile"])}")
326
+ write_tochtmltxt(basetmpdir, "1\t#{File.basename(@params["advfile"])}\t#{@epub.res.v("advtitle")}")
327
+ end
328
+
329
+ if @params["colophon"]
330
+ if @params["colophon"].instance_of?(String) # FIXME:このやり方はやめる?
331
+ FileUtils.cp(@params["colophon"], "#{basetmpdir}/colophon.#{@params["htmlext"]}")
332
+ else
333
+ File.open("#{basetmpdir}/colophon.#{@params["htmlext"]}", "w") {|f| @epub.colophon(f) }
334
+ end
335
+ write_tochtmltxt(basetmpdir, "1\tcolophon.#{@params["htmlext"]}\t#{@epub.res.v("colophontitle")}")
336
+ end
337
+ end
338
+
339
+ def write_tochtmltxt(basetmpdir, s)
340
+ File.open("#{basetmpdir}/#{@tochtmltxt}", "a") do |f|
341
+ f.puts s
342
+ end
343
+ end
344
+
345
+ def header(title)
346
+ # titleはすでにエスケープ済みと想定
347
+ s = <<EOT
348
+ <?xml version="1.0" encoding="UTF-8"?>
349
+ EOT
350
+ if @params["htmlversion"] == 5
351
+ s << <<EOT
352
+ <!DOCTYPE html>
353
+ <html xml:lang='ja' xmlns:ops='http://www.idpf.org/2007/ops' xmlns='http://www.w3.org/1999/xhtml'>
354
+ <head>
355
+ <meta charset="UTF-8" />
356
+ EOT
357
+ else
358
+ s << <<EOT
359
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
360
+ <html xml:lang='ja' xmlns:ops='http://www.idpf.org/2007/ops' xmlns='http://www.w3.org/1999/xhtml'>
361
+ <head>
362
+ <meta http-equiv='Content-Type' content='text/html;charset=UTF-8' />
363
+ <meta http-equiv='Content-Style-Type' content='text/css' />
364
+ EOT
365
+ end
366
+ if @params["stylesheet"].size > 0
367
+ @params["stylesheet"].each do |sfile|
368
+ s << <<EOT
369
+ <link rel='stylesheet' type='text/css' href='#{sfile}' />
370
+ EOT
371
+ end
372
+ end
373
+ s << <<EOT
374
+ <meta content='Re:VIEW' name='generator'/>
375
+ <title>#{title}</title>
376
+ </head>
377
+ <body>
378
+ EOT
379
+ end
380
+
381
+ def footer
382
+ <<EOT
383
+ </body>
384
+ </html>
385
+ EOT
386
+ end
387
+
388
+ class ReVIEWHeaderListener
389
+ include REXML::StreamListener
390
+ def initialize(headlines)
391
+ @level = nil
392
+ @content = ""
393
+ @headlines = headlines
394
+ end
395
+
396
+ def tag_start(name, attrs)
397
+ if name =~ /\Ah(\d+)/
398
+ unless @level.nil?
399
+ raise "#{name}, #{attrs}"
400
+ end
401
+ @level = $1.to_i
402
+ @id = attrs["id"] if !attrs["id"].nil?
403
+ elsif !@level.nil?
404
+ if name == "img" && !attrs["alt"].nil?
405
+ @content << attrs["alt"]
406
+ elsif name == "a" && !attrs["id"].nil?
407
+ @id = attrs["id"]
408
+ end
409
+ end
410
+ end
411
+
412
+ def tag_end(name)
413
+ if name =~ /\Ah\d+/
414
+ @headlines.push({"level" => @level, "id" => @id, "title" => @content}) unless @id.nil?
415
+ @content = ""
416
+ @level = nil
417
+ @id = nil
418
+ end
419
+ end
420
+
421
+ def text(text)
422
+ unless @level.nil?
423
+ @content << text.gsub("\t", " ") # FIXME:区切り文字
424
+ end
425
+ end
426
+ end
427
+ end
428
+ end