ea 0.6.96 → 0.6.98

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3b98c8ee8bf786f5160291763db47b38d8213a029ef4d647df1d73c386551dba
4
- data.tar.gz: 3f0ee88fdb07d831f70e1ec51f634048140887a5231baa3dc0eea778a7f60d7a
3
+ metadata.gz: b3c13e631927ad57c6168b6d7c02eaed8e933c89ad7dfcd0a055fb6b8a69e8e1
4
+ data.tar.gz: 3edb35912780be1170fce402dd141823f4c5e44c92a6f84451fa136d3eb026a8
5
5
  SHA512:
6
- metadata.gz: f0297306864b343dc4bdd5cae1c6cdaabeb7a5bb043cc592c76f11c5b42cfce4e7895bbfb3e92c51a263072203ca7e46b5eb3640633856474afbdc22798db2ba
7
- data.tar.gz: de6096010a2802088ea9d8cee6fa9105c1b6622f4de8b89274668a5231645fd19727ad7633c74db66fbc6be04f70009f0d4d270f8ba4440189ff282f168d1313
6
+ metadata.gz: 6544102e41ff1bde04737e18f93de5ea0ee657c47335f7eeb12316162ca33fbf2e0d5b0dc1572fccc5174373781b1368508d606641286c88ebe2d7f9f2232079
7
+ data.tar.gz: eb3cb4702ea44ba8759ab9774a1ca445684b9d6ce7043153b4823657c191fff45c6db267ee74b1ed20cdec2064248b60a0f5112827b92edfa4aaecaa495fa093
@@ -0,0 +1,8 @@
1
+ id: "39"
2
+ name: EA theme 39 connector labels
3
+ font:
4
+ unit: px
5
+ label_font:
6
+ family: Cambria
7
+ size: 7
8
+ unit: pt
@@ -8,6 +8,113 @@ module Ea
8
8
  module Routing
9
9
  private
10
10
 
