epuber 0.7.4 → 0.8.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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -2
  3. data/README.md +2 -1
  4. data/epuber.gemspec +10 -12
  5. data/lib/epuber/book/contributor.rb +0 -1
  6. data/lib/epuber/book/file_request.rb +1 -1
  7. data/lib/epuber/book/target.rb +20 -23
  8. data/lib/epuber/book/toc_item.rb +2 -4
  9. data/lib/epuber/book.rb +19 -19
  10. data/lib/epuber/checker/bookspec_checker.rb +26 -0
  11. data/lib/epuber/checker/text_checker.rb +2 -1
  12. data/lib/epuber/checker.rb +16 -2
  13. data/lib/epuber/checker_transformer_base.rb +2 -5
  14. data/lib/epuber/command/build.rb +34 -24
  15. data/lib/epuber/command/init.rb +23 -23
  16. data/lib/epuber/command/server.rb +2 -2
  17. data/lib/epuber/command.rb +17 -20
  18. data/lib/epuber/compiler/compilation_context.rb +10 -8
  19. data/lib/epuber/compiler/file_database.rb +0 -2
  20. data/lib/epuber/compiler/file_finders/abstract.rb +33 -23
  21. data/lib/epuber/compiler/file_finders/imaginary.rb +40 -35
  22. data/lib/epuber/compiler/file_resolver.rb +77 -88
  23. data/lib/epuber/compiler/file_stat.rb +4 -4
  24. data/lib/epuber/compiler/file_types/abstract_file.rb +3 -4
  25. data/lib/epuber/compiler/file_types/bade_file.rb +12 -7
  26. data/lib/epuber/compiler/file_types/coffee_script_file.rb +1 -1
  27. data/lib/epuber/compiler/file_types/generated_file.rb +1 -1
  28. data/lib/epuber/compiler/file_types/image_file.rb +4 -2
  29. data/lib/epuber/compiler/file_types/nav_file.rb +0 -1
  30. data/lib/epuber/compiler/file_types/opf_file.rb +0 -1
  31. data/lib/epuber/compiler/file_types/source_file.rb +8 -3
  32. data/lib/epuber/compiler/file_types/xhtml_file.rb +67 -13
  33. data/lib/epuber/compiler/generator.rb +1 -2
  34. data/lib/epuber/compiler/meta_inf_generator.rb +1 -1
  35. data/lib/epuber/compiler/nav_generator.rb +5 -6
  36. data/lib/epuber/compiler/opf_generator.rb +22 -23
  37. data/lib/epuber/compiler/problem.rb +12 -21
  38. data/lib/epuber/compiler/xhtml_processor.rb +61 -31
  39. data/lib/epuber/compiler.rb +66 -19
  40. data/lib/epuber/config.rb +13 -7
  41. data/lib/epuber/dsl/attribute.rb +16 -17
  42. data/lib/epuber/dsl/attribute_support.rb +3 -3
  43. data/lib/epuber/dsl/object.rb +17 -15
  44. data/lib/epuber/dsl/tree_object.rb +2 -3
  45. data/lib/epuber/epubcheck.rb +15 -0
  46. data/lib/epuber/helper.rb +0 -1
  47. data/lib/epuber/lockfile.rb +7 -9
  48. data/lib/epuber/plugin.rb +1 -2
  49. data/lib/epuber/ruby_extensions/match_data.rb +1 -1
  50. data/lib/epuber/ruby_extensions/thread.rb +1 -0
  51. data/lib/epuber/server/base.styl +0 -1
  52. data/lib/epuber/server/basic.styl +1 -30
  53. data/lib/epuber/server/handlers.rb +1 -1
  54. data/lib/epuber/server.rb +67 -66
  55. data/lib/epuber/third_party/bower.rb +5 -5
  56. data/lib/epuber/transformer/text_transformer.rb +4 -2
  57. data/lib/epuber/transformer.rb +2 -2
  58. data/lib/epuber/user_interface.rb +49 -38
  59. data/lib/epuber/vendor/hash_binding.rb +9 -2
  60. data/lib/epuber/vendor/ruby_templater.rb +1 -5
  61. data/lib/epuber/vendor/version.rb +10 -10
  62. data/lib/epuber/version.rb +1 -1
  63. metadata +67 -69
  64. data/lib/epuber/server/fonts/AvenirNext/AvenirNext-Bold.ttf +0 -0
  65. data/lib/epuber/server/fonts/AvenirNext/AvenirNext-BoldItalic.ttf +0 -0
  66. data/lib/epuber/server/fonts/AvenirNext/AvenirNext-Italic.ttf +0 -0
  67. data/lib/epuber/server/fonts/AvenirNext/AvenirNext-Regular.ttf +0 -0
