epuber 0.7.4 → 0.9.0

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +10 -2
  3. data/LICENSE.txt +1 -1
  4. data/README.md +4 -3
  5. data/epuber.gemspec +11 -16
  6. data/lib/epuber/book/contributor.rb +10 -7
  7. data/lib/epuber/book/file_request.rb +3 -3
  8. data/lib/epuber/book/target.rb +30 -33
  9. data/lib/epuber/book/toc_item.rb +2 -4
  10. data/lib/epuber/book.rb +21 -21
  11. data/lib/epuber/checker/bookspec_checker.rb +26 -0
  12. data/lib/epuber/checker/text_checker.rb +16 -7
  13. data/lib/epuber/checker.rb +16 -2
  14. data/lib/epuber/checker_transformer_base.rb +3 -6
  15. data/lib/epuber/command/build.rb +40 -25
  16. data/lib/epuber/command/from_file.rb +39 -0
  17. data/lib/epuber/command/init.rb +34 -32
  18. data/lib/epuber/command/server.rb +3 -3
  19. data/lib/epuber/command.rb +18 -20
  20. data/lib/epuber/compiler/compilation_context.rb +10 -8
  21. data/lib/epuber/compiler/file_database.rb +2 -4
  22. data/lib/epuber/compiler/file_finders/abstract.rb +36 -26
  23. data/lib/epuber/compiler/file_finders/imaginary.rb +40 -35
  24. data/lib/epuber/compiler/file_resolver.rb +79 -89
  25. data/lib/epuber/compiler/file_stat.rb +4 -4
  26. data/lib/epuber/compiler/file_types/abstract_file.rb +4 -7
  27. data/lib/epuber/compiler/file_types/bade_file.rb +20 -15
  28. data/lib/epuber/compiler/file_types/coffee_script_file.rb +1 -1
  29. data/lib/epuber/compiler/file_types/css_file.rb +103 -0
  30. data/lib/epuber/compiler/file_types/generated_file.rb +1 -1
  31. data/lib/epuber/compiler/file_types/image_file.rb +4 -2
  32. data/lib/epuber/compiler/file_types/nav_file.rb +0 -1
  33. data/lib/epuber/compiler/file_types/opf_file.rb +0 -1
  34. data/lib/epuber/compiler/file_types/source_file.rb +8 -3
  35. data/lib/epuber/compiler/file_types/stylus_file.rb +4 -3
  36. data/lib/epuber/compiler/file_types/xhtml_file.rb +67 -13
  37. data/lib/epuber/compiler/generator.rb +1 -2
  38. data/lib/epuber/compiler/meta_inf_generator.rb +1 -1
  39. data/lib/epuber/compiler/nav_generator.rb +10 -11
  40. data/lib/epuber/compiler/opf_generator.rb +26 -27
  41. data/lib/epuber/compiler/problem.rb +12 -21
  42. data/lib/epuber/compiler/xhtml_processor.rb +63 -32
  43. data/lib/epuber/compiler.rb +77 -25
  44. data/lib/epuber/config.rb +16 -10
  45. data/lib/epuber/dsl/attribute.rb +17 -18
  46. data/lib/epuber/dsl/attribute_support.rb +7 -7
  47. data/lib/epuber/dsl/object.rb +19 -17
  48. data/lib/epuber/dsl/tree_object.rb +2 -3
  49. data/lib/epuber/epubcheck.rb +15 -0
  50. data/lib/epuber/from_file/bookspec_generator.rb +371 -0
  51. data/lib/epuber/from_file/encryption_handler.rb +146 -0
  52. data/lib/epuber/from_file/from_file_executor.rb +140 -0
  53. data/lib/epuber/from_file/nav_file.rb +163 -0
  54. data/lib/epuber/from_file/opf_file.rb +219 -0
  55. data/lib/epuber/helper.rb +0 -1
  56. data/lib/epuber/lockfile.rb +7 -9
  57. data/lib/epuber/plugin.rb +2 -3
  58. data/lib/epuber/ruby_extensions/match_data.rb +1 -1
  59. data/lib/epuber/ruby_extensions/thread.rb +1 -0
  60. data/lib/epuber/server/base.styl +0 -1
  61. data/lib/epuber/server/basic.styl +1 -30
  62. data/lib/epuber/server/handlers.rb +1 -1
  63. data/lib/epuber/server.rb +81 -80
  64. data/lib/epuber/third_party/bower.rb +5 -5
  65. data/lib/epuber/transformer/book_transformer.rb +108 -0
  66. data/lib/epuber/transformer/text_transformer.rb +4 -2
  67. data/lib/epuber/transformer.rb +4 -2
  68. data/lib/epuber/user_interface.rb +49 -38
  69. data/lib/epuber/vendor/hash_binding.rb +9 -2
  70. data/lib/epuber/vendor/ruby_templater.rb +4 -8
  71. data/lib/epuber/vendor/version.rb +12 -12
  72. data/lib/epuber/version.rb +1 -1
  73. metadata +79 -100
  74. data/lib/epuber/server/fonts/AvenirNext/AvenirNext-Bold.ttf +0 -0
  75. data/lib/epuber/server/fonts/AvenirNext/AvenirNext-BoldItalic.ttf +0 -0
  76. data/lib/epuber/server/fonts/AvenirNext/AvenirNext-Italic.ttf +0 -0
  77. data/lib/epuber/server/fonts/AvenirNext/AvenirNext-Regular.ttf +0 -0
