ruby3mf 0.1.13 → 0.1.15

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 53cf0043b4bcebef905de98f2137ad6db1b36fc4
4
- data.tar.gz: 3a970b15e198109bf6638f7206569e0b90bc51fc
3
+ metadata.gz: 49c3ed54c8588a2979b63822dbab8f3e339b5207
4
+ data.tar.gz: 50e088646a7159cf293fb896ce6ce7a5284b1131
5
5
  SHA512:
6
- metadata.gz: 91a20f9e6da5bf9f89b582509764e0ef3cf0a320c798c0020fd5fc4fd6e9e1a931193005549c0777e30b19292fcb33b7b044fee6ff70ba6ec8b36fcd0411e7e7
7
- data.tar.gz: a2203b11cb4f4ecdd9ec342ae73ae5fa2b59e46d58474671ff2123e7dd88c3438c85d7cfe54bfb3c59ac024f501dfd8ed6a818a43647dc02d9bb3866ca137095
6
+ metadata.gz: 9ecf6eaf61cbb478408212ef0741133b8c1d88d6891eaa6b330dc3102c752572da8b13f664f35067d5a167fbc25bc0333d07885a8dfb94ea29c6421a0f06e085
7
+ data.tar.gz: f220c8fc02162b03dbfbb2fa66b4e380f19e7a684ca0d59a5ab8cba6342333b00c35060802b9a2758f7109ff823792f62847e9b61ac6f606049ccd5c222db9fc
data/lib/ruby3mf.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require_relative "ruby3mf/version"
2
+ require_relative "ruby3mf/interpolation"
2
3
  require_relative "ruby3mf/log3mf"
3
4
  require_relative "ruby3mf/document"
4
5
  require_relative "ruby3mf/content_types"
@@ -8,11 +8,18 @@ class Document
8
8
  attr_accessor :objects
9
9
  attr_accessor :zip_filename
10
10
 
11
+ # Relationship schemas
12
+ MODEL_TYPE = 'http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel'
13
+ THUMBNAIL_TYPE = 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail'
14
+ TEXTURE_TYPE = 'http://schemas.microsoft.com/3dmanufacturing/2013/01/3dtexture'
15
+ PRINT_TICKET_TYPE = 'http://schemas.microsoft.com/3dmanufacturing/2013/01/printticket'
16
+
11
17
  # Relationship Type => Class validating relationship type
12
18
  RELATIONSHIP_TYPES = {
13
- 'http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel' => {klass: 'Model3mf', collection: :models},
14
- 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail' => {klass: 'Thumbnail3mf', collection: :thumbnails},
15
- 'http://schemas.microsoft.com/3dmanufacturing/2013/01/3dtexture' => {klass: 'Texture3mf', collection: :textures}
19
+ MODEL_TYPE => {klass: 'Model3mf', collection: :models},
20
+ THUMBNAIL_TYPE => {klass: 'Thumbnail3mf', collection: :thumbnails},
21
+ TEXTURE_TYPE => {klass: 'Texture3mf', collection: :textures},
22
+ PRINT_TICKET_TYPE => {}
16
23
  }
17
24
 
18
25
  def initialize(zip_filename)
@@ -85,6 +92,11 @@ class Document
85
92
  end
86
93
  end
87
94
 
95
+ l.context "print tickets" do |l|
96
+ print_ticket_types = m.relationships.select { |rel| rel[:type] == PRINT_TICKET_TYPE }
97
+ l.error :multiple_print_tickets if print_ticket_types.size > 1
98
+ end
99
+
88
100
  l.context "relationship elements" do |l|
89
101
  # 3. Validate all relationships
90
102
  m.relationships.each do |rel|
@@ -107,13 +119,15 @@ class Document
107
119
  if relationship_file
108
120
  relationship_type = RELATIONSHIP_TYPES[rel[:type]]
109
121
  if relationship_type.nil?
