ruby3mf 0.1.11 → 0.1.13

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: 8b924df1af6b462ddf619c0cd9d1d4d21435275d
4
- data.tar.gz: 960b2be3c49a86a51adf0ba7882434e74a7317a8
3
+ metadata.gz: 53cf0043b4bcebef905de98f2137ad6db1b36fc4
4
+ data.tar.gz: 3a970b15e198109bf6638f7206569e0b90bc51fc
5
5
  SHA512:
6
- metadata.gz: 99e441a31af2a9a92392da4afc48469e7f286f97bf93d0cbfbae12ca6c5445c3e2b0276415aa85bf57285fec12650a18a12e1c25e07970bbe75ca9e9e08027aa
7
- data.tar.gz: 95fb5fcca5571bb3925fb20318a088cba5d7e8fa7969fcd3ef00156cb3fd1903fd1ae2174281f4cc12e678ef43ae252dc5c761230bf12da95c7aa0f33d29fd63
6
+ metadata.gz: 91a20f9e6da5bf9f89b582509764e0ef3cf0a320c798c0020fd5fc4fd6e9e1a931193005549c0777e30b19292fcb33b7b044fee6ff70ba6ec8b36fcd0411e7e7
7
+ data.tar.gz: a2203b11cb4f4ecdd9ec342ae73ae5fa2b59e46d58474671ff2123e7dd88c3438c85d7cfe54bfb3c59ac024f501dfd8ed6a818a43647dc02d9bb3866ca137095
data/bin/cli.rb CHANGED
@@ -3,3 +3,10 @@
3
3
  require_relative '../lib/ruby3mf'
4
4
 
5
5
  doc = Document.read(ARGV.first)
6
+
7
+ errors = Log3mf.entries(:error, :fatal_error)
8
+ puts "Validating file: #{ARGV.first}"
9
+ errors.each do |ent|
10
+ h = { context: ent[0], severity: ent[1], message: ent[2] }
11
+ puts h
12
+ end
data/lib/ruby3mf.rb CHANGED
@@ -6,14 +6,15 @@ require_relative "ruby3mf/model3mf"
6
6
  require_relative "ruby3mf/relationships"
7
7
  require_relative "ruby3mf/thumbnail3mf"
8
8
  require_relative "ruby3mf/texture3mf"
9
+ require_relative "ruby3mf/global_xml_validations"
9
10
 
10
11
  require 'zip'
11
12
  require 'nokogiri'
12
13
  require 'json'
13
14
  require 'mimemagic'
14
- # require 'csv'
15
+ require 'uri'
16
+ require 'yaml'
15
17
 
16
18
  module Ruby3mf
17
-
18
19
  # Your code goes here...
19
20
  end
@@ -6,9 +6,7 @@ class ContentTypes
6
6
  Log3mf.context "parse" do |l|
7
7
  begin
8
8
 
9
- doc = Nokogiri::XML(zip_entry.get_input_stream) do |config|
10
- config.strict.nonet.noblanks
11
- end
9
+ doc = GlobalXMLValidations.validate_parse(zip_entry)
12
10
 
13
11
  l.warning '[Content_Types].xml must contain exactly one root node' unless doc.children.size == 1
14
12
  l.warning '[Content_Types].xml must contain root name Types' unless doc.children.first.name == "Types"
@@ -28,7 +28,6 @@ class Document
28
28
  def self.read(input_file)
29
29
 
30
30
  m = new(input_file)
31
-
32
31
  begin
33
32
  Log3mf.context "zip" do |l|
34
33
  begin
@@ -43,6 +42,25 @@ class Document
43
42
 
44
43
  l.info "Zip file is valid"
45
44
 
45
+ # check for valid, absolute URI's for each path name
46
+
47
+ zip_file.each do |part|
48
+ l.context "part names /#{part.name}" do |l|
49
+ unless part.name.end_with? '[Content_Types].xml'
50
+ begin
51
+ u = URI part.name
52
+ rescue ArgumentError
53
+ l.error :err_uri_bad
54
+ next
55
+ end
56
+
57
+ u.path.split('/').each do |segment|
58
+ l.error :err_uri_hidden_file if (segment.start_with? '.') && !(segment.end_with? '.rels')
59
+ end
60
+ end
61
+ end
62
+ end
63
+
46
64
  l.context "content types" do |l|
