Module: Emfsvg::Compat

Defined in:
lib/emfsvg/compat.rb

Defined Under Namespace

Classes: Result

Constant Summary collapse

REF_BINARY_DEFAULT =
"/Users/mulgogi/src/claricle/libemf2svg/emf2svg_ref"
REF_LIB_DIR_DEFAULT =
"/Users/mulgogi/src/claricle/libemf2svg/build"

Class Method Summary collapse

Class Method Details

.compare_glob(fixtures_glob) ⇒ Object

Walk a fixtures glob, produce both outputs, return Result array.



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/emfsvg/compat.rb', line 98

def compare_glob(fixtures_glob)
  results = []
  Dir.glob(fixtures_glob).each do |emf|
    ref_svg = generate_reference(emf)
    next unless ref_svg

    emfsvg_svg = begin
      generate_emfsvg(emf)
    rescue StandardError
      nil
    end
    next unless emfsvg_svg

    results << Result.new(
      fixture: File.basename(emf),
      ref_bytes: ref_svg.bytesize,
      emfsvg_bytes: emfsvg_svg.bytesize,
      ref_elements: count_elements(parse_svg(ref_svg)),
      emfsvg_elements: count_elements(parse_svg(emfsvg_svg))
    )
  end
  results
end

.count_elements(svg_root) ⇒ Object

Count drawing elements in an SVG. Returns a hash like { "rect" => 3, "path" => 17, "ellipse" => 2 }.



66
67
68
69
70
71
72
73
74
# File 'lib/emfsvg/compat.rb', line 66

def count_elements(svg_root)
  counts = Hash.new(0)
  return counts unless svg_root

  REXML::XPath.each(svg_root, "descendant-or-self::*") do |el|
    counts[el.name] += 1
  end
  counts
end

.generate_emfsvg(emf_path, options = {}) ⇒ Object

Generate emfsvg output. Requires the emfsvg gem to be loaded.



51
52
53
# File 'lib/emfsvg/compat.rb', line 51

def generate_emfsvg(emf_path, options = {})
  Emfsvg.from_file(emf_path, **options)
end

.generate_reference(emf_path, output_path = nil) ⇒ Object

Generate a reference SVG using libemf2svg. Returns the SVG bytes, or nil if generation failed.



37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/emfsvg/compat.rb', line 37

def generate_reference(emf_path, output_path = nil)
  binary = ENV.fetch("EMF2SVG_REF_PATH", REF_BINARY_DEFAULT)
  lib_dir = ENV.fetch("EMF2SVG_REF_LIB_DIR", REF_LIB_DIR_DEFAULT)
  env = { "DYLD_LIBRARY_PATH" => lib_dir }
  output_path ||= "#{Dir.mktmpdir}/#{File.basename(emf_path, '.emf')}.svg"
  _, _, status = Open3.capture3(env, binary, emf_path, output_path)
  return nil unless status.success?

  File.binread(output_path)
ensure
  File.delete(output_path) if output_path && !block_given?
end

.parse_svg(svg_string) ⇒ Object

Normalise an SVG document for structural comparison:

  • parse to REXML
  • return the root element


58
59
60
61
62
# File 'lib/emfsvg/compat.rb', line 58

def parse_svg(svg_string)
  REXML::Document.new(svg_string).root
rescue REXML::ParseException
  nil
end

.ref_binary_available?Boolean

Returns:

  • (Boolean)


30
31
32
33
# File 'lib/emfsvg/compat.rb', line 30

def ref_binary_available?
  path = ENV.fetch("EMF2SVG_REF_PATH", REF_BINARY_DEFAULT)
  File.executable?(path)
end

.summarize(results) ⇒ Object

Summary across many results.



123
124
125
126
127
128
129
130
131
132
# File 'lib/emfsvg/compat.rb', line 123

def summarize(results)
  return "no results" if results.empty?

  total_ref = results.sum(&:ref_bytes)
  total_out = results.sum(&:emfsvg_bytes)
  ratio = total_ref.zero? ? 0 : (total_out.to_f / total_ref * 100).round(1)
  avg_overlap = results.sum(&:element_overlap) / results.size
  format("fixtures=%d  total_ref=%d  total_out=%d  byte_ratio=%.1f%%  avg_element_overlap=%.1f%%",
         results.size, total_ref, total_out, ratio, avg_overlap * 100)
end