Class: Emfsvg::Translation::Context

Inherits:
Object
  • Object
show all
Defined in:
lib/emfsvg/translation/context.rb

Overview

Mutable translation state carried through the element tree walk.

Holds the GDI device context (pen/brush/font/transform), the 1-indexed object table, the SaveDC/RestoreDC stack, the coordinate scaler, and the output record list. All handlers receive a Context instance and operate on it functionally.

Context also carries a reference to the HandlerRegistry so that GroupHandler (and other container handlers) can recurse into their children uniformly.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(handler_registry: EmfRenderer::DEFAULT_REGISTRY, clip_path_registry: nil, scaler: Scaler::Identity.new) ⇒ Context

Returns a new instance of Context.



21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/emfsvg/translation/context.rb', line 21

def initialize(handler_registry: EmfRenderer::DEFAULT_REGISTRY,
               clip_path_registry: nil,
               scaler: Scaler::Identity.new)
  @device_context = DeviceContext.new
  @object_table = ObjectTable.new
  @transform_stack = TransformStack.new
  @emitter = RecordEmitter.new
  @handler_registry = handler_registry
  @clip_path_registry = clip_path_registry
  @scaler = scaler
  @next_handle = 1
end

Instance Attribute Details

#clip_path_registryObject (readonly)

Returns the value of attribute clip_path_registry.



18
19
20
# File 'lib/emfsvg/translation/context.rb', line 18

def clip_path_registry
  @clip_path_registry
end

#device_contextObject (readonly)

Returns the value of attribute device_context.



18
19
20
# File 'lib/emfsvg/translation/context.rb', line 18

def device_context
  @device_context
end

#emitterObject (readonly)

Returns the value of attribute emitter.



18
19
20
# File 'lib/emfsvg/translation/context.rb', line 18

def emitter
  @emitter
end

#handler_registryObject (readonly)

Returns the value of attribute handler_registry.



18
19
20
# File 'lib/emfsvg/translation/context.rb', line 18

def handler_registry
  @handler_registry
end

#object_tableObject (readonly)

Returns the value of attribute object_table.



18
19
20
# File 'lib/emfsvg/translation/context.rb', line 18

def object_table
  @object_table
end

#scalerObject (readonly)

Returns the value of attribute scaler.



18
19
20
# File 'lib/emfsvg/translation/context.rb', line 18

def scaler
  @scaler
end

#transform_stackObject (readonly)

Returns the value of attribute transform_stack.



18
19
20
# File 'lib/emfsvg/translation/context.rb', line 18

def transform_stack
  @transform_stack
end

Instance Method Details

#allocate_handleObject

Allocate the next 1-indexed GDI object handle. Always increments — no reuse in v1 (see TODO.roadmap/09-coalescing-optimization.adoc).



36
37
38
39
40
# File 'lib/emfsvg/translation/context.rb', line 36

def allocate_handle
  handle = @next_handle
  @next_handle += 1
  handle
end

#dispatch(element) ⇒ Object

Dispatch an element through the registry. Container handlers (e.g. GroupHandler) call this to recurse into their children.



52
53
54
55
# File 'lib/emfsvg/translation/context.rb', line 52

def dispatch(element)
  handler = @handler_registry.handler_for(element)
  handler&.call(element, self)
end

#emit(wire_class, **attrs) ⇒ Object



42
43
44
# File 'lib/emfsvg/translation/context.rb', line 42

def emit(wire_class, **attrs)
  @emitter.emit(wire_class, **attrs)
end

#point_l(x, y) ⇒ Object

---- Coordinate-scaled wire helpers (DRY: handlers route all coords through these so the scaler affects everything) ----



60
61
62
# File 'lib/emfsvg/translation/context.rb', line 60

def point_l(x, y)
  Emf::Binary::Types::PointL.new(x: scale_int(x), y: scale_int(y))
end

#recordsObject



46
47
48
# File 'lib/emfsvg/translation/context.rb', line 46

def records
  @emitter.records
end

#rect_l(left, top, right, bottom) ⇒ Object



64
65
66
67
68
69
# File 'lib/emfsvg/translation/context.rb', line 64

def rect_l(left, top, right, bottom)
  Emf::Binary::Types::RectL.new(
    left: scale_int(left), top: scale_int(top),
    right: scale_int(right), bottom: scale_int(bottom)
  )
end

#size_l(cx, cy) ⇒ Object



71
72
73
# File 'lib/emfsvg/translation/context.rb', line 71

def size_l(cx, cy)
  Emf::Binary::Types::SizeL.new(cx: scale_int(cx), cy: scale_int(cy))
end

#xform_wire_from(matrix) ⇒ Object

Convert a Matrix to its wire form, scaling the translation (dx/dy) by the scaler's factor. The 2x2 rotation/scale coefficients are NOT scaled — they're dimensionless ratios and survive any isometric map-mode change intact.



79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/emfsvg/translation/context.rb', line 79

def xform_wire_from(matrix)
  factor = @scaler.factor
  return matrix.to_wire if factor == 1

  scaled = Emf::Model::Geometry::Matrix.new(
    m11: matrix.m11, m12: matrix.m12,
    m21: matrix.m21, m22: matrix.m22,
    dx: matrix.dx * factor,
    dy: matrix.dy * factor
  )
  scaled.to_wire
end