Module: Emfsvg::Translation::DecimalDetector
- Defined in:
- lib/emfsvg/translation/decimal_detector.rb
Overview
Walks an Svg::Document tree to decide whether any coordinate
attribute has a non-integer value. If yes, the renderer installs
Scaler::Fixed and emits MapMode scaling records so the
decimals survive the int32 truncation in EMF.
Also detects overflow: if any decimal coord is so large that scaling would overflow int32, the renderer falls back to Identity (accepting the truncation for that fixture).
Class Method Summary collapse
-
.any_decimal?(element) ⇒ Boolean
---- private ----.
- .any_unsafe?(element, scaler) ⇒ Boolean
-
.decimal_attrs_for(element) ⇒ Object
Coordinate-bearing attributes per element class.
- .extract_floats(str) ⇒ Object
-
.has_decimal?(value) ⇒ Boolean
valuemay be: Float, Integer, Array of [x,y] pairs, Array of PathData::Command, or nil. - .needs_scaling?(document) ⇒ Boolean
- .pair_or_command?(value) ⇒ Boolean
- .path_commands(element) ⇒ Object
- .path_unsafe?(path_str, scaler) ⇒ Boolean
- .safe_for_fixed_scale?(document, scaler) ⇒ Boolean
- .unsafe_value?(value, scaler) ⇒ Boolean
Class Method Details
.any_decimal?(element) ⇒ Boolean
---- private ----
26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/emfsvg/translation/decimal_detector.rb', line 26 def any_decimal?(element) return false unless element element_attrs = decimal_attrs_for(element) return true if element_attrs.any? { |attr| has_decimal?(element.send(attr)) } path = path_commands(element) return true if path && path.any? { |cmd| cmd.args.any? { |a| a.is_a?(Float) && a != a.to_i } } element.children.any? { |c| any_decimal?(c) } end |
.any_unsafe?(element, scaler) ⇒ Boolean
38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/emfsvg/translation/decimal_detector.rb', line 38 def any_unsafe?(element, scaler) return false unless element element_attrs = decimal_attrs_for(element) return true if element_attrs.any? { |attr| unsafe_value?(element.send(attr), scaler) } path = path_commands(element) return true if path && path.any? { |cmd| cmd.args.any? { |a| scaler.unsafe?(a) } } element.children.any? { |c| any_unsafe?(c, scaler) } end |
.decimal_attrs_for(element) ⇒ Object
Coordinate-bearing attributes per element class. Returns [] for elements with no coords (Group, Defs, etc).
52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/emfsvg/translation/decimal_detector.rb', line 52 def decimal_attrs_for(element) case element when Svg::Elements::Rect then %i[x y width height rx ry] when Svg::Elements::Ellipse then %i[cx cy rx ry] when Svg::Elements::Circle then %i[cx cy r] when Svg::Elements::Line then %i[x1 y1 x2 y2] when Svg::Elements::Polyline, Svg::Elements::Polygon then [:points] when Svg::Elements::Path then [:commands] when Svg::Elements::Text then %i[x y font_size] when Svg::Elements::Image then %i[x y width height] else [] end end |
.extract_floats(str) ⇒ Object
107 108 109 |
# File 'lib/emfsvg/translation/decimal_detector.rb', line 107 def extract_floats(str) str.to_s.scan(/-?\d+(?:\.\d+)?/).map(&:to_f) end |
.has_decimal?(value) ⇒ Boolean
value may be: Float, Integer, Array of [x,y] pairs, Array of
PathData::Command, or nil.
74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/emfsvg/translation/decimal_detector.rb', line 74 def has_decimal?(value) return false if value.nil? case value when Float then !value.finite? || (value != value.to_i) when Integer then false when Array value.any? { |v| pair_or_command?(v) ? has_decimal?(v) : v.is_a?(Float) && v != v.to_i } when String then value.match?(/\.\d/) else false end end |
.needs_scaling?(document) ⇒ Boolean
16 17 18 |
# File 'lib/emfsvg/translation/decimal_detector.rb', line 16 def needs_scaling?(document) any_decimal?(document.root_element) end |
.pair_or_command?(value) ⇒ Boolean
87 88 89 |
# File 'lib/emfsvg/translation/decimal_detector.rb', line 87 def pair_or_command?(value) value.is_a?(Array) || value.is_a?(Svg::PathData::Command) end |
.path_commands(element) ⇒ Object
66 67 68 69 70 |
# File 'lib/emfsvg/translation/decimal_detector.rb', line 66 def path_commands(element) return nil unless element.is_a?(Svg::Elements::Path) element.commands end |
.path_unsafe?(path_str, scaler) ⇒ Boolean
103 104 105 |
# File 'lib/emfsvg/translation/decimal_detector.rb', line 103 def path_unsafe?(path_str, scaler) extract_floats(path_str).any? { |f| scaler.unsafe?(f) } end |
.safe_for_fixed_scale?(document, scaler) ⇒ Boolean
20 21 22 |
# File 'lib/emfsvg/translation/decimal_detector.rb', line 20 def safe_for_fixed_scale?(document, scaler) !any_unsafe?(document.root_element, scaler) end |
.unsafe_value?(value, scaler) ⇒ Boolean
91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/emfsvg/translation/decimal_detector.rb', line 91 def unsafe_value?(value, scaler) return false if value.nil? case value when Float then scaler.unsafe?(value) when Integer then false when Array then value.any? { |v| unsafe_value?(v, scaler) } when String then extract_floats(value).any? { |f| scaler.unsafe?(f) } else false end end |