ebook_generator 0.0.3 → 0.0.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bf15d724642369b00ca8059e0e543843b9d90994
4
- data.tar.gz: 88b3f1ea8d9cf688592d9fe3c3da998002699c9c
3
+ metadata.gz: 4430a1086ca77ff50348df43afafb4890e520053
4
+ data.tar.gz: 87a99f1c44cb867ad21eb46301343b92e5d12188
5
5
  SHA512:
6
- metadata.gz: 2e7d7a9fc89599202c886abe6537de107caca1b886e304b46a333144c324f0bcfd2c803bd645c2d09e5e2da8e4dd03fe0a68a350b8f2892b0fd9cb84118ac99e
7
- data.tar.gz: 716ca817854459a200355b5e53ede17c12dae748560d7f9dcf19730710fe8e6b71bbf80766596b6bc0c14b2212b66152f814dc1a276aac2c7c31f298960fe255
6
+ metadata.gz: c6bbf83277abbf49711c4db131f1a4391e4dc4e11a6f185bceac23e439f367bfb726dde392ea109d884fb55608865fe35aa90dc02cb8fd50feda8cc75666a9ef
7
+ data.tar.gz: 0c279a1b23d92c7b83c8c381a69da4ace0b6556f517f5cee84859a437b257a8ece13cb1507a8b76aea164eacc1d81cf167112455ff3926a95be6c3aee9c3e0a6
data/Changelog.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # EbookGenerator Changelog
2
2
 
3
+ ## 0.0.4 (2014-04-40)
4
+
5
+ - Added missing file "zip_file_processor"
6
+ - Refactored code to use builder instead of strings
7
+
3
8
  ## 0.0.3 (2014-04-18)
4
9
 
5
10
  - Added gem dependencies
data/README.md CHANGED
@@ -27,22 +27,15 @@ Or install it yourself as:
27
27
  ## Usage
28
28
 
29
29
  Generate the tables needed to process the ebooks:
30
-
31
30
  `rails generate ebook_generator`
32
31
 
33
- Migrate the db:
34
-
32
+ Migrate the database:
35
33
  `rake db:migrate`
36
34
 
37
- Require the ebook_generator module in your class:
38
-
39
- `include 'EbookGenerator'`
40
-
41
35
  Pass the id for the ebook you want to generate:
42
-
43
36
  `EbookGenerator.generate_ebook(ebook.id)`
44
37
 
45
- This will then generate an ebook based on the values in the db in the /tmp folder.
38
+ This will then generate an ePub based on the values in the db and output to the /tmp folder.
46
39
 
47
40
  ## Feature roadmap
48
41
 
@@ -10,8 +10,10 @@ Gem::Specification.new do |s|
10
10
  s.description = "A simple eBook (ePub) generator gem"
11
11
  s.authors = ["Kris Quigley"]
12
12
  s.email = 'kris@krisquigley.co.uk'
13
- s.homepage = 'https://github.com/krisquigley/ebook_generator'
13
+ s.homepage =
14
+ 'https://github.com/krisquigley/ebook_generator'
14
15
 
16
+ s.add_runtime_dependency 'rails', '>= 4.0.0'
15
17
  s.add_runtime_dependency 'friendly_id', '>= 5.0.3'
16
18
  s.add_runtime_dependency 'pg', '>= 0.17.1'
17
19
  s.add_runtime_dependency 'redcarpet', '>= 3.0.0'
