epuber 0.3.5

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 (206) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +6 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +46 -0
  5. data/bin/epuber +10 -0
  6. data/epuber.gemspec +51 -0
  7. data/lib/epuber.rb +14 -0
  8. data/lib/epuber/book.rb +267 -0
  9. data/lib/epuber/book/contributor.rb +90 -0
  10. data/lib/epuber/book/file_request.rb +68 -0
  11. data/lib/epuber/book/target.rb +284 -0
  12. data/lib/epuber/book/toc_item.rb +130 -0
  13. data/lib/epuber/checker.rb +19 -0
  14. data/lib/epuber/checker/text_checker.rb +129 -0
  15. data/lib/epuber/checker_transformer_base.rb +71 -0
  16. data/lib/epuber/command.rb +65 -0
  17. data/lib/epuber/command/compile.rb +142 -0
  18. data/lib/epuber/command/init.rb +145 -0
  19. data/lib/epuber/command/server.rb +59 -0
  20. data/lib/epuber/compiler.rb +255 -0
  21. data/lib/epuber/compiler/compilation_context.rb +86 -0
  22. data/lib/epuber/compiler/file_finders/abstract.rb +270 -0
  23. data/lib/epuber/compiler/file_finders/imaginary.rb +167 -0
  24. data/lib/epuber/compiler/file_finders/normal.rb +22 -0
  25. data/lib/epuber/compiler/file_resolver.rb +316 -0
  26. data/lib/epuber/compiler/file_types/abstract_file.rb +119 -0
  27. data/lib/epuber/compiler/file_types/bade_file.rb +39 -0
  28. data/lib/epuber/compiler/file_types/container_xml_file.rb +26 -0
  29. data/lib/epuber/compiler/file_types/generated_file.rb +29 -0
  30. data/lib/epuber/compiler/file_types/ibooks_display_options_file.rb +31 -0
  31. data/lib/epuber/compiler/file_types/image_file.rb +42 -0
  32. data/lib/epuber/compiler/file_types/mime_type_file.rb +20 -0
  33. data/lib/epuber/compiler/file_types/nav_file.rb +42 -0
  34. data/lib/epuber/compiler/file_types/opf_file.rb +27 -0
  35. data/lib/epuber/compiler/file_types/source_file.rb +52 -0
  36. data/lib/epuber/compiler/file_types/static_file.rb +18 -0
  37. data/lib/epuber/compiler/file_types/stylus_file.rb +20 -0
  38. data/lib/epuber/compiler/file_types/xhtml_file.rb +102 -0
  39. data/lib/epuber/compiler/generator.rb +67 -0
  40. data/lib/epuber/compiler/meta_inf_generator.rb +41 -0
  41. data/lib/epuber/compiler/nav_generator.rb +201 -0
  42. data/lib/epuber/compiler/opf_generator.rb +284 -0
  43. data/lib/epuber/compiler/xhtml_processor.rb +254 -0
  44. data/lib/epuber/config.rb +133 -0
  45. data/lib/epuber/dsl/attribute.rb +248 -0
  46. data/lib/epuber/dsl/attribute_support.rb +130 -0
  47. data/lib/epuber/dsl/object.rb +145 -0
  48. data/lib/epuber/dsl/tree_object.rb +101 -0
  49. data/lib/epuber/helper.rb +19 -0
  50. data/lib/epuber/lockfile.rb +55 -0
  51. data/lib/epuber/plugin.rb +108 -0
  52. data/lib/epuber/ruby_extensions/match_data.rb +40 -0
  53. data/lib/epuber/ruby_extensions/thread.rb +13 -0
  54. data/lib/epuber/server.rb +614 -0
  55. data/lib/epuber/server/auto_refresh/auto_refresh.coffee +97 -0
  56. data/lib/epuber/server/auto_refresh/connector.coffee +125 -0
  57. data/lib/epuber/server/auto_refresh/protocol.coffee +41 -0
  58. data/lib/epuber/server/auto_refresh/reloader.coffee +261 -0
  59. data/lib/epuber/server/base.styl +3 -0
  60. data/lib/epuber/server/basic.styl +211 -0
  61. data/lib/epuber/server/book_content.styl +39 -0
  62. data/lib/epuber/server/default_cover.png +0 -0
  63. data/lib/epuber/server/fonts/AvenirNext/AvenirNext-Bold.ttf +0 -0
  64. data/lib/epuber/server/fonts/AvenirNext/AvenirNext-BoldItalic.ttf +0 -0
  65. data/lib/epuber/server/fonts/AvenirNext/AvenirNext-Italic.ttf +0 -0
  66. data/lib/epuber/server/fonts/AvenirNext/AvenirNext-Regular.ttf +0 -0
  67. data/lib/epuber/server/handlers.rb +67 -0
  68. data/lib/epuber/server/keyboard_control.coffee +6 -0
  69. data/lib/epuber/server/pages/book.bade +75 -0
  70. data/lib/epuber/server/pages/common.bade +59 -0
  71. data/lib/epuber/server/pages/files.bade +17 -0
  72. data/lib/epuber/server/pages/toc.bade +29 -0
  73. data/lib/epuber/server/support.coffee +10 -0
  74. data/lib/epuber/templates/template.bookspec +143 -0
  75. data/lib/epuber/third_party/bower.rb +22 -0
  76. data/lib/epuber/third_party/bower/bower.json +10 -0
  77. data/lib/epuber/third_party/bower/bower_components/cookies-js/bower.json +10 -0
  78. data/lib/epuber/third_party/bower/bower_components/cookies-js/dist/cookies.d.ts +33 -0
  79. data/lib/epuber/third_party/bower/bower_components/cookies-js/dist/cookies.js +173 -0
  80. data/lib/epuber/third_party/bower/bower_components/cookies-js/dist/cookies.min.js +6 -0
  81. data/lib/epuber/third_party/bower/bower_components/jquery/MIT-LICENSE.txt +21 -0
  82. data/lib/epuber/third_party/bower/bower_components/jquery/bower.json +28 -0
  83. data/lib/epuber/third_party/bower/bower_components/jquery/dist/jquery.js +9210 -0
  84. data/lib/epuber/third_party/bower/bower_components/jquery/dist/jquery.min.js +5 -0
  85. data/lib/epuber/third_party/bower/bower_components/jquery/dist/jquery.min.map +1 -0
  86. data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax.js +786 -0
  87. data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/jsonp.js +89 -0
  88. data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/load.js +75 -0
  89. data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/parseJSON.js +13 -0
  90. data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/parseXML.js +28 -0
  91. data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/script.js +64 -0
  92. data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/var/nonce.js +5 -0
  93. data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/var/rquery.js +3 -0
  94. data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/xhr.js +136 -0
  95. data/lib/epuber/third_party/bower/bower_components/jquery/src/attributes.js +11 -0
  96. data/lib/epuber/third_party/bower/bower_components/jquery/src/attributes/attr.js +141 -0
  97. data/lib/epuber/third_party/bower/bower_components/jquery/src/attributes/classes.js +158 -0
  98. data/lib/epuber/third_party/bower/bower_components/jquery/src/attributes/prop.js +94 -0
  99. data/lib/epuber/third_party/bower/bower_components/jquery/src/attributes/support.js +35 -0
  100. data/lib/epuber/third_party/bower/bower_components/jquery/src/attributes/val.js +161 -0
  101. data/lib/epuber/third_party/bower/bower_components/jquery/src/callbacks.js +205 -0
  102. data/lib/epuber/third_party/bower/bower_components/jquery/src/core.js +502 -0
  103. data/lib/epuber/third_party/bower/bower_components/jquery/src/core/access.js +60 -0
  104. data/lib/epuber/third_party/bower/bower_components/jquery/src/core/init.js +123 -0
  105. data/lib/epuber/third_party/bower/bower_components/jquery/src/core/parseHTML.js +39 -0
  106. data/lib/epuber/third_party/bower/bower_components/jquery/src/core/ready.js +97 -0
  107. data/lib/epuber/third_party/bower/bower_components/jquery/src/core/var/rsingleTag.js +4 -0
  108. data/lib/epuber/third_party/bower/bower_components/jquery/src/css.js +450 -0
  109. data/lib/epuber/third_party/bower/bower_components/jquery/src/css/addGetHookIf.js +22 -0
  110. data/lib/epuber/third_party/bower/bower_components/jquery/src/css/curCSS.js +57 -0
  111. data/lib/epuber/third_party/bower/bower_components/jquery/src/css/defaultDisplay.js +70 -0
  112. data/lib/epuber/third_party/bower/bower_components/jquery/src/css/hiddenVisibleSelectors.js +15 -0
  113. data/lib/epuber/third_party/bower/bower_components/jquery/src/css/support.js +96 -0
  114. data/lib/epuber/third_party/bower/bower_components/jquery/src/css/swap.js +28 -0
  115. data/lib/epuber/third_party/bower/bower_components/jquery/src/css/var/cssExpand.js +3 -0
  116. data/lib/epuber/third_party/bower/bower_components/jquery/src/css/var/getStyles.js +12 -0
  117. data/lib/epuber/third_party/bower/bower_components/jquery/src/css/var/isHidden.js +13 -0
  118. data/lib/epuber/third_party/bower/bower_components/jquery/src/css/var/rmargin.js +3 -0
  119. data/lib/epuber/third_party/bower/bower_components/jquery/src/css/var/rnumnonpx.js +5 -0
  120. data/lib/epuber/third_party/bower/bower_components/jquery/src/data.js +178 -0
  121. data/lib/epuber/third_party/bower/bower_components/jquery/src/data/Data.js +181 -0
  122. data/lib/epuber/third_party/bower/bower_components/jquery/src/data/accepts.js +20 -0
  123. data/lib/epuber/third_party/bower/bower_components/jquery/src/data/var/data_priv.js +5 -0
  124. data/lib/epuber/third_party/bower/bower_components/jquery/src/data/var/data_user.js +5 -0
  125. data/lib/epuber/third_party/bower/bower_components/jquery/src/deferred.js +149 -0
  126. data/lib/epuber/third_party/bower/bower_components/jquery/src/deprecated.js +13 -0
  127. data/lib/epuber/third_party/bower/bower_components/jquery/src/dimensions.js +50 -0
  128. data/lib/epuber/third_party/bower/bower_components/jquery/src/effects.js +648 -0
  129. data/lib/epuber/third_party/bower/bower_components/jquery/src/effects/Tween.js +114 -0
  130. data/lib/epuber/third_party/bower/bower_components/jquery/src/effects/animatedSelector.js +13 -0
  131. data/lib/epuber/third_party/bower/bower_components/jquery/src/event.js +868 -0
  132. data/lib/epuber/third_party/bower/bower_components/jquery/src/event/ajax.js +13 -0
  133. data/lib/epuber/third_party/bower/bower_components/jquery/src/event/alias.js +39 -0
  134. data/lib/epuber/third_party/bower/bower_components/jquery/src/event/support.js +9 -0
  135. data/lib/epuber/third_party/bower/bower_components/jquery/src/exports/amd.js +24 -0
  136. data/lib/epuber/third_party/bower/bower_components/jquery/src/exports/global.js +32 -0
  137. data/lib/epuber/third_party/bower/bower_components/jquery/src/intro.js +44 -0
  138. data/lib/epuber/third_party/bower/bower_components/jquery/src/jquery.js +37 -0
  139. data/lib/epuber/third_party/bower/bower_components/jquery/src/manipulation.js +580 -0
  140. data/lib/epuber/third_party/bower/bower_components/jquery/src/manipulation/_evalUrl.js +18 -0
  141. data/lib/epuber/third_party/bower/bower_components/jquery/src/manipulation/support.js +32 -0
  142. data/lib/epuber/third_party/bower/bower_components/jquery/src/manipulation/var/rcheckableType.js +3 -0
  143. data/lib/epuber/third_party/bower/bower_components/jquery/src/offset.js +207 -0
  144. data/lib/epuber/third_party/bower/bower_components/jquery/src/outro.js +1 -0
  145. data/lib/epuber/third_party/bower/bower_components/jquery/src/queue.js +142 -0
  146. data/lib/epuber/third_party/bower/bower_components/jquery/src/queue/delay.js +22 -0
  147. data/lib/epuber/third_party/bower/bower_components/jquery/src/selector-native.js +172 -0
  148. data/lib/epuber/third_party/bower/bower_components/jquery/src/selector-sizzle.js +14 -0
  149. data/lib/epuber/third_party/bower/bower_components/jquery/src/selector.js +1 -0
  150. data/lib/epuber/third_party/bower/bower_components/jquery/src/serialize.js +111 -0
  151. data/lib/epuber/third_party/bower/bower_components/jquery/src/sizzle/dist/sizzle.js +2067 -0
  152. data/lib/epuber/third_party/bower/bower_components/jquery/src/sizzle/dist/sizzle.min.js +3 -0
  153. data/lib/epuber/third_party/bower/bower_components/jquery/src/sizzle/dist/sizzle.min.map +1 -0
  154. data/lib/epuber/third_party/bower/bower_components/jquery/src/traversing.js +199 -0
  155. data/lib/epuber/third_party/bower/bower_components/jquery/src/traversing/findFilter.js +100 -0
  156. data/lib/epuber/third_party/bower/bower_components/jquery/src/traversing/var/rneedsContext.js +6 -0
  157. data/lib/epuber/third_party/bower/bower_components/jquery/src/var/arr.js +3 -0
  158. data/lib/epuber/third_party/bower/bower_components/jquery/src/var/class2type.js +4 -0
  159. data/lib/epuber/third_party/bower/bower_components/jquery/src/var/concat.js +5 -0
  160. data/lib/epuber/third_party/bower/bower_components/jquery/src/var/hasOwn.js +5 -0
  161. data/lib/epuber/third_party/bower/bower_components/jquery/src/var/indexOf.js +5 -0
  162. data/lib/epuber/third_party/bower/bower_components/jquery/src/var/pnum.js +3 -0
  163. data/lib/epuber/third_party/bower/bower_components/jquery/src/var/push.js +5 -0
  164. data/lib/epuber/third_party/bower/bower_components/jquery/src/var/rnotwhite.js +3 -0
  165. data/lib/epuber/third_party/bower/bower_components/jquery/src/var/slice.js +5 -0
  166. data/lib/epuber/third_party/bower/bower_components/jquery/src/var/strundefined.js +3 -0
  167. data/lib/epuber/third_party/bower/bower_components/jquery/src/var/support.js +4 -0
  168. data/lib/epuber/third_party/bower/bower_components/jquery/src/var/toString.js +5 -0
  169. data/lib/epuber/third_party/bower/bower_components/jquery/src/wrap.js +79 -0
  170. data/lib/epuber/third_party/bower/bower_components/keymaster/MIT-LICENSE +20 -0
  171. data/lib/epuber/third_party/bower/bower_components/keymaster/Makefile +4 -0
  172. data/lib/epuber/third_party/bower/bower_components/keymaster/README.markdown +212 -0
  173. data/lib/epuber/third_party/bower/bower_components/keymaster/bower.json +27 -0
  174. data/lib/epuber/third_party/bower/bower_components/keymaster/keymaster.js +296 -0
  175. data/lib/epuber/third_party/bower/bower_components/keymaster/package.json +11 -0
  176. data/lib/epuber/third_party/bower/bower_components/keymaster/test.html +93 -0
  177. data/lib/epuber/third_party/bower/bower_components/spin.js/LICENSE.txt +21 -0
  178. data/lib/epuber/third_party/bower/bower_components/spin.js/README.md +21 -0
  179. data/lib/epuber/third_party/bower/bower_components/spin.js/bower.json +18 -0
  180. data/lib/epuber/third_party/bower/bower_components/spin.js/jquery.spin.js +80 -0
  181. data/lib/epuber/third_party/bower/bower_components/spin.js/spin.js +337 -0
  182. data/lib/epuber/third_party/bower/bower_components/uri.js/LICENSE.txt +21 -0
  183. data/lib/epuber/third_party/bower/bower_components/uri.js/README.md +534 -0
  184. data/lib/epuber/third_party/bower/bower_components/uri.js/bower.json +16 -0
  185. data/lib/epuber/third_party/bower/bower_components/uri.js/contributing.md +19 -0
  186. data/lib/epuber/third_party/bower/bower_components/uri.js/src/IPv6.js +188 -0
  187. data/lib/epuber/third_party/bower/bower_components/uri.js/src/SecondLevelDomains.js +241 -0
  188. data/lib/epuber/third_party/bower/bower_components/uri.js/src/URI.fragmentQuery.js +104 -0
  189. data/lib/epuber/third_party/bower/bower_components/uri.js/src/URI.fragmentURI.js +97 -0
  190. data/lib/epuber/third_party/bower/bower_components/uri.js/src/URI.js +2115 -0
  191. data/lib/epuber/third_party/bower/bower_components/uri.js/src/URI.min.js +86 -0
  192. data/lib/epuber/third_party/bower/bower_components/uri.js/src/URITemplate.js +499 -0
  193. data/lib/epuber/third_party/bower/bower_components/uri.js/src/jquery.URI.js +235 -0
  194. data/lib/epuber/third_party/bower/bower_components/uri.js/src/jquery.URI.min.js +7 -0
  195. data/lib/epuber/third_party/bower/bower_components/uri.js/src/punycode.js +508 -0
  196. data/lib/epuber/transformer.rb +19 -0
  197. data/lib/epuber/transformer/text_transformer.rb +60 -0
  198. data/lib/epuber/user_interface.rb +186 -0
  199. data/lib/epuber/vendor/globals_context.rb +26 -0
  200. data/lib/epuber/vendor/hash_binding.rb +26 -0
  201. data/lib/epuber/vendor/nokogiri_extensions.rb +30 -0
  202. data/lib/epuber/vendor/ruby_templater.rb +71 -0
  203. data/lib/epuber/vendor/size.rb +20 -0
  204. data/lib/epuber/vendor/version.rb +83 -0
  205. data/lib/epuber/version.rb +4 -0
  206. metadata +556 -0
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+
3
+ require 'stylus'
4
+
5
+
6
+ module Epuber
7
+ class Compiler
8
+ module FileTypes
9
+ require_relative 'source_file'
10
+
11
+ class StylusFile < SourceFile
12
+ # @param [Compiler::CompilationContext] compilation_context
13
+ #
14
+ def process(compilation_context)
15
+ write_compiled(Stylus.compile(File.new(abs_source_path)))
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,102 @@
1
+ # encoding: utf-8
2
+
3
+
4
+ module Epuber
5
+ class Compiler
6
+ require_relative '../xhtml_processor'
7
+
8
+ module FileTypes
9
+ require_relative 'source_file'
10
+
11
+ class XHTMLFile < SourceFile
12
+
13
+ # @return [Epuber::Book::TocItem]
14
+ #
15
+ attr_accessor :toc_item
16
+
17
+ # @param [Book::Target] target
18
+ # @param [FileResolver] file_resolver
19
+ #
20
+ # @return [Array<String>] list of paths to styles relative to this file
21
+ #
22
+ def default_styles(target, file_resolver)
23
+ default_styles = target.default_styles.map do |default_style_request|
24
+ Array(file_resolver.file_from_request(default_style_request))
25
+ end.flatten
26
+
27
+ default_styles.map do |style|
28
+ Pathname.new(style.final_destination_path).relative_path_from(Pathname.new(File.dirname(final_destination_path))).to_s
29
+ end
30
+ end
31
+
32
+ # @param [Compiler::CompilationContext] compilation_context
33
+ #
34
+ def load_source(compilation_context)
35
+ xhtml_content = File.read(abs_source_path)
36
+
37
+ compilation_context.perform_plugin_things(Transformer, :source_text_file) do |transformer|
38
+ xhtml_content = transformer.call(abs_source_path, xhtml_content, compilation_context)
39
+ end
40
+
41
+ # perform custom validation
42
+ if compilation_context.should_check
43
+ compilation_context.perform_plugin_things(Checker, :source_text_file) do |checker|
44
+ checker.call(abs_source_path, xhtml_content, compilation_context)
45
+ end
46
+ end
47
+
48
+
49
+ if compilation_context.should_write
50
+ self.class.write_to_file(xhtml_content, abs_source_path)
51
+ end
52
+
53
+ xhtml_content
54
+ end
55
+
56
+ # @param [String] content xhtml in string
57
+ # @param [Compiler::CompilationContext] compilation_context
58
+ #
59
+ # @return [String] new xhtml string
60
+ #
61
+ def common_process(content, compilation_context)
62
+ target = compilation_context.target
63
+ book = compilation_context.book
64
+ file_resolver = compilation_context.file_resolver
65
+
66
+ xhtml_doc = XHTMLProcessor.xml_document_from_string(content, source_path)
67
+ XHTMLProcessor.add_missing_root_elements(xhtml_doc, book.title, target.epub_version)
68
+
69
+ XHTMLProcessor.add_styles(xhtml_doc, default_styles(target, file_resolver))
70
+
71
+ XHTMLProcessor.add_viewport(xhtml_doc, target.default_viewport) unless target.default_viewport.nil?
72
+ self.properties << :scripted if XHTMLProcessor.using_javascript?(xhtml_doc)
73
+
74
+ XHTMLProcessor.resolve_links(xhtml_doc, destination_path, file_resolver.dest_finder)
75
+ XHTMLProcessor.resolve_images(xhtml_doc, destination_path, file_resolver)
76
+
77
+ xhtml_string = xhtml_doc.to_s
78
+
79
+ # perform transformations
80
+ compilation_context.perform_plugin_things(Transformer, :result_text_xhtml_string) do |transformer|
81
+ xhtml_string = transformer.call(final_destination_path, xhtml_string, compilation_context)
82
+ end
83
+
84
+ # perform custom validation
85
+ if compilation_context.should_check
86
+ compilation_context.perform_plugin_things(Checker, :result_text_xhtml_string) do |checker|
87
+ checker.call(final_destination_path, xhtml_string, compilation_context)
88
+ end
89
+ end
90
+
91
+ xhtml_string
92
+ end
93
+
94
+ # @param [Compiler::CompilationContext]
95
+ #
96
+ def process(compilation_context)
97
+ write_processed(common_process(load_source(compilation_context), compilation_context))
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,67 @@
1
+ # encoding: utf-8
2
+
3
+ require 'nokogiri'
4
+
5
+ module Nokogiri
6
+ module XML
7
+ class Builder
8
+ class NodeBuilder
9
+ attr_reader :node
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+
16
+ module Epuber
17
+ class Compiler
18
+ class Generator
19
+ # @return [Epuber::Compiler::CompilationContext]
20
+ #
21
+ attr_reader :compilation_context
22
+
23
+ # @param [Epuber::Compiler::CompilationContext] compilation_context
24
+ #
25
+ def initialize(compilation_context)
26
+ @compilation_context = compilation_context
27
+ @book = compilation_context.book
28
+ @target = compilation_context.target
29
+ @file_resolver = compilation_context.file_resolver
30
+ end
31
+
32
+ protected
33
+
34
+ def pretty_path_for_toc_item(toc_item, fragment: true)
35
+ file = @file_resolver.file_from_request(toc_item.file_request)
36
+ [file.destination_path, fragment ? toc_item.file_fragment : nil].compact.join('#')
37
+ end
38
+
39
+ # @param [Epuber::Compiler::FileTypes::AbstractFile] file
40
+ #
41
+ # @return [String]
42
+ #
43
+ def pretty_path(file)
44
+ file.destination_path
45
+ end
46
+
47
+ # Helper function for generating XML
48
+ #
49
+ # @yields xml_builder
50
+ # @yieldsparam [Nokogiri::XML::Builder] xml_builder
51
+ #
52
+ # @return [Nokogiri::XML::Document]
53
+ #
54
+ def generate_xml(&block)
55
+ builder = Nokogiri::XML::Builder.new(encoding: 'utf-8') do |xml|
56
+ @xml = xml
57
+
58
+ block.call(xml) unless block.nil?
59
+
60
+ @xml = nil
61
+ end
62
+
63
+ builder.doc
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+
3
+ require 'mime-types'
4
+
5
+ require_relative 'generator'
6
+ require_relative '../book/file_request'
7
+
8
+
9
+ module Epuber
10
+ class Compiler
11
+ class MetaInfGenerator < Generator
12
+ # @return [Nokogiri::XML::Document]
13
+ #
14
+ def generate_container_xml
15
+ generate_xml do |xml|
16
+ xml.container(version: 1.0, xmlns: 'urn:oasis:names:tc:opendocument:xmlns:container') do
17
+ xml.rootfiles do
18
+ @file_resolver.package_files.select { |file| file.kind_of?(FileTypes::OPFFile) }.each do |file|
19
+ path = file.pkg_destination_path
20
+ xml.rootfile('full-path' => path, 'media-type' => MIME::Types.of(path).first.content_type)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ # @return nil
28
+ #
29
+ def generate_ibooks_display_options_xml
30
+ generate_xml do |xml|
31
+ xml.display_options do
32
+ xml.platform(name: '*') do
33
+ xml.option(true.to_s, name: 'specified-fonts') if @target.custom_fonts
34
+ xml.option(true.to_s, name: 'fixed-layout') if @target.fixed_layout
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,201 @@
1
+ # encoding: utf-8
2
+
3
+ require_relative '../book/toc_item'
4
+
5
+
6
+ module Epuber
7
+ class Compiler
8
+ require_relative 'file_resolver'
9
+ require_relative 'generator'
10
+
11
+ class NavGenerator < Generator
12
+
13
+ NCX_NAMESPACES = {
14
+ 'xmlns' => 'http://www.daisy.org/z3986/2005/ncx/',
15
+ }.freeze
16
+
17
+ XHTML_NAMESPACES = {
18
+ 'xmlns' => 'http://www.w3.org/1999/xhtml',
19
+ 'xmlns:epub' => 'http://www.idpf.org/2007/ops',
20
+ }.freeze
21
+
22
+ XHTML_IBOOKS_NAMESPACES = {
23
+ 'xmlns:ibooks' => 'http://vocabulary.itunes.apple.com/rdf/ibooks/vocabulary-extensions-1.0',
24
+ 'epub:prefix' => 'ibooks: http://vocabulary.itunes.apple.com/rdf/ibooks/vocabulary-extensions-1.0',
25
+ }.freeze
26
+
27
+ # resource page http://www.idpf.org/epub/301/spec/epub-contentdocs.html#sec-xhtml-nav-def-types-landmarks
28
+ LANDMARKS_MAP = {
29
+ landmark_cover: { type: 'cover', text: 'Cover page' },
30
+ landmark_start_page: { type: %w(bodymatter ibooks:reader-start-page), text: 'Start Reading' },
31
+ landmark_copyright: { type: 'copyright-page', text: 'Copyright page' },
32
+ landmark_toc: { type: 'toc', text: 'Table of contents' },
33
+ }.freeze
34
+
35
+ # Generates XML for toc document, the structure differs depend on epub_version
36
+ #
37
+ # Use method #to_s to generate nice string from XML document
38
+ #
39
+ # @return [Nokogiri::XML::Document]
40
+ #
41
+ def generate_nav
42
+ generate_xml do
43
+ if @target.epub_version >= 3
44
+ generate_xhtml_content
45
+ else
46
+ generate_ncx_content
47
+ end
48
+ end
49
+ end
50
+
51
+
52
+ private
53
+
54
+ # @return [Hash<String, String>]
55
+ #
56
+ def nav_namespaces
57
+ if @target.epub_version >= 3
58
+ dict = XHTML_NAMESPACES
59
+ dict = dict.merge(XHTML_IBOOKS_NAMESPACES) if @target.ibooks?
60
+ dict
61
+ else
62
+ NCX_NAMESPACES
63
+ end
64
+ end
65
+
66
+ # @return nil
67
+ #
68
+ def generate_xhtml_content
69
+ @xml.doc.create_internal_subset('html', nil, nil)
70
+ @xml.html(nav_namespaces) do
71
+ @xml.head do
72
+ @xml.title_(@book.title)
73
+ end
74
+ @xml.body do
75
+ # toc
76
+ @xml.nav('epub:type' => 'toc') do
77
+ visit_toc_items(@target.root_toc.sub_items)
78
+ end
79
+
80
+ # landmarks
81
+ @xml.nav('epub:type' => 'landmarks') do
82
+ @xml.ol do
83
+ landmarks_visit_toc_item(@target.root_toc)
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ def generate_ncx_content
91
+ @xml.doc.create_internal_subset('ncx',
92
+ '-//NISO//DTD ncx 2005-1//EN',
93
+ 'http://www.daisy.org/z3986/2005/ncx-2005-1.dtd')
94
+
95
+ @xml.ncx(nav_namespaces, version: '2005-1') do
96
+ # head
97
+ @xml.head do
98
+ @xml.meta(name: 'dtb:uid', content: (@target.identifier || "urn:isbn:#{@target.isbn}"))
99
+ end
100
+
101
+ # title
102
+ @xml.docTitle do
103
+ @xml.text_(@book.title)
104
+ end
105
+
106
+ @nav_nav_point_id = 1
107
+
108
+ # nav map
109
+ @xml.navMap do
110
+ visit_toc_items(@target.root_toc.sub_items)
111
+ end
112
+ end
113
+ end
114
+
115
+ # @param toc_items [Array<Epuber::Book::TocItem>]
116
+ #
117
+ def visit_toc_items(toc_items)
118
+ iterate_lambda = lambda do
119
+ toc_items.each do |child_item|
120
+ visit_toc_item(child_item)
121
+ end
122
+ end
123
+
124
+ if @target.epub_version >= 3 && toc_items.length > 0
125
+ @xml.ol do
126
+ iterate_lambda.call
127
+ end
128
+ else
129
+ iterate_lambda.call
130
+ end
131
+ end
132
+
133
+ # @param toc_item [Epuber::Book::TocItem]
134
+ #
135
+ def visit_toc_item(toc_item)
136
+ result_file_path = pretty_path_for_toc_item(toc_item)
137
+
138
+ if toc_item.title.nil?
139
+ visit_toc_items(toc_item.sub_items)
140
+ elsif @target.epub_version >= 3
141
+ @xml.li do
142
+ node_builder = @xml.a(href: result_file_path)
143
+ node_builder.node.inner_html = toc_item.title
144
+
145
+ visit_toc_items(toc_item.sub_items)
146
+ end
147
+ else
148
+ @xml.navPoint(id: "navPoint_#{@nav_nav_point_id}") do
149
+ @xml.navLabel do
150
+ node_builder = @xml.text_
151
+ node_builder.node.inner_html = toc_item.title
152
+ end
153
+ @xml.content(src: result_file_path)
154
+
155
+ @nav_nav_point_id += 1
156
+
157
+ visit_toc_items(toc_item.sub_items)
158
+ end
159
+ end
160
+ end
161
+
162
+
163
+ # --------------- landmarks -----------------------------
164
+
165
+ # @param toc_items [Array<Epuber::Book::TocItem>]
166
+ #
167
+ def landmarks_visit_toc_items(toc_items)
168
+ toc_items.each do |child_item|
169
+ landmarks_visit_toc_item(child_item)
170
+ end
171
+ end
172
+
173
+ # @param toc_item [Epuber::Book::TocItem]
174
+ #
175
+ def landmarks_visit_toc_item(toc_item)
176
+ landmarks = toc_item.landmarks
177
+ landmarks.each do |landmark|
178
+ dict = LANDMARKS_MAP[landmark]
179
+
180
+ raise "Unknown landmark `#{landmark.inspect}`, supported are #{LANDMARKS_MAP.keys}" if dict.nil?
181
+
182
+ types = Array(dict[:type])
183
+
184
+
185
+ # filter out ibooks specific when the target is not ibooks
186
+ types = types.reject { |type| type.to_s.start_with?('ibooks:') } unless @target.ibooks?
187
+
188
+ result_file_path = pretty_path_for_toc_item(toc_item, fragment: false)
189
+
190
+ types.each do |type|
191
+ @xml.li do
192
+ @xml.a(dict[:text], 'epub:type' => type, 'href' => result_file_path)
193
+ end
194
+ end
195
+ end
196
+
197
+ landmarks_visit_toc_items(toc_item.sub_items)
198
+ end
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,284 @@
1
+ # encoding: utf-8
2
+
3
+ require 'time'
4
+
5
+ require 'mime-types'
6
+
7
+ require_relative 'generator'
8
+ require_relative '../book/toc_item'
9
+
10
+
11
+ module Epuber
12
+ class Compiler
13
+ class OPFGenerator < Generator
14
+
15
+ EPUB2_NAMESPACES = {
16
+ 'xmlns' => 'http://www.idpf.org/2007/opf',
17
+ 'xmlns:dc' => 'http://purl.org/dc/elements/1.1/',
18
+ 'xmlns:opf' => 'http://www.idpf.org/2007/opf',
19
+ }.freeze
20
+
21
+ EPUB3_NAMESPACES = {
22
+ 'prefix' => 'rendition: http://www.idpf.org/vocab/rendition/',
23
+ 'xmlns:epub' => 'http://www.idpf.org/2007/ops',
24
+ }.freeze
25
+
26
+ IBOOKS_NAMESPACES = {
27
+ 'prefix' => 'rendition: http://www.idpf.org/vocab/rendition/# ibooks: http://vocabulary.itunes.apple.com/rdf/ibooks/vocabulary-extensions-1.0/',
28
+ 'xmlns:ibooks' => 'http://apple.com/ibooks/html-extensions',
29
+ }.freeze
30
+
31
+ # resource page http://www.idpf.org/epub/20/spec/OPF_2.0.1_draft.htm#TOC2.6
32
+ LANDMARKS_MAP = {
33
+
34
+ # my favorite
35
+ landmark_cover: 'cover',
36
+ landmark_start_page: 'text',
37
+ landmark_copyright: 'copyright-page',
38
+ landmark_toc: 'toc',
39
+
40
+ # others
41
+ landmark_title: 'title-page',
42
+ landmark_index: 'index',
43
+ landmark_glossary: 'glossary',
44
+ landmark_acknowledgements: 'acknowledgements',
45
+ landmark_bibliography: 'bibliography',
46
+ landmark_colophon: 'colophon',
47
+ landmark_dedication: 'dedication',
48
+ landmark_epigraph: 'epigraph',
49
+ landmark_foreword: 'foreword',
50
+ landmark_list_of_illustrations: 'loi',
51
+ landmark_list_of_tables: 'lot',
52
+ landmark_notes: 'notes',
53
+ landmark_preface: 'preface',
54
+
55
+ }.freeze
56
+
57
+ PROPERTIES_MAP = {
58
+ cover_image: 'cover-image',
59
+ navigation: 'nav',
60
+ scripted: 'scripted',
61
+ }.freeze
62
+
63
+ OPF_UNIQUE_ID = 'bookid'
64
+
65
+ # Generates XML for opf document
66
+ #
67
+ # Use method #to_s to generate nice string from XML document
68
+ #
69
+ # @return [Nokogiri::XML::Document]
70
+ #
71
+ def generate_opf
72
+ generate_xml do |xml|
73
+ xml.package(package_namespaces, :version => @target.epub_version, 'unique-identifier' => OPF_UNIQUE_ID) do
74
+ generate_metadata
75
+ generate_manifest
76
+ generate_spine
77
+ generate_guide
78
+ end
79
+ end
80
+ end
81
+
82
+ private
83
+
84
+ # --------- METADATA --------------------------
85
+
86
+ # @return nil
87
+ #
88
+ def generate_metadata
89
+ epub_version = @target.epub_version
90
+
91
+ @xml.metadata do
92
+ # identifier
93
+ identifier = @target.identifier || "urn:isbn:#{@target.isbn}"
94
+ @xml['dc'].identifier(identifier, id: OPF_UNIQUE_ID)
95
+
96
+ # title
97
+ if @book.title
98
+ if epub_version >= 3
99
+ @xml['dc'].title(@book.title, id: 'title')
100
+ @xml.meta('main', refines: '#title', property: 'title-type')
101
+ else
102
+ @xml['dc'].title(@book.title)
103
+ end
104
+ end
105
+
106
+ # authors
107
+ authors = @book.authors || []
108
+ authors.each_with_index do |author, idx|
109
+ author_id = "author_#{idx + 1}"
110
+ author_id_ref = "##{author_id}"
111
+
112
+ if epub_version >= 3
113
+ @xml['dc'].creator(author.pretty_name, id: author_id)
114
+ @xml.meta(author.file_as, property: 'file-as', refines: author_id_ref)
115
+ @xml.meta(author.role, property: 'role', refines: author_id_ref, scheme: 'marc:relators')
116
+ else
117
+ @xml['dc'].creator(author.pretty_name, 'opf:file-as' => author.file_as, 'opf:role' => author.role)
118
+ end
119
+ end
120
+
121
+ @xml['dc'].publisher(@book.publisher) unless @book.publisher.nil?
122
+ @xml['dc'].language(@book.language) unless @book.language.nil?
123
+ @xml['dc'].date(@book.published.iso8601) unless @book.published.nil?
124
+
125
+ cover_image = @target.cover_image
126
+ unless cover_image.nil?
127
+ cover_image_result = @file_resolver.file_from_request(cover_image)
128
+ @xml.meta(name: 'cover', content: create_id_from_path(pretty_path(cover_image_result)))
129
+ end
130
+
131
+ if epub_version >= 3
132
+ @xml.meta(Time.now.utc.iso8601, property: 'dcterms:modified')
133
+
134
+ if @target.fixed_layout
135
+ @xml.meta('pre-paginated', property: 'rendition:layout')
136
+ @xml.meta('auto', property: 'rendition:orientation')
137
+ @xml.meta('auto', property: 'rendition:spread')
138
+ end
139
+
140
+ if @target.ibooks?
141
+ @xml.meta(@book.version, property: 'ibooks:version') unless @book.version.nil?
142
+ @xml.meta(@target.custom_fonts, property: 'ibooks:specified-fonts') unless @target.custom_fonts.nil?
143
+ end
144
+ end
145
+
146
+ @xml.meta(name: 'Created by', content: Epuber.to_s)
147
+ @xml.meta(name: 'Epuber version', content: Epuber::VERSION)
148
+ end
149
+ end
150
+
151
+ # --------- MANIFEST --------------------------
152
+
153
+ # @return nil
154
+ #
155
+ def generate_manifest
156
+ @xml.manifest do
157
+ @file_resolver.manifest_files.each do |file|
158
+ pretty_path = pretty_path(file)
159
+ attrs = {}
160
+ attrs['id'] = create_id_from_path(pretty_path)
161
+ attrs['href'] = pretty_path
162
+ attrs['media-type'] = mime_type_for(file)
163
+
164
+ properties = file.properties
165
+ if properties.length > 0 && @target.epub_version >= 3
166
+ pretty_properties = properties.to_a.map { |property| PROPERTIES_MAP[property] }.join(' ')
167
+ attrs['properties'] = pretty_properties
168
+ end
169
+
170
+ @xml.item(attrs)
171
+ end
172
+ end
173
+ end
174
+
175
+ # --------- SPINE --------------------------
176
+
177
+ # @return nil
178
+ #
179
+ def generate_spine
180
+ args = if @target.epub_version >= 3
181
+ {}
182
+ else
183
+ nav_file = @file_resolver.manifest_files.find { |file| file.destination_path.end_with?('.ncx') }
184
+ raise 'not found nav file' if nav_file.nil?
185
+
186
+ { toc: create_id_from_path(pretty_path(nav_file)) }
187
+ end
188
+
189
+ all_items = @target.root_toc.flat_sub_items.map do |toc_item|
190
+ result_file = @file_resolver.file_from_request(toc_item.file_request)
191
+
192
+ attrs = {}
193
+ attrs['idref'] = create_id_from_path(pretty_path(result_file))
194
+ attrs['linear'] = 'no' unless toc_item.linear?
195
+
196
+ attrs
197
+ end
198
+
199
+ # uniq, but without any other attributes
200
+ all_items.uniq! { |i| i['idref'] }
201
+
202
+ @xml.spine(args) do
203
+ all_items.each do |attrs|
204
+ @xml.itemref(attrs)
205
+ end
206
+ end
207
+ end
208
+
209
+ # --------- GUIDE --------------------------
210
+
211
+ def generate_guide
212
+ return if @target.epub_version >= 3
213
+
214
+ @xml.guide do
215
+ guide_visit_toc_item(@target.root_toc)
216
+ end
217
+ end
218
+
219
+ # @param toc_items [Array<Epuber::Book::TocItem>]
220
+ #
221
+ # @return nil
222
+ #
223
+ def guide_visit_toc_items(toc_items)
224
+ toc_items.each do |child_item|
225
+ guide_visit_toc_item(child_item)
226
+ end
227
+ end
228
+
229
+ # @param toc_item [Epuber::Book::TocItem]
230
+ #
231
+ # @return nil
232
+ #
233
+ def guide_visit_toc_item(toc_item)
234
+ unless toc_item.file_request.nil?
235
+ result_file = @file_resolver.file_from_request(toc_item.file_request)
236
+
237
+ toc_item.landmarks.each do |landmark|
238
+ type = LANDMARKS_MAP[landmark]
239
+
240
+ raise "Unknown landmark `#{landmark.inspect}`, supported are #{LANDMARKS_MAP.keys}" if type.nil?
241
+
242
+ @xml.reference(type: type, href: pretty_path(result_file))
243
+ end
244
+ end
245
+
246
+ guide_visit_toc_items(toc_item.sub_items)
247
+ end
248
+
249
+ # --------- other methods --------------------------
250
+
251
+ # Creates id from file path
252
+ #
253
+ # @param path [String]
254
+ #
255
+ # @return [String]
256
+ #
257
+ def create_id_from_path(path)
258
+ path && path.gsub('/', '.')
259
+ end
260
+
261
+ # Creates proper mime-type for file
262
+ #
263
+ # @param file [Epuber::Book::File]
264
+ #
265
+ # @return [String]
266
+ #
267
+ def mime_type_for(file)
268
+ filename = file.destination_path
269
+ MIME::Types.of(filename).first.content_type
270
+ end
271
+
272
+ # Creates hash of namespaces for root package element
273
+ #
274
+ # @return [Hash<String, String>]
275
+ #
276
+ def package_namespaces
277
+ namespaces = [EPUB2_NAMESPACES]
278
+ namespaces << EPUB3_NAMESPACES if @target.epub_version >= 3
279
+ namespaces << IBOOKS_NAMESPACES if @target.epub_version >= 3 && @target.ibooks?
280
+ namespaces.reduce(:merge)
281
+ end
282
+ end
283
+ end
284
+ end