ruby3mf 0.1.13 → 0.1.15

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