Class: Sevgi::Derender::Document

Inherits:
Object
  • Object
show all
Defined in:
lib/sevgi/derender/document.rb

Overview

Parsed SVG/XML document wrapper used by the derender pipeline.

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(content) { ... } ⇒ void

Builds a parsed document wrapper from SVG/XML content.

Parameters:

  • content (String)

    SVG/XML source content

Yields:

  • optional initializer used by load_file to install cached parse state

Yield Returns:

  • (void)


60
61
62
63
64
65
# File 'lib/sevgi/derender/document.rb', line 60

def initialize(content, &block)
  instance_exec(&block) if block

  @doc ||= self.class.parse(content)
  @decl = self.class.declaration(content)
end

Class Attribute Details

.cacheHash{String => Nokogiri::XML::Document} (readonly)

Returns parsed document cache keyed by expanded file path.

Returns:

  • (Hash{String => Nokogiri::XML::Document})

    parsed document cache keyed by expanded file path



13
14
15
# File 'lib/sevgi/derender/document.rb', line 13

def cache
  @cache
end

Instance Attribute Details

#declObject (readonly)

Returns the value of attribute decl.



53
# File 'lib/sevgi/derender/document.rb', line 53

attr_reader :doc, :decl

#docNokogiri::XML::Document (readonly)

Returns parsed XML document.

Returns:

  • (Nokogiri::XML::Document)

    parsed XML document



53
54
55
# File 'lib/sevgi/derender/document.rb', line 53

def doc
  @doc
end

Class Method Details

.declaration(content) ⇒ String?

Extracts the XML declaration from SVG/XML content.

Parameters:

  • content (String)

    SVG/XML source content

Returns:

  • (String, nil)

    XML declaration line, if present



43
44
45
46
47
# File 'lib/sevgi/derender/document.rb', line 43

def self.declaration(content)
  return unless (content = content.lstrip).start_with?("<?xml ")

  content.split("\n").first
end

.load_file(path) ⇒ Sevgi::Derender::Document

Loads and parses an SVG/XML file, using the parse cache when possible.

Parameters:

  • path (String)

    path to the source file, with or without .svg extension

Returns:

Raises:

  • (Sevgi::ArgumentError)

    when the file cannot be found

  • (Errno::EACCES)

    when the file cannot be read



21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/sevgi/derender/document.rb', line 21

def self.load_file(path)
  entry = ::File.expand_path(F.qualify(path, "svg"))

  ArgumentError.("File not found: #{path}") unless ::File.exist?(entry)

  content = ::File.read(entry)
  new(content) do
    @doc = self.class.cache[entry] ||
      begin
        self.class.cache[entry] = self.class.parse(content)
      end
  end
end

.parse(content) ⇒ Nokogiri::XML::Document

Parses SVG/XML content.

Parameters:

  • content (String)

    SVG/XML source content

Returns:

  • (Nokogiri::XML::Document)

    parsed XML document



38
# File 'lib/sevgi/derender/document.rb', line 38

def self.parse(content) = Nokogiri::XML(content)

Instance Method Details

#decompile(id = nil) ⇒ Sevgi::Derender::Node

Converts the root or selected node into a derender node.

Parameters:

  • id (String, nil) (defaults to: nil)

    optional SVG id selecting a node inside the document

Returns:

Raises:

  • (Sevgi::ArgumentError)

    when the id is absent



71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/sevgi/derender/document.rb', line 71

def decompile(id = nil)
  if id
    if (found = doc.xpath("//*[@id=#{xpath_literal(id)}]") || []).empty?
      ArgumentError.("No such element with id '#{id}' in document")
    end

    found.first
  else
    doc.root
  end => element

  Node.new(element, pres)
end

#presArray<String>

Returns XML declaration and pre-root nodes preserved for root decompilation.

Returns:

  • (Array<String>)

    preamble XML lines



87
88
89
90
91
92
# File 'lib/sevgi/derender/document.rb', line 87

def pres
  @pres ||= [].tap do |lines|
    lines.append(*doc.children.take_while { |node| node != doc.root }.map(&:to_xml))
    lines.unshift(decl) unless lines.first == decl
  end
end