11
+ # An empty custom-line Path has no authored corner to preserve.
12
+ # Keep normalized port routes; replace only the computed EDGE2 jog.
13
+ def direct_empty_path_ray(model, link_row, diagram_row, geom, context)
14
+ return model if context
15
+ return model unless empty_path_ray_candidate?(model, link_row, geom)
16
+
17
+ source, target = stored_ray_boxes(link_row, diagram_row)
18
+ return model unless empty_path_ray_gap?(source, target, geom)
19
+
20
+ model.waypoints = [model.source_port, model.target_port].map do |point|
21
+ Ea::Model::Waypoint.new(position: point)
22
+ end
23
+ set_stored_ray_origins(model, source, target, geom)
24
+ end
25
+
26
+ def empty_path_ray_gap?(source, target, geom)
27
+ return false unless source.x + source.width < target.x
28
+
29
+ [stored_ray_origin(source, geom[:sx], geom[:sy]),
30
+ stored_ray_origin(target, geom[:ex], geom[:ey])].all? do |origin|
31
+ within_height?(origin.y, source) && within_height?(origin.y, target)
32
+ end
33
+ end
34
+
35
+ def empty_path_ray_candidate?(model, link_row, geom)
36
+ %w[Generalization Aggregation].include?(model.connector_type) &&
37
+ model.waypoints.size == 3 && empty_path_ray_style?(model.style, link_row, geom) &&
38
+ model.has_geometry_offsets && model.source_port && model.target_port
39
+ end
40
+
41
+ def empty_path_ray_style?(style, link_row, geom)
42
+ style[:mode] == "3" && style[:lwidth] == "2" && !style[:tree] &&
43
+ geom[:edge] == 2 && link_row.path.to_s.empty?
44
+ end
45
+
46
+ # Stored SY/EY use mathematical Y; ray origins use screen Y.
47
+ # Computed-jog origins are already normalized to their derived ports.
48
+ def correct_stored_ray_origins(model, link_row, diagram_row, geom, context)
49
+ return model if context
50
+ return model unless stored_ray_candidate?(model, link_row, geom)
51
+
52
+ source, target = stored_ray_boxes(link_row, diagram_row)
53
+ return model unless stored_ray_gap?(source, target, model.style[:mode], geom[:edge])
54
+
55
+ set_stored_ray_origins(model, source, target, geom)
56
+ end
57
+
58
+ def stored_ray_boxes(link_row, diagram_row)
59
+ connector = database.find_connector(link_row.connectorid)
60
+ [source_placement_for(connector, diagram_row),
61
+ target_placement_for(connector, diagram_row)].map { |placement| bounds_from_rect(placement) }
62
+ end
63
+
64
+ def set_stored_ray_origins(model, source, target, geom)
65
+ model.source_anchor = stored_ray_origin(source, geom[:sx], geom[:sy])
66
+ model.target_anchor = stored_ray_origin(target, geom[:ex], geom[:ey])
67
+ model
68
+ end
69
+
70
+ def stored_ray_candidate?(model, link_row, geom)
71
+ %w[Generalization Aggregation].include?(model.connector_type) &&
72
+ model.waypoints.size == 2 && stored_ray_style?(model.style, link_row) &&
73
+ stored_ray_geometry?(model, geom) && !normalized_port_anchors?(model)
74
+ end
75
+
76
+ def stored_ray_style?(style, link_row)
77
+ %w[1 3].include?(style[:mode]) && !style[:tree] &&
78
+ (style[:mode] == "1" || link_row.path.to_s.empty?)
79
+ end
80
+
81
+ def stored_ray_geometry?(model, geom)
82
+ model.has_geometry_offsets && model.source_port && model.target_port &&
83
+ (!geom[:sy].to_i.zero? || !geom[:ey].to_i.zero?)
84
+ end
85
+
86
+ def normalized_port_anchors?(model)
87
+ same_port_anchor?(model.source_anchor, model.source_port) &&
88
+ same_port_anchor?(model.target_anchor, model.target_port)
89
+ end
90
+
91
+ def same_port_anchor?(anchor, port)
92
+ anchor.x == port.x && anchor.y == port.y
93
+ end
94
+
95
+ def stored_ray_gap?(source, target, mode, edge)
96
+ return horizontal_ray_gap?(source, target) || vertical_ray_gap?(source, target) if mode == "1"
97
+
98
+ if edge == 1
99
+ target.y + target.height < source.y
100
+ elsif edge == 4
101
+ target.x + target.width < source.x
102
+ end
103
+ end
104
+
105
+ def horizontal_ray_gap?(source, target)
106
+ source.x + source.width < target.x || target.x + target.width < source.x
107
+ end
108
+
109
+ def vertical_ray_gap?(source, target)
110
+ source.y + source.height < target.y || target.y + target.height < source.y
111
+ end
112
+
113
+ def stored_ray_origin(bounds, x_offset, y_offset)
114
+ Ea::Model::Point.new(x: bounds.x + bounds.width / 2 + x_offset.to_i,
115
+ y: bounds.y + bounds.height / 2 - y_offset.to_i)
116
+ end
117
+
11
118
  # Square routing keeps the stored orthogonal trunk, even when a
12
119
  # shorter facing-edge route exists between the same boxes.
13
120
  def stored_square_route(link_row, connector, source, target)
@@ -16,16 +16,11 @@ module Ea
16
16
 
17
17
  attr_reader :database
18
18
 
19
- # Generalizations whose EA CustomProperties carry
20
- # isSubstitutable draw '{isSubstitutable=false}' at the label
21
- # seat (t_xref 'connector property' rows keyed by connector
22
- # guid; VALU 0/empty both mean false).
19
+ # This flag requests the false annotation, not the property's Boolean
20
+ # value. An explicit true (-1) must not request a false annotation.
23
21
  def substitutable_connector?(conn)
24
- @substitutable_guids ||= database.collections[:xrefs]
25
- .select do |x|
26
- x.xref_type == "connector property" &&
27
- x.name == "CustomProperties" &&
28
- x.description.to_s.include?("isSubstitutable")
22
+ @substitutable_guids ||= database.collections[:xrefs].select do |xref|
23
+ false_substitutability_xref?(xref)
29
24
  end.map(&:client).to_set
30
25
  @substitutable_guids.include?(conn.ea_guid)
31
26
  end
@@ -99,6 +94,22 @@ module Ea
99
94
 
