Module: Emfsvg::Translation::Handlers::SharedGdi

Includes:
Emf::Emr::Binary::TypeCodes
Included in:
EllipseHandler, ImageHandler, LineHandler, PathHandler, PolylineHandler, RectHandler, TextHandler
Defined in:
lib/emfsvg/translation/handlers/shared_gdi.rb

Overview

Mixed into element handlers. Provides the per-element GDI object lifecycle: Create+Select before the block, Delete after.

Strategy (v1): always emit CreatePen + CreateBrushIndirect + SelectObject + (draw) + DeleteObject, even when the SVG value is "none". This produces a literal 1:1 mapping of the SVG tree to EMF records and is what the round-trip test exercises.

If the element has a rectangular clip-path, the entire draw is wrapped in SaveDC + IntersectClipRect + RestoreDC so the clip is scoped to this element only.

Instance Method Summary collapse

Instance Method Details

#clip_bounds_for(element, context) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
# File 'lib/emfsvg/translation/handlers/shared_gdi.rb', line 40

def clip_bounds_for(element, context)
  return nil if context.clip_path_registry.nil?

  attr = element.clip_path
  return nil if attr.nil?

  clip = context.clip_path_registry.lookup(attr)
  return nil unless clip

  clip.clip_bounds
end

#emit_create_brush(context, paint) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/emfsvg/translation/handlers/shared_gdi.rb', line 75

def emit_create_brush(context, paint)
  handle = context.allocate_handle
  context.emit(Emf::Emr::Binary::Records::CreateBrushIndirect,
               i_type: CREATEBRUSHINDIRECT,
               ih_brush: handle,
               brush_style: paint.style,
               color: paint.color.to_emf_wire.to_wire,
               brush_hatch: 0)
  context.emit(Emf::Emr::Binary::Records::SelectObject,
               i_type: SELECTOBJECT,
               ih_object: handle)
  handle
end

#emit_create_pen(context, stroke) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/emfsvg/translation/handlers/shared_gdi.rb', line 61

def emit_create_pen(context, stroke)
  handle = context.allocate_handle
  context.emit(Emf::Emr::Binary::Records::CreatePen,
               i_type: CREATEPEN,
               ih_pen: handle,
               pen_style: stroke.pen_style,
               width: context.point_l(stroke.width, 0),
               color: stroke.paint.color.to_emf_wire.to_wire)
  context.emit(Emf::Emr::Binary::Records::SelectObject,
               i_type: SELECTOBJECT,
               ih_object: handle)
  handle
end

#emit_delete(context, handle) ⇒ Object



89
90
91
92
93
# File 'lib/emfsvg/translation/handlers/shared_gdi.rb', line 89

def emit_delete(context, handle)
  context.emit(Emf::Emr::Binary::Records::DeleteObject,
               i_type: DELETEOBJECT,
               ih_object: handle)
end

#emit_restore_dc(context) ⇒ Object



56
57
58
59
# File 'lib/emfsvg/translation/handlers/shared_gdi.rb', line 56

def emit_restore_dc(context)
  context.emit(Emf::Emr::Binary::Records::RestoreDc,
               i_type: RESTOREDC, saved_dc: -1)
end

#emit_save_dc(context) ⇒ Object



52
53
54
# File 'lib/emfsvg/translation/handlers/shared_gdi.rb', line 52

def emit_save_dc(context)
  context.emit(Emf::Emr::Binary::Records::SaveDc, i_type: SAVEDC)
end

#with_gdi_state(element, context) ⇒ Object



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/emfsvg/translation/handlers/shared_gdi.rb', line 22

def with_gdi_state(element, context)
  clip_bounds = clip_bounds_for(element, context)
  wrap_with_clip = !clip_bounds.nil?

  emit_save_dc(context) if wrap_with_clip
  pen_handle = emit_create_pen(context, element.stroke) if element.stroke
  brush_handle = emit_create_brush(context, element.fill) if element.fill
  if wrap_with_clip
    context.emit(Emf::Emr::Binary::Records::IntersectClipRect,
                 i_type: INTERSECTCLIPRECT,
                 rcl_clip: context.rect_l(*clip_bounds))
  end
  yield
  emit_delete(context, pen_handle) if pen_handle
  emit_delete(context, brush_handle) if brush_handle
  emit_restore_dc(context) if wrap_with_clip
end