ea 0.6.95 → 0.6.96
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/config/themes/119.yml +3 -0
- data/lib/ea/fonts/cambria_7_regular_widths.json +77 -0
- data/lib/ea/fonts/metrics.rb +36 -1
- data/lib/ea/sources/qea/diagram_builder/routing.rb +109 -0
- data/lib/ea/sources/qea/diagram_builder.rb +3 -1
- data/lib/ea/svg/ea_emitter/label/end_label.rb +98 -10
- data/lib/ea/svg/ea_emitter/labels.rb +15 -2
- data/lib/ea/theme/definition.rb +8 -0
- data/lib/ea/theme/loader.rb +3 -0
- data/lib/ea/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3b98c8ee8bf786f5160291763db47b38d8213a029ef4d647df1d73c386551dba
|
|
4
|
+
data.tar.gz: 3f0ee88fdb07d831f70e1ec51f634048140887a5231baa3dc0eea778a7f60d7a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f0297306864b343dc4bdd5cae1c6cdaabeb7a5bb043cc592c76f11c5b42cfce4e7895bbfb3e92c51a263072203ca7e46b5eb3640633856474afbdc22798db2ba
|
|
7
|
+
data.tar.gz: de6096010a2802088ea9d8cee6fa9105c1b6622f4de8b89274668a5231645fd19727ad7633c74db66fbc6be04f70009f0d4d270f8ba4440189ff282f168d1313
|
data/config/themes/119.yml
CHANGED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
" ": 2,
|
|
3
|
+
"(": 4,
|
|
4
|
+
")": 4,
|
|
5
|
+
"*": 4,
|
|
6
|
+
"+": 6,
|
|
7
|
+
"-": 3,
|
|
8
|
+
".": 2,
|
|
9
|
+
"0": 6,
|
|
10
|
+
"1": 6,
|
|
11
|
+
"2": 6,
|
|
12
|
+
"3": 6,
|
|
13
|
+
"4": 6,
|
|
14
|
+
"5": 6,
|
|
15
|
+
"6": 6,
|
|
16
|
+
"8": 6,
|
|
17
|
+
"9": 6,
|
|
18
|
+
":": 3,
|
|
19
|
+
"<": 6,
|
|
20
|
+
"=": 6,
|
|
21
|
+
">": 6,
|
|
22
|
+
"A": 6,
|
|
23
|
+
"B": 6,
|
|
24
|
+
"C": 6,
|
|
25
|
+
"D": 7,
|
|
26
|
+
"E": 6,
|
|
27
|
+
"F": 5,
|
|
28
|
+
"G": 6,
|
|
29
|
+
"H": 7,
|
|
30
|
+
"I": 3,
|
|
31
|
+
"K": 6,
|
|
32
|
+
"L": 5,
|
|
33
|
+
"M": 8,
|
|
34
|
+
"N": 7,
|
|
35
|
+
"O": 7,
|
|
36
|
+
"P": 6,
|
|
37
|
+
"Q": 7,
|
|
38
|
+
"R": 6,
|
|
39
|
+
"S": 5,
|
|
40
|
+
"T": 6,
|
|
41
|
+
"U": 6,
|
|
42
|
+
"V": 6,
|
|
43
|
+
"W": 9,
|
|
44
|
+
"X": 6,
|
|
45
|
+
"Y": 6,
|
|
46
|
+
"Z": 5,
|
|
47
|
+
"[": 4,
|
|
48
|
+
"]": 4,
|
|
49
|
+
"a": 5,
|
|
50
|
+
"b": 5,
|
|
51
|
+
"c": 4,
|
|
52
|
+
"d": 6,
|
|
53
|
+
"e": 5,
|
|
54
|
+
"f": 3,
|
|
55
|
+
"g": 5,
|
|
56
|
+
"h": 6,
|
|
57
|
+
"i": 3,
|
|
58
|
+
"j": 3,
|
|
59
|
+
"k": 5,
|
|
60
|
+
"l": 3,
|
|
61
|
+
"m": 8,
|
|
62
|
+
"n": 6,
|
|
63
|
+
"o": 5,
|
|
64
|
+
"p": 6,
|
|
65
|
+
"q": 5,
|
|
66
|
+
"r": 4,
|
|
67
|
+
"s": 4,
|
|
68
|
+
"t": 3,
|
|
69
|
+
"u": 6,
|
|
70
|
+
"v": 5,
|
|
71
|
+
"w": 8,
|
|
72
|
+
"x": 5,
|
|
73
|
+
"y": 5,
|
|
74
|
+
"z": 5,
|
|
75
|
+
"{": 4,
|
|
76
|
+
"}": 4
|
|
77
|
+
}
|
data/lib/ea/fonts/metrics.rb
CHANGED
|
@@ -29,9 +29,18 @@ module Ea
|
|
|
29
29
|
"calibri" => "Carlito",
|
|
30
30
|
"carlito" => "Carlito",
|
|
31
31
|
"arial" => "Arial",
|
|
32
|
-
"arial narrow" => "Arial Narrow"
|
|
32
|
+
"arial narrow" => "Arial Narrow",
|
|
33
|
+
"cambria" => "Cambria"
|
|
33
34
|
}.freeze
|
|
34
35
|
|
|
36
|
+
# Cambria measurements are in SVG units at 7pt, not em advances.
|
|
37
|
+
# Unmeasured characters and other sizes/styles must use the fallback.
|
|
38
|
+
CAMBRIA_7_REGULAR_WIDTHS = JSON.parse(File.read(
|
|
39
|
+
File.expand_path("cambria_7_regular_widths.json", __dir__)
|
|
40
|
+
)).freeze
|
|
41
|
+
CAMBRIA_7_GUILLEMET_PAIR_WIDTH = 10
|
|
42
|
+
private_constant :CAMBRIA_7_REGULAR_WIDTHS, :CAMBRIA_7_GUILLEMET_PAIR_WIDTH
|
|
43
|
+
|
|
35
44
|
# Advance used for codepoints missing from the tables.
|
|
36
45
|
DEFAULT_ADVANCE = 0.25
|
|
37
46
|
|
|
@@ -64,6 +73,12 @@ module Ea
|
|
|
64
73
|
return nil unless supported_family?(family)
|
|
65
74
|
return nil if text.nil? || text.empty?
|
|
66
75
|
|
|
76
|
+
return cambria_text_length(text, size_pt, weight, style) if resolve_family(family) == "Cambria"
|
|
77
|
+
|
|
78
|
+
advance_text_length(text, size_pt, family, weight, style)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def advance_text_length(text, size_pt, family, weight, style)
|
|
67
82
|
resolved = resolve_family(family)
|
|
68
83
|
if GDI_HEIGHT_FAMILIES.include?(resolved)
|
|
69
84
|
height = (size_pt.to_f * 10 / 7).round
|
|
@@ -86,6 +101,26 @@ module Ea
|
|
|
86
101
|
cjk_count * CJK_ADVANCE * size_pt.to_f).round(3)
|
|
87
102
|
end
|
|
88
103
|
|
|
104
|
+
private_class_method :advance_text_length
|
|
105
|
+
|
|
106
|
+
def cambria_text_length(text, size_pt, weight, style)
|
|
107
|
+
return nil unless size_pt.to_f.to_r == 7
|
|
108
|
+
return nil unless ["", "0", "400", "normal"].include?(weight.to_s)
|
|
109
|
+
return nil unless ["", "normal"].include?(style.to_s)
|
|
110
|
+
|
|
111
|
+
return cambria_ascii_length(text) unless text.start_with?("«") && text.end_with?("»")
|
|
112
|
+
|
|
113
|
+
inner_width = cambria_ascii_length(text[1...-1])
|
|
114
|
+
inner_width + CAMBRIA_7_GUILLEMET_PAIR_WIDTH if inner_width
|
|
115
|
+
end
|
|
116
|
+
private_class_method :cambria_text_length
|
|
117
|
+
|
|
118
|
+
def cambria_ascii_length(text)
|
|
119
|
+
widths = text.each_char.map { |char| CAMBRIA_7_REGULAR_WIDTHS[char] }
|
|
120
|
+
widths.sum unless widths.empty? || widths.include?(nil)
|
|
121
|
+
end
|
|
122
|
+
private_class_method :cambria_ascii_length
|
|
123
|
+
|
|
89
124
|
# Families whose EA driver quantizes per-glyph advances
|
|
90
125
|
# (fitted from published textLengths).
|
|
91
126
|
GDI_HEIGHT_FAMILIES = %w[Carlito].freeze
|
|
@@ -8,6 +8,115 @@ module Ea
|
|
|
8
8
|
module Routing
|
|
9
9
|
private
|
|
10
10
|
|
|
11
|
+
# Square routing keeps the stored orthogonal trunk, even when a
|
|
12
|
+
# shorter facing-edge route exists between the same boxes.
|
|
13
|
+
def stored_square_route(link_row, connector, source, target)
|
|
14
|
+
return nil unless stored_square_style?(link_row, connector)
|
|
15
|
+
|
|
16
|
+
corners = stored_square_corners(link_row)
|
|
17
|
+
square_corner_route(corners, source, target) if corners
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def stored_square_style?(link_row, connector)
|
|
21
|
+
style = DiagramStyleParser.parse(link_row.style)
|
|
22
|
+
%w[Association Aggregation Generalization].include?(connector.connector_type) &&
|
|
23
|
+
style[:mode] == "3" && style[:tree] == "OS" && style[:lwidth] == "2"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def stored_square_corners(link_row)
|
|
27
|
+
return nil unless link_row.path.to_s.match?(/\A(?:-?\d+:-?\d+;)*-?\d+:-?\d+;?\z/)
|
|
28
|
+
|
|
29
|
+
corners = intermediate_waypoints(link_row)
|
|
30
|
+
corners if corners.each_cons(2).all? { |a, b| a[0] == b[0] || a[1] == b[1] }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def square_corner_route(corners, source, target)
|
|
34
|
+
first = square_corner_dock(corners.first, source)
|
|
35
|
+
last = square_corner_dock(corners.last, target)
|
|
36
|
+
return nil unless first && last
|
|
37
|
+
|
|
38
|
+
points = corners.dup
|
|
39
|
+
points.unshift(first) unless points.first == first
|
|
40
|
+
points.push(last) unless points.last == last
|
|
41
|
+
points if points.size >= 3
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def square_corner_dock(corner, bounds)
|
|
45
|
+
x, y = corner
|
|
46
|
+
projected = [x.clamp(bounds.x, bounds.x + bounds.width),
|
|
47
|
+
y.clamp(bounds.y, bounds.y + bounds.height)]
|
|
48
|
+
return nil if projected[0] != x && projected[1] != y
|
|
49
|
+
|
|
50
|
+
projected if rectangle_edge?(projected, bounds)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def rectangle_edge?(point, bounds)
|
|
54
|
+
[bounds.x, bounds.x + bounds.width].include?(point[0]) ||
|
|
55
|
+
[bounds.y, bounds.y + bounds.height].include?(point[1])
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# A valid lateral corner joins one vertical leg and one horizontal
|
|
59
|
+
# leg. Keep it even when the boxes also admit a shorter direct route.
|
|
60
|
+
def stored_lateral_route(link_row, connector, source, target)
|
|
61
|
+
return nil unless %w[Aggregation Association].include?(connector.connector_type)
|
|
62
|
+
|
|
63
|
+
style = DiagramStyleParser.parse(link_row.style)
|
|
64
|
+
return nil unless stored_lateral_style?(style)
|
|
65
|
+
|
|
66
|
+
corner = stored_lateral_corner(link_row)
|
|
67
|
+
return nil unless corner
|
|
68
|
+
|
|
69
|
+
if style[:tree] == "LH"
|
|
70
|
+
horizontal_corner_route(corner, source, target)
|
|
71
|
+
else
|
|
72
|
+
vertical_corner_route(corner, source, target)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def stored_lateral_corner(link_row)
|
|
77
|
+
return nil unless link_row.path.to_s.match?(/\A-?\d+:-?\d+;?\z/)
|
|
78
|
+
|
|
79
|
+
intermediate_waypoints(link_row).first
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def stored_lateral_style?(style)
|
|
83
|
+
style[:mode] == "3" && %w[0 2].include?(style[:lwidth]) && %w[LH LV].include?(style[:tree])
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def horizontal_corner?(corner, source, target)
|
|
87
|
+
within_horizontal_span?(corner, source) && within_height?(corner[1], target) &&
|
|
88
|
+
!within_height?(corner[1], source) && !within_horizontal_span?(corner, target)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def horizontal_corner_route(corner, source, target)
|
|
92
|
+
return nil unless horizontal_corner?(corner, source, target)
|
|
93
|
+
|
|
94
|
+
x, y = corner
|
|
95
|
+
|
|
96
|
+
source_y = y < source.y ? source.y : source.y + source.height
|
|
97
|
+
target_x = x < target.x ? target.x : target.x + target.width
|
|
98
|
+
[[x, source_y], corner, [target_x, y]]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def vertical_corner?(corner, source, target)
|
|
102
|
+
within_height?(corner[1], source) && within_horizontal_span?(corner, target) &&
|
|
103
|
+
!within_horizontal_span?(corner, source) && !within_height?(corner[1], target)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def vertical_corner_route(corner, source, target)
|
|
107
|
+
return nil unless vertical_corner?(corner, source, target)
|
|
108
|
+
|
|
109
|
+
x, y = corner
|
|
110
|
+
|
|
111
|
+
source_x = x < source.x ? source.x : source.x + source.width
|
|
112
|
+
target_y = y < target.y ? target.y : target.y + target.height
|
|
113
|
+
[[source_x, y], corner, [x, target_y]]
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def within_height?(height, bounds)
|
|
117
|
+
height.between?(bounds.y, bounds.y + bounds.height)
|
|
118
|
+
end
|
|
119
|
+
|
|
11
120
|
# A lateral tree leaves the side edge before turning upward.
|
|
12
121
|
# Keep that dock only when its first leg actually points outward.
|
|
13
122
|
def lateral_source_exit?(points, link_row, bounds)
|
|
@@ -648,7 +648,9 @@ module Ea
|
|
|
648
648
|
# EDGE=2 routes are excluded; they are already exact.
|
|
649
649
|
src = bounds_from_rect(source_placement)
|
|
650
650
|
tgt = bounds_from_rect(target_placement)
|
|
651
|
-
tree_route =
|
|
651
|
+
tree_route = stored_square_route(link_row, connector, src, tgt) ||
|
|
652
|
+
stored_lateral_route(link_row, connector, src, tgt) ||
|
|
653
|
+
horizontal_tree_route(points, link_row, connector, src, tgt)
|
|
652
654
|
if tree_route
|
|
653
655
|
points = tree_route
|
|
654
656
|
elsif %w[Association Aggregation].include?(connector.connector_type) &&
|
|
@@ -75,11 +75,8 @@ module Ea
|
|
|
75
75
|
text_pos, mult_pos = tree_positions(end_kind, anchor, role,
|
|
76
76
|
text_pos, mult_pos)
|
|
77
77
|
elsif mult_box && stored_offsets_zero?(mult_box)
|
|
78
|
-
#
|
|
79
|
-
#
|
|
80
|
-
# endpoint anchor, regardless of line direction
|
|
81
|
-
# (87-sample corpus fit: dy = +15 constant, dx = +12
|
|
82
|
-
# dominant with a +3 minority).
|
|
78
|
+
# Start at the legacy default; measured directional seats
|
|
79
|
+
# below replace it for default Cambria labels.
|
|
83
80
|
mult_pos = [anchor[0] + 12, anchor[1] + 15]
|
|
84
81
|
end
|
|
85
82
|
# The end's stored stereotype renders verbatim («Type» on
|
|
@@ -89,8 +86,11 @@ module Ea
|
|
|
89
86
|
stereo = stereo.nil? || stereo.empty? ? nil : "«#{stereo}»"
|
|
90
87
|
direction = leg_direction(end_kind, heading)
|
|
91
88
|
if direction && !(tree && zero_offset?(text_box, mult_box))
|
|
92
|
-
text_pos =
|
|
93
|
-
|
|
89
|
+
text_pos = source_property_seat(connector, direction, anchor, text_box, role) ||
|
|
90
|
+
plain_upward_seat(connector, direction, anchor, text_box, role) ||
|
|
91
|
+
role_seat(direction, anchor, text_box, role) || text_pos
|
|
92
|
+
mult_pos = directional_multiplicity(connector, direction, anchor, mult_box, mult) ||
|
|
93
|
+
mult_seat(direction, anchor, mult_box) || mult_pos
|
|
94
94
|
end
|
|
95
95
|
|
|
96
96
|
build_texts(role:, mult:, standalone:,
|
|
@@ -147,19 +147,22 @@ module Ea
|
|
|
147
147
|
# right: the multiplicity 6px above it on an upward leg and
|
|
148
148
|
# 15px below it on a rightward one, the role 7px above it on
|
|
149
149
|
# a rightward one. Other sizes, headings, the target end,
|
|
150
|
-
# custom presentation keep their offsets
|
|
150
|
+
# custom presentation keep their offsets. Zero multiplicity boxes
|
|
151
|
+
# follow the direction for Cambria and keep the default otherwise.
|
|
151
152
|
def leg_direction(end_kind, heading)
|
|
152
153
|
return nil unless source_seat?(end_kind, heading)
|
|
153
154
|
|
|
154
155
|
dx, dy = heading
|
|
155
156
|
return :up if dx.zero? && dy.negative?
|
|
156
157
|
return :right if dy.zero? && dx.positive?
|
|
158
|
+
return :left if dy.zero? && dx.negative?
|
|
159
|
+
return :down if dx.zero? && dy.positive?
|
|
157
160
|
|
|
158
161
|
nil
|
|
159
162
|
end
|
|
160
163
|
|
|
161
164
|
# Role text on a rightward leg is left-aligned at the corner,
|
|
162
|
-
# 7px above it.
|
|
165
|
+
# 7px above it.
|
|
163
166
|
def role_seat(direction, anchor, box, role)
|
|
164
167
|
return nil unless direction == :right
|
|
165
168
|
return nil if box.nil? || role_empty?(role)
|
|
@@ -169,8 +172,93 @@ module Ea
|
|
|
169
172
|
[x + SEAT_X_INSET, y - 7]
|
|
170
173
|
end
|
|
171
174
|
|
|
175
|
+
# Property-role clusters use a direction-specific right edge;
|
|
176
|
+
# alignment controls the stereotype line within that cluster.
|
|
177
|
+
def source_property_seat(connector, direction, anchor, box, role)
|
|
178
|
+
return nil unless %i[up left down].include?(direction) && property_role_box?(box, direction)
|
|
179
|
+
return nil unless source_property_font?(connector, direction)
|
|
180
|
+
|
|
181
|
+
width = Ea::Fonts::Metrics.text_length(role, font_size, family: font_family)
|
|
182
|
+
return nil unless width
|
|
183
|
+
|
|
184
|
+
x, y = seat_corner(anchor, box)
|
|
185
|
+
inset, baseline = property_role_offsets(direction)
|
|
186
|
+
[x - inset - width, y + baseline]
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def plain_upward_seat(connector, direction, anchor, box, role)
|
|
190
|
+
return nil unless direction == :up && cambria_box?(box)
|
|
191
|
+
return nil unless [0, 1].include?(box["alignment"])
|
|
192
|
+
return nil unless connector_source_dest_stereotype(connector, :source).empty?
|
|
193
|
+
return nil unless plain_role_route?(connector.style.to_h)
|
|
194
|
+
|
|
195
|
+
width = Ea::Fonts::Metrics.text_length(role, font_size, family: font_family)
|
|
196
|
+
return nil unless width
|
|
197
|
+
|
|
198
|
+
x, y = seat_corner(anchor, box)
|
|
199
|
+
[x - SEAT_X_INSET - width, y - 6]
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def plain_role_route?(style)
|
|
203
|
+
%w[1 3].include?(style[:mode]) && %w[0 2].include?(style[:lwidth]) &&
|
|
204
|
+
[nil, "V"].include?(style[:tree])
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def property_role_box?(box, direction)
|
|
208
|
+
alignments = direction == :down ? [1] : [0, 1]
|
|
209
|
+
box && alignments.include?(box["alignment"]) && default_presentation?(box)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def property_role_offsets(direction)
|
|
213
|
+
case direction
|
|
214
|
+
when :up then [60, -6]
|
|
215
|
+
when :left then [60, -7]
|
|
216
|
+
else [65, 15]
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def source_property_font?(connector, direction)
|
|
221
|
+
font_family.to_s.casecmp("Cambria").zero? &&
|
|
222
|
+
property_label?(connector, :source) &&
|
|
223
|
+
source_property_route?(connector.style.to_h, direction)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def source_property_route?(style, direction)
|
|
227
|
+
case direction
|
|
228
|
+
when :left then left_property_route?(style)
|
|
229
|
+
when :down then downward_property_route?(style)
|
|
230
|
+
else style[:lwidth] == "2" && style[:tree] != "OS"
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def left_property_route?(style)
|
|
235
|
+
style[:mode] == "3" && %w[0 2].include?(style[:lwidth]) &&
|
|
236
|
+
[nil, "H", "LV"].include?(style[:tree])
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def downward_property_route?(style)
|
|
240
|
+
%w[1 3].include?(style[:mode]) && style[:lwidth] == "2" && !style[:tree]
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def directional_multiplicity(connector, direction, anchor, box, mult)
|
|
244
|
+
return nil unless cambria_box?(box)
|
|
245
|
+
|
|
246
|
+
x, y = seat_corner(anchor, box)
|
|
247
|
+
return [x + 8, y + 15] if direction == :down
|
|
248
|
+
return nil unless direction == :left && source_property_font?(connector, direction)
|
|
249
|
+
|
|
250
|
+
width = Ea::Fonts::Metrics.text_length(mult, font_size, family: font_family)
|
|
251
|
+
[x - width - 3, y + 15] if width
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def cambria_box?(box)
|
|
255
|
+
box && default_presentation?(box) && font_family.to_s.casecmp("Cambria").zero?
|
|
256
|
+
end
|
|
257
|
+
|
|
172
258
|
def mult_seat(direction, anchor, box)
|
|
173
|
-
return nil
|
|
259
|
+
return nil unless %i[up right].include?(direction)
|
|
260
|
+
return nil if box.nil?
|
|
261
|
+
return nil if stored_offsets_zero?(box) && !cambria_box?(box)
|
|
174
262
|
return nil unless default_presentation?(box)
|
|
175
263
|
|
|
176
264
|
x, y = seat_corner(anchor, box)
|
|
@@ -91,7 +91,7 @@ module Ea
|
|
|
91
91
|
end_renderer.texts(text_box: src_tb, mult_box: src_mb,
|
|
92
92
|
anchor: source_pt, connector: connector,
|
|
93
93
|
end_kind: :source, tree: tree && tree[:source],
|
|
94
|
-
heading: stored_src &&
|
|
94
|
+
heading: stored_src && source_heading(connector, points),
|
|
95
95
|
stored_labels: stored_labels).each { |t| texts << t }
|
|
96
96
|
end_renderer.texts(text_box: tgt_tb,
|
|
97
97
|
mult_box: tgt_mb,
|
|
@@ -112,6 +112,19 @@ module Ea
|
|
|
112
112
|
{ source: true, target: true }
|
|
113
113
|
end
|
|
114
114
|
|
|
115
|
+
# Clipping can slant a stored vertical leg. Keep its label direction
|
|
116
|
+
# while the label position follows the final emitted endpoint.
|
|
117
|
+
def source_heading(connector, points)
|
|
118
|
+
heading = leg_heading(points)
|
|
119
|
+
return heading unless points.size == 2 && heading[0].nonzero? && heading[1].negative?
|
|
120
|
+
|
|
121
|
+
stored = waypoint_pairs(connector)
|
|
122
|
+
return heading unless stored.size == 2
|
|
123
|
+
|
|
124
|
+
original = leg_heading(stored)
|
|
125
|
+
original[0].zero? && original[1].negative? ? original : heading
|
|
126
|
+
end
|
|
127
|
+
|
|
115
128
|
def leg_heading(points)
|
|
116
129
|
[points[1][0] - points[0][0], points[1][1] - points[0][1]]
|
|
117
130
|
end
|
|
@@ -195,7 +208,7 @@ module Ea
|
|
|
195
208
|
end
|
|
196
209
|
|
|
197
210
|
def label_font_family
|
|
198
|
-
theme.
|
|
211
|
+
theme.label_font_family || DEFAULT_FAMILY
|
|
199
212
|
end
|
|
200
213
|
|
|
201
214
|
def label_font_size
|
data/lib/ea/theme/definition.rb
CHANGED
|
@@ -65,6 +65,7 @@ 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
69
|
font_size_unit: "pt", text_weight_normal: 400,
|
|
69
70
|
text_weight_bold: 700,
|
|
70
71
|
element_font_size: 9, element_font_unit: "pt",
|
|
@@ -81,6 +82,7 @@ module Ea
|
|
|
81
82
|
@id = id.to_s
|
|
82
83
|
@name = name
|
|
83
84
|
@font_family = font_family
|
|
85
|
+
@label_font_family = label_font_family
|
|
84
86
|
@font_size = font_size
|
|
85
87
|
@font_size_unit = font_size_unit
|
|
86
88
|
@text_weight_normal = text_weight_normal
|
|
@@ -119,7 +121,12 @@ module Ea
|
|
|
119
121
|
fills[classifier.class.name]
|
|
120
122
|
end
|
|
121
123
|
|
|
124
|
+
def label_font_family
|
|
125
|
+
@label_font_family || font_family
|
|
126
|
+
end
|
|
127
|
+
|
|
122
128
|
def with(**overrides)
|
|
129
|
+
overrides[:label_font_family] = nil if overrides.key?(:font_family) && !overrides.key?(:label_font_family)
|
|
123
130
|
Definition.new(**to_h.merge(overrides))
|
|
124
131
|
end
|
|
125
132
|
|
|
@@ -151,6 +158,7 @@ module Ea
|
|
|
151
158
|
id: id, name: name,
|
|
152
159
|
font_family: font_family, font_size: font_size,
|
|
153
160
|
font_size_unit: font_size_unit,
|
|
161
|
+
label_font_family: @label_font_family,
|
|
154
162
|
text_weight_normal: text_weight_normal,
|
|
155
163
|
text_weight_bold: text_weight_bold,
|
|
156
164
|
element_font_size: element_font_size,
|
data/lib/ea/theme/loader.rb
CHANGED
|
@@ -10,6 +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
14
|
# colors: { text: "#000000", attribute_text: "#66413F", ... }
|
|
14
15
|
# strokes: { element_border: 2, connector_line: 2, ... }
|
|
15
16
|
# text_metrics: { width_factor: 0.65 }
|
|
@@ -38,6 +39,7 @@ module Ea
|
|
|
38
39
|
def from_hash(data)
|
|
39
40
|
font = data["font"] || {}
|
|
40
41
|
elem_font = data["element_font"] || {}
|
|
42
|
+
label_font = data["label_font"] || {}
|
|
41
43
|
colors = data["colors"] || {}
|
|
42
44
|
strokes = data["strokes"] || {}
|
|
43
45
|
text_metrics = data["text_metrics"] || {}
|
|
@@ -53,6 +55,7 @@ module Ea
|
|
|
53
55
|
id: data["id"],
|
|
54
56
|
name: data["name"],
|
|
55
57
|
font_family: font["family"],
|
|
58
|
+
label_font_family: label_font["family"],
|
|
56
59
|
font_size: font["size"],
|
|
57
60
|
font_size_unit: font["unit"] || "pt",
|
|
58
61
|
text_weight_normal: font["weight_normal"] || 400,
|
data/lib/ea/version.rb
CHANGED
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.
|
|
4
|
+
version: 0.6.96
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -615,6 +615,7 @@ files:
|
|
|
615
615
|
- lib/ea/export/xsd/generator.rb
|
|
616
616
|
- lib/ea/export/xsd/namespace_registry.rb
|
|
617
617
|
- lib/ea/fonts.rb
|
|
618
|
+
- lib/ea/fonts/cambria_7_regular_widths.json
|
|
618
619
|
- lib/ea/fonts/carlito_metrics.json
|
|
619
620
|
- lib/ea/fonts/metrics.rb
|
|
620
621
|
- lib/ea/guid_format.rb
|