ruby3mf 0.2.5 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,118 +1,117 @@
1
- class Model3mf
2
-
3
- MATERIAL_EXTENSION = 'http://schemas.microsoft.com/3dmanufacturing/material/2015/02'
4
- SLICE_EXTENSION = 'http://schemas.microsoft.com/3dmanufacturing/slice/2015/07'
5
- PRODUCTION_EXTENSION = 'http://schemas.microsoft.com/3dmanufacturing/production/2015/06'
6
-
7
- VALID_EXTENSIONS = {
8
- MATERIAL_EXTENSION => {},
9
- SLICE_EXTENSION => {},
10
- PRODUCTION_EXTENSION => {}
11
- }.freeze
12
-
13
- VALID_CORE_METADATA_NAMES = ['Title', 'Designer', 'Description', 'Copyright', 'LicenseTerms', 'Rating', 'CreationDate', 'ModificationDate'].freeze
14
-
15
- def self.parse(document, zip_entry)
16
- model_doc = nil
17
-
18
- Log3mf.context "parsing model" do |l|
19
- begin
20
- model_doc = XmlVal.validate_parse(zip_entry, SchemaFiles::SchemaTemplate)
21
- rescue Nokogiri::XML::SyntaxError => e
22
- l.fatal_error :model_invalid_xml, e: e
23
- end
24
-
25
- l.context "verifying requiredextensions" do |l|
26
- required_extensions = model_doc.css("//model")[0]["requiredextensions"]
27
- if required_extensions
28
- required_extensions.split(" ").each do |ns|
29
- namespace_uri = model_doc.namespaces["xmlns:#{ns}"]
30
- if namespace_uri
31
- if VALID_EXTENSIONS.has_key? namespace_uri
32
- l.info "Found a valid required extension: #{namespace_uri}"
33
- else
34
- l.error :unknown_required_extension, ext: namespace_uri
35
- end
36
- else
37
- l.error :missing_extension_namespace_uri, ns: ns
38
- end
39
- end
40
- end
41
- end
42
-
43
- l.context "verifying 3D payload required resources" do |l|
44
- # results = model_doc.css("model resources m:texture2d")
45
- required_resources = model_doc.css("//model//resources//*[path]").collect { |n| n["path"] }
46
- required_resources += model_doc.css("//model//resources//object[thumbnail]").collect { |n| n["thumbnail"] }
47
-
48
- # for each, ensure that they exist in m.relationships
49
- relationship_resources = []
50
- rel_file = "#{Pathname(zip_entry.name).dirname.to_s}/_rels/#{File.basename(zip_entry.name)}.rels"
51
- relationships = document.relationships[rel_file]
52
- unless (relationships.nil?)
53
- relationship_resources = relationships.map { |relationship| relationship[:target] }
54
- end
55
-
56
- missing_resources = (required_resources - relationship_resources)
57
- if missing_resources.empty?
58
- l.info "All model required resources are defined in .rels relationship files."
59
- else
60
- missing_resources.each { |mr|
61
- l.error :model_resource_not_in_rels, mr: mr
62
- }
63
- end
64
-
65
- end
66
-
67
- l.context 'verifying resources' do |l|
68
- resources = model_doc.root.css("resources")
69
- if resources
70
- ids = resources.children.map { |child| child.attributes['id'].to_s if child.attributes['id'] }
71
- l.error :resource_id_collision if ids.uniq.size != ids.size
72
- pids = resources.children.map { |child| child.attributes['pid'].to_s }
73
- missing_pids = pids.select { |pid| !pid.empty? and !ids.include? pid }
74
- missing_pids.each do |p|
75
- l.error :resource_pid_missing, pid: p
76
- end
77
- end
78
- end
79
-
80
- l.context "verifying build items" do |l|
81
-
82
- l.error :build_with_other_item if model_doc.css('build/item').map { |x| x.attributes["objectid"].value }.map{ |id| model_doc.search(".//xmlns:object[@id=$id][@type=$type]", nil, { :id => id, :type => 'other' } ) }.flatten.any?
83
-
84
- end
85
-
86
- l.context "checking metadata" do |l|
87
- metadata_names = model_doc.root.css("metadata").map { |met| met['name'] }
88
- l.error :metadata_elements_with_same_name unless metadata_names.uniq!.nil?
89
-
90
- unless (metadata_names - VALID_CORE_METADATA_NAMES).empty?
91
- extra_names = metadata_names - VALID_CORE_METADATA_NAMES
92
- ns_names = extra_names.select { |n| n.include? ':' }
93
-
94
- l.error :invalid_metadata_under_defaultns unless (extra_names - ns_names).empty?
95
-
96
- unless ns_names.empty?
97
- prefixes = model_doc.root.namespace_definitions.map { |defs| defs.prefix }.reject { |pre| pre.nil? }
98
- l.error :invalid_metadata_name unless (ns_names.collect { |i| i.split(':').first } - prefixes).empty?
99
- end
100
- end
101
- end
102
-
103
- includes_material = model_doc.namespaces.values.include?(MATERIAL_EXTENSION)
104
- MeshAnalyzer.validate(model_doc, includes_material)
105
-
106
- l.context "verifying triangle normal" do |l|
107
- model_doc.css('model/resources/object').select { |object| ['model', 'solidsupport', ''].include?(object.attributes['type'].to_s) }.each do |object|
108
- meshes = object.css('mesh')
109
- meshes.each do |mesh|
110
- processor = MeshNormalAnalyzer.new(mesh)
111
- l.error :inward_facing_normal if processor.found_inward_triangle
112
- end
113
- end
114
- end
115
- end
116
- model_doc
117
- end
118
- end
1
+ class Model3mf
2
+
3
+ MATERIAL_EXTENSION = 'http://schemas.microsoft.com/3dmanufacturing/material/2015/02'
4
+ SLICE_EXTENSION = 'http://schemas.microsoft.com/3dmanufacturing/slice/2015/07'
5
+ PRODUCTION_EXTENSION = 'http://schemas.microsoft.com/3dmanufacturing/production/2015/06'
6
+
7
+ VALID_EXTENSIONS = {
8
+ MATERIAL_EXTENSION => {},
9
+ SLICE_EXTENSION => {},
10
+ PRODUCTION_EXTENSION => {}
11
+ }.freeze
12
+
13
+ VALID_CORE_METADATA_NAMES = ['Title', 'Designer', 'Description', 'Copyright', 'LicenseTerms', 'Rating', 'CreationDate', 'ModificationDate'].freeze
14
+
15
+ def self.parse(document, zip_entry)
16
+ model_doc = nil
17
+
18
+ Log3mf.context "parsing model" do |l|
19
+ begin
20
+ model_doc = XmlVal.validate_parse(zip_entry, SchemaFiles::SchemaTemplate)
21
+ rescue Nokogiri::XML::SyntaxError => e
22
+ l.fatal_error :model_invalid_xml, e: e
23
+ end
24
+
25
+ l.context "verifying requiredextensions" do |l|
26
+ model_doc.css("//model").map{|node| node.attributes["requiredextensions"]}.compact.each do |required_extension|
27
+ required_extension.value.split(" ").each do |ns|
28
+ namespace_uri = model_doc.namespaces["xmlns:#{ns}"]
29
+ if namespace_uri
30
+ if VALID_EXTENSIONS.has_key? namespace_uri
31
+ l.info "Found a valid required extension: #{namespace_uri}"
32
+ else
33
+ l.error :unknown_required_extension, ext: namespace_uri
34
+ end
35
+ else
36
+ l.error :missing_extension_namespace_uri, ns: ns
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ l.context "verifying 3D payload required resources" do |l|
43
+ # results = model_doc.css("model resources m:texture2d")
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"] }
46
+
47
+ # for each, ensure that they exist in m.relationships
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
54
+
55
+ missing_resources = (required_resources - relationship_resources)
56
+ if missing_resources.empty?
57
+ l.info "All model required resources are defined in .rels relationship files."
58
+ else
59
+ missing_resources.each { |mr|
60
+ l.error :model_resource_not_in_rels, mr: mr
61
+ }
62
+ end
63
+
64
+ end
65
+
66
+ l.context 'verifying resources' do |l|
67
+ resources = model_doc.root.css("resources")
68
+ if resources
69
+ ids = resources.children.map { |child| child.attributes['id'].to_s if child.attributes['id'] }
70
+ l.error :resource_id_collision if ids.uniq.size != ids.size
71
+ pids = resources.children.map { |child| child.attributes['pid'].to_s }
72
+ missing_pids = pids.select { |pid| !pid.empty? and !ids.include? pid }
73
+ missing_pids.each do |p|
74
+ l.error :resource_pid_missing, pid: p
75
+ end
76
+ end
77
+ end
78
+
79
+ l.context "verifying build items" do |l|
80
+
81
+ l.error :build_with_other_item if model_doc.css('build/item').map { |x| x.attributes["objectid"].value }.map{ |id| model_doc.search(".//xmlns:object[@id=$id][@type=$type]", nil, { :id => id, :type => 'other' } ) }.flatten.any?
82
+
83
+ end
84
+
85
+ l.context "checking metadata" do |l|
86
+ metadata_names = model_doc.root.css("metadata").map { |met| met['name'] }
87
+ l.error :metadata_elements_with_same_name unless metadata_names.uniq!.nil?
88
+
89
+ unless (metadata_names - VALID_CORE_METADATA_NAMES).empty?
90
+ extra_names = metadata_names - VALID_CORE_METADATA_NAMES
91
+ ns_names = extra_names.select { |n| n.include? ':' }
92
+
93
+ l.error :invalid_metadata_under_defaultns unless (extra_names - ns_names).empty?
94
+
95
+ unless ns_names.empty?
96
+ prefixes = model_doc.root.namespace_definitions.map { |defs| defs.prefix }.reject { |pre| pre.nil? }
97
+ l.error :invalid_metadata_name unless (ns_names.collect { |i| i.split(':').first } - prefixes).empty?
98
+ end
99
+ end
100
+ end
101
+
102
+ includes_material = model_doc.namespaces.values.include?(MATERIAL_EXTENSION)
103
+ MeshAnalyzer.validate(model_doc, includes_material)
104
+
105
+ l.context "verifying triangle normal" do |l|
106
+ model_doc.css('model/resources/object').select { |object| ['model', 'solidsupport', ''].include?(object.attributes['type'].to_s) }.each do |object|
107
+ meshes = object.css('mesh')
108
+ meshes.each do |mesh|
109
+ processor = MeshNormalAnalyzer.new(mesh)
110
+ l.error :inward_facing_normal if processor.found_inward_triangle
111
+ end
112
+ end
113
+ end
114
+ end
115
+ model_doc
116
+ end
117
+ end
@@ -1,50 +1,50 @@
1
- class Relationships
2
-
3
- def self.parse(zip_entry)
4
- relationships = []
5
- Log3mf.context "parsing relationships" do |l|
6
- begin
7
- # Parse Relationships XML
8
- doc = XmlVal.validate_parse(zip_entry)
9
-
10
- # Verify <Relationships><Relationship/></Relationships>
11
- root_element = doc.children[0]
12
- if root_element.name == "Relationships"
13
- relationship_elements = root_element.children
14
- if relationship_elements.size > 0
15
- relationship_elements.each do |node|
16
- if node.is_a?(Nokogiri::XML::Element) && node.name == "Relationship"
17
- l.error :multiple_relationships if (relationships.select { |r| r[:target] == node['Target'] && r[:type] == node['Type'] }.size > 0)
18
- l.error :non_unique_rel_id, :file => zip_entry.name, :id => node['Id'] if (relationships.select { |r| r[:id] == node['Id'] }.size > 0)
19
- relationships << {target: node['Target'], type: node['Type'], id: node['Id']}
20
- l.info "adding relationship: #{relationships.last.inspect}"
21
- else
22
- unless node.is_a? Nokogiri::XML::Text
23
- l.info "found non-Relationship node: #{node.name}"
24
- end
25
- end
26
- end
27
-
28
- if zip_entry.name=="_rels/.rels"
29
- l.context "Verifying StartPart" do |l|
30
- start_part_type = Document::MODEL_TYPE
31
- start_part_count = relationships.select { |r| r[:type] == start_part_type }.size
32
- if start_part_count != 1
33
- l.error :invalid_startpart_type
34
- end
35
- end
36
- end
37
- else
38
- l.error :dot_rels_file_no_relationship_element
39
- end
40
- else
41
- l.error :dot_rels_file_missing_relationships_element
42
- end
43
-
44
- rescue Nokogiri::XML::SyntaxError => e
45
- l.error :dot_rels_file_has_invalid_xml, e: "#{e.message}"
46
- end
47
- end
48
- relationships
49
- end
50
- end
1
+ class Relationships
2
+
3
+ def self.parse(zip_entry)
4
+ relationships = []
5
+ Log3mf.context "parsing relationships" do |l|
6
+ begin
7
+ # Parse Relationships XML
8
+ doc = XmlVal.validate_parse(zip_entry)
9
+
10
+ # Verify <Relationships><Relationship/></Relationships>
11
+ root_element = doc.children[0]
12
+ if root_element.name == "Relationships"
13
+ relationship_elements = root_element.children
14
+ if relationship_elements.size > 0
15
+ relationship_elements.each do |node|
16
+ if node.is_a?(Nokogiri::XML::Element) && node.name == "Relationship"
17
+ l.error :multiple_relationships if (relationships.select { |r| r[:target] == node['Target'] && r[:type] == node['Type'] }.size > 0)
18
+ l.error :non_unique_rel_id, :file => zip_entry.name, :id => node['Id'] if (relationships.select { |r| r[:id] == node['Id'] }.size > 0)
19
+ relationships << {target: node['Target'], type: node['Type'], id: node['Id']}
20
+ l.info "adding relationship: #{relationships.last.inspect}"
21
+ else
22
+ unless node.is_a? Nokogiri::XML::Text
23
+ l.info "found non-Relationship node: #{node.name}"
24
+ end
25
+ end
26
+ end
27
+
28
+ if zip_entry.name=="_rels/.rels"
29
+ l.context "Verifying StartPart" do |l|
30
+ start_part_type = Document::MODEL_TYPE
31
+ start_part_count = relationships.select { |r| r[:type] == start_part_type }.size
32
+ if start_part_count != 1
33
+ l.error :invalid_startpart_type
34
+ end
35
+ end
36
+ end
37
+ else
38
+ l.error :dot_rels_file_no_relationship_element
39
+ end
40
+ else
41
+ l.error :dot_rels_file_missing_relationships_element
42
+ end
43
+
44
+ rescue Nokogiri::XML::SyntaxError => e
45
+ l.error :dot_rels_file_has_invalid_xml, e: "#{e.message}"
46
+ end
47
+ end
48
+ relationships
49
+ end
50
+ end
@@ -1,26 +1,26 @@
1
- require 'erb'
2
-
3
- class SchemaFiles
4
-
5
- SchemaTemplate = File.join(File.dirname(__FILE__), "3MFcoreSpec_1.1.xsd.template")
6
- SchemaLocation = File.join(File.dirname(__FILE__), "xml.xsd")
7
-
8
- class << self
9
-
10
- def open(file)
11
- @@template ||= File.open(file, "r") do |file|
12
- file.read
13
- end
14
-
15
- yield(SchemaFiles.render)
16
-
17
- end
18
-
19
- def render
20
- @@xsd_content ||= ERB.new(@@template).result( binding )
21
- end
22
-
23
- end
24
-
25
-
26
- end
1
+ require 'erb'
2
+
3
+ class SchemaFiles
4
+
5
+ SchemaTemplate = File.join(File.dirname(__FILE__), "3MFcoreSpec_1.1.xsd.template")
6
+ SchemaLocation = File.join(File.dirname(__FILE__), "xml.xsd")
7
+
8
+ class << self
9
+
10
+ def open(file)
11
+ @@template ||= File.open(file, "r") do |file|
12
+ file.read
13
+ end
14
+
15
+ yield(SchemaFiles.render)
16
+
17
+ end
18
+
19
+ def render
20
+ @@xsd_content ||= ERB.new(@@template).result( binding )
21
+ end
22
+
23
+ end
24
+
25
+
26
+ end
@@ -1,29 +1,29 @@
1
- class Texture3mf
2
- attr_accessor :name
3
-
4
- def initialize(document)
5
- @doc = document
6
- end
7
-
8
- def self.parse(document, relationship_file)
9
- t = new(document)
10
- t.name = relationship_file.name
11
- stream = relationship_file.get_input_stream
12
- img_type = MimeMagic.by_magic(stream)
13
- Log3mf.context "Texture3mf" do |l|
14
- l.fatal_error :zero_size_texture unless img_type
15
- l.debug "texture is of type: #{img_type}"
16
- l.error(:invalid_texture_file_type, type: img_type) unless ['image/png', 'image/jpeg'].include? img_type.type
17
- end
18
- t
19
- end
20
-
21
- def update(bytes)
22
- @doc.objects[name]=bytes
23
- end
24
-
25
- def contents
26
- @doc.objects[name] || @doc.contents_for(name)
27
- end
28
-
29
- end
1
+ class Texture3mf
2
+ attr_accessor :name
3
+
4
+ def initialize(document)
5
+ @doc = document
6
+ end
7
+
8
+ def self.parse(document, relationship_file)
9
+ t = new(document)
10
+ t.name = relationship_file.name
11
+ stream = relationship_file.get_input_stream
12
+ img_type = MimeMagic.by_magic(stream)
13
+ Log3mf.context "Texture3mf" do |l|
14
+ l.fatal_error :zero_size_texture unless img_type
15
+ l.debug "texture is of type: #{img_type}"
16
+ l.error(:invalid_texture_file_type, type: img_type) unless ['image/png', 'image/jpeg'].include? img_type.type
17
+ end
18
+ t
19
+ end
20
+
21
+ def update(bytes)
22
+ @doc.objects[name]=bytes
23
+ end
24
+
25
+ def contents
26
+ @doc.objects[name] || @doc.contents_for(name)
27
+ end
28
+
29
+ end