ruby3mf 0.1.17 → 0.2.0
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/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
|