@@ -1,3 +1,3 @@
1
1
  module EbookGenerator
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -0,0 +1,48 @@
1
+ require 'zip'
2
+
3
+ # This is a simple example which uses rubyzip to
4
+ # recursively generate a zip file from the contents of
5
+ # a specified directory. The directory itself is not
6
+ # included in the archive, rather just its contents.
7
+ #
8
+ # Usage:
9
+ # directoryToZip = "/tmp/input"
10
+ # outputFile = "/tmp/out.zip"
11
+ # zf = ZipFileGenerator.new(directoryToZip, outputFile)
12
+ # zf.write()
13
+ class ZipFileProcessor
14
+
15
+ # Initialize with the directory to zip and the location of the output archive.
16
+ def initialize(inputDir, outputFile)
17
+ @inputDir = inputDir
18
+ @outputFile = outputFile
19
+ end
20
+
21
+ # Zip the input directory.
22
+ def write()
23
+ entries = Dir.entries(@inputDir); entries.delete("."); entries.delete("..")
24
+ io = Zip::File.open(@outputFile, Zip::File::CREATE);
25
+
26
+ writeEntries(entries, "", io)
27
+ io.close();
28
+ end
29
+
30
+ # A helper method to make the recursion work.
31
+ private
32
+ def writeEntries(entries, path, io)
33
+
34
+ entries.each { |e|
35
+ zipFilePath = path == "" ? e : File.join(path, e)
36
+ diskFilePath = File.join(@inputDir, zipFilePath)
37
+ puts "Deflating " + diskFilePath
38
+ if File.directory?(diskFilePath)
39
+ io.mkdir(zipFilePath)
40
+ subdir =Dir.entries(diskFilePath); subdir.delete("."); subdir.delete("..")
41
+ writeEntries(subdir, zipFilePath, io)
42
+ else
43
+ io.get_output_stream(zipFilePath) { |f| f.puts(File.open(diskFilePath, "rb").read())}
44
+ end
45
+ }
46
+ end
47
+
48
+ end
@@ -1,4 +1,6 @@
1
1
  require "ebook_generator/version"
2
+ require "ebook_generator/zip_file_processor"
3
+ require "builder"
2
4
 
3
5
  module EbookGenerator
4
6
 
@@ -7,170 +9,136 @@ module EbookGenerator
7
9
  end
8
10
 
9
11
  # Move writing of files into its own class that accepts an array
10
- def self.make_dir(path)
11
- Dir.mkdir(path, 0777) unless File.exists?(path)
12
+ def self.make_dirs(paths)
13
+ paths.each do |path|
14
+ Dir.mkdir(path, 0777) unless File.exists?(path)
15
+ end
12
16
  end
13
17
 
14
- def self.initialise_files(path)
15
- metainf_path = path + "/META-INF"
18
+ def self.generate_container(path)
16
19
 
17
- Dir.mkdir(metainf_path) unless File.exists?(metainf_path)
20
+ file = File.new(path + "/container.xml", "wb")
21
+ xm = Builder::XmlMarkup.new(:target => file, :indent => 2)
22
+ xm.instruct!
23
+ xm.container("version" => "1.0", "xmlns" => "urn:oasis:names:tc:opendocument:xmlns:container") {
24
+ xm.rootfiles {
25
+ xm.rootfile("full-path" => "OEBPS/content.opf", "media-type" => "application/oebps-package+xml")
26
+ }
27
+ }
18
28
 
19
- metainf = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
20
- <container version=\"1.0\" xmlns=\"urn:oasis:names:tc:opendocument:xmlns:container\">
21
- <rootfiles>
22
- <rootfile full-path=\"OEBPS/content.opf\" media-type=\"application/oebps-package+xml\"/>
23
- </rootfiles>
24
- </container>"
29
+ file.close
25
30
 
26
- File.open(metainf_path + "/container.xml", "w+") do |f|
27
- f.write(metainf)
28
- end
31
+ end
29
32
 
30
- mimetype = "application/epub+zip"
31
- mimetype_path = path + "/mimetype"
33
+ def self.generate_mimetype(path)
32
34
 
33
- File.open(mimetype_path, "w+") do |f|
34
- f.write(mimetype)
35
+ File.open(path, "w+") do |f|
36
+ f.write("application/epub+zip")
35
37
  end
36
38
 
37
- FileUtils.chmod 0755, mimetype_path
38
-
39
- content_path = path + "/OEBPS"
40
-
41
- Dir.mkdir(content_path) unless File.exists?(content_path)
42
-
43
- Dir.mkdir(content_path + "/Text") unless File.exists?(content_path + "/Text")
44
-
45
- Dir.mkdir(content_path + "/Styles") unless File.exists?(content_path + "/Styles")
46
-
47
- root = Rails.root.to_s
48
-
49
- FileUtils.cp "#{root}/app/ebook/style.css", content_path+"/Styles"
50
-
51
- return content_path
52
-
53
39
  end
54
40
 
55
- def self.generate_headers(attrs)
56
- xml = "<title>#{attrs.title}</title>
57
- <meta content=\"#{attrs.title}\" name=\"Title\" />
58
- <meta content=\"#{attrs.title}\" name=\"Author\" />
59
- <link href=\"../Styles/style.css\" rel=\"stylesheet\" type=\"text/css\" />"
60
- return xml
41
+ def self.copy_style(path)
42
+ FileUtils.cp Rails.root.to_s + "/app/ebook/style.css", path
61
43
  end
