pubgen 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ doc
5
+ pkg
6
+ *.epub
7
+ .rvmrc
8
+ Gemfile.lock
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ (The MIT License)
2
+
3
+ Copyright (c) 2012 Hyun Rae Cho
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the 'Software'), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,145 @@
1
+ # Pubgen
2
+
3
+ Pubgen is a simple command-line based epub generator. With the simple YAML
4
+ file, Pubgen generate the epub file for you.
5
+
6
+ ## Installation
7
+
8
+ ```bash
9
+ $ # First, install Ruby and then
10
+ $ gem install pubgen
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```bash
16
+ $ pubgen -h
17
+ pubgen 0.1.0, a epub generator.
18
+
19
+ Usage:
20
+ pubgen <yaml file> [-o <epub file>] [-v]
21
+ pubgen <yaml file> -m
22
+
23
+ -o, --output EPUB_PATH Specify output epub file path
24
+ -m, --meta-file-only Generate .opf, .ncx, mimetype, ...
25
+ -v, --verbose Verbose output
26
+ ```
27
+
28
+ ## Quick Start
29
+
30
+ Prepare files (documents, images, style sheets, etc.) that make up the
31
+ publication. iBooks requires strict xhtml format,
32
+ [`tidy -asxhtml`] (http://tidy.sourceforge.net/) will be helpful.
33
+
34
+ ```bash
35
+ $ find .
36
+ .
37
+ ./contents
38
+ ./contents/a-1.html
39
+ ./contents/a-2.html
40
+ ./contents/a.html
41
+ ./contents/b.html
42
+ ./images
43
+ ./images/1.jpg
44
+ ./images/2.jpg
45
+ ./images/3.jpg
46
+ ./images/cover.jpg
47
+ ./style.css
48
+ ```
49
+ Create the YAML file describing your publication. As a example,
50
+ `will_oldham.yml`.
51
+
52
+ ```yaml
53
+ # METADATA: Publication metadata (title, author, publisher, etc.).
54
+ #
55
+ # See http://idpf.org/epub/20/spec/OPF_2.0.1_draft.htm#Section2.2
56
+ # -*- encoding: utf-8 -*-
57
+ metadata:
58
+ title: "Will Oldham: Wikipedia, the free encyclopedia"
59
+ creator: Wikipedia
60
+ date: 2012
61
+ language: en
62
+ subject: American alternative country singers
63
+ publisher:
64
+ contrubuter:
65
+ description:
66
+ source: "http://en.wikipedia.org/wiki/Will_Oldham"
67
+ rights:
68
+ relation:
69
+
70
+ # GUIDE: A set of references to fundamental structural features of the
71
+ # publication, such as table of contents, foreword, bibliography, etc.
72
+ #
73
+ # See http://idpf.org/epub/20/spec/OPF_2.0.1_draft.htm#Section2.6
74
+ guide:
75
+ toc-page:
76
+ title-page:
77
+ # If you provide cover-image without cover-page, pubgen automatically
78
+ # generate cover-page xhtml, and add it to manifest and spine
79
+ cover-image: images/cover.jpg
80
+ cover-page:
81
+
82
+ # MANIFEST: A list of files (documents, images, style sheets, etc.) that make
83
+ # up the publication.
84
+ #
85
+ # All the files in manifest ought to be in the same or sub-directory of yaml.
86
+ # Say yaml's path is /book/a.yaml.
87
+ # - a/b/c.html # good. in the sub-directory
88
+ # - d.jpg # good. in the same directory
89
+ # - ./e.jpg # good. in the same directory
90
+ # - /a/b/c.html # bad. in the different directory
91
+ # - ../d.png # bad. in the parent directory
92
+ # - /book/e.html # bad. although in the same directory
93
+ # - ../book/f.png # bad. although in the same directory
94
+ manifest:
95
+ - contents/a.html
96
+ - contents/a-1.html
97
+ - contents/a-2.html
98
+ - contents/b.html
99
+ - images/cover.jpg
100
+ - images/1.jpg
101
+ - images/2.jpg
102
+ - images/3.jpg
103
+ - style.css
104
+
105
+ # SPINE: An arrangement of documents providing a linear reading order.
106
+ spine:
107
+ - contents/a.html
108
+ - contents/a-1.html
109
+ - contents/a-2.html
110
+ - contents/b.html
111
+
112
+ # TOC: Table of contents
113
+ toc:
114
+ - Music -- contents/a.html: # don't forget colon to indent
115
+ - Discography -- contents/a-1.html
116
+ - Response -- contents/a-2.html
117
+ - Film -- contents/b.html
118
+ ```
119
+
120
+ Run pubgen.
121
+
122
+ ```bash
123
+ $ pubgen /path/to/will_oldham.yml -v
124
+ mkdir .pubgen-4f4a210e
125
+ cp ./contents/a.html .pubgen-4f4a210e/contents
126
+ cp ./contents/a-1.html .pubgen-4f4a210e/contents
127
+ cp ./contents/a-2.html .pubgen-4f4a210e/contents
128
+ cp ./contents/b.html .pubgen-4f4a210e/contents
129
+ cp ./images/cover.jpg .pubgen-4f4a210e/images
130
+ cp ./images/1.jpg .pubgen-4f4a210e/images
131
+ cp ./images/2.jpg .pubgen-4f4a210e/images
132
+ cp ./images/3.jpg .pubgen-4f4a210e/images
133
+ cp ./style.css .pubgen-4f4a210e/.
134
+ cd .pubgen-4f4a210e
135
+ cat > META-INF/container.xml
136
+ cat > mimetype
137
+ cat > cover-pubgen.xhtml
138
+ cat > content.opf
139
+ cat > toc.ncx
140
+ zip > pubgen.epub
141
+ cd /path/to/pwd
142
+ mv .pubgen-4f4a210e /pubgen.epub 'Will Oldham_ Wikipedia, the free encyclopedia.epub'
143
+ rm -rf .pubgen-4f4a210e
144
+ # Successfully generated 'Will Oldham_ Wikipedia, the free encyclopedia.epub'
145
+ ```
@@ -0,0 +1,211 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ PACKAGE = 'pubgen'
4
+ VERSION = '0.1.0'
5
+
6
+ require 'yaml'
7
+ require 'fileutils'
8
+ require 'optparse'
9
+
10
+ require 'zipruby'
11
+
12
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
13
+ require 'pubgen'
14
+
15
+ HELP = <<EOF
16
+ #{PACKAGE} #{VERSION}, a epub generator.
17
+
18
+ Usage:
19
+ #{PACKAGE} <yaml file> [-o <epub file>] [-v]
20
+ #{PACKAGE} <yaml file> -m
21
+
22
+ EOF
23
+
24
+ options = {}
25
+ opts = OptionParser.new do |opts|
26
+ opts.banner = HELP
27
+
28
+ opts.on('-o', '--output EPUB_PATH', 'Specify output epub file path') do |epub_path|
29
+ options[:epub_path] = epub_path
30
+ end
31
+
32
+ opts.on('-m', '--meta-file-only', 'Generate .opf, .ncx, mimetype, ...') do |m|
33
+ options[:meta_file_only] = m
34
+ end
35
+
36
+ opts.on('-v', '--verbose', 'Verbose output') do |v|
37
+ options[:verbose] = v
38
+ end
39
+ end
40
+ opts.parse!(ARGV)
41
+
42
+ # check options validity
43
+ if (options[:meta_file_only] == true && options[:epub_path] != nil)
44
+ $stderr << <<EOF
45
+ #{PACKAGE}: `--meta-file-only' with `--output EPUB_PATH', not allowed
46
+
47
+ Try `#{PACKAGE} -h' for more informations
48
+ EOF
49
+ exit 1
50
+ end
51
+
52
+ if ARGV.size != 1
53
+ $stderr << <<EOF
54
+ #{PACKAGE}: specify one yaml file
55
+
56
+ Try `#{PACKAGE} -h' for more informations
57
+ EOF
58
+ exit 1
59
+ else
60
+ options[:yaml_path] = ARGV[0]
61
+ end
62
+
63
+ begin
64
+ yaml = YAML::load File.open(options[:yaml_path])
65
+ rescue Exception=>e
66
+ pubgen_err("While loading yaml: " + e.to_s)
67
+ exit 1
68
+ end
69
+
70
+ if yaml['metadata']['title'] == nil
71
+ pubgen_err("Failed to find the value of 'title' attribute in: " + options[:yaml_path])
72
+ exit 1
73
+ end
74
+
75
+ # if `--output EPUB_PATH' is not given. get it from yaml
76
+ if !options[:meta_file_only] && !options[:epub_path]
77
+ options[:epub_path] = yaml['metadata']['title'] + ".epub"
78
+ options[:epub_path].gsub!(/[\/:?]/, "_") # file-system friendly
79
+ end
80
+ # now, options done
81
+
82
+ # just for one line
83
+ def pubgen_log(log, verbose = true)
84
+ $stdout << log + "\n" if verbose
85
+ end
86
+
87
+ # just for one line
88
+ def pubgen_err(err)
89
+ $stderr << '# ' + err + "\n"
90
+ end
91
+
92
+ yaml['metadata']['creator'] = "#{PACKAGE}-#{VERSION}" if yaml['metadata']['creator'] == nil
93
+ yaml['metadata']['contributor'] = "#{PACKAGE}-#{VERSION}" if yaml['metadata']['contributor'] == nil
94
+
95
+ $tmpdir = ""
96
+ $pwd_old = Dir.pwd
97
+ $exit_code = 0
98
+
99
+ begin
100
+ if !options[:meta_file_only]
101
+ # we need temporal working directory
102
+ $tmpdir = ".pubgen-%08x" % Time.now.to_i
103
+ Dir.mkdir($tmpdir)
104
+ pubgen_log("mkdir " + $tmpdir, options[:verbose])
105
+
106
+ # copy all files of manifest to $tmpdir
107
+ yaml['manifest'].each do |file|
108
+ if Pubgen::OPF.valid_manifest_element?(file) == false
109
+ raise "Invalid manifest (not in sub-directory of yaml file): " + file
110
+ end
111
+ target_dir = "#{$tmpdir}/#{File.dirname(file)}"
112
+ FileUtils.mkdir_p(target_dir)
113
+ FileUtils.cp("#{File.dirname(options[:yaml_path])}/#{file}", target_dir, :verbose=>options[:verbose])
114
+ end
115
+
116
+ # change pwd
117
+ Dir.chdir($tmpdir)
118
+ pubgen_log("cd " + $tmpdir, options[:verbose])
119
+ end
120
+
121
+ # generate container.xml
122
+ # it's name and path are fixed (META-INF/container.xml)
123
+ Dir.mkdir('META-INF') if !File::directory?('META-INF')
124
+ container = File.new('META-INF/container.xml', 'w')
125
+ container.write(Pubgen::Container.generate)
126
+ container.close
127
+ pubgen_log('cat > META-INF/container.xml', options[:meta_file_only] || options[:verbose])
128
+
129
+ # generate mimetype
130
+ # it's name and path are also fixed (./mimetype)
131
+ mimetype = File.new('mimetype', 'w')
132
+ mimetype.write('application/epub+zip')
133
+ mimetype.close
134
+ pubgen_log('cat > mimetype', options[:meta_file_only] || options[:verbose])
135
+
136
+ # pubgen automatically generates cover page if cover-image is given without
137
+ # cover-page
138
+ if Pubgen::CoverPage.need_to_generate?(yaml['guide'])
139
+ COVER_PAGE_PATH = 'cover-pubgen.xhtml'
140
+ cover_page = File.new(COVER_PAGE_PATH, 'w')
141
+ cover_page.write(Pubgen::CoverPage.generate(yaml['guide']['cover-image']))
142
+ cover_page.close
143
+ pubgen_log("cat > " + COVER_PAGE_PATH, options[:meta_file_only] || options[:verbose])
144
+
145
+ # add/set it to manifest, spine, and cover-page
146
+ yaml['manifest'] << COVER_PAGE_PATH
147
+ yaml['spine'].unshift(COVER_PAGE_PATH)
148
+ yaml['guide']['cover-page'] = COVER_PAGE_PATH
149
+ end
150
+
151
+ # generate uuid
152
+ uuid = "%08x" % Time.now.to_i + "-" + [4,4,4,12].map {|l| "%0#{l}x" % rand(1 << l*4) }.join('-')
153
+ # require 'uuid'
154
+ # uuid = UUID.new.generate
155
+
156
+ # generate opf file
157
+ opf = File.new(Pubgen::Container.opf_path, 'w')
158
+ opf.write(Pubgen::OPF.generate(yaml, uuid))
159
+ opf.close
160
+ pubgen_log("cat > " + Pubgen::Container.opf_path, options[:meta_file_only] || options[:verbose])
161
+ # pubgen automatically added ncx file to opf's manifest (but not to yaml[]'s)
162
+
163
+ # generate ncx file
164
+ ncx = File.new(Pubgen::OPF.ncx_path, 'w')
165
+ ncx.write(Pubgen::NCX.generate(yaml['metadata']['title'], yaml['toc'], uuid))
166
+ ncx.close
167
+ pubgen_log("cat > " + Pubgen::OPF.ncx_path, options[:meta_file_only] || options[:verbose])
168
+
169
+ # generate epub
170
+ if !options[:meta_file_only]
171
+ Zip::Archive.open("pubgen.epub", Zip::CREATE | Zip::TRUNC) do |ar|
172
+ Dir.glob('**/*').each do |path|
173
+ if File.directory?(path)
174
+ ar.add_dir(path)
175
+ else
176
+ ar.add_file(path, path)
177
+ end
178
+ end
179
+ end
180
+ pubgen_log("zip > pubgen.epub", options[:verbose])
181
+
182
+ # mv pubgen.epub to options[:epub_path]
183
+ Dir.chdir($pwd_old)
184
+ pubgen_log("cd " + $pwd_old, options[:verbose])
185
+ FileUtils.mv($tmpdir + "/pubgen.epub", options[:epub_path])
186
+ pubgen_log("mv #{$tmpdir} /pubgen.epub '#{options[:epub_path]}'", options[:verbose])
187
+ end
188
+ rescue Exception=>e
189
+ $exit_code = 1
190
+ pubgen_err(e.to_s)
191
+ ensure
192
+ # remove $tmpdir
193
+ if Dir.pwd != $pwd_old
194
+ Dir.chdir($pwd_old)
195
+ pubgen_log("cd " + $pwd_old, options[:verbose])
196
+ end
197
+ if File::directory?($tmpdir)
198
+ FileUtils.rm_rf($tmpdir)
199
+ pubgen_log("rm -rf " + $tmpdir, options[:verbose])
200
+ end
201
+ end
202
+
203
+ if $exit_code == 0 && !options[:meta_file_only]
204
+ if options[:verbose]
205
+ pubgen_log("# Successfully generated '#{options[:epub_path]}'")
206
+ else
207
+ pubgen_log("zip > '#{options[:epub_path]}'")
208
+ end
209
+ end
210
+
211
+ exit $exit_code
@@ -0,0 +1,4 @@
1
+ require 'pubgen/cover_page'
2
+ require 'pubgen/ncx'
3
+ require 'pubgen/opf'
4
+ require 'pubgen/container'
@@ -0,0 +1,20 @@
1
+ module Pubgen
2
+ module Container
3
+ def self.opf_path
4
+ 'content.opf'
5
+ end
6
+
7
+ def self.generate
8
+ <<EOF
9
+ <?xml version="1.0"?>
10
+ <container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container"
11
+ >
12
+ <rootfiles>
13
+ <rootfile full-path="#{opf_path}" media-type="application/oebps-package+xml"
14
+ />
15
+ </rootfiles>
16
+ </container>
17
+ EOF
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,31 @@
1
+ module Pubgen
2
+ module CoverPage
3
+ def self.need_to_generate?(guide)
4
+ guide['cover-image'] != nil && guide['cover-page'] == nil
5
+ end
6
+
7
+ def self.generate(cover_image)
8
+ <<EOF
9
+ <?xml version='1.0' encoding='utf-8'?>
10
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
11
+ <head>
12
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
13
+ <meta name="pubgen:cover" content="true"/>
14
+ <title>Cover</title>
15
+ <style type="text/css" title="override_css">
16
+ @page {padding: 0pt; margin:0pt}
17
+ body { text-align: center; padding:0pt; margin: 0pt; }
18
+ </style>
19
+ </head>
20
+ <body>
21
+ <div>
22
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="100%" height="100%" viewBox="0 0 469 616" preserveAspectRatio="xMinYMin">
23
+ <image width="469" height="616" xlink:href="#{cover_image}"/>
24
+ </svg>
25
+ </div>
26
+ </body>
27
+ </html>
28
+ EOF
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,90 @@
1
+ module Pubgen
2
+ module NCX
3
+ def self.generate(title, toc, uuid)
4
+ # header
5
+ toc_xml = TOC_XML_HEADER_FORMAT % [uuid, CGI.escapeHTML(title || '')]
6
+ # NavPoint traces indentation, so we need class and instantiation of it
7
+ nav_point = NavPointImpl.new
8
+ toc.each do |name_and_path|
9
+ toc_xml += nav_point.generate(name_and_path)
10
+ end
11
+ # footer
12
+ toc_xml += TOC_XML_FOOTER
13
+ end
14
+
15
+ # define NavPointImpl class and some constants
16
+ # they are all private, only Pubgen.NCX.generate use them
17
+ NAVPOINT_XML_HEADER_FORMAT = <<EOF
18
+ <navPoint id="d%03d" playOrder="%d">
19
+ <navLabel>
20
+ <text>%s</text>
21
+ </navLabel>
22
+ <content src="%s"/>
23
+ EOF
24
+ NAVPOINT_XML_FOOTER = " </navPoint>\n"
25
+ TOC_XML_HEADER_FORMAT = <<EOF
26
+ <?xml version='1.0' encoding='utf-8'?>
27
+ <ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1" xml:lang="en">
28
+ <head>
29
+ <meta content="%s" name="dtb:uid"/>
30
+ <meta content="2" name="dtb:depth"/>
31
+ <meta content="pubgen" name="dtb:generator"/>
32
+ <meta content="0" name="dtb:totalPageCount"/>
33
+ <meta content="0" name="dtb:maxPageNumber"/>
34
+ </head>
35
+ <docTitle>
36
+ <text>%s</text>
37
+ </docTitle>
38
+ <navMap>
39
+ EOF
40
+ TOC_XML_FOOTER = " </navMap>\n</ncx>"
41
+
42
+ class NavPointImpl
43
+ private
44
+ def generate_impl(name_and_path, depth)
45
+ header = NAVPOINT_XML_HEADER_FORMAT
46
+ footer = NAVPOINT_XML_FOOTER
47
+ @toc_indent_order += 1
48
+ if depth
49
+ depth.times do
50
+ header = header.gsub(/^/, ' ')
51
+ footer = footer.gsub(/^/, ' ')
52
+ end
53
+ end
54
+
55
+ navpoint_xml = ''
56
+ if name_and_path.is_a?(String)
57
+ if name_and_path.split(" -- ").size != 2
58
+ raise "Bad toc contents format: " + name_and_path
59
+ end
60
+ navpoint_xml = header % [@toc_indent_order, @toc_indent_order,
61
+ CGI.escapeHTML(name_and_path.split(" -- ")[0]),
62
+ name_and_path.split(" -- ")[1]]
63
+ else
64
+ # if not string, it's hash map with just one element
65
+ name_and_path.each do |key, value|
66
+ if key.split(" -- ").size != 2
67
+ raise "Bad toc contents format: " + key
68
+ end
69
+ navpoint_xml = header % [@toc_indent_order, @toc_indent_order,
70
+ CGI.escapeHTML(key.split(" -- ")[0]), key.split(" -- ")[1]]
71
+ value.each do |v|
72
+ navpoint_xml += generate_impl(v, depth + 1)
73
+ end
74
+ end
75
+ end
76
+
77
+ navpoint_xml += footer
78
+ end
79
+
80
+ public
81
+ def initialize
82
+ @toc_indent_order = 0
83
+ end
84
+
85
+ def generate(name_and_path)
86
+ generate_impl(name_and_path, 0)
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,166 @@
1
+ require 'cgi'
2
+
3
+ module Pubgen
4
+ module OPF
5
+ def self.ncx_path
6
+ NCX_PATH
7
+ end
8
+
9
+ def self.generate(yaml, uuid)
10
+ cover_id, manifest_xml, file2id =
11
+ OPFImpl.get_cover_id_and_manifest_xml(yaml['guide']['cover-image'],
12
+ yaml['manifest'])
13
+ metadata_xml = OPFImpl.get_metadata_xml(yaml['metadata'], uuid, cover_id)
14
+ spine_xml = OPFImpl.get_spine_xml(yaml['spine'], file2id)
15
+ guide_xml = OPFImpl.get_guide_xml(yaml['guide'], file2id)
16
+ return OPF_XML_HEADER + metadata_xml + manifest_xml + spine_xml +
17
+ guide_xml + OPF_XML_FOOTER
18
+ end
19
+
20
+ def self.valid_manifest_element?(e)
21
+ e[0..2] != "../" && e[0] != "/"
22
+ end
23
+
24
+ # define OPFImpl class and some constants
25
+ # they are all private, only Pubgen.OPF.generate use them
26
+ NCX_PATH = "toc.ncx"
27
+ METADATA_FORMAT = <<EOF
28
+ <metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf">
29
+ <dc:title>%s</dc:title>
30
+ <dc:creator opf:role="aut">%s</dc:creator>
31
+ <dc:date>%s</dc:date>
32
+ <dc:language>%s</dc:language>
33
+ <dc:subject>%s</dc:subject>
34
+ <dc:publisher>%s</dc:publisher>
35
+ <dc:description>%s</dc:description>
36
+ <dc:contributor opf:role="bkp">%s</dc:contributor>
37
+ <dc:source>%s</dc:source>
38
+ <dc:rights>%s</dc:rights>
39
+ <dc:relation>%s</dc:relation>
40
+ <dc:identifier id="BookID" opf:scheme="UUID">%s</dc:identifier>%s
41
+ </metadata>
42
+ EOF
43
+ OPF_XML_HEADER = <<EOF
44
+ <?xml version='1.0' encoding='utf-8'?>
45
+ <package xmlns="http://www.idpf.org/2007/opf" version="2.0" unique-identifier="uuid_id">
46
+ EOF
47
+ OPF_XML_FOOTER = '</package>'
48
+
49
+ module OPFImpl
50
+ def self.guess_media_type(filename)
51
+ case filename.downcase
52
+ when /.*\.x?html?$/i
53
+ 'application/xhtml+xml'
54
+ when /.*\.css$/i
55
+ 'text/css'
56
+ when /.*\.(jpeg|jpg)$/
57
+ 'image/jpeg'
58
+ when /.*\.png$/i
59
+ 'image/png'
60
+ when /.*\.gif$/i
61
+ 'image/gif'
62
+ when /.*\.svg$/i
63
+ 'image/svg+xml'
64
+ when /.*\.ncx$/i
65
+ 'application/x-dtbncx+xml'
66
+ when /.*\.opf$/i
67
+ 'application/oebps-package+xml'
68
+ else
69
+ 'application/octet-stream'
70
+ end
71
+ end
72
+
73
+ def self.get_cover_id_and_manifest_xml(cover_path, manifest)
74
+ no = 1;
75
+ manifest_xml = " <manifest>\n"
76
+ cover_id = nil
77
+ file2id = {}
78
+
79
+ manifest.each do |path|
80
+ if OPF.valid_manifest_element?(path) == false
81
+ raise "One of manifest element (" + path + ") is not in sub-directory of yaml file"
82
+ end
83
+ id = "i%03d" % no
84
+ manifest_xml += " <item id=\"#{id}\" href=\"#{path}\" media-type=\"#{guess_media_type(path)}\"/>\n"
85
+ if path == cover_path
86
+ cover_id = id
87
+ end
88
+ file2id[path] = id
89
+
90
+ no += 1
91
+ end
92
+
93
+ if cover_path && cover_id == nil
94
+ raise "Can't find cover-image from manifest"
95
+ end
96
+
97
+ manifest_xml += " <item id=\"ncx\" href=\"#{NCX_PATH}\" media-type=\"application/x-dtbncx+xml\"/>\n </manifest>\n"
98
+
99
+ return cover_id, manifest_xml, file2id
100
+ end
101
+
102
+ def self.get_metadata_xml(metadata, uuid, cover_id)
103
+ cover_id_xml = ''
104
+ if cover_id != nil
105
+ cover_id_xml = "\n <meta name=\"cover\" content=\"%s\"/>" % cover_id
106
+ end
107
+ METADATA_FORMAT % [
108
+ CGI.escapeHTML(metadata['title'] || ''),
109
+ CGI.escapeHTML(metadata['creator'] || ''),
110
+ CGI.escapeHTML((metadata['date'].is_a?(Fixnum) ?
111
+ metadata['date'].to_s : metadata['date']) || ''),
112
+ CGI.escapeHTML(metadata['language'] || ''),
113
+ CGI.escapeHTML(metadata['subject'] || ''),
114
+ CGI.escapeHTML(metadata['publisher'] || ''),
115
+ CGI.escapeHTML(metadata['description'] || ''),
116
+ CGI.escapeHTML(metadata['contributor'] || ''),
117
+ CGI.escapeHTML(metadata['source'] || ''),
118
+ CGI.escapeHTML(metadata['rights'] || ''),
119
+ CGI.escapeHTML(metadata['relation'] || ''),
120
+ uuid, cover_id_xml
121
+ ]
122
+ end
123
+
124
+ def self.get_guide_xml(guide, file2id)
125
+ guide_xml = " <guide>\n"
126
+ cover_page = guide['cover-page']
127
+ if cover_page != nil
128
+ if file2id[cover_page] == nil
129
+ raise "Can't find cover-page from manifest"
130
+ else
131
+ guide_xml += " <reference href=\"%s\" type=\"cover\" title=\"Cover\"/>\n" % cover_page
132
+ end
133
+ end
134
+ toc_page = guide['toc-page']
135
+ if toc_page != nil
136
+ if file2id[toc_page] == nil
137
+ raise "Can't find toc-page from manifest"
138
+ else
139
+ guide_xml += " <reference href=\"%s\" type=\"toc\" title=\"Table of Contents\"/>\n" % toc_page
140
+ end
141
+ end
142
+ title_page = guide['title-page']
143
+ if title_page != nil
144
+ if file2id[title_page] == nil
145
+ raise "Can't find title-page from manifest"
146
+ else
147
+ guide_xml += " <reference href=\"%s\" type=\"title-page\" title=\"Title Page\"/>\n" % toc_page
148
+ end
149
+ end
150
+ guide_xml += " </guide>\n"
151
+ end
152
+
153
+ def self.get_spine_xml(spine, file2id)
154
+ spine_xml = " <spine toc=\"ncx\">\n"
155
+ spine.each do |path|
156
+ if file2id[path] != nil
157
+ spine_xml += " <itemref idref=\"#{file2id[path]}\"/>\n"
158
+ else
159
+ raise "Can't find spine element `#{path}' from manifest"
160
+ end
161
+ end
162
+ spine_xml += " </spine>\n"
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,17 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "pubgen"
5
+ s.version = "0.1.0"
6
+ s.platform = Gem::Platform::RUBY
7
+ s.author = "9beach"
8
+ s.email = ["9beach@gmail.com"]
9
+ s.homepage = "https://github.com/9beach/pubgen"
10
+ s.files = `git ls-files`.split("\n")
11
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ s.bindir = 'bin'
13
+ s.require_paths = ["lib"]
14
+ s.summary = "command-line based epub generator"
15
+ s.description = "Pubgen is a simple command-line based epub generator. With the simple YAML file, Pubgen generate the epub file for you."
16
+ s.add_dependency "rubyzip"
17
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pubgen
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - 9beach
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-26 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rubyzip
16
+ requirement: &2157457660 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2157457660
25
+ description: Pubgen is a simple command-line based epub generator. With the simple
26
+ YAML file, Pubgen generate the epub file for you.
27
+ email:
28
+ - 9beach@gmail.com
29
+ executables:
30
+ - pubgen
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - .gitignore
35
+ - LICENSE
36
+ - README.md
37
+ - bin/pubgen
38
+ - lib/pubgen.rb
39
+ - lib/pubgen/container.rb
40
+ - lib/pubgen/cover_page.rb
41
+ - lib/pubgen/ncx.rb
42
+ - lib/pubgen/opf.rb
43
+ - pubgen.gemspec
44
+ homepage: https://github.com/9beach/pubgen
45
+ licenses: []
46
+ post_install_message:
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ requirements: []
63
+ rubyforge_project:
64
+ rubygems_version: 1.8.15
65
+ signing_key:
66
+ specification_version: 3
67
+ summary: command-line based epub generator
68
+ test_files: []