ea 0.6.50 → 0.6.52
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 +4 -4
- data/lib/ea/model/instance_specification.rb +2 -0
- data/lib/ea/sources/qea/diagram_builder.rb +9 -21
- data/lib/ea/sources/qea/instance_builder.rb +1 -0
- data/lib/ea/svg/ea_emitter/compartment/instance_slots.rb +16 -5
- data/lib/ea/svg/ea_emitter/element/header_line_pipeline.rb +45 -7
- data/lib/ea/svg/ea_emitter/element/header_line_provider/instance_spec.rb +19 -4
- data/lib/ea/svg/ea_emitter/element/header_line_provider/name.rb +5 -1
- data/lib/ea/svg/ea_emitter/element/header_line_provider/stereotype_label.rb +4 -0
- data/lib/ea/svg/ea_emitter/elements.rb +11 -2
- data/lib/ea/svg/ea_emitter/font_resolver.rb +4 -12
- data/lib/ea/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 592e737c7721bcd03e989dc13572a79c4e8aa53aa654daad717398edb66377ef
|
|
4
|
+
data.tar.gz: 351a11a714b03d40859c3ad86000eb22efc9edde45d3bb376961fd0f1cbad44d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e6cdf30f3261579e60f8d72a48b50adf011d4a2afc6398de8edff40ee2eeb701c4a2f605262714fadd19d6e28d2144df3fb7fa51bb04ce4b9306b04beda16df1
|
|
7
|
+
data.tar.gz: 0d0cb7de7ad76775de6751904ea3de403d8d645ad3c7acfee0d4394237d0d330846927ba8d720cf7dd5293c44c9c46c3c8c9bd604119962c45dc98ffc34423bb
|
|
@@ -10,6 +10,7 @@ module Ea
|
|
|
10
10
|
attribute :name, :string
|
|
11
11
|
attribute :classifier_id, :string
|
|
12
12
|
attribute :classifier_name, :string
|
|
13
|
+
attribute :stereotype, :string
|
|
13
14
|
attribute :package_id, :string
|
|
14
15
|
attribute :package_name, :string
|
|
15
16
|
attribute :qualified_name, :string
|
|
@@ -22,6 +23,7 @@ module Ea
|
|
|
22
23
|
map "name", to: :name
|
|
23
24
|
map "classifierId", to: :classifier_id
|
|
24
25
|
map "classifierName", to: :classifier_name
|
|
26
|
+
map "stereotype", to: :stereotype
|
|
25
27
|
map "packageId", to: :package_id
|
|
26
28
|
map "packageName", to: :package_name
|
|
27
29
|
map "qualifiedName", to: :qualified_name
|
|
@@ -112,12 +112,14 @@ module Ea
|
|
|
112
112
|
end
|
|
113
113
|
|
|
114
114
|
# EA stores the element font size in tenths of a point in the
|
|
115
|
-
# objectstyle string
|
|
116
|
-
#
|
|
117
|
-
#
|
|
118
|
-
#
|
|
119
|
-
#
|
|
120
|
-
#
|
|
115
|
+
# objectstyle string, SHIFTED one point up from what it
|
|
116
|
+
# renders: explicit fontsz 100 renders 9pt (0D31AED1: every
|
|
117
|
+
# element stores 100, all 321 texts publish at 9pt), 70
|
|
118
|
+
# renders 6pt, 140 renders 13pt. 0 means "use default", and
|
|
119
|
+
# 80/90 (8pt / 9pt) are EA's classic defaults the dialogs
|
|
120
|
+
# write back when the user never customized — such elements
|
|
121
|
+
# render at the theme size. Only genuinely custom sizes are
|
|
122
|
+
# surfaced as explicit element fonts.
|
|
121
123
|
USE_DEFAULT_FONTSZ = [0, 80, 90].freeze
|
|
122
124
|
|
|
123
125
|
def font_size_from(style_hash)
|
|
@@ -126,23 +128,9 @@ module Ea
|
|
|
126
128
|
|
|
127
129
|
return nil if raw.to_i.zero?
|
|
128
130
|
|
|
129
|
-
stored = (raw.to_f / 10).round
|
|
130
|
-
return stored - 1 if note_element? && stored > 1
|
|
131
|
-
|
|
132
131
|
return nil if USE_DEFAULT_FONTSZ.include?(raw.to_i)
|
|
133
132
|
|
|
134
|
-
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
# Text/Note elements carry their real font size in fontsz
|
|
138
|
-
# shifted by one point (stored 100 renders 9pt, 80 renders
|
|
139
|
-
# 7pt - corpus-verified on EA-published note SVGs);
|
|
140
|
-
# classifiers keep the use-default convention.
|
|
141
|
-
def note_element?
|
|
142
|
-
return false unless database && @current_obj_row
|
|
143
|
-
|
|
144
|
-
obj = database.find_object(@current_obj_row.ea_object_id.to_i)
|
|
145
|
-
obj&.object_type.to_s == "Text"
|
|
133
|
+
(raw.to_f / 10).round - 1
|
|
146
134
|
end
|
|
147
135
|
|
|
148
136
|
def build_connectors(diagram_row)
|
|
@@ -36,6 +36,7 @@ module Ea
|
|
|
36
36
|
name: row.name.to_s,
|
|
37
37
|
classifier_id: classifier_id_for(row),
|
|
38
38
|
classifier_name: classifier_name_for(row),
|
|
39
|
+
stereotype: row.stereotype.to_s,
|
|
39
40
|
package_id: package_id_for(row),
|
|
40
41
|
package_name: package_name_for(row),
|
|
41
42
|
qualified_name: qualified_name_for(row),
|
|
@@ -7,9 +7,10 @@ module Ea
|
|
|
7
7
|
# Instance slot compartment. Renders each slot on an
|
|
8
8
|
# InstanceSpecification as "name op value", then appends
|
|
9
9
|
# "(from PackageName)" in italics below the slots when the
|
|
10
|
-
# instance
|
|
11
|
-
# element is not an
|
|
12
|
-
# slots nor a
|
|
10
|
+
# instance is foreign to the diagram's package. Skipped
|
|
11
|
+
# entirely when the model element is not an
|
|
12
|
+
# InstanceSpecification or has neither slots nor a
|
|
13
|
+
# classifier.
|
|
13
14
|
module InstanceSlots
|
|
14
15
|
ROW_OFFSET = 13
|
|
15
16
|
SUBTITLE_EXTRA_OFFSET = 10
|
|
@@ -21,7 +22,7 @@ module Ea
|
|
|
21
22
|
|
|
22
23
|
instance = context.model_element
|
|
23
24
|
slots = instance.slots || []
|
|
24
|
-
subtitle = from_package_subtitle(instance)
|
|
25
|
+
subtitle = from_package_subtitle(instance, context.diagram)
|
|
25
26
|
return nil if slots.empty? && subtitle.nil?
|
|
26
27
|
|
|
27
28
|
first_y = context.geometry.attr_first_y
|
|
@@ -44,7 +45,17 @@ module Ea
|
|
|
44
45
|
end
|
|
45
46
|
module_function :instance?
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
# EA draws "(from PackageName)" only for instances FOREIGN
|
|
49
|
+
# to the diagram's own package: every instance diagram in
|
|
50
|
+
# the corpus has local instances and EA renders the
|
|
51
|
+
# subtitle nowhere (FC590D99: 23 local FIG2010 instances,
|
|
52
|
+
# zero "(from" lines in the published SVG).
|
|
53
|
+
def from_package_subtitle(instance, diagram)
|
|
54
|
+
if diagram && instance.package_id && diagram.package_id &&
|
|
55
|
+
instance.package_id == diagram.package_id
|
|
56
|
+
return nil
|
|
57
|
+
end
|
|
58
|
+
|
|
48
59
|
pkg = instance.package_name
|
|
49
60
|
return nil if pkg.nil? || pkg.empty?
|
|
50
61
|
|
|
@@ -22,6 +22,7 @@ module Ea
|
|
|
22
22
|
:bounds_width, :font_size, :family,
|
|
23
23
|
:off_canvas_parent_name,
|
|
24
24
|
:foreign_package_name,
|
|
25
|
+
:suppress_stereotypes,
|
|
25
26
|
keyword_init: true)
|
|
26
27
|
|
|
27
28
|
PROVIDERS = [
|
|
@@ -39,9 +40,18 @@ module Ea
|
|
|
39
40
|
end
|
|
40
41
|
|
|
41
42
|
# Greedy word wrap measured with the header's own font.
|
|
42
|
-
# EA wraps overflowing headers at word boundaries
|
|
43
|
-
# (
|
|
43
|
+
# EA wraps overflowing headers at word boundaries and after
|
|
44
|
+
# "::" qualifiers (F851A657: class "Content
|
|
45
|
+
# information::MD_FeatureCatalogueDescription" renders as
|
|
46
|
+
# "Content information::" / "MD_FeatureCatalogueDescription";
|
|
47
|
+
# 81F92FC7: instance header "new ownership: LA_Right"
|
|
44
48
|
# renders as "new ownership:" / "LA_Right").
|
|
49
|
+
# Headers wrap against a USABLE width of box width - 6:
|
|
50
|
+
# EA wrapped "SU_PB2: LA_SpatialUnit" (parts 36 + 59 +
|
|
51
|
+
# space 4 = 99) inside a 100px box, while
|
|
52
|
+
# "FuelStation: LA_BAUnit" (94) stayed single in a 100px
|
|
53
|
+
# box - the corpus boundary is margin 5 wrapped vs 6
|
|
54
|
+
# unwrapped (FC590D99 vs CBC03448).
|
|
45
55
|
def self.wrap_words(text, context, weight)
|
|
46
56
|
font_style = weight == :bold_italic ? "italic" : "normal"
|
|
47
57
|
width = lambda do |s|
|
|
@@ -51,15 +61,17 @@ module Ea
|
|
|
51
61
|
).round
|
|
52
62
|
end
|
|
53
63
|
return [text] if context.bounds_width.nil?
|
|
54
|
-
|
|
64
|
+
usable = context.bounds_width.to_i - 6
|
|
65
|
+
return [text] if width.call(text) <= usable
|
|
55
66
|
|
|
67
|
+
segments = wrap_segments(text)
|
|
56
68
|
lines = []
|
|
57
69
|
current = +""
|
|
58
|
-
|
|
59
|
-
candidate = current
|
|
60
|
-
if !current.empty? && width.call(candidate) >
|
|
70
|
+
segments.each do |segment|
|
|
71
|
+
candidate = current + segment
|
|
72
|
+
if !current.empty? && width.call(candidate) > usable
|
|
61
73
|
lines << current
|
|
62
|
-
current =
|
|
74
|
+
current = +segment.lstrip
|
|
63
75
|
else
|
|
64
76
|
current = candidate
|
|
65
77
|
end
|
|
@@ -67,6 +79,32 @@ module Ea
|
|
|
67
79
|
lines << current unless current.empty?
|
|
68
80
|
lines
|
|
69
81
|
end
|
|
82
|
+
|
|
83
|
+
# Break opportunities: after each space and after each
|
|
84
|
+
# "::". Each segment carries the separator that PRECEDES
|
|
85
|
+
# it ("" after "::", " " after a word), so joining all
|
|
86
|
+
# segments reproduces the original text exactly.
|
|
87
|
+
def self.wrap_segments(text)
|
|
88
|
+
segments = []
|
|
89
|
+
pending = ""
|
|
90
|
+
text.split(" ").each do |word|
|
|
91
|
+
chunk = +""
|
|
92
|
+
word.split(/(::)/).each_slice(2) do |part, sep|
|
|
93
|
+
chunk << pending << part
|
|
94
|
+
pending = ""
|
|
95
|
+
if sep
|
|
96
|
+
chunk << sep
|
|
97
|
+
segments << chunk.dup
|
|
98
|
+
chunk.clear
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
unless chunk.empty?
|
|
102
|
+
segments << chunk.dup
|
|
103
|
+
pending = " "
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
segments
|
|
107
|
+
end
|
|
70
108
|
end
|
|
71
109
|
end
|
|
72
110
|
end
|
|
@@ -6,22 +6,37 @@ module Ea
|
|
|
6
6
|
module Element
|
|
7
7
|
module HeaderLineProvider
|
|
8
8
|
# InstanceSpecification: short-circuits the pipeline. The
|
|
9
|
-
# header for an instance is
|
|
10
|
-
#
|
|
11
|
-
#
|
|
9
|
+
# header for an instance is its own «stereotype» line (the
|
|
10
|
+
# t_object.Stereotype value, verbatim - «featureType»,
|
|
11
|
+
# «FeatureType», «type» …) followed by "name: Classifier"
|
|
12
|
+
# on one bold line, word-wrapped when it overflows the box
|
|
13
|
+
# (81F92FC7: "new ownership:" / "LA_Right"). The stereotype
|
|
14
|
+
# line is suppressed by the diagram pdata flag
|
|
15
|
+
# HideEStereo=1 (FC590D99 shows it; the Annex C "Case"
|
|
16
|
+
# diagrams with HideEStereo=1 show none).
|
|
12
17
|
class InstanceSpec
|
|
13
18
|
def self.call(context)
|
|
14
19
|
inst = context.classifier
|
|
15
20
|
return [] unless inst.is_a?(Ea::Model::InstanceSpecification)
|
|
16
21
|
|
|
22
|
+
lines = stereotype_lines(inst, context)
|
|
17
23
|
label = inst.name.to_s
|
|
18
24
|
if inst.classifier_name && !inst.classifier_name.empty?
|
|
19
25
|
label = "#{label}: #{inst.classifier_name}"
|
|
20
26
|
end
|
|
21
|
-
HeaderLinePipeline.wrap_words(label, context, :bold).map do |line|
|
|
27
|
+
lines + HeaderLinePipeline.wrap_words(label, context, :bold).map do |line|
|
|
22
28
|
[line, :bold]
|
|
23
29
|
end
|
|
24
30
|
end
|
|
31
|
+
|
|
32
|
+
def self.stereotype_lines(inst, context)
|
|
33
|
+
return [] if context.suppress_stereotypes
|
|
34
|
+
|
|
35
|
+
st = inst.stereotype.to_s
|
|
36
|
+
return [] if st.empty?
|
|
37
|
+
|
|
38
|
+
[["«#{st}»", :normal]]
|
|
39
|
+
end
|
|
25
40
|
end
|
|
26
41
|
end
|
|
27
42
|
end
|
|
@@ -58,7 +58,11 @@ module Ea
|
|
|
58
58
|
name, context.font_size || 9, nil,
|
|
59
59
|
family: context.family, weight: "700", style: font_style
|
|
60
60
|
).round
|
|
61
|
-
|
|
61
|
+
# Same usable width as wrap_words: box width - 6.
|
|
62
|
+
# EA wraps "Content information::MD_FeatureCatalogue-
|
|
63
|
+
# Description" (parts 86 + 137 + space 4 = 227) inside
|
|
64
|
+
# a 230px box (F851A657).
|
|
65
|
+
len > context.bounds_width.to_i - 6
|
|
62
66
|
end
|
|
63
67
|
|
|
64
68
|
# EA renders the plain classifier name in element headers;
|
|
@@ -17,6 +17,10 @@ module Ea
|
|
|
17
17
|
def self.call(context)
|
|
18
18
|
classifier = context.classifier
|
|
19
19
|
return [] if classifier.is_a?(Ea::Model::InstanceSpecification)
|
|
20
|
+
# Diagram pdata HideEStereo=1 suppresses classifier
|
|
21
|
+
# stereotype lines too (the Annex C "Case" diagrams
|
|
22
|
+
# render zero «...» lines, classes included).
|
|
23
|
+
return [] if context.suppress_stereotypes
|
|
20
24
|
|
|
21
25
|
labels = label_for(classifier, context.umldi_keyword)
|
|
22
26
|
return [] if labels.empty?
|
|
@@ -126,7 +126,8 @@ module Ea
|
|
|
126
126
|
font_size: size,
|
|
127
127
|
family: family,
|
|
128
128
|
off_canvas_parent_name: parent_name,
|
|
129
|
-
foreign_package_name: foreign_package_name_for(classifier)
|
|
129
|
+
foreign_package_name: foreign_package_name_for(classifier),
|
|
130
|
+
suppress_stereotypes: suppress_stereotypes?) : []
|
|
130
131
|
is_classifier = classifier.is_a?(Ea::Model::Classifier)
|
|
131
132
|
attr_lines = if is_classifier && show_attributes? &&
|
|
132
133
|
!classifier.is_a?(Ea::Model::Interface) &&
|
|
@@ -262,7 +263,7 @@ module Ea
|
|
|
262
263
|
first = header_lines.first
|
|
263
264
|
return theme.compartments.header_top_padding unless first
|
|
264
265
|
|
|
265
|
-
if first.last == :italic
|
|
266
|
+
if first.last == :italic || first.last == :italic_tight
|
|
266
267
|
6 # ghost-led: EA seats the right-aligned ghost at +13
|
|
267
268
|
elsif first.first.to_s.start_with?("«")
|
|
268
269
|
enumeration_or_interface?(classifier) ? 6 : 9
|
|
@@ -276,6 +277,14 @@ module Ea
|
|
|
276
277
|
classifier.is_a?(Ea::Model::Interface)
|
|
277
278
|
end
|
|
278
279
|
|
|
280
|
+
# t_diagram pdata HideEStereo=1 suppresses every stereotype
|
|
281
|
+
# line on the diagram - instance boxes ("Case C25" renders
|
|
282
|
+
# no «featureType» despite every instance carrying one) and
|
|
283
|
+
# classifier boxes alike.
|
|
284
|
+
def suppress_stereotypes?
|
|
285
|
+
diagram.style.to_s.match?(/HideEStereo=1/)
|
|
286
|
+
end
|
|
287
|
+
|
|
279
288
|
# A stereotype group header («Property») sits 4px below where
|
|
280
289
|
# plain rows start (divider+18 vs divider+14 at 7pt).
|
|
281
290
|
def attr_first_offset_for(attr_lines)
|
|
@@ -42,8 +42,11 @@ module Ea
|
|
|
42
42
|
explicit = element&.font_size
|
|
43
43
|
explicit = nil if explicit&.zero?
|
|
44
44
|
return explicit if explicit
|
|
45
|
-
return diagram_default_size if diagram_default_size
|
|
46
45
|
|
|
46
|
+
# An element's own explicit fontsz never re-sizes its
|
|
47
|
+
# neighbours: default elements always render at the theme
|
|
48
|
+
# size (9BA1CE54: one 9pt label + six use-default
|
|
49
|
+
# instances; EA publishes the six at 7pt).
|
|
47
50
|
DEFAULT_ELEMENT_FONT_SIZE
|
|
48
51
|
end
|
|
49
52
|
|
|
@@ -77,13 +80,6 @@ module Ea
|
|
|
77
80
|
most_common(families)
|
|
78
81
|
end
|
|
79
82
|
|
|
80
|
-
def diagram_default_size
|
|
81
|
-
sizes = element_sizes
|
|
82
|
-
return nil if sizes.empty?
|
|
83
|
-
|
|
84
|
-
most_common(sizes)
|
|
85
|
-
end
|
|
86
|
-
|
|
87
83
|
FALLBACK_DEFAULT_FAMILY = "Calibri"
|
|
88
84
|
|
|
89
85
|
def most_common(values)
|
|
@@ -97,10 +93,6 @@ module Ea
|
|
|
97
93
|
(diagram.elements || []).map(&:font_family).compact
|
|
98
94
|
end
|
|
99
95
|
|
|
100
|
-
def element_sizes
|
|
101
|
-
(diagram.elements || []).map(&:font_size).compact.reject(&:zero?)
|
|
102
|
-
end
|
|
103
|
-
|
|
104
96
|
def tally_values(values)
|
|
105
97
|
values.each_with_object(Hash.new(0)) { |v, acc| acc[v] += 1 }
|
|
106
98
|
end
|
data/lib/ea/version.rb
CHANGED