100
95
  private
101
96
 
97
+ def false_substitutability_xref?(xref)
98
+ return false unless xref.xref_type == "connector property" && xref.name == "CustomProperties"
99
+
100
+ description = xref.description.to_s
101
+ description.include?("isSubstitutable") && !true_substitutability_property?(description)
102
+ end
103
+
104
+ def true_substitutability_property?(description)
105
+ description.scan(/@PROP=(.*?)@ENDPROP;/m).any? do |match|
106
+ property = match.first
107
+ property.include?("@NAME=isSubstitutable@ENDNAME;") &&
108
+ property.include?("@TYPE=Boolean@ENDTYPE;") &&
109
+ property[/@VALU=(.*?)@ENDVALU;/m, 1] == "-1"
110
+ end
111
+ end
112
+
102
113
  def build_elements(diagram_row)
103
114
  objs = database.diagram_objects_for(diagram_row.diagram_id) || []
104
115
  objs.map { |obj_row| build_element(obj_row, diagram_row) }
@@ -385,7 +396,7 @@ module Ea
385
396
  geom = parse_geometry_fields(link_row.geometry)
386
397
  context = ContextDiagramSupport.package_context(diagram_row) ||
387
398
  ContextDiagramSupport.element_regenerate?(diagram_row)
388
- collapse_computed_jog(Ea::Model::DiagramConnector.new(
399
+ model = collapse_computed_jog(Ea::Model::DiagramConnector.new(
389
400
  id: IdNormalizer.synthetic("dc", diagram_row.diagram_id,
390
401
  link_row.instance_id),
391
402
  diagram_id: IdNormalizer.from_guid(diagram_row.ea_guid),
@@ -410,6 +421,8 @@ module Ea
410
421
  has_geometry_offsets: geometry_has_offsets?(link_row.geometry),
411
422
  is_substitutable: substitutable_connector?(connector)
412
423
  ), link_row, diagram_row, geom)
424
+ model = direct_empty_path_ray(model, link_row, diagram_row, geom, context)
425
+ correct_stored_ray_origins(model, link_row, diagram_row, geom, context)
413
426
  end
414
427
 
415
428
  # Ray origin for one connector end: the element's center
@@ -106,7 +106,7 @@ module Ea
106
106
  def outline_connector?(connector, bounds)
107
107
  return true if %w[Generalization Aggregation].include?(connector.connector_type)
108
108
 
109
- return false unless stored_association?(connector)
109
+ return false unless stored_association?(connector) || offset_free_association?(connector)
110
110
 
111
111
  # Diagonal Association rounding and tree docks keep their existing rules.
112
112
  pairs = clipped_pairs(bounds, [connector.source_anchor, connector.target_anchor])
@@ -122,6 +122,20 @@ module Ea
122
122
  end
123
123
  private_class_method :stored_association?
124
124
 
125
+ def offset_free_association?(connector)
126
+ style = connector.style.to_h
127
+ connector.connector_type == "Association" && style[:mode] == "3" && !style[:tree] &&
128
+ %i[direct regenerated].none? { |key| style.key?(key) } && offset_free_anchors?(connector)
129
+ end
130
+ private_class_method :offset_free_association?
131
+
132
+ def offset_free_anchors?(connector)
133
+ !connector.has_geometry_offsets &&
134
+ [connector.source_port, connector.target_port].all?(&:nil?) &&
135
+ [connector.source_anchor, connector.target_anchor].all?
136
+ end
137
+ private_class_method :offset_free_anchors?
138
+
125
139
  def clipped_pairs(bounds, anchors)
126
140
  source, target = bounds
127
141
  source_anchor, target_anchor = anchors
@@ -195,18 +209,46 @@ module Ea
195
209
  divider_y_by_ref[target_ref]]
196
210
  return nil unless bounds.all? && dividers.all?
197
211
 
198
- adjusted = row_slot_pairs(pairs, bounds, dividers)
212
+ preserve_source = explicit_horizontal_source_dock?(pairs, connector)
213
+ adjusted = row_slot_pairs(pairs, bounds, dividers, preserve_source: preserve_source)
199
214
  if stroke_outline?(connector, bounds) &&
200
215
  pairs == pairs_for(connector, diagram, model_index, bounds_map)
201
216
  original = clipped_pairs(bounds, [connector.source_anchor, connector.target_anchor])
202
- original_final = row_slot_pairs(original, bounds, dividers)
217
+ original_final = row_slot_pairs(original, bounds, dividers, preserve_source: preserve_source)
203
218
  adjusted = original_final if original != pairs && !forward_route?(original_final, adjusted)
204
219
  end
205
220
  adjusted == pairs ? nil : adjusted
206
221
  end
207
222
 
208
- def row_slot_pairs(pairs, bounds, dividers)
223
+ # A supplied horizontal port already specifies its row. Moving it
224
+ # to the next compartment row would discard that explicit coordinate.
225
+ def explicit_horizontal_source_dock?(pairs, connector)
226
+ return false unless explicit_horizontal_ray?(connector)
227
+
228
+ connector.source_anchor.y == connector.target_anchor.y &&
229
+ connector.source_port.y == connector.source_anchor.y &&
230
+ pairs.first[1] == connector.source_port.y
231
+ end
232
+ private_class_method :explicit_horizontal_source_dock?
233
+
234
+ def explicit_horizontal_ray?(connector)
235
+ explicit_horizontal_ray_style?(connector) && connector.has_geometry_offsets &&
236
+ connector.source_port && connector.target_port &&
237
+ connector.source_anchor && connector.target_anchor
238
+ end
239
+ private_class_method :explicit_horizontal_ray?
240
+
241
+ def explicit_horizontal_ray_style?(connector)
242
+ style = connector.style.to_h
243
+ %w[Generalization Aggregation].include?(connector.connector_type) &&
244
+ style[:mode] == "3" && !style[:tree] && !style[:direct] && !style[:regenerated]
245
+ end
246
+ private_class_method :explicit_horizontal_ray_style?
247
+
248
+ def row_slot_pairs(pairs, bounds, dividers, preserve_source: false)
209
249
  pairs.each_with_index.map do |(x, y), index|
250
+ next [x, y] if preserve_source && index.zero?
251
+
210
252
  row_slot?(x, y, bounds[index], dividers[index]) ? [x, y + ROW_SLOT_PITCH] : [x, y]
211
253
  end
212
254
  end
@@ -86,11 +86,15 @@ module Ea
86
86
  stereo = stereo.nil? || stereo.empty? ? nil : "«#{stereo}»"
87
87
  direction = leg_direction(end_kind, heading)
88
88
  if direction && !(tree && zero_offset?(text_box, mult_box))
89
- text_pos = source_property_seat(connector, direction, anchor, text_box, role) ||
89
+ text_pos = plain_vertical_role(connector, direction, anchor, text_box, role) ||
90
+ source_property_seat(connector, direction, anchor, text_box, role) ||
90
91
  plain_upward_seat(connector, direction, anchor, text_box, role) ||
91
92
  role_seat(direction, anchor, text_box, role) || text_pos
92
93
  mult_pos = directional_multiplicity(connector, direction, anchor, mult_box, mult) ||
93
94
  mult_seat(direction, anchor, mult_box) || mult_pos
95
+ elsif end_kind == :target && !(tree && zero_offset?(text_box, mult_box))
96
+ text_pos = target_upward_role(connector, heading, anchor, text_box, role) || text_pos
97
+ mult_pos = target_multiplicity_seat(heading, anchor, mult_box) || mult_pos
94
98
  end
95
99
 
96
100
  build_texts(role:, mult:, standalone:,
@@ -199,6 +203,36 @@ module Ea
199
203
  [x - SEAT_X_INSET - width, y - 6]
200
204
  end
201
205
 
206
+ def target_upward_role(connector, heading, anchor, box, role)
207
+ return nil unless heading && heading[0].zero? && heading[1].negative?
208
+
209
+ plain_vertical_role(connector, :target_up, anchor, box, role)
210
+ end
211
+
212
+ def plain_vertical_role(connector, direction, anchor, box, role)
213
+ return nil unless %i[down target_up].include?(direction) && plain_vertical_box?(box)
214
+
215
+ end_kind = direction == :down ? :source : :target
216
+ return nil unless plain_vertical_connector?(connector, end_kind)
217
+
218
+ width = Ea::Fonts::Metrics.text_length(role, font_size, family: font_family)
219
+ return nil unless width
220
+
221
+ x, y = seat_corner(anchor, box)
222
+ [x - width - 8, y + (end_kind == :source ? 15 : -7)]
223
+ end
224
+
225
+ def plain_vertical_box?(box)
226
+ seat_font? && cambria_box?(box) && [0, 1].include?(box["alignment"])
227
+ end
228
+
229
+ def plain_vertical_connector?(connector, end_kind)
230
+ style = connector.style.to_h
231
+ connector.connector_type == "Association" &&
232
+ connector_source_dest_stereotype(connector, end_kind).empty? &&
233
+ style[:mode] == "3" && style[:lwidth] == "0" && !style[:tree]
234
+ end
235
+
202
236
  def plain_role_route?(style)
203
237
  %w[1 3].include?(style[:mode]) && %w[0 2].include?(style[:lwidth]) &&
204
238
  [nil, "V"].include?(style[:tree])
@@ -251,6 +285,14 @@ module Ea
251
285
  [x - width - 3, y + 15] if width
252
286
  end
253
287
 
288
+ def target_multiplicity_seat(heading, anchor, box)
289
+ return nil unless seat_font?
290
+ return nil unless heading && heading[0].zero? && heading[1].negative?
291
+ return nil unless cambria_box?(box) && stored_offsets_zero?(box)
292
+
293
+ [anchor[0] + 8, anchor[1] - 7]
294
+ end
295
+
254
296
  def cambria_box?(box)
255
297
  box && default_presentation?(box) && font_family.to_s.casecmp("Cambria").zero?
256
298
  end
@@ -96,6 +96,9 @@ module Ea
96
96
  LINE_PITCH = 13
97
97
 
98
98
  def label_position(connector, points)
99
+ name_seat = stored_name_position(connector, points)
100
+ return name_seat if name_seat
101
+
99
102
  return midpoint(points) unless default_substitutable_seat?(connector) &&
100
103
  upward_four_point_route?(points)
101
104
 
@@ -103,6 +106,40 @@ module Ea
103
106
  [source[0] + 3, ((source[1] + bend[1]) / 2).floor - 1]
104
107
  end
105
108
 
109
+ # At 7pt, a centered name's stored corner is 3px below its baseline.
110
+ def stored_name_position(connector, points)
111
+ box = stored_name_box(connector)
112
+ return nil unless box
113
+
114
+ width = Ea::Fonts::Metrics.text_length(midpoint_content(connector), font_size, family: font_family)
115
+ return nil unless width
116
+
117
+ x, y = midpoint(points)
118
+ offset_x, offset_y = box.values_at("ox", "oy").map(&:to_i)
119
+ [x + offset_x - (width / 2.0).floor, y + offset_y - 3]
120
+ end
121
+
122
+ def stored_name_box(connector)
123
+ return nil unless connector.is_a?(Ea::Model::DiagramConnector) && measured_name_font?
124
+ return nil if connector.is_substitutable || stereotype_label(connector)
125
+ return nil if midpoint_content(connector).include?(": ")
126
+
127
+ box = connector.label_boxes.to_h[:lmt]
128
+ box if default_name_box?(box)
129
+ end
130
+
131
+ def measured_name_font?
132
+ font_family.to_s.casecmp("Cambria").zero? && font_size == 7 && font_unit == "pt"
133
+ end
134
+
135
+ def default_name_box?(box)
136
+ return false unless box && box["alignment"] == 1
137
+ return false unless box.values_at("ox", "oy").all?
138
+ return false unless [nil, 0, 1].include?(box["direction"]) && [nil, -1].include?(box["color"])
139
+
140
+ box["rotation"].to_i.zero? && box.values_at("bold", "italic", "underline", "hidden").none?
141
+ end
142
+
106
143
  def default_substitutable_seat?(connector)
107
144
  connector.is_a?(Ea::Model::DiagramConnector) && connector.is_substitutable &&
108
145
  connector.connector_type == "Generalization" && font_size == 7 &&
@@ -97,6 +97,7 @@ module Ea
97
97
  mult_box: tgt_mb,
98
98
  anchor: target_pt, connector: connector,
99
99
  end_kind: :target, tree: tree && tree[:target],
100
+ heading: stored_tgt && leg_heading(points.reverse),
100
101
  stored_labels: stored_labels).each { |t| texts << t }
101
102
  texts
102
103
  end
@@ -112,11 +113,21 @@ module Ea
112
113
  { source: true, target: true }
113
114
  end
114
115
 
115
- # Clipping can slant a stored vertical leg. Keep its label direction
116
- # while the label position follows the final emitted endpoint.
116
+ # A diagonal clipped leg keeps the seat of its unambiguous exit face.
117
+ # Unknown faces retain the stored-upward fallback.
117
118
  def source_heading(connector, points)
118
119
  heading = leg_heading(points)
119
- return heading unless points.size == 2 && heading[0].nonzero? && heading[1].negative?
120
+ return heading unless points.size == 2 && heading.none?(&:zero?)
121
+
122
+ box = source_dock_bounds(connector)
123
+ face = box && dock_face_heading(points.first, box)
124
+ return face if face && face.zip(heading).any? { |axis, delta| (axis * delta).positive? }
125
+
126
+ stored_upward_heading(connector, heading)
127
+ end
128
+
129
+ def stored_upward_heading(connector, heading)
130
+ return heading unless heading[1].negative?
120
131
 
121
132
  stored = waypoint_pairs(connector)
122
133
  return heading unless stored.size == 2
@@ -125,6 +136,49 @@ module Ea
125
136
  original[0].zero? && original[1].negative? ? original : heading
126
137
  end
127
138
 
139
+ def source_dock_bounds(connector)
140
+ return nil unless model_index && drawn_bounds
141
+
142
+ relationship = model_index[connector.relationship_ref]
143
+ return nil unless relationship
144
+
145
+ source, target = ConnectorRedock.end_refs(relationship)
146
+ return nil if source == target
147
+ return nil unless drawn_bounds[target]
148
+
149
+ drawn_bounds[source]
150
+ end
151
+
152
+ def dock_face_heading(point, box)
153
+ return nil unless box.width.positive? && box.height.positive?
154
+
155
+ vertical_dock_heading(point, box) || horizontal_dock_heading(point, box)
156
+ end
157
+
158
+ def vertical_dock_heading(point, box)
159
+ x, y = point
160
+ left = box.x
161
+ return nil unless x > left && x < left + box.width
162
+
163
+ top = box.y
164
+ return [0, -1] if [top, top - 1].include?(y)
165
+
166
+ bottom = top + box.height
167
+ [0, 1] if [bottom, bottom + 1].include?(y)
168
+ end
169
+
170
+ def horizontal_dock_heading(point, box)
171
+ x, y = point
172
+ top = box.y
173
+ return nil unless y > top && y < top + box.height
174
+
175
+ left = box.x
176
+ return [-1, 0] if [left, left - 1].include?(x)
177
+
178
+ right = left + box.width
179
+ [1, 0] if [right, right + 1].include?(x)
180
+ end
181
+
128
182
  def leg_heading(points)
129
183
  [points[1][0] - points[0][0], points[1][1] - points[0][1]]
130
184
  end
@@ -172,7 +226,7 @@ module Ea
172
226
  theme: theme,
173
227
  font_family: label_font_family,
174
228
  font_size: label_font_size,
175
- font_unit: theme.font_size_unit
229
+ font_unit: theme.label_font_size_unit
176
230
  )