110
- l.warning "Relationship file defines a type that is not used in a normal 3mf file: #{rel[:type]}. Ignoring relationship."
122
+ l.error :invalid_relationship_type, type: rel[:type]
111
123
  else
112
- m.send(relationship_type[:collection]) << {
113
- rel_id: rel[:id],
114
- target: rel[:target],
115
- object: Object.const_get(relationship_type[:klass]).parse(m, relationship_file)
116
- }
124
+ unless relationship_type[:klass].nil?
125
+ m.send(relationship_type[:collection]) << {
126
+ rel_id: rel[:id],
127
+ target: rel[:target],
128
+ object: Object.const_get(relationship_type[:klass]).parse(m, relationship_file)
129
+ }
130
+ end
117
131
  end
118
132
  else
119
133
  l.error "Relationship Target file #{rel[:target]} not found", page: 11
@@ -1,5 +1,5 @@
1
1
  resource_contentype_invalid:
2
- msg: "resource in model has invalid contenttype %{bt}"
2
+ msg: "Resource in model has invalid contenttype %{bt}"
3
3
  page: 10
4
4
  err_uri_empty_segment:
5
5
  msg: 'No segment of a 3MF part name path may be empty'
@@ -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_relationship_type:
53
+ 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
+ page: 10
52
55
  invalid_thumbnail_colorspace:
53
56
  msg: "CMYK JPEG images must not be used for the thumbnail"
54
57
  page: 36
@@ -79,18 +82,39 @@ not_a_zip:
79
82
  zero_size_texture:
80
83
  msg: "Texture file must be valid image file"
81
84
  page: 16
82
- resource_contentype_invalid:
83
- msg: "resource in model has invalid contenttype image/ping"
84
- page: 10
85
85
  has_xml_space_attribute:
86
- msg: "found an xml:space attribute when it is not allowed"
86
+ msg: "xml:space attribute is not allowed"
87
87
  page: 16
88
+ invalid_model_unit:
89
+ msg: "Invalid unit value of '%{unit}' specified in model file."
90
+ page: 17
88
91
  wrong_encoding:
89
- msg: "found XML content that was not UTF8 encoded"
92
+ msg: "XML content must be UTF8 encoded"
90
93
  page: 15
91
94
  missing_object_pid:
92
- msg: "object with triangle color override missing object level pid"
95
+ msg: "Object with triangle color override missing object level pid"
93
96
  page: 26
94
97
  missing_model_children:
95
- msg: "model element must include resources and build as child elements"
98
+ msg: "Model element must include resources and build as child elements"
96
99
  page: 20
100
+ object_with_components_and_pid:
101
+ msg: "object with components and pid or pindex"
102
+ page: 26
103
+ build_with_other_item:
104
+ msg: "build item with object of type other"
105
+ page: 27
106
+ resource_id_collision:
107
+ msg: "resources must be unique within the model"
108
+ page: 22
109
+ metadata_elements_with_same_name:
110
+ msg: "metadata elements must not share the same name"
111
+ page: 22
112
+ multiple_print_tickets:
113
+ msg: "Only one print ticket allowed for any given model"
114
+ page: 13
115
+ unknown_required_extension:
116
+ msg: "Required extension not supported: %{ext}"
117
+ page: 14
118
+ missing_extension_namespace_uri:
119
+ msg: "Required extension '%{ns}' MUST refer to namespace with URI"
120
+ page: 14
@@ -0,0 +1,43 @@
1
+ module Interpolation
2
+
3
+ INTERPOLATION_PATTERN = Regexp.union(
4
+ /%%/,
5
+ /%\{(\w+)\}/, # matches placeholders like "%{foo}"
6
+ /%<(\w+)>(.*?\d*\.?\d*[bBdiouxXeEfgGcps])/ # matches placeholders like "%<foo>.d"
7
+ )
8
+
9
+ def interpolate(string, values = {})
10
+ if values.keys == 0
11
+ string
12
+ else
13
+ string.gsub(INTERPOLATION_PATTERN) do |match|
14
+ if match == '%%'
15
+ '%'
16
+ else
17
+ key = ($1 || $2 || match.tr("%{}", "")).to_sym
18
+ value = values[key]
19
+ value = value.call(values) if value.respond_to?(:call)
20
+ $3 ? sprintf("%#{$3}", value) : value
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ def symbolize_recursive(hash)
27
+ {}.tap do |h|
28
+ hash.each { |key, value| h[key.to_sym] = map_value(value) }
29
+ end
30
+ end
31
+
32
+ def map_value(thing)
33
+ case thing
34
+ when Hash
35
+ symbolize_recursive(thing)
36
+ when Array
37
+ thing.map { |v| map_value(v) }
38
+ else
39
+ thing
40
+ end
41
+ end
42
+
43
+ end
@@ -23,6 +23,7 @@ require 'yaml'
23
23
 
