@@ -9,6 +9,13 @@ let https = require('https');
9
9
let fs = require ( 'fs' ) ;
10
10
let crypto = require ( 'crypto' ) ;
11
11
let sh = require ( 'shorthash' ) ;
12
+ // http://stackoverflow.com/a/17133012
13
+ // Must set encoding to null for the response body type to be buffer
14
+ let request = require ( 'request' ) . defaults ( { encoding : null } ) ;
15
+
16
+ let Canvas = require ( 'canvas' ) ;
17
+ let Image = Canvas . Image ;
18
+
12
19
13
20
let firebase = require ( './firebase' ) ;
14
21
let GCloud = require ( 'gcloud' ) ;
@@ -66,13 +73,34 @@ var MemeNG = function (config) {
66
73
// console.log('Storage bucket: ');
67
74
// console.log(this.storage);
68
75
76
+ // meme image canvas attributes
77
+ this . canvasWidth = 700 ;
78
+ this . canvasHeight = 700 ;
79
+
80
+ this . memeWidth = this . canvasWidth ;
81
+ this . memeHeight = this . canvasHeight ;
82
+
83
+ this . canvas = new Canvas ( this . canvasWidth , this . canvasHeight ) ;
84
+ this . ctx = this . canvas . getContext ( '2d' ) ;
85
+
86
+ this . canvasImg = new Image ( ) ;
87
+
69
88
return this ;
70
89
} ;
71
90
91
+ /**
92
+ * Get data for all the available memes
93
+ * @returns {!firebase.Promise.<*>|firebase.Promise<any> }
94
+ */
72
95
MemeNG . prototype . getMemes = function ( ) {
73
96
return this . database . ref ( '/memes' ) . once ( 'value' ) ;
74
97
} ;
75
98
99
+ /**
100
+ * Get the details of the meme with the specified id
101
+ * @param id
102
+ * @returns {!firebase.Promise.<*>|firebase.Thenable<any>|firebase.Promise<any> }
103
+ */
76
104
MemeNG . prototype . getMemeDetails = function ( id ) {
77
105
return this . database . ref ( '/memes/' + id ) . once ( 'value' ) . then ( function ( snapshot ) {
78
106
return snapshot . val ( ) ;
@@ -81,18 +109,46 @@ MemeNG.prototype.getMemeDetails = function (id) {
81
109
82
110
/**
83
111
* returns the URL of the generated meme
112
+ * @version 1.0
84
113
* @param opts.id
85
114
* @param opts.top
86
115
* @param opts.bottom
87
116
*
88
117
*/
89
- MemeNG . prototype . createMeme = function ( opts ) {
118
+ MemeNG . prototype . createMemeLink = function ( opts ) {
90
119
var id = opts . id ;
91
120
var topText = opts . top ;
92
121
var bottomText = opts . bottom ;
93
122
94
123
var that = this ;
95
124
125
+ return new Promise ( function ( resolve , reject ) {
126
+ that . getMemeTemplateURL ( id ) . then ( function ( imageURL ) {
127
+
128
+ var url = 'https://memegen.link/custom/<top>/<bottom>.jpg?alt=' + encodeURIComponent ( imageURL ) ;
129
+
130
+ // var url = 'https://memegen.link/api/templates/' + id + '/' + MemeNG.encodeMemeText(topText) + '/' + MemeNG.encodeMemeText(bottomText) + '';
131
+ url = url
132
+ . replace ( '<top>' , MemeNG . encodeMemeText ( topText ) || '' )
133
+ . replace ( '<bottom>' , MemeNG . encodeMemeText ( bottomText ) || '' ) ;
134
+
135
+ var result = {
136
+ url : url ,
137
+ opts : opts
138
+ } ;
139
+ resolve ( result ) ;
140
+ } ) . catch ( reject ) ;
141
+ } ) ;
142
+
143
+ } ;
144
+
145
+ /**
146
+ * Generate a valid URL for the meme template
147
+ * @param id
148
+ * @returns {Promise }
149
+ */
150
+ MemeNG . prototype . getMemeTemplateURL = function ( id ) {
151
+ var that = this ;
96
152
return new Promise ( function ( resolve , reject ) {
97
153
// If the meme doesn't exist, don't continue
98
154
// if(!this.memeExists(id))return false;
@@ -132,23 +188,11 @@ MemeNG.prototype.createMeme = function (opts) {
132
188
return false ;
133
189
}
134
190
135
- var url = 'https://memegen.link/custom/<top>/<bottom>.jpg?alt=' + encodeURIComponent ( imageURL ) ;
136
-
137
- // var url = 'https://memegen.link/api/templates/' + id + '/' + MemeNG.encodeMemeText(topText) + '/' + MemeNG.encodeMemeText(bottomText) + '';
138
- url = url
139
- . replace ( '<top>' , MemeNG . encodeMemeText ( topText ) || 'top' )
140
- . replace ( '<bottom>' , MemeNG . encodeMemeText ( bottomText ) || 'bottom' ) ;
141
-
142
- var result = {
143
- url : url ,
144
- opts : opts
145
- } ;
146
- resolve ( result ) ;
191
+ resolve ( imageURL ) ;
147
192
} ) ;
148
193
} ) ;
149
194
} ) ;
150
195
} ) ;
151
-
152
196
} ;
153
197
154
198
/**
@@ -169,6 +213,52 @@ MemeNG.prototype.authenticate = function (email, password) {
169
213
} ) ;
170
214
} ;
171
215
216
+ MemeNG . prototype . generateMemeCanvas = function ( config ) {
217
+ var defaults = {
218
+ top : '' ,
219
+ bottom : '' ,
220
+ imageURL : ''
221
+ } ;
222
+ var opts = Object . assign ( { } , defaults , config ) ;
223
+
224
+ var that = this ;
225
+
226
+ console . log ( opts ) ;
227
+
228
+ return new Promise ( function ( resolve , reject ) {
229
+ request . get ( opts . imageURL , function ( err , response , body ) {
230
+
231
+ if ( ! err && response . statusCode == 200 ) {
232
+ that . canvasImg . src = new Buffer ( body ) ;
233
+ console . log ( 'image loaded.' , new Buffer ( body ) ) ;
234
+ that . calculateCanvasSize ( ) ;
235
+ that . drawMeme ( opts . top , opts . bottom ) ;
236
+ resolve ( that . canvas . toBuffer ( ) ) ;
237
+ }
238
+ else {
239
+ reject ( new Error ( 'The image could not be loaded.' ) ) ;
240
+ }
241
+ } ) ;
242
+ } ) ;
243
+ } ;
244
+
245
+ MemeNG . prototype . createMeme = function ( opts ) {
246
+ var id = opts . id ;
247
+ var topText = opts . top ;
248
+ var bottomText = opts . bottom ;
249
+
250
+ var that = this ;
251
+
252
+ return new Promise ( function ( resolve , reject ) {
253
+ that . getMemeTemplateURL ( id ) . then ( function ( imageURL ) {
254
+ opts . imageURL = imageURL ;
255
+ that . generateMemeCanvas ( opts ) . then ( ( memeURL ) => {
256
+ resolve ( memeURL ) ;
257
+ } ) ;
258
+ } ) . catch ( ( err ) => { reject ( new Error ( 'Issue encountered generating template URL.' , err . message ) ) } ) ;
259
+ } ) ;
260
+ } ;
261
+
172
262
/**
173
263
* encodes the text using the prescribed format from memegen.link
174
264
* @param text
@@ -188,6 +278,11 @@ MemeNG.encodeMemeText = function (text) {
188
278
. replace ( / " / g, "''" ) ;
189
279
} ;
190
280
281
+ /**
282
+ * Downloads the file in the specified URL and returns the local URL
283
+ * @param url
284
+ * @returns {Promise }
285
+ */
191
286
MemeNG . downloadFile = function ( url ) {
192
287
193
288
// let memeHash = crypto.createHash('md5').update(url).digest("hex");
@@ -207,7 +302,130 @@ MemeNG.downloadFile = function (url) {
207
302
208
303
} ;
209
304
305
+ MemeNG . prototype . calculateCanvasSize = function ( ) {
306
+ console . log ( this . canvasImg . width , this . canvasImg . height ) ;
307
+ if ( this . canvasImg . width > this . canvasImg . height ) {
308
+ this . canvas . height = this . canvasImg . height / this . canvasImg . width * this . canvas . width ;
309
+ this . memeWidth = this . canvas . width ;
310
+ this . memeHeight = this . canvas . height ;
311
+ console . log ( this . memeWidth , this . memeHeight ) ;
312
+ }
313
+ return { width : this . memeWidth , height : this . memeHeight } ;
314
+ } ;
315
+
316
+ MemeNG . prototype . drawMeme = function ( topText , bottomText ) {
317
+ this . ctx . clearRect ( 0 , 0 , this . canvas . width , this . canvas . height ) ;
318
+
319
+ this . ctx . drawImage ( this . canvasImg , 0 , 0 , this . memeWidth , this . memeHeight ) ;
320
+
321
+ this . ctx . lineWidth = 8 ;
322
+ this . ctx . font = 'bold 50pt Impact' ;
323
+ this . ctx . strokeStyle = 'black' ;
324
+ this . ctx . mutterLine = 2 ;
325
+ this . ctx . lineJoin = "miter" ; //Experiment with "bevel" & "round" for the effect you want!
326
+ this . ctx . miterLimit = 3 ;
327
+ this . ctx . fillStyle = 'white' ;
328
+ this . ctx . textAlign = 'center' ;
329
+ this . ctx . textBaseline = 'top' ;
330
+
331
+ topText = topText . toUpperCase ( ) ;
332
+ var x = this . memeWidth / 2 ;
333
+ var y = 0 ;
334
+
335
+ this . writeTextOnCanvas ( {
336
+ text : topText ,
337
+ x : x ,
338
+ y : y ,
339
+ maxWidth : this . memeWidth ,
340
+ lineHeightRatio : 1.6 ,
341
+ fromBottom : false ,
342
+ fontSize : 50
343
+ } ) ;
210
344
345
+ this . ctx . textBaseline = 'bottom' ;
346
+ bottomText = bottomText . toUpperCase ( ) ;
347
+ y = this . memeHeight ;
348
+
349
+ this . writeTextOnCanvas ( {
350
+ text : bottomText ,
351
+ x : x ,
352
+ y : y ,
353
+ maxWidth : this . memeWidth ,
354
+ lineHeightRatio : 1.6 ,
355
+ fromBottom : true ,
356
+ fontSize : 50
357
+ } ) ;
358
+ } ;
359
+
360
+ MemeNG . prototype . writeTextOnCanvas = function ( config ) {
361
+ var defaults = {
362
+ text : 'MemeNG' ,
363
+ x : 0 ,
364
+ y : 0 ,
365
+ maxWidth : 1000 ,
366
+ lineHeightRatio : 2 ,
367
+ fromBottom : false ,
368
+ fontSize : 50 ,
369
+ maxLines : 2
370
+ } ;
371
+ var opts = Object . assign ( { } , defaults , config ) ;
372
+
373
+ this . ctx . font = 'bold ' + opts . fontSize + 'pt Impact' ;
374
+
375
+ // If from the bottom, use unshift so the lines can be added to the top of the array.
376
+ // Required since the lines at the bottom are laid out from bottom up.
377
+ var pushMethod = ( opts . fromBottom ) ? 'unshift' : 'push' ;
378
+
379
+ var _lineHeightRatio = ( opts . fromBottom ) ? - opts . lineHeightRatio : opts . lineHeightRatio ;
380
+ var lineHeight = _lineHeightRatio * opts . fontSize ;
381
+
382
+ console . log ( 'lineH' , lineHeight , opts . lineHeightRatio , opts . fontSize ) ;
383
+
384
+ var lines = [ ] ;
385
+ var line = '' ;
386
+ var words = opts . text . split ( ' ' ) ;
387
+
388
+ for ( var n = 0 , len = words . length ; n < len ; n ++ ) {
389
+ // Create a line by adding the words one after the other
390
+ var testLine = line + ' ' + words [ n ] ;
391
+
392
+ // Get the width of the line after adding the current word
393
+ var metrics = this . ctx . measureText ( testLine ) ;
394
+ var testWidth = metrics . width ;
395
+
396
+ // Then checking the line width to see if it is more than the specified maxWidth
397
+ if ( testWidth > opts . maxWidth ) {
398
+ lines [ pushMethod ] ( line ) ;
399
+ line = words [ n ] + ' ' ;
400
+ } else {
401
+ line = testLine ;
402
+ }
403
+ }
404
+ // Don't forget to add the last line after the loop
405
+ lines [ pushMethod ] ( line ) ;
406
+
407
+ // Check to make sure the number of lines isn't more than the maximum number of lines
408
+ // If it is, then reduce the font size and try again
409
+ if ( lines . length > opts . maxLines ) {
410
+ console . log ( 'Too big.' , opts . fontSize ) ;
411
+ this . writeTextOnCanvas ( {
412
+ text : opts . text ,
413
+ x : opts . x ,
414
+ y : opts . y ,
415
+ maxWidth : opts . maxWidth ,
416
+ lineHeightRatio : opts . lineHeightRatio ,
417
+ fromBottom : opts . fromBottom ,
418
+ fontSize : opts . fontSize - 10
419
+ } ) ;
420
+ }
421
+ // If it isn't, then write the text
422
+ else {
423
+ for ( var k in lines ) {
424
+ this . ctx . strokeText ( lines [ k ] , opts . x , opts . y + lineHeight * k ) ;
425
+ this . ctx . fillText ( lines [ k ] , opts . x , opts . y + lineHeight * k ) ;
426
+ }
427
+ }
428
+ } ;
211
429
//---------
212
430
/*
213
431
Meme model
0 commit comments