emfsvg 0.1.3 → 0.1.4

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/README.adoc +98 -14
  3. data/lib/emfsvg/device_context.rb +9 -6
  4. data/lib/emfsvg/dib_decoder.rb +12 -8
  5. data/lib/emfsvg/drawing_hint.rb +88 -0
  6. data/lib/emfsvg/error.rb +11 -0
  7. data/lib/emfsvg/format_helpers.rb +46 -2
  8. data/lib/emfsvg/glyph_index_mapper.rb +5 -6
  9. data/lib/emfsvg/object_table.rb +7 -15
  10. data/lib/emfsvg/record_access.rb +38 -0
  11. data/lib/emfsvg/renderer.rb +114 -55
  12. data/lib/emfsvg/svg/clip_path_registry.rb +287 -4
  13. data/lib/emfsvg/svg/color.rb +32 -9
  14. data/lib/emfsvg/svg/document.rb +7 -0
  15. data/lib/emfsvg/svg/element.rb +3 -3
  16. data/lib/emfsvg/svg/elements/clip_path.rb +89 -24
  17. data/lib/emfsvg/svg/elements/defs.rb +85 -4
  18. data/lib/emfsvg/svg/elements/group.rb +30 -4
  19. data/lib/emfsvg/svg/elements/path.rb +239 -3
  20. data/lib/emfsvg/svg/elements/rect.rb +5 -2
  21. data/lib/emfsvg/svg/elements/stylable.rb +41 -11
  22. data/lib/emfsvg/svg/elements/text.rb +101 -16
  23. data/lib/emfsvg/svg/paint.rb +8 -2
  24. data/lib/emfsvg/svg/parser.rb +8 -1
  25. data/lib/emfsvg/svg/path_data/command.rb +1 -1
  26. data/lib/emfsvg/svg/path_data.rb +47 -36
  27. data/lib/emfsvg/svg/stroke.rb +78 -12
  28. data/lib/emfsvg/svg_builder.rb +38 -9
  29. data/lib/emfsvg/translation/context.rb +262 -10
  30. data/lib/emfsvg/translation/decimal_detector.rb +26 -8
  31. data/lib/emfsvg/translation/emf_renderer.rb +16 -12
  32. data/lib/emfsvg/translation/handler_registry.rb +16 -6
  33. data/lib/emfsvg/translation/handlers/circular_arc.rb +103 -0
  34. data/lib/emfsvg/translation/handlers/defs_handler.rb +33 -4
  35. data/lib/emfsvg/translation/handlers/ellipse_handler.rb +2 -2
  36. data/lib/emfsvg/translation/handlers/elliptical_arc.rb +125 -0
  37. data/lib/emfsvg/translation/handlers/group_handler.rb +7 -0
  38. data/lib/emfsvg/translation/handlers/image_handler.rb +53 -8
  39. data/lib/emfsvg/translation/handlers/line_handler.rb +2 -2
  40. data/lib/emfsvg/translation/handlers/mapped_arc.rb +41 -0
  41. data/lib/emfsvg/translation/handlers/mapped_circular_arc.rb +55 -0
  42. data/lib/emfsvg/translation/handlers/mapped_pie.rb +30 -0
  43. data/lib/emfsvg/translation/handlers/path_flattener.rb +70 -49
  44. data/lib/emfsvg/translation/handlers/path_handler.rb +169 -14
  45. data/lib/emfsvg/translation/handlers/polyline_handler.rb +2 -2
  46. data/lib/emfsvg/translation/handlers/rect_handler.rb +21 -5
  47. data/lib/emfsvg/translation/handlers/shared_gdi.rb +75 -12
  48. data/lib/emfsvg/translation/handlers/text_handler.rb +92 -29
  49. data/lib/emfsvg/translation/handlers.rb +5 -0
  50. data/lib/emfsvg/translation/header_builder.rb +20 -0
  51. data/lib/emfsvg/translation/local_origin.rb +40 -0
  52. data/lib/emfsvg/translation/record_emitter.rb +8 -5
  53. data/lib/emfsvg/translation/scaler/fixed.rb +8 -2
  54. data/lib/emfsvg/translation.rb +1 -0
  55. data/lib/emfsvg/version.rb +1 -1
  56. data/lib/emfsvg/visitors/emr_visitor.rb +197 -129
  57. data/lib/emfsvg.rb +49 -2
  58. metadata +24 -10
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emfsvg
4
+ module Translation
5
+ # A complete absolute line can use a temporary integer device origin when
6
+ # its fixed-scale coordinates overflow. Restrict this to independent draws:
7
+ # pending paths, relative commands and clips carry additional state.
8
+ module LocalOrigin
9
+ module_function
10
+
11
+ def for_path(element, scaler)
12
+ return unless scaler.scaled? && independent_line?(element)
13
+
14
+ points = element.commands.map(&:args)
15
+ return unless finite_points?(points)
16
+ return unless points.flatten.any? { |value| scaler.unsafe?(value) }
17
+
18
+ origin = points.transpose.map do |values|
19
+ values.any? { |value| scaler.unsafe?(value) } ? ((values.min + values.max) / 2.0).floor : 0
20
+ end
21
+ return unless origin.all? { |value| Scaler::Fixed::INT32_RANGE.cover?(value) }
22
+ return unless points.all? { |point| point.zip(origin).all? { |value, offset| !scaler.unsafe?(value - offset) } }
23
+
24
+ origin
25
+ end
26
+
27
+ def independent_line?(element)
28
+ element.is_a?(Svg::Elements::Path) && element.children.empty? &&
29
+ !element.clip_path && element.fill.null? && element.path_record_kind.nil? &&
30
+ [nil, :polyline].include?(element.line_record_kind) && element.commands.map(&:letter) == %w[M L]
31
+ end
32
+
33
+ def finite_points?(points)
34
+ points.all? do |point|
35
+ point.size == 2 && point.all? { |value| value.is_a?(Numeric) && value.finite? }
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -19,17 +19,20 @@ module Emfsvg
19
19
 
