My favorites | Sign in
Project Home Downloads Wiki Issues Source
Checkout   Browse   Changes    
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
=begin

Copyright 2008 Arachne Jericho <arachne.jericho@gmail.com>

This file is part of RubyEpub.

RubyEpub is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

RubyEpub is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with RubyEpub. If not, see <http://www.gnu.org/licenses/>.

=end
=begin rdoc

A "project" directory for an EPub publication, similar
in concept to a project in Mobipocket Creator.

TODO: Add unit tests for Project!

=end
require 'epub/opf'
require 'epub/ncx'
require 'epub/container'
require 'epub/templates'

require 'fileutils'
require 'ftools'
require 'socket'
require 'time'

module Epub

# An Epub project with its attendant source files.
# The main goal of this class is to coordinate metadata
# across multiple files (in particular, the OPF and the NCX).
class Project
DEFAULT_OPF_FILE = 'metadata.opf'
DEFAULT_NCX_FILE_ID = 'toc'
DEFAULT_NCX_FILE = 'toc.ncx'
DEFAULT_TITLE = 'TITLE'
DEFAULT_LANGUAGE = 'en-US'

attr_reader :directory, :title, :opf_file, :ncx_file, :identifier

# Constructor
#
# Parameters:
# - directory : Epub project directory
def initialize(directory)
@directory = directory

if (File.exists? @directory)
raise "'#{@directory}' is not a directory" if (!File.directory? @directory)
initialize_from_existing
else
create_new_project
end
end

# Sets the title of the project.
def title=(title)
@title = title
@opf_file.title = title
@ncx_file.title = title
end

# Sets the unique identifier of the project.
def identifier=(identifier)
@identifier = identifier
@opf_file.identifier = identifier
@ncx_file.identifier = identifier
end

# Sets the language of the project
def language=(language)
@opf_file.language = language
end

# Adds a creator.
#
# Parameters:
# - name : name of the contributor, in last_name,first_name form
# - role : role of the creator, defaults to author
def add_creator(name, role = 'aut')
@opf_file.add_creator(name, role)
end

# Writes all files using the current state.
def save
@opf_file.write
@ncx_file.write
end

# Compiles the actual epub file. Saves the project
# before proceeding.
#
# TODO: use Archive::Zip instead of external command
def compile
save

old_dir = Dir.pwd
begin
Dir.chdir @directory

epub_file = "#{File.join(old_dir, File.basename(Dir.pwd).gsub(/[ ,:]/,'_'))}.epub"

system(%Q(zip -Xr9D '#{epub_file}' mimetype META-INF #{localpath(@opf_file.file)}))
@opf_file.manifest.values.each do |item|
system(%Q(zip -Xr9D '#{epub_file}' '#{item.href}' -x mimetype))
end
ensure
Dir.chdir old_dir
end
end

# Registers a content file with the OPF.
#
# Parameters:
# - id : unique id for the file
# - href : path relative to the directory to the file
# - add_to_spine : (optional) if true, adds id to the spine; default false
# - media_type : (optional) media type of the file
def register_with_opf(id, href, add_to_spine = false, media_type = nil)
@opf_file.add_manifest_item(id, href, media_type)
if (add_to_spine)
@opf_file.add_spine_itemref(id)
end
end

# Sets up a reference guide item with the OPF.
#
def register_guide_reference(type, title, href)
@opf_file.add_guide_reference(type, title, href)
end

# Registers a content file with the NCX.
#
# Parameters:
# - id : unique id for the navigation point
# - label : label for the navigation point
# - src : path relative to the directory to the file
# - play_order : (optional) the play order; by default, the
# next available in the NCX.
def register_with_ncx(id, label, src, play_order = nil)
if (play_order)
@ncx_file.insert_navigation_point(id, label, src, play_order)
else
@ncx_file.add_navigation_point(id, label, src)
end
end

private

# Full path to the project directory.
#
# Parameters:
# - localpath : if given, prepends the full path;
# can be an array of path elements.
def fullpath(localpath)
return File.join(@directory, *localpath)
end

# Localize path with respect to the project, if
# appropriate.
#
# Parameters:
# - path : removes @directory from the path, raises
# exception if @directory is not part of
# the path
def localpath(path)
if (path !~ /^#{@directory}/)
raise "#{@directory} is not part of path '#{path}'!"
else
return path.sub(/^#{@directory}[\/]?/, '')
end
end

# Reads in data from an existing project
def initialize_from_existing
opf_location = ContainerFile.get_opf_path(@directory)

# The OPF file has a pointer to the TOC file,
# the title, and the identifier for this project
@opf_file = Epub::Opf::OpfFile.new(fullpath(opf_location))
@title = @opf_file.title
@identifier = @opf_file.identifier

ncx_location = @opf_file.get_toc_location

# The NCX file is the last main file to open.
@ncx_file = Epub::Ncx::NcxFile.new(fullpath(ncx_location))
end

# Creates a brand new project, using defaults.
#
# If create_template is true, a template file (title.html) is
# created in the content directory and registered as part
# of the OPF manifest and the NCX file.
#
# Parameters:
# - create_template: create a template file. Default: false
#
def create_new_project(create_template = false)
FileUtils.mkdir_p(@directory)
Epub::MimeTypeFile.create(@directory)
Epub::ContainerFile.create(@directory, DEFAULT_OPF_FILE)

@ncx_file = Epub::Ncx::NcxFile.new(fullpath(DEFAULT_NCX_FILE))
@ncx_file.add_metadata('dtb:depth', '1')
@ncx_file.add_metadata('dtb:totalPageCount', '0')
@ncx_file.add_metadata('dtb:maxPageNumber', '0')

@opf_file = Epub::Opf::OpfFile.new(fullpath(DEFAULT_OPF_FILE))
@opf_file.language = DEFAULT_LANGUAGE
@opf_file.toc = DEFAULT_NCX_FILE_ID
@opf_file.add_manifest_item(DEFAULT_NCX_FILE_ID, DEFAULT_NCX_FILE)

self.identifier = "#{Socket.gethostname} [#{Time.now.to_s}]"
self.title = DEFAULT_TITLE

FileUtils.mkdir_p(fullpath('content'))

if create_template
File.open(fullpath(['content', 'title.html']), 'w') do |file|
Epub::Templates.writeHtmlTemplate(file, @title)
end
register_with_opf('title', 'content/title.html', true)
register_with_ncx('title', @title, 'content/title.html')
end
end
end
end

Change log

r30 by arachne.jericho on Jan 17, 2009   Diff
- Fixed bug: interpret last_name,
first_name correctly
- Comment speling
- By default, don't add a template HTML
file on project creation
- Ability to add an NCX navigation point
(no nesting yet)
Go to: 
Project members, sign in to write a code review

Older revisions

r26 by arachne.jericho on Jan 15, 2009   Diff
- require lowercase FileUtils to
prevent conflicts
- declare epub executable
- more help
r22 by arachne.jericho on Jan 15, 2009   Diff
- Add more convenience functions to
the main epub script.
r21 by arachne.jericho on Jan 3, 2009   Diff
- Refactored unit tests slightly
- Corrected OpfFile to output
dc:language not dc:lang
- Updated TODO list
- OpfFile: added add_spine_itemref
...
All revisions of this file

File info

Size: 8112 bytes, 240 lines
Powered by Google Project Hosting