177
231
  end
178
232
 
@@ -182,7 +236,7 @@ module Ea
182
236
  model_index: model_index,
183
237
  font_family: label_font_family,
184
238
  font_size: label_font_size,
185
- font_unit: theme.font_size_unit
239
+ font_unit: theme.label_font_size_unit
186
240
  )
187
241
  end
188
242
 
@@ -212,7 +266,7 @@ module Ea
212
266
  end
213
267
 
214
268
  def label_font_size
215
- theme.font_size || DEFAULT_SIZE
269
+ theme.label_font_size || DEFAULT_SIZE
216
270
  end
217
271
  end
218
272
  end
@@ -65,7 +65,8 @@ module Ea
65
65
  text_y_offset: 12, line_height: 13).freeze
66
66
 
67
67
  def initialize(id:, name: nil, font_family: nil, font_size: nil,
68
- label_font_family: nil,
68
+ label_font_family: nil, label_font_size: nil,
69
+ label_font_size_unit: nil,
69
70
  font_size_unit: "pt", text_weight_normal: 400,
70
71
  text_weight_bold: 700,
71
72
  element_font_size: 9, element_font_unit: "pt",
@@ -83,6 +84,8 @@ module Ea
83
84
  @name = name
84
85
  @font_family = font_family
85
86
  @label_font_family = label_font_family
87
+ @label_font_size = label_font_size
88
+ @label_font_size_unit = label_font_size_unit
86
89
  @font_size = font_size
87
90
  @font_size_unit = font_size_unit
88
91
  @text_weight_normal = text_weight_normal
@@ -125,8 +128,18 @@ module Ea
125
128
  @label_font_family || font_family
126
129
  end
127
130
 
131
+ def label_font_size
132
+ @label_font_size || font_size
133
+ end
134
+
135
+ def label_font_size_unit
136
+ @label_font_size_unit || font_size_unit
137
+ end
138
+
128
139
  def with(**overrides)
129
- overrides[:label_font_family] = nil if overrides.key?(:font_family) && !overrides.key?(:label_font_family)
140
+ clear_label_override(overrides, :font_family, :label_font_family)
141
+ clear_label_override(overrides, :font_size, :label_font_size)
142
+ clear_label_override(overrides, :font_size_unit, :label_font_size_unit)
130
143
  Definition.new(**to_h.merge(overrides))
131
144
  end
132
145
 
@@ -159,6 +172,8 @@ module Ea
159
172
  font_family: font_family, font_size: font_size,
160
173
  font_size_unit: font_size_unit,
161
174
  label_font_family: @label_font_family,
175
+ label_font_size: @label_font_size,
176
+ label_font_size_unit: @label_font_size_unit,
162
177
  text_weight_normal: text_weight_normal,
163
178
  text_weight_bold: text_weight_bold,
164
179
  element_font_size: element_font_size,
@@ -179,6 +194,12 @@ module Ea
179
194
  package: package, note: note, fills: fills
180
195
  }
