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 +55 -0
- data/lib/meme_captain/caption.rb +45 -0
- data/lib/meme_captain/caption_choice.rb +33 -0
- data/lib/meme_captain/draw.rb +35 -0
- data/lib/meme_captain/filesystem_cache.rb +3 -0
- data/lib/meme_captain/meme.rb +44 -25
- data/lib/meme_captain/server.rb +22 -15
- data/lib/meme_captain/version.rb +3 -0
- data/lib/meme_captain.rb +5 -1
- data/meme_captain.gemspec +3 -0
- data/public/1.gif +0 -0
- data/public/thumbs.jpg +0 -0
- data/script/thumb_sprites.rb +20 -0
- data/views/index.erb +172 -22
- metadata +12 -5
- data/README.textile +0 -20
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
|
+

|
|
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|
|
data/lib/meme_captain/meme.rb
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
18
|
-
|
|
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
|
-
|
|
34
|
-
|
|
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(
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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,
|
data/lib/meme_captain/server.rb
CHANGED
|
@@ -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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
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/
|
|
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
|
|
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
|
|
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
|
-
<
|
|
46
|
-
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
<
|
|
56
|
-
|
|
57
|
-
<
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
4
|
+
hash: 17
|
|
5
5
|
prerelease:
|
|
6
6
|
segments:
|
|
7
7
|
- 0
|
|
8
8
|
- 0
|
|
9
|
-
-
|
|
10
|
-
version: 0.0.
|
|
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-
|
|
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.
|
|
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>
|