Skip to content

Commit

Permalink
Improve file output in general (#67)
Browse files Browse the repository at this point in the history
  • Loading branch information
fnando committed Jan 26, 2024
1 parent f4853d5 commit 57a6c7b
Show file tree
Hide file tree
Showing 59 changed files with 656 additions and 723 deletions.
20 changes: 10 additions & 10 deletions README.md
Expand Up @@ -21,7 +21,7 @@ starting at \$15/mo.
- Write using Markdown
- Book layout support
- Syntax highlight
- Generate HTML, PDF, e-Pub, and Mobi
- Generate HTML, PDF, e-Pub (version 3.3), and Mobi
- Table of Contents automatically generated from chapter titles

## Installation
Expand Down Expand Up @@ -262,6 +262,15 @@ Kitabu::Markdown.processor = Redcarpet::Markdown.new(

The above options are Kitabu's defaults.

### Exporting PDFs using DocRaptor

If you're not planning to buy PrinceXML, consider using
[DocRaptor](http://docraptor.com). Here's how you can easily do it:

```bash
curl -H "Content-Type:application/json" -d'{"user_credentials":"YOUR_CREDENTIALS_HERE", "doc":{"name":"kitabu.pdf", "document_type":"pdf", "test":"false", "document_url":"https://example.com/output/kitabu.pdf.html"}}' http://docraptor.com/docs > kitabu.pdf
```

## References

- Markdown: <http://daringfireball.net/projects/markdown/syntax>
Expand All @@ -271,15 +280,6 @@ The above options are Kitabu's defaults.

- PrinceXML: [license](http://www.princexml.com/license/)

Alternatives:

- If you're not planning to buy PrinceXML, consider using
[DocRaptor](http://docraptor.com). Here's how you can easily do it:

```bash
curl -H "Content-Type:application/json" -d'{"user_credentials":"YOUR_CREDENTIALS_HERE", "doc":{"name":"kitabu.pdf", "document_type":"pdf", "test":"false", "document_url":"https://dl.dropboxusercontent.com/u/123456789/output/kitabu.pdf.html"}}' http://docraptor.com/docs > kitabu.pdf
```

## Maintainer

- [Nando Vieira](https://github.com/fnando)
Expand Down
Binary file modified attachments/browser-version.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified attachments/cover.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified attachments/kitabu.epub
Binary file not shown.
Binary file modified attachments/kitabu.mobi
Binary file not shown.
Binary file modified attachments/kitabu.pdf
Binary file not shown.
3 changes: 1 addition & 2 deletions kitabu.gemspec
Expand Up @@ -26,14 +26,13 @@ Gem::Specification.new do |s|
s.require_paths = ["lib"]

s.add_dependency "activesupport"
s.add_dependency "eeepub-with-cover-support"
s.add_dependency "epub-rb"
s.add_dependency "i18n"
s.add_dependency "nokogiri"
s.add_dependency "redcarpet"
s.add_dependency "rouge"
s.add_dependency "rubyzip"
s.add_dependency "thor"
s.add_dependency "zip-zip"

s.add_development_dependency "pry-meta"
s.add_development_dependency "rake"
Expand Down
9 changes: 1 addition & 8 deletions lib/kitabu.rb
Expand Up @@ -2,9 +2,8 @@

require "active_support/all"
require "digest/md5"
require "eeepub"
require "epub"
require "erb"
require "logger"
require "nokogiri"
require "open3"
require "optparse"
Expand All @@ -30,7 +29,6 @@ module Kitabu

require "kitabu/extensions/string"
require "kitabu/extensions/rouge"
require "kitabu/extensions/eeepub"
require "kitabu/errors"
require "kitabu/version"
require "kitabu/generator"
Expand All @@ -49,7 +47,6 @@ module Kitabu
require "kitabu/footnotes/pdf"
require "kitabu/hook"
require "kitabu/toc/html"
require "kitabu/toc/html/stream"
require "kitabu/toc/epub"
require "kitabu/dependency"
require "kitabu/stats"
Expand Down Expand Up @@ -90,8 +87,4 @@ def self.config(root_dir = nil)
erb = ERB.new(content).result
YAML.safe_load(erb).with_indifferent_access
end

def self.logger
@logger ||= Logger.new(File.open("/tmp/kitabu.log", "a"))
end
end
34 changes: 25 additions & 9 deletions lib/kitabu/cli.rb
Expand Up @@ -93,18 +93,34 @@ def permalinks
inside_ebook!

html = Kitabu::Exporter::HTML.new(root_dir).content
toc = Kitabu::TOC::HTML.generate(html)
toc = Kitabu::TOC::HTML.generate(Nokogiri::HTML(html))

toc.toc.each do |options|
level = options[:level] - 1
title = " #{options[:text]}: "
permalink = "##{options[:permalink]}"
renderer = lambda do |hierarchy, level = 1|
hierarchy.each do |entry|
title = " #{entry[:label]}: "
permalink = "##{entry[:content]}"

text = "*" * level
text << color(title, :blue)
text << color(permalink, :yellow)
say(text)
text = "*" * level
text << color(title, :blue)
text << color(permalink, :yellow)
say(text)

renderer.call(entry[:hierarchy], level + 1) if entry[:hierarchy].any?
end
end

renderer.call(toc.hierarchy)

# toc.hierarchy.each do |options|
# level = options[:level] - 1
# title = " #{options[:text]}: "
# permalink = "##{options[:permalink]}"

# text = "*" * level
# text << color(title, :blue)
# text << color(permalink, :yellow)
# say(text)
# end
end

desc "stats", "Display some stats about your e-book"
Expand Down
170 changes: 50 additions & 120 deletions lib/kitabu/exporter/epub.rb
Expand Up @@ -3,9 +3,12 @@
module Kitabu
class Exporter
class Epub < Base
SECTION_SELECTOR =
"div.chapter, div.section, section.chapter, section.section"

def sections
@sections ||=
html.css("div.chapter").each_with_index.map do |chapter, index|
html.css(SECTION_SELECTOR).each_with_index.map do |chapter, index|
html = Nokogiri::HTML(chapter.inner_html)

OpenStruct.new(
Expand All @@ -18,7 +21,18 @@ def sections
end

def epub
@epub ||= EeePub::Maker.new
@epub ||= ::Epub.new(
root_dir: tmp_dir,
title: config[:title],
subtitle: config[:subtitle],
language: config[:language],
copyright: config[:copyright],
publisher: config[:publisher],
contributors: Array(config[:contributors]),
creators: Array(config[:creators]),
identifiers: Array(config[:identifiers]),
date: config[:published_at] || Date.today.to_s
)
end

def html
Expand All @@ -30,19 +44,15 @@ def export

copy_styles!
copy_images!
File.open(root_dir.join("output/epub/cover.html"), "w") do |io|
io << render_template(
root_dir.join("templates/epub/cover.erb"),
config
)
end
set_metadata!

write_sections!
write_cover!
write_toc!

epub.files sections.map(&:filepath) + assets
epub.nav navigation
epub.toc_page toc_path
epub.files += [tmp_dir.join("cover.html"), tmp_dir.join("toc.html")]
epub.files += tmp_dir.glob("**/*").select do |entry|
!epub.files.include?(entry) && entry.file?
end

epub.save(epub_path)

Expand All @@ -61,56 +71,31 @@ def copy_images!
copy_directory("output/images", "output/epub/images")
end

def set_metadata!
epub.title config[:title]
epub.language config[:language]
epub.creator config[:authors].to_sentence
epub.publisher config[:publisher]
epub.date config[:published_at]
epub.uid config[:uid]
epub.identifier config[:identifier][:id],
scheme: config[:identifier][:type]

# epubchecker complains when assigning an image directly,
# but if we don't, then Apple Books doesn't render the cover.
# Need to investigate some more.
epub.cover_page cover_image if cover_image && File.exist?(cover_image)
end

def write_toc!
toc = TOC::Epub.new(navigation)

File.open(toc_path, "w") do |file|
file << toc.to_html
root_dir.join(toc_path).open("w") do |io|
io << render_template(
root_dir.join("templates/epub/toc.erb"),
config.merge(
navigation: ::Epub::Navigation.extract_html(
tmp_dir.glob("**/*.{xhtml,html}"),
root_dir: tmp_dir
)
)
)
end
end

def write_sections!
# First we need to get all ids, which are used as
# the anchor target.
#
links = sections.each_with_object({}) do |section, buffer|
section.html.css("[id]").each do |element|
anchor = "##{element['id']}"
buffer[anchor] = "#{section.filename}#{anchor}"
end
def write_cover!
root_dir.join(cover_path).open("w") do |io|
io << render_template(
root_dir.join("templates/epub/cover.erb"),
config
)
end
end

# Then we can normalize all links and
# manipulate other paths.
#
def write_sections!
sections.each do |section|
section.html.css("a[href^='#']").each do |link|
href = link["href"]
link.set_attribute("href", links.fetch(href, href))
end

# Normalize <ol>
#
section.html.css("ol[start]").each do |node|
node.remove_attribute("start")
end

# Remove anchors (only useful for html exports).
#
section.html.css("a.anchor").each(&:remove)
Expand All @@ -121,82 +106,23 @@ def write_sections!
node.remove_attribute("tabindex")
end

# Replace all srcs.
#
section.html.css("[src]").each do |element|
src = File.basename(element["src"]).gsub(/\.svg$/, ".png")
element.set_attribute("src", src)
element.set_attribute("alt", "")
element.node_name = "img"
end

FileUtils.mkdir_p(tmp_dir)

# Save file to disk.
#
File.open(section.filepath, "w") do |file|
body = section.html.css("body").to_xhtml.gsub(
section.html = Kitabu.run_hooks(:process_epub_section, section.html)

content = section.html.css("body").to_xhtml.gsub(
%r{<body>(.*?)</body>}m, "\\1"
)
file << render_chapter(body)
end
end
end

def render_chapter(content)
locals = config.merge(content:)
render_template(template_path, locals)
end
page_title = section.html.css("h2").first.text.strip
locals = config.merge(content:, page_title:)

def assets
@assets ||= begin
assets = Dir[root_dir.join("output/epub/styles/**/*.css")]
assets += Dir[root_dir.join("images/**/*.{jpg,png,gif}")]
assets
end
end

def cover_image
path = Dir[root_dir.join("output/epub/images/cover.{jpg,png,gif}").to_s]
.first

path if path && File.exist?(path)
end

def navigation
klass = Struct.new(:level, :data, :parent, keyword_init: true)

root = klass.new(level: 1, data: {nav: []})
current = root

sections.each do |section|
section.html.css("h2, h3, h4, h5, h6").each do |node|
label = CGI.escape_html(node.text.strip)
level = node.name[1].to_i

data = {
label:,
content: "#{section.filename}##{node.attributes['id']}",
nav: []
}

if level > current.level
current = klass.new(level:, data:, parent: current)
elsif level == current.level
current = klass.new(level:, data:, parent: current.parent)
else
while current.parent && current.parent.level >= level
current = current.parent
end

current = klass.new(level:, data:, parent: current.parent)
end

current.parent.data[:nav] << data
file << render_template(template_path, locals)
end
end

root.data[:nav]
end

def template_path
Expand All @@ -218,6 +144,10 @@ def tmp_dir
def toc_path
tmp_dir.join("toc.html")
end

def cover_path
tmp_dir.join("cover.html")
end
end
end
end

0 comments on commit 57a6c7b

Please sign in to comment.