emfsvg 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.adoc +86 -58
  3. data/exe/emfsvg +21 -2
  4. data/lib/emfsvg/dib_encoder.rb +67 -0
  5. data/lib/emfsvg/error.rb +2 -0
  6. data/lib/emfsvg/png_decoder.rb +23 -0
  7. data/lib/emfsvg/svg/attribute_parser.rb +31 -0
  8. data/lib/emfsvg/svg/clip_path_registry.rb +43 -0
  9. data/lib/emfsvg/svg/color.rb +105 -0
  10. data/lib/emfsvg/svg/document.rb +70 -0
  11. data/lib/emfsvg/svg/element.rb +66 -0
  12. data/lib/emfsvg/svg/elements/circle.rb +38 -0
  13. data/lib/emfsvg/svg/elements/clip_path.rb +128 -0
  14. data/lib/emfsvg/svg/elements/defs.rb +24 -0
  15. data/lib/emfsvg/svg/elements/ellipse.rb +35 -0
  16. data/lib/emfsvg/svg/elements/group.rb +33 -0
  17. data/lib/emfsvg/svg/elements/image.rb +65 -0
  18. data/lib/emfsvg/svg/elements/line.rb +35 -0
  19. data/lib/emfsvg/svg/elements/path.rb +29 -0
  20. data/lib/emfsvg/svg/elements/polygon.rb +29 -0
  21. data/lib/emfsvg/svg/elements/polyline.rb +29 -0
  22. data/lib/emfsvg/svg/elements/rect.rb +44 -0
  23. data/lib/emfsvg/svg/elements/stylable.rb +28 -0
  24. data/lib/emfsvg/svg/elements/text.rb +67 -0
  25. data/lib/emfsvg/svg/elements.rb +42 -0
  26. data/lib/emfsvg/svg/paint.rb +36 -0
  27. data/lib/emfsvg/svg/parser.rb +44 -0
  28. data/lib/emfsvg/svg/path_data/command.rb +15 -0
  29. data/lib/emfsvg/svg/path_data.rb +135 -0
  30. data/lib/emfsvg/svg/stroke.rb +107 -0
  31. data/lib/emfsvg/svg/transform_parser.rb +123 -0
  32. data/lib/emfsvg/svg.rb +25 -0
  33. data/lib/emfsvg/translation/context.rb +99 -0
  34. data/lib/emfsvg/translation/decimal_detector.rb +112 -0
  35. data/lib/emfsvg/translation/emf_renderer.rb +146 -0
  36. data/lib/emfsvg/translation/handler_registry.rb +38 -0
  37. data/lib/emfsvg/translation/handlers/circle_handler.rb +17 -0
  38. data/lib/emfsvg/translation/handlers/clip_path_handler.rb +16 -0
  39. data/lib/emfsvg/translation/handlers/defs_handler.rb +17 -0
  40. data/lib/emfsvg/translation/handlers/ellipse_handler.rb +28 -0
  41. data/lib/emfsvg/translation/handlers/group_handler.rb +40 -0
  42. data/lib/emfsvg/translation/handlers/image_handler.rb +94 -0
  43. data/lib/emfsvg/translation/handlers/line_handler.rb +30 -0
  44. data/lib/emfsvg/translation/handlers/path_flattener.rb +139 -0
  45. data/lib/emfsvg/translation/handlers/path_handler.rb +78 -0
  46. data/lib/emfsvg/translation/handlers/polygon_handler.rb +17 -0
  47. data/lib/emfsvg/translation/handlers/polyline_handler.rb +51 -0
  48. data/lib/emfsvg/translation/handlers/rect_handler.rb +43 -0
  49. data/lib/emfsvg/translation/handlers/shared_gdi.rb +97 -0
  50. data/lib/emfsvg/translation/handlers/text_handler.rb +137 -0
  51. data/lib/emfsvg/translation/handlers.rb +25 -0
  52. data/lib/emfsvg/translation/header_builder.rb +89 -0
  53. data/lib/emfsvg/translation/record_emitter.rb +36 -0
  54. data/lib/emfsvg/translation/scaler/fixed.rb +41 -0
  55. data/lib/emfsvg/translation/scaler/identity.rb +23 -0
  56. data/lib/emfsvg/translation/scaler.rb +19 -0
  57. data/lib/emfsvg/translation.rb +16 -0
  58. data/lib/emfsvg/version.rb +1 -1
  59. data/lib/emfsvg.rb +21 -1
  60. metadata +62 -7
  61. data/lib/emfsvg/compat.rb +0 -134
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: emfsvg
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
+ autorequire:
8
9
  bindir: exe
