ruby3mf 0.1.15 → 0.1.17
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 +4 -4
- data/bin/batch.rb +20 -0
- data/bin/cli.rb +3 -3
- data/bin/folder_test.sh +13 -0
- data/lib/ruby3mf.rb +1 -1
- data/lib/ruby3mf/content_types.rb +1 -1
- data/lib/ruby3mf/document.rb +46 -9
- data/lib/ruby3mf/errors.yml +17 -2
- data/lib/ruby3mf/model3mf.rb +8 -1
- data/lib/ruby3mf/relationships.rb +2 -6
- data/lib/ruby3mf/thumbnail3mf.rb +7 -9
- data/lib/ruby3mf/version.rb +1 -1
- data/lib/ruby3mf/xml_val.rb +47 -0
- metadata +5 -3
- data/lib/ruby3mf/global_xml_validations.rb +0 -36
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 152e1cd32d11dadcadb62904f2942100278d059c
|
|
4
|
+
data.tar.gz: 3359551e080d311dbdbe6a8607f0336cbfedf298
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 670d9a203081c4da7d93cdb8dc81e8db53352a5bd6635511f028a29e073bc8ffb5325365f7539d9718227bd6646fb2d5b00b5c6b1cc3161dfe77528cea6ae242
|
|
7
|
+
data.tar.gz: 48bf7933386810d11e503953607545b3db168949c97123836fadec03fb10392530732b11b02b40d02c761eccac96fca9e62bb5716c2d289da3c32c46a5026bdb
|
data/bin/batch.rb
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require_relative '../lib/ruby3mf'
|
|
4
|
+
|
|
5
|
+
files = Dir["spec/ruby3mf-testfiles/#{ARGV[0] || "failing_cases"}/*.#{ARGV[1] || '3mf'}"]
|
|
6
|
+
|
|
7
|
+
files.each do |file|
|
|
8
|
+
|
|
9
|
+
Log3mf.reset_log
|
|
10
|
+
doc = Document.read(file)
|
|
11
|
+
errors = Log3mf.entries(:error, :fatal_error)
|
|
12
|
+
|
|
13
|
+
puts "=" * 100
|
|
14
|
+
puts "Validating file: #{file}"
|
|
15
|
+
errors.each do |ent|
|
|
16
|
+
h = { context: ent[0], severity: ent[1], message: ent[2] }
|
|
17
|
+
puts h
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
data/bin/cli.rb
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative '../lib/ruby3mf'
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
filename = ARGV.first
|
|
6
|
+
doc = Document.read(filename)
|
|
6
7
|
|
|
7
8
|
errors = Log3mf.entries(:error, :fatal_error)
|
|
8
|
-
puts "Validating file: #{ARGV.first}"
|
|
9
9
|
errors.each do |ent|
|
|
10
|
-
h = { context: ent[0], severity: ent[1], message: ent[2] }
|
|
10
|
+
h = { file: filename, context: ent[0], severity: ent[1], message: ent[2] }
|
|
11
11
|
puts h
|
|
12
12
|
end
|
data/bin/folder_test.sh
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
NAME=$1
|
|
4
|
+
echo "input: ${NAME}"
|
|
5
|
+
echo "Starting test on: ${NAME}"
|
|
6
|
+
echo "Starting test on: ${NAME}" >> ${NAME}_test.json
|
|
7
|
+
|
|
8
|
+
for filename in ${NAME}/*.3mf; do
|
|
9
|
+
echo "Validating ${filename}"
|
|
10
|
+
~/src/ruby3mf/bin/cli.rb ${filename} >> ${NAME}_test.json
|
|
11
|
+
done
|
|
12
|
+
|
|
13
|
+
echo "All Done."
|
data/lib/ruby3mf.rb
CHANGED
|
@@ -7,7 +7,7 @@ require_relative "ruby3mf/model3mf"
|
|
|
7
7
|
require_relative "ruby3mf/relationships"
|
|
8
8
|
require_relative "ruby3mf/thumbnail3mf"
|
|
9
9
|
require_relative "ruby3mf/texture3mf"
|
|
10
|
-
require_relative "ruby3mf/
|
|
10
|
+
require_relative "ruby3mf/xml_val"
|
|
11
11
|
|
|
12
12
|
require 'zip'
|
|
13
13
|
require 'nokogiri'
|
|
@@ -6,7 +6,7 @@ class ContentTypes
|
|
|
6
6
|
Log3mf.context "parse" do |l|
|
|
7
7
|
begin
|
|
8
8
|
|
|
9
|
-
doc =
|
|
9
|
+
doc = XmlVal.validate_parse(zip_entry)
|
|
10
10
|
|
|
11
11
|
l.warning '[Content_Types].xml must contain exactly one root node' unless doc.children.size == 1
|
|
12
12
|
l.warning '[Content_Types].xml must contain root name Types' unless doc.children.first.name == "Types"
|
data/lib/ruby3mf/document.rb
CHANGED
|
@@ -6,6 +6,7 @@ class Document
|
|
|
6
6
|
attr_accessor :thumbnails
|
|
7
7
|
attr_accessor :textures
|
|
8
8
|
attr_accessor :objects
|
|
9
|
+
attr_accessor :parts
|
|
9
10
|
attr_accessor :zip_filename
|
|
10
11
|
|
|
11
12
|
# Relationship schemas
|
|
@@ -22,6 +23,8 @@ class Document
|
|
|
22
23
|
PRINT_TICKET_TYPE => {}
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
TEXTURE_TYPES = %w[image/jpeg image/png application/vnd.ms-package.3dmanufacturing-3dmodeltexture]
|
|
27
|
+
|
|
25
28
|
def initialize(zip_filename)
|
|
26
29
|
self.models=[]
|
|
27
30
|
self.thumbnails=[]
|
|
@@ -29,25 +32,56 @@ class Document
|
|
|
29
32
|
self.objects={}
|
|
30
33
|
self.relationships=[]
|
|
31
34
|
self.types=[]
|
|
35
|
+
self.parts=[]
|
|
32
36
|
@zip_filename = zip_filename
|
|
33
37
|
end
|
|
34
38
|
|
|
39
|
+
#verify that each texture part in the 3MF is related to the model through a texture relationship in a rels file
|
|
40
|
+
def self.validate_texture_parts(document, log)
|
|
41
|
+
|
|
42
|
+
return unless document.types.size > 0
|
|
43
|
+
document.parts.each do |filename|
|
|
44
|
+
ext = File::extname(filename).delete '.'
|
|
45
|
+
content_type = document.types[ext]
|
|
46
|
+
next unless TEXTURE_TYPES.include?(content_type)
|
|
47
|
+
|
|
48
|
+
has_relationship = false
|
|
49
|
+
document.textures.each do |texture_file|
|
|
50
|
+
if texture_file[:target] == filename
|
|
51
|
+
has_relationship = true
|
|
52
|
+
break
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
next unless !has_relationship
|
|
57
|
+
document.thumbnails.each do |thumbnail_file|
|
|
58
|
+
if thumbnail_file[:target] == filename
|
|
59
|
+
has_relationship = true
|
|
60
|
+
break
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
log.context "part names /#{filename}" do |l|
|
|
64
|
+
l.error :texture_without_relationship, name: filename unless has_relationship
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
35
69
|
def self.read(input_file)
|
|
36
70
|
|
|
37
71
|
m = new(input_file)
|
|
38
72
|
begin
|
|
39
|
-
Log3mf.context
|
|
73
|
+
Log3mf.context 'zip' do |l|
|
|
40
74
|
begin
|
|
41
75
|
Zip.warn_invalid_date = false
|
|
42
76
|
|
|
43
77
|
# check for the general purpose flag set - if so, warn that 3mf may not work on some systems
|
|
44
78
|
if File.read(input_file)[6] == "\b"
|
|
45
|
-
l.warning
|
|
79
|
+
l.warning 'File format: this file may not open on all systems'
|
|
46
80
|
end
|
|
47
81
|
|
|
48
82
|
Zip::File.open(input_file) do |zip_file|
|
|
49
83
|
|
|
50
|
-
l.info
|
|
84
|
+
l.info 'Zip file is valid'
|
|
51
85
|
|
|
52
86
|
# check for valid, absolute URI's for each path name
|
|
53
87
|
|
|
@@ -56,7 +90,7 @@ class Document
|
|
|
56
90
|
unless part.name.end_with? '[Content_Types].xml'
|
|
57
91
|
begin
|
|
58
92
|
u = URI part.name
|
|
59
|
-
rescue ArgumentError
|
|
93
|
+
rescue ArgumentError, URI::InvalidURIError
|
|
60
94
|
l.error :err_uri_bad
|
|
61
95
|
next
|
|
62
96
|
end
|
|
@@ -64,28 +98,29 @@ class Document
|
|
|
64
98
|
u.path.split('/').each do |segment|
|
|
65
99
|
l.error :err_uri_hidden_file if (segment.start_with? '.') && !(segment.end_with? '.rels')
|
|
66
100
|
end
|
|
101
|
+
m.parts << '/' + part.name unless part.directory?
|
|
67
102
|
end
|
|
68
103
|
end
|
|
69
104
|
end
|
|
70
105
|
|
|
71
|
-
l.context
|
|
106
|
+
l.context 'content types' do |l|
|
|
72
107
|
# 1. Get Content Types
|
|
73
108
|
content_type_match = zip_file.glob('\[Content_Types\].xml').first
|
|
74
109
|
if content_type_match
|
|
75
110
|
m.types = ContentTypes.parse(content_type_match)
|
|
76
111
|
else
|
|
77
|
-
l.error
|
|
112
|
+
l.error 'Missing required file: [Content_Types].xml', page: 4
|
|
78
113
|
end
|
|
79
114
|
end
|
|
80
115
|
|
|
81
|
-
l.context
|
|
116
|
+
l.context 'relationships' do |l|
|
|
82
117
|
# 2. Get Relationships
|
|
83
118
|
# rel_folders = zip_file.glob('**/_rels')
|
|
84
119
|
# l.fatal_error "Missing any _rels folder", page: 4 unless rel_folders.size>0
|
|
85
120
|
|
|
86
121
|
# 2.1 Validate that the top level _rels/.rel file exists
|
|
87
122
|
rel_file = zip_file.glob('_rels/.rels').first
|
|
88
|
-
l.fatal_error
|
|
123
|
+
l.fatal_error 'Missing required file _rels/.rels', page: 4 unless rel_file
|
|
89
124
|
|
|
90
125
|
zip_file.glob('**/*.rels').each do |rel|
|
|
91
126
|
m.relationships += Relationships.parse(rel)
|
|
@@ -135,6 +170,8 @@ class Document
|
|
|
135
170
|
end
|
|
136
171
|
end
|
|
137
172
|
end
|
|
173
|
+
|
|
174
|
+
validate_texture_parts(m, l)
|
|
138
175
|
end
|
|
139
176
|
|
|
140
177
|
return m
|
|
@@ -168,7 +205,7 @@ class Document
|
|
|
168
205
|
end
|
|
169
206
|
end
|
|
170
207
|
|
|
171
|
-
File.open(output_file,
|
|
208
|
+
File.open(output_file, 'wb') { |f| f.write(buffer.string) }
|
|
172
209
|
|
|
173
210
|
end
|
|
174
211
|
|
data/lib/ruby3mf/errors.yml
CHANGED
|
@@ -52,11 +52,14 @@ invalid_startpart_type:
|
|
|
52
52
|
invalid_relationship_type:
|
|
53
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
54
|
page: 10
|
|
55
|
+
invalid_thumbnail_file:
|
|
56
|
+
msg: "thumbnail file must be valid image file"
|
|
57
|
+
page: 10
|
|
55
58
|
invalid_thumbnail_colorspace:
|
|
56
59
|
msg: "CMYK JPEG images must not be used for the thumbnail"
|
|
57
60
|
page: 36
|
|
58
61
|
invalid_thumbnail_file_type:
|
|
59
|
-
msg: "Expected a png or jpeg thumbnail but the thumbnail was of type
|
|
62
|
+
msg: "Expected a png or jpeg thumbnail but the thumbnail was of type %{type}"
|
|
60
63
|
page: 36
|
|
61
64
|
invalid_texture_file_type:
|
|
62
65
|
msg: "Expected a png or jpeg texture but the texture was of type image/gif"
|
|
@@ -117,4 +120,16 @@ unknown_required_extension:
|
|
|
117
120
|
page: 14
|
|
118
121
|
missing_extension_namespace_uri:
|
|
119
122
|
msg: "Required extension '%{ns}' MUST refer to namespace with URI"
|
|
120
|
-
page: 14
|
|
123
|
+
page: 14
|
|
124
|
+
texture_without_relationship:
|
|
125
|
+
msg: "%{name} appears to be a texture file but no rels file declares any relationship to the model"
|
|
126
|
+
page: 13
|
|
127
|
+
invalid_metadata_under_defaultns:
|
|
128
|
+
msg: "Metadata without a namespace name must only contain allowed name values"
|
|
129
|
+
page: 21
|
|
130
|
+
has_commas_for_floats:
|
|
131
|
+
msg: "numbers not formatted for the en-US locale"
|
|
132
|
+
page: 15
|
|
133
|
+
invalid_language_locale:
|
|
134
|
+
msg: "locale should be en-US"
|
|
135
|
+
page: 15
|
data/lib/ruby3mf/model3mf.rb
CHANGED
|
@@ -14,7 +14,7 @@ class Model3mf
|
|
|
14
14
|
|
|
15
15
|
Log3mf.context "parsing model" do |l|
|
|
16
16
|
begin
|
|
17
|
-
model_doc =
|
|
17
|
+
model_doc = XmlVal.validate_parse(zip_entry)
|
|
18
18
|
rescue Nokogiri::XML::SyntaxError => e
|
|
19
19
|
l.fatal_error "Model file invalid XML. Exception #{e}"
|
|
20
20
|
end
|
|
@@ -108,6 +108,13 @@ class Model3mf
|
|
|
108
108
|
metadata = model_doc.root.css("metadata")
|
|
109
109
|
metadata_names = metadata.map { |met| met['name'] }
|
|
110
110
|
l.error :metadata_elements_with_same_name unless metadata_names.uniq!.nil?
|
|
111
|
+
|
|
112
|
+
# metadata values allowed under default namespace (xmlns):
|
|
113
|
+
metadata_values = ['Title', 'Designer', 'Description', 'Copyright', 'LicenseTerms', 'Rating', 'CreationDate', 'ModificationDate' ]
|
|
114
|
+
|
|
115
|
+
unless model_doc.root.namespace.href.nil? || model_doc.root.namespace_definitions.count > 1
|
|
116
|
+
l.error :invalid_metadata_under_defaultns unless (metadata_names - metadata_values).empty?
|
|
117
|
+
end
|
|
111
118
|
end
|
|
112
119
|
|
|
113
120
|
MeshAnalyzer.validate(model_doc)
|
|
@@ -5,7 +5,7 @@ class Relationships
|
|
|
5
5
|
Log3mf.context "parsing relationships" do |l|
|
|
6
6
|
begin
|
|
7
7
|
# Parse Relationships XML
|
|
8
|
-
doc =
|
|
8
|
+
doc = XmlVal.validate_parse(zip_entry)
|
|
9
9
|
|
|
10
10
|
# Verify <Relationships><Relationship/></Relationships>
|
|
11
11
|
root_element = doc.children[0]
|
|
@@ -14,11 +14,7 @@ class Relationships
|
|
|
14
14
|
if relationship_elements.size > 0
|
|
15
15
|
relationship_elements.each do |node|
|
|
16
16
|
if node.is_a?(Nokogiri::XML::Element) && node.name == "Relationship"
|
|
17
|
-
relationships.
|
|
18
|
-
if previous_rel[:target] == node['Target'] && previous_rel[:type] == node['Type']
|
|
19
|
-
l.error :multiple_relationships
|
|
20
|
-
end
|
|
21
|
-
end
|
|
17
|
+
l.error :multiple_relationships if (relationships.select { |r| r[:target] == node['Target'] && r[:type] == node['Type'] }.size > 0)
|
|
22
18
|
relationships << {target: node['Target'], type: node['Type'], id: node['Id']}
|
|
23
19
|
l.info "adding relationship: #{relationships.last.inspect}"
|
|
24
20
|
else
|
data/lib/ruby3mf/thumbnail3mf.rb
CHANGED
|
@@ -3,16 +3,14 @@ require 'mini_magick'
|
|
|
3
3
|
class Thumbnail3mf
|
|
4
4
|
|
|
5
5
|
def self.parse(doc, relationship_file)
|
|
6
|
-
img_type = MimeMagic.by_magic(relationship_file.get_input_stream)
|
|
7
|
-
img_colorspace = MiniMagick::Image.read(relationship_file.get_input_stream).colorspace
|
|
8
|
-
|
|
9
6
|
Log3mf.context "Thumbnail3mf" do |l|
|
|
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"
|
|
12
|
-
l.debug "thumbnail is of type: #{img_type}"
|
|
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
|
|
14
|
-
end
|
|
15
7
|
|
|
16
|
-
|
|
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
|
|
17
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
|
+
end
|
|
15
|
+
end
|
|
18
16
|
end
|
data/lib/ruby3mf/version.rb
CHANGED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'nokogiri'
|
|
2
|
+
|
|
3
|
+
class XmlVal
|
|
4
|
+
|
|
5
|
+
def self.validate_parse(file)
|
|
6
|
+
doc = Nokogiri::XML(file.get_input_stream) do |config|
|
|
7
|
+
config.strict.nonet.noblanks
|
|
8
|
+
end
|
|
9
|
+
validate(file, doc)
|
|
10
|
+
doc
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.validate(file, document)
|
|
14
|
+
Log3mf.context "validations" do |l|
|
|
15
|
+
l.error :invalid_language_locale if invalid_locale?(document)
|
|
16
|
+
l.error :has_xml_space_attribute if space_attribute_exists?(document)
|
|
17
|
+
l.error :wrong_encoding if xml_not_utf8_encoded?(document)
|
|
18
|
+
l.error :dtd_not_allowed if dtd_exists?(file)
|
|
19
|
+
l.error :has_commas_for_floats if bad_floating_numbers?(document)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.invalid_locale?(document)
|
|
24
|
+
!document.xpath('//@xml:lang').empty? && document.xpath('//@xml:lang').text != "en-US"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.bad_floating_numbers?(document)
|
|
28
|
+
!document.xpath('.//*[find_with_regex(., "[0-9]+\,[0-9]+")]', Class.new {
|
|
29
|
+
def find_with_regex node_set, regex
|
|
30
|
+
node_set.find_all { |node| node.values.any? {|v| v =~ /#{regex}/ } }
|
|
31
|
+
end
|
|
32
|
+
}.new).empty?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.space_attribute_exists?(document)
|
|
36
|
+
!(document.xpath('//*[@xml:space]').empty?)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.xml_not_utf8_encoded?(document)
|
|
40
|
+
!document.encoding.nil? && (document.encoding.to_s.downcase != 'utf-8')
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.dtd_exists?(file)
|
|
44
|
+
found = file.get_input_stream.find { |line| line =~ /(!DOCTYPE\b)|(!ELEMENT\b)|(!ENTITY\b)|(!NOTATION\b)|(!ATTLIST\b)/ }
|
|
45
|
+
!found.nil?
|
|
46
|
+
end
|
|
47
|
+
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.
|
|
4
|
+
version: 0.1.17
|
|
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-
|
|
11
|
+
date: 2017-01-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -141,15 +141,16 @@ files:
|
|
|
141
141
|
- LICENSE.txt
|
|
142
142
|
- README.md
|
|
143
143
|
- Rakefile
|
|
144
|
+
- bin/batch.rb
|
|
144
145
|
- bin/cli.rb
|
|
145
146
|
- bin/console
|
|
147
|
+
- bin/folder_test.sh
|
|
146
148
|
- bin/setup
|
|
147
149
|
- lib/ruby3mf.rb
|
|
148
150
|
- lib/ruby3mf/content_types.rb
|
|
149
151
|
- lib/ruby3mf/document.rb
|
|
150
152
|
- lib/ruby3mf/edge_list.rb
|
|
151
153
|
- lib/ruby3mf/errors.yml
|
|
152
|
-
- lib/ruby3mf/global_xml_validations.rb
|
|
153
154
|
- lib/ruby3mf/interpolation.rb
|
|
154
155
|
- lib/ruby3mf/log3mf.rb
|
|
155
156
|
- lib/ruby3mf/mesh_analyzer.rb
|
|
@@ -158,6 +159,7 @@ files:
|
|
|
158
159
|
- lib/ruby3mf/texture3mf.rb
|
|
159
160
|
- lib/ruby3mf/thumbnail3mf.rb
|
|
160
161
|
- lib/ruby3mf/version.rb
|
|
162
|
+
- lib/ruby3mf/xml_val.rb
|
|
161
163
|
- ruby3mf.gemspec
|
|
162
164
|
homepage: https://github.com/IPGPTP/ruby3mf
|
|
163
165
|
licenses:
|
|
@@ -1,36 +0,0 @@
|
|
|
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
|
-
|