Created
June 26, 2025 03:08
-
-
Save ronaldtse/f878ebca1b9a4c4e52851fc00509c75c to your computer and use it in GitHub Desktop.
Metanorma script that fixes Lucid / LucidChart SVG files that have `<use>` elements reference invalid IDs, see https://community.lucid.co/lucid-for-engineering-19/svg-export-results-in-invalid-idref-on-a-use-element-8050
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env ruby | |
| # This script is developed for Metanorma users that need to fix Lucid-generated | |
| # SVGs. Lucid-generated SVGs have `<use>` elements reference invalid IDs, which | |
| # result in invalid SVG XML. Metanorma PDFs and Word outputs rely on valid SVG | |
| # XML files otherwise the PDF output will omit broken SVGs, and the Word output | |
| # will have EMF files that have areas of the figures appear in black. | |
| # | |
| # The issue is described at: | |
| # https://community.lucid.co/lucid-for-engineering-19/svg-export-results-in-invalid-idref-on-a-use-element-8050 | |
| # | |
| # As of 2025-06-26, this issue is not fixed. | |
| require 'thor' | |
| require 'nokogiri' | |
| require 'fileutils' | |
| require 'set' | |
| class SvgLucidFix < Thor | |
| # Fix Thor deprecation warning | |
| def self.exit_on_failure? | |
| true | |
| end | |
| # Make fix the default command | |
| default_command :fix | |
| desc "fix PATH", "Fix SVG files by removing use elements with invalid IDREF" | |
| method_option :inplace, type: :boolean, default: false, aliases: "-i", | |
| desc: "Modify files in place" | |
| method_option :output, type: :string, aliases: "-o", | |
| desc: "Output directory for modified files (required if not using --inplace)" | |
| def fix(path_pattern) | |
| files = Dir.glob(path_pattern) | |
| if files.empty? | |
| puts "No files found matching pattern: #{path_pattern}" | |
| exit 1 | |
| end | |
| unless options[:inplace] || options[:output] | |
| puts "Error: Either --inplace or --output must be specified" | |
| exit 1 | |
| end | |
| if options[:output] && !options[:inplace] | |
| unless Dir.exist?(options[:output]) | |
| begin | |
| FileUtils.mkdir_p(options[:output]) | |
| puts "Created output directory: #{options[:output]}" | |
| rescue => e | |
| puts "Error creating output directory: #{e.message}" | |
| exit 1 | |
| end | |
| end | |
| end | |
| files.each do |file| | |
| process_svg_file(file) | |
| end | |
| end | |
| private | |
| def process_svg_file(file) | |
| begin | |
| # Parse the SVG file | |
| doc = File.open(file) { |f| Nokogiri::XML(f) } | |
| # Find all elements with ID attributes | |
| existing_ids = Set.new | |
| doc.xpath('//*[@id]').each do |element| | |
| existing_ids.add(element['id']) | |
| end | |
| # Find all use elements with xlink:href attributes | |
| invalid_use_elements = [] | |
| doc.xpath('//svg:use', 'svg' => 'http://www.w3.org/2000/svg').each do |use_element| | |
| href = use_element['xlink:href'] || use_element['href'] | |
| next unless href | |
| # Extract the ID from the href (removing the # prefix) | |
| id_ref = href.sub(/^#/, '') | |
| # Check if the ID exists in the document | |
| unless existing_ids.include?(id_ref) | |
| invalid_use_elements << use_element | |
| end | |
| end | |
| # Remove invalid use elements | |
| if invalid_use_elements.any? | |
| invalid_use_elements.each(&:remove) | |
| puts "Fixed #{file}: Removed #{invalid_use_elements.size} invalid use elements" | |
| # Save the modified SVG | |
| if options[:inplace] | |
| output_path = file | |
| else | |
| output_path = File.join(options[:output], File.basename(file)) | |
| end | |
| File.write(output_path, doc.to_xml) | |
| else | |
| puts "No invalid use elements found in #{file}" | |
| end | |
| rescue => e | |
| puts "Error processing #{file}: #{e.message}" | |
| end | |
| end | |
| end | |
| # Allow the script to be run with bundle exec | |
| if $0 == __FILE__ | |
| SvgLucidFix.start(ARGV) | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment