0

I have multiple SVG files (such as https://gist.github.com/matkoniecz/8e71fcfabcc52818ed47a430f23ca88b )

All of them have the same height, width, viewbox etc. I want to combine them into one file.

Doing it manually is fairly simple, see https://answers.launchpad.net/inkscape/+question/267743

The tricky part is that I want to automate it due to large number of files and frequent repetitions (every time I tweak something).


To avoid XY problem: I am generating (using mapshaper) several SVG files with labels at specific locations.

I also generate with mapshaper a background map.

I want to merge this vector data into one file, for use in laser cutter.

7
  • SVGs are just text files. Theoretically you could just copy the SVG code for the paths, and paste them into another SVG using a plain text editor. – Billy Kerr Feb 25 at 13:32
  • From saner things that I tried: inkscape -f a.svg k.svg --export-text-to-path --export-plain-svg out.svg exports just last file, k.svg. – reducing activity Feb 25 at 13:43
  • @BillyKerr I know, but SVG is relatively complex format. I have some horrific hack that relies on stripping prefix and suffix, merging everything, opening that in inkscape (in headless mode, automatically) and exporting in hope that Inkscape fixes this mess. Right now it works, but only by accident and sometimes silently corrupts data. Yes, processing SVG is only text processing, but plenty of horribly complicated things is also "just text files". – reducing activity Feb 25 at 13:50
  • SVGstore might be what you need. It takes a folder of SVGs and combines them into a single file. It's a node.js package and is also available in versions for Grunt and Gulp usage. npmjs.com/package/svgstore – Mysterfxit Feb 26 at 3:30
  • You can use the Inkscape editor for this. – elika kohen Mar 8 at 21:32
1

I can't really answer the automation part of this, and I suspect it would involve coding it in some way, which is way beyond my abilities.

However, to get you on the right track, SVGs are just text files. Theoretically, you could just copy the SVG code for the paths/objects, then paste them into another SVG file using a plain text editor.

I use Notepad++ for things like this. In the example below, I have two SVG files open, one contains a circle, and the other a rectangle.

In this example, I'm copying and pasting the SVG circle code into the other SVG which contains a rectangle.

enter image description here

If you have more complex objects to copy over, you need to select everything between the <SVG ....> and </SVG> tags, and paste it into the other SVG file, just before the closing tag.

5
  • Thanks! If noone will have a better idea I will upgrade from "throw away two first lines except the first file" + "throw away two last lines except last file" to doing something like that automatically. This at least will not crash if svg file is without newlines, everything in one line. – reducing activity Feb 25 at 13:52
  • @reducingactivity If you are going to find some way of automating this, you will probably need a solution which can find/locate the tags, specifically the <SVG> and </SVG>, copy everything bewteen those, and paste everything just before the </SVG> tag. – Billy Kerr Feb 25 at 13:55
  • Probably some XML parser, as SVG is subset of XML (it is, right?). – reducing activity Feb 25 at 14:15
  • @reducingactivity Yes, SVG is a subset of XML – Billy Kerr Feb 25 at 14:22
  • I always wondered what kind of monster uses a white theme in a code editor... – Joonas Mar 8 at 7:49
0

I used following Ruby code:

def merge_svg_files(list_of_svg_filepaths, output_svg_filepath)
  svg_data = []
  list_of_svg_filepaths.each do |filepath|
    assert_that_file_exists(filepath)
    svg_data << {'svg' => File.open(filepath).read, 'identifier' => filepath }
  end
  return merge_svg_documents(svg_data, output_svg_filepath)
end

def parse_svg(text)
  return parse_xml(text)
end

def merge_svg_documents(list_of_svg_data, output_svg_filepath)
  svg_merged = parse_svg(list_of_svg_data[0]['svg'])
  svg_merged.remove_namespaces!
  merge_into = get_svg_element(svg_merged, list_of_svg_data[0]['identifier'])

  list_of_svg_data[1..].each do |entry|
    svg_parsed = parse_svg(entry['svg'])
    raise svg_parsed.errors if svg_merged.errors != []
    svg_parsed.remove_namespaces!
    svg = get_svg_element(svg_parsed, entry['identifier'])
    svg.element_children.each do |element|
      element.parent = merge_into
    end
  end
  merge_into["xmlns"] = "http://www.w3.org/2000/svg"
  File.open(output_svg_filepath, 'w') do |file|
    file.puts svg_merged.to_xml(indent:3, indent_text:" ")
  end
  assert_that_file_exists(output_svg_filepath)
end

# see https://github.com/sparklemotion/nokogiri/issues/2201
def parse_xml(text)
    # noblanks - removes spaces
    # huge - blocks data corruption on huge lines (10MB+, for example detailed European coastline SVG)
    # norecover - avoids silent failure
    return Nokogiri::XML(text) { |config| config.noblanks.huge.norecover }
end

In case that someone wants to use it, I release it as CC0.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.