meme_captain 0.0.6 → 0.0.7

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.
data/README.md ADDED
@@ -0,0 +1,55 @@
1
+ Ruby gem to create meme images (images with text added at the top and bottom).
2
+
3
+ Runs locally and has no web dependencies.
4
+
5
+ Works with animated gifs.
6
+
7
+ ```ruby
8
+ require 'open-uri'
9
+
10
+ require 'meme_captain'
11
+
12
+ open('http://memecaptain.com/troll_face.jpg', 'rb') do |f|
13
+ i = MemeCaptain.meme(f, 'test', '1 2 3')
14
+ i.display
15
+ i.write('out.jpg')
16
+ end
17
+ ```
18
+
19
+ Also includes a Sinatra app that exposes the API over HTTP which is currently
20
+ running http://memecaptain.com/
21
+
22
+ You can use the memecaptain.com API if you prefer it to using the gem.
23
+
24
+ Simplest API:
25
+
26
+ ```
27
+ http://memecaptain.com/i?u=<url encoded source image url>&tt=<url encoded top text>&tb=<url encoded bottom text>
28
+ ```
29
+
30
+ Example:
31
+
32
+ ```
33
+ http://memecaptain.com/i?u=http%3A%2F%2Fmemecaptain.com%2Fyao_ming.jpg&tt=sure+i%27ll+test&tb=the+api
34
+ ```
35
+
36
+ ![Sure I'll test the API](http://memecaptain.com/i?u=http%3A%2F%2Fmemecaptain.com%2Fyao_ming.jpg&tt=sure+i%27ll+test&tb=the+api)
37
+
38
+ If you want better error messages, use this which will return JSON:
39
+
40
+ ```
41
+ http://memecaptain.com/g?u=<url encoded source image url>&tt=<url encoded top text>&tb=<url encoded bottom text>
42
+ ```
43
+
44
+ Example:
45
+
46
+ ```
47
+ http://memecaptain.com/g?u=http%3A%2F%2Fmemecaptain.com%2Fyao_ming.jpg&tt=sure+i%27ll+test&tb=the+api
48
+ ```
49
+
50
+ ```json
51
+ {
52
+ permUrl: "http://memecaptain.com/i?u=http%3A%2F%2Fmemecaptain.com%2Fyao_ming.jpg&tt=sure+i%27ll+test&tb=the+api"
53
+ tempUrl: "http://memecaptain.com/tmp/de55f7a78c6559d4a24ef3e72e2de89992b82695.jpeg"
54
+ }
55
+ ```
@@ -0,0 +1,45 @@
1
+ module MemeCaptain
2
+
3
+ class Caption < String
4
+
5
+ def initialize(s='')
6
+ super s.to_s
7
+ end
8
+
9
+ # Return the contents of the string quoted for ImageMagick annotate.
10
+ def annotate_quote
11
+ Caption.new(gsub('\\', '\\\\\\').gsub('%', '\%'))
12
+ end
13
+
14
+ # Whether the string contains any non-whitespace.
15
+ def drawable?
16
+ match(/[^\s]/) ? true : false
17
+ end
18
+
19
+ # Wrap the string of into num_lines lines.
20
+ def wrap(num_lines)
21
+ cleaned = gsub(/\s+/, ' ').strip
22
+
23
+ chars_per_line = cleaned.size / num_lines.to_f
24
+
25
+ lines = []
26
+ cleaned.split.each do |word|
27
+ if lines.empty?
28
+ lines << word
29
+ else
30
+ if (lines[-1].size + 1 + word.size) <= chars_per_line or
31
+ lines.size >= num_lines
32
+ lines[-1] << ' ' unless lines[-1].empty?
33
+ lines[-1] << word
34
+ else
35
+ lines << word
36
+ end
37
+ end
38
+ end
39
+
40
+ Caption.new(lines.join("\n"))
41
+ end
42
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,33 @@
1
+ module MemeCaptain
2
+
3
+ # For comparing different caption line break and pointsize choices.
4
+ class CaptionChoice
5
+ include Comparable
6
+
7
+ def initialize(fits, pointsize, text)
8
+ @fits = fits
9
+ @pointsize = pointsize
10
+ @text = text
11
+ end
12
+
13
+ def num_lines
14
+ text.count("\n") + 1
15
+ end
16
+
17
+ def fits_i
18
+ fits ? 1: 0
19
+ end
20
+
21
+ def <=>(other)
22
+ [fits_i, pointsize, fits ? -num_lines : num_lines] <=>
23
+ [other.fits_i, other.pointsize,
24
+ other.fits ? -other.num_lines : other.num_lines]
25
+ end
26
+
27
+ attr_accessor :fits
28
+ attr_accessor :pointsize
29
+ attr_accessor :text
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,35 @@
1
+ module MemeCaptain
2
+
3
+ # Mix-in for Magick::Draw
4
+ module Draw
5
+
6
+ # Calculate the largest pointsize for text that will be in a width x
7
+ # height box.
8
+ #
9
+ # Return [pointsize, fits] where pointsize is the largest pointsize and
10
+ # fits is true if that pointsize will fit in the box.
11
+ def calc_pointsize(width, height, text, min_pointsize)
12
+ current_pointsize = min_pointsize
13
+
14
+ fits = false
15
+
16
+ loop {
17
+ self.pointsize = current_pointsize
18
+ metrics = get_multiline_type_metrics(text)
19
+ if metrics.width > width or metrics.height > height
20
+ if current_pointsize > min_pointsize
21
+ current_pointsize -= 1
22
+ fits = true
23
+ end
24
+ break
25
+ else
26
+ current_pointsize += 1
27
+ end
28
+ }
29
+
30
+ [current_pointsize, fits]
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -46,6 +46,9 @@ module MemeCaptain
46
46
  # Put data in the cache and return its path.
47
47
  def put(id, data)
48
48
  mime_type = MemeCaptain.mime_type(data)
49
+ unless mime_type
50
+ raise 'Data loaded from source image url is not an image'
51
+ end
49
52
  file_path = id_path(id, ".#{mime_type.extensions[0]}")
50
53
 
51
54
  open(file_path, 'w') do |f|
@@ -6,7 +6,7 @@ module MemeCaptain
6
6
 
7
7
  # Create a meme image.
8
8
  # Input can be an IO object or a blob of data.
9
- def meme(input, line1, line2, options={})
9
+ def meme(input, top_text, bottom_text, options={})
10
10
  img = Magick::ImageList.new
11
11
  if input.respond_to?(:read)
12
12
  img.from_blob(input.read)
@@ -14,34 +14,53 @@ module MemeCaptain
14
14
  img.from_blob(input)
15
15
  end
16
16
 
17
- options = {
18
- :density => 144,
19
- :fill => 'white',
20
- :font => 'Impact-Regular',
21
- :gravity => Magick::CenterGravity,
22
- :size => "#{img.page.width * 1.8}x#{img.page.height / 2}",
23
- :stroke => 'black',
24
- :stroke_width => 2,
25
- :background_color => 'none',
26
- }.merge(options)
27
-
28
- line1_caption = Magick::Image.read("caption:#{line1.to_s.upcase}") {
29
- options.each { |k,v| self.send("#{k}=", v) }
30
- }
31
- line1_caption[0].resize!(0.5)
17
+ max_lines = 16
18
+ super_sample = 2.0
32
19
 
33
- line2_caption = Magick::Image.read("caption:#{line2.to_s.upcase}") {
34
- options.each { |k,v| self.send("#{k}=", v) }
35
- }
36
- line2_caption[0].resize!(0.5)
20
+ min_pointsize = 12 * super_sample
21
+ text_width = img.page.width * 0.9 * super_sample
22
+ text_height = img.page.height / 4.0 * super_sample
37
23
 
38
- text_layer = Magick::Image.new(img.page.width, img.page.height) {
24
+ text_layer = Magick::Image.new(
25
+ img.page.width * super_sample, img.page.height * super_sample) {
39
26
  self.background_color = 'none'
27
+ self.density = 144
40
28
  }
41
- text_layer.composite!(line1_caption[0], Magick::NorthGravity,
42
- Magick::OverCompositeOp)
43
- text_layer.composite!(line2_caption[0], Magick::SouthGravity,
44
- Magick::OverCompositeOp)
29
+
30
+ draw = Magick::Draw.new.extend(Draw)
31
+
32
+ draw.fill = 'white'
33
+ draw.font = 'Impact'
34
+
35
+ [
36
+ [Caption.new(top_text), Magick::NorthGravity],
37
+ [Caption.new(bottom_text), Magick::SouthGravity],
38
+ ].select { |x| x[0].drawable? }.each do |caption, gravity|
39
+ wrap_tries = (1..max_lines).map { |num_lines|
40
+ caption.wrap(num_lines).upcase.annotate_quote
41
+ }.uniq
42
+
43
+ choices = wrap_tries.map do |wrap_try|
44
+ pointsize, fits = draw.calc_pointsize(
45
+ text_width, text_height, wrap_try, min_pointsize)
46
+
47
+ CaptionChoice.new(fits, pointsize, wrap_try)
48
+ end
49
+
50
+ choice = choices.max
51
+
52
+ draw.gravity = gravity
53
+ draw.pointsize = choice.pointsize
54
+
55
+ draw.stroke = 'black'
56
+ draw.stroke_width = 8
57
+ draw.annotate text_layer, 0, 0, 0, 0, choice.text
58
+
59
+ draw.stroke = 'none'
60
+ draw.annotate text_layer, 0, 0, 0, 0, choice.text
61
+ end
62
+
63
+ text_layer.resize!(1 / super_sample)
45
64
 
46
65
  img.each do |frame|
47
66
  frame.composite!(text_layer, -frame.page.x, -frame.page.y,
@@ -13,6 +13,8 @@ module MemeCaptain
13
13
 
14
14
  ImageExts = %w{.jpeg .gif .png}
15
15
 
16
+ set :root, File.join(File.dirname(__FILE__), '..', '..')
17
+
16
18
  get '/' do
17
19
  @u = params[:u]
18
20
  @tt= params[:tt]
@@ -32,6 +34,9 @@ module MemeCaptain
32
34
  curl = Curl::Easy.perform(params[:u]) do |c|
33
35
  c.useragent = 'Meme Captain http://memecaptain.com/'
34
36
  end
37
+ unless curl.response_code == 200
38
+ raise "Error loading source image url #{params[:u]}"
39
+ end
35
40
  curl.body_str
36
41
  }
37
42
 
@@ -49,21 +54,23 @@ module MemeCaptain
49
54
  end
50
55
 
51
56
  get '/g' do
52
- content_type :json
53
-
54
- processed_cache_path = gen(params)
55
-
56
- temp_url = URI(request.url)
57
- temp_url.path = processed_cache_path.sub('public', '')
58
- temp_url.query = nil
59
-
60
- perm_url = URI(request.url)
61
- perm_url.path = '/i'
62
-
63
- {
64
- 'tempUrl' => temp_url.to_s,
65
- 'permUrl' => perm_url.to_s,
66
- }.to_json
57
+ begin
58
+ processed_cache_path = gen(params)
59
+
60
+ temp_url = URI(request.url)
61
+ temp_url.path = processed_cache_path.sub('public', '')
62
+ temp_url.query = nil
63
+
64
+ perm_url = URI(request.url)
65
+ perm_url.path = '/i'
66
+
67
+ [200, { 'Content-Type' => 'application/json' }, {
68
+ 'tempUrl' => temp_url.to_s,
69
+ 'permUrl' => perm_url.to_s,
70
+ }.to_json]
71
+ rescue => error
72
+ [500, { 'Content-Type' => 'text/plain' }, error.to_s]
73
+ end
67
74
  end
68
75
 
69
76
  get '/i' do
@@ -0,0 +1,3 @@
1
+ module MemeCaptain
2
+ VERSION = '0.0.7'
3
+ end
data/lib/meme_captain.rb CHANGED
@@ -1,5 +1,9 @@
1
+ require 'meme_captain/caption'
2
+ require 'meme_captain/caption_choice'
3
+ require 'meme_captain/draw'
1
4
  require 'meme_captain/file_body'
5
+ require 'meme_captain/filesystem_cache'
2
6
  require 'meme_captain/meme'
3
7
  require 'meme_captain/mime_type'
4
8
  require 'meme_captain/server'
5
- require 'meme_captain/filesystem_cache'
9
+ require 'meme_captain/version'
data/meme_captain.gemspec CHANGED
@@ -2,9 +2,12 @@
2
2
 
3
3
  $:.unshift(File.join(File.dirname(__FILE__), 'lib'))
4
4
 
5
+ require 'meme_captain/version'
6
+
5
7
  Gem::Specification.new do |s|
6
8
  s.name = 'meme_captain'
7
9
  s.version = '0.0.6'
10
+ s.version = MemeCaptain::VERSION
8
11
  s.summary = 'create meme images'
9
12
  s.description = s.summary
10
13
  s.homepage = 'https://github.com/mmb/meme_captain'
data/public/1.gif ADDED
Binary file
data/public/thumbs.jpg ADDED
Binary file
@@ -0,0 +1,20 @@
1
+ require 'RMagick'
2
+
3
+ # make a combined thumbnail image from a list of images for use in CSS sprites
4
+
5
+ puts 'images = ['
6
+
7
+ thumbs = Magick::ImageList.new
8
+
9
+ ARGV.sort.each do |file|
10
+ image = Magick::ImageList.new(file)
11
+ image.resize_to_fit!(0, 50)
12
+ image.each do |frame|
13
+ thumbs.push frame
14
+ puts " ['#{File.basename(file)}', #{frame.columns}],"
15
+ end
16
+ end
17
+
18
+ puts ']'
19
+
20
+ thumbs.append(false).write('thumbs.jpg')
data/views/index.erb CHANGED
@@ -1,16 +1,28 @@
1
1
  <!DOCTYPE html>
2
- <html lang="en">
2
+ <html lan="en" xmlns:fb="https://www.facebook.com/2008/fbml">
3
3
 
4
4
  <head>
5
5
  <title>Meme Captain</title>
6
6
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
7
+ <style type="text/css">
8
+ #sourceImages {
9
+ background-color : #eee;
10
+ border-top : 1px solid #ccc;
11
+ border-bottom : 1px solid #ccc;
12
+ padding : 0em 1em 0.5em 1em;
13
+ margin : 1em 0em 1em 0em;
14
+ }
15
+
16
+ img.thumb {
17
+ background-image : url('thumbs.jpg');
18
+ height : 50px;
19
+ }
20
+ </style>
7
21
  </head>
8
22
 
9
23
  <body>
10
24
 
11
- <p><a href="/">Meme Captain</a></p>
12
-
13
- <p>Add text to images from the internet.</p>
25
+ <p><a href="/">Meme Captain</a> - add text to images from the internet</p>
14
26
 
15
27
  <div id="img"></div>
16
28
 
@@ -42,45 +54,183 @@
42
54
 
43
55
  </form>
44
56
 
45
- <img src="http://memecaptain.com/bear_grylls_thumb.jpg" onClick="$('#u').val('http://memecaptain.com/bear_grylls.jpg'); return false;" />
46
-
47
- <img src="http://memecaptain.com/most_interesting_thumb.jpg" onClick="$('#u').val('http://memecaptain.com/most_interesting.jpg'); return false;" />
48
-
49
- <img src="http://memecaptain.com/philosoraptor_thumb.jpg" onClick="$('#u').val('http://memecaptain.com/philosoraptor.jpg'); return false;" />
50
-
51
- <img src="http://memecaptain.com/scumbag_steve_thumb.jpg" onClick="$('#u').val('http://memecaptain.com/scumbag_steve.jpg'); return false;" />
52
-
53
- <img src="http://memecaptain.com/town_crier_thumb.jpg" onClick="$('#u').val('http://memecaptain.com/town_crier.jpg'); return false;" />
57
+ <div id="sourceImages">
58
+
59
+ <p>Locally hosted source images or search Bing for source images (click thumbnail to use):</p>
60
+
61
+ <%
62
+
63
+ images = [
64
+ ['all_the_things.jpg', 67],
65
+ ['bear_grylls.jpg', 45],
66
+ ['business_cat.jpg', 50],
67
+ ['courage_wolf.jpg', 50],
68
+ ['dwight_schrute.jpg', 73],
69
+ ['fry.png', 67],
70
+ ['good_guy_greg.jpg', 51],
71
+ ['grandma.jpg', 68],
72
+ ['insanity_wolf.jpg', 50],
73
+ ['internet_husband.jpg', 45],
74
+ ['joseph_ducreux.jpg', 38],
75
+ ['me_gusta.png', 50],
76
+ ['most_interesting.jpg', 40],
77
+ ['ok.png', 50],
78
+ ['philosoraptor.jpg', 50],
79
+ ['rage.png', 50],
80
+ ['sap.jpg', 50],
81
+ ['scumbag_steve.jpg', 50],
82
+ ['seriously.png', 50],
83
+ ['slowpoke.jpg', 50],
84
+ ['success_kid.jpg', 50],
85
+ ['town_crier.jpg', 75],
86
+ ['troll_face.jpg', 55],
87
+ ['trolldad.png', 50],
88
+ ['trolldad_dancing.png', 50],
89
+ ['tyler_durden.jpg', 50],
90
+ ['xzibit.jpg', 77],
91
+ ['y_u_no.jpg', 67],
92
+ ['yao_ming.jpg', 42],
93
+ ]
94
+
95
+ pos = 0
96
+ images.each do |name, offset|
97
+ %>
98
+ <img src="1.gif" class="thumb" style="width : <%= offset %>px; background-position : <%= pos %>px 0px;" onClick="$('#u').val('http://memecaptain.com/<%= name %>'); return false;" />
99
+ <%
100
+ pos -= offset
101
+ end
102
+ %>
103
+
104
+ <div id="bingImageResults"></div>
54
105
 
55
- <img src="http://memecaptain.com/troll_face_thumb.jpg" onClick="$('#u').val('http://memecaptain.com/troll_face.jpg'); return false;" />
56
-
57
- <img src="http://memecaptain.com/yao_ming_thumb.jpg" onClick="$('#u').val('http://memecaptain.com/yao_ming.jpg'); return false;" />
106
+ <form action="" method="get">
107
+ <input type="text" id="bingSearch" />
108
+ <input type="button" id="bingButton" value="Bing Image Search" />
109
+ </form>
58
110
 
59
- <img src="http://memecaptain.com/y_u_no_thumb.jpg" onClick="$('#u').val('http://memecaptain.com/y_u_no.jpg'); return false;" />
111
+ </div>
60
112
 
61
113
  <p>by Matthew M. Boedicker <a href="mailto:matthewm@boedicker.org">matthewm@boedicker.org</a></p>
62
114
 
63
- <p><a href="https://github.com/mmb/meme_captain">source code</a></p>
115
+ <p><a href="https://github.com/mmb/meme_captain">source code and API</a></p>
64
116
 
65
117
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.0/jquery.min.js"></script>
66
118
  <script>
119
+ function showBingImages(resp) {
120
+ var div = $('#bingImageResults'),
121
+ searchResponse = resp.SearchResponse,
122
+ image = searchResponse.Image;
123
+
124
+ div.empty();
125
+
126
+ if (image.Total > 0) {
127
+ $.each(image.Results, function (i, img) {
128
+ div.append($('<img />').attr('src', img.Thumbnail.Url).click(
129
+ function () { $('#u').val(img.MediaUrl); }
130
+ ));
131
+ });
132
+ } else {
133
+ div.append($('<p />').append(
134
+ 'No results for "' + searchResponse.Query.SearchTerms + '".'));
135
+ }
136
+ }
137
+
138
+ function bingImageSearch() {
139
+ var bingSearch = $('#bingSearch'),
140
+ bingSearchVal = bingSearch.val();
141
+
142
+ if (bingSearchVal.match(/[^\s]/)) {
143
+ bingSearch.val('');
144
+
145
+ $.ajax({
146
+ type : 'GET',
147
+ url : 'http://api.bing.net/json.aspx',
148
+ data : {
149
+ AppId : 'A120380275E87F0071F163210211F0592D0E964C',
150
+ Query : bingSearchVal + ' imagesize:Medium',
151
+ Sources : 'Image',
152
+ Version : '2.0',
153
+ Market : 'en-us',
154
+ 'Image.Count' : 5,
155
+ 'Image.Offset' : 0,
156
+ JsonType : 'callback'
157
+ },
158
+ dataType : 'jsonp',
159
+ jsonpCallback : 'showBingImages',
160
+ jsonp : 'JsonCallback'
161
+ });
162
+ }
163
+ }
164
+
67
165
  $(function () {
166
+ var bingSearch,
167
+ bingSearchVal,
168
+ imgDiv;
169
+
68
170
  if (window.location.search.match(/u=[^&$]/)) {
69
- var imgDiv = $('#img');
171
+ $('#tt').focus();
172
+
173
+ imgDiv = $('#img');
70
174
 
71
175
  imgDiv.append($('<p />').append('Creating image ...'));
72
176
 
73
- $.get('/g' + window.location.search, function(data) {
177
+ $.get('/g' + window.location.search, function (data) {
74
178
  var img = $('<img />').attr('src', data.tempUrl),
75
179
  tempLink = $('<a />').attr('href', data.tempUrl).append(data.tempUrl),
76
- permLink = $('<a />').attr('href', data.permUrl).append(data.permUrl);
180
+ permLink = $('<a />').attr('href', data.permUrl).append(data.permUrl),
181
+ tweetLink = $('<a />').attr({
182
+ href : 'http://twitter.com/share',
183
+ 'class' : 'twitter-share-button',
184
+ 'data-count' : 'none',
185
+ 'data-text' : ' ',
186
+ 'data-url' : data.permUrl
187
+ }).append('Tweet');
77
188
 
78
189
  imgDiv.empty().append(
79
190
  img).append(
80
191
  $('<p />').append('Temporary image url: ').append(tempLink)).append(
81
- $('<p />').append('Permanent image url: ').append(permLink));
192
+ $('<p />').append('Permanent image url: ').append(permLink)).append(
193
+ $('<p />').append('Temporary url not guaranteed to work forever. Permanent url should work as long as source image exists.')).append(
194
+ $('<p />').append(tweetLink).append($('<script />').attr('src',
195
+ 'http://platform.twitter.com/widgets.js')));
196
+
197
+ // Facebook like
198
+ imgDiv.append(
199
+ $('<div />').attr('id', 'fb-root')).append(
200
+ $('<fb:like />').attr({
201
+ href : data.tempUrl,
202
+ send : 'true',
203
+ width : '450',
204
+ show_faces : 'true',
205
+ font : ''
206
+ }));
207
+
208
+ window.fbAsyncInit = function () {
209
+ FB.init({
210
+ appId : '108445492580525',
211
+ status : true,
212
+ cookie : true,
213
+ xfbml : true
214
+ });
215
+ };
216
+
217
+ imgDiv.append($('<script />').attr({
218
+ src : 'http://connect.facebook.net/en_US/all.js'}));
219
+ }).error(function (j) {
220
+ imgDiv.empty().append($('<p />').text(j.responseText));
82
221
  });
222
+ } else {
223
+ $('#u').focus();
83
224
  }
225
+
226
+ $('#bingSearch').keypress(function (event) {
227
+ if (event.which == 13) {
228
+ event.preventDefault();
229
+ bingImageSearch();
230
+ }
231
+ });
232
+
233
+ $('#bingButton').click(bingImageSearch);
84
234
  });
85
235
  </script>
86
236
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: meme_captain
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 17
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 6
10
- version: 0.0.6
9
+ - 7
10
+ version: 0.0.7
11
11
  platform: ruby
12
12
  authors:
13
13
  - Matthew M. Boedicker
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-05-15 00:00:00 -04:00
18
+ date: 2011-11-08 00:00:00 -05:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -114,17 +114,24 @@ extra_rdoc_files: []
114
114
  files:
115
115
  - .gitignore
116
116
  - COPYING
117
- - README.textile
117
+ - README.md
118
118
  - config.ru
119
119
  - img_cache/source/.gitignore
120
120
  - lib/meme_captain.rb
121
+ - lib/meme_captain/caption.rb
122
+ - lib/meme_captain/caption_choice.rb
123
+ - lib/meme_captain/draw.rb
121
124
  - lib/meme_captain/file_body.rb
122
125
  - lib/meme_captain/filesystem_cache.rb
123
126
  - lib/meme_captain/meme.rb
124
127
  - lib/meme_captain/mime_type.rb
125
128
  - lib/meme_captain/server.rb
129
+ - lib/meme_captain/version.rb
126
130
  - meme_captain.gemspec
131
+ - public/1.gif
132
+ - public/thumbs.jpg
127
133
  - public/tmp/.gitignore
134
+ - script/thumb_sprites.rb
128
135
  - views/index.erb
129
136
  has_rdoc: true
130
137
  homepage: https://github.com/mmb/meme_captain
data/README.textile DELETED
@@ -1,20 +0,0 @@
1
- Ruby gem to create meme images (images with text added at the top and bottom).
2
-
3
- Also includes Sinatra app that exposes API over HTTP which is currently
4
- running "http://memecaptain.com/":http://memecaptain.com/
5
-
6
- Works with animated gifs.
7
-
8
- <pre>
9
- <code>
10
- require 'meme_captain'
11
-
12
- require 'open-uri'
13
-
14
- open('http://image_from_web_or_local_file.jpg', 'rb') do |f|
15
- i = MemeCaptain.meme(f, 'test', '1 2 3')
16
- i.display
17
- i.write('out.jpg')
18
- end
19
- </code>
20
- </pre>