ea 0.6.35 → 0.6.37

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.
@@ -91,7 +91,7 @@ module Ea
91
91
  end
92
92
 
93
93
  # EA's scale factor varies by point size (GDI quantization):
94
- # Carlito regular fits 1.390 at 7pt but 1.432 at 9pt. Sizes
94
+ # Carlito regular fits 1.390 at 7pt but 1.440 at 9pt. Sizes
95
95
  # without a fitted entry use the style's default (7pt, the
96
96
  # corpus's dominant size).
97
97
  def factor_for(family, size_pt, weight, style)
@@ -396,12 +396,9 @@ module Ea
396
396
  drawn_bounds_for(target))
397
397
  end
398
398
 
399
- # The outline EA clips connector rays against: the DRAWN
400
- # autosized box, not the stored logical rect. Packages draw
401
- # narrower than their stored rect (body = autosized tab +
402
- # 15), so their docks land on the drawn edges (E58034A3:
403
- # supplier right-edge dock 155 drawn vs 121 stored). All
404
- # other element kinds draw at the stored rect.
399
+ # The outline EA clips connector rays against: the drawn box.
400
+ # Package bodies = max(stored width, autosized tab + 15);
401
+ # every other kind draws at the stored rect.
405
402
  PACKAGE_BODY_EXTRA = 15
406
403
 
407
404
  def drawn_bounds_for(placement)
@@ -416,7 +413,7 @@ module Ea
416
413
  Ea::Model::Bounds.new(
417
414
  x: logical.x,
418
415
  y: logical.y,
419
- width: tab + PACKAGE_BODY_EXTRA,
416
+ width: [logical.width, tab + PACKAGE_BODY_EXTRA].max,
420
417
  height: logical.height
421
418
  )
422
419
  end
@@ -41,31 +41,63 @@ module Ea
41
41
  # the box) — verified against EA-published package SVGs.
42
42
  Y_OFFSET_FROM_BOTTOM = 15
43
43
 
44
+ LINE_PITCH = 13
45
+ WRAP_PADDING = 15
46
+
44
47
  def subtitle(text, context)
45
48
  bounds = context.bounds
46
49
  fill = context.theme.attribute_text_color
47
50
  y = bounds.y + bounds.height + Y_OFFSET_FROM_BOTTOM
48
- # EA centers the subtitle by its integer textLength inside
49
- # the package BODY and floors x.
50
- body_width = Element::PackageShapeRenderer.body_width_for(
51
- label: context.model_element.name.to_s,
52
- stereotype: nil, size: context.size
53
- )
54
- len = TextRenderer.estimate_width(
55
- text, context.size, nil,
56
- family: context.family, style: "italic"
57
- ).round
58
- x = (bounds.x + (body_width - len) / 2.0).floor
59
- body = TextRenderer.new(
60
- content: text, x: x, y: y,
61
- family: context.family, size: context.size,
62
- size_unit: context.size_unit, fill: fill,
63
- style: "italic"
64
- ).to_svg
65
- wrap(body, fill)
51
+ # EA word-wraps the subtitle to the package BODY width
52
+ # (minus padding), centers each line by its integer
53
+ # textLength inside the body, and floors x; successive
54
+ # lines pitch +13 (05DF5000: "(from ISO 19115-1:2014
55
+ # Metadata" / "Fundamentals)" at y 123/136).
56
+ body_width = bounds.width
57
+ lines = wrap_lines(text, context, body_width - WRAP_PADDING)
58
+ bodies = lines.each_with_index.map do |line, i|
59
+ len = TextRenderer.estimate_width(
60
+ line, context.size, nil,
61
+ family: context.family, style: "italic"
62
+ ).round
63
+ x = (bounds.x + (body_width - len) / 2.0).floor
64
+ TextRenderer.new(
65
+ content: line, x: x, y: y + i * LINE_PITCH,
66
+ family: context.family, size: context.size,
67
+ size_unit: context.size_unit, fill: fill,
68
+ style: "italic"
69
+ ).to_svg
70
+ end
71
+ wrap(bodies.join("\n"), fill)
66
72
  end
67
73
  module_function :subtitle
68
74
 
75
+ # EA carries the break space onto the broken line: line 1
76
+ # is "(from ... Metadata - " with the trailing space, and
77
+ # its textLength includes it (05DF5000: tl 146).
78
+ def wrap_lines(text, context, max_width)
79
+ words = text.split(" ")
80
+ return [text] if words.size <= 1
81
+
82
+ lines = []
83
+ current = +""
84
+ words.each do |word|
85
+ candidate = current.empty? ? word : "#{current} #{word}"
86
+ if !current.empty? && TextRenderer.estimate_width(
87
+ candidate, context.size, nil,
88
+ family: context.family, style: "italic"
89
+ ) > max_width
90
+ lines << "#{current} "
91
+ current = word
92
+ else
93
+ current = candidate
94
+ end
95
+ end
96
+ lines << current unless current.empty?
97
+ lines
98
+ end
99
+ module_function :wrap_lines
100
+
69
101
  def wrap(body, fill)