181
196
  end
197
+
198
+ private
199
+
200
+ def clear_label_override(overrides, global_key, label_key)
201
+ overrides[label_key] = nil if overrides.key?(global_key) && !overrides.key?(label_key)
202
+ end
182
203
  end
183
204
  end
184
205
  end
@@ -10,7 +10,7 @@ module Ea
10
10
  # name: EA White Theme
11
11
  # font: { family: Carlito, size: 7, unit: pt, weight_normal: 400, ... }
12
12
  # element_font: { size: 9, unit: pt }
13
- # label_font: { family: Cambria }
13
+ # label_font: { family: Cambria, size: 7, unit: pt }
14
14
  # colors: { text: "#000000", attribute_text: "#66413F", ... }
15
15
  # strokes: { element_border: 2, connector_line: 2, ... }
16
16
  # text_metrics: { width_factor: 0.65 }
@@ -56,6 +56,8 @@ module Ea
56
56
  name: data["name"],
57
57
  font_family: font["family"],
58
58
  label_font_family: label_font["family"],
59
+ label_font_size: label_font["size"],
60
+ label_font_size_unit: label_font["unit"],
59
61
  font_size: font["size"],
60
62
  font_size_unit: font["unit"] || "pt",
61
63
  text_weight_normal: font["weight_normal"] || 400,
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.96"
7
+ VERSION = "0.6.98"
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.96
4
+ version: 0.6.98
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -501,6 +501,7 @@ files:
501
501
  - config/qea_schema.yml
502
502
  - config/stereotype_colors.yml
503
503
  - config/themes/119.yml
504
+ - config/themes/39.yml
504
505
  - config/themes/default.yml
505
506
  - docs/ea_to_uml_type_mapping.md
506
507
  - docs/xmi_qea_conversion_capabilities.md