Module: DataUri

Defined in:
lib/datauris.rb,
lib/datauris/version.rb

Constant Summary collapse

REGEX =

make parameters key/value more strict - why? why not?

e.g. MIME_PARAM_RE = /^;([-\w.+]+)=([^;,]+)/.freeze

allow  +(plus) or .(dot) in param key - possible?? why? why not?
%r{
  \A
  data:
  (?<mediatype>
    (?:
        (?<type> [\w-]+? )  
         / 
        (?<subtype> [\w.+-]+? )
    )?
    (?<parameters> (?: ; 
                       [\w-]+? 
                          = 
                          .+? 
                    )* 
    )
  )?
  (?:;
     (?<extension>base64|utf8)
   )?
  ,
  (?<data>.*)
  \z
}x
NOT_SAFECHARS_RX =
/([^a-zA-Z0-9\-_.~]+)/
MAJOR =

todo: namespace inside version or something - why? why not??

1
MINOR =
1
PATCH =
1
VERSION =
[MAJOR,MINOR,PATCH].join('.')

Class Method Summary collapse

Class Method Details

._parse(str) ⇒ Object



65
# File 'lib/datauris.rb', line 65

def self._parse( str ) REGEX.match( str ); end


12
13
14
# File 'lib/datauris/version.rb', line 12

def self.banner
  "datauris/#{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}] in (#{root})"
end

.build(data, type = nil, base64: nil, utf8: nil, uri: nil) ⇒ Object Also known as: encode

base64 - force base64 encoding instead of “automagic” (base64: true) utf8 - or force utf8 encoding (utf8: true) uri - or force STOPPING uri encoding (uri: false)



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/datauris.rb', line 174

def self.build( data, type=nil, base64: nil, utf8: nil, uri: nil )
    str = "data:"
    str += type      if type   ## note: allow optional / no type

    
    ## puts "  type: #{type.inspect}, base64: #{base64.inspect}"


    ## add more (binary) media types here - why? why not?

    ##   note svg is text AND an image => image/svg+xml

    if base64.nil?                   
        base64 = if type 
                   if type.start_with?( 'image/svg+xml' )
                     false
                   elsif type.start_with?( 'image/') ||
                         type.start_with?( 'application/octet-stream' )
                     true
                   else 
                     false
                   end
                 else # no type (assume text)

                    false
                 end
    end

    if base64    
        str += ";base64," + Base64.strict_encode64( data )
    elsif utf8 
        str += ";utf8," + data          
    else
        ## use encode_uri_component by default - why? why not?

        ##  space becomes %20

        ##  :     becomes %3A

        ##  ,     becomes %2C  and so on

        ##

        ## note: use uri: false to turn of uri encode!!!

        str += "," +  (uri == false ? data : encode_uri( data ))
    end   
end

.encode_uri(str) ⇒ Object



163
164
165
166
167
168
# File 'lib/datauris.rb', line 163

def self.encode_uri( str )
  encoding = str.encoding
  str.b.gsub( NOT_SAFECHARS_RX ) do |m|
    '%' + m.unpack('H2' * m.bytesize).join('%').upcase
  end.force_encoding(encoding)
end

.parse(str, utf8: false) ⇒ Object Also known as: decode

allow force utf8 with utf8: true



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/datauris.rb', line 67

def self.parse( str, utf8: false )   ## allow force utf8 with utf8: true

    m = _parse( str ) 
  
    if m
      ## 1) return mediatype (mimetype PLUS optional parameters)

      ## 2) return data (base64 decoded or not)

  
      ## todo/check:

      ##   add force_encoding( 'UTF-8' ) to returned data (if not base64) - why? why not?


      mediatype = m[:mediatype]  
      data      = if m[:extension] && m[:extension] == 'base64'  ## assume base64 encoded

                     Base64.strict_decode64(m[:data])
                  elsif utf8 || (m[:extension] && m[:extension] == 'utf8')
                     m[:data]    ## note: no decode; assume "plain" utf8 string  

                  else
                     ## e.g. %20 => space(20)

                     ##  etc.

                     ## todo/double check - use a different URI decoder - why? why not?

                     URI.decode_uri_component(m[:data])
                  end

      ## returns data first!!!

      ##  and mediatype second (to follow the order in build)

      ##   - also mediatype is optional -  arguable more intuitive - yes,no?

      [data, mediatype]
    else
       raise ArgumentError, "invalid datauri - cannot match regex; sorry"
    end
end

.rootObject



16
17
18
# File 'lib/datauris/version.rb', line 16

def self.root
  File.expand_path( File.dirname(File.dirname(File.dirname(__FILE__))) )
end

.valid?(str, utf8: false) ⇒ Boolean Also known as: is_valid?

Returns:

  • (Boolean)


99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/datauris.rb', line 99

def self.valid?( str, utf8: false )
  m = _parse( str )
  if m 
    if m[:extension] && m[:extension] == 'base64'   ## assume base64

      begin
        Base64.strict_decode64(m[:data])
        true
      rescue ArgumentError
        false
      end
    elsif utf8 || (m[:extension] && m[:extension] == 'utf8') 
       true   ## pass through as is; assume always true (check valid utf8 encoding - why? why not?) 

    else
      begin 
        URI.decode_uri_component(m[:data])            
        true
      rescue ArgumentError   ## check if decode errors are argument errors??

        false
      end
    end
  else
    false
  end
end

.versionObject



8
9
10
# File 'lib/datauris/version.rb', line 8

def self.version
  VERSION
end