62
44
 
63
45
  def self.generate_sections(path, attrs)
64
46
  markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML, autolink: true, tables: true)
65
- headers = generate_headers(attrs)
66
47
 
67
48
  attrs.sections.each do |section|
68
- xml = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>
69
- <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"
70
- \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">
71
- <html xmlns=\"http://www.w3.org/1999/xhtml\">
72
- <head>
73
- #{headers}
74
- </head>
75
- <body>
76
- <div id=\"#{section.title}\">
77
- "
78
- xml += markdown.render(section.content)
79
- xml += "
80
- </div>
81
- </body>
82
- </html>"
83
-
84
- File.open(path + "/Section#{section.position}.html", "w+") do |f|
85
- f.write(xml)
86
- end
49
+ file = File.new(path + "/Section#{section.position}.html", "wb")
50
+
51
+ xm = Builder::XmlMarkup.new(:target => file, :indent => 2)
52
+ xm.instruct!
53
+ xm.declare! :DOCTYPE, :html, :PUBLIC, "-//W3C//DTD XHTML 1.1//EN", "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"
54
+ xm.html("xmlns" => "http://www.w3.org/1999/xhtml"){
55
+ xm.head {
56
+ xm.title { attrs.title }
57
+ xm.meta("content" => attrs.title, "name" => "Title")
58
+ xm.meta("content" => attrs.creator, "name" => "Author")
59
+ xm.link("href" => "../Styles/style.css", "rel" => "stylesheet", "type" => "text/css")
60
+ }
61
+ xm.body { |b| b << "<div id=\"#{section.title}\">" + markdown.render(section.content) + "</div>" }
62
+ }
63
+
64
+ file.close
87
65
  end
88
66
  end
89
67
 
90
68
  def self.generate_content_opf(path, attrs)