@@ -11,6 +11,8 @@ module Epuber
11
11
  #
12
12
  attr_accessor :entries
13
13
 
14
+ # @return [String]
15
+ #
14
16
  attr_reader :name
15
17
 
16
18
  def initialize(name)
@@ -32,8 +34,12 @@ module Epuber
32
34
  end
33
35
 
34
36
  class FileEntry
37
+ # @return [String]
38
+ #
35
39
  attr_reader :name
36
40
 
41
+ # @return [String]
42
+ #
37
43
  attr_reader :absolute_path
38
44
 
39
45
  def initialize(name, absolute_path)
@@ -42,7 +48,7 @@ module Epuber
42
48
  end
43
49
 
44
50
  def ==(other)
45
- other.is_a?(FileEntry) ? name == other.name : name == other.to_s
51
+ name == (other.is_a?(FileEntry) ? other.name : other.to_s)
46
52
  end
47
53
  end
48
54
 
@@ -83,14 +89,11 @@ module Epuber
83
89
  make_dir_p(path)[file_name] = FileEntry.new(file_name, file_path)
84
90
  end
85
91
 
86
-
87
92
  def __core_find_files_from_pattern(pattern)
88
93
  parts = self.class.path_parts(pattern)
89
94
  found_entries = find_recurser(root, parts).flatten
90
95
  file_entries = found_entries.reject { |entry| entry.is_a?(DirEntry) }
91
- file_entries.map do |item|
92
- item.absolute_path
93
- end
96
+ file_entries.map(&:absolute_path)
94
97
  end
95
98
 
96
99
  def __core_file?(path)
@@ -104,12 +107,14 @@ module Epuber
104
107
  current.is_a?(FileEntry)
105
108
  end
106
109
 
107
- private
108
-
109
- def self.path_parts(path)
110
- path.split(File::SEPARATOR).reject(&:empty?)
110
+ class << self
111
+ def path_parts(path)
112
+ path.split(File::SEPARATOR).reject(&:empty?)
113
+ end
111
114
  end
112
115
 
116
+ private
117
+
113
118
  def find_recurser(dir, parts)
114
119
  return [] unless dir.respond_to? :[]
115
120
 
@@ -117,25 +122,25 @@ module Epuber
117
122
  return [] if pattern.nil?
118
123
 
119
124
  matches = case pattern
120
- when '**'
121
- case parts
122
- when ['*']
123
- parts = [] # end recursion
124
- directories_under(dir).map do |d|
125
- d.entries.select do |f|
126
- (f.is_a?(FileEntry) || f.is_a?(DirEntry)) &&
127
- f.name.match(/\A(?!\.)/)
128
- end
129
- end.flatten.uniq
130
- when []
131
- parts = [] # end recursion
132
- dir.entries.values.flatten.uniq
133
- else
134
- directories_under(dir)
135
- end
125
+ when '**'
126
+ case parts
127
+ when ['*']
128
+ parts = [] # end recursion
129
+ directories_under(dir).map do |d|
130
+ d.entries.select do |f|
131
+ (f.is_a?(FileEntry) || f.is_a?(DirEntry)) &&
132
+ f.name.match(/\A(?!\.)/)
133
+ end
134
+ end.flatten.uniq
135
+ when []
136
+ parts = [] # end recursion
137
+ dir.entries.values.flatten.uniq
136
138
  else
