Class: Vectory::SvgDocument

Inherits:
Object
  • Object
show all
Defined in:
lib/vectory/svg_document.rb

Overview

Represents and processes an SVG document within XML.

SvgDocument handles SVG ID suffixing for uniqueness in multi-document and multi-svgmap scenarios. It applies two types of suffixes:

  1. **ID suffix** - Derived from document/container identity Provides cross-document uniqueness

  2. **Index suffix** - Derived from svgmap position Provides multi-svgmap uniqueness within a document

Final ID format: <original_id><id_suffix><index_suffix> Example: fig1 + _ISO_17301-1_2016 + _000000000 = fig1_ISO_17301-1_2016_000000000

Constant Summary collapse

SVG_NS =
"http://www.w3.org/2000/svg".freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(content) ⇒ SvgDocument

Creates a new SvgDocument from SVG content.

Parameters:

  • content (String)

    SVG XML content



78
79
80
# File 'lib/vectory/svg_document.rb', line 78

def initialize(content)
  @document = Nokogiri::XML(content)
end

Class Method Details

.update_ids_attrs(document, ids, suffix) ⇒ void

This method returns an undefined value.

Updates ID attributes in SVG elements.

Parameters:

  • document (Nokogiri::XML::Document)

    The SVG document

  • ids (Array<String>)

    IDs to update

  • suffix (Integer, String)

    The suffix to apply (integers are zero-padded)



63
64
65
66
67
68
69
70
71
72
# File 'lib/vectory/svg_document.rb', line 63