@@ -25,7 +25,7 @@ module Epuber
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,98 @@ 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,
285
250
 
286
- '.coffee' => FileTypes::CoffeeScriptFile,
251
+ '.coffee' => FileTypes::CoffeeScriptFile,
287
252
 
288
- '.bade' => FileTypes::BadeFile,
289
- '.xhtml' => FileTypes::XHTMLFile,
290
- '.html' => FileTypes::XHTMLFile,
253
+ '.bade' => FileTypes::BadeFile,
254
+ '.xhtml' => FileTypes::XHTMLFile,
255
+ '.html' => FileTypes::XHTMLFile,
291
256
 
292
- '.jpg' => FileTypes::ImageFile,
293
- '.jpeg' => FileTypes::ImageFile,
294
- '.png' => FileTypes::ImageFile,
295
- }
257
+ '.jpg' => FileTypes::ImageFile,
258
+ '.jpeg' => FileTypes::ImageFile,
259
+ '.png' => FileTypes::ImageFile,
260
+ }
296
261
 
297
- mapping[extname] || FileTypes::StaticFile
298
- end
262
+ mapping[extname] || FileTypes::StaticFile
263
+ end
299
264
 
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
265
+ # @param [String] root_path path to root of the package
266
+ # @param [Symbol] path_type path type of file
267
+ #
268
+ # @return [Array<String>] path components
269
+ #
270
+ def path_comps_for(root_path, path_type)
271
+ case path_type
307
272
  when :spine, :manifest
308
273
  Array(root_path) + [Compiler::EPUB_CONTENT_FOLDER]
309
274
  when :package
310
275
  Array(root_path)
311
- else
312
- nil
276
+ end
313
277
  end
314
278
  end
279
+
280
+ private
281
+
282
+ # @param file [Epuber::Compiler::AbstractFile]
283
+ #
284
+ # @return [nil]
285
+ #
286
+ def resolve_destination_path(file)
287
+ return unless file.final_destination_path.nil?
288
+
289
+ dest_path = if file.respond_to?(:source_path) && !file.source_path.nil?
290
+ file.abs_source_path = File.expand_path(file.source_path, source_path)
291
+ self.class.renamed_file_with_path(file.source_path)
292
+ elsif !file.destination_path.nil?
293
+ file.destination_path
294
+ else
295
+ raise ResolveError, <<~ERROR
296
+ What should I do with file that doesn't have source path or destination path? file: #{file.inspect}
297
+ ERROR
298
+ end
299
+
300
+ file.destination_path = dest_path
301
+ file.pkg_destination_path = File.join(*self.class.path_comps_for(nil, file.path_type), dest_path)
302
+ file.final_destination_path = File.join(destination_path, file.pkg_destination_path)
303
+ end
315
304
  end
316
305
  end
317
306
  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
@@ -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
@@ -49,7 +54,9 @@ module Epuber
49
54
  renderer.render(new_line: '', indent: '')
50
55
  end
51
56
  else
52
- UI.print_processing_debug_info('Parsing new version of source file') if compilation_context.incremental_build?
57
+ if compilation_context.incremental_build?
58
+ UI.print_processing_debug_info('Parsing new version of source file')
59
+ end
53
60
 
54
61
  bade_content = load_source(compilation_context)
55
62
 
@@ -58,9 +65,7 @@ module Epuber
58
65
  .with_locals(variables)
59
66
 
60
67
  # turn on optimizations when can
61
- if renderer.respond_to?(:optimize=)
62
- renderer.optimize = true
63
- end
68
+ renderer.optimize = true if renderer.respond_to?(:optimize=)
64
69
 
65
70
  FileUtils.mkdir_p(File.dirname(precompiled_path))
66
71
  renderer.precompiled.write_yaml_to_file(precompiled_path)
@@ -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)))
@@ -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
 
@@ -6,7 +6,6 @@ module Epuber
6
6
  require_relative 'abstract_file'
7
7
 
8
8
  class SourceFile < AbstractFile
9
-
10
9
  # @return [String] relative source path
11
10
  #
12
11
  attr_reader :source_path
@@ -22,6 +21,8 @@ module Epuber
22
21
  # @param [String] source_path relative path from project root to source file
23
22
  #
24
23
  def initialize(source_path)
24
+ super()
25
+
25
26
  @source_path = source_path
26
27
  end
27
28
 
@@ -95,7 +96,9 @@ module Epuber
95
96
  UI.print_processing_debug_info("Writing compiled version to #{pkg_destination_path}")
96
97
  self.class.write_to_file!(content, final_destination_path)
97
98
  else
98
- UI.print_processing_debug_info("Not writing to disk ... compiled version at #{pkg_destination_path} is same")
99
+ UI.print_processing_debug_info(<<~MSG)
100
+ Not writing to disk ... compiled version at #{pkg_destination_path} is same
101
+ MSG
99
102
  end
