ruby3mf 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +1 -1
- data/bin/batch.rb +11 -10
- data/bin/suite.rb +50 -0
- data/lib/ruby3mf.rb +2 -0
- data/lib/ruby3mf/3MFcoreSpec_1.1.xsd +12 -12
- data/lib/ruby3mf/document.rb +16 -26
- data/lib/ruby3mf/errors.yml +21 -9
- data/lib/ruby3mf/interpolation.rb +8 -12
- data/lib/ruby3mf/mesh_analyzer.rb +4 -3
- data/lib/ruby3mf/model3mf.rb +37 -18
- data/lib/ruby3mf/relationships.rb +1 -1
- data/lib/ruby3mf/version.rb +1 -1
- data/lib/ruby3mf/xml_val.rb +25 -13
- data/ruby3mf.gemspec +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e69399223f22bf72d431f634a3dae81966252da
|
4
|
+
data.tar.gz: f8a87375c839cb3e42e00496104ee0c0a6a1c93f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8ad2102f909a1df3f614010c4738b81e5b0ece6415d6f3f6cc47951151ed57f282b3150e4c2c581b41cd2685f46253df48c40117fbf0a323968a41502f594412
|
7
|
+
data.tar.gz: 7c0182a9da87926ad9a76eb604a72dfc68926720b14e7174a4672d8c8f18773bdfb835b6fff4c428983bdb9dbdf169b7980b55fec53aea070d697a98c8e36322
|
data/.rspec
CHANGED
data/bin/batch.rb
CHANGED
@@ -5,16 +5,17 @@ require_relative '../lib/ruby3mf'
|
|
5
5
|
files = Dir["spec/ruby3mf-testfiles/#{ARGV[0] || "failing_cases"}/*.#{ARGV[1] || '3mf'}"]
|
6
6
|
|
7
7
|
files.each do |file|
|
8
|
+
begin
|
9
|
+
Log3mf.reset_log
|
10
|
+
doc = Document.read(file)
|
11
|
+
errors = Log3mf.entries(:error, :fatal_error)
|
8
12
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
h = { context: ent[0], severity: ent[1], message: ent[2] }
|
17
|
-
puts h
|
13
|
+
puts "=" * 100
|
14
|
+
puts "Validating file: #{file}"
|
15
|
+
errors.each do |ent|
|
16
|
+
h = { context: ent[0], severity: ent[1], message: ent[2] }
|
17
|
+
puts h
|
18
|
+
end
|
19
|
+
rescue
|
18
20
|
end
|
19
|
-
|
20
21
|
end
|
data/bin/suite.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative '../lib/ruby3mf'
|
4
|
+
|
5
|
+
# usage
|
6
|
+
# bin/suite.rb {path to 3mf-test-suite} optional
|
7
|
+
# assumes . is ~/src/ruby3mf and that ~/src/3mf-test-suite is path to suite repo files
|
8
|
+
|
9
|
+
$stdout.sync = true
|
10
|
+
|
11
|
+
good_files = Dir["#{ARGV[0] || '../3mf-test-suite'}/Positive/*.3mf"]
|
12
|
+
bad_files = Dir["#{ARGV[0] || '../3mf-test-suite'}/Negative/*.3mf"]
|
13
|
+
|
14
|
+
false_negatives = {}
|
15
|
+
true_negatives={}
|
16
|
+
false_positives = []
|
17
|
+
|
18
|
+
def val3mf(f)
|
19
|
+
Log3mf.reset_log
|
20
|
+
Document.read(f)
|
21
|
+
Log3mf.entries(:fatal_error, :error)
|
22
|
+
end
|
23
|
+
|
24
|
+
puts "\n\nPositive"
|
25
|
+
good_files.each do |file|
|
26
|
+
print "." # "Validating file: #{file}"
|
27
|
+
errors=val3mf(file)
|
28
|
+
|
29
|
+
if errors.size > 0
|
30
|
+
false_negatives[file]=errors
|
31
|
+
puts "\n#{file}"
|
32
|
+
errors.each do |ent|
|
33
|
+
h = {context: ent[0], severity: ent[1], message: ent[2]}
|
34
|
+
puts " #{h}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
puts "\n\nNegative"
|
41
|
+
bad_files.each do |file|
|
42
|
+
print "." #puts "Validating file #{file}"
|
43
|
+
errors=val3mf(file)
|
44
|
+
if errors.size > 0
|
45
|
+
true_negatives[file] = errors
|
46
|
+
else
|
47
|
+
false_positives << file
|
48
|
+
puts "\n#{file} - No Errors Found!"
|
49
|
+
end
|
50
|
+
end
|
data/lib/ruby3mf.rb
CHANGED
@@ -8,6 +8,8 @@ require_relative "ruby3mf/relationships"
|
|
8
8
|
require_relative "ruby3mf/thumbnail3mf"
|
9
9
|
require_relative "ruby3mf/texture3mf"
|
10
10
|
require_relative "ruby3mf/xml_val"
|
11
|
+
require_relative "ruby3mf/edge_list"
|
12
|
+
require_relative "ruby3mf/mesh_analyzer"
|
11
13
|
|
12
14
|
require 'zip'
|
13
15
|
require 'nokogiri'
|
@@ -16,10 +16,10 @@ Items within this schema follow a simple naming convention of appending a prefix
|
|
16
16
|
<!-- Complex Types -->
|
17
17
|
<xs:complexType name="CT_Model">
|
18
18
|
<xs:sequence>
|
19
|
-
<xs:element ref="metadata" minOccurs="0" maxOccurs="
|
19
|
+
<xs:element ref="metadata" minOccurs="0" maxOccurs="unbounded"/>
|
20
20
|
<xs:element ref="resources"/>
|
21
21
|
<xs:element ref="build"/>
|
22
|
-
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="
|
22
|
+
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
|
23
23
|
</xs:sequence>
|
24
24
|
<xs:attribute name="unit" type="ST_Unit" default="millimeter"/>
|
25
25
|
<xs:attribute ref="xml:lang"/>
|
@@ -28,21 +28,21 @@ Items within this schema follow a simple naming convention of appending a prefix
|
|
28
28
|
</xs:complexType>
|
29
29
|
<xs:complexType name="CT_Resources">
|
30
30
|
<xs:sequence>
|
31
|
-
<xs:element ref="basematerials" minOccurs="0" maxOccurs="
|
32
|
-
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="
|
33
|
-
<xs:element ref="object" minOccurs="0" maxOccurs="
|
31
|
+
<xs:element ref="basematerials" minOccurs="0" maxOccurs="unbounded"/>
|
32
|
+
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
|
33
|
+
<xs:element ref="object" minOccurs="0" maxOccurs="unbounded"/>
|
34
34
|
</xs:sequence>
|
35
35
|
<xs:anyAttribute namespace="##other" processContents="lax"/>
|
36
36
|
</xs:complexType>
|
37
37
|
<xs:complexType name="CT_Build">
|
38
38
|
<xs:sequence>
|
39
|
-
<xs:element ref="item" minOccurs="0" maxOccurs="
|
39
|
+
<xs:element ref="item" minOccurs="0" maxOccurs="unbounded"/>
|
40
40
|
</xs:sequence>
|
41
41
|
<xs:anyAttribute namespace="##other" processContents="lax"/>
|
42
42
|
</xs:complexType>
|
43
43
|
<xs:complexType name="CT_BaseMaterials">
|
44
44
|
<xs:sequence>
|
45
|
-
<xs:element ref="base" maxOccurs="
|
45
|
+
<xs:element ref="base" maxOccurs="unbounded"/>
|
46
46
|
</xs:sequence>
|
47
47
|
<xs:attribute name="id" type="ST_ResourceID" use="required"/>
|
48
48
|
</xs:complexType>
|
@@ -71,12 +71,12 @@ Items within this schema follow a simple naming convention of appending a prefix
|
|
71
71
|
<xs:sequence>
|
72
72
|
<xs:element ref="vertices"/>
|
73
73
|
<xs:element ref="triangles"/>
|
74
|
-
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="
|
74
|
+
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
|
75
75
|
</xs:sequence>
|
76
76
|
</xs:complexType>
|
77
77
|
<xs:complexType name="CT_Vertices">
|
78
78
|
<xs:sequence>
|
79
|
-
<xs:element ref="vertex" minOccurs="3" maxOccurs="
|
79
|
+
<xs:element ref="vertex" minOccurs="3" maxOccurs="unbounded"/>
|
80
80
|
</xs:sequence>
|
81
81
|
</xs:complexType>
|
82
82
|
<xs:complexType name="CT_Vertex">
|
@@ -87,7 +87,7 @@ Items within this schema follow a simple naming convention of appending a prefix
|
|
87
87
|
</xs:complexType>
|
88
88
|
<xs:complexType name="CT_Triangles">
|
89
89
|
<xs:sequence>
|
90
|
-
<xs:element ref="triangle" minOccurs="1" maxOccurs="
|
90
|
+
<xs:element ref="triangle" minOccurs="1" maxOccurs="unbounded"/>
|
91
91
|
</xs:sequence>
|
92
92
|
</xs:complexType>
|
93
93
|
<xs:complexType name="CT_Triangle">
|
@@ -102,7 +102,7 @@ Items within this schema follow a simple naming convention of appending a prefix
|
|
102
102
|
</xs:complexType>
|
103
103
|
<xs:complexType name="CT_Components">
|
104
104
|
<xs:sequence>
|
105
|
-
<xs:element ref="component" maxOccurs="
|
105
|
+
<xs:element ref="component" maxOccurs="unbounded"/>
|
106
106
|
</xs:sequence>
|
107
107
|
</xs:complexType>
|
108
108
|
<xs:complexType name="CT_Component">
|
@@ -185,4 +185,4 @@ Items within this schema follow a simple naming convention of appending a prefix
|
|
185
185
|
<xs:element name="component" type="CT_Component"/>
|
186
186
|
<xs:element name="metadata" type="CT_Metadata"/>
|
187
187
|
<xs:element name="item" type="CT_Item"/>
|
188
|
-
</xs:schema>
|
188
|
+
</xs:schema>
|
data/lib/ruby3mf/document.rb
CHANGED
@@ -38,31 +38,16 @@ class Document
|
|
38
38
|
|
39
39
|
#verify that each texture part in the 3MF is related to the model through a texture relationship in a rels file
|
40
40
|
def self.validate_texture_parts(document, log)
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
document.textures.each do |texture_file|
|
50
|
-
if texture_file[:target] == filename
|
51
|
-
has_relationship = true
|
52
|
-
break
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
next unless !has_relationship
|
57
|
-
document.thumbnails.each do |thumbnail_file|
|
58
|
-
if thumbnail_file[:target] == filename
|
59
|
-
has_relationship = true
|
60
|
-
break
|
41
|
+
unless document.types.empty?
|
42
|
+
document.parts.select { |part| TEXTURE_TYPES.include?(document.types[File.extname(part).strip.downcase[1..-1]]) }.each do |tfile|
|
43
|
+
if document.textures.select { |f| f[:target] == tfile }.size == 0
|
44
|
+
if document.thumbnails.select { |t| t[:target] == tfile }.size == 0
|
45
|
+
log.context "part names" do |l|
|
46
|
+
l.warning "#{tfile} appears to be a texture file but no rels file declares any relationship to the model", page: 13
|
47
|
+
end
|
48
|
+
end
|
61
49
|
end
|
62
50
|
end
|
63
|
-
log.context "part names /#{filename}" do |l|
|
64
|
-
l.error :texture_without_relationship, name: filename unless has_relationship
|
65
|
-
end
|
66
51
|
end
|
67
52
|
end
|
68
53
|
|
@@ -75,8 +60,10 @@ class Document
|
|
75
60
|
Zip.warn_invalid_date = false
|
76
61
|
|
77
62
|
# check for the general purpose flag set - if so, warn that 3mf may not work on some systems
|
78
|
-
|
79
|
-
|
63
|
+
File.open(input_file, "r") do |file|
|
64
|
+
if file.read[6] == "\b"
|
65
|
+
l.warning 'File format: this file may not open on all systems'
|
66
|
+
end
|
80
67
|
end
|
81
68
|
|
82
69
|
Zip::File.open(input_file) do |zip_file|
|
@@ -108,6 +95,9 @@ class Document
|
|
108
95
|
content_type_match = zip_file.glob('\[Content_Types\].xml').first
|
109
96
|
if content_type_match
|
110
97
|
m.types = ContentTypes.parse(content_type_match)
|
98
|
+
model_extension = m.types.key('application/vnd.ms-package.3dmanufacturing-3dmodel+xml')
|
99
|
+
model_file = zip_file.glob("**/*.#{model_extension}").first
|
100
|
+
l.error :no_3d_model, extension: model_extension if model_file.nil?
|
111
101
|
else
|
112
102
|
l.error 'Missing required file: [Content_Types].xml', page: 4
|
113
103
|
end
|
@@ -115,7 +105,7 @@ class Document
|
|
115
105
|
|
116
106
|
l.context 'relationships' do |l|
|
117
107
|
rel_file = zip_file.glob('_rels/.rels').first
|
118
|
-
l.fatal_error
|
108
|
+
l.fatal_error :missing_dot_rels_file unless rel_file
|
119
109
|
|
120
110
|
zip_file.glob('**/*.rels').each do |rel|
|
121
111
|
m.relationships[rel.name] = Relationships.parse(rel)
|
data/lib/ruby3mf/errors.yml
CHANGED
@@ -49,6 +49,9 @@ invalid_content_type:
|
|
49
49
|
invalid_startpart_type:
|
50
50
|
msg: "rels/.rels Relationship file has an invalid attribute type for the root 3D Model (StartPart). The required type is \"http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel\""
|
51
51
|
page: 10
|
52
|
+
invalid_startpart_target:
|
53
|
+
msg: "Invalid StartPart target '%{target}'. The 3MF Document StartPart relationship MUST point to the 3D Model part that identifies the root of the 3D payload."
|
54
|
+
page: 10
|
52
55
|
invalid_relationship_type:
|
53
56
|
msg: "Invalid relationship type '%{type}' specified in .rels relationship file. Parts in the 3D payload MUST use one of the appropriate relationship types to establish that relationship between two parts in the payload."
|
54
57
|
page: 10
|
@@ -77,8 +80,8 @@ multiple_relationships:
|
|
77
80
|
msg: "There MUST NOT be more than one relationship of a given relationship type from one part to a second part"
|
78
81
|
page: 10
|
79
82
|
no_3d_model:
|
80
|
-
msg: "
|
81
|
-
page:
|
83
|
+
msg: "Required 3D model payload not found with provided Content Type model extension: .%{extension}"
|
84
|
+
page: 10
|
82
85
|
not_a_zip:
|
83
86
|
msg: "File provided is not a valid ZIP archive"
|
84
87
|
page: 9
|
@@ -109,6 +112,9 @@ build_with_other_item:
|
|
109
112
|
resource_id_collision:
|
110
113
|
msg: "resources must be unique within the model"
|
111
114
|
page: 22
|
115
|
+
resource_pid_missing:
|
116
|
+
msg: "A model resource referenced a property group id (pid) that is not present. Missing pid is: %{pid}"
|
117
|
+
page: 20
|
112
118
|
metadata_elements_with_same_name:
|
113
119
|
msg: "metadata elements must not share the same name"
|
114
120
|
page: 22
|
@@ -121,18 +127,24 @@ unknown_required_extension:
|
|
121
127
|
missing_extension_namespace_uri:
|
122
128
|
msg: "Required extension '%{ns}' MUST refer to namespace with URI"
|
123
129
|
page: 14
|
124
|
-
texture_without_relationship:
|
125
|
-
msg: "%{name} appears to be a texture file but no rels file declares any relationship to the model"
|
126
|
-
page: 13
|
127
130
|
invalid_metadata_under_defaultns:
|
128
131
|
msg: "Metadata without a namespace name must only contain allowed name values"
|
129
132
|
page: 21
|
133
|
+
invalid_metadata_name:
|
134
|
+
msg: "Metadata names must be prefixed with a valid namespace"
|
135
|
+
page: 21
|
130
136
|
has_commas_for_floats:
|
131
137
|
msg: "numbers not formatted for the en-US locale"
|
132
138
|
page: 15
|
133
|
-
invalid_language_locale:
|
134
|
-
msg: "locale should be en-US"
|
135
|
-
page: 15
|
136
139
|
invalid_xml_core:
|
137
140
|
msg: "XML file doesn't pass validation with the XSD file"
|
138
|
-
page:
|
141
|
+
page: 15
|
142
|
+
missing_object_reference:
|
143
|
+
msg: "3D objects not referenced by an item element"
|
144
|
+
page: 23
|
145
|
+
non_distinct_indices:
|
146
|
+
msg: "The indices v1, v2 and v3 MUST be distinct."
|
147
|
+
page: 31
|
148
|
+
has_improper_base_color:
|
149
|
+
msg: "An sRGB color MUST be specified with a value of a 6 or 8 digit hexadecimal number"
|
150
|
+
page: 35
|
@@ -7,18 +7,14 @@ module Interpolation
|
|
7
7
|
)
|
8
8
|
|
9
9
|
def interpolate(string, values = {})
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
value = values[key]
|
19
|
-
value = value.call(values) if value.respond_to?(:call)
|
20
|
-
$3 ? sprintf("%#{$3}", value) : value
|
21
|
-
end
|
10
|
+
string.gsub(INTERPOLATION_PATTERN) do |match|
|
11
|
+
if match == '%%'
|
12
|
+
'%'
|
13
|
+
else
|
14
|
+
key = ($1 || $2 || match.tr("%{}", "")).to_sym
|
15
|
+
value = values[key]
|
16
|
+
value = value.call(values) if value.respond_to?(:call)
|
17
|
+
$3 ? sprintf("%#{$3}", value) : value
|
22
18
|
end
|
23
19
|
end
|
24
20
|
end
|
@@ -1,6 +1,3 @@
|
|
1
|
-
require_relative 'edge_list'
|
2
|
-
|
3
|
-
|
4
1
|
def find_child(node, child_name)
|
5
2
|
node.children.each do |child|
|
6
3
|
if child.name == child_name
|
@@ -37,6 +34,10 @@ class MeshAnalyzer
|
|
37
34
|
v2 = triangle.attributes["v2"].to_s().to_i()
|
38
35
|
v3 = triangle.attributes["v3"].to_s().to_i()
|
39
36
|
|
37
|
+
if v1 == v2 || v2 == v3 || v3 == v1
|
38
|
+
l.error :non_distinct_indices
|
39
|
+
end
|
40
|
+
|
40
41
|
list.add_edge(v1, v2)
|
41
42
|
list.add_edge(v2, v3)
|
42
43
|
list.add_edge(v3, v1)
|
data/lib/ruby3mf/model3mf.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
|
-
require_relative 'mesh_analyzer'
|
2
|
-
|
3
1
|
class Model3mf
|
4
2
|
|
5
3
|
VALID_UNITS = ['micron', 'millimeter', 'centimeter', 'meter', 'inch', 'foot'].freeze
|
6
4
|
VALID_EXTENSIONS = {
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
'http://schemas.microsoft.com/3dmanufacturing/slice/2015/07' => {},
|
6
|
+
'http://schemas.microsoft.com/3dmanufacturing/material/2015/02' => {},
|
7
|
+
'http://schemas.microsoft.com/3dmanufacturing/production/2015/06' => {},
|
10
8
|
}.freeze
|
11
9
|
|
12
10
|
SCHEMA = '3MFcoreSpec_1.1.xsd'
|
13
11
|
|
12
|
+
VALID_CORE_METADATA_NAMES = ['Title', 'Designer', 'Description', 'Copyright', 'LicenseTerms', 'Rating', 'CreationDate', 'ModificationDate'].freeze
|
13
|
+
|
14
14
|
def self.parse(document, zip_entry)
|
15
15
|
model_doc = nil
|
16
16
|
|
@@ -77,13 +77,32 @@ class Model3mf
|
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
80
|
+
l.context "verifying StartPart relationship points to the root 3D Model" do |l|
|
81
|
+
#Find the root 3D model which is pointed to by the start part
|
82
|
+
root_rels = document.relationships['_rels/.rels']
|
83
|
+
unless root_rels.nil?
|
84
|
+
start_part_rel = root_rels.select { |rel| rel[:type] == Document::MODEL_TYPE }.first
|
85
|
+
unless start_part_rel.nil? || start_part_rel[:target] != '/' + zip_entry.name
|
86
|
+
#Verify that the model is a valid root 3D model by checking if it has at least one object
|
87
|
+
l.fatal_error :invalid_startpart_target, :target => start_part_rel[:target] if model_doc.css("//model//resources//object").size == 0
|
88
|
+
end
|
89
|
+
else
|
90
|
+
l.fatal_error :missing_dot_rels_file
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
80
94
|
end
|
81
95
|
|
82
|
-
l.context
|
83
|
-
resources = find_child(model_doc.root,
|
96
|
+
l.context 'verifying resources' do |l|
|
97
|
+
resources = find_child(model_doc.root, 'resources')
|
84
98
|
if resources
|
85
|
-
ids = resources.children.map { |child| child.attributes[
|
99
|
+
ids = resources.children.map { |child| child.attributes['id'].to_s if child.attributes['id'] }
|
86
100
|
l.error :resource_id_collision if ids.uniq.size != ids.size
|
101
|
+
pids = resources.children.map { |child| child.attributes['pid'].to_s }
|
102
|
+
missing_pids = pids.select { |pid| !pid.empty? and !ids.include? pid }
|
103
|
+
missing_pids.each do |p|
|
104
|
+
l.error :resource_pid_missing, pid: p
|
105
|
+
end
|
87
106
|
end
|
88
107
|
end
|
89
108
|
|
@@ -103,19 +122,19 @@ class Model3mf
|
|
103
122
|
end
|
104
123
|
|
105
124
|
l.context "checking metadata" do |l|
|
106
|
-
|
107
|
-
metadata = model_doc.root.css("metadata")
|
108
|
-
|
109
|
-
metadata_names = metadata.map { |met| met['name'] }
|
125
|
+
metadata_names = model_doc.root.css("metadata").map { |met| met['name'] }
|
110
126
|
l.error :metadata_elements_with_same_name unless metadata_names.uniq!.nil?
|
111
127
|
|
112
|
-
|
113
|
-
|
128
|
+
unless (metadata_names - VALID_CORE_METADATA_NAMES).empty?
|
129
|
+
extra_names = metadata_names - VALID_CORE_METADATA_NAMES
|
130
|
+
ns_names = extra_names.select { |n| n.include? ':' }
|
131
|
+
|
132
|
+
l.error :invalid_metadata_under_defaultns unless (extra_names - ns_names).empty?
|
114
133
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
134
|
+
unless ns_names.empty?
|
135
|
+
prefixes = model_doc.root.namespace_definitions.map { |defs| defs.prefix }.reject { |pre| pre.nil? }
|
136
|
+
l.error :invalid_metadata_name unless (ns_names.collect { |i| i.split(':').first } - prefixes).empty?
|
137
|
+
end
|
119
138
|
end
|
120
139
|
end
|
121
140
|
|
@@ -26,7 +26,7 @@ class Relationships
|
|
26
26
|
|
27
27
|
if zip_entry.name=="_rels/.rels"
|
28
28
|
l.context "Verifying StartPart" do |l|
|
29
|
-
start_part_type =
|
29
|
+
start_part_type = Document::MODEL_TYPE
|
30
30
|
start_part_count = relationships.select { |r| r[:type] == start_part_type }.size
|
31
31
|
if start_part_count != 1
|
32
32
|
l.error :invalid_startpart_type
|
data/lib/ruby3mf/version.rb
CHANGED
data/lib/ruby3mf/xml_val.rb
CHANGED
@@ -12,32 +12,40 @@ class XmlVal
|
|
12
12
|
|
13
13
|
def self.validate(file, document, schema_filename=nil)
|
14
14
|
Log3mf.context "validations" do |l|
|
15
|
-
l.error :
|
16
|
-
l.error :
|
17
|
-
l.error :
|
18
|
-
l.error :
|
19
|
-
l.
|
15
|
+
l.error :has_xml_space_attribute if space_attribute_exists?(document)
|
16
|
+
l.error :wrong_encoding if xml_not_utf8_encoded?(document)
|
17
|
+
l.error :dtd_not_allowed if dtd_exists?(file)
|
18
|
+
l.error :has_commas_for_floats if bad_floating_numbers?(document)
|
19
|
+
l.warning :missing_object_reference if objects_not_referenced?(document)
|
20
20
|
|
21
21
|
if schema_filename
|
22
22
|
Log3mf.context "validating core schema" do |l|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
File.open(File.join(File.dirname(__FILE__), schema_filename), "r") do |file|
|
24
|
+
xsd = Nokogiri::XML::Schema(file.read)
|
25
|
+
puts "the schema is NIL!" if xsd.nil?
|
26
|
+
core_schema_errors = xsd.validate(document)
|
27
|
+
l.error :invalid_xml_core if core_schema_errors.size > 0
|
28
|
+
core_schema_errors.each do |error|
|
29
|
+
if error_involves_colorvalue?(error)
|
30
|
+
l.error :has_improper_base_color
|
31
|
+
else
|
32
|
+
l.error error
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
27
36
|
end
|
28
37
|
end
|
29
38
|
end
|
30
39
|
end
|
31
40
|
|
32
|
-
|
33
|
-
|
34
|
-
!document.xpath('//@xml:lang').empty? && document.xpath('//@xml:lang').text != "en-US"
|
41
|
+
def self.objects_not_referenced?(document)
|
42
|
+
document.css('object').map { |x| x.attributes["id"].value } != document.css('item').map { |x| x.attributes["objectid"].value }
|
35
43
|
end
|
36
44
|
|
37
45
|
def self.bad_floating_numbers?(document)
|
38
46
|
!document.xpath('.//*[find_with_regex(., "[0-9]+\,[0-9]+")]', Class.new {
|
39
47
|
def find_with_regex node_set, regex
|
40
|
-
node_set.find_all { |node| node.values.any? {|v| v =~ /#{regex}/
|
48
|
+
node_set.find_all { |node| node.values.any? { |v| v =~ /#{regex}/ } }
|
41
49
|
end
|
42
50
|
}.new).empty?
|
43
51
|
end
|
@@ -54,4 +62,8 @@ class XmlVal
|
|
54
62
|
found = file.get_input_stream.find { |line| line =~ /(!DOCTYPE\b)|(!ELEMENT\b)|(!ENTITY\b)|(!NOTATION\b)|(!ATTLIST\b)/ }
|
55
63
|
!found.nil?
|
56
64
|
end
|
65
|
+
|
66
|
+
def self.error_involves_colorvalue?(error)
|
67
|
+
error.to_s.include?("ST_ColorValue") || error.to_s.include?("displaycolor")
|
68
|
+
end
|
57
69
|
end
|
data/ruby3mf.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
|
22
22
|
spec.add_development_dependency "bundler", "~> 1.11"
|
23
23
|
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
-
spec.add_development_dependency "rspec", "~> 3.
|
24
|
+
spec.add_development_dependency "rspec", "~> 3.5"
|
25
25
|
spec.add_development_dependency "simplecov"
|
26
26
|
|
27
27
|
spec.add_runtime_dependency 'rubyzip'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby3mf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Whitmarsh, Jeff Porter, and William Hertling
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-01-
|
11
|
+
date: 2017-01-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '3.
|
47
|
+
version: '3.5'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '3.
|
54
|
+
version: '3.5'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: simplecov
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -146,6 +146,7 @@ files:
|
|
146
146
|
- bin/console
|
147
147
|
- bin/folder_test.sh
|
148
148
|
- bin/setup
|
149
|
+
- bin/suite.rb
|
149
150
|
- lib/ruby3mf.rb
|
150
151
|
- lib/ruby3mf/3MFcoreSpec_1.1.xsd
|
151
152
|
- lib/ruby3mf/content_types.rb
|