70
102
  group_style = "stroke-width:1;stroke-linecap:round;" \
71
103
  "stroke-linejoin:bevel; fill:#{fill};" \
@@ -31,9 +31,11 @@ module Ea
31
31
  TAB_LABEL_Y_OFFSET = 13
32
32
  TAB_LINE_OFFSET = 13
33
33
  DEFAULT_TAB_WIDTH = 105
34
- # EA autosizes: tab = max(text width + 15, 105) and the body
35
- # is always 15px wider than the tab. Corpus-verified against
36
- # EA-published package element polygons.
34
+ # EA autosizes the TAB: max(text width + 15, 105). The BODY
35
+ # width = max(stored rect, tab + 15): user-widened packages
36
+ # keep their stored width (05DF5000: bodies 248/194 with
37
+ # tabs 141/105), while EA-autosized packages grow to
38
+ # tab + 15 (E58034A3: stored 86 -> body 120 with tab 105).
37
39
  TAB_NAME_EXTRA = 15
38
40
  TAB_BODY_EXTRA = 15
39
41
 
@@ -143,7 +145,7 @@ module Ea
143
145
  def body_points
144
146
  x = bounds.x
145
147
  y = tab_top_y + tab_height
146
- w = self.class.body_width_for(label: label, stereotype: stereotype, size: size)
148
+ w = body_width
147
149
  h = body_height(y)
148
150
  [
149
151
  [x, y],
@@ -173,8 +175,13 @@ module Ea
173
175
  ]
174
176
  end
175
177
 
178
+ def body_width
179
+ [bounds.width, self.class.autosized_tab_width(label: label, stereotype: stereotype, size: size) + TAB_BODY_EXTRA].max
180
+ end
181
+
176
182
  def tab_width
177
- self.class.autosized_tab_width(label: label, stereotype: stereotype, size: size)
183
+ [body_width,
184
+ self.class.autosized_tab_width(label: label, stereotype: stereotype, size: size)].min
178
185
  end
179
186
 
180
187
  # Tab width fits the widest text line (stereotype or name)
@@ -189,6 +196,7 @@ module Ea
189
196
  def self.body_width_for(label:, stereotype:, size:)
190
197
  autosized_tab_width(label: label, stereotype: stereotype, size: size) + TAB_BODY_EXTRA
191
198
  end
199
+
192
200
  end
193
201
  end
194
202
  end
@@ -63,6 +63,14 @@ module Ea
63
63
  # packages). Heights/widths are translation-invariant, so the
64
64
  # connector emitters can redock in logical space and run the
65
65
  # normal canvas translation afterwards.
66
+ def classifier_package?(classifier)
67
+ classifier.is_a?(Ea::Model::Package)
68
+ end
69
+
70
+ def package_stereotype(context)
71
+ context.classifier.stereotype_refs.first.to_s
72
+ end
73
+
66
74
  def record_drawn_bounds(element, context)
67
75
  return unless element.model_element_ref
68
76
 
@@ -76,7 +84,7 @@ module Ea
76
84
  stereotype: package_stereotype(context),
77
85
  size: context.size
78
86
  )
79
- width = tab + Element::PackageShapeRenderer::TAB_BODY_EXTRA
87
+ width = [raw.width, tab + Element::PackageShapeRenderer::TAB_BODY_EXTRA].max
80
88
  end
81
89
  @drawn_bounds[element.model_element_ref] = Ea::Model::Bounds.new(
82
90
  x: raw.x, y: raw.y,
@@ -85,13 +93,7 @@ module Ea
85
93
  )
86
94
  end
87
95
 
88
- def classifier_package?(classifier)
89
- classifier.class == Ea::Model::Package
90
- end
91
96
 
92
- def package_stereotype(context)
93
- context.classifier.stereotype_refs.first.to_s
94
- end
95
97
 
96
98
  # Build the RenderContext for one element. Returns nil when
97
99
  # the element has no usable bounds (skip rendering).
data/lib/ea/version.rb CHANGED
@@ -4,5 +4,5 @@ module Ea
4
4
  # Gem release version. Extracted to its own file so the
5
5
  # `gem-release` tool's `gem bump` can find and update it via the
6
6
  # standard `lib/<gem>/version.rb` convention.
7
- VERSION = "0.6.35"
7
+ VERSION = "0.6.37"
8
8
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ea
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.35
4
+ version: 0.6.37
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.