9
10
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
11
+ date: 2026-07-26 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: base64
@@ -80,19 +81,19 @@ dependencies:
80
81
  - !ruby/object:Gem::Version
81
82
  version: '1.6'
82
83
  - !ruby/object:Gem::Dependency
83
- name: rexml
84
+ name: nokogiri
84
85
  requirement: !ruby/object:Gem::Requirement
85
86
  requirements:
86
87
  - - "~>"
87
88
  - !ruby/object:Gem::Version
88
- version: '3.3'
89
+ version: '1.16'
89
90
  type: :runtime
90
91
  prerelease: false
91
92
  version_requirements: !ruby/object:Gem::Requirement
92
93
  requirements:
93
94
  - - "~>"
94
95
  - !ruby/object:Gem::Version
95
- version: '3.3'
96
+ version: '1.16'
96
97
  description: Convert Microsoft Enhanced Metafile (EMF) to SVG and back. Consumes the
97
98
  `emf` gem's domain model. Replaces the FFI wrapper around libemf2svg with a pure-Ruby
98
99
  transformer.
@@ -110,18 +111,70 @@ files:
110
111
  - lib/emfsvg/bsd_rand.rb
111
112
  - lib/emfsvg/bsd_rand_sequence.txt
112
113
  - lib/emfsvg/clip_region.rb
113
- - lib/emfsvg/compat.rb
114
114
  - lib/emfsvg/device_context.rb
115
115
  - lib/emfsvg/dib_decoder.rb
116
+ - lib/emfsvg/dib_encoder.rb
116
117
  - lib/emfsvg/error.rb
117
118
  - lib/emfsvg/format_helpers.rb
118
119
  - lib/emfsvg/glyph_index_mapper.rb
119
120
  - lib/emfsvg/object_table.rb
120
121
  - lib/emfsvg/options.rb
122
+ - lib/emfsvg/png_decoder.rb
121
123
  - lib/emfsvg/png_encoder.rb
122
124
  - lib/emfsvg/renderer.rb
125
+ - lib/emfsvg/svg.rb
126
+ - lib/emfsvg/svg/attribute_parser.rb
127
+ - lib/emfsvg/svg/clip_path_registry.rb
128
+ - lib/emfsvg/svg/color.rb
129
+ - lib/emfsvg/svg/document.rb
130
+ - lib/emfsvg/svg/element.rb
131
+ - lib/emfsvg/svg/elements.rb
132
+ - lib/emfsvg/svg/elements/circle.rb
133
+ - lib/emfsvg/svg/elements/clip_path.rb
134
+ - lib/emfsvg/svg/elements/defs.rb
135
+ - lib/emfsvg/svg/elements/ellipse.rb
136
+ - lib/emfsvg/svg/elements/group.rb
137
+ - lib/emfsvg/svg/elements/image.rb
138
+ - lib/emfsvg/svg/elements/line.rb
139
+ - lib/emfsvg/svg/elements/path.rb
140
+ - lib/emfsvg/svg/elements/polygon.rb
141
+ - lib/emfsvg/svg/elements/polyline.rb
142
+ - lib/emfsvg/svg/elements/rect.rb
143
+ - lib/emfsvg/svg/elements/stylable.rb
144
+ - lib/emfsvg/svg/elements/text.rb
145
+ - lib/emfsvg/svg/paint.rb
146
+ - lib/emfsvg/svg/parser.rb
147
+ - lib/emfsvg/svg/path_data.rb
148
+ - lib/emfsvg/svg/path_data/command.rb
149
+ - lib/emfsvg/svg/stroke.rb
150
+ - lib/emfsvg/svg/transform_parser.rb
123
151
  - lib/emfsvg/svg_builder.rb
124
152
  - lib/emfsvg/transform_stack.rb