47
65
  # 1. Get Content Types
48
66
  content_type_match = zip_file.glob('\[Content_Types\].xml').first
@@ -71,7 +89,19 @@ class Document
71
89
  # 3. Validate all relationships
72
90
  m.relationships.each do |rel|
73
91
  l.context rel[:target] do |l|
92
+
93
+ begin
94
+ u = URI rel[:target]
95
+ rescue URI::InvalidURIError
96
+ l.error :err_uri_bad
97
+ next
98
+ end
99
+
100
+ l.error :err_uri_relative_path unless u.to_s.start_with? '/'
101
+
74
102
  target = rel[:target].gsub(/^\//, "")
103
+ l.error :err_uri_empty_segment if target.end_with? '/' or target.include? '//'
104
+ l.error :err_uri_relative_path if target.include? '/../'
75
105
  relationship_file = zip_file.glob(target).first
76
106
 
77
107
  if relationship_file
@@ -92,6 +122,7 @@ class Document
92
122
  end
93
123
  end
94
124
  end
125
+
95
126
  return m
96
127
  rescue Zip::Error
97
128
  l.fatal_error 'File provided is not a valid ZIP archive', page: 9
@@ -123,7 +154,7 @@ class Document
123
154
  end
124
155
  end
125
156
 
126
- File.open(output_file, "wb") {|f| f.write(buffer.string) }
157
+ File.open(output_file, "wb") { |f| f.write(buffer.string) }
127
158
 
128
159
  end
129
160
 
@@ -0,0 +1,61 @@
1
+ class EdgeList
2
+
3
+ def initialize()
4
+ @edges = { }
5
+ end
6
+
7
+ def add_edge(first_vertex, second_vertex)
8
+ if first_vertex < second_vertex
9
+ edge = "#{first_vertex}:#{second_vertex}"
10
+ else
11
+ edge = "#{second_vertex}:#{first_vertex}"
12
+ end
13
+
14
+ (pos_count, neg_count) = @edges[edge]
15
+
16
+ if pos_count == nil or neg_count == nil
17
+ pos_count = 0
18
+ neg_count = 0
19
+ end
20
+
21
+ pos_count += 1 if first_vertex < second_vertex
22
+ neg_count += 1 if second_vertex < first_vertex
23
+
24
+ @edges[edge] = [pos_count, neg_count]
25
+ end
26
+
27
+ def edge_count(first_vertex, second_vertex)
28
+ if first_vertex < second_vertex
29
+ edge = "#{first_vertex}:#{second_vertex}"
30
+ else
31
+ edge = "#{second_vertex}:#{first_vertex}"
32
+ end
33
+
34
+ @edges[edge]
35
+ end
36
+
37
+ def print_list()
38
+ @edges.each do |key, value|
39
+ (pos, neg) = value
40
+ puts "#{pos} : #{neg}"
41
+ end
42
+ end
43
+
44
+ def verify_edges()
45
+ @edges.each do |key, value|
46
+ (pos, neg) = value
47
+
48
+ if (pos > 1 and neg == 0) or (pos == 0 and neg > 1)
49
+ return :bad_orientation
50
+ elsif pos + neg == 1
51
+ return :hole
52
+ elsif pos != 1 or neg != 1
53
+ return :nonmanifold
54
+ end
55
+ end
56
+
57
+ :ok
58
+ end
59
+
60
+ end
61
+
@@ -0,0 +1,96 @@
1
+ resource_contentype_invalid:
2
+ msg: "resource in model has invalid contenttype %{bt}"
3
+ page: 10
4
+ err_uri_empty_segment:
5
+ msg: 'No segment of a 3MF part name path may be empty'
6
+ page: 13
7
+ err_uri_hidden_file:
8
+ msg: "Other than /_rels/.rels, no segment of a 3MF part name may start with the '.' character"
9
+ page: 13
10
+ err_uri_bad:
11
+ msg: 'Path names must be valid Open Package Convention URIs or IRIs'
12
+ page: 13
13
+ err_uri_relative_path:
14
+ msg: 'Part names must not include relative paths'
15
+ page: 13
16
+ model_resource_not_in_rels:
17
+ msg: "Missing required resource: %{mr} Resource referenced in model, but not in .rels relationship file"
18
+ page: 10
19
+ resource_3dmodel_orientation:
20
+ msg: "Bad triangle orientation"
21
+ page: 27
22
+ resource_3dmodel_hole:
23
+ msg: "Hole in model"
24
+ page: 27
25
+ resource_3dmodel_nonmanifold:
26
+ msg: "Non-manifold edge in 3dmodel"
27
+ page: 27
28
+ 3d_model_invalid_xml:
29
+ msg: "Model file invalid XML. Exception Start tag expected, '<' not found"
30
+ page:
31
+ 3d_payload_files:
32
+ msg: "Relationship Target file /3D/Texture/texture2.texture not found"
33
+ page: 11
34
+ content_types_invalid_xml:
35
+ msg: "[Content_Types].xml file is not valid XML. Start tag expected, '<' not found"
36
+ page: 15
37
+ dot_rels_file_has_invalid_xml:
38
+ msg: "Relationships (.rel) file is not a valid XML file: Start tag expected, '<' not found"
39
+ page: 4
40
+ dot_rels_file_missing_relationships_element:
41
+ msg: ".rels XML must have &lt;Relationships&gt; root element"
42
+ page: 4
43
+ dot_rels_file_no_relationship_element:
44
+ msg: "No relationship elements found"
45
+ page: 4
46
+ invalid_content_type:
47
+ msg: "[Content_Types].xml is missing required ContentType \"application/vnd.openxmlformats-package.relationships+xml\""
48
+ page: 10
49
+ invalid_startpart_type:
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
+ page: 10
52
+ invalid_thumbnail_colorspace:
53
+ msg: "CMYK JPEG images must not be used for the thumbnail"
54
+ page: 36
55
+ invalid_thumbnail_file_type:
56
+ msg: "Expected a png or jpeg thumbnail but the thumbnail was of type image/gif"
57
+ page: 36
58
+ invalid_texture_file_type:
59
+ msg: "Expected a png or jpeg texture but the texture was of type image/gif"
60
+ page: 16
61
+ missing_content_types:
62
+ msg: "Missing required file: [Content_Types].xml"
63
+ page: 4
64
+ missing_dot_rels_file:
65
+ msg: "Missing required file _rels/.rels"
66
+ page: 4
67
+ missing_rels_folder:
68
+ msg: "Missing required file _rels/.rels"
69
+ page: 4
70
+ multiple_relationships:
71
+ msg: "There MUST NOT be more than one relationship of a given relationship type from one part to a second part"
72
+ page: 10
73
+ no_3d_model:
74
+ msg: "Relationship Target file /3D/3dmodel.model not found"
75
+ page: 11
76
+ not_a_zip:
77
+ msg: "File provided is not a valid ZIP archive"
78
+ page: 9
79
+ zero_size_texture:
80
+ msg: "Texture file must be valid image file"
81
+ page: 16
82
+ resource_contentype_invalid:
83
+ msg: "resource in model has invalid contenttype image/ping"
84
+ page: 10
85
+ has_xml_space_attribute:
86
+ msg: "found an xml:space attribute when it is not allowed"
87
+ page: 16
88
+ wrong_encoding:
89
+ msg: "found XML content that was not UTF8 encoded"
90
+ page: 15
91
+ missing_object_pid:
92
+ msg: "object with triangle color override missing object level pid"
93
+ page: 26
94
+ missing_model_children:
95
+ msg: "model element must include resources and build as child elements"
96
+ page: 20
@@ -0,0 +1,36 @@
1
+ class GlobalXMLValidations
2
+
3
+ def self.validate_parse(file)
4
+ doc = Nokogiri::XML(file.get_input_stream) do |config|
5
+ config.strict.nonet.noblanks
6
+ end
7
+ validate(file, doc)
8
+ doc
9
+ end
10
+
11
+ def self.validate(file, document)
12
+ Log3mf.context "global xml validations" do |l|
13
+ l.error :has_xml_space_attribute if space_attribute_exists?(document)
14
+ l.error :wrong_encoding if xml_not_utf8_encoded?(document)
15
+ l.error :dtd_not_allowed if dtd_exists?(file)
16
+ end
17
+ end
18
+
19
+ def self.space_attribute_exists?(document)
20
+ !(document.xpath('//*[@xml:space]').empty?)
21
+ end
22
+
23
+ def self.xml_not_utf8_encoded?(document)
24
+ !(document.encoding == 'UTF-8' || document.encoding == 'utf-8')
25
+ end
26
+
27
+ def self.dtd_exists?(file)
28
+ found = file.get_input_stream.find { |line| line =~ /(!DOCTYPE\b)|(!ELEMENT\b)|(!ENTITY\b)|(!NOTATION\b)|(!ATTLIST\b)/ }
29
+ !found.nil?
30
+ end
31
+ end
32
+
33
+
34
+
35
+
36
+
@@ -1,4 +1,5 @@
1
1
  require 'singleton'
2
+ require 'yaml'
2
3
 
3
4
  # Example usage:
4
5
 
@@ -33,6 +34,8 @@ class Log3mf
33
34
  @log_list = []
34
35
  @context_stack = []
35
36
  @ledger = []
37
+ errormap_path = File.join(File.dirname(__FILE__),"errors.yml")
38
+ @errormap = YAML.load_file(errormap_path)
36
39
  end
37
40
 
38
41
  def reset_log
@@ -68,11 +71,20 @@ class Log3mf
68
71
  end
69
72
 
70
73
  def log(severity, message, options={})
71
- @log_list << ["#{@context_stack.join("/")}", severity, message, options] unless severity==:debug && ENV['LOGDEBUG'].nil?
72
- # puts "[#{@context_stack.join("/")}] #{severity.to_s.upcase} #{message}"
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
80
  raise FatalError if severity == :fatal_error
74
81
  end
75
82
 
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
+
76
88
  def count_entries(*levels)
77
89
  entries(*levels).count
78
90
  end
@@ -120,4 +132,3 @@ class Log3mf
120
132
  Log3mf.instance.to_json
121
133
  end
122
134
  end
123
-
@@ -0,0 +1,78 @@
1
+ require_relative 'edge_list'
2
+
3
+
4
+ def find_child(node, child_name)
5
+ node.children.each do |child|
6
+ if child.name == child_name
7
+ return child
8
+ end
9
+ end
10
+
11
+ nil
12
+ end
13
+
14
+
15
+ class MeshAnalyzer
16
+
17
+ def self.validate_object(object)
18
+ Log3mf.context "validating geometry" do |l|
19
+ list = EdgeList.new
20
+
21
+ # if a triangle has a pid, then the object needs a pid
22
+ has_triangle_pid = false
23
+
24
+ mesh = find_child(object, "mesh")
25
+ if mesh
26
+ triangles = find_child(mesh, "triangles")
27
+
28
+ if triangles
29
+ triangles.children.each do |triangle|
30
+ v1 = triangle.attributes["v1"].to_s().to_i()
31
+ v2 = triangle.attributes["v2"].to_s().to_i()
32
+ v3 = triangle.attributes["v3"].to_s().to_i()
33
+
34
+ list.add_edge(v1, v2)
35
+ list.add_edge(v2, v3)
36
+ list.add_edge(v3, v1)
37
+
38
+ if not has_triangle_pid
39
+ has_triangle_pid = triangle.attributes["pid"] != nil
40
+ end
41
+ end
42
+
43
+ has_object_material = object.attributes["pid"] and object.attributes["pindex"]
44
+ if has_triangle_pid and not has_object_material
45
+ l.error :missing_object_pid
46
+ end
47
+
48
+ result = list.verify_edges()
49
+ if result == :bad_orientation
50
+ l.fatal_error :resource_3dmodel_orientation
51
+ elsif result == :hole
52
+ l.fatal_error :resource_3dmodel_hole
53
+ elsif result == :nonmanifold
54
+ l.fatal_error :resource_3dmodel_nonmanifold
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ def self.validate(model_doc)
62
+ root = model_doc.root
63
+ node = root
64
+
65
+ if node.name == "model"
66
+ resources = find_child(node, "resources")
67
+
68
+ if resources
69
+ resources.children.each do |resource|
70
+ solid_model = resource.attributes["type"].to_s() == "model" or resource.attributes["type"].to_s() == "solidsupport"
71
+ if resource.name == "object" and solid_model
72
+ validate_object(resource)
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -1,3 +1,5 @@
1
+ require_relative 'mesh_analyzer'
2
+
1
3
  class Model3mf
2
4
 
3
5
  def self.parse(document, zip_entry)
@@ -5,10 +7,7 @@ class Model3mf
5
7
 
6
8
  Log3mf.context "parsing model" do |l|
7
9
  begin
8
- model_doc = Nokogiri::XML(zip_entry.get_input_stream) do |config|
9
- config.strict.nonet.noblanks
10
- end
11
- l.info "We Found a Model, and it's XML!"
10
+ model_doc = GlobalXMLValidations.validate_parse(zip_entry)
12
11
  rescue Nokogiri::XML::SyntaxError => e
13
12
  l.fatal_error "Model file invalid XML. Exception #{e}"
14
13
  end
@@ -25,7 +24,7 @@ class Model3mf
25
24
  l.info "All model required resources are defined in .rels relationship files."
26
25
  else
27
26
  missing_resources.each { |mr|
28
- l.error "Missing required resource: #{mr} Resource referenced in model, but not in .rels relationship file", page: 10
27
+ l.error :model_resource_not_in_rels, mr: mr
29
28
  }
30
29
  end
31
30
 
@@ -40,13 +39,22 @@ class Model3mf
40
39
  l.info "All model resource contenttypes are valid"
41
40
  else
42
41
  bad_types.each { |bt|
43
- puts "bad type: #{bt}"
44
- l.error "resource in model has invalid contenttype #{bt}", page: 10
42
+ l.error :resource_contentype_invalid, bt: bt
45
43
  }
46
44
  end
47
45
  end
48
46
 
49
47
  end
48
+
49
+ l.context "verifying model structure" do |l|
50
+ root = model_doc.root
51
+ l.error :root_3dmodel_element_not_model if root.name != "model"
52
+
53
+ children = model_doc.root.children.map { |child| child.name }
54
+ l.error :missing_model_children unless children.include?("resources") && children.include?("build")
55
+ end
56
+
57
+ MeshAnalyzer.validate(model_doc)
50
58
  end
51
59
  model_doc
52
60
  end
@@ -5,9 +5,7 @@ class Relationships
5
5
  Log3mf.context "parsing relationships" do |l|
6
6
  begin
7
7
  # Parse Relationships XML
8
- doc = Nokogiri::XML(zip_entry.get_input_stream) do |config|
9
- config.strict.nonet.noblanks
10
- end
8
+ doc = GlobalXMLValidations.validate_parse(zip_entry)
11
9
 
12
10
  # Verify <Relationships><Relationship/></Relationships>
13
11
  root_element = doc.children[0]
@@ -16,6 +14,11 @@ class Relationships
16
14
  if relationship_elements.size > 0
17
15
  relationship_elements.each do |node|
18
16
  if node.is_a?(Nokogiri::XML::Element) && node.name == "Relationship"
17
+ relationships.each do |previous_rel|
18
+ if previous_rel[:target] == node['Target'] && previous_rel[:type] == node['Type']
19
+ l.error :multiple_relationships
20
+ end
21
+ end
19
22
  relationships << {target: node['Target'], type: node['Type'], id: node['Id']}
20
23
  l.info "adding relationship: #{relationships.last.inspect}"
21
24
  else
@@ -30,8 +33,7 @@ class Relationships
30
33
  start_part_type = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel"
31
34
  start_part_count = relationships.select { |r| r[:type] == start_part_type }.size
32
35
  if start_part_count != 1
33
- l.error "rels/.rels Relationship file has an invalide attribute type for the root 3D Model (StartPart).
34
- The correct attribute type should be \"#{start_part_type}\"", page: 10
36
+ l.error :invalid_startpart_type
35
37
  end
36
38
  end
37
39
  end
@@ -6,7 +6,6 @@ class Texture3mf
6
6
  end
7
7
 
8
8
  def self.parse(document, relationship_file)
9
-
10
9
  t = new(document)
11
10
  t.name = relationship_file.name
12
11
  stream = relationship_file.get_input_stream
@@ -1,12 +1,18 @@
1
+ require 'mini_magick'
2
+
1
3
  class Thumbnail3mf
2
4
 
3
5
  def self.parse(doc, relationship_file)
4
6
  img_type = MimeMagic.by_magic(relationship_file.get_input_stream)
7
+ img_colorspace = MiniMagick::Image.read(relationship_file.get_input_stream).colorspace
8
+
5
9
  Log3mf.context "Thumbnail3mf" do |l|
6
10
  l.fatal_error "thumbnail file must be valid image file", page: 10 unless img_type
11
+ l.fatal_error "CMYK JPEG images must not be used for the thumbnail", page: 36 if img_colorspace.include? "CMYK"
7
12
  l.debug "thumbnail is of type: #{img_type}"
8
13
  l.error "Expected a png or jpeg thumbnail but the thumbnail was of type #{img_type}", page: 12 unless ['image/png', 'image/jpeg'].include? img_type.type
9
14
  end
10
15
 
11
16
  end
17
+
12
18
  end
@@ -1,3 +1,3 @@
1
1
  module Ruby3mf
2
- VERSION = "0.1.11"
2
+ VERSION = "0.1.13"
3
3
  end
data/ruby3mf.gemspec CHANGED
@@ -14,14 +14,6 @@ Gem::Specification.new do |spec|
14
14
  spec.homepage = "https://github.com/IPGPTP/ruby3mf"
15
15
  spec.license = "MIT"
16
16
 
17
- # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
18
- # delete this section to allow pushing this gem to any host.
19
- # if spec.respond_to?(:metadata)
20
- # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
21
- # else
22
- # raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
- # end
24
-
25
17
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
18
  spec.bindir = "exe"
27
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
@@ -35,4 +27,5 @@ Gem::Specification.new do |spec|
35
27
  spec.add_runtime_dependency 'rubyzip'
36
28
  spec.add_runtime_dependency 'nokogiri', '~>1.6.8'
37
29
  spec.add_runtime_dependency 'mimemagic'
30
+ spec.add_runtime_dependency 'mini_magick', '~> 4.6'
38
31
  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.11
4
+ version: 0.1.13
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: 2016-12-21 00:00:00.000000000 Z
11
+ date: 2017-01-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: mini_magick
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '4.6'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '4.6'
111
125
  description: Read, write and validate 3MF files with Ruby easily.
112
126
  email:
113
127
  - mwhit@hp.com
@@ -133,7 +147,11 @@ files:
133
147
  - lib/ruby3mf.rb
134
148
  - lib/ruby3mf/content_types.rb
135
149
  - lib/ruby3mf/document.rb
150
+ - lib/ruby3mf/edge_list.rb
151
+ - lib/ruby3mf/errors.yml
152
+ - lib/ruby3mf/global_xml_validations.rb
136
153
  - lib/ruby3mf/log3mf.rb
154
+ - lib/ruby3mf/mesh_analyzer.rb
137
155
  - lib/ruby3mf/model3mf.rb
138
156
  - lib/ruby3mf/relationships.rb
139
157
  - lib/ruby3mf/texture3mf.rb