ruby3mf 0.2.6 → 0.2.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,117 +1,120 @@
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
+ 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
+ KNOWN_EXTENSIONS = {
8
+ MATERIAL_EXTENSION => {name: '3MF Materials and Properties Extension', supported: false},
9
+ SLICE_EXTENSION => {name: '3MF Slice Extension', supported: false},
10
+ PRODUCTION_EXTENSION => {name: '3MF Production Extension', supported: false}
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 supported extensions" do |l|
26
+ model_doc.css("//model").first.namespaces.each do |prefix, uri|
27
+ unless prefix == "xmlns"
28
+ ext = KNOWN_EXTENSIONS[uri]
29
+ if ext.nil? || !ext[:supported]
30
+ l.warning :unsupported_extension, ext: (ext.nil? ? uri : ext[:name])
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ l.context "verifying requiredextensions" do |l|
37
+ model_doc.css("//model").map{|node| node.attributes["requiredextensions"]}.compact.each do |required_extension|
38
+ required_extension.value.split(" ").each do |ns|
39
+ namespace_uri = model_doc.namespaces["xmlns:#{ns}"]
40
+ l.error :missing_extension_namespace_uri, ns: ns unless namespace_uri
41
+ end
42
+ end
43
+ end
44
+
45
+ l.context "verifying 3D payload required resources" do |l|
46
+ # results = model_doc.css("model resources m:texture2d")
47
+ required_resources = model_doc.css("//model//resources//*[path]").collect { |n| n["path"] }
48
+ required_resources += model_doc.css("//model//resources//object[thumbnail]").collect { |n| n["thumbnail"] }
49
+
50
+ # for each, ensure that they exist in m.relationships
51
+ relationship_resources = []
52
+ rel_file = "#{Pathname(zip_entry.name).dirname.to_s}/_rels/#{File.basename(zip_entry.name)}.rels"
53
+ relationships = document.relationships[rel_file]
54
+ unless (relationships.nil?)
55
+ relationship_resources = relationships.map { |relationship| relationship[:target] }
56
+ end
57
+
58
+ missing_resources = (required_resources - relationship_resources)
59
+ if missing_resources.empty?
60
+ l.info "All model required resources are defined in .rels relationship files."
61
+ else
62
+ missing_resources.each { |mr|
63
+ l.error :model_resource_not_in_rels, mr: mr
64
+ }
65
+ end
66
+
67
+ end
68
+
69
+ l.context 'verifying resources' do |l|
70
+ resources = model_doc.root.css("resources")
71
+ if resources
72
+ ids = resources.children.map { |child| child.attributes['id'].to_s if child.attributes['id'] }
73
+ l.error :resource_id_collision if ids.uniq.size != ids.size
74
+ pids = resources.children.map { |child| child.attributes['pid'].to_s }
75
+ missing_pids = pids.select { |pid| !pid.empty? and !ids.include? pid }
76
+ missing_pids.each do |p|
77
+ l.error :resource_pid_missing, pid: p
78
+ end
79
+ end
80
+ end
81
+
82
+ l.context "verifying build items" do |l|
83
+
84
+ 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?
85
+
86
+ end
87
+
88
+ l.context "checking metadata" do |l|
89
+ metadata_names = model_doc.root.css("metadata").map { |met| met['name'] }
90
+ l.error :metadata_elements_with_same_name unless metadata_names.uniq!.nil?
91
+
92
+ unless (metadata_names - VALID_CORE_METADATA_NAMES).empty?
93
+ extra_names = metadata_names - VALID_CORE_METADATA_NAMES
94
+ ns_names = extra_names.select { |n| n.include? ':' }
95
+
96
+ l.error :invalid_metadata_under_defaultns unless (extra_names - ns_names).empty?
97
+
98
+ unless ns_names.empty?
99
+ prefixes = model_doc.root.namespace_definitions.map { |defs| defs.prefix }.reject { |pre| pre.nil? }
100
+ l.error :invalid_metadata_name unless (ns_names.collect { |i| i.split(':').first } - prefixes).empty?
101
+ end
102
+ end
103
+ end
104
+
105
+ includes_material = model_doc.namespaces.values.include?(MATERIAL_EXTENSION)
106
+ MeshAnalyzer.validate(model_doc, includes_material)
107
+
108
+ l.context "verifying triangle normal" do |l|
109
+ model_doc.css('model/resources/object').select { |object| ['model', 'solidsupport', ''].include?(object.attributes['type'].to_s) }.each do |object|
110
+ meshes = object.css('mesh')
111
+ meshes.each do |mesh|
112
+ processor = MeshNormalAnalyzer.new(mesh)
113
+ l.error :inward_facing_normal if processor.found_inward_triangle
114
+ end
115
+ end
116
+ end
117
+ end
118
+ model_doc
119
+ end
120
+ 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
@@ -1,21 +1,21 @@
1
- require 'mini_magick'
2
-
3
- class Thumbnail3mf
4
-
5
- def self.parse(doc, relationship_file)
6
- Log3mf.context "Thumbnail3mf" do |l|
7
-
8
- img_type = MimeMagic.by_magic(relationship_file.get_input_stream)
9
- l.fatal_error :invalid_thumbnail_file unless img_type
10
- l.error(:invalid_thumbnail_file_type, type: img_type) unless ['image/png', 'image/jpeg'].include? img_type.type
11
-
12
- img_colorspace = MiniMagick::Image.read(relationship_file.get_input_stream).colorspace
13
- l.fatal_error :invalid_thumbnail_colorspace if img_colorspace.include? "CMYK"
14
-
15
- if relationship_file.respond_to?(:name)
16
- decl_type = doc.types.get_type(relationship_file.name)
17
- l.error :thumbnail_image_type_mismatch if decl_type != img_type.to_s
18
- end
19
- end
20
- end
21
- end
1
+ require 'mini_magick'
2
+
3
+ class Thumbnail3mf
4
+
5
+ def self.parse(doc, relationship_file)
6
+ Log3mf.context "Thumbnail3mf" do |l|
7
+
8
+ img_type = MimeMagic.by_magic(relationship_file.get_input_stream)
9
+ l.fatal_error :invalid_thumbnail_file unless img_type
10
+ l.error(:invalid_thumbnail_file_type, type: img_type) unless ['image/png', 'image/jpeg'].include? img_type.type
11
+
12
+ img_colorspace = MiniMagick::Image.read(relationship_file.get_input_stream).colorspace
13
+ l.fatal_error :invalid_thumbnail_colorspace if img_colorspace.include? "CMYK"
14
+
15
+ if relationship_file.respond_to?(:name)
16
+ decl_type = doc.types.get_type(relationship_file.name)
17
+ l.error :thumbnail_image_type_mismatch if decl_type != img_type.to_s
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,3 +1,3 @@
1
- module Ruby3mf
2
- VERSION = "0.2.6"
3
- end
1
+ module Ruby3mf
2
+ VERSION = "0.2.7"
3
+ end