def update_ids_attrs(document, ids, suffix)
  suffix = suffix.is_a?(Integer) ? sprintf("%09d", suffix) : suffix
  document.xpath(". | .//*[@*]").each do |a|
    a.attribute_nodes.each do |x|
      val = x.value.sub(/^#/, "")
      ids.include?(val) and x.value += "_#{suffix}"
      x.value = x.value.sub(%r{url\(#([^()]+)\)}, "url(#\\1_#{suffix})")
    end
  end
end

.update_ids_css(document, ids, suffix) ⇒ void

This method returns an undefined value.

Updates ID references in CSS style statements within a document.

Parameters:

  • document (Nokogiri::XML::Document)

    The SVG document

  • ids (Array<String>)

    IDs to update

  • suffix (Integer, String)

    The suffix to apply (integers are zero-padded)



28
29
30
31
32
33
34
35
36
37
# File 'lib/vectory/svg_document.rb', line 28

def update_ids_css(document, ids, suffix)
  suffix = suffix.is_a?(Integer) ? sprintf("%09d", suffix) : suffix
  document.xpath(".//m:style", "m" => SVG_NS).each do |s|
    c = s.children.to_xml
    s.children = update_ids_css_string(c, ids, suffix)
  end
  document.xpath(".//*[@style]").each do |s|
    s["style"] = update_ids_css_string(s["style"], ids, suffix)
  end
end

.update_ids_css_string(style, ids, suffix) ⇒ String

Updates ID references in a CSS style string.

Parameters:

  • style (String)

    The CSS style content

  • ids (Array<String>)

    IDs to update

  • suffix (Integer, String)

    The suffix to apply (integers are zero-padded)

Returns:

  • (String)

    Updated style content



45
46
47
48
49
50
51
52
53
54
55
# File 'lib/vectory/svg_document.rb', line 45

def update_ids_css_string(style, ids, suffix)
  ids.each do |i|
    style = style.gsub(%r[##{i}\b],
                       sprintf("#%<id>s_%<suffix>s", id: i,
                                                     suffix: suffix))
      .gsub(%r(\[id\s*=\s*['"]?#{i}['"]?\]),
            sprintf("[id='%<id>s_%<suffix>s']", id: i,
                                                suffix: suffix))
  end
  style
end

Instance Method Details

#apply_id_suffix(id_suffix) ⇒ self

Applies an ID suffix to all SVG IDs for cross-document uniqueness.

This is called BEFORE index-based suffixing.

Parameters:

  • id_suffix (String)

    The suffix to apply (e.g., “_ISO_17301-1_2016”)

Returns:

  • (self)

    The document



143
144
145
146
147
148
149
150
151
# File 'lib/vectory/svg_document.rb', line 143

def apply_id_suffix(id_suffix)
  ids = collect_ids
  return if ids.empty?

  self.class.update_ids_attrs(@document.root, ids, id_suffix)
  self.class.update_ids_css(@document.root, ids, id_suffix)

  self
end

#apply_index_suffix(index) ⇒ self

Applies an index-based suffix to all SVG IDs.

The index is converted to a 9-digit zero-padded string. This is called AFTER ID-based suffixing.

Parameters:

  • index (Integer)

    The index suffix (converted to “_000000000” format)

Returns:

  • (self)

    The document



160
161
162
163
164
165
166
167
168
# File 'lib/vectory/svg_document.rb', line 160

def apply_index_suffix(index)
  ids = collect_ids
  return if ids.empty?

  self.class.update_ids_attrs(@document.root, ids, index)
  self.class.update_ids_css(@document.root, ids, index)

  self
end

#contentString

Returns the processed SVG content as XML string.

Returns:

  • (String)

    SVG XML content



85
86
87
# File 'lib/vectory/svg_document.rb', line 85

def content
  @document.root.to_xml
end

#namespace(index_suffix, links, xpath_to_remove, id_suffix: nil) ⇒ self

Applies namespace transformations to the SVG document.

This method performs the following operations in order:

  1. Remap internal links to their targets

  2. Apply ID suffix (if provided) for cross-document uniqueness

  3. Apply index suffix for multi-svgmap uniqueness

  4. Remove processing instructions

Parameters:

  • index_suffix (Integer)

    The position-based suffix (0, 1, 2…) Converted to 9-digit zero-padded string (e.g., “_000000000”)

  • links (Hash{String => String})

    Mapping of link hrefs to their targets

  • xpath_to_remove (String)

    XPath for elements to remove (processing instructions)

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

    Optional suffix derived from document/container identity. Applied BEFORE index_suffix for two-stage disambiguation. Example: “_ISO_17301-1_2016”

Returns:

  • (self)

    The processed document



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/vectory/svg_document.rb', line 106

def namespace(index_suffix, links, xpath_to_remove, id_suffix: nil)
  remap_links(links)

  # Apply ID suffix first (cross-document uniqueness)
  # Then apply index suffix (multi-svgmap uniqueness)
  # Final format: <original_id><id_suffix><index_suffix>
  if id_suffix
    apply_id_suffix(id_suffix)
  end

  apply_index_suffix(index_suffix)
  remove_xpath(xpath_to_remove)

  self
end

Remaps internal SVG links to their target definitions.

Parameters:

  • map (Hash{String => String})

    Mapping of expanded hrefs to targets

Returns:

  • (self)

    The document



126
127
128
129
130
131
132
133
134
135
# File 'lib/vectory/svg_document.rb', line 126

def remap_links(map)
  @document.xpath(".//m:a", "m" => SVG_NS).each do |a|
    href_attrs = ["xlink:href", "href"]
    href_attrs.each do |p|
      a[p] and x = map[File.expand_path(a[p])] and a[p] = x
    end
  end

  self
end

#remove_xpath(xpath) ⇒ self

Removes elements matching the given XPath.

Parameters:

  • xpath (String)

    XPath expression for elements to remove

Returns:

  • (self)

    The document



185
186
187
188
189
# File 'lib/vectory/svg_document.rb', line 185

def remove_xpath(xpath)
  @document.xpath(xpath).remove

  self
end

#suffix_ids(suffix) ⇒ self

Deprecated.

Use #apply_index_suffix instead for clarity.

Applies an index-based suffix to all SVG IDs.

Maintained for backward compatibility.

Parameters:

  • suffix (Integer, String)

    The suffix to apply (integers are zero-padded)

Returns:

  • (self)

    The document



177
178
179
# File 'lib/vectory/svg_document.rb', line 177

def suffix_ids(suffix)
  apply_index_suffix(suffix)
end