kitabu 2.0.0 → 2.0.1

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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -1
  3. data/Gemfile.lock +1 -1
  4. data/README.md +7 -5
  5. data/lib/kitabu.rb +12 -1
  6. data/lib/kitabu/cli.rb +1 -1
  7. data/lib/kitabu/exporter.rb +5 -5
  8. data/lib/kitabu/{parser.rb → exporter/base.rb} +9 -11
  9. data/lib/kitabu/exporter/css.rb +24 -0
  10. data/lib/kitabu/{parser → exporter}/epub.rb +2 -2
  11. data/lib/kitabu/exporter/html.rb +109 -0
  12. data/lib/kitabu/{parser → exporter}/mobi.rb +2 -2
  13. data/lib/kitabu/exporter/pdf.rb +43 -0
  14. data/lib/kitabu/{parser → exporter}/txt.rb +2 -2
  15. data/lib/kitabu/footnotes/base.rb +33 -0
  16. data/lib/kitabu/footnotes/html.rb +52 -0
  17. data/lib/kitabu/footnotes/pdf.rb +47 -0
  18. data/lib/kitabu/source_list.rb +73 -0
  19. data/lib/kitabu/stats.rb +1 -1
  20. data/lib/kitabu/stream.rb +1 -1
  21. data/lib/kitabu/toc/html.rb +6 -6
  22. data/lib/kitabu/version.rb +1 -1
  23. data/spec/kitabu/exporter/css_spec.rb +16 -0
  24. data/spec/kitabu/{parser → exporter}/epub_spec.rb +3 -3
  25. data/spec/kitabu/exporter/html_spec.rb +36 -0
  26. data/spec/kitabu/{parser → exporter}/mobi_spec.rb +3 -3
  27. data/spec/kitabu/{parser → exporter}/pdf_spec.rb +3 -3
  28. data/spec/kitabu/{parser → exporter}/txt_spec.rb +3 -3
  29. data/spec/kitabu/footnotes/html_spec.rb +67 -0
  30. data/spec/kitabu/{parser/html_spec.rb → source_list_spec.rb} +5 -34
  31. data/spec/kitabu/stats_spec.rb +7 -7
  32. data/templates/templates/styles/pdf.scss +1 -47
  33. data/templates/text/04_Dynamic_Content.erb +1 -1
  34. metadata +29 -54
  35. data/examples/kitabu/output/epub/images/.gitkeep +0 -0
  36. data/examples/kitabu/output/epub/images/kitabu-icon.png +0 -0
  37. data/examples/kitabu/output/epub/images/kitabu-icon.svg +0 -19
  38. data/examples/kitabu/output/epub/images/kitabu-word.png +0 -0
  39. data/examples/kitabu/output/epub/images/kitabu-word.svg +0 -14
  40. data/examples/kitabu/output/epub/images/kitabu.png +0 -0
  41. data/examples/kitabu/output/epub/images/kitabu.svg +0 -20
  42. data/examples/kitabu/output/epub/section_0.html +0 -266
  43. data/examples/kitabu/output/epub/section_1.html +0 -246
  44. data/examples/kitabu/output/epub/section_2.html +0 -520
  45. data/examples/kitabu/output/epub/section_3.html +0 -282
  46. data/examples/kitabu/output/epub/section_4.html +0 -276
  47. data/examples/kitabu/output/epub/styles/epub.css +0 -437
  48. data/examples/kitabu/output/epub/styles/html.css +0 -712
  49. data/examples/kitabu/output/epub/styles/pdf.css +0 -840
  50. data/examples/kitabu/output/epub/styles/print.css +0 -1278
  51. data/examples/kitabu/output/epub/toc.html +0 -37
  52. data/examples/kitabu/output/images/.gitkeep +0 -0
  53. data/examples/kitabu/output/images/kitabu-icon.png +0 -0
  54. data/examples/kitabu/output/images/kitabu-icon.svg +0 -19
  55. data/examples/kitabu/output/images/kitabu-word.png +0 -0
  56. data/examples/kitabu/output/images/kitabu-word.svg +0 -14
  57. data/examples/kitabu/output/images/kitabu.png +0 -0
  58. data/examples/kitabu/output/images/kitabu.svg +0 -20
  59. data/examples/kitabu/output/kitabu.epub +0 -0
  60. data/examples/kitabu/output/kitabu.html +0 -513
  61. data/examples/kitabu/output/kitabu.mobi +0 -0
  62. data/examples/kitabu/output/kitabu.pdf +0 -0
  63. data/examples/kitabu/output/kitabu.pdf.html +0 -729
  64. data/examples/kitabu/output/kitabu.print.html +0 -729
  65. data/examples/kitabu/output/kitabu.print.pdf +0 -0
  66. data/examples/kitabu/output/kitabu.txt +0 -440
  67. data/examples/kitabu/output/styles/epub.css +0 -437
  68. data/examples/kitabu/output/styles/html.css +0 -712
  69. data/examples/kitabu/output/styles/pdf.css +0 -840
  70. data/examples/kitabu/output/styles/print.css +0 -1278
  71. data/lib/kitabu/parser/html.rb +0 -208
  72. data/lib/kitabu/parser/pdf.rb +0 -88
@@ -1,208 +0,0 @@
1
- module Kitabu
2
- module Parser
3
- class HTML < Base
4
- # List of directories that should be skipped.
5
- #
6
- IGNORE_DIR = %w[. .. .svn]
7
-
8
- # Files that should be skipped.
9
- #
10
- IGNORE_FILES = /^(CHANGELOG|TOC)\..*?$/
11
-
12
- # List of recognized extensions.
13
- #
14
- EXTENSIONS = %w[md erb]
15
-
16
- class << self
17
- # The footnote index control. We have to manipulate footnotes
18
- # because each chapter starts from 1, so we have duplicated references.
19
- #
20
- attr_accessor :footnote_index
21
- end
22
-
23
- # Parse all files and save the parsed content
24
- # to <tt>output/book_name.html</tt>.
25
- #
26
- def parse
27
- reset_footnote_index!
28
- copy_images!
29
- export_stylesheets!
30
-
31
- File.open(root_dir.join("output/#{name}.html"), "w") do |file|
32
- file << parse_layout(content)
33
- end
34
-
35
- true
36
- rescue Exception => error
37
- handle_error(error)
38
- false
39
- end
40
-
41
- def reset_footnote_index!
42
- self.class.footnote_index = 1
43
- end
44
-
45
- # Return all chapters wrapped in a <tt>div.chapter</tt> tag.
46
- #
47
- def content
48
- String.new.tap do |chapters|
49
- entries.each do |entry|
50
- files = chapter_files(entry)
51
-
52
- # no markup files, so skip to the next one!
53
- next if files.empty?
54
-
55
- chapters << %[<div class="chapter">#{render_chapter(files)}</div>]
56
- end
57
- end
58
- end
59
-
60
- # Return a list of all recognized files.
61
- #
62
- def entries
63
- Dir.entries(source).sort.inject([]) do |buffer, entry|
64
- buffer << source.join(entry) if valid_entry?(entry)
65
- buffer
66
- end
67
- end
68
-
69
- private
70
- def chapter_files(entry)
71
- # Chapters can be files outside a directory.
72
- if File.file?(entry)
73
- [entry]
74
- else
75
- Dir["#{entry}/**/*.{#{EXTENSIONS.join(",")}}"].sort
76
- end
77
- end
78
-
79
- # Check if path is a valid entry.
80
- # Files/directories that start with a dot or underscore will be skipped.
81
- #
82
- def valid_entry?(entry)
83
- entry !~ /^(\.|_)/ && (valid_directory?(entry) || valid_file?(entry))
84
- end
85
-
86
- # Check if path is a valid directory.
87
- #
88
- def valid_directory?(entry)
89
- File.directory?(source.join(entry)) && !IGNORE_DIR.include?(File.basename(entry))
90
- end
91
-
92
- # Check if path is a valid file.
93
- #
94
- def valid_file?(entry)
95
- ext = File.extname(entry).gsub(/\./, "").downcase
96
- File.file?(source.join(entry)) && EXTENSIONS.include?(ext) && entry !~ IGNORE_FILES
97
- end
98
-
99
- # Render +file+ considering its extension.
100
- #
101
- def render_file(file)
102
- if format(file) == :erb
103
- content = render_template(file, config)
104
- else
105
- content = File.read(file)
106
- end
107
-
108
- content = Kitabu::Markdown.render(content)
109
- render_footnotes(content)
110
- end
111
-
112
- def render_footnotes(content)
113
- html = Nokogiri::HTML(content)
114
- footnotes = html.css("p[id^='fn']")
115
-
116
- return content if footnotes.empty?
117
-
118
- reset_footnote_index! unless self.class.footnote_index
119
-
120
- footnotes.each do |fn|
121
- index = self.class.footnote_index
122
- actual_index = fn["id"].gsub(/[^\d]/, "")
123
-
124
- fn.set_attribute("id", "_fn#{index}")
125
-
126
- html.css("a[href='#fn#{actual_index}']").each do |link|
127
- link.set_attribute("href", "#_fn#{index}")
128
- end
129
-
130
- html.css("a[href='#fnr#{actual_index}']").each do |link|
131
- link.set_attribute("href", "#_fnr#{index}")
132
- end
133
-
134
- html.css("[id=fnr#{actual_index}]").each do |tag|
135
- tag.set_attribute("id", "_fnr#{index}")
136
- end
137
-
138
- self.class.footnote_index += 1
139
- end
140
-
141
- html.css("body").inner_html
142
- end
143
-
144
- def format(file)
145
- if File.extname(file) == '.erb'
146
- :erb
147
- else
148
- :markdown
149
- end
150
- end
151
-
152
- # Parse layout file, making available all configuration entries.
153
- #
154
- def parse_layout(html)
155
- toc = TOC::HTML.generate(html)
156
- locals = config.merge({
157
- content: toc.content,
158
- toc: toc.to_html,
159
- changelog: render_changelog
160
- })
161
- render_template(root_dir.join("templates/html/layout.erb"), locals)
162
- end
163
-
164
- # Render changelog file.
165
- # This file can be used to inform any book change.
166
- #
167
- def render_changelog
168
- changelog = Dir[root_dir.join("text/CHANGELOG.*")].first
169
- render_file(changelog) if changelog
170
- end
171
-
172
- # Render all +files+ from a given chapter.
173
- #
174
- def render_chapter(files)
175
- String.new.tap do |chapter|
176
- files.each do |file|
177
- chapter << render_file(file) << "\n\n"
178
- end
179
- end
180
- end
181
-
182
- # Copy images
183
- #
184
- def copy_images!
185
- copy_directory("images", "output/images")
186
- end
187
-
188
- # Export all root stylesheets.
189
- #
190
- def export_stylesheets!
191
- files = Dir[root_dir.join("templates/styles/*.{scss,sass}").to_s]
192
- options = {
193
- style: :expanded,
194
- line_numbers: true,
195
- load_paths: [root_dir.join("templates/styles")]
196
- }
197
-
198
- files.each do |file|
199
- _, file_name, syntax = *File.basename(file).match(/(.*?)\.(.*?)$/)
200
- engine = Sass::Engine.new(File.read(file), options.merge(syntax: syntax.to_sym))
201
- target = root_dir.join("output/styles", "#{file_name}.css")
202
- FileUtils.mkdir_p(File.dirname(target))
203
- File.open(target, "w") {|io| io << engine.render }
204
- end
205
- end
206
- end
207
- end
208
- end
@@ -1,88 +0,0 @@
1
- module Kitabu
2
- module Parser
3
- class PDF < Base
4
- def parse
5
- apply_footnotes!
6
- spawn_command ["prince", html_for_pdf.to_s, "-o", pdf_file.to_s]
7
- spawn_command ["prince", html_for_print.to_s, "-o", print_file.to_s]
8
- end
9
-
10
- def apply_footnotes!
11
- html = Nokogiri::HTML(html_file.read)
12
- footnote_count = 1
13
-
14
- # https://github.com/sparklemotion/nokogiri/issues/339
15
- html.css("html").first.tap do |element|
16
- next unless element
17
- element.delete("xmlns")
18
- element.delete("xml:lang")
19
- end
20
-
21
- chapters = html.css(".chapter")
22
-
23
- chapters.each do |chapter|
24
- footnotes = chapter.css(".footnotes li")
25
-
26
- footnotes.each do |fn|
27
- # Get current footnote number
28
- footnote_number = footnote_count
29
- footnote_count += 1
30
-
31
- # Remove rev links
32
- fn.css('[rev=footnote]').map(&:remove)
33
-
34
- # Create an element for storing the footnote description
35
- fn_desc = Nokogiri::XML::Node.new('span', Nokogiri::HTML::DocumentFragment.parse(''))
36
- fn_desc.set_attribute 'class', 'fn-desc'
37
- fn_desc.inner_html = fn.css('p').map(&:inner_html).join("\n")
38
-
39
- # Find ref based on footnote's id
40
- fn_id = fn.get_attribute('id')
41
- ref = chapter.css("a[href='##{fn_id}']").first
42
-
43
- # Go up to parent and reformat it.
44
- sup = ref.parent
45
- sup.delete("id")
46
- sup.set_attribute 'class', 'fn-ref'
47
- sup.inner_html = ''
48
-
49
- # Finally, add the description after the <sup> tag.
50
- sup.after(fn_desc)
51
- end
52
-
53
- # Remove the footnotes element
54
- chapter.css('.footnotes').map(&:remove)
55
- end
56
-
57
- create_html_file(html_for_print, html, 'print')
58
- create_html_file(html_for_pdf, html, 'pdf')
59
- end
60
-
61
- def create_html_file(target, html, class_name)
62
- html.css("html").first.set_attribute "class", class_name
63
- html.css("link[name=stylesheet]").first.set_attribute "href", "styles/#{class_name}.css"
64
- File.open(target, "w") {|f| f << html.to_html }
65
- end
66
-
67
- def html_for_pdf
68
- root_dir.join("output/#{name}.pdf.html")
69
- end
70
-
71
- def html_for_print
72
- root_dir.join("output/#{name}.print.html")
73
- end
74
-
75
- def html_file
76
- root_dir.join("output/#{name}.html")
77
- end
78
-
79
- def pdf_file
80
- root_dir.join("output/#{name}.pdf")
81
- end
82
-
83
- def print_file
84
- root_dir.join("output/#{name}.print.pdf")
85
- end
86
- end
87
- end
88
- end