24
24
  class Log3mf
25
25
  include Singleton
26
+ include Interpolation
26
27
 
27
28
  LOG_LEVELS = [:fatal_error, :error, :warning, :info, :debug]
28
29
 
@@ -63,28 +64,20 @@ class Log3mf
63
64
 
64
65
  def method_missing(name, *args, &block)
65
66
  if LOG_LEVELS.include? name.to_sym
66
- #puts "***** #{name} called from #{caller[0]}"
67
67
  log(name.to_sym, *args)
68
68
  else
69
69
  super
70
70
  end
71
71
  end
72
72
 
73
- def log(severity, message, options={})
74
- if message.is_a?(Symbol)
75
- new_log(severity, message, options)
76
- else
77
- @log_list << ["#{@context_stack.join("/")}", severity, message, options] unless severity==:debug && ENV['LOGDEBUG'].nil?
78
- # puts "[#{@context_stack.join("/")}] #{severity.to_s.upcase} #{message}"
79
- end
73
+ def log(severity, message, options = {})
74
+ error = @errormap.fetch(message.to_s) { {"msg" => message.to_s, "page" => nil } }
75
+ options[:page] = error["page"] unless options[:page]
76
+ message = interpolate(error["msg"], options)
77
+ @log_list << ["#{@context_stack.join("/")}", severity, message, options] unless severity==:debug && ENV['LOGDEBUG'].nil?
80
78
  raise FatalError if severity == :fatal_error
81
79
  end
82
80
 
83
- def new_log(severity, message, options={})
84
- error_info = @errormap.fetch(message.to_s)
85
- @log_list << ["#{@context_stack.join("/")}", severity, error_info["msg"], page: error_info["page"]] unless severity==:debug && ENV['LOGDEBUG'].nil?
86
- end
87
-
88
81
  def count_entries(*levels)
89
82
  entries(*levels).count
90
83
  end
@@ -15,6 +15,12 @@ end
15
15
  class MeshAnalyzer
16
16
 
17
17
  def self.validate_object(object)
18
+ Log3mf.context "verifying object" do |l|
19
+ children = object.children.map { |child| child.name }
20
+ have_override = object.attributes["pid"] or object.attributes["pindex"]
21
+ l.error :object_with_components_and_pid if have_override && children.include?("components")
22
+ end
23
+
18
24
  Log3mf.context "validating geometry" do |l|
19
25
  list = EdgeList.new
20
26
 
@@ -2,6 +2,13 @@ require_relative 'mesh_analyzer'
2
2
 
3
3
  class Model3mf
4
4
 
5
+ VALID_UNITS = [ 'micron', 'millimeter', 'centimeter', 'meter', 'inch', 'foot' ].freeze
6
+ VALID_EXTENSIONS = {
7
+ 'http://schemas.microsoft.com/3dmanufacturing/slice/2015/07' => {},
8
+ 'http://schemas.microsoft.com/3dmanufacturing/material/2015/02' => {},
9
+ 'http://schemas.microsoft.com/3dmanufacturing/production/2015/06' => {},
10
+ }.freeze
11
+
5
12
  def self.parse(document, zip_entry)
