ruby3mf 0.2.5 → 0.2.6

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.
@@ -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