Class: Epuber::Compiler::XHTMLProcessor
- Inherits:
-
Object
- Object
- Epuber::Compiler::XHTMLProcessor
- Defined in:
- lib/epuber/compiler/xhtml_processor.rb
Defined Under Namespace
Classes: UnparseableLinkError
Class Method Summary collapse
-
.add_missing_root_elements(xhtml_doc, title, epub_version) ⇒ Object
Method to add all missing items in XML root.
-
.add_scripts(xhtml_doc, scripts) ⇒ Object
Method for adding scripts with links, method will not add duplicate items.
-
.add_styles(xhtml_doc, styles) ⇒ Object
Method for adding style sheets with links, method will not add duplicate items.
-
.add_viewport(xhtml_doc, viewport_size) ⇒ Object
Adds viewport meta tag to head of some document, but only if there is not some existing tag.
-
.resolve_images(xhtml_doc, file_path, file_resolver) ⇒ Object
Nil.
-
.resolve_links(xhtml_doc, file_path, file_finder) ⇒ Array<URI>
Resolves all links to files in XHTML document and returns the valid and resolved versions.
-
.resolve_links_for(xhtml_doc, tag_name, attribute_name, groups, file_path, file_finder) ⇒ Array<URI>
Resolves all links to files in XHTML document and returns the valid and resolved versions.
- .resolve_mathml_namespace(xhtml_doc) ⇒ Object
- .resolve_resources_in(node_css_query, attribute_name, resource_group, xhtml_doc, file_path, file_resolver) ⇒ Object
-
.resolve_scripts(xhtml_doc, file_path, file_resolver) ⇒ Object
Nil.
-
.resolve_stylesheets(xhtml_doc, file_path, file_resolver) ⇒ Object
Nil.
-
.resolved_link_to_file(path, groups, file_path, file_finder) ⇒ URI
Method which will resolve path to file from pattern.
- .using_javascript?(xhtml_doc) ⇒ Bool
- .using_mathml?(xhtml_doc) ⇒ Bool
- .using_remote_resources?(xhtml_doc) ⇒ Boolean
-
.xml_document_from_string(text, file_path = nil) ⇒ Nokogiri::XML::Document
Method for parsing incomplete XML, supports multiple root elements.
Class Method Details
.add_missing_root_elements(xhtml_doc, title, epub_version) ⇒ Object
Method to add all missing items in XML root
Required items:
- html (with all namespaces and other attributes)
- body
- head (with title)
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/epuber/compiler/xhtml_processor.rb', line 83 def self.add_missing_root_elements(xhtml_doc, title, epub_version) # add missing body element if xhtml_doc.at_css('body').nil? xhtml_doc.root.surround_with_element('body') end # add missing root html element if xhtml_doc.at_css('html').nil? attrs = {} attrs['xmlns'] = 'http://www.w3.org/1999/xhtml' attrs['xmlns:epub'] = 'http://www.idpf.org/2007/ops' if epub_version >= 3 xhtml_doc.root.surround_with_element('html', attrs) end # add missing head in html if xhtml_doc.at_css('html > head').nil? html = xhtml_doc.css('html').first head = xhtml_doc.create_element('head') head << xhtml_doc.create_element('title', title) head << xhtml_doc.create_element('meta', charset: 'utf-8') if epub_version >= 3.0 html.children.first.before(head) end # https://github.com/IDPF/epubcheck/issues/631 if epub_version < 3.0 xhtml_doc.internal_subset.remove unless xhtml_doc.internal_subset.nil? xhtml_doc.create_internal_subset('html', "-//W3C//DTD XHTML 1.1//EN", "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd") end end |
.add_scripts(xhtml_doc, scripts) ⇒ Object
Method for adding scripts with links, method will not add duplicate items
139 140 141 142 143 144 145 146 147 148 |
# File 'lib/epuber/compiler/xhtml_processor.rb', line 139 def self.add_scripts(xhtml_doc, scripts) head = xhtml_doc.at_css('html > head') old_links = head.css('script').map { |node| node['src'] } links_to_add = scripts - old_links links_to_add.each do |path| head << xhtml_doc.create_element('script', src: path, type: 'text/javascript') end end |
.add_styles(xhtml_doc, styles) ⇒ Object
Method for adding style sheets with links, method will not add duplicate items
121 122 123 124 125 126 127 128 129 130 |
# File 'lib/epuber/compiler/xhtml_processor.rb', line 121 def self.add_styles(xhtml_doc, styles) head = xhtml_doc.at_css('html > head') old_links = head.css('link[rel="stylesheet"]').map { |node| node['href'] } links_to_add = styles - old_links links_to_add.each do |path| head << xhtml_doc.create_element('link', href: path, rel: 'stylesheet', type: 'text/css') end end |
.add_viewport(xhtml_doc, viewport_size) ⇒ Object
Adds viewport meta tag to head of some document, but only if there is not some existing tag
155 156 157 158 159 160 161 |
# File 'lib/epuber/compiler/xhtml_processor.rb', line 155 def self.(xhtml_doc, ) head = xhtml_doc.at_css('html > head') return unless head.at_css("meta[name='viewport']").nil? s = head << xhtml_doc.create_element('meta', name: 'viewport', content: "width=#{s.width},height=#{s.height}") end |
.resolve_images(xhtml_doc, file_path, file_resolver) ⇒ Object
Returns nil.
287 288 289 |
# File 'lib/epuber/compiler/xhtml_processor.rb', line 287 def self.resolve_images(xhtml_doc, file_path, file_resolver) resolve_resources_in('img', 'src', :image, xhtml_doc, file_path, file_resolver) end |
.resolve_links(xhtml_doc, file_path, file_finder) ⇒ Array<URI>
Resolves all links to files in XHTML document and returns the valid and resolved versions
243 244 245 246 247 248 |
# File 'lib/epuber/compiler/xhtml_processor.rb', line 243 def self.resolve_links(xhtml_doc, file_path, file_finder) [ resolve_links_for(xhtml_doc, 'a', 'href', :text, file_path, file_finder), resolve_links_for(xhtml_doc, 'map > area', 'href', :text, file_path, file_finder), ].flatten end |
.resolve_links_for(xhtml_doc, tag_name, attribute_name, groups, file_path, file_finder) ⇒ Array<URI>
Resolves all links to files in XHTML document and returns the valid and resolved versions
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/epuber/compiler/xhtml_processor.rb', line 210 def self.resolve_links_for(xhtml_doc, tag_name, attribute_name, groups, file_path, file_finder) founded_links = [] xhtml_doc.css("#{tag_name}[#{attribute_name}]").each do |node| begin src = node[attribute_name] # @type [String] src next if src.nil? target_file = resolved_link_to_file(src, groups, file_path, file_finder) founded_links << target_file node[attribute_name] = target_file.to_s rescue UnparseableLinkError, FileFinders::FileNotFoundError, FileFinders::MultipleFilesFoundError => e UI.warning(e.to_s, location: node) # skip not found files next end end founded_links end |
.resolve_mathml_namespace(xhtml_doc) ⇒ Object
275 276 277 278 279 |
# File 'lib/epuber/compiler/xhtml_processor.rb', line 275 def self.resolve_mathml_namespace(xhtml_doc) xhtml_doc.css('math').each do |math_node| math_node.add_namespace('xmlns', 'http://www.w3.org/1998/Math/MathML') end end |
.resolve_resources_in(node_css_query, attribute_name, resource_group, xhtml_doc, file_path, file_resolver) ⇒ Object
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 |
# File 'lib/epuber/compiler/xhtml_processor.rb', line 311 def self.resolve_resources_in(node_css_query, attribute_name, resource_group, xhtml_doc, file_path, file_resolver) dirname = File.dirname(file_path) xhtml_doc.css(node_css_query).each do |img| path = img[attribute_name] next if path.nil? begin new_path = file_resolver.dest_finder.find_file(path, groups: resource_group, context_path: dirname) rescue UnparseableLinkError, FileFinders::FileNotFoundError, FileFinders::MultipleFilesFoundError begin new_path = resolved_link_to_file(path, resource_group, dirname, file_resolver.source_finder).to_s pkg_abs_path = File.(new_path, dirname).unicode_normalize pkg_new_path = Pathname.new(pkg_abs_path).relative_path_from(Pathname.new(file_resolver.source_path)).to_s file_class = FileResolver.file_class_for(File.extname(new_path)) file = file_class.new(pkg_new_path) file.path_type = :manifest file_resolver.add_file(file) new_path = FileResolver::renamed_file_with_path(new_path) rescue UnparseableLinkError, FileFinders::FileNotFoundError, FileFinders::MultipleFilesFoundError => e UI.warning(e.to_s, location: img) next end end img[attribute_name] = new_path end end |
.resolve_scripts(xhtml_doc, file_path, file_resolver) ⇒ Object
Returns nil.
297 298 299 |
# File 'lib/epuber/compiler/xhtml_processor.rb', line 297 def self.resolve_scripts(xhtml_doc, file_path, file_resolver) resolve_resources_in('script', 'src', :script, xhtml_doc, file_path, file_resolver) end |
.resolve_stylesheets(xhtml_doc, file_path, file_resolver) ⇒ Object
Returns nil.
307 308 309 |
# File 'lib/epuber/compiler/xhtml_processor.rb', line 307 def self.resolve_stylesheets(xhtml_doc, file_path, file_resolver) resolve_resources_in('link[rel="stylesheet"]', 'href', :style, xhtml_doc, file_path, file_resolver) end |
.resolved_link_to_file(path, groups, file_path, file_finder) ⇒ URI
Method which will resolve path to file from pattern
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/epuber/compiler/xhtml_processor.rb', line 174 def self.resolved_link_to_file(path, groups, file_path, file_finder) raise FileFinders::FileNotFoundError.new(path, file_path) if path.empty? begin uri = URI(path) rescue URI::InvalidURIError begin uri = URI(URI::encode(path)) rescue URI::InvalidURIError # skip not valid uri raise UnparseableLinkError, "Unparseable link `#{path}`" end end # skip uri with scheme (links to web pages) return uri unless uri.scheme.nil? # skip empty path return uri if uri.path.empty? && !uri.fragment.nil? && !uri.fragment.empty? uri.path = file_finder.find_file(uri.path, groups: groups, context_path: file_path) uri end |
.using_javascript?(xhtml_doc) ⇒ Bool
254 255 256 |
# File 'lib/epuber/compiler/xhtml_processor.rb', line 254 def self.using_javascript?(xhtml_doc) !xhtml_doc.at_css('script').nil? end |
.using_mathml?(xhtml_doc) ⇒ Bool
271 272 273 |
# File 'lib/epuber/compiler/xhtml_processor.rb', line 271 def self.using_mathml?(xhtml_doc) !xhtml_doc.at_css('math|math', 'math' => 'http://www.w3.org/1998/Math/MathML').nil? end |
.using_remote_resources?(xhtml_doc) ⇒ Boolean
258 259 260 261 262 263 264 265 |
# File 'lib/epuber/compiler/xhtml_processor.rb', line 258 def self.using_remote_resources?(xhtml_doc) regexp = %r{^[^:/?#]+://.*} result = false result ||= xhtml_doc.css('[src]').any? { |node| node['src'] =~ regexp } result ||= xhtml_doc.css('link[href]').any? { |node| node['href'] =~ regexp } result end |
.xml_document_from_string(text, file_path = nil) ⇒ Nokogiri::XML::Document
Method for parsing incomplete XML, supports multiple root elements
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/epuber/compiler/xhtml_processor.rb', line 22 def self.xml_document_from_string(text, file_path = nil) if /\A[\n\r ]+(<\?xml)/ =~ text UI.warning('XML header must be at the beginning of document', location: UI::Location.new(file_path, 1)) text = text.lstrip end xml_header = '' if /\A\s*(<\?xml[^>]*\?>)/ =~ text match = Regexp.last_match xml_header = text[match.begin(1)...match.end(1)] text[match.begin(1)...match.end(1)] = '' end doctypes = [] while /(\n|\?>|\A)?(<!DOCTYPE [^>]*>\n*)/ =~ text doctypes << $2.strip match = Regexp.last_match text[match.begin(2)...match.end(2)] = '' end before = ([xml_header] + doctypes).compact.join("\n") unless before.empty? before = before + "\n" end = Nokogiri::XML::ParseOptions::DEFAULT_XML | Nokogiri::XML::ParseOptions::NOERROR | # to silence any errors or warnings printing into console Nokogiri::XML::ParseOptions::NOWARNING doc = Nokogiri::XML("#{before}<root>#{text}</root>", nil, nil, ) doc.encoding = 'UTF-8' doc.file_path = file_path root = root_node = doc.root root_elements = root.children.select { |a| a.element? || a.comment? } if root_elements.count == 1 doc.root = root_elements.first elsif root_node.at_css('body').nil? root_node.node_name = 'body' end doc end |