137
- regex_body = pattern_to_regex(pattern)
138
- dir.entries.reject { |k, _v| /\A#{regex_body}\Z/ !~ k }.values
139
+ directories_under(dir)
140
+ end
141
+ else
142
+ regex_body = pattern_to_regex(pattern)
143
+ dir.entries.select { |k, _v| /\A#{regex_body}\Z/ =~ k }.values
139
144
  end
140
145
 
141
146
  if parts.empty? # we're done recursing
@@ -156,14 +161,14 @@ module Epuber
156
161
 
157
162
  def pattern_to_regex(pattern)
158
163
  pattern.gsub('.', '\.')
159
- .gsub('?', '.')
160
- .gsub('*', '.*')
161
- .gsub('(', '\(')
162
- .gsub(')', '\)')
163
- .gsub(/\{(.*?)\}/) do
164
- "(#{Regexp.last_match[1].gsub(',', '|')})"
165
- end
166
- .gsub(/\A\./, '(?!\.).')
164
+ .gsub('?', '.')
165
+ .gsub('*', '.*')
166
+ .gsub('(', '\(')
167
+ .gsub(')', '\)')
168
+ .gsub(/\{(.*?)\}/) do
169
+ "(#{Regexp.last_match[1].gsub(',', '|')})"
170
+ end
171
+ .gsub(/\A\./, '(?!\.).')
167
172
  end
168
173
  end
169
174
  end
@@ -20,12 +20,12 @@ module Epuber
20
20
  require_relative 'file_types/container_xml_file'
21
21
  require_relative 'file_types/ibooks_display_options_file'
22
22
  require_relative 'file_types/coffee_script_file'
23
-
23
+ require_relative 'file_types/css_file'
24
24
 
25
25
  class FileResolver
26
26
  class ResolveError < StandardError; end
27
27
 
28
- PATH_TYPES = [:spine, :manifest, :package, nil]
28
+ PATH_TYPES = [:spine, :manifest, :package, nil].freeze
29
29
 
30
30
  # @return [String] path where should look for source files
31
31
  #
@@ -125,24 +125,16 @@ module Epuber
125
125
  existing_file = @final_destination_path_to_file[file.final_destination_path]
126
126
 
127
127
  # save mapping from file_request to file, file_request can be different, but result file could be the same ...
128
- unless file.try(:file_request).nil?
129
- @request_to_files[file.file_request] << (existing_file || file)
130
- end
128
+ @request_to_files[file.file_request] << (existing_file || file) unless file.try(:file_request).nil?
131
129
 
132
130
  # return existing file if already exists, new file will be thrown away
133
131
  return existing_file unless existing_file.nil?
134
132
 
135
- if [:spine].include?(type)
136
- @spine_files << file
137
- end
133
+ @spine_files << file if [:spine].include?(type)
138
134
 
139
- if [:spine, :manifest].include?(type)
140
- @manifest_files << file
141
- end
135
+ @manifest_files << file if %i[spine manifest].include?(type)
142
136
 
143
- if [:spine, :manifest, :package].include?(type)
144
- @package_files << file
145
- end
137
+ @package_files << file if %i[spine manifest package].include?(type)
146
138
 
147
139
  @files << file
148
140
 
@@ -151,13 +143,11 @@ module Epuber
151
143
 
152
144
  @final_destination_path_to_file[file.final_destination_path] = file
153
145
 
154
- if file.respond_to?(:source_path) && !file.source_path.nil?
155
- @source_path_to_file[file.source_path] = file
156
- end
146
+ @source_path_to_file[file.source_path] = file if file.respond_to?(:source_path) && !file.source_path.nil?
157
147
 
158
- if file.respond_to?(:abs_source_path) && !file.abs_source_path.nil?
159
- @abs_source_path_to_file[file.abs_source_path] = file
160
- end
148
+ return unless file.respond_to?(:abs_source_path) && !file.abs_source_path.nil?
149
+
150
+ @abs_source_path_to_file[file.abs_source_path] = file
161
151
  end
162
152
 
163
153
  # Get instance of file from request instance
@@ -184,7 +174,7 @@ module Epuber
184
174
  end
185
175
 
186
176
  if file_request.only_one
187
- files.first # @request_to_files always returns array, see #initialize method
177
+ files.first # @request_to_files always returns array, see #initialize method
188
178
  else
189
179
  files
190
180
  end
@@ -219,99 +209,99 @@ module Epuber
219
209
  # @return [Array<String>] list of files that should be deleted in destination directory
220
210
  #
221
211
  def unneeded_files_in_destination
222
- requested_paths = files.map do |file|
223
- file.pkg_destination_path
224
- end
212
+ requested_paths = files.map(&:pkg_destination_path)
225
213
 
226
214
  existing_paths = FileFinders::Normal.new(destination_path).find_all('*')
227
215
 
228
216
  unnecessary_paths = existing_paths - requested_paths
229
217
 
230
- unnecessary_paths.select! do |path|
231
- !::File.directory?(File.join(destination_path, path))
218
+ unnecessary_paths.reject! do |path|
219
+ ::File.directory?(File.join(destination_path, path))
232
220
  end
233
221
 
234
222
  unnecessary_paths
235
223
  end
236
224
 
237
-
238
225
  ##################################################################################################################
239
226
 
240
- private
241
-
242
- # @param [String] path path to some file
243
- #
244
- # @return [String] path with changed extension
245
- #
246
- def self.renamed_file_with_path(path)
247
- extname = File.extname(path)
248
- new_extname = FileFinders::EXTENSIONS_RENAME[extname]
249
-
250
- if new_extname.nil?
251
- path
252
- else
253
- path.sub(/#{Regexp.escape(extname)}$/, new_extname)
254
- end
255
- end
256
-
257
- # @param file [Epuber::Compiler::AbstractFile]
258
- #
259
- # @return [nil]
260
- #
261
- def resolve_destination_path(file)
262
- if file.final_destination_path.nil?
263
- dest_path = if file.respond_to?(:source_path) && !file.source_path.nil?
264
- file.abs_source_path = File.expand_path(file.source_path, source_path)
265
- self.class.renamed_file_with_path(file.source_path)
266
- elsif !file.destination_path.nil?
267
- file.destination_path
268
- else
269
- raise ResolveError, "What should I do with file that doesn't have source path or destination path? file: #{file.inspect}"
270
- end
271
-
272
- file.destination_path = dest_path
273
- file.pkg_destination_path = File.join(*self.class.path_comps_for(file.path_type), dest_path)
274
- file.final_destination_path = File.join(destination_path, file.pkg_destination_path)
227
+ class << self
228
+ # @param [String] path path to some file
229
+ #
230
+ # @return [String] path with changed extension
231
+ #
232
+ def renamed_file_with_path(path)
233
+ extname = File.extname(path)
234
+ new_extname = FileFinders::EXTENSIONS_RENAME[extname]
235
+
236
+ if new_extname.nil?
237
+ path
238
+ else
239
+ path.sub(/#{Regexp.escape(extname)}$/, new_extname)
240
+ end
275
241
  end
276
- end
277
242
 
278
- # @param [String] extname extension of file
279
- #
280
- # @return [Class]
281
- #
282
- def self.file_class_for(extname)
283
- mapping = {
284
- '.styl' => FileTypes::StylusFile,
243
+ # @param [String] extname extension of file
244
+ #
245
+ # @return [Class]
246
+ #
247
+ def file_class_for(extname)
248
+ mapping = {
249
+ '.styl' => FileTypes::StylusFile,
250
+ '.css' => FileTypes::CSSFile,
285
251
 
286
- '.coffee' => FileTypes::CoffeeScriptFile,
252
+ '.coffee' => FileTypes::CoffeeScriptFile,
287
253
 
288
- '.bade' => FileTypes::BadeFile,
289
- '.xhtml' => FileTypes::XHTMLFile,
290
- '.html' => FileTypes::XHTMLFile,
254
+ '.bade' => FileTypes::BadeFile,
255
+ '.xhtml' => FileTypes::XHTMLFile,
256
+ '.html' => FileTypes::XHTMLFile,
291
257
 
292
- '.jpg' => FileTypes::ImageFile,
293
- '.jpeg' => FileTypes::ImageFile,
294
- '.png' => FileTypes::ImageFile,
295
- }
258
+ '.jpg' => FileTypes::ImageFile,
259
+ '.jpeg' => FileTypes::ImageFile,
260
+ '.png' => FileTypes::ImageFile,
261
+ }
296
262
 
297
- mapping[extname] || FileTypes::StaticFile
298
- end
263
+ mapping[extname] || FileTypes::StaticFile
264
+ end
299
265
 
300
- # @param [String] root_path path to root of the package
301
- # @param [Symbol] path_type path type of file
302
- #
303
- # @return [Array<String>] path components
304
- #
305
- def self.path_comps_for(root_path = nil, path_type)
306
- case path_type
266
+ # @param [String] root_path path to root of the package
267
+ # @param [Symbol] path_type path type of file
268
+ #
269
+ # @return [Array<String>] path components
270
+ #
271
+ def path_comps_for(root_path, path_type)
272
+ case path_type
307
273
  when :spine, :manifest
308
274
  Array(root_path) + [Compiler::EPUB_CONTENT_FOLDER]
309
275
  when :package
310
276
  Array(root_path)
311
- else
312
- nil
277
+ end
313
278
  end
314
279
  end
280
+
281
+ private
282
+
283
+ # @param [Epuber::Compiler::AbstractFile] file
284
+ #
285
+ # @return [nil]
286
+ #
287
+ def resolve_destination_path(file)
288
+ return unless file.final_destination_path.nil?
289
+
290
+ dest_path = if file.respond_to?(:source_path) && !file.source_path.nil?
291
+ file.abs_source_path = File.expand_path(file.source_path, source_path)
292
+ self.class.renamed_file_with_path(file.source_path)
293
+ elsif !file.destination_path.nil?
294
+ file.destination_path
295
+ else
296
+ raise ResolveError, <<~ERROR
297
+ What should I do with file that doesn't have source path or destination path? file: #{file.inspect}
298
+ ERROR
299
+ end
300
+
301
+ file.destination_path = dest_path
302
+ file.pkg_destination_path = File.join(*self.class.path_comps_for(nil, file.path_type), dest_path)
303
+ file.final_destination_path = File.join(destination_path, file.pkg_destination_path)
304
+ end
315
305
  end
316
306
  end
317
307
  end
@@ -36,7 +36,7 @@ module Epuber
36
36
  @mtime = stat.mtime
37
37
  @ctime = stat.ctime
38
38
  @size = stat.size
39
- rescue
39
+ rescue StandardError
40
40
  # noop
41
41
  end
42
42
  end
@@ -66,9 +66,9 @@ module Epuber
66
66
  raise AttributeError, "other must be class of #{self.class}" unless other.is_a?(FileStat)
67
67
 
68
68
  file_path == other.file_path &&
69
- size == other.size &&
70
- mtime == other.mtime &&
71
- ctime == other.ctime
69
+ size == other.size &&
70
+ mtime == other.mtime &&
71
+ ctime == other.ctime
72
72
  end
73
73
  end
74
74
  end
@@ -4,18 +4,18 @@ module Epuber
4
4
  class Compiler
5
5
  module FileTypes
6
6
  class AbstractFile
7
-
8
7
  # @return [String] relative destination path
9
8
  #
10
9
  attr_accessor :destination_path
11
10
 
12
- # @return [Symbol] group of this file (:text, :image, :font, ...), see Epuber::Compiler::FileFinder::GROUP_EXTENSIONS
11
+ # @return [Symbol] group of this file (:text, :image, :font, ...), see
12
+ # Epuber::Compiler::FileFinder::GROUP_EXTENSIONS
13
13
  #
14
14
  attr_accessor :group
15
15
 
16
16
  # @return [Set<Symbol>] list of properties
17
17
  #
18
- attr_accessor :properties
18
+ attr_writer :properties
19
19
 
20
20
  # @return [Set<Symbol>] list of properties
21
21
  #
@@ -23,7 +23,6 @@ module Epuber
23
23
  @properties ||= Set.new
24
24
  end
25
25
 
26
-
27
26
  # @return [String] final relative destination path from root of the package calculated by FileResolver
28
27
  #
29
28
  attr_accessor :pkg_destination_path
@@ -88,9 +87,7 @@ module Epuber
88
87
  def self.write_to_file!(content, to_path)
89
88
  FileUtils.mkdir_p(File.dirname(to_path))
90
89
 
91
- File.open(to_path, 'w') do |file_handle|
92
- file_handle.write(content)
93
- end
90
+ File.write(to_path, content)
94
91
  end
95
92
  end
96
93
  end
@@ -26,10 +26,15 @@ module Epuber
26
26
  __file_resolver: file_resolver,
27
27
  __file: self,
28
28
  __toc_item: toc_item,
29
- __const: Hash.new { |_hash, key| UI.warning("Undefined constant with key `#{key}`", location: caller_locations[0]) }.merge!(target.constants),
29
+ __const: Hash.new do |_hash, key|
30
+ UI.warning("Undefined constant with key `#{key}`", location: caller_locations[0])
31
+ end.merge!(target.constants),
30
32
  }
31
33
 
32
- should_load_from_precompiled = up_to_date && precompiled_exists && compilation_context.incremental_build? && (!compilation_context.should_write)
34
+ should_load_from_precompiled = up_to_date &&
35
+ precompiled_exists &&
36
+ compilation_context.incremental_build? &&
37
+ !compilation_context.should_write
33
38
 
34
39
  precompiled = if should_load_from_precompiled
35
40
  begin
@@ -40,16 +45,10 @@ module Epuber
40
45
  end
41
46
  end
42
47
 
43
- if !precompiled.nil?
44
- xhtml_content = UI.print_step_processing_time('rendering precompiled Bade') do
45
- renderer = Bade::Renderer.from_precompiled(precompiled)
46
- .with_locals(variables)
47
- renderer.file_path = source_path
48
-
49
- renderer.render(new_line: '', indent: '')
48
+ if precompiled.nil?
49
+ if compilation_context.incremental_build?
50
+ UI.print_processing_debug_info('Parsing new version of source file')
50
51
  end
51
- else
52
- UI.print_processing_debug_info('Parsing new version of source file') if compilation_context.incremental_build?
53
52
 
54
53
  bade_content = load_source(compilation_context)
55
54
 
@@ -58,13 +57,19 @@ module Epuber
58
57
  .with_locals(variables)
59
58
 
60
59
  # turn on optimizations when can
61
- if renderer.respond_to?(:optimize=)
62
- renderer.optimize = true
63
- end
60
+ renderer.optimize = true if renderer.respond_to?(:optimize=)
64
61
 
65
62
  FileUtils.mkdir_p(File.dirname(precompiled_path))
66
63
  renderer.precompiled.write_yaml_to_file(precompiled_path)
67
64
 
65
+ renderer.render(new_line: '', indent: '')
66
+ end
67
+ else
68
+ xhtml_content = UI.print_step_processing_time('rendering precompiled Bade') do
69
+ renderer = Bade::Renderer.from_precompiled(precompiled)
70
+ .with_locals(variables)
71
+ renderer.file_path = source_path
72
+
68
73
  renderer.render(new_line: '', indent: '')
69
74
  end
70
75
  end
@@ -82,7 +87,7 @@ module Epuber
82
87
  # @return [String]
83
88
  #
84
89
  def precompiled_path
85
- File.join(Config.instance.build_cache_path(PRECOMPILED_CACHE_NAME), source_path + '.precompiled.yml')
90
+ File.join(Config.instance.build_cache_path(PRECOMPILED_CACHE_NAME), "#{source_path}.precompiled.yml")
86
91
  end
87
92
 
88
93
  def pretty_precompiled_path
@@ -10,7 +10,7 @@ module Epuber
10
10
  class CoffeeScriptFile < SourceFile
11
11
  # @param [Compiler::CompilationContext] compilation_context
12
12
  #
13
- def process(compilation_context)
13
+ def process(_compilation_context)
14
14
  return if destination_file_up_to_date?
15
15
 
16
16
  write_compiled(CoffeeScript.compile(File.new(abs_source_path)))
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'css_parser'
4
+
5
+ module Epuber
6
+ class Compiler
7
+ module FileTypes
8
+ require_relative 'source_file'
9
+
10
+ class CSSFile < SourceFile
11
+ DECLARATION_TO_FILE_GROUP_MAP = {
12
+ 'background' => :image,
13
+ 'background-image' => :image,
14
+ 'list-style' => :image,
15
+ 'list-style-image' => :image,
16
+ 'content' => :image,
17
+ 'cursor' => :image,
18
+ 'border-image' => :image,
19
+ 'border-image-source' => :image,
20
+ 'mask-image' => :image,
21
+ 'mask-box-image' => :image,
22
+ 'src' => :font,
23
+ }.freeze
24
+
25
+ URL_REGEXP = /url\((.+)\)/.freeze
26
+
27
+ # @param [Compiler::CompilationContext] compilation_context
28
+ #
29
+ def process(compilation_context)
30
+ return if destination_file_up_to_date?
31
+
32
+ write_processed(process_css(File.read(abs_source_path), compilation_context))
33
+ end
34
+
35
+ # Processes CSS file, resolves all linked files and adds them to file resolver
36
+ #
37
+ # @param [String] content
38
+ # @param [Compiler::CompilationContext] compilation_context
39
+ #
40
+ # @return [String]
41
+ #
42
+ def process_css(content, compilation_context)
43
+ file_resolver = compilation_context.file_resolver
44
+
45
+ parser = UI.print_step_processing_time('css parsing') do
46
+ parser = CssParser::Parser.new
47
+ parser.load_string!(content)
48
+ parser
49
+ end
50
+
51
+ # resolve links to files, add other linked resources and compute correct path
52
+ UI.print_step_processing_time('resolving url()') do
53
+ parser.each_rule_set do |rule_set, _media_types|
54
+ declarations = rule_set.instance_eval { @declarations }
55
+ declarations.each do |property, decl_value|
56
+ value = decl_value.to_s
57
+ next unless value =~ URL_REGEXP
58
+
59
+ path = Regexp.last_match(1)
60
+ if path.start_with?('"') && path.end_with?('"')
61
+ path = path[1..-2]
62
+ quote = '"'
63
+ end
64
+ if path.start_with?("'") && path.end_with?("'")
65
+ path = path[1..-2]
66
+ quote = "'"
67
+ end
68
+
69
+ next if path.start_with?('data:') || path.start_with?('http://') || path.start_with?('https://')
70
+
71
+ resource_group = DECLARATION_TO_FILE_GROUP_MAP[property]
72
+ dirname = File.dirname(destination_path)
73
+
74
+ begin
75
+ new_path = file_resolver.dest_finder.find_file(path, groups: resource_group, context_path: dirname)
76
+ rescue FileFinders::FileNotFoundError, FileFinders::MultipleFilesFoundError
77
+ new_path = XHTMLProcessor.resolved_link_to_file(path,
78
+ resource_group,
79
+ dirname,
80
+ file_resolver.source_finder).to_s
81
+ pkg_abs_path = File.expand_path(new_path, dirname).unicode_normalize
82
+ pkg_new_path = Pathname.new(pkg_abs_path)
83
+ .relative_path_from(Pathname.new(file_resolver.source_path))
84
+ .to_s
85
+
86
+ file_class = FileResolver.file_class_for(File.extname(new_path))
87
+ file = file_class.new(pkg_new_path)
88
+ file.path_type = :manifest
89
+ file_resolver.add_file(file)
90
+ end
91
+
92
+ new_url = FileResolver.renamed_file_with_path(new_path)
93
+ content = content.gsub(value, "url(#{quote}#{new_url}#{quote})")
94
+ end
95
+ end
96
+ end
97
+
98
+ content
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -12,7 +12,7 @@ module Epuber
12
12
 
13
13
  # @param [Compiler::CompilationContext] compilation_context
14
14
  #
15
- def process(compilation_context)
15
+ def process(_compilation_context)
16
16
  write_generate(content.to_s)
17
17
  end
18
18
 
@@ -16,14 +16,16 @@ module Epuber
16
16
  dest = final_destination_path
17
17
  source = abs_source_path
18
18
 
19
- img = Magick::Image::read(source).first
19
+ img = Magick::Image.read(source).first
20
20
 
21
21
  resolution = img.columns * img.rows
22
22
  max_resolution = 3_000_000
23
23
 
24
24
  if resolution > max_resolution
25
25
  img = img.change_geometry("#{max_resolution}@>") do |width, height, b_img|
26
- UI.print_processing_debug_info("downscaling from resolution #{b_img.columns}x#{b_img.rows} to #{width}x#{height}")
26
+ UI.print_processing_debug_info(<<~MSG)
27
+ downscaling from resolution #{b_img.columns}x#{b_img.rows} to #{width}x#{height}
28
+ MSG
27
29
  b_img.resize!(width, height)
28
30
  end
29
31
 
@@ -6,7 +6,6 @@ module Epuber
6
6
  require_relative 'generated_file'
7
7
 
8
8
  class NavFile < GeneratedFile
9
-
10
9
  # @return [Epuber::Version]
11
10
  #
12
11
  attr_reader :epub_version
@@ -6,7 +6,6 @@ module Epuber
6
6
  require_relative 'generated_file'
7
7
 
8
8
  class OPFFile < GeneratedFile
9
-
10
9
  def initialize
11
10
  super
12
11