100
103
  end
101
104
 
@@ -104,7 +107,9 @@ module Epuber
104
107
  UI.print_processing_debug_info("Writing processed version to #{pkg_destination_path}")
105
108
  self.class.write_to_file!(content, final_destination_path)
106
109
  else
107
- UI.print_processing_debug_info("Not writing to disk ... processed version at #{pkg_destination_path} is same")
110
+ UI.print_processing_debug_info(<<~MSG)
111
+ Not writing to disk ... processed version at #{pkg_destination_path} is same
112
+ MSG
108
113
  end
109
114
  end
110
115
  end
@@ -12,6 +12,21 @@ module Epuber
12
12
  #
13
13
  attr_accessor :toc_item
14
14
 
15
+ # @return [Array<String>]
16
+ #
17
+ attr_accessor :global_ids
18
+
19
+ # @return [Array<String>]
20
+ #
21
+ attr_accessor :global_links
22
+
23
+ def initialize(source_path)
24
+ super
25
+
26
+ self.global_ids = []
27
+ self.global_links = []
28
+ end
29
+
15
30
  # @param [Book::Target] target
16
31
  # @param [FileResolver] file_resolver
17
32
  #
@@ -22,8 +37,9 @@ module Epuber
22
37
  Array(file_resolver.file_from_request(default_style_request))
23
38
  end.flatten
24
39
 
40
+ dirname = Pathname.new(File.dirname(final_destination_path))
25
41
  default_styles.map do |style|
26
- Pathname.new(style.final_destination_path).relative_path_from(Pathname.new(File.dirname(final_destination_path))).to_s
42
+ Pathname.new(style.final_destination_path).relative_path_from(dirname).to_s
27
43
  end
28
44
  end
29
45
 
@@ -37,8 +53,9 @@ module Epuber
37
53
  Array(file_resolver.file_from_request(default_style_request))
38
54
  end.flatten
39
55
 
56
+ dirname = Pathname.new(File.dirname(final_destination_path))
40
57
  default_scripts.map do |style|
41
- Pathname.new(style.final_destination_path).relative_path_from(Pathname.new(File.dirname(final_destination_path))).to_s
58
+ Pathname.new(style.final_destination_path).relative_path_from(dirname).to_s
42
59
  end
43
60
  end
44
61
 
@@ -58,10 +75,7 @@ module Epuber
58
75
  end
59
76
  end
60
77
 
61
-
62
- if compilation_context.should_write
63
- self.class.write_to_file(xhtml_content, abs_source_path)
64
- end
78
+ self.class.write_to_file(xhtml_content, abs_source_path) if compilation_context.should_write
65
79
 
66
80
  xhtml_content
67
81
  end
@@ -88,9 +102,7 @@ module Epuber
88
102
  XHTMLProcessor.xml_doc_from_str_with_errors(content, source_path)
89
103
  end
90
104
 
91
- if compilation_context.release_build && xhtml_doc.errors.count > 0
92
- process_nokogiri_errors(errors)
93
- end
105
+ process_nokogiri_errors(errors) if compilation_context.release_build && xhtml_doc.errors.count.positive?
94
106
 
95
107
  UI.print_step_processing_time('adding missing elements') do
96
108
  XHTMLProcessor.add_missing_root_elements(xhtml_doc, book.title, target.epub_version)
@@ -120,13 +132,13 @@ module Epuber
120
132
  XHTMLProcessor.resolve_mathml_namespace(xhtml_doc)
121
133
 
122
134
  UI.print_step_processing_time('investigating properties') do
123
- self.properties << :remote_resources if XHTMLProcessor.using_remote_resources?(xhtml_doc)
124
- self.properties << :scripted if XHTMLProcessor.using_javascript?(xhtml_doc)
125
- self.properties << :mathml if XHTMLProcessor.using_mathml?(xhtml_doc)
135
+ properties << :remote_resources if XHTMLProcessor.using_remote_resources?(xhtml_doc)
136
+ properties << :scripted if XHTMLProcessor.using_javascript?(xhtml_doc)
137
+ properties << :mathml if XHTMLProcessor.using_mathml?(xhtml_doc)
126
138
  end
127
139
 
128
140
  xhtml_string = UI.print_step_processing_time('converting to XHTML') do
129
- xhtml_doc.to_s
141
+ xhtml_doc.to_s
130
142
  end
131
143
 
132
144
  # perform transformations
@@ -145,6 +157,12 @@ module Epuber
145
157
  end
146
158
  end
147
159
 
