text_to_epub 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +19 -0
  4. data/Gemfile.lock +38 -0
  5. data/LICENSE.txt +26 -0
  6. data/README.rdoc +41 -0
  7. data/Rakefile +55 -0
  8. data/VERSION +1 -0
  9. data/bin/gen_epub +40 -0
  10. data/bin/new_book +23 -0
  11. data/config_sample.yml +7 -0
  12. data/lib/book.rb +30 -0
  13. data/lib/chapter.rb +68 -0
  14. data/lib/epub_setup.rb +70 -0
  15. data/lib/text_to_epub.rb +15 -0
  16. data/spec/book_spec.rb +25 -0
  17. data/spec/chapter_spec.rb +65 -0
  18. data/spec/epub_setup_spec.rb +52 -0
  19. data/spec/spec_helper.rb +12 -0
  20. data/templates/META-INF/container.xml +7 -0
  21. data/templates/OEBPS/chapter.html.liquid +24 -0
  22. data/templates/OEBPS/content.opf.liquid +41 -0
  23. data/templates/OEBPS/end_of_book.html.liquid +33 -0
  24. data/templates/OEBPS/fonts.css +127 -0
  25. data/templates/OEBPS/fonts/BergamoStd-Bold.otf +0 -0
  26. data/templates/OEBPS/fonts/BergamoStd-BoldItalic.otf +0 -0
  27. data/templates/OEBPS/fonts/BergamoStd-Italic.otf +0 -0
  28. data/templates/OEBPS/fonts/BergamoStd-Regular.otf +0 -0
  29. data/templates/OEBPS/fonts/OFLGoudyStM-Italic.otf +0 -0
  30. data/templates/OEBPS/fonts/OFLGoudyStM.otf +0 -0
  31. data/templates/OEBPS/fonts/licenses.txt +158 -0
  32. data/templates/OEBPS/fonts/texgyrepagella-bold.otf +0 -0
  33. data/templates/OEBPS/fonts/texgyrepagella-bolditalic.otf +0 -0
  34. data/templates/OEBPS/fonts/texgyrepagella-italic.otf +0 -0
  35. data/templates/OEBPS/fonts/texgyrepagella-regular.otf +0 -0
  36. data/templates/OEBPS/fonts/texgyretermes-bold.otf +0 -0
  37. data/templates/OEBPS/fonts/texgyretermes-bolditalic.otf +0 -0
  38. data/templates/OEBPS/fonts/texgyretermes-italic.otf +0 -0
  39. data/templates/OEBPS/fonts/texgyretermes-regular.otf +0 -0
  40. data/templates/OEBPS/images/made_by_obf.jpg +0 -0
  41. data/templates/OEBPS/print.css +4 -0
  42. data/templates/OEBPS/stylesheet.css +82 -0
  43. data/templates/OEBPS/title.html.liquid +13 -0
  44. data/templates/OEBPS/toc.ncx.liquid +30 -0
  45. data/templates/mimetype +1 -0
  46. data/text_to_epub.gemspec +126 -0
  47. metadata +292 -0
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ gem 'RedCloth'
7
+ gem 'liquid'
8
+ gem 'uuid'
9
+ gem 'linguistics'
10
+
11
+
12
+ # Add dependencies to develop your gem here.
13
+ # Include everything needed to run rake, tests, features, etc.
14
+ group :development do
15
+ gem "rspec", "~> 2.2.0"
16
+ gem "bundler", "~> 1.0.0"
17
+ gem "jeweler", "~> 1.5.1"
18
+ gem "rcov", ">= 0"
19
+ end
@@ -0,0 +1,38 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ RedCloth (4.2.3)
5
+ diff-lcs (1.1.2)
6
+ git (1.2.5)
7
+ jeweler (1.5.1)
8
+ bundler (~> 1.0.0)
9
+ git (>= 1.2.5)
10
+ rake
11
+ linguistics (1.0.8)
12
+ liquid (2.2.2)
13
+ macaddr (1.0.0)
14
+ rake (0.8.7)
15
+ rcov (0.9.9)
16
+ rspec (2.2.0)
17
+ rspec-core (~> 2.2)
18
+ rspec-expectations (~> 2.2)
19
+ rspec-mocks (~> 2.2)
20
+ rspec-core (2.2.1)
21
+ rspec-expectations (2.2.0)
22
+ diff-lcs (~> 1.1.2)
23
+ rspec-mocks (2.2.0)
24
+ uuid (2.3.1)
25
+ macaddr (~> 1.0)
26
+
27
+ PLATFORMS
28
+ ruby
29
+
30
+ DEPENDENCIES
31
+ RedCloth
32
+ bundler (~> 1.0.0)
33
+ jeweler (~> 1.5.1)
34
+ linguistics
35
+ liquid
36
+ rcov
37
+ rspec (~> 2.2.0)
38
+ uuid
@@ -0,0 +1,26 @@
1
+ Copyright (c) 2010 Jason LaPier
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+
23
+ NOTE:
24
+ Some open source fonts are included for embedding in epubs. Please
25
+ refer to the license file for usage, found here:
26
+ templates/OEBPS/fonts/licenses.txt
@@ -0,0 +1,41 @@
1
+ = text_to_epub
2
+
3
+ Generates a template directory that you can use to build a custom epub file.
4
+
5
+ Usage:
6
+ new_book /path/to/new/book
7
+ (if the directory does not exist, it will be created)
8
+
9
+ Go to the new directory and modify the file config.yml
10
+
11
+ Run gen_epub from within the new directory.
12
+
13
+ Text files will be html-ized using textile (via the RedCloth gem). Check the templates directory for files you may want to customize (such as stylesheet.css or the .liquid html files).
14
+
15
+ == Converting to PDF
16
+
17
+ After you've created an epub file, you can easily convert it to PDF. Here are a couple of hints:
18
+
19
+ using Calibre (http://calibre-ebook.com/):
20
+ ebook-convert epub_folder/file_name.epub .pdf --custom-size=5x8 --base-font-size=12 --margin-top=54 --margin-left=54 --margin-bottom=54 --margin-right=54
21
+
22
+ using wkhtmltopdf (http://code.google.com/p/wkhtmltopdf/):
23
+ wkhtmltopdf --page-width 5in --page-height 8in -L 0.5in -R 0.5in -B 0.5in -T 0.25in --footer-center '[page]' --footer-spacing 2 --print-media-type --footer-font-name TexGyreTermes --footer-font-size 9 epub_folder/OEBPS/*.html mybook.pdf
24
+
25
+
26
+
27
+ == Contributing to text_to_epub
28
+
29
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
30
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
31
+ * Fork the project
32
+ * Start a feature/bugfix branch
33
+ * Commit and push until you are happy with your contribution
34
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
35
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
36
+
37
+ == Copyright
38
+
39
+ Copyright (c) 2010 Jason LaPier. See LICENSE.txt for
40
+ further details.
41
+
@@ -0,0 +1,55 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = "text_to_epub"
16
+ gem.homepage = "http://github.com/jlapier/text_to_epub"
17
+ gem.license = "MIT"
18
+ gem.summary = %Q{Generate epub skeleton, convert plain text files into xhtml and create epub}
19
+ gem.description = %Q{Generates a template directory that you can use to build a custom epub.
20
+ After customizing templates, use bin/gen_epub.rb to create and validate an epub archive.}
21
+ gem.email = "jason.lapier@gmail.com"
22
+ gem.authors = ["Jason LaPier"]
23
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
24
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
25
+ # gem.add_runtime_dependency 'jabber4r', '> 0.1'
26
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
27
+ gem.add_runtime_dependency 'RedCloth'
28
+ gem.add_runtime_dependency 'liquid'
29
+ gem.add_runtime_dependency 'uuid'
30
+ gem.add_runtime_dependency 'linguistics'
31
+ end
32
+ Jeweler::RubygemsDotOrgTasks.new
33
+
34
+ require 'rspec/core'
35
+ require 'rspec/core/rake_task'
36
+ RSpec::Core::RakeTask.new(:spec) do |spec|
37
+ spec.pattern = FileList['spec/**/*_spec.rb']
38
+ end
39
+
40
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
41
+ spec.pattern = 'spec/**/*_spec.rb'
42
+ spec.rcov = true
43
+ end
44
+
45
+ task :default => :spec
46
+
47
+ require 'rake/rdoctask'
48
+ Rake::RDocTask.new do |rdoc|
49
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
50
+
51
+ rdoc.rdoc_dir = 'rdoc'
52
+ rdoc.title = "text_to_epub #{version}"
53
+ rdoc.rdoc_files.include('README*')
54
+ rdoc.rdoc_files.include('lib/**/*.rb')
55
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.1
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/ruby
2
+
3
+ @base_dir = File.expand_path(File.dirname(__FILE__) + '/..')
4
+
5
+ $: << File.join(@base_dir, 'lib')
6
+
7
+ require File.join(@base_dir, 'lib', 'text_to_epub')
8
+
9
+ include EpubSetup
10
+
11
+ @config = YAML::load(File.read('config.yml'))
12
+
13
+ @epub_folder = File.join(@base_dir, @config[:temp_epub_folder])
14
+
15
+ make_skeleton @base_dir, @epub_folder
16
+
17
+ @book = Book.new @config[:book_title], @config[:author]
18
+ @book.chapters = EpubSetup::read_chapters(@config[:chapter_glob])
19
+
20
+ write_templates(@book)
21
+
22
+ # TODO: use rubyzip or zipruby or something - they all seem to be a PITA compared to unix zip
23
+
24
+ FileUtils.cd @epub_folder
25
+ FileUtils.rm @config[:file_name] if File.exists?(@config[:file_name])
26
+
27
+ puts "\nGenerating #{@epub_folder}/#{@config[:file_name]}"
28
+
29
+ system "zip -0Xq #{@config[:file_name]} mimetype"
30
+ system "zip -Xr9Dq #{@config[:file_name]} *"
31
+
32
+ puts "\nRunning epubcheck..."
33
+ system "java -jar #{@config[:epubcheck]} #{@config[:file_name]}"
34
+
35
+
36
+ puts "\nnext convert to pdf for POD:"
37
+ puts "\nebook-convert #{@epub_folder}/#{@config[:file_name]} .pdf --custom-size=5x8 --base-font-size=12 --margin-top=54 --margin-left=54 --margin-bottom=54 --margin-right=54"
38
+
39
+ puts "\n -- or use wkhtmltopdf:"
40
+ puts "\nwkhtmltopdf --page-width 5in --page-height 8in -L 0.5in -R 0.5in -B 0.5in -T 0.25in --footer-center '[page]' --footer-spacing 2 --print-media-type --footer-font-name TexGyreTermes --footer-font-size 9 #{@epub_folder}/OEBPS/*.html mybook1.pdf"
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'fileutils'
4
+
5
+ if ARGV[0]
6
+ @destination = ARGV[0]
7
+ else
8
+ puts "Usage:\n"
9
+ puts "new_book.rb path/to/copy/templates/to"
10
+ exit
11
+ end
12
+
13
+ @base_dir = File.expand_path(File.dirname(__FILE__) + '/..')
14
+
15
+ puts "Creating templates..."
16
+ FileUtils.mkdir_p @destination
17
+ puts "Copying necessary files..."
18
+ FileUtils.cp_r File.join(@base_dir, 'templates'), @destination
19
+ FileUtils.cp_r File.join(@base_dir, 'lib'), @destination
20
+ FileUtils.cp_r File.join(@base_dir, 'bin'), @destination
21
+ FileUtils.cp 'config_sample.yml', File.join(@destination, 'config.yml')
22
+
23
+ puts "Done! Go to #{@destination} and edit config.yml and then run ./bin/gen_epub.rb from that directory."
@@ -0,0 +1,7 @@
1
+ ---
2
+ :book_title: My Book Title
3
+ :author: My Name
4
+ :epubcheck: /opt/epubcheck/epubcheck-1.1.jar
5
+ :file_name: mybook.epub
6
+ :chapter_glob: ~/some/path/*.txt
7
+ :temp_epub_folder: epub
@@ -0,0 +1,30 @@
1
+ # Book: contains info like title and creator
2
+ # also contains an array of chapters (Chapter class)
3
+ class Book
4
+ attr_accessor :title, :creator, :chapters, :pub_date, :pub_year, :isbn
5
+ liquid_methods :title, :creator, :chapters, :pub_date, :pub_year, :book_id, :cc_url
6
+
7
+ def initialize(title, creator, pub_date = Date.today, isbn = nil)
8
+ self.title = title
9
+ self.creator = creator
10
+ self.pub_date = pub_date
11
+ self.isbn = isbn
12
+ end
13
+
14
+ def book_id
15
+ @book_id ||= (isbn || "urn:uuid:#{UUID.new.generate}")
16
+ end
17
+
18
+ def pub_year
19
+ pub_date.year
20
+ end
21
+
22
+ def cc_url
23
+ # attribution only, free to distribute, modify, or use commercially:
24
+ # "http://creativecommons.org/licenses/by/3.0/"
25
+
26
+ # free to share with attribution, non-commercial, no derivatives
27
+ "http://creativecommons.org/licenses/by-nc-nd/3.0/"
28
+ end
29
+ end
30
+
@@ -0,0 +1,68 @@
1
+ # Chapter
2
+ # contains name, content (text-only)
3
+ # generates html
4
+ # file_name should reference html file after it is written
5
+ class Chapter
6
+ attr_accessor :number, :name, :subhead, :file_name, :content
7
+ liquid_methods :number, :name, :subhead, :file_name, :word_count, :html, :chapter_id,
8
+ :number_as_word, :number_or_name, :name_or_number
9
+
10
+ def initialize(lines)
11
+ meta = true
12
+ while meta and line = lines.shift do
13
+ line.strip!
14
+ if line =~ /^Chapter:/
15
+ meta_num = line.scan(/^Chapter:\s*(\d+)/).flatten.first
16
+ self.number = meta_num.strip.to_i if meta_num
17
+ elsif line =~ /^Name:/
18
+ meta_name = line.scan(/^Name:\s*(.+)/).flatten.first
19
+ self.name = meta_name.strip if meta_name
20
+ elsif line =~ /^Subhead:/
21
+ meta_sub = line.scan(/^Subhead:\s*(.+)/).flatten.first
22
+ self.subhead = meta_sub.strip if meta_sub
23
+ else
24
+ lines = [line] + lines if line
25
+ meta = false
26
+ end
27
+ end
28
+
29
+ self.content = lines.join
30
+ end
31
+
32
+ def word_count
33
+ @word_count ||= (name || "" + content).gsub(/(_|\*|,|:)/, '').scan(/(\w|-|')+/).size
34
+ end
35
+
36
+ def html
37
+ content.strip!
38
+ @html ||= RedCloth.new(content).to_html
39
+ end
40
+
41
+ def chapter_id
42
+ @book_id ||= file_name.gsub(/\W/,'_').gsub('.html', '')
43
+ end
44
+
45
+ def number_as_word
46
+ number ? Linguistics::EN.numwords(number).capitalize : nil
47
+ end
48
+
49
+ # if there is a number, give us that written out as words; otherwise give the chapter name
50
+ def number_or_name
51
+ if number
52
+ "Chapter #{number_as_word}"
53
+ else
54
+ name || ""
55
+ end
56
+ end
57
+
58
+ # if there is a name, give us that; otherwise give the number written out as words
59
+ def name_or_number
60
+ if name and !name.empty?
61
+ name
62
+ elsif number
63
+ "Chapter #{number_as_word}"
64
+ else
65
+ ""
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,70 @@
1
+ # EpubSetup
2
+ # making directories and moving files and whatnot
3
+ module EpubSetup
4
+ def make_skeleton(base_dir, epub_folder)
5
+ @epub_folder = epub_folder
6
+ @source_templates_dir = File.join(base_dir, 'templates')
7
+ @target_meta_dir = File.join(@epub_folder, 'META-INF')
8
+ @target_oebps_dir = File.join(@epub_folder, 'OEBPS')
9
+
10
+ FileUtils.rm_rf @epub_folder if File.exists?(@epub_folder)
11
+ FileUtils.mkdir_p @epub_folder
12
+ FileUtils.mkdir_p @target_meta_dir
13
+ FileUtils.mkdir_p @target_oebps_dir
14
+
15
+ FileUtils.cp File.join(@source_templates_dir, 'mimetype'), @epub_folder
16
+ FileUtils.cp File.join(@source_templates_dir, 'META-INF', 'container.xml'), @target_meta_dir
17
+
18
+ # TODO - somehow detect these "asset" folders and files -
19
+ # for now they are these defaults: css, images, fonts
20
+ FileUtils.cp Dir[File.join(@source_templates_dir, 'OEBPS', '*.css')], @target_oebps_dir
21
+ FileUtils.cp_r File.join(@source_templates_dir, 'OEBPS', 'images'), @target_oebps_dir
22
+ FileUtils.cp_r File.join(@source_templates_dir, 'OEBPS', 'fonts'), @target_oebps_dir
23
+
24
+ # liquid templates for rest of files
25
+ @chapter_liq_template = Liquid::Template.parse(File.read(File.join(@source_templates_dir, 'OEBPS', 'chapter.html.liquid' )))
26
+ @content_liq_template = Liquid::Template.parse(File.read(File.join(@source_templates_dir, 'OEBPS', 'content.opf.liquid' )))
27
+ @title_liq_template = Liquid::Template.parse(File.read(File.join(@source_templates_dir, 'OEBPS', 'title.html.liquid' )))
28
+ @toc_liq_template = Liquid::Template.parse(File.read(File.join(@source_templates_dir, 'OEBPS', 'toc.ncx.liquid' )))
29
+ @eob_liq_template = Liquid::Template.parse(File.read(File.join(@source_templates_dir, 'OEBPS', 'end_of_book.html.liquid')))
30
+ end
31
+
32
+ def read_chapters(file_glob)
33
+ file_glob = File.expand_path(file_glob)
34
+ puts "Reading files: #{file_glob} (#{Dir[file_glob].size} files found)"
35
+ chapters = []
36
+
37
+ Dir[file_glob].each do |txtfile|
38
+ chapter = nil
39
+ File.open(txtfile) do |f|
40
+ chapter = Chapter.new(f.readlines)
41
+ chapter.file_name = "#{File.basename(txtfile, '.txt')}.html"
42
+ end
43
+ chapters << chapter if chapter
44
+ end
45
+
46
+ # returns chapters as an array sorted by name
47
+ chapters.sort_by { |c| [c.number, c.name, c.file_name] }
48
+ end
49
+
50
+ def write_templates(book)
51
+ book.chapters.each do |chapter|
52
+ html_output = @chapter_liq_template.render 'chapter' => chapter
53
+ puts "Writing: #{@epub_folder}/OEBPS/#{chapter.file_name}"
54
+ File.open(File.join(@target_oebps_dir, chapter.file_name), "w") { |f| f.puts html_output }
55
+ end
56
+
57
+ puts "Writing: #{@epub_folder}/OEBPS/content.opf"
58
+ File.open(File.join(@target_oebps_dir, 'content.opf'), "w") { |f| f.puts @content_liq_template.render('book' => book,
59
+ 'css_files' => Dir[File.join(@source_templates_dir, 'OEBPS', '*.css')].map { |f| File.basename(f) },
60
+ 'image_files' => Dir[File.join(@source_templates_dir, 'OEBPS', 'images', '*')].map { |f| File.basename(f) },
61
+ 'font_files' => Dir[File.join(@source_templates_dir, 'OEBPS', 'fonts', '*')].map { |f| File.basename(f) } ) }
62
+ puts "Writing: #{@epub_folder}/OEBPS/title.html"
63
+ File.open(File.join(@target_oebps_dir, 'title.html'), "w") { |f| f.puts @title_liq_template.render('book' => book) }
64
+ puts "Writing: #{@epub_folder}/OEBPS/end_of_book.html"
65
+ File.open(File.join(@target_oebps_dir, 'end_of_book.html'), "w") { |f| f.puts @eob_liq_template.render('book' => book) }
66
+ puts "Writing: #{@epub_folder}/OEBPS/toc.ncx"
67
+ File.open(File.join(@target_oebps_dir, 'toc.ncx'), "w") { |f| f.puts @toc_liq_template.render('book' => book) }
68
+ end
69
+ end
70
+
@@ -0,0 +1,15 @@
1
+ # gems
2
+ require 'liquid'
3
+ require 'RedCloth'
4
+ require 'linguistics'
5
+ require 'uuid'
6
+
7
+ # ruby libs
8
+ require 'fileutils'
9
+ require 'date'
10
+
11
+ # text_to_epub libs
12
+ require 'book'
13
+ require 'chapter'
14
+ require 'epub_setup'
15
+