ruby3mf 0.1.17 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/ruby3mf/3MFcoreSpec_1.1.xsd +188 -0
- data/lib/ruby3mf/document.rb +30 -34
- data/lib/ruby3mf/errors.yml +5 -2
- data/lib/ruby3mf/model3mf.rb +17 -15
- data/lib/ruby3mf/version.rb +1 -1
- data/lib/ruby3mf/xml_val.rb +14 -4
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c96a243d6561a8d9bf5a047012d87275035191ae
|
4
|
+
data.tar.gz: 7e913c8167d75dff3aad79a74fda2270b4c2811a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 537228530d6b5baf851932a7c920b99b85ad25a202dfb6d5f20887b7cf9cb195588425031079bf483533d2f9bb06e856fe5f34f17fa18d8574baed3162fac2c5
|
7
|
+
data.tar.gz: d416b8615337f1a199e6f8e0f968a754e07204a377d0b9cce7de63822bef42c84eb08feadd179552ae36f84b7a7bac0293fdae121b79053d354573f9efaa20bd
|
@@ -0,0 +1,188 @@
|
|
1
|
+
<xs:schema xmlns="http://schemas.microsoft.com/3dmanufacturing/core/2015/02" xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
2
|
+
xmlns:xml="http://www.w3.org/XML/1998/namespace"
|
3
|
+
targetNamespace="http://schemas.microsoft.com/3dmanufacturing/core/2015/02" elementFormDefault="unqualified"
|
4
|
+
attributeFormDefault="unqualified" blockDefault="#all">
|
5
|
+
<xs:import namespace="http://www.w3.org/XML/1998/namespace"
|
6
|
+
schemaLocation="http://www.w3.org/2001/xml.xsd"/>
|
7
|
+
<xs:annotation>
|
8
|
+
<xs:documentation><![CDATA[
|
9
|
+
Schema notes:
|
10
|
+
Items within this schema follow a simple naming convention of appending a prefix indicating the type of element for references:
|
11
|
+
Unprefixed: Element names
|
12
|
+
CT_: Complex types
|
13
|
+
ST_: Simple types
|
14
|
+
]]></xs:documentation>
|
15
|
+
</xs:annotation>
|
16
|
+
<!-- Complex Types -->
|
17
|
+
<xs:complexType name="CT_Model">
|
18
|
+
<xs:sequence>
|
19
|
+
<xs:element ref="metadata" minOccurs="0" maxOccurs="30000"/>
|
20
|
+
<xs:element ref="resources"/>
|
21
|
+
<xs:element ref="build"/>
|
22
|
+
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="30000"/>
|
23
|
+
</xs:sequence>
|
24
|
+
<xs:attribute name="unit" type="ST_Unit" default="millimeter"/>
|
25
|
+
<xs:attribute ref="xml:lang"/>
|
26
|
+
<xs:attribute name="requiredextensions" type="xs:string"/>
|
27
|
+
<xs:anyAttribute namespace="##other" processContents="lax"/>
|
28
|
+
</xs:complexType>
|
29
|
+
<xs:complexType name="CT_Resources">
|
30
|
+
<xs:sequence>
|
31
|
+
<xs:element ref="basematerials" minOccurs="0" maxOccurs="30000"/>
|
32
|
+
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="30000"/>
|
33
|
+
<xs:element ref="object" minOccurs="0" maxOccurs="30000"/>
|
34
|
+
</xs:sequence>
|
35
|
+
<xs:anyAttribute namespace="##other" processContents="lax"/>
|
36
|
+
</xs:complexType>
|
37
|
+
<xs:complexType name="CT_Build">
|
38
|
+
<xs:sequence>
|
39
|
+
<xs:element ref="item" minOccurs="0" maxOccurs="30000"/>
|
40
|
+
</xs:sequence>
|
41
|
+
<xs:anyAttribute namespace="##other" processContents="lax"/>
|
42
|
+
</xs:complexType>
|
43
|
+
<xs:complexType name="CT_BaseMaterials">
|
44
|
+
<xs:sequence>
|
45
|
+
<xs:element ref="base" maxOccurs="30000"/>
|
46
|
+
</xs:sequence>
|
47
|
+
<xs:attribute name="id" type="ST_ResourceID" use="required"/>
|
48
|
+
</xs:complexType>
|
49
|
+
<xs:complexType name="CT_Base">
|
50
|
+
<xs:attribute name="name" type="xs:string" use="required"/>
|
51
|
+
<xs:attribute name="displaycolor" type="ST_ColorValue" use="required"/>
|
52
|
+
<xs:anyAttribute namespace="##other" processContents="lax"/>
|
53
|
+
</xs:complexType>
|
54
|
+
<xs:complexType name="CT_Object">
|
55
|
+
<xs:choice>
|
56
|
+
<xs:element ref="mesh"/>
|
57
|
+
<xs:element ref="components"/>
|
58
|
+
</xs:choice>
|
59
|
+
<xs:attribute name="id" type="ST_ResourceID" use="required"/>
|
60
|
+
<xs:attribute name="type" type="ST_ObjectType" default="model"/>
|
61
|
+
<xs:attribute name="matid" type="ST_ResourceID"/>
|
62
|
+
<xs:attribute name="matindex" type="ST_ResourceIndex"/>
|
63
|
+
<xs:attribute name="partnumber" type="xs:string"/>
|
64
|
+
<xs:attribute name="name" type="xs:string"/>
|
65
|
+
<xs:attribute name="pid" type="ST_ResourceID"/>
|
66
|
+
<xs:attribute name="pindex" type="ST_ResourceIndex"/>
|
67
|
+
<xs:attribute name="thumbnail" type="ST_UriReference" />
|
68
|
+
<xs:anyAttribute namespace="##other" processContents="lax"/>
|
69
|
+
</xs:complexType>
|
70
|
+
<xs:complexType name="CT_Mesh">
|
71
|
+
<xs:sequence>
|
72
|
+
<xs:element ref="vertices"/>
|
73
|
+
<xs:element ref="triangles"/>
|
74
|
+
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="30000"/>
|
75
|
+
</xs:sequence>
|
76
|
+
</xs:complexType>
|
77
|
+
<xs:complexType name="CT_Vertices">
|
78
|
+
<xs:sequence>
|
79
|
+
<xs:element ref="vertex" minOccurs="3" maxOccurs="30000"/>
|
80
|
+
</xs:sequence>
|
81
|
+
</xs:complexType>
|
82
|
+
<xs:complexType name="CT_Vertex">
|
83
|
+
<xs:attribute name="x" type="ST_Number" use="required"/>
|
84
|
+
<xs:attribute name="y" type="ST_Number" use="required"/>
|
85
|
+
<xs:attribute name="z" type="ST_Number" use="required"/>
|
86
|
+
<xs:anyAttribute namespace="##other" processContents="lax"/>
|
87
|
+
</xs:complexType>
|
88
|
+
<xs:complexType name="CT_Triangles">
|
89
|
+
<xs:sequence>
|
90
|
+
<xs:element ref="triangle" minOccurs="1" maxOccurs="30000"/>
|
91
|
+
</xs:sequence>
|
92
|
+
</xs:complexType>
|
93
|
+
<xs:complexType name="CT_Triangle">
|
94
|
+
<xs:attribute name="v1" type="ST_ResourceIndex" use="required"/>
|
95
|
+
<xs:attribute name="v2" type="ST_ResourceIndex" use="required"/>
|
96
|
+
<xs:attribute name="v3" type="ST_ResourceIndex" use="required"/>
|
97
|
+
<xs:attribute name="p1" type="ST_ResourceIndex"/>
|
98
|
+
<xs:attribute name="p2" type="ST_ResourceIndex"/>
|
99
|
+
<xs:attribute name="p3" type="ST_ResourceIndex"/>
|
100
|
+
<xs:attribute name="pid" type="ST_ResourceID"/>
|
101
|
+
<xs:anyAttribute namespace="##other" processContents="lax"/>
|
102
|
+
</xs:complexType>
|
103
|
+
<xs:complexType name="CT_Components">
|
104
|
+
<xs:sequence>
|
105
|
+
<xs:element ref="component" maxOccurs="30000"/>
|
106
|
+
</xs:sequence>
|
107
|
+
</xs:complexType>
|
108
|
+
<xs:complexType name="CT_Component">
|
109
|
+
<xs:attribute name="objectid" type="ST_ResourceID" use="required"/>
|
110
|
+
<xs:attribute name="transform" type="ST_Matrix3D"/>
|
111
|
+
<xs:anyAttribute namespace="##other" processContents="lax"/>
|
112
|
+
</xs:complexType>
|
113
|
+
<xs:complexType name="CT_Metadata" mixed="true">
|
114
|
+
<xs:attribute name="name" type="xs:QName" use="required"/>
|
115
|
+
</xs:complexType>
|
116
|
+
<xs:complexType name="CT_Item">
|
117
|
+
<xs:attribute name="objectid" type="ST_ResourceID" use="required"/>
|
118
|
+
<xs:attribute name="transform" type="ST_Matrix3D"/>
|
119
|
+
<xs:attribute name="itemref" type="xs:ID"/>
|
120
|
+
<xs:anyAttribute namespace="##other" processContents="lax"/>
|
121
|
+
</xs:complexType>
|
122
|
+
<!-- Simple Types -->
|
123
|
+
<xs:simpleType name="ST_Unit">
|
124
|
+
<xs:restriction base="xs:string">
|
125
|
+
<xs:enumeration value="micron"/>
|
126
|
+
<xs:enumeration value="millimeter"/>
|
127
|
+
<xs:enumeration value="centimeter"/>
|
128
|
+
<xs:enumeration value="inch"/>
|
129
|
+
<xs:enumeration value="foot"/>
|
130
|
+
<xs:enumeration value="meter"/>
|
131
|
+
</xs:restriction>
|
132
|
+
</xs:simpleType>
|
133
|
+
<xs:simpleType name="ST_ColorValue">
|
134
|
+
<xs:restriction base="xs:string">
|
135
|
+
<xs:pattern value="#[0-9|A-F|a-f][0-9|A-F|a-f][0-9|A-F|a-f][0-9|A-F|a-f][0-9|A-F|a-f][0-9|A-F|a-f]([0-9|A-F|a-f][0-9|A-F|a-f])?"/>
|
136
|
+
</xs:restriction>
|
137
|
+
</xs:simpleType>
|
138
|
+
<xs:simpleType name="ST_UriReference">
|
139
|
+
<xs:restriction base="xs:anyURI">
|
140
|
+
<xs:pattern value="/.*"/>
|
141
|
+
</xs:restriction>
|
142
|
+
</xs:simpleType>
|
143
|
+
<xs:simpleType name="ST_Matrix3D">
|
144
|
+
<xs:restriction base="xs:string">
|
145
|
+
<xs:pattern value="((\-|\+)?(([0-9]+(\.[0-9]+)?)|(\.[0-9]+))((e|E)(\-|\+)?[0-9]+)?) ((\-|\+)?(([0-9]+(\.[0-9]+)?)|(\.[0-9]+))((e|E)(\-|\+)?[0-9]+)?) ((\-|\+)?(([0-9]+(\.[0-9]+)?)|(\.[0-9]+))((e|E)(\-|\+)?[0-9]+)?) ((\-|\+)?(([0-9]+(\.[0-9]+)?)|(\.[0-9]+))((e|E)(\-|\+)?[0-9]+)?) ((\-|\+)?(([0-9]+(\.[0-9]+)?)|(\.[0-9]+))((e|E)(\-|\+)?[0-9]+)?) ((\-|\+)?(([0-9]+(\.[0-9]+)?)|(\.[0-9]+))((e|E)(\-|\+)?[0-9]+)?) ((\-|\+)?(([0-9]+(\.[0-9]+)?)|(\.[0-9]+))((e|E)(\-|\+)?[0-9]+)?) ((\-|\+)?(([0-9]+(\.[0-9]+)?)|(\.[0-9]+))((e|E)(\-|\+)?[0-9]+)?) ((\-|\+)?(([0-9]+(\.[0-9]+)?)|(\.[0-9]+))((e|E)(\-|\+)?[0-9]+)?) ((\-|\+)?(([0-9]+(\.[0-9]+)?)|(\.[0-9]+))((e|E)(\-|\+)?[0-9]+)?) ((\-|\+)?(([0-9]+(\.[0-9]+)?)|(\.[0-9]+))((e|E)(\-|\+)?[0-9]+)?) ((\-|\+)?(([0-9]+(\.[0-9]+)?)|(\.[0-9]+))((e|E)(\-|\+)?[0-9]+)?)"/>
|
146
|
+
</xs:restriction>
|
147
|
+
</xs:simpleType>
|
148
|
+
<xs:simpleType name="ST_Number">
|
149
|
+
<xs:restriction base="xs:double">
|
150
|
+
<xs:whiteSpace value="collapse"/>
|
151
|
+
<xs:pattern value="((\-|\+)?(([0-9]+(\.[0-9]+)?)|(\.[0-9]+))((e|E)(\-|\+)?[0-9]+)?)"/>
|
152
|
+
</xs:restriction>
|
153
|
+
</xs:simpleType>
|
154
|
+
<xs:simpleType name="ST_ResourceID">
|
155
|
+
<xs:restriction base="xs:positiveInteger">
|
156
|
+
<xs:maxExclusive value="2147483648"/>
|
157
|
+
</xs:restriction>
|
158
|
+
</xs:simpleType>
|
159
|
+
<xs:simpleType name="ST_ResourceIndex">
|
160
|
+
<xs:restriction base="xs:nonNegativeInteger">
|
161
|
+
<xs:maxExclusive value="2147483648"/>
|
162
|
+
</xs:restriction>
|
163
|
+
</xs:simpleType>
|
164
|
+
<xs:simpleType name="ST_ObjectType">
|
165
|
+
<xs:restriction base="xs:string">
|
166
|
+
<xs:enumeration value="model"/>
|
167
|
+
<xs:enumeration value="solidsupport"/>
|
168
|
+
<xs:enumeration value="support"/>
|
169
|
+
<xs:enumeration value="other"/>
|
170
|
+
</xs:restriction>
|
171
|
+
</xs:simpleType>
|
172
|
+
<!-- Elements -->
|
173
|
+
<xs:element name="model" type="CT_Model"/>
|
174
|
+
<xs:element name="resources" type="CT_Resources"/>
|
175
|
+
<xs:element name="build" type="CT_Build"/>
|
176
|
+
<xs:element name="basematerials" type="CT_BaseMaterials"/>
|
177
|
+
<xs:element name="base" type="CT_Base"/>
|
178
|
+
<xs:element name="object" type="CT_Object"/>
|
179
|
+
<xs:element name="mesh" type="CT_Mesh"/>
|
180
|
+
<xs:element name="vertices" type="CT_Vertices"/>
|
181
|
+
<xs:element name="vertex" type="CT_Vertex"/>
|
182
|
+
<xs:element name="triangles" type="CT_Triangles"/>
|
183
|
+
<xs:element name="triangle" type="CT_Triangle"/>
|
184
|
+
<xs:element name="components" type="CT_Components"/>
|
185
|
+
<xs:element name="component" type="CT_Component"/>
|
186
|
+
<xs:element name="metadata" type="CT_Metadata"/>
|
187
|
+
<xs:element name="item" type="CT_Item"/>
|
188
|
+
</xs:schema>
|
data/lib/ruby3mf/document.rb
CHANGED
@@ -30,7 +30,7 @@ class Document
|
|
30
30
|
self.thumbnails=[]
|
31
31
|
self.textures=[]
|
32
32
|
self.objects={}
|
33
|
-
self.relationships=
|
33
|
+
self.relationships={}
|
34
34
|
self.types=[]
|
35
35
|
self.parts=[]
|
36
36
|
@zip_filename = zip_filename
|
@@ -91,6 +91,7 @@ class Document
|
|
91
91
|
begin
|
92
92
|
u = URI part.name
|
93
93
|
rescue ArgumentError, URI::InvalidURIError
|
94
|
+
l.fatal_error "This NEVER Happens! mdw 12Jan2017"
|
94
95
|
l.error :err_uri_bad
|
95
96
|
next
|
96
97
|
end
|
@@ -104,7 +105,6 @@ class Document
|
|
104
105
|
end
|
105
106
|
|
106
107
|
l.context 'content types' do |l|
|
107
|
-
# 1. Get Content Types
|
108
108
|
content_type_match = zip_file.glob('\[Content_Types\].xml').first
|
109
109
|
if content_type_match
|
110
110
|
m.types = ContentTypes.parse(content_type_match)
|
@@ -114,58 +114,54 @@ class Document
|
|
114
114
|
end
|
115
115
|
|
116
116
|
l.context 'relationships' do |l|
|
117
|
-
# 2. Get Relationships
|
118
|
-
# rel_folders = zip_file.glob('**/_rels')
|
119
|
-
# l.fatal_error "Missing any _rels folder", page: 4 unless rel_folders.size>0
|
120
|
-
|
121
|
-
# 2.1 Validate that the top level _rels/.rel file exists
|
122
117
|
rel_file = zip_file.glob('_rels/.rels').first
|
123
118
|
l.fatal_error 'Missing required file _rels/.rels', page: 4 unless rel_file
|
124
119
|
|
125
120
|
zip_file.glob('**/*.rels').each do |rel|
|
126
|
-
m.relationships
|
121
|
+
m.relationships[rel.name] = Relationships.parse(rel)
|
127
122
|
end
|
128
123
|
end
|
129
124
|
|
130
125
|
l.context "print tickets" do |l|
|
131
|
-
print_ticket_types = m.relationships.select { |rel| rel[:type] == PRINT_TICKET_TYPE }
|
126
|
+
print_ticket_types = m.relationships.flat_map { |k, v| v }.select { |rel| rel[:type] == PRINT_TICKET_TYPE }
|
132
127
|
l.error :multiple_print_tickets if print_ticket_types.size > 1
|
133
128
|
end
|
134
129
|
|
135
130
|
l.context "relationship elements" do |l|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
131
|
+
m.relationships.each do |file_name, rels|
|
132
|
+
rels.each do |rel|
|
133
|
+
l.context rel[:target] do |l|
|
134
|
+
|
135
|
+
begin
|
136
|
+
u = URI rel[:target]
|
137
|
+
rescue URI::InvalidURIError
|
138
|
+
l.error :err_uri_bad
|
139
|
+
next
|
140
|
+
end
|
146
141
|
|
147
|
-
|
142
|
+
l.error :err_uri_relative_path unless u.to_s.start_with? '/'
|
148
143
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
144
|
+
target = rel[:target].gsub(/^\//, "")
|
145
|
+
l.error :err_uri_empty_segment if target.end_with? '/' or target.include? '//'
|
146
|
+
l.error :err_uri_relative_path if target.include? '/../'
|
147
|
+
relationship_file = zip_file.glob(target).first
|
153
148
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
149
|
+
if relationship_file
|
150
|
+
relationship_type = RELATIONSHIP_TYPES[rel[:type]]
|
151
|
+
if relationship_type.nil?
|
152
|
+
l.error :invalid_relationship_type, type: rel[:type]
|
153
|
+
else
|
154
|
+
unless relationship_type[:klass].nil?
|
155
|
+
m.send(relationship_type[:collection]) << {
|
161
156
|
rel_id: rel[:id],
|
162
157
|
target: rel[:target],
|
163
158
|
object: Object.const_get(relationship_type[:klass]).parse(m, relationship_file)
|
164
|
-
|
159
|
+
}
|
160
|
+
end
|
165
161
|
end
|
162
|
+
else
|
163
|
+
l.error "Relationship Target file #{rel[:target]} not found", page: 11
|
166
164
|
end
|
167
|
-
else
|
168
|
-
l.error "Relationship Target file #{rel[:target]} not found", page: 11
|
169
165
|
end
|
170
166
|
end
|
171
167
|
end
|
data/lib/ruby3mf/errors.yml
CHANGED
@@ -14,7 +14,7 @@ err_uri_relative_path:
|
|
14
14
|
msg: 'Part names must not include relative paths'
|
15
15
|
page: 13
|
16
16
|
model_resource_not_in_rels:
|
17
|
-
msg: "Missing required resource: %{mr} Resource referenced in model, but not in .rels relationship file"
|
17
|
+
msg: "Missing required resource: %{mr}. Resource referenced in model, but not in .rels relationship file"
|
18
18
|
page: 10
|
19
19
|
resource_3dmodel_orientation:
|
20
20
|
msg: "Bad triangle orientation"
|
@@ -132,4 +132,7 @@ has_commas_for_floats:
|
|
132
132
|
page: 15
|
133
133
|
invalid_language_locale:
|
134
134
|
msg: "locale should be en-US"
|
135
|
-
page: 15
|
135
|
+
page: 15
|
136
|
+
invalid_xml_core:
|
137
|
+
msg: "XML file doesn't pass validation with the XSD file"
|
138
|
+
page: 41
|
data/lib/ruby3mf/model3mf.rb
CHANGED
@@ -2,19 +2,21 @@ require_relative 'mesh_analyzer'
|
|
2
2
|
|
3
3
|
class Model3mf
|
4
4
|
|
5
|
-
VALID_UNITS = [
|
5
|
+
VALID_UNITS = ['micron', 'millimeter', 'centimeter', 'meter', 'inch', 'foot'].freeze
|
6
6
|
VALID_EXTENSIONS = {
|
7
7
|
'http://schemas.microsoft.com/3dmanufacturing/slice/2015/07' => {},
|
8
8
|
'http://schemas.microsoft.com/3dmanufacturing/material/2015/02' => {},
|
9
9
|
'http://schemas.microsoft.com/3dmanufacturing/production/2015/06' => {},
|
10
10
|
}.freeze
|
11
11
|
|
12
|
+
SCHEMA = '3MFcoreSpec_1.1.xsd'
|
13
|
+
|
12
14
|
def self.parse(document, zip_entry)
|
13
15
|
model_doc = nil
|
14
16
|
|
15
17
|
Log3mf.context "parsing model" do |l|
|
16
18
|
begin
|
17
|
-
model_doc = XmlVal.validate_parse(zip_entry)
|
19
|
+
model_doc = XmlVal.validate_parse(zip_entry, SCHEMA)
|
18
20
|
rescue Nokogiri::XML::SyntaxError => e
|
19
21
|
l.fatal_error "Model file invalid XML. Exception #{e}"
|
20
22
|
end
|
@@ -40,9 +42,15 @@ class Model3mf
|
|
40
42
|
l.context "verifying 3D payload required resources" do |l|
|
41
43
|
# results = model_doc.css("model resources m:texture2d")
|
42
44
|
required_resources = model_doc.css("//model//resources//*[path]").collect { |n| n["path"] }
|
45
|
+
required_resources += model_doc.css("//model//resources//object[thumbnail]").collect { |n| n["thumbnail"] }
|
43
46
|
|
44
47
|
# for each, ensure that they exist in m.relationships
|
45
|
-
relationship_resources =
|
48
|
+
relationship_resources = []
|
49
|
+
rel_file = "#{Pathname(zip_entry.name).dirname.to_s}/_rels/#{File.basename(zip_entry.name)}.rels"
|
50
|
+
relationships = document.relationships[rel_file]
|
51
|
+
unless (relationships.nil?)
|
52
|
+
relationship_resources = relationships.map { |relationship| relationship[:target] }
|
53
|
+
end
|
46
54
|
|
47
55
|
missing_resources = (required_resources - relationship_resources)
|
48
56
|
if missing_resources.empty?
|
@@ -79,16 +87,6 @@ class Model3mf
|
|
79
87
|
end
|
80
88
|
end
|
81
89
|
|
82
|
-
l.context "verifying model structure" do |l|
|
83
|
-
root = model_doc.root
|
84
|
-
l.error :root_3dmodel_element_not_model if root.name != "model"
|
85
|
-
|
86
|
-
l.error(:invalid_model_unit, unit: root.attr('unit')) unless VALID_UNITS.include?(root.attr('unit'))
|
87
|
-
|
88
|
-
children = model_doc.root.children.map { |child| child.name }
|
89
|
-
l.error :missing_model_children unless children.include?("resources") && children.include?("build")
|
90
|
-
end
|
91
|
-
|
92
90
|
l.context "verifying build items" do |l|
|
93
91
|
build = find_child(model_doc.root, "build")
|
94
92
|
if build
|
@@ -105,14 +103,18 @@ class Model3mf
|
|
105
103
|
end
|
106
104
|
|
107
105
|
l.context "checking metadata" do |l|
|
106
|
+
|
108
107
|
metadata = model_doc.root.css("metadata")
|
108
|
+
|
109
109
|
metadata_names = metadata.map { |met| met['name'] }
|
110
110
|
l.error :metadata_elements_with_same_name unless metadata_names.uniq!.nil?
|
111
111
|
|
112
112
|
# metadata values allowed under default namespace (xmlns):
|
113
|
-
metadata_values = ['Title', 'Designer', 'Description', 'Copyright', 'LicenseTerms', 'Rating', 'CreationDate', 'ModificationDate'
|
113
|
+
metadata_values = ['Title', 'Designer', 'Description', 'Copyright', 'LicenseTerms', 'Rating', 'CreationDate', 'ModificationDate']
|
114
114
|
|
115
|
-
|
115
|
+
known_namespaces = ["http://schemas.microsoft.com/3dmanufacturing/core/2015/02", "http://schemas.microsoft.com/3dmanufacturing/material/2015/02"]
|
116
|
+
only_known_namespaces = model_doc.root.namespace_definitions.all? { |ns| known_namespaces.include?(ns.href) }
|
117
|
+
unless model_doc.root.namespace.href.nil? || !only_known_namespaces
|
116
118
|
l.error :invalid_metadata_under_defaultns unless (metadata_names - metadata_values).empty?
|
117
119
|
end
|
118
120
|
end
|
data/lib/ruby3mf/version.rb
CHANGED
data/lib/ruby3mf/xml_val.rb
CHANGED
@@ -2,24 +2,34 @@ require 'nokogiri'
|
|
2
2
|
|
3
3
|
class XmlVal
|
4
4
|
|
5
|
-
def self.validate_parse(
|
6
|
-
doc = Nokogiri::XML(
|
5
|
+
def self.validate_parse(xml_file, schema_name=nil)
|
6
|
+
doc = Nokogiri::XML(xml_file.get_input_stream) do |config|
|
7
7
|
config.strict.nonet.noblanks
|
8
8
|
end
|
9
|
-
validate(
|
9
|
+
validate(xml_file, doc, schema_name)
|
10
10
|
doc
|
11
11
|
end
|
12
12
|
|
13
|
-
def self.validate(file, document)
|
13
|
+
def self.validate(file, document, schema_filename=nil)
|
14
14
|
Log3mf.context "validations" do |l|
|
15
15
|
l.error :invalid_language_locale if invalid_locale?(document)
|
16
16
|
l.error :has_xml_space_attribute if space_attribute_exists?(document)
|
17
17
|
l.error :wrong_encoding if xml_not_utf8_encoded?(document)
|
18
18
|
l.error :dtd_not_allowed if dtd_exists?(file)
|
19
19
|
l.error :has_commas_for_floats if bad_floating_numbers?(document)
|
20
|
+
|
21
|
+
if schema_filename
|
22
|
+
Log3mf.context "validating core schema" do |l|
|
23
|
+
xsd = Nokogiri::XML::Schema(File.read(File.join(File.dirname(__FILE__), schema_filename)))
|
24
|
+
core_schema_errors = xsd.validate(document)
|
25
|
+
core_schema_errors.each { |error| puts error } if ENV["DEBUG_XSD_VALIDATION"]
|
26
|
+
l.error :invalid_xml_core if core_schema_errors.size > 0
|
27
|
+
end
|
28
|
+
end
|
20
29
|
end
|
21
30
|
end
|
22
31
|
|
32
|
+
|
23
33
|
def self.invalid_locale?(document)
|
24
34
|
!document.xpath('//@xml:lang').empty? && document.xpath('//@xml:lang').text != "en-US"
|
25
35
|
end
|
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.
|
4
|
+
version: 0.2.0
|
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-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -147,6 +147,7 @@ files:
|
|
147
147
|
- bin/folder_test.sh
|
148
148
|
- bin/setup
|
149
149
|
- lib/ruby3mf.rb
|
150
|
+
- lib/ruby3mf/3MFcoreSpec_1.1.xsd
|
150
151
|
- lib/ruby3mf/content_types.rb
|
151
152
|
- lib/ruby3mf/document.rb
|
152
153
|
- lib/ruby3mf/edge_list.rb
|