datauris 1.0.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8b17d925dd560274e1e3a4f21e5caa118762e74c1d5b1e4e07777c9456b64f0f
4
+ data.tar.gz: 34c7263eea9cd31e1bfede00bb4002d303dc59d7870d3bad620a88a7345d292a
5
+ SHA512:
6
+ metadata.gz: 509299375bebbc22eee68c3c3a704030df51c1d555a57ee5ff83435b1b5d6a0cf9de75c49d1551f277e89db01280bb7f15dc2f2ddd42cfa9bf3ef7279cef0ce4
7
+ data.tar.gz: b0aba84982c23968350395e3aedf91e11bf7f31a20566e1262f715f9638fdcf05b91aa2e575f4189ca0d58318f8702de0d587efbd7262d7a0c466a3b8e1e33e9
data/CHANGELOG.md ADDED
@@ -0,0 +1,4 @@
1
+ ### 1.0.0
2
+ ### 0.0.1 / 2023-11-23
3
+
4
+ * Everything is new. First release
data/Manifest.txt ADDED
@@ -0,0 +1,7 @@
1
+ CHANGELOG.md
2
+ Manifest.txt
3
+ README.md
4
+ Rakefile
5
+ lib/datauri.rb
6
+ lib/datauris.rb
7
+ lib/datauris/version.rb
data/README.md ADDED
@@ -0,0 +1,174 @@
1
+ # Data URI Helpers
2
+
3
+ datauris - helpers to parse (decode) and build (encode) data uris incl. (strict)base64-encoded/decoded images and more
4
+
5
+ * home :: [github.com/s6ruby/rubidity](https://github.com/s6ruby/rubidity)
6
+ * bugs :: [github.com/s6ruby/rubidity/issues](https://github.com/s6ruby/rubidity/issues)
7
+ * gem :: [rubygems.org/gems/datauris](https://rubygems.org/gems/datauris)
8
+ * rdoc :: [rubydoc.info/gems/datauris](http://rubydoc.info/gems/datauris)
9
+
10
+
11
+
12
+ ## What are Data URIs?!
13
+
14
+ Data URI Syntax via [Wikipedia](https://en.wikipedia.org/wiki/Data_URI_scheme):
15
+
16
+ > The syntax of data URIs is defined in [Request for Comments (RFC) 2397](https://datatracker.ietf.org/doc/html/rfc2397),
17
+ > published in August 1998, and follows the URI scheme syntax. A data URI consists of:
18
+ >
19
+ > data:[<mediatype>][;base64],<data>
20
+ >
21
+ > - The **scheme**, `data`. It is followed by a colon (`:`).
22
+ > - An optional **media type**. The media type part may include one or more parameters,
23
+ > in the format `attribute=value`, separated by semicolons (`;`).
24
+ > A common media type parameter is charset, specifying the character
25
+ > set of the media type, where the value is from the IANA list of character set names.
26
+ > If one is not specified, the media type of the data URI is assumed
27
+ > to be `text/plain;charset=US-ASCII`.
28
+ > - An optional **base64 extension** `base64`, separated from the preceding part by a semicolon.
29
+ > When present, this indicates that the data content of the URI is binary data, encoded in ASCII format using the Base64 scheme for binary-to-text encoding.
30
+ > The base64 extension is distinguished from any media type parameters
31
+ > by virtue of not having a `=value` component and
32
+ > by coming after any media type parameters.
33
+ > - The **data**, separated from the preceding part by a comma (`,`).
34
+ > The data is a sequence of zero or more octets represented as characters.
35
+ > The comma is required in a data URI, even when the data part has zero length.
36
+ > The characters permitted within the data part include ASCII upper
37
+ > and lowercase letters, digits, and many ASCII punctuation and special characters.
38
+ > Note that this may include characters, such as colon, semicolon,
39
+ > and comma which are delimiters in the URI components preceding the data part.
40
+ > Other octets must be percent-encoded. If the data is Base64-encoded,
41
+ > then the data part may contain only valid Base64 characters.
42
+ > Note that Base64-encoded data: URIs use the standard Base64 character set
43
+ > (with `+` and `/` as characters 62 and 63)
44
+ > rather than the so-called "URL-safe Base64" character set.
45
+
46
+
47
+
48
+ ## Usage
49
+
50
+ [Parse](#parse) • [Build](#build)
51
+
52
+ ### Parse
53
+
54
+
55
+ Let's try and parse the Wikipedia Data URI examples:
56
+
57
+ ``` ruby
58
+ uri = "data:text/vnd-example+xyz;foo=bar;base64,R0lGODdh"
59
+ DataUri.valid?( uri )
60
+ #=> true
61
+ mediatype, data = DataUri.parse( uri ) ## returns 1) mediatype (+parameters), 2) data
62
+ #=> "text/vnd-example+xyz;foo=bar", "<blob>"
63
+
64
+ uri = "data:text/plain;charset=UTF-8;page=21,the%20data:1234,5678"
65
+ DataUri.valid?( uri )
66
+ #=> true
67
+ mediatype, data = DataUri.parse( uri ) ## returns 1) mediatype (+parameters), 2) data
68
+ #=> "text/plain;charset=UTF-8;page=21", "the data:1234,5678"
69
+
70
+
71
+ uri = "data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAZABkAAD/"
72
+ DataUri.valid?( uri )
73
+ #=> true
74
+ mediatype, data = DataUri.parse( uri ) ## returns 1) mediatype (+parameters), 2) data
75
+ #=> "image/jpeg", "<blob>"
76
+ ```
77
+
78
+
79
+
80
+ Let's try the DataUri helpers on inscriptions (that encode the "on-chain" data
81
+ in Data URIs). A valid inscribe must use a valid data uri in the calldata.
82
+
83
+ The "useless" (null) minimum - no media-type, and zero-length data - is:
84
+
85
+ ``` ruby
86
+ uri = "data:,"
87
+
88
+ DataUri.valid?( uri )
89
+ #=> true
90
+ mediatype, data = DataUri.parse( uri ) ## returns 1) mediatype (+parameters), 2) data
91
+ #=> "", ""
92
+ ```
93
+
94
+ Let's try the (genesis) inscribe no. 0:
95
+
96
+ ``` ruby
97
+ uri = "data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHV..."
98
+
99
+ DataUri.valid?( uri )
100
+ #=> true
101
+ mediatype, data = DataUri.parse( uri ) ## returns 1) mediatype (+parameters), 2) data
102
+ #=> "image/jpeg", "<blob>"
103
+
104
+ ## let's save the jpeg image (blob)
105
+ write_blob( "0.jpeg", data )
106
+ ```
107
+
108
+ and voila!
109
+
110
+ ![](i/0.jpeg)
111
+
112
+
113
+ Let's try the inscribe no. 15:
114
+
115
+ ``` ruby
116
+ uri = "data:image/png;base64,/9j/4gxYSUNDX1BST0ZJTEUAAQEAAAxITGlubwIQAAB..."
117
+
118
+ DataUri.valid?( uri )
119
+ #=> true
120
+ mediatype, data = DataUri.parse( uri ) ## returns 1) mediatype (+parameters), 2) data
121
+ #=> "image/png", "<blob>"
122
+
123
+ ## let's save the png image (blob)
124
+ write_blob( "15.png", data )
125
+ ```
126
+
127
+ and voila!
128
+
129
+ ![](i/15.png)
130
+
131
+
132
+
133
+
134
+ ### Build
135
+
136
+ Let's build Data URIs.
137
+
138
+ ```ruby
139
+ uri = DataUri.build( "" )
140
+ #=> "data:,"
141
+
142
+ uri = DataUri.build( "the data:1234,5678", "text/plain" )
143
+ #=> "data:text/plain,the%20data:1234,5678"
144
+
145
+ # let's try images
146
+ data = read_blob( "0.jpeg" )
147
+ uri = DataUri.build( data, "image/jpeg" )
148
+ #=> "data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHV..."
149
+
150
+ data = read_blob( "15.png" )
151
+ uri = DataUri.build( data, "image/png" )
152
+ #=> "data:image/png;base64,/9j/4gxYSUNDX1BST0ZJTEUAAQEAAAxITGlubwIQAAB..."
153
+ ```
154
+
155
+
156
+
157
+ That's it for now.
158
+
159
+
160
+
161
+
162
+ ## Bonus - More Blockchain (Crypto) Tools, Libraries & Scripts In Ruby
163
+
164
+ See [**/blockchain**](https://github.com/rubycocos/blockchain)
165
+ at the ruby code commons (rubycocos) org.
166
+
167
+
168
+ ## Questions? Comments?
169
+
170
+ Join us in the [Rubidity (community) discord (chat server)](https://discord.gg/3JRnDUap6y). Yes you can.
171
+ Your questions and commentary welcome.
172
+
173
+ Or post them over at the [Help & Support](https://github.com/geraldb/help) page. Thanks.
174
+
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ require 'hoe'
2
+ require './lib/datauris/version.rb'
3
+
4
+
5
+ Hoe.spec 'datauris' do
6
+ self.version = DataUri::VERSION
7
+
8
+ self.summary = "datauris gem - DataUri helpers to parse (decode) and build (encode) data uris incl. (strict) base64-encoded/decoded images and more"
9
+ self.description = summary
10
+
11
+ self.urls = { home: 'https://github.com/s6ruby/rubidity' }
12
+
13
+ self.author = 'Gerald Bauer'
14
+ self.email = 'gerald.bauer@gmail.com'
15
+
16
+ # switch extension to .markdown for gihub formatting
17
+ self.readme_file = 'README.md'
18
+ self.history_file = 'CHANGELOG.md'
19
+
20
+ self.extra_deps = [
21
+ ]
22
+
23
+ self.licenses = ['Public Domain']
24
+
25
+ self.spec_extras = {
26
+ required_ruby_version: '>= 2.3'
27
+ }
28
+ end
data/lib/datauri.rb ADDED
@@ -0,0 +1,5 @@
1
+
2
+ ## let's you use require 'datauri' singular too
3
+ require_relative 'datauri'
4
+
5
+
@@ -0,0 +1,19 @@
1
+
2
+ module DataUri
3
+ MAJOR = 1 ## todo: namespace inside version or something - why? why not??
4
+ MINOR = 0
5
+ PATCH = 0
6
+ VERSION = [MAJOR,MINOR,PATCH].join('.')
7
+
8
+ def self.version
9
+ VERSION
10
+ end
11
+
12
+ def self.banner
13
+ "datauris/#{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}] in (#{root})"
14
+ end
15
+
16
+ def self.root
17
+ File.expand_path( File.dirname(File.dirname(File.dirname(__FILE__))) )
18
+ end
19
+ end
data/lib/datauris.rb ADDED
@@ -0,0 +1,211 @@
1
+
2
+ require 'base64' ## pull in Base64.strict_encode64/decode64 (standard) module
3
+ require 'uri' ## pull in URI.decode_uri_component
4
+
5
+
6
+ ## our own code
7
+ require_relative 'datauris/version' # let version go first
8
+
9
+
10
+ module DataUri
11
+ REGEX_V0 = %r{
12
+ \Adata:
13
+ (?<mediatype>
14
+ (?<type> .+? / .+? )?
15
+ (?<parameters> (?: ; .+? = .+? )* )
16
+ )?
17
+ (?<base64extension>;base64)?
18
+ ,
19
+ (?<data>.*)\z
20
+ }x
21
+
22
+ ## allow type only - why? why not?
23
+ ## split subtype into [tree prefix] and subtype
24
+ ## check if type can include dash (-) - why? why not?
25
+ ##
26
+ ## note: \w => A word character
27
+ ## is a character a-z, A-Z, 0-9, including _ (underscore).
28
+ ##
29
+ ## official grammar:
30
+ ## mime-type = type "/" [tree "."] subtype ["+" suffix]* [";" parameter];
31
+ ##
32
+ ## Types
33
+ ## The "type" part defines the broad use of the media type.
34
+ ## As of November 1996, the registered types were:
35
+ ## application, audio, image, message, multipart, text and video.
36
+ # By December 2020, the registered types included the foregoing,
37
+ ## plus font, example, and model.
38
+ ##
39
+ ## An unofficial top-level type in common use is chemical
40
+
41
+ ## examples with numbers:
42
+ # application/vnd.software602.filler.form+xml
43
+ # application/x-shockwave-flash2-preview
44
+ # application/vnd.isac.fcs; version="1.0-3.1"
45
+
46
+ ## make parameters key/value more strict - why? why not?
47
+ ## e.g. MIME_PARAM_RE = /^;([-\w.+]+)=([^;,]+)/.freeze
48
+ ##
49
+ ## allow +(plus) or .(dot) in param key - possible?? why? why not?
50
+
51
+ REGEX = %r{
52
+ \A
53
+ data:
54
+ (?<mediatype>
55
+ (?:
56
+ (?<type> [\w-]+? )
57
+ /
58
+ (?<subtype> [\w.+-]+? )
59
+ )?
60
+ (?<parameters> (?: ;
61
+ [\w.+-]+?
62
+ =
63
+ .+?
64
+ )*
65
+ )
66
+ )?
67
+ (?<base64extension>;base64)?
68
+ ,
69
+ (?<data>.*)
70
+ \z
71
+ }x
72
+
73
+
74
+ def self._parse( str ) REGEX.match( str ); end
75
+
76
+ def self.parse( str )
77
+ m = _parse( str )
78
+
79
+ if m
80
+ ## 1) return mediatype (mimetype PLUS optional parameters)
81
+ ## 2) return data (base64 decoded or not)
82
+
83
+ mediatype = m[:mediatype]
84
+ data = if m[:base64extension] ## assume base64 encoded
85
+ Base64.strict_decode64(m[:data])
86
+ else
87
+ ## e.g. %20 => space(20)
88
+ ## etc.
89
+ ## todo/double check - use a different URI decoder - why? why not?
90
+ URI.decode_uri_component(m[:data])
91
+ end
92
+ [mediatype, data]
93
+ else
94
+ raise ArgumentError, "invalid datauri - cannot match regex; sorry"
95
+ end
96
+ end
97
+
98
+
99
+ def self.valid?( str )
100
+ m = _parse( str )
101
+ if m
102
+ if m[:base64extension] ## assume base64
103
+ begin
104
+ Base64.strict_decode64(m[:data])
105
+ true
106
+ rescue ArgumentError
107
+ false
108
+ end
109
+ else
110
+ true
111
+ end
112
+ else
113
+ false
114
+ end
115
+ end
116
+
117
+
118
+ ###
119
+ ## fix-fix-fix - use our own encode_uri instead of encode_uri_component
120
+ ## why? keep more chars unescaped
121
+ ## e.g. ??
122
+ ##
123
+ ## check about space ( ) - must be encoded - why? why not??
124
+ ## comma (,)
125
+ ## more from json - [] or {} or ""
126
+ ##
127
+ ## see <https://en.wikipedia.org/wiki/Percent-encoding>
128
+
129
+
130
+ ## RFC 3986 section 2.3 Unreserved Characters (January 2005)
131
+ ## A-Z => A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
132
+ ## a-z => a b c d e f g h i j k l m n o p q r s t u v w x y z
133
+ ## 0-9 => 0 1 2 3 4 5 6 7 8 9
134
+ ## - _ . ~
135
+ ## Other characters in a URI must be percent-encoded.
136
+
137
+ ## RFC 3986 section 2.2 Reserved Characters (January 2005)
138
+ ## ! # $ & ' ( ) * + , / : ; = ? @ [ ]
139
+ ##
140
+ ## what's missing in reserved?
141
+ ## space(20) and
142
+ ## double quoutes (") and
143
+ ## curly bracket ({}), and ??
144
+ ##
145
+ ## note: Reserved characters that have no reserved purpose in a particular context
146
+ ## may also be percent-encoded but
147
+ ## are not semantically different from those that are not.
148
+
149
+
150
+ ## add more reserved chars here - to keep verbatim (as literal) and NOT percent-encoded
151
+ ## why? why not?
152
+
153
+
154
+ NOT_SAFECHARS_RX = /([^a-zA-Z0-9\-_.~]+)/
155
+
156
+ def self.encode_uri( str )
157
+ encoding = str.encoding
158
+ str.b.gsub( NOT_SAFECHARS_RX ) do |m|
159
+ '%' + m.unpack('H2' * m.bytesize).join('%').upcase
160
+ end.force_encoding(encoding)
161
+ end
162
+
163
+
164
+ ## base64 - force base64 encoding (instead of "automagic")
165
+ def self.build( data, type=nil, base64: nil )
166
+ uri = "data:"
167
+ uri += type if type ## note: allow optional / no type
168
+
169
+ ## puts " type: #{type.inspect}, base64: #{base64.inspect}"
170
+
171
+ ## add more (binary) media types here - why? why not?
172
+ ## note svg is text AND an image => image/svg+xml
173
+ if base64.nil?
174
+ base64 = if type
175
+ if type.start_with?( 'image/svg+xml' )
176
+ false
177
+ elsif type.start_with?( 'image/') ||
178
+ type.start_with?( 'application/octet-stream' )
179
+ true
180
+ else
181
+ false
182
+ end
183
+ else # no type (assume text)
184
+ false
185
+ end
186
+ end
187
+
188
+ if base64
189
+ uri += ";base64," + Base64.strict_encode64( data )
190
+ else
191
+ ## use encode_uri_component by default - why? why not?
192
+ ## space becomes %20
193
+ ## : becomes %3A
194
+ ## , becomes %2C and so on
195
+ uri += "," + encode_uri( data )
196
+ end
197
+ end
198
+
199
+
200
+
201
+ ## add alias convenience names - why? why not?
202
+ class << self
203
+ alias_method :is_valid?, :valid?
204
+ alias_method :decode, :parse
205
+ alias_method :encode, :build
206
+ end
207
+ end # module DataUri
208
+
209
+
210
+
211
+ puts DataUri.banner # say hello
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: datauris
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Gerald Bauer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-11-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rdoc
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '7'
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '4.0'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '7'
33
+ - !ruby/object:Gem::Dependency
34
+ name: hoe
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '4.0'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '4.0'
47
+ description: datauris gem - DataUri helpers to parse (decode) and build (encode)
48
+ data uris incl. (strict) base64-encoded/decoded images and more
49
+ email: gerald.bauer@gmail.com
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files:
53
+ - CHANGELOG.md
54
+ - Manifest.txt
55
+ - README.md
56
+ files:
57
+ - CHANGELOG.md
58
+ - Manifest.txt
59
+ - README.md
60
+ - Rakefile
61
+ - lib/datauri.rb
62
+ - lib/datauris.rb
63
+ - lib/datauris/version.rb
64
+ homepage: https://github.com/s6ruby/rubidity
65
+ licenses:
66
+ - Public Domain
67
+ metadata: {}
68
+ post_install_message:
69
+ rdoc_options:
70
+ - "--main"
71
+ - README.md
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '2.3'
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubygems_version: 3.4.10
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: datauris gem - DataUri helpers to parse (decode) and build (encode) data
89
+ uris incl. (strict) base64-encoded/decoded images and more
90
+ test_files: []