ea 0.6.15 → 0.6.17
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/fonts/metrics.rb +38 -0
- data/lib/ea/model/classifier.rb +4 -0
- data/lib/ea/sources/qea/classifier_builder.rb +2 -0
- data/lib/ea/svg/ea_emitter/element/compartment_geometry.rb +23 -4
- data/lib/ea/svg/ea_emitter/elements.rb +31 -5
- data/lib/ea/version.rb +1 -1
- data/lib/tasks/fonts.rake +40 -0
- 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: 23adcefba5c783bfccc47f9cbb8d8cdf327431629857a4a7b46b67bbef529b21
|
|
4
|
+
data.tar.gz: ad21177acd96ceaa4e4c0e30cf47073e6ae333639a8673ebc9771ee6ddd8d2d3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6a8b2a05a0ab5dd288494d62b24f7911a40e823db514a20a6ed6ee9897c2290b997b3b85d5580d6ba4bad516ed8da96f4b44f4eb69a69b064b38c50e8193faea
|
|
7
|
+
data.tar.gz: f66f47990a91b56ee9d25c4e714355f4bbe603fd6f77418e8bfb022b7b71e0dff4d1ebb712a1404493fbb6a1d2a21f452f789999b38bdb098081e0216fbcf6ac
|
data/lib/ea/fonts/metrics.rb
CHANGED
|
@@ -99,6 +99,44 @@ module Ea
|
|
|
99
99
|
def cp_key(ch)
|
|
100
100
|
format("U+%04X", ch.ord)
|
|
101
101
|
end
|
|
102
|
+
|
|
103
|
+
# Resolves the four Carlito style TTFs through the fontist gem
|
|
104
|
+
# (system index + downloaded formulas). Empty when fontist is
|
|
105
|
+
# unavailable or Carlito is not installed.
|
|
106
|
+
def carlito_font_files
|
|
107
|
+
require "fontist"
|
|
108
|
+
styles = %w[Regular Bold Italic BoldItalic]
|
|
109
|
+
Fontist::SystemFont.find("Carlito").filter_map do |path|
|
|
110
|
+
next unless path.downcase.end_with?(".ttf")
|
|
111
|
+
|
|
112
|
+
style = File.basename(path, ".ttf").delete_prefix("Carlito-")
|
|
113
|
+
match = styles.find { |s| s.casecmp?(style) }
|
|
114
|
+
next unless match
|
|
115
|
+
|
|
116
|
+
[match, path]
|
|
117
|
+
end.to_h
|
|
118
|
+
rescue LoadError, StandardError => e
|
|
119
|
+
raise if ENV['EA_FONTS_DEBUG']
|
|
120
|
+
|
|
121
|
+
{}
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Reads advance widths from a TTF via the fontian gem (used by
|
|
125
|
+
# `rake fonts:validate` to re-derive the tables).
|
|
126
|
+
def advances_from_font(path)
|
|
127
|
+
require "fontisan"
|
|
128
|
+
font = Fontisan::FontLoader.load(path)
|
|
129
|
+
hmtx = font.table("hmtx")
|
|
130
|
+
hmtx.parse_with_context(font.table("hhea").number_of_h_metrics,
|
|
131
|
+
font.table("maxp").num_glyphs)
|
|
132
|
+
upem = font.table("head").units_per_em
|
|
133
|
+
cmap = font.table("cmap").unicode_mappings
|
|
134
|
+
cmap.each_with_object({}) do |(cp, gid), out|
|
|
135
|
+
adv = hmtx.metric_for(gid)[:advance_width]
|
|
136
|
+
out[cp] = (adv.to_f / upem).round(4) if adv
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
102
140
|
end
|
|
103
141
|
end
|
|
104
142
|
end
|
data/lib/ea/model/classifier.rb
CHANGED
|
@@ -15,6 +15,8 @@ module Ea
|
|
|
15
15
|
attribute :package_id, :string # containing package
|
|
16
16
|
attribute :package_name, :string # containing package name
|
|
17
17
|
attribute :is_abstract, :boolean, default: false
|
|
18
|
+
attribute :is_root, :boolean, default: false
|
|
19
|
+
attribute :is_leaf, :boolean, default: false
|
|
18
20
|
attribute :visibility, :string
|
|
19
21
|
attribute :model_kind, :string, default: -> { "classifier" }
|
|
20
22
|
attribute :properties, Property, collection: true, initialize_empty: true
|
|
@@ -31,6 +33,8 @@ module Ea
|
|
|
31
33
|
map "packageId", to: :package_id
|
|
32
34
|
map "packageName", to: :package_name
|
|
33
35
|
map "isAbstract", to: :is_abstract, render_default: true
|
|
36
|
+
map "isRoot", to: :is_root, render_default: true
|
|
37
|
+
map "isLeaf", to: :is_leaf, render_default: true
|
|
34
38
|
map "visibility", to: :visibility
|
|
35
39
|
map "modelKind", to: :model_kind, render_default: true
|
|
36
40
|
map "properties", to: :properties, render_empty: true
|
|
@@ -94,6 +94,8 @@ module Ea
|
|
|
94
94
|
package_name: package_name_for(object),
|
|
95
95
|
qualified_name: qualified_name_for(object),
|
|
96
96
|
is_abstract: abstract?(object),
|
|
97
|
+
is_root: object.isroot.to_i == 1,
|
|
98
|
+
is_leaf: object.isleaf.to_i == 1,
|
|
97
99
|
visibility: visibility_from_scope(object.scope),
|
|
98
100
|
properties: properties_for(object),
|
|
99
101
|
operations: OperationBuilder.new(database).build_all_for(object),
|
|
@@ -20,6 +20,8 @@ module Ea
|
|
|
20
20
|
attr_reader :bounds, :size, :header_lines_count,
|
|
21
21
|
:attr_lines_count, :op_lines_count,
|
|
22
22
|
:tagged_values_count,
|
|
23
|
+
:constraints_count,
|
|
24
|
+
:enum_literals_count,
|
|
23
25
|
:header_top_padding, :header_line_offset,
|
|
24
26
|
:divider_offset, :attr_line_offset,
|
|
25
27
|
:attr_first_offset
|
|
@@ -27,6 +29,8 @@ module Ea
|
|
|
27
29
|
def initialize(bounds:, size:, header_lines_count:,
|
|
28
30
|
attr_lines_count:, op_lines_count:,
|
|
29
31
|
tagged_values_count:,
|
|
32
|
+
constraints_count: 0,
|
|
33
|
+
enum_literals_count: 0,
|
|
30
34
|
header_top_padding: 12,
|
|
31
35
|
header_line_offset: 6,
|
|
32
36
|
divider_offset: 8,
|
|
@@ -38,6 +42,8 @@ module Ea
|
|
|
38
42
|
@attr_lines_count = attr_lines_count
|
|
39
43
|
@op_lines_count = op_lines_count
|
|
40
44
|
@tagged_values_count = tagged_values_count
|
|
45
|
+
@constraints_count = constraints_count
|
|
46
|
+
@enum_literals_count = enum_literals_count
|
|
41
47
|
@header_top_padding = header_top_padding
|
|
42
48
|
@header_line_offset = header_line_offset
|
|
43
49
|
@divider_offset = divider_offset
|
|
@@ -102,11 +108,24 @@ module Ea
|
|
|
102
108
|
# present). EA does not emit a separate divider — the
|
|
103
109
|
# italic "tags" header marks the compartment.
|
|
104
110
|
def tagged_value_first_y
|
|
105
|
-
return nil unless tagged_values_count
|
|
111
|
+
return nil unless tagged_values_count.to_i.positive? || constraints_count.to_i.positive?
|
|
106
112
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
113
|
+
content_bottom_y + size + 5
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# The lowest content edge among attributes, enum literals,
|
|
117
|
+
# and operations — the anchor for trailing compartments.
|
|
118
|
+
def content_bottom_y
|
|
119
|
+
bottoms = [attr_bottom_y]
|
|
120
|
+
bottoms << enum_literals_bottom_y if enum_literals_count.to_i.positive?
|
|
121
|
+
bottoms << op_bottom_y if op_lines_count.to_i.positive?
|
|
122
|
+
bottoms.compact.max
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def enum_literals_bottom_y
|
|
126
|
+
return nil unless enum_literal_first_y
|
|
127
|
+
|
|
128
|
+
enum_literal_first_y + ([enum_literals_count, 1].max - 1) * (size + 6)
|
|
110
129
|
end
|
|
111
130
|
end
|
|
112
131
|
end
|
|
@@ -82,7 +82,9 @@ module Ea
|
|
|
82
82
|
op_lines = (is_classifier && show_operations?) ? Element::OperationRenderer.lines_for(classifier) : []
|
|
83
83
|
geometry = compartment_geometry(bounds, size, attr_lines, op_lines,
|
|
84
84
|
tagged_values_for(classifier).size,
|
|
85
|
-
header_lines
|
|
85
|
+
header_lines,
|
|
86
|
+
constraints_count: constraints_for(classifier).size,
|
|
87
|
+
enum_literals_count: enum_literals_for(classifier).size)
|
|
86
88
|
# EA suppresses the whole attribute compartment when it does
|
|
87
89
|
# not fit the stored box height (never truncates rows) - the
|
|
88
90
|
# AOS box on CityFurniture hides its association property
|
|
@@ -92,7 +94,9 @@ module Ea
|
|
|
92
94
|
attr_lines = []
|
|
93
95
|
geometry = compartment_geometry(bounds, size, attr_lines, op_lines,
|
|
94
96
|
tagged_values_for(classifier).size,
|
|
95
|
-
header_lines
|
|
97
|
+
header_lines,
|
|
98
|
+
constraints_count: constraints_for(classifier).size,
|
|
99
|
+
enum_literals_count: enum_literals_for(classifier).size)
|
|
96
100
|
end
|
|
97
101
|
# EA suppresses the operations compartment when its rows
|
|
98
102
|
# overflow the stored box height (same rule as attributes).
|
|
@@ -101,7 +105,9 @@ module Ea
|
|
|
101
105
|
op_lines = []
|
|
102
106
|
geometry = compartment_geometry(bounds, size, attr_lines, op_lines,
|
|
103
107
|
tagged_values_for(classifier).size,
|
|
104
|
-
header_lines
|
|
108
|
+
header_lines,
|
|
109
|
+
constraints_count: constraints_for(classifier).size,
|
|
110
|
+
enum_literals_count: enum_literals_for(classifier).size)
|
|
105
111
|
end
|
|
106
112
|
RenderContext.new(
|
|
107
113
|
element: element,
|
|
@@ -130,12 +136,15 @@ module Ea
|
|
|
130
136
|
end
|
|
131
137
|
|
|
132
138
|
def compartment_geometry(bounds, size, attr_lines, op_lines,
|
|
133
|
-
tagged_count, header_lines
|
|
139
|
+
tagged_count, header_lines,
|
|
140
|
+
constraints_count: 0, enum_literals_count: 0)
|
|
134
141
|
CompartmentGeometry.new(bounds: bounds, size: size,
|
|
135
142
|
header_lines_count: header_lines.size,
|
|
136
143
|
attr_lines_count: attr_lines.size,
|
|
137
144
|
op_lines_count: op_lines.size,
|
|
138
145
|
tagged_values_count: tagged_count,
|
|
146
|
+
constraints_count: constraints_count,
|
|
147
|
+
enum_literals_count: enum_literals_count,
|
|
139
148
|
header_top_padding: header_padding_for(header_lines),
|
|
140
149
|
header_line_offset: theme.compartments.header_line_offset,
|
|
141
150
|
divider_offset: theme.compartments.divider_offset,
|
|
@@ -185,7 +194,24 @@ module Ea
|
|
|
185
194
|
def constraints_for(classifier)
|
|
186
195
|
return [] unless classifier.is_a?(Ea::Model::Classifier)
|
|
187
196
|
|
|
188
|
-
|
|
197
|
+
marker_line = root_leaf_marker_for(classifier)
|
|
198
|
+
marker = marker_line ? [marker_constraint(marker_line)] : []
|
|
199
|
+
marker + (classifier.constraints || [])
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# EA renders the element's Root/Leaf checkboxes as {root},
|
|
203
|
+
# {leaf}, or {root,leaf} first in the constraints compartment
|
|
204
|
+
# (t_object.IsRoot/IsLeaf — verified on LanguageCode boxes).
|
|
205
|
+
def root_leaf_marker_for(classifier)
|
|
206
|
+
parts = []
|
|
207
|
+
parts << "root" if classifier.is_root
|
|
208
|
+
parts << "leaf" if classifier.is_leaf
|
|
209
|
+
parts.empty? ? nil : "{#{parts.join(',')}}"
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def marker_constraint(text)
|
|
213
|
+
name = text[1..-2]
|
|
214
|
+
Ea::Model::Constraint.new(name: name, kind: "Marker")
|
|
189
215
|
end
|
|
190
216
|
|
|
191
217
|
# EA renders the off-canvas parent classifier's name as an
|
data/lib/ea/version.rb
CHANGED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ea/fonts/metrics"
|
|
4
|
+
|
|
5
|
+
namespace :fonts do
|
|
6
|
+
desc "Validate the shipped Carlito metrics against Fontist-resolved TTFs"
|
|
7
|
+
task :validate do
|
|
8
|
+
files = Ea::Fonts::Metrics.carlito_font_files
|
|
9
|
+
abort "Carlito not resolvable via Fontist — install it first" if files.empty?
|
|
10
|
+
|
|
11
|
+
shipped = JSON.parse(File.read(Ea::Fonts::Metrics::DATA_PATH))["advance"]
|
|
12
|
+
style_key = { "regular" => "regular", "bold" => "bold",
|
|
13
|
+
"italic" => "italic", "bold_italic" => "bold_italic",
|
|
14
|
+
"bolditalic" => "bold_italic" }
|
|
15
|
+
drift = []
|
|
16
|
+
|
|
17
|
+
files.each do |style, path|
|
|
18
|
+
table = shipped[style_key[style.downcase]]
|
|
19
|
+
actual = Ea::Fonts::Metrics.advances_from_font(path)
|
|
20
|
+
missing = actual.keys.count { |cp| table[format("U+%04X", cp)].nil? }
|
|
21
|
+
puts " (shipped table lacks #{missing} of #{actual.size} codepoints — skipped)"
|
|
22
|
+
actual.each do |cp, adv|
|
|
23
|
+
key = format("U+%04X", cp)
|
|
24
|
+
ours = table[key]
|
|
25
|
+
next if ours.nil? || (ours - adv).abs <= 0.0005
|
|
26
|
+
|
|
27
|
+
drift << format("%s %s: json=%s font=%s", style, key, ours, adv)
|
|
28
|
+
end
|
|
29
|
+
puts "#{style}: #{path} — #{actual.size} codepoints checked"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
if drift.empty?
|
|
33
|
+
puts "carlito_metrics.json matches the Fontist-resolved fonts."
|
|
34
|
+
else
|
|
35
|
+
puts "DRIFT (#{drift.size} entries):"
|
|
36
|
+
drift.first(20).each { |line| puts " #{line}" }
|
|
37
|
+
exit 1
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
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.
|
|
4
|
+
version: 0.6.17
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -999,6 +999,7 @@ files:
|
|
|
999
999
|
- lib/ea/xmi/lookup_service.rb
|
|
1000
1000
|
- lib/ea/xmi/parser.rb
|
|
1001
1001
|
- lib/ea/xml_escape.rb
|
|
1002
|
+
- lib/tasks/fonts.rake
|
|
1002
1003
|
- lib/tasks/svg_bench.rake
|
|
1003
1004
|
- reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes-list.png
|
|
1004
1005
|
- reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes.png
|