160
+ if xhtml_string.include?('="$')
161
+ xhtml_doc = XHTMLProcessor.xml_document_from_string(xhtml_string, final_destination_path)
162
+ self.global_ids = XHTMLProcessor.find_global_ids(xhtml_doc)
163
+ self.global_links = XHTMLProcessor.find_global_links(xhtml_doc)
164
+ end
165
+
148
166
  xhtml_string
149
167
  end
150
168
 
@@ -153,6 +171,42 @@ module Epuber
153
171
  def process(compilation_context)
154
172
  write_processed(common_process(load_source(compilation_context), compilation_context))
155
173
  end
174
+
175
+ # @param [Compiler::CompilationContext] compilation_context
176
+ # @param [Hash<String, XHTMLFile>] global_ids
177
+ #
178
+ def process_global_ids(compilation_context, global_ids)
179
+ return if self.global_ids.empty? && global_links.empty?
180
+
181
+ xhtml_doc = XHTMLProcessor.xml_document_from_string(File.read(final_destination_path), final_destination_path)
182
+
183
+ XHTMLProcessor.find_global_ids_nodes(xhtml_doc).each do |node|
184
+ id = node['id']
185
+ node['id'] = id[1..-1]
186
+ end
187
+
188
+ XHTMLProcessor.find_global_links_nodes(xhtml_doc).each do |node|
189
+ href = node['href'][1..-1]
190
+
191
+ dest_file = global_ids[href]
192
+ if dest_file
193
+ rel_path = Pathname(dest_file.final_destination_path.unicode_normalize)
194
+ .relative_path_from(Pathname(File.dirname(final_destination_path.unicode_normalize))).to_s
195
+
196
+ node['href'] = "#{rel_path}##{href}"
197
+ else
198
+ message = "Can't find global id '#{href}' from link in file #{source_path}"
199
+ location = UserInterface::Location.new(path: final_destination_path, lineno: node.line)
200
+ if compilation_context.release_build?
201
+ UI.error!(message, location: location)
202
+ else
203
+ UI.warning(message, location: location)
204
+ end
205
+ end
206
+ end
207
+
208
+ write_processed(xhtml_doc.to_s)
209
+ end
156
210
  end
157
211
  end
158
212
  end
@@ -12,7 +12,6 @@ module Nokogiri
12
12
  end
13
13
  end
14
14
 
15
-
16
15
  module Epuber
17
16
  class Compiler
18
17
  class Generator
@@ -55,7 +54,7 @@ module Epuber
55
54
  builder = Nokogiri::XML::Builder.new(encoding: 'utf-8') do |xml|
56
55
  @xml = xml
57
56
 
58
- block.call(xml) unless block.nil?
57
+ block&.call(xml)
59
58
 
60
59
  @xml = nil
61
60
  end
@@ -15,7 +15,7 @@ module Epuber
15
15
  generate_xml do |xml|
16
16
  xml.container(version: 1.0, xmlns: 'urn:oasis:names:tc:opendocument:xmlns:container') do
17
17
  xml.rootfiles do
18
- @file_resolver.package_files.select { |file| file.kind_of?(FileTypes::OPFFile) }.each do |file|
18
+ @file_resolver.package_files.select { |file| file.is_a?(FileTypes::OPFFile) }.each do |file|
19
19
  path = file.pkg_destination_path
20
20
  xml.rootfile('full-path' => path, 'media-type' => MIME::Types.of(path).first.content_type)
21
21
  end
@@ -9,7 +9,6 @@ module Epuber
9
9
  require_relative 'generator'
10
10
 
11
11
  class NavGenerator < Generator
12
-
13
12
  NCX_NAMESPACES = {
14
13
  'xmlns' => 'http://www.daisy.org/z3986/2005/ncx/',
15
14
  }.freeze
@@ -26,10 +25,10 @@ module Epuber
26
25
 
27
26
  # resource page http://www.idpf.org/epub/301/spec/epub-contentdocs.html#sec-xhtml-nav-def-types-landmarks
28
27
  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' },
28
+ landmark_cover: { type: 'cover', text: 'Cover page' },
29
+ landmark_start_page: { type: %w[bodymatter ibooks:reader-start-page], text: 'Start Reading' },
30
+ landmark_copyright: { type: 'copyright-page', text: 'Copyright page' },
31
+ landmark_toc: { type: 'toc', text: 'Table of contents' },
33
32
  }.freeze
34
33
 
35
34
  # Generates XML for toc document, the structure differs depend on epub_version
@@ -121,7 +120,7 @@ module Epuber
121
120
  end
122
121
  end
123
122
 
124
- if @target.epub_version >= 3 && toc_items.length > 0 && contains_item_with_title(toc_items)
123
+ if @target.epub_version >= 3 && toc_items.length.positive? && contains_item_with_title(toc_items)
125
124
  @xml.ol do
126
125
  iterate_lambda.call
127
126
  end