20
20
  attr_reader :records
21
21
 
22
- def emit(wire_class, **attrs)
23
- record = build(wire_class, **attrs)
22
+ def emit(wire_class, attrs)
23
+ record = build(wire_class, attrs)
24
24
  @records << record
25
25
  record
26
26
  end
27
27
 
28
28
  # Construct without appending. Useful for inline inspection in tests.
29
- def build(wire_class, **attrs)
29
+ def build(wire_class, attrs)
30
+ adapter = Emf::Model::Emr::Records::WireAdapter
31
+ return adapter.build(wire_class, **attrs) if adapter.respond_to?(:build)
32
+
30
33
  wire = wire_class.new(**attrs)
31
- wire.n_size = wire.to_binary_s.bytesize
32
- Emf::Model::Emr::Records::WireAdapter.new(wire: wire)
34
+ wire.n_size = wire.num_bytes
35
+ adapter.new(wire: wire)
33
36
  end
34
37
  end
35
38
  end
@@ -13,8 +13,11 @@ module Emfsvg
13
13
  # Larger factors risk int32 overflow on big coords (max safe
14
14
  # SVG coord ~ 214_000 with FACTOR=10_000).
15
15
  class Fixed
16
+ class Overflow < RangeError; end
17
+
18
+ INT32_RANGE = (-(2**31)..((2**31) - 1))
16
19
  FACTOR = 10_000
17
- MAX_SAFE_INPUT = 214_748 # (2**31 - 1) / FACTOR, with margin
20
+ MAX_SAFE_INPUT = 214_748 # ((2**31) - 1) / FACTOR, with margin
18
21
 
19
22
  attr_reader :factor
20
23
 
@@ -23,6 +26,8 @@ module Emfsvg
23
26
  end
24
27
 
25
28
  def to_int(decimal)
29
+ raise Overflow, "coordinate exceeds scaled int32 range: #{decimal}" if unsafe?(decimal)
30
+
26
31
  (decimal.to_f * factor).round
27
32
  end
28
33
 
@@ -33,7 +38,8 @@ module Emfsvg
33
38
  # True if `value` would overflow int32 when scaled. The
34
39
  # renderer falls back to Identity when ANY coord is unsafe.
35
40
  def unsafe?(value)
36
- value.to_f.abs > MAX_SAFE_INPUT
41
+ scaled = value.to_f * factor
42
+ !scaled.finite? || !INT32_RANGE.cover?(scaled.round)
37
43
  end
38
44
  end
39
45
  end
@@ -12,5 +12,6 @@ module Emfsvg
12
12
  autoload :Handlers, "emfsvg/translation/handlers"
13
13
  autoload :Scaler, "emfsvg/translation/scaler"
14
14
  autoload :DecimalDetector, "emfsvg/translation/decimal_detector"
15
+ autoload :LocalOrigin, "emfsvg/translation/local_origin"
15
16
  end
16
17
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Emfsvg
4
- VERSION = "0.1.3"
4
+ VERSION = "0.1.4"
5
5
  end