153
+ - lib/emfsvg/translation.rb
154
+ - lib/emfsvg/translation/context.rb
155
+ - lib/emfsvg/translation/decimal_detector.rb
156
+ - lib/emfsvg/translation/emf_renderer.rb
157
+ - lib/emfsvg/translation/handler_registry.rb
158
+ - lib/emfsvg/translation/handlers.rb
159
+ - lib/emfsvg/translation/handlers/circle_handler.rb
160
+ - lib/emfsvg/translation/handlers/clip_path_handler.rb
161
+ - lib/emfsvg/translation/handlers/defs_handler.rb
162
+ - lib/emfsvg/translation/handlers/ellipse_handler.rb
163
+ - lib/emfsvg/translation/handlers/group_handler.rb
164
+ - lib/emfsvg/translation/handlers/image_handler.rb
165
+ - lib/emfsvg/translation/handlers/line_handler.rb
166
+ - lib/emfsvg/translation/handlers/path_flattener.rb
167
+ - lib/emfsvg/translation/handlers/path_handler.rb
168
+ - lib/emfsvg/translation/handlers/polygon_handler.rb
169
+ - lib/emfsvg/translation/handlers/polyline_handler.rb
170
+ - lib/emfsvg/translation/handlers/rect_handler.rb
171
+ - lib/emfsvg/translation/handlers/shared_gdi.rb
172
+ - lib/emfsvg/translation/handlers/text_handler.rb
173
+ - lib/emfsvg/translation/header_builder.rb
174
+ - lib/emfsvg/translation/record_emitter.rb
175
+ - lib/emfsvg/translation/scaler.rb
176
+ - lib/emfsvg/translation/scaler/fixed.rb
177
+ - lib/emfsvg/translation/scaler/identity.rb
125
178
  - lib/emfsvg/version.rb
126
179
  - lib/emfsvg/visitors.rb
127
180
  - lib/emfsvg/visitors/emr_visitor.rb
@@ -133,6 +186,7 @@ metadata:
133
186
  source_code_uri: https://github.com/claricle/emfsvg
134
187
  changelog_uri: https://github.com/claricle/emfsvg/blob/main/CHANGELOG.adoc
135
188
  rubygems_mfa_required: 'true'
189
+ post_install_message:
136
190
  rdoc_options: []
137
191
  require_paths:
138
192
  - lib
@@ -147,7 +201,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
147
201
  - !ruby/object:Gem::Version
148
202
  version: '0'
149
203
  requirements: []
150
- rubygems_version: 4.0.16
204
+ rubygems_version: 3.5.22
205
+ signing_key:
151
206
  specification_version: 4
152
207
  summary: EMF <-> SVG transformer (Ruby).
153
208
  test_files: []
data/lib/emfsvg/compat.rb DELETED
@@ -1,134 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Compares emfsvg's output against libemf2svg's reference output for the
4
- # same EMF input. Used by spec/compat_with_libemf2svg_spec.rb.
5
- #
6
- # This is NOT a "must produce identical SVG" test — emfsvg is a clean-room
7
- # rewrite that handles a subset of EMF records. It IS:
8
- # - a structural-equivalence check for the records emfsvg handles
9
- # - a quantification of the gap (compatibility ratio per fixture)
10
- # - a regression guard so the ratio doesn't shrink over time
11
- #
12
- # Usage:
13
- # Emfsvg::Compat::Comparator.call(fixtures_glob, ref_dir, out_dir)
14
- #
15
- # Requires the libemf2svg-conv binary (or our emf2svg_ref wrapper) at
16
- # Emfsvg::Compat::LIBEMF2SVG_REF_PATH. The spec auto-skips if missing.
17
-
18
- require "open3"
19
- require "fileutils"
20
- require "tmpdir"
21
- require "rexml/document"
22
-
23
- module Emfsvg
24
- module Compat
25
- REF_BINARY_DEFAULT = "/Users/mulgogi/src/claricle/libemf2svg/emf2svg_ref"
26
- REF_LIB_DIR_DEFAULT = "/Users/mulgogi/src/claricle/libemf2svg/build"
27
-
28
- module_function
29
-
30
- def ref_binary_available?
31
- path = ENV.fetch("EMF2SVG_REF_PATH", REF_BINARY_DEFAULT)
32
- File.executable?(path)
33
- end
34
-
35
- # Generate a reference SVG using libemf2svg. Returns the SVG bytes,
36
- # or nil if generation failed.
37
- def generate_reference(emf_path, output_path = nil)
38
- binary = ENV.fetch("EMF2SVG_REF_PATH", REF_BINARY_DEFAULT)
39
- lib_dir = ENV.fetch("EMF2SVG_REF_LIB_DIR", REF_LIB_DIR_DEFAULT)
40
- env = { "DYLD_LIBRARY_PATH" => lib_dir }
41
- output_path ||= "#{Dir.mktmpdir}/#{File.basename(emf_path, '.emf')}.svg"
42
- _, _, status = Open3.capture3(env, binary, emf_path, output_path)
43
- return nil unless status.success?
44
-
45
- File.binread(output_path)
46
- ensure
47
- File.delete(output_path) if output_path && !block_given?
48
- end
49
-
50
- # Generate emfsvg output. Requires the emfsvg gem to be loaded.
51
- def generate_emfsvg(emf_path, options = {})
52
- Emfsvg.from_file(emf_path, **options)
53
- end
54
-
55
- # Normalise an SVG document for structural comparison:
56
- # - parse to REXML
57
- # - return the root element
58
- def parse_svg(svg_string)
59
- REXML::Document.new(svg_string).root
60
- rescue REXML::ParseException
61
- nil
62
- end
63
-
64
- # Count drawing elements in an SVG. Returns a hash like
65
- # { "rect" => 3, "path" => 17, "ellipse" => 2 }.
66
- def count_elements(svg_root)
67
- counts = Hash.new(0)
68
- return counts unless svg_root
69
-
70
- REXML::XPath.each(svg_root, "descendant-or-self::*") do |el|
71
- counts[el.name] += 1
72
- end
73
- counts
74
- end
75
-
76
- # Per-fixture comparison result.
77
- Result = Struct.new(:fixture, :ref_bytes, :emfsvg_bytes, :ref_elements,
78
- :emfsvg_elements, keyword_init: true) do
79
- def byte_ratio
80
- ref_bytes.zero? ? 0.0 : (emfsvg_bytes.to_f / ref_bytes * 100).round(1)
81
- end
82
-
83
- def element_overlap
84
- return 0.0 if emfsvg_elements.empty?
85
-
86
- shared = emfsvg_elements.keys & ref_elements.keys
87
- shared.sum { |k| [emfsvg_elements[k], ref_elements[k]].min }.to_f /
88
- emfsvg_elements.values.sum
89
- end
90
-
91
- def to_s
92
- format("%-40s ref=%8d out=%7d (%5.1f%%) elements_overlap=%.1f",
93
- fixture, ref_bytes, emfsvg_bytes, byte_ratio, element_overlap * 100)
94
- end
95
- end
96
-
97
- # Walk a fixtures glob, produce both outputs, return Result array.
98
- def compare_glob(fixtures_glob)
99
- results = []
100
- Dir.glob(fixtures_glob).each do |emf|
101
- ref_svg = generate_reference(emf)
102
- next unless ref_svg
103
-
104
- emfsvg_svg = begin
105
- generate_emfsvg(emf)
106
- rescue StandardError
107
- nil
108
- end
109
- next unless emfsvg_svg
110
-
111
- results << Result.new(
112
- fixture: File.basename(emf),
113
- ref_bytes: ref_svg.bytesize,
114
- emfsvg_bytes: emfsvg_svg.bytesize,
115
- ref_elements: count_elements(parse_svg(ref_svg)),
116
- emfsvg_elements: count_elements(parse_svg(emfsvg_svg))
117
- )
118
- end
119
- results
120
- end
121
-
122
- # Summary across many results.
123
- def summarize(results)
124
- return "no results" if results.empty?
125
-
126
- total_ref = results.sum(&:ref_bytes)
127
- total_out = results.sum(&:emfsvg_bytes)
128
- ratio = total_ref.zero? ? 0 : (total_out.to_f / total_ref * 100).round(1)
129
- avg_overlap = results.sum(&:element_overlap) / results.size
130
- format("fixtures=%d total_ref=%d total_out=%d byte_ratio=%.1f%% avg_element_overlap=%.1f%%",
131
- results.size, total_ref, total_out, ratio, avg_overlap * 100)
132
- end
133
- end
134
- end