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