91
- xml = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>
92
- <package xmlns=\"http://www.idpf.org/2007/opf\" unique-identifier=\"BookId\" version=\"2.0\">
93
- <metadata xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:opf=\"http://www.idpf.org/2007/opf\">
94
- <dc:identifier id=\"BookId\" opf:scheme=\"UUID\">#{attrs.id}</dc:identifier>
95
- <dc:title>#{attrs.title}</dc:title>
96
- <dc:creator opf:role=\"aut\">#{attrs.creator}</dc:creator>
97
- <dc:language>#{attrs.language}</dc:language>
98
- <dc:date opf:event=\"modification\">#{attrs.updated_at}</dc:date>
99
- <dc:description>#{attrs.description}</dc:description>
100
- <dc:publisher>#{attrs.publisher}</dc:publisher>
101
- <dc:rights>#{attrs.rights}</dc:rights>
102
- <dc:subject>#{attrs.subject}</dc:subject>
103
- <dc:contributor opf:role=\"cov\">#{attrs.contributor}</dc:contributor>
104
- <meta content=\"0.7.2\" name=\"Sigil version\" />
105
- </metadata>
106
- <manifest>
107
- <item href=\"toc.ncx\" id=\"ncx\" media-type=\"application/x-dtbncx+xml\" />
108
- <item href=\"Styles/style.css\" id=\"style.css\" media-type=\"text/css\" />"
109
-
110
- attrs.sections.each do |section|
111
- xml += "<item href=\"Text/Section#{section.position}.html\" id=\"Section#{section.position}.html\" media-type=\"application/xhtml+xml\" />"
112
- end
113
-
114
- xml += "<item href=\"Fonts/junicode-italic-webfont.ttf\" id=\"junicode-italic-webfont.ttf\" media-type=\"application/x-font-ttf\" />
115
- <item href=\"Fonts/junicode-webfont.ttf\" id=\"junicode-webfont.ttf\" media-type=\"application/x-font-ttf\" />
116
- <item href=\"Fonts/junicode-bold-webfont.ttf\" id=\"junicode-bold-webfont.ttf\" media-type=\"application/x-font-ttf\" />
117
- </manifest>
118
- <spine toc=\"ncx\">"
119
-
120
- attrs.sections.each do |section|
121
- xml += "<itemref idref=\"Section#{section.position}.html\" />"
122
-
123
- end
124
- xml += "</spine>
125
- <guide />
126
- </package>"
127
-
128
- content_path = path + "/content.opf"
129
-
130
- File.open(content_path, "w+") do |f|
131
- f.write(xml)
132
- end
133
-
134
- FileUtils.chmod 0755, content_path
69
+ file = File.new(path + "/content.opf", "wb")
70
+
71
+ xm = Builder::XmlMarkup.new(:target => file, :indent => 2)
72
+ xm.instruct!
73
+ xm.package("xmlns" => "http://www.idpf.org/2007/opf", "unique-identifier" => "BookId", "version" => "2.0") {
74
+ xm.metadata("xmlns:dc" => "http://purl.org/dc/elements/1.1/", "xmlns:opf" => "http://www.idpf.org/2007/opf") {
75
+ xm.tag!("dc:identifier", attrs.id, "id" => "BookId", "opf:scheme" => "UUID")
76
+ xm.tag!("dc:title", attrs.title)
77
+ xm.tag!("dc:creator", attrs.creator, "opf:role" => "aut")
78
+ xm.tag!("dc:language", attrs.language)
79
+ xm.tag!("dc:date", attrs.updated_at, "opf:event" => "modification")
80
+ xm.tag!("dc:description", attrs.description)
81
+ xm.tag!("dc:publisher", attrs.publisher)
82
+ xm.tag!("dc:rights", attrs.rights)
83
+ xm.tag!("dc:subject", attrs.subject)
84
+ xm.tag!("dc:contributor", attrs.contributor, "opf:role" => "cov")
85
+ }
86
+ xm.manifest {
87
+ xm.item("href" => "toc.ncx", "id" => "ncx", "media-type" => "application/x-dtbncx+xml")
88
+ xm.item("href" => "Styles/style.css", "media-type" => "text/css")
89
+ attrs.sections.each do |section|
90
+ xm.item("href" => "Text/Section#{section.position}.html", "id" => "Section#{section.position}.html", "media-type" => "application/xhtml+xml")
91
+ end
92
+ }
93
+ xm.spine("toc" => "ncx") {
94
+ attrs.sections.each do |section|
95
+ xm.itemref("idref" => "Section#{section.position}.html")
96
+ end
97
+ }
98
+ xm.guide()
99
+ }
100
+
101
+ file.close
135
102
 
136
103
  end
137
104
 
138
105
  def self.generate_toc_ncx(path, attrs)
139
- xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>
140
- <!DOCTYPE ncx PUBLIC \"-//NISO//DTD ncx 2005-1//EN\"
141
- \"http://www.daisy.org/z3986/2005/ncx-2005-1.dtd\">
142
- <ncx xmlns=\"http://www.daisy.org/z3986/2005/ncx/\" version=\"2005-1\">
143
- <head>
144
- <meta content=\"urn:uuid:${attrs.id}\" name=\"dtb:uid\"/>
145
- <meta content=\"2\" name=\"dtb:depth\"/>
146
- <meta content=\"0\" name=\"dtb:totalPageCount\"/>
147
- <meta content=\"0\" name=\"dtb:maxPageNumber\"/>
148
- </head>
149
- <docTitle>
150
- <text>#{attrs.title}</text>
151
- </docTitle>
152
- <navMap>"
153
-
154
106
 
155
- attrs.sections.each do |section|
156
- xml += "<navPoint id=\"navPoint-#{section.position}\" playOrder=\"#{section.position}\">
157
- <navLabel>
158
- <text>#{section.title}</text>
159
- </navLabel>
160
- <content src=\"Text/Section#{section.position}.html\"/>
161
- </navPoint>"
162
- end
107
+ file = File.new(path + "/toc.ncx", "wb")
108
+
109
+ xm = Builder::XmlMarkup.new(:target => file, :indent => 2)
110
+ xm.instruct!
111
+ xm.declare! :DOCTYPE, :ncx, :PUBLIC, "-//NISO//DTD ncx 2005-1//EN", "http://www.daisy.org/z3986/2005/ncx-2005-1.dtd"
112
+ xm.ncx("xmlns" => "http://www.daisy.org/z3986/2005/ncx/", "version" => "2005-1") {
113
+ xm.head {
114
+ xm.meta("content" => "urn:uuid:${attrs.id}", "name" => "dtb:uid")
115
+ xm.meta("content" => "2", "name" => "dtb:depth")
116
+ xm.meta("content" => "0", "name" => "dtb:totalPageCount")
117
+ xm.meta("content" => "0", "name" => "dtb:maxPageNumber")
118
+ }
119
+ xm.docTitle {
120
+ xm.text(attrs.title)
121
+ }
122
+ xm.navMap {
123
+ attrs.sections.each do |section|
124
+ xm.navPoint("id" => "navPoint-#{section.position}", "playOrder" => "#{section.position}") {
125
+ xm.navLabel {
126
+ xm.text(section.title)
127
+ }
128
+ xm.content("src" => "Text/Section#{section.position}.html")
129
+ }
130
+ end
131
+ }
132
+ }
133
+
134
+ file.close
163
135
 