6
13
  model_doc = nil
7
14
 
@@ -12,6 +19,24 @@ class Model3mf
12
19
  l.fatal_error "Model file invalid XML. Exception #{e}"
13
20
  end
14
21
 
22
+ l.context "verifying requiredextensions" do |l|
23
+ required_extensions = model_doc.css("//model")[0]["requiredextensions"]
24
+ if required_extensions
25
+ required_extensions.split(" ").each do |ns|
26
+ namespace_uri = model_doc.namespaces["xmlns:#{ns}"]
27
+ if namespace_uri
28
+ if VALID_EXTENSIONS.has_key? namespace_uri
29
+ l.info "Found a valid required extension: #{namespace_uri}"
30
+ else
31
+ l.error :unknown_required_extension, ext: namespace_uri
32
+ end
33
+ else
34
+ l.error :missing_extension_namespace_uri, ns: ns
35
+ end
36
+ end
37
+ end
38
+ end
39
+
15
40
  l.context "verifying 3D payload required resources" do |l|
16
41
  # results = model_doc.css("model resources m:texture2d")
17
42
  required_resources = model_doc.css("//model//resources//*[path]").collect { |n| n["path"] }
@@ -46,14 +71,45 @@ class Model3mf
46
71
 
47
72
  end
48
73
 
74
+ l.context "verifying resources" do |l|
75
+ resources = find_child(model_doc.root, "resources")
76
+ if resources
77
+ ids = resources.children.map { |child| child.attributes["id"].to_s() if child.attributes["id"] }
78
+ l.error :resource_id_collision if ids.uniq.size != ids.size
79
+ end
80
+ end
81
+
49
82
  l.context "verifying model structure" do |l|
50
83
  root = model_doc.root
51
84
  l.error :root_3dmodel_element_not_model if root.name != "model"
52
85
 
86
+ l.error(:invalid_model_unit, unit: root.attr('unit')) unless VALID_UNITS.include?(root.attr('unit'))
87
+
53
88
  children = model_doc.root.children.map { |child| child.name }
54
89
  l.error :missing_model_children unless children.include?("resources") && children.include?("build")
55
90
  end
56
91
 
92
+ l.context "verifying build items" do |l|
93
+ build = find_child(model_doc.root, "build")
94
+ if build
95
+ items = build.children.map { |child| child.attributes["objectid"].to_s() if child.name == "item" }
96
+
97
+ resources = find_child(model_doc.root, "resources")
98
+ resources.children.each do |resource|
99
+ if resource.name == "object"
100
+ object_id = resource.attributes["id"].to_s()
101
+ l.error :build_with_other_item if resource.attributes["type"].to_s() == "other" and items.include?(object_id)
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ l.context "checking metadata" do |l|
108
+ metadata = model_doc.root.css("metadata")
109
+ metadata_names = metadata.map { |met| met['name'] }
110
+ l.error :metadata_elements_with_same_name unless metadata_names.uniq!.nil?
111
+ end
112
+
57
113
  MeshAnalyzer.validate(model_doc)
58
114
  end
59
115
  model_doc
@@ -1,3 +1,3 @@
1
1
  module Ruby3mf
2
- VERSION = "0.1.13"
2
+ VERSION = "0.1.15"
3
3
  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.1.13
4
+ version: 0.1.15
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-07 00:00:00.000000000 Z
11
+ date: 2017-01-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -150,6 +150,7 @@ files:
150
150
  - lib/ruby3mf/edge_list.rb
151
151
  - lib/ruby3mf/errors.yml
152
152
  - lib/ruby3mf/global_xml_validations.rb
153
+ - lib/ruby3mf/interpolation.rb
153
154
  - lib/ruby3mf/log3mf.rb
154
155
  - lib/ruby3mf/mesh_analyzer.rb
155
156
  - lib/ruby3mf/model3mf.rb