164
- xml += "</navMap>
165
- </ncx>"
166
-
167
- toc_path = path + "/toc.ncx"
136
+ end
168
137
 
169
- File.open(toc_path, "w+") do |f|
170
- f.write(xml)
138
+ def self.change_perms(files)
139
+ files.each do |file|
140
+ FileUtils.chmod 0755, file
171
141
  end
172
-
173
- FileUtils.chmod 0755, toc_path
174
142
  end
175
143
 
176
144
  def self.remove_tmp_dir(directory)
@@ -179,29 +147,41 @@ module EbookGenerator
179
147
 
180
148
  def self.generate_ebook(ebook_id)
181
149
 
182
- # create tmp directory based on UUID
183
- path = Rails.root.join "tmp/#{ebook_id}"
184
- make_dir(path.to_s)
150
+ # Set the root path of the ebook
151
+ path = Rails.root.to_s + "/tmp/#{ebook_id}"
152
+
153
+ # Make all required dirs
154
+ dirs = [path, path + "/META-INF", path + "/OEBPS", path + "/OEBPS/Text", path + "/OEBPS/Styles"]
155
+ make_dirs(dirs)
185
156
 
186
- # set up required files and dirs
187
- content_path = initialise_files(path.to_s)
157
+ # Create container.xml
158
+ generate_container(path + "/META-INF")
159
+
160
+ # Create mimetype
161
+ generate_mimetype(path + "/mimetype")
162
+
163
+ # Move default stylesheet into styles folder
164
+ copy_style(path + "/OEBPS/Styles")
188
165
 
189
166
  # loop through each section loading the reference header and saving as it's own section
190
167
  attrs = Ebook.find(ebook_id)
191
- generate_sections(content_path + "/Text", attrs)
168
+ generate_sections(path + "/OEBPS/Text", attrs)
192
169
 
193
170
  # generate toc based on the number of sections generated
194
- generate_content_opf(content_path, attrs)
195
- generate_toc_ncx(content_path, attrs)
171
+ generate_content_opf(path + "/OEBPS", attrs)
172
+ generate_toc_ncx(path + "/OEBPS", attrs)
173
+
174
+ # change permissions for files that need to be executable
175
+ files = [path + "/OEBPS/toc.ncx", path + "/OEBPS/content.opf", path + "/mimetype"]
176
+ change_perms(files)
196
177
 
197
178
  # zip all files
198
179
  zipfile_name = Rails.root.to_s + "/tmp/" + attrs.slug + ".epub"
199
-
200
- zf = ZipFileProcessor.new(path.to_s, zipfile_name)
180
+ zf = ZipFileProcessor.new(path, zipfile_name)
201
181
  zf.write
202
182
 
203
183
  # Clean up the tmp dir
204
- remove_tmp_dir(path.to_s + "/")
184
+ remove_tmp_dir(path + "/")
205
185
 
206
186
  end
207
187
 
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ebook_generator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kris Quigley
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-18 00:00:00.000000000 Z
11
+ date: 2014-04-30 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: 4.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: 4.0.0
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: friendly_id
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -83,6 +97,7 @@ files:
83
97
  - lib/ebook_generator/initializer.rb
84
98
  - lib/ebook_generator/migration.rb
85
99
  - lib/ebook_generator/version.rb
100
+ - lib/ebook_generator/zip_file_processor.rb
86
101
  - lib/generators/ebook_generator_generator.rb
87
102
  homepage: https://github.com/krisquigley/ebook_generator
88
103
  licenses: