Project

General

Profile

1
<!---
2
	image.cfc v2.19, written by Rick Root (rick@webworksllc.com)
3
	Derivative of work originally done originally by James Dew.
4

    
5
	Related Web Sites:
6
	- http://www.opensourcecf.com/imagecfc (home page)
7
	- http://www.cfopen.org/projects/imagecfc (project page)
8

    
9
	LICENSE
10
	-------
11
	Copyright (c) 2007, Rick Root <rick@webworksllc.com>
12
	All rights reserved.
13

    
14
	Redistribution and use in source and binary forms, with or
15
	without modification, are permitted provided that the
16
	following conditions are met:
17

    
18
	- Redistributions of source code must retain the above
19
	  copyright notice, this list of conditions and the
20
	  following disclaimer.
21
	- Redistributions in binary form must reproduce the above
22
	  copyright notice, this list of conditions and the
23
	  following disclaimer in the documentation and/or other
24
	  materials provided with the distribution.
25
	- Neither the name of the Webworks, LLC. nor the names of
26
	  its contributors may be used to endorse or promote products
27
	  derived from this software without specific prior written
28
	  permission.
29

    
30
	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
31
	CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
32
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
33
	MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
34
	DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
35
	CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
36
	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
37
	BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
38
	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39
	HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
40
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
41
	OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
42
	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43

    
44
	============================================================
45
	This is a derivative work.  Following is the original
46
	Copyright notice.
47
	============================================================
48

    
49
	Copyright (c) 2004 James F. Dew <jdew@yggdrasil.ca>
50

    
51
	Permission to use, copy, modify, and distribute this software for any
52
	purpose with or without fee is hereby granted, provided that the above
53
	copyright notice and this permission notice appear in all copies.
54

    
55
	THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
56
	WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
57
	MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
58
	ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
59
	WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
60
	ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
61
	OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
62
--->
63
<!---
64
	SPECIAL NOTE FOR HEADLESS SYSTEMS
65
	---------------------------------
66
	If you get a "cannot connect to X11 server" when running certain
67
	parts of this component under Bluedragon (Linux), you must
68
	add "-Djava.awt.headless=true" to the java startup line in
69
	<bluedragon>/bin/StartBluedragon.sh.  This isssue is discussed
70
	in the Bluedragon Installation Guide section 3.8.1 for
71
	Bluedragon 6.2.1.
72

    
73
	Bluedragon may also report a ClassNotFound exception when trying
74
	to instantiate the java.awt.image.BufferedImage class.  This is
75
	most likely the same issue.
76

    
77
	If you get "This graphics environment can be used only in the
78
	software emulation mode" when calling certain parts of this
79
	component under Coldfusion MX, you should refer to Technote
80
	ID #18747:  http://www.macromedia.com/go/tn_18747
81
--->
82

    
83
<cfcomponent displayname="Image">
84

    
85
<cfset variables.throwOnError = "Yes">
86
<cfset variables.defaultJpegCompression = "90">
87
<cfset variables.interpolation = "bicubic">
88
<cfset variables.textAntiAliasing = "Yes">
89
<cfset variables.tempDirectory = "#expandPath(".")#">
90

    
91
<cfset variables.javanulls = "no">
92
<cftry>
93
	<cfset nullvalue = javacast("null","")>
94
	<cfset variables.javanulls = "yes">
95
	<cfcatch type="any">
96
		<cfset variables.javanulls = "no">
97
		<!--- javacast null not supported, so filters won't work --->
98
	</cfcatch>
99
</cftry>
100
<!---
101
<cfif javanulls>
102
	<cfset variables.blurFilter = createObject("component","blurFilter")>
103
	<cfset variables.sharpenFilter = createObject("component","sharpenFilter")>
104
	<cfset variables.posterizeFilter = createObject("component","posterizeFilter")>
105
</cfif>
106
--->
107

    
108
<cfset variables.Math = createobject("java", "java.lang.Math")>
109
<cfset variables.arrObj = createobject("java", "java.lang.reflect.Array")>
110
<cfset variables.floatClass = createobject("java", "java.lang.Float").TYPE>
111
<cfset variables.intClass = createobject("java", "java.lang.Integer").TYPE>
112
<cfset variables.shortClass = createobject("java", "java.lang.Short").TYPE>
113

    
114
<cffunction name="getImageInfo" access="public" output="true" returntype="struct" hint="Rotate an image (+/-)90, (+/-)180, or (+/-)270 degrees.">
115
	<cfargument name="objImage" required="yes" type="Any">
116
	<cfargument name="inputFile" required="yes" type="string">
117

    
118
	<cfset var retVal = StructNew()>
119
	<cfset var loadImage = StructNew()>
120
	<cfset var img = "">
121

    
122
	<cfset retVal.errorCode = 0>
123
	<cfset retVal.errorMessage = "">
124

    
125
	<cfif inputFile neq "">
126
		<cfset loadImage = readImage(inputFile, "NO")>
127
		<cfif loadImage.errorCode is 0>
128
			<cfset img = loadImage.img>
129
		<cfelse>
130
			<cfset retVal = throw(loadImage.errorMessage)>
131
			<cfreturn retVal>
132
		</cfif>
133
		<cfset retVal.metaData = getImageMetadata(loadImage.inFile)>
134
	<cfelse>
135
		<cfset img = objImage>
136
		<cfset retVal.metadata = getImageMetadata("")>
137
	</cfif>
138
	<cftry>
139
		<cfset retVal.width = img.getWidth()>
140
		<cfset retVal.height = img.getHeight()>
141
		<cfset retVal.colorModel = img.getColorModel().toString()>
142
		<cfset retVal.colorspace = img.getColorModel().getColorSpace().toString()>
143
		<cfset retVal.objColorModel = img.getColorModel()>
144
		<cfset retVal.objColorspace = img.getColorModel().getColorSpace()>
145
		<cfset retVal.sampleModel = img.getSampleModel().toString()>
146
		<cfset retVal.imageType = img.getType()>
147
		<cfset retVal.misc = img.toString()>
148
		<cfset retVal.canModify = true>
149
		<cfreturn retVal>
150
		<cfcatch type="any">
151
			<cfset retVal = throw( "#cfcatch.message#: #cfcatch.detail#")>
152
			<cfreturn retVal>
153
		</cfcatch>
154
	</cftry>
155
</cffunction>
156

    
157
<cffunction name="getImageMetadata" access="private" output="false" returntype="query">
158
	<cfargument name="inFile" required="yes" type="Any"><!--- java.io.File --->
159

    
160
	<cfset var retQry = queryNew("dirName,tagName,tagValue")>
161
	<cfset var paths = arrayNew(1)>
162
	<cfset var loader = "">
163
	<cfset var JpegMetadatareader = "">
164
	<cfset var myMetadata = "">
165
	<cfset var directories = "">
166
	<cfset var currentDirectory = "">
167
	<cfset var tags = "">
168
	<cfset var currentTag = "">
169
	<cfset var tagName = "">
170

    
171
	<cftry>
172
	<cfscript>
173
		paths = arrayNew(1);
174
		paths[1] = expandPath("metadata-extractor-2.3.1.jar");
175
		loader = createObject("component", "javaloader.JavaLoader").init(paths);
176

    
177
		//at this stage we only have access to the class, but we don't have an instance
178
		JpegMetadataReader = loader.create("com.drew.imaging.jpeg.JpegMetadataReader");
179

    
180
		myMetaData = JpegMetadataReader.readMetadata(inFile);
181
		directories = myMetaData.getDirectoryIterator();
182
		while (directories.hasNext()) {
183
			currentDirectory = directories.next();
184
			tags = currentDirectory.getTagIterator();
185
			while (tags.hasNext()) {
186
				currentTag = tags.next();
187
				if (currentTag.getTagName() DOES NOT CONTAIN "Unknown") { //leave out the junk data
188
					queryAddRow(retQry);
189
					querySetCell(retQry,"dirName",replace(currentTag.getDirectoryName(),' ','_','ALL'));
190
					tagName = replace(currentTag.getTagName(),' ','','ALL');
191
					tagName = replace(tagName,'/','','ALL');
192
					querySetCell(retQry,"tagName",tagName);
193
					querySetCell(retQry,"tagValue",currentTag.getDescription());
194
				}
195
			}
196
		}
197
		return retQry;
198
		</cfscript>
199
		<cfcatch type="any">
200
			<cfreturn retQry />
201
		</cfcatch>
202
	</cftry>
203
</cffunction>
204

    
205
<cffunction name="flipHorizontal" access="public" output="true" returntype="struct" hint="Flip an image horizontally.">
206
	<cfargument name="objImage" required="yes" type="Any">
207
	<cfargument name="inputFile" required="yes" type="string">
208
	<cfargument name="outputFile" required="yes" type="string">
209
	<cfargument name="jpegCompression" required="no" type="numeric" default="#variables.defaultJpegCompression#">
210

    
211
	<cfreturn flipflop(objImage, inputFile, outputFile, "horizontal", jpegCompression)>
212
</cffunction>
213

    
214
<cffunction name="flipVertical" access="public" output="true" returntype="struct" hint="Flop an image vertically.">
215
	<cfargument name="objImage" required="yes" type="Any">
216
	<cfargument name="inputFile" required="yes" type="string">
217
	<cfargument name="outputFile" required="yes" type="string">
218
	<cfargument name="jpegCompression" required="no" type="numeric" default="#variables.defaultJpegCompression#">
219

    
220
	<cfreturn flipflop(objImage, inputFile, outputFile, "vertical", jpegCompression)>
221
</cffunction>
222

    
223
<cffunction name="scaleWidth" access="public" output="true" returntype="struct" hint="Scale an image to a specific width.">
224
	<cfargument name="objImage" required="yes" type="Any">
225
	<cfargument name="inputFile" required="yes" type="string">
226
	<cfargument name="outputFile" required="yes" type="string">
227
	<cfargument name="newWidth" required="yes" type="numeric">
228
	<cfargument name="jpegCompression" required="no" type="numeric" default="#variables.defaultJpegCompression#">
229

    
230
	<cfreturn resize(objImage, inputFile, outputFile, newWidth, 0, "false", "false", jpegCompression)>
231
</cffunction>
232

    
233
<cffunction name="scaleHeight" access="public" output="true" returntype="struct" hint="Scale an image to a specific height.">
234
	<cfargument name="objImage" required="yes" type="Any">
235
	<cfargument name="inputFile" required="yes" type="string">
236
	<cfargument name="outputFile" required="yes" type="string">
237
	<cfargument name="newHeight" required="yes" type="numeric">
238
	<cfargument name="jpegCompression" required="no" type="numeric" default="#variables.defaultJpegCompression#">
239

    
240
	<cfreturn resize(objImage, inputFile, outputFile, 0, newHeight, "false", "false", jpegCompression)>
241
</cffunction>
242

    
243
<cffunction name="resize" access="public" output="true" returntype="struct" hint="Resize an image to a specific width and height.">
244
	<cfargument name="objImage" required="yes" type="Any">
245
	<cfargument name="inputFile" required="yes" type="string">
246
	<cfargument name="outputFile" required="yes" type="string">
247
	<cfargument name="newWidth" required="yes" type="numeric">
248
	<cfargument name="newHeight" required="yes" type="numeric">
249
	<cfargument name="preserveAspect" required="no" type="boolean" default="FALSE">
250
	<cfargument name="cropToExact" required="no" type="boolean" default="FALSE">
251
	<cfargument name="jpegCompression" required="no" type="numeric" default="#variables.defaultJpegCompression#">
252

    
253
	<cfset var retVal = StructNew()>
254
	<cfset var loadImage = StructNew()>
255
	<cfset var saveImage = StructNew()>
256
	<cfset var at = "">
257
	<cfset var op = "">
258
	<cfset var w = "">
259
	<cfset var h = "">
260
	<cfset var scale = 1>
261
	<cfset var scaleX = 1>
262
	<cfset var scaleY = 1>
263
	<cfset var resizedImage = "">
264
	<cfset var rh = getRenderingHints()>
265
	<cfset var specifiedWidth = arguments.newWidth>
266
	<cfset var specifiedHeight = arguments.newHeight>
267
	<cfset var imgInfo = "">
268
	<cfset var img = "">
269
	<cfset var cropImageResult = "">
270
	<cfset var cropOffsetX = "">
271
	<cfset var cropOffsetY = "">
272

    
273
	<cfset retVal.errorCode = 0>
274
	<cfset retVal.errorMessage = "">
275

    
276
	<cfif inputFile neq "">
277
		<cfset loadImage = readImage(inputFile, "NO")>
278
		<cfif loadImage.errorCode is 0>
279
			<cfset img = loadImage.img>
280
		<cfelse>
281
			<cfset retVal = throw(loadImage.errorMessage)>
282
			<cfreturn retVal>
283
		</cfif>
284
	<cfelse>
285
		<cfset img = objImage>
286
	</cfif>
287
	<cfif img.getType() eq 0>
288
		<cfset img = convertImageObject(img,img.TYPE_3BYTE_BGR)>
289
	</cfif>
290
	<cfscript>
291
		resizedImage = CreateObject("java", "java.awt.image.BufferedImage");
292
		at = CreateObject("java", "java.awt.geom.AffineTransform");
293
		op = CreateObject("java", "java.awt.image.AffineTransformOp");
294

    
295
		w = img.getWidth();
296
		h = img.getHeight();
297

    
298
		if (preserveAspect and cropToExact and newHeight gt 0 and newWidth gt 0)
299
		{
300
			if (w / h gt newWidth / newHeight){
301
				newWidth = 0;
302
			} else if (w / h lt newWidth / newHeight){
303
				newHeight = 0;
304
		    }
305
		} else if (preserveAspect and newHeight gt 0 and newWidth gt 0) {
306
			if (w / h gt newWidth / newHeight){
307
				newHeight = 0;
308
			} else if (w / h lt newWidth / newHeight){
309
				newWidth = 0;
310
		    }
311
		}
312
		if (newWidth gt 0 and newHeight eq 0) {
313
			scale = newWidth / w;
314
			w = newWidth;
315
			h = round(h*scale);
316
		} else if (newHeight gt 0 and newWidth eq 0) {
317
			scale = newHeight / h;
318
			h = newHeight;
319
			w = round(w*scale);
320
		} else if (newHeight gt 0 and newWidth gt 0) {
321
			w = newWidth;
322
			h = newHeight;
323
		} else {
324
			retVal = throw( retVal.errorMessage);
325
			return retVal;
326
		}
327
		resizedImage.init(javacast("int",w),javacast("int",h),img.getType());
328

    
329
		w = w / img.getWidth();
330
		h = h / img.getHeight();
331

    
332

    
333

    
334
		op.init(at.getScaleInstance(javacast("double",w),javacast("double",h)), rh);
335
		// resizedImage = op.createCompatibleDestImage(img, img.getColorModel());
336
		op.filter(img, resizedImage);
337

    
338
		imgInfo = getimageinfo(resizedImage, "");
339
		if (imgInfo.errorCode gt 0)
340
		{
341
			return imgInfo;
342
		}
343

    
344
		cropOffsetX = max( Int( (imgInfo.width/2) - (newWidth/2) ), 0 );
345
		cropOffsetY = max( Int( (imgInfo.height/2) - (newHeight/2) ), 0 );
346
		// There is a chance that the image is exactly the correct
347
		// width and height and don't need to be cropped
348
		if
349
			(
350
			preserveAspect and cropToExact
351
			and
352
			(imgInfo.width IS NOT specifiedWidth OR imgInfo.height IS NOT specifiedHeight)
353
			)
354
		{
355
			// Get the correct offset to get the center of the image
356
			cropOffsetX = max( Int( (imgInfo.width/2) - (specifiedWidth/2) ), 0 );
357
			cropOffsetY = max( Int( (imgInfo.height/2) - (specifiedHeight/2) ), 0 );
358

    
359
			cropImageResult = crop( resizedImage, "", "", cropOffsetX, cropOffsetY, specifiedWidth, specifiedHeight );
360
			if ( cropImageResult.errorCode GT 0)
361
			{
362
				return cropImageResult;
363
			} else {
364
				resizedImage = cropImageResult.img;
365
			}
366
		}
367
		if (outputFile eq "")
368
		{
369
			retVal.img = resizedImage;
370
			return retVal;
371
		} else {
372
			saveImage = writeImage(outputFile, resizedImage, jpegCompression);
373
			if (saveImage.errorCode gt 0)
374
			{
375
				return saveImage;
376
			} else {
377
				return retVal;
378
			}
379
		}
380
	</cfscript>
381
</cffunction>
382

    
383
<cffunction name="crop" access="public" output="true" returntype="struct" hint="Crop an image.">
384
	<cfargument name="objImage" required="yes" type="Any">
385
	<cfargument name="inputFile" required="yes" type="string">
386
	<cfargument name="outputFile" required="yes" type="string">
387
	<cfargument name="fromX" required="yes" type="numeric">
388
	<cfargument name="fromY" required="yes" type="numeric">
389
	<cfargument name="newWidth" required="yes" type="numeric">
390
	<cfargument name="newHeight" required="yes" type="numeric">
391
	<cfargument name="jpegCompression" required="no" type="numeric" default="#variables.defaultJpegCompression#">
392

    
393
	<cfset var retVal = StructNew()>
394
	<cfset var loadImage = StructNew()>
395
	<cfset var saveImage = StructNew()>
396
	<cfset var croppedImage = "">
397
	<cfset var rh = getRenderingHints()>
398
	<cfset var img = "">
399

    
400
	<cfset retVal.errorCode = 0>
401
	<cfset retVal.errorMessage = "">
402

    
403
	<cfif inputFile neq "">
404
		<cfset loadImage = readImage(inputFile, "NO")>
405
		<cfif loadImage.errorCode is 0>
406
			<cfset img = loadImage.img>
407
		<cfelse>
408
			<cfset retVal = throw(loadImage.errorMessage)>
409
			<cfreturn retVal>
410
		</cfif>
411
	<cfelse>
412
		<cfset img = objImage>
413
	</cfif>
414
	<cfif img.getType() eq 0>
415
		<cfset img = convertImageObject(img,img.TYPE_3BYTE_BGR)>
416
	</cfif>
417
	<cfscript>
418
		if (fromX + newWidth gt img.getWidth()
419
			OR
420
			fromY + newHeight gt img.getHeight()
421
			)
422
		{
423
			retval = throw( "The cropped image dimensions go beyond the original image dimensions.");
424
			return retVal;
425
		}
426
		croppedImage = img.getSubimage(javaCast("int", fromX), javaCast("int", fromY), javaCast("int", newWidth), javaCast("int", newHeight) );
427
		if (outputFile eq "")
428
		{
429
			retVal.img = croppedImage;
430
			return retVal;
431
		} else {
432
			saveImage = writeImage(outputFile, croppedImage, jpegCompression);
433
			if (saveImage.errorCode gt 0)
434
			{
435
				return saveImage;
436
			} else {
437
				return retVal;
438
			}
439
		}
440
	</cfscript>
441
</cffunction>
442

    
443
<cffunction name="rotate" access="public" output="true" returntype="struct" hint="Rotate an image (+/-)90, (+/-)180, or (+/-)270 degrees.">
444
	<cfargument name="objImage" required="yes" type="Any">
445
	<cfargument name="inputFile" required="yes" type="string">
446
	<cfargument name="outputFile" required="yes" type="string">
447
	<cfargument name="degrees" required="yes" type="numeric">
448
	<cfargument name="jpegCompression" required="no" type="numeric" default="#variables.defaultJpegCompression#">
449

    
450
	<cfset var retVal = StructNew()>
451
	<cfset var img = "">
452
	<cfset var loadImage = StructNew()>
453
	<cfset var saveImage = StructNew()>
454
	<cfset var at = "">
455
	<cfset var op = "">
456
	<cfset var w = 0>
457
	<cfset var h = 0>
458
	<cfset var iw = 0>
459
	<cfset var ih = 0>
460
	<cfset var x = 0>
461
	<cfset var y = 0>
462
	<cfset var rotatedImage = "">
463
	<cfset var rh = getRenderingHints()>
464

    
465
	<cfset retVal.errorCode = 0>
466
	<cfset retVal.errorMessage = "">
467

    
468
	<cfif inputFile neq "">
469
		<cfset loadImage = readImage(inputFile, "NO")>
470
		<cfif loadImage.errorCode is 0>
471
			<cfset img = loadImage.img>
472
		<cfelse>
473
			<cfset retVal = throw(loadImage.errorMessage)>
474
			<cfreturn retVal>
475
		</cfif>
476
	<cfelse>
477
		<cfset img = objImage>
478
	</cfif>
479
	<cfif img.getType() eq 0>
480
		<cfset img = convertImageObject(img,img.TYPE_3BYTE_BGR)>
481
	</cfif>
482
	<cfif ListFind("-270,-180,-90,90,180,270",degrees) is 0>
483
		<cfset retVal = throw( "At this time, image.cfc only supports rotating images in 90 degree increments.")>
484
		<cfreturn retVal>
485
	</cfif>
486

    
487
	<cfscript>
488
		rotatedImage = CreateObject("java", "java.awt.image.BufferedImage");
489
		at = CreateObject("java", "java.awt.geom.AffineTransform");
490
		op = CreateObject("java", "java.awt.image.AffineTransformOp");
491

    
492
		iw = img.getWidth(); h = iw;
493
		ih = img.getHeight(); w = ih;
494

    
495
		if(arguments.degrees eq 180) { w = iw; h = ih; }
496

    
497
		x = (w/2)-(iw/2);
498
		y = (h/2)-(ih/2);
499

    
500
		rotatedImage.init(javacast("int",w),javacast("int",h),img.getType());
501

    
502
		at.rotate(arguments.degrees * 0.0174532925,w/2,h/2);
503
		at.translate(x,y);
504
		op.init(at, rh);
505

    
506
		op.filter(img, rotatedImage);
507

    
508
		if (outputFile eq "")
509
		{
510
			retVal.img = rotatedImage;
511
			return retVal;
512
		} else {
513
			saveImage = writeImage(outputFile, rotatedImage, jpegCompression);
514
			if (saveImage.errorCode gt 0)
515
			{
516
				return saveImage;
517
			} else {
518
				return retVal;
519
			}
520
		}
521
	</cfscript>
522
</cffunction>
523

    
524
<cffunction name="convert" access="public" output="true" returntype="struct" hint="Convert an image from one format to another.">
525
	<cfargument name="objImage" required="yes" type="Any">
526
	<cfargument name="inputFile" required="yes" type="string">
527
	<cfargument name="outputFile" required="yes" type="string">
528
	<cfargument name="jpegCompression" required="no" type="numeric" default="#variables.defaultJpegCompression#">
529

    
530
	<cfset var retVal = StructNew()>
531
	<cfset var loadImage = StructNew()>
532
	<cfset var saveImage = StructNew()>
533
	<cfset var img = "">
534

    
535
	<cfset retVal.errorCode = 0>
536
	<cfset retVal.errorMessage = "">
537

    
538
	<cfif inputFile neq "">
539
		<cfset loadImage = readImage(inputFile, "NO")>
540
		<cfif loadImage.errorCode is 0>
541
			<cfset img = loadImage.img>
542
		<cfelse>
543
			<cfset retVal = throw(loadImage.errorMessage)>
544
			<cfreturn retVal>
545
		</cfif>
546
	<cfelse>
547
		<cfset img = objImage>
548
	</cfif>
549

    
550
	<cfscript>
551
		if (outputFile eq "")
552
		{
553
			retVal = throw( "The convert method requires a valid output filename.");
554
			return retVal;
555
		} else {
556
			saveImage = writeImage(outputFile, img, jpegCompression);
557
			if (saveImage.errorCode gt 0)
558
			{
559
				return saveImage;
560
			} else {
561
				return retVal;
562
			}
563
		}
564
	</cfscript>
565
</cffunction>
566

    
567
<cffunction name="setOption" access="public" output="true" returnType="void" hint="Sets values for allowed CFC options.">
568
	<cfargument name="key" type="string" required="yes">
569
	<cfargument name="val" type="string" required="yes">
570

    
571
	<cfset var validKeys = "interpolation,textantialiasing,throwonerror,defaultJpegCompression">
572
	<cfset arguments.key = lcase(trim(arguments.key))>
573
	<cfset arguments.val = lcase(trim(arguments.val))>
574
	<cfif listFind(validKeys, arguments.key) gt 0>
575
		<cfset variables[arguments.key] = arguments.val>
576
	</cfif>
577
</cffunction>
578

    
579
<cffunction name="getOption" access="public" output="true" returnType="any" hint="Returns the current value for the specified CFC option.">
580
	<cfargument name="key" type="string" required="yes">
581

    
582
	<cfset var validKeys = "interpolation,textantialiasing,throwonerror,defaultJpegCompression">
583
	<cfset arguments.key = lcase(trim(arguments.key))>
584
	<cfif listFindNoCase(validKeys, arguments.key) gt 0>
585
		<cfreturn variables[arguments.key]>
586
	<cfelse>
587
		<cfreturn "">
588
	</cfif>
589
</cffunction>
590

    
591
<cffunction name="getRenderingHints" access="private" output="true" returnType="any" hint="Internal method controls various aspects of rendering quality.">
592
	<cfset var rh = CreateObject("java","java.awt.RenderingHints")>
593
	<cfset var initMap = CreateObject("java","java.util.HashMap")>
594
	<cfset initMap.init()>
595
	<cfset rh.init(initMap)>
596
	<cfset rh.put(rh.KEY_ALPHA_INTERPOLATION, rh.VALUE_ALPHA_INTERPOLATION_QUALITY)> <!--- QUALITY, SPEED, DEFAULT --->
597
	<cfset rh.put(rh.KEY_ANTIALIASING, rh.VALUE_ANTIALIAS_ON)> <!--- ON, OFF, DEFAULT --->
598
	<cfset rh.put(rh.KEY_COLOR_RENDERING, rh.VALUE_COLOR_RENDER_QUALITY)>  <!--- QUALITY, SPEED, DEFAULT --->
599
	<cfset rh.put(rh.KEY_DITHERING, rh.VALUE_DITHER_DEFAULT)> <!--- DISABLE, ENABLE, DEFAULT --->
600
	<cfset rh.put(rh.KEY_RENDERING, rh.VALUE_RENDER_QUALITY)> <!--- QUALITY, SPEED, DEFAULT --->
601
	<cfset rh.put(rh.KEY_FRACTIONALMETRICS, rh.VALUE_FRACTIONALMETRICS_DEFAULT)> <!--- DISABLE, ENABLE, DEFAULT --->
602
	<cfset rh.put(rh.KEY_STROKE_CONTROL, rh.VALUE_STROKE_DEFAULT)>
603

    
604
	<cfif variables.textAntiAliasing>
605
		<cfset rh.put(rh.KEY_TEXT_ANTIALIASING, rh.VALUE_TEXT_ANTIALIAS_ON)>
606
	<cfelse>
607
		<cfset rh.put(rh.KEY_TEXT_ANTIALIASING, rh.VALUE_TEXT_ANTIALIAS_OFF)>
608
	</cfif>
609

    
610
	<cfif variables.interpolation eq "nearest_neighbor">
611
		<cfset rh.put(rh.KEY_INTERPOLATION, rh.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)>
612
	<cfelseif variables.interpolation eq "bilinear">
613
		<cfset rh.put(rh.KEY_INTERPOLATION, rh.VALUE_INTERPOLATION_BILINEAR)>
614
	<cfelse>
615
		<cfset rh.put(rh.KEY_INTERPOLATION, rh.VALUE_INTERPOLATION_BICUBIC)>
616
	</cfif>
617

    
618
	<cfreturn rh>
619
</cffunction>
620

    
621
<cffunction name="readImage" access="public" output="true" returntype="struct" hint="Reads an image from a local file.  Requires an absolute path.">
622
	<cfargument name="source" required="yes" type="string">
623
	<cfargument name="forModification" required="no" type="boolean" default="yes">
624

    
625
	<cfif isURL(source)>
626
		<cfreturn readImageFromURL(source, forModification)>
627
	<cfelse>
628
		<cfreturn readImageFromFile(source, forModification)>
629
	</cfif>
630
</cffunction>
631

    
632
<cffunction name="readImageFromFile" access="private" output="true" returntype="struct" hint="Reads an image from a local file.  Requires an absolute path.">
633
	<cfargument name="inputFile" required="yes" type="string">
634
	<cfargument name="forModification" required="no" type="boolean" default="yes">
635

    
636
	<cfset var retVal = StructNew()>
637
	<cfset var img = "">
638
	<cfset var inFile = "">
639
	<cfset var filename = getFileFromPath(inputFile)>
640
	<cfset var extension = lcase(listLast(inputFile,"."))>
641
	<cfset var imageIO = CreateObject("java", "javax.imageio.ImageIO")>
642
	<cfset var validExtensionsToRead = ArrayToList(imageIO.getReaderFormatNames())>
643

    
644
	<cfset retVal.errorCode = 0>
645
	<cfset retVal.errorMessage = "">
646

    
647
	<cfif not fileExists(arguments.inputFile)>
648
		<cfset retVal = throw("The specified file #Chr(34)##arguments.inputFile##Chr(34)# could not be found.")>
649
		<cfreturn retVal>
650
	<cfelseif listLen(filename,".") lt 2>
651
		<cfset retVal = throw("Sorry, image files without extensions cannot be manipulated.")>
652
		<cfreturn retVal>
653
	<cfelseif listFindNoCase(validExtensionsToRead, extension) is 0>
654
		<cfset retVal = throw("Java is unable to read #extension# files.")>
655
		<cfreturn retVal>
656
	<cfelseif NOT fileExists(arguments.inputFile)>
657
		<cfset retVal = throw("The specified input file does not exist.")>
658
		<cfreturn retVal>
659
	<cfelse>
660
		<cfset img = CreateObject("java", "java.awt.image.BufferedImage")>
661
		<cfset inFile = CreateObject("java", "java.io.File")>
662
		<cfset inFile.init(arguments.inputFile)>
663
		<cfif NOT inFile.canRead()>
664
			<cfset retVal = throw("Unable to open source file #Chr(34)##arguments.inputFile##Chr(34)#.")>
665
			<cfreturn retVal>
666
		<cfelse>
667
			<cftry>
668
				<cfset img = imageIO.read(inFile)>
669
				<cfcatch type="any">
670
					<cfset retval = throw("An error occurred attempting to read the specified image.  #cfcatch.message# - #cfcatch.detail#")>
671
					<cfreturn retVal>
672
				</cfcatch>
673
			</cftry>
674
			<cfset retVal.img = img>
675
			<cfset retVal.inFile = inFile>
676
			<cfreturn retVal>
677
		</cfif>
678
	</cfif>
679
</cffunction>
680

    
681
<cffunction name="readImageFromURL" access="private" output="true" returntype="struct" hint="Read an image from a URL.  Requires an absolute URL.">
682
	<cfargument name="inputURL" required="yes" type="string">
683
	<cfargument name="forModification" required="no" type="boolean" default="yes">
684

    
685
	<cfset var retVal = StructNew()>
686
	<cfset var img = CreateObject("java", "java.awt.image.BufferedImage")>
687
	<cfset var inURL	= CreateObject("java", "java.net.URL")>
688
	<cfset var imageIO = CreateObject("java", "javax.imageio.ImageIO")>
689

    
690
	<cfset retVal.errorCode = 0>
691
	<cfset retVal.errorMessage = "">
692

    
693

    
694
	<cfset inURL.init(arguments.inputURL)>
695
	<cftry>
696
		<cfset img = imageIO.read(inURL)>
697
		<cfcatch type="any">
698
			<cfset retval = throw("An error occurred attempting to read the specified image.  #cfcatch.message# - #cfcatch.detail#")>
699
			<cfreturn retVal>
700
		</cfcatch>
701
	</cftry>
702
	<cfset retVal.img = img>
703
	<cfreturn retVal>
704
</cffunction>
705

    
706
<cffunction name="writeImage" access="public" output="true" returntype="struct" hint="Write an image to disk.">
707
	<cfargument name="outputFile" required="yes" type="string">
708
	<cfargument name="img" required="yes" type="any">
709
	<cfargument name="jpegCompression" required="no" type="numeric" default="#variables.defaultJpegCompression#">
710

    
711
	<cfset var retVal = StructNew()>
712
	<cfset var outFile = "">
713
	<cfset var filename = getFileFromPath(outputFile)>
714
	<cfset var extension = lcase(listLast(filename,"."))>
715
	<cfset var imageIO = CreateObject("java", "javax.imageio.ImageIO")>
716
	<cfset var validExtensionsToWrite = ArrayToList(imageIO.getWriterFormatNames())>
717
	<!--- used for jpeg output method --->
718
	<cfset var out = "">
719
	<cfset var fos = "">
720
	<cfset var JPEGCodec = "">
721
	<cfset var encoder = "">
722
	<cfset var param = "">
723
	<cfset var quality = javacast("float", jpegCompression/100)>
724
	<cfset var tempOutputFile = "#variables.tempDirectory#\#createUUID()#.#extension#">
725

    
726
	<cfset retVal.errorCode = 0>
727
	<cfset retVal.errorMessage = "">
728

    
729
	<cfif listFindNoCase(validExtensionsToWrite, extension) eq 0>
730
		<cfset throw("Java is unable to write #extension# files.  Valid formats include: #validExtensionsToWrite#")>
731
	</cfif>
732

    
733
	<cfif extension neq "jpg" and extension neq "jpeg">
734
		<!---
735
			Simple output method for non jpeg images
736
		--->
737
		<cfset outFile = CreateObject("java", "java.io.File")>
738
		<cfset outFile.init(tempOutputFile)>
739
		<cfset imageIO.write(img, extension, outFile)>
740
	<cfelse>
741
		<cfscript>
742
			/*
743
				JPEG output method handles compression
744
			*/
745
			out = createObject("java", "java.io.BufferedOutputStream");
746
			fos = createObject("java", "java.io.FileOutputStream");
747
			fos.init(tempOutputFile);
748
			out.init(fos);
749
			JPEGCodec = createObject("java", "com.sun.image.codec.jpeg.JPEGCodec");
750
			encoder = JPEGCodec.createJPEGEncoder(out);
751
		    param = encoder.getDefaultJPEGEncodeParam(img);
752
		    param.setQuality(quality, false);
753
		    encoder.setJPEGEncodeParam(param);
754
		    encoder.encode(img);
755
		    out.close();
756
		</cfscript>
757
	</cfif>
758
	<!--- move file to its final destination --->
759
	<cffile action="MOVE" source="#tempOutputFile#" destination="#arguments.outputFile#">
760
	<cfreturn retVal>
761
</cffunction>
762

    
763
<cffunction name="flipflop" access="private" output="true" returntype="struct" hint="Internal method used for flipping and flopping images.">
764
	<cfargument name="objImage" required="yes" type="Any">
765
	<cfargument name="inputFile" required="yes" type="string">
766
	<cfargument name="outputFile" required="yes" type="string">
767
	<cfargument name="direction" required="yes" type="string"><!--- horizontal or vertical --->
768
	<cfargument name="jpegCompression" required="no" type="numeric" default="#variables.defaultJpegCompression#">
769

    
770
	<cfset var retVal = StructNew()>
771
	<cfset var loadImage = StructNew()>
772
	<cfset var saveImage = StructNew()>
773
	<cfset var flippedImage = "">
774
	<cfset var rh = getRenderingHints()>
775
	<cfset var img = "">
776

    
777
	<cfset retVal.errorCode = 0>
778
	<cfset retVal.errorMessage = "">
779

    
780
	<cfif inputFile neq "">
781
		<cfset loadImage = readImage(inputFile, "NO")>
782
		<cfif loadImage.errorCode is 0>
783
			<cfset img = loadImage.img>
784
		<cfelse>
785
			<cfset retVal = throw(loadImage.errorMessage)>
786
			<cfreturn retVal>
787
		</cfif>
788
	<cfelse>
789
		<cfset img = objImage>
790
	</cfif>
791
	<cfif img.getType() eq 0>
792
		<cfset img = convertImageObject(img,img.TYPE_3BYTE_BGR)>
793
	</cfif>
794
	<cfscript>
795
		flippedImage = CreateObject("java", "java.awt.image.BufferedImage");
796
		at = CreateObject("java", "java.awt.geom.AffineTransform");
797
		op = CreateObject("java", "java.awt.image.AffineTransformOp");
798

    
799
		flippedImage.init(img.getWidth(), img.getHeight(), img.getType());
800

    
801
		if (direction eq "horizontal") {
802
			at = at.getScaleInstance(-1, 1);
803
			at.translate(-img.getWidth(), 0);
804
		} else {
805
			at = at.getScaleInstance(1,-1);
806
			at.translate(0, -img.getHeight());
807
		}
808
		op.init(at, rh);
809
		op.filter(img, flippedImage);
810

    
811
		if (outputFile eq "")
812
		{
813
			retVal.img = flippedImage;
814
			return retVal;
815
		} else {
816
			saveImage = writeImage(outputFile, flippedImage, jpegCompression);
817
			if (saveImage.errorCode gt 0)
818
			{
819
				return saveImage;
820
			} else {
821
				return retVal;
822
			}
823
		}
824
	</cfscript>
825
</cffunction>
826

    
827

    
828

    
829
<cffunction name="filterFastBlur" access="public" output="true" returntype="struct" hint="Internal method used for flipping and flopping images.">
830
	<cfargument name="objImage" required="yes" type="Any">
831
	<cfargument name="inputFile" required="yes" type="string">
832
	<cfargument name="outputFile" required="yes" type="string">
833
	<cfargument name="blurAmount" required="yes" type="numeric">
834
	<cfargument name="iterations" required="yes" type="numeric">
835
	<cfargument name="jpegCompression" required="no" type="numeric" default="#variables.defaultJpegCompression#">
836

    
837
	<cfset var retVal = StructNew()>
838
	<cfset var loadImage = StructNew()>
839
	<cfset var saveImage = StructNew()>
840
	<cfset var srcImage = "">
841
	<cfset var destImage = "">
842
	<cfset var rh = getRenderingHints()>
843

    
844
	<cfset retVal.errorCode = 0>
845
	<cfset retVal.errorMessage = "">
846

    
847
	<cfif NOT variables.javanulls>
848
		<cfset throw("Sorry, but the blur filter is not supported on this platform.")>
849
	</cfif>
850
	<cfif inputFile neq "">
851
		<cfset loadImage = readImage(inputFile, "NO")>
852
		<cfif loadImage.errorCode is 0>
853
			<cfset srcImage = loadImage.img>
854
		<cfelse>
855
			<cfset retVal = throw(loadImage.errorMessage)>
856
			<cfreturn retVal>
857
		</cfif>
858
	<cfelse>
859
		<cfset srcImage = objImage>
860
	</cfif>
861
	<cfif srcImage.getType() eq 0>
862
		<cfset srcImage = convertImageObject(srcImage,srcImage.TYPE_3BYTE_BGR)>
863
	</cfif>
864

    
865
	<cfscript>
866

    
867
		// initialize the blur filter
868
		variables.blurFilter.init(arguments.blurAmount);
869
		// move the source image into the destination image
870
		// so we can repeatedly blur it.
871
		destImage = srcImage;
872

    
873
		for (i=1; i lte iterations; i=i+1)
874
		{
875
			// do the blur i times
876
			destImage = variables.blurFilter.filter(destImage);
877
		}
878

    
879

    
880
		if (outputFile eq "")
881
		{
882
			// return the image object
883
			retVal.img = destImage;
884
			return retVal;
885
		} else {
886
			// write the image object to the specified file.
887
			saveImage = writeImage(outputFile, destImage, jpegCompression);
888
			if (saveImage.errorCode gt 0)
889
			{
890
				return saveImage;
891
			} else {
892
				return retVal;
893
			}
894
		}
895
	</cfscript>
896
</cffunction>
897

    
898
<cffunction name="filterSharpen" access="public" output="true" returntype="struct" hint="Internal method used for flipping and flopping images.">
899
	<cfargument name="objImage" required="yes" type="Any">
900
	<cfargument name="inputFile" required="yes" type="string">
901
	<cfargument name="outputFile" required="yes" type="string">
902
	<cfargument name="jpegCompression" required="no" type="numeric" default="#variables.defaultJpegCompression#">
903

    
904
	<cfset var retVal = StructNew()>
905
	<cfset var loadImage = StructNew()>
906
	<cfset var saveImage = StructNew()>
907
	<cfset var srcImage = "">
908
	<cfset var destImage = "">
909
	<cfset var rh = getRenderingHints()>
910

    
911
	<cfset retVal.errorCode = 0>
912
	<cfset retVal.errorMessage = "">
913

    
914
	<cfif NOT variables.javanulls>
915
		<cfset throw("Sorry, but the blur filter is not supported on this platform.")>
916
	</cfif>
917

    
918
	<cfif inputFile neq "">
919
		<cfset loadImage = readImage(inputFile, "NO")>
920
		<cfif loadImage.errorCode is 0>
921
			<cfset srcImage = loadImage.img>
922
		<cfelse>
923
			<cfset retVal = throw(loadImage.errorMessage)>
924
			<cfreturn retVal>
925
		</cfif>
926
	<cfelse>
927
		<cfset srcImage = objImage>
928
	</cfif>
929
	<cfif srcImage.getType() eq 0>
930
		<cfset srcImage = convertImageObject(srcImage,srcImage.TYPE_3BYTE_BGR)>
931
	</cfif>
932

    
933
	<cfscript>
934
		// initialize the sharpen filter
935
		variables.sharpenFilter.init();
936

    
937
		destImage = variables.sharpenFilter.filter(srcImage);
938

    
939

    
940
		if (outputFile eq "")
941
		{
942
			// return the image object
943
			retVal.img = destImage;
944
			return retVal;
945
		} else {
946
			// write the image object to the specified file.
947
			saveImage = writeImage(outputFile, destImage, jpegCompression);
948
			if (saveImage.errorCode gt 0)
949
			{
950
				return saveImage;
951
			} else {
952
				return retVal;
953
			}
954
		}
955
	</cfscript>
956
</cffunction>
957

    
958

    
959
<cffunction name="filterPosterize" access="public" output="true" returntype="struct" hint="Internal method used for flipping and flopping images.">
960
	<cfargument name="objImage" required="yes" type="Any">
961
	<cfargument name="inputFile" required="yes" type="string">
962
	<cfargument name="outputFile" required="yes" type="string">
963
	<cfargument name="amount" required="yes" type="string">
964
	<cfargument name="jpegCompression" required="no" type="numeric" default="#variables.defaultJpegCompression#">
965

    
966
	<cfset var retVal = StructNew()>
967
	<cfset var loadImage = StructNew()>
968
	<cfset var saveImage = StructNew()>
969
	<cfset var srcImage = "">
970
	<cfset var destImage = "">
971
	<cfset var rh = getRenderingHints()>
972

    
973
	<cfset retVal.errorCode = 0>
974
	<cfset retVal.errorMessage = "">
975

    
976
	<cfif NOT variables.javanulls>
977
		<cfset throw("Sorry, but the blur filter is not supported on this platform.")>
978
	</cfif>
979

    
980
	<cfif inputFile neq "">
981
		<cfset loadImage = readImage(inputFile, "NO")>
982
		<cfif loadImage.errorCode is 0>
983
			<cfset srcImage = loadImage.img>
984
		<cfelse>
985
			<cfset retVal = throw(loadImage.errorMessage)>
986
			<cfreturn retVal>
987
		</cfif>
988
	<cfelse>
989
		<cfset srcImage = objImage>
990
	</cfif>
991
	<cfif srcImage.getType() eq 0>
992
		<cfset srcImage = convertImageObject(srcImage,srcImage.TYPE_3BYTE_BGR)>
993
	</cfif>
994
	<cfif srcImage.getType() neq 5>
995
		<cfset throw("ImageCFC cannot posterize this image type (#srcImage.getType()#)")>
996
	</cfif>
997
	<cfscript>
998
		// initialize the posterize filter
999
		variables.posterizeFilter.init(arguments.amount);
1000

    
1001
		destImage = variables.posterizeFilter.filter(srcImage);
1002

    
1003

    
1004
		if (outputFile eq "")
1005
		{
1006
			// return the image object
1007
			retVal.img = destImage;
1008
			return retVal;
1009
		} else {
1010
			// write the image object to the specified file.
1011
			saveImage = writeImage(outputFile, destImage, jpegCompression);
1012
			if (saveImage.errorCode gt 0)
1013
			{
1014
				return saveImage;
1015
			} else {
1016
				return retVal;
1017
			}
1018
		}
1019
	</cfscript>
1020
</cffunction>
1021

    
1022

    
1023
<cffunction name="addText" access="public" output="true" returntype="struct" hint="Add text to an image.">
1024
	<cfargument name="objImage" required="yes" type="Any">
1025
	<cfargument name="inputFile" required="yes" type="string">
1026
	<cfargument name="outputFile" required="yes" type="string">
1027
	<cfargument name="x" required="yes" type="numeric">
1028
	<cfargument name="y" required="yes" type="numeric">
1029
	<cfargument name="fontDetails" required="yes" type="struct">
1030
	<cfargument name="content" required="yes" type="String">
1031
	<cfargument name="jpegCompression" required="no" type="numeric" default="#variables.defaultJpegCompression#">
1032

    
1033
	<cfset var retVal = StructNew()>
1034
	<cfset var loadImage = StructNew()>
1035
	<cfset var img = "">
1036
	<cfset var saveImage = StructNew()>
1037
	<cfset var g2d = "">
1038
	<cfset var bgImage = "">
1039
	<cfset var fontImage = "">
1040
	<cfset var overlayImage = "">
1041
	<cfset var Color = "">
1042
	<cfset var font = "">
1043
	<cfset var font_stream = "">
1044
	<cfset var ac = "">
1045
	<cfset var rgb = "">
1046

    
1047
	<cfset retVal.errorCode = 0>
1048
	<cfset retVal.errorMessage = "">
1049

    
1050
	<cfparam name="arguments.fontDetails.size" default="12">
1051
	<cfparam name="arguments.fontDetails.color" default="black">
1052
	<cfparam name="arguments.fontDetails.fontFile" default="">
1053
	<cfparam name="arguments.fontDetails.fontName" default="serif">
1054

    
1055
	<cfif arguments.fontDetails.fontFile neq "" and not fileExists(arguments.fontDetails.fontFile)>
1056
		<cfset retVal = throw("The specified font file #Chr(34)##arguments.inputFile##Chr(34)# could not be found on the server.")>
1057
		<cfreturn retVal>
1058
	</cfif>
1059
	<cftry>
1060
		<cfset rgb = getRGB(arguments.fontDetails.color)>
1061
		<cfcatch type="any">
1062
			<cfset retVal = throw("Invalid color #Chr(34)##arguments.fontDetails.color##Chr(34)#")>
1063
			<cfreturn retVal>
1064
		</cfcatch>
1065
	</cftry>
1066
	<cfif inputFile neq "">
1067
		<cfset loadImage = readImage(inputFile, "NO")>
1068
		<cfif loadImage.errorCode is 0>
1069
			<cfset img = loadImage.img>
1070
		<cfelse>
1071
			<cfset retVal = throw(loadImage.errorMessage)>
1072
			<cfreturn retVal>
1073
		</cfif>
1074
	<cfelse>
1075
		<cfset img = objImage>
1076
	</cfif>
1077
	<cfif img.getType() eq 0>
1078
		<cfset img = convertImageObject(img,img.TYPE_3BYTE_BGR)>
1079
	</cfif>
1080
	<cfscript>
1081
		// load objects
1082
		bgImage = CreateObject("java", "java.awt.image.BufferedImage");
1083
		fontImage = CreateObject("java", "java.awt.image.BufferedImage");
1084
		overlayImage = CreateObject("java", "java.awt.image.BufferedImage");
1085
		Color = CreateObject("java","java.awt.Color");
1086
		font = createObject("java","java.awt.Font");
1087
		font_stream = createObject("java","java.io.FileInputStream");
1088
		ac = CreateObject("Java", "java.awt.AlphaComposite");
1089

    
1090
		// set up basic needs
1091
		fontColor = Color.init(javacast("int", rgb.red), javacast("int", rgb.green), javacast("int", rgb.blue));
1092

    
1093
		if (fontDetails.fontFile neq "")
1094
		{
1095
			font_stream.init(arguments.fontDetails.fontFile);
1096
			font = font.createFont(font.TRUETYPE_FONT, font_stream);
1097
			font = font.deriveFont(javacast("float",arguments.fontDetails.size));
1098
		} else {
1099
			font.init(fontDetails.fontName, evaluate(fontDetails.style), fontDetails.size);
1100
		}
1101
		// get font metrics using a 1x1 bufferedImage
1102
		fontImage.init(1,1,img.getType());
1103
		g2 = fontImage.createGraphics();
1104
		g2.setRenderingHints(getRenderingHints());
1105
		fc = g2.getFontRenderContext();
1106
		bounds = font.getStringBounds(content,fc);
1107

    
1108
		g2 = img.createGraphics();
1109
		g2.setRenderingHints(getRenderingHints());
1110
		g2.setFont(font);
1111
		g2.setColor(fontColor);
1112
		// in case you want to change the alpha
1113
		// g2.setComposite(ac.getInstance(ac.SRC_OVER, 0.50));
1114

    
1115
		// the location (arguments.fontDetails.size+y) doesn't really work
1116
		// the way I want it to.
1117
		g2.drawString(content,javacast("int",x),javacast("int",arguments.fontDetails.size+y));
1118

    
1119
		if (outputFile eq "")
1120
		{
1121
			retVal.img = img;
1122
			return retVal;
1123
		} else {
1124
			saveImage = writeImage(outputFile, img, jpegCompression);
1125
			if (saveImage.errorCode gt 0)
1126
			{
1127
				return saveImage;
1128
			} else {
1129
				return retVal;
1130
			}
1131
		}
1132
	</cfscript>
1133
</cffunction>
1134

    
1135
<cffunction name="watermark" access="public" output="false">
1136
	<cfargument name="objImage1" required="yes" type="Any">
1137
	<cfargument name="objImage2" required="yes" type="Any">
1138
	<cfargument name="inputFile1" required="yes" type="string">
1139
	<cfargument name="inputFile2" required="yes" type="string">
1140
	<cfargument name="alpha" required="yes" type="numeric">
1141
	<cfargument name="placeAtX" required="yes" type="numeric">
1142
	<cfargument name="placeAtY" required="yes" type="numeric">
1143
	<cfargument name="outputFile" required="yes" type="string">
1144
	<cfargument name="jpegCompression" required="no" type="numeric" default="#variables.defaultJpegCompression#">
1145

    
1146
	<cfset var retVal = StructNew()>
1147
	<cfset var loadImage = StructNew()>
1148
	<cfset var originalImage = "">
1149
	<cfset var wmImage = "">
1150
	<cfset var saveImage = StructNew()>
1151
	<cfset var ac = "">
1152
	<cfset var rh = getRenderingHints()>
1153

    
1154
	<cfset retVal.errorCode = 0>
1155
	<cfset retVal.errorMessage = "">
1156

    
1157
	<cfif inputFile1 neq "">
1158
		<cfset loadImage = readImage(inputFile1, "NO")>
1159
		<cfif loadImage.errorCode is 0>
1160
			<cfset originalImage = loadImage.img>
1161
		<cfelse>
1162
			<cfset retVal = throw(loadImage.errorMessage)>
1163
			<cfreturn retVal>
1164
		</cfif>
1165
	<cfelse>
1166
		<cfset originalImage = objImage1>
1167
	</cfif>
1168
	<cfif originalImage.getType() eq 0>
1169
		<cfset originalImage = convertImageObject(originalImage,originalImage.TYPE_3BYTE_BGR)>
1170
	</cfif>
1171

    
1172
	<cfif inputFile2 neq "">
1173
		<cfset loadImage = readImage(inputFile2, "NO")>
1174
		<cfif loadImage.errorCode is 0>
1175
			<cfset wmImage = loadImage.img>
1176
		<cfelse>
1177
			<cfset retVal = throw(loadImage.errorMessage)>
1178
			<cfreturn retVal>
1179
		</cfif>
1180
	<cfelse>
1181
		<cfset wmImage = objImage2>
1182
	</cfif>
1183
	<cfif wmImage.getType() eq 0>
1184
		<cfset wmImage = convertImageObject(wmImage,wmImage.TYPE_3BYTE_BGR)>
1185
	</cfif>
1186

    
1187

    
1188
	<cfscript>
1189
		at = CreateObject("java", "java.awt.geom.AffineTransform");
1190
		op = CreateObject("java", "java.awt.image.AffineTransformOp");
1191
		ac = CreateObject("Java", "java.awt.AlphaComposite");
1192
		gfx = originalImage.getGraphics();
1193
		gfx.setComposite(ac.getInstance(ac.SRC_OVER, alpha));
1194

    
1195
		at.init();
1196
		// op.init(at,op.TYPE_BILINEAR);
1197
		op.init(at, rh);
1198

    
1199
		gfx.drawImage(wmImage, op, javaCast("int",arguments.placeAtX), javacast("int", arguments.placeAtY));
1200

    
1201
		gfx.dispose();
1202

    
1203
		if (outputFile eq "")
1204
		{
1205
			retVal.img = originalImage;
1206
			return retVal;
1207
		} else {
1208
			saveImage = writeImage(outputFile, originalImage, jpegCompression);
1209
			if (saveImage.errorCode gt 0)
1210
			{
1211
				return saveImage;
1212
			} else {
1213
				return retVal;
1214
			}
1215
		}
1216
	</cfscript>
1217
</cffunction>
1218

    
1219
<cffunction name="isURL" access="private" output="false" returnType="boolean">
1220
	<cfargument name="stringToCheck" required="yes" type="string">
1221
	<cfif REFindNoCase("^(((https?:)\/\/))[-[:alnum:]\?%,\.\/&##!@:=\+~_]+[A-Za-z0-9\/]$",stringToCheck) NEQ 0>
1222
		<cfreturn true>
1223
	<cfelse>
1224
		<cfreturn false>
1225
	</cfif>
1226
</cffunction>
1227

    
1228
<!--- function returns RGB values in a structure for hex or the 16
1229
	HTML named colors --->
1230
<cffunction name="getRGB" access="private" output="true" returnType="struct">
1231
	<cfargument name="color" type="string" required="yes">
1232

    
1233
	<cfset var retVal = structNew()>
1234
	<cfset var cnt = 0>
1235
	<cfset var namedColors = "aqua,black,blue,fuchsia,gray,green,lime,maroon,navy,olive,purple,red,silver,teal,white,yellow">
1236
	<cfset var namedColorsHexValues = "00ffff,000000,0000ff,ff00ff,808080,008000,00ff00,800000,000080,808080,ff0000,c0c0c0,008080,ffffff,ffff00">
1237

    
1238
	<cfset retVal.red = 0>
1239
	<cfset retVal.green = 0>
1240
	<cfset retVal.blue = 0>
1241

    
1242
	<cfset arguments.color = trim(arguments.color)>
1243
	<cfif len(arguments.color) is 0>
1244
		<cfreturn retVal>
1245
	<cfelseif listFind(namedColors, arguments.color) gt 0>
1246
		<cfset arguments.color = listGetAt(namedColorsHexValues, listFind(namedColors, arguments.color))>
1247
	</cfif>
1248
	<cfif left(arguments.color,1) eq "##">
1249
		<cfset arguments.color = right(arguments.color,len(arguments.color)-1)>
1250
	</cfif>
1251
	<cfif len(arguments.color) neq 6>
1252
		<cfreturn retVal>
1253
	<cfelse>
1254
		<cftry>
1255
			<cfset retVal.red = InputBaseN(mid(arguments.color,1,2),16)>
1256
			<cfset retVal.green = InputBaseN(mid(arguments.color,3,2),16)>
1257
			<cfset retVal.blue = InputBaseN(mid(arguments.color,5,2),16)>
1258
			<cfcatch type="any">
1259
				<cfset retVal.red = 0>
1260
				<cfset retVal.green = 0>
1261
				<cfset retVal.blue = 0>
1262
				<cfreturn retVal>
1263
			</cfcatch>
1264
		</cftry>
1265
	</cfif>
1266
	<cfreturn retVal>
1267
</cffunction>
1268

    
1269
<cffunction name="throw" access="private" output="false" returnType="struct">
1270
	<cfargument name="detail" type="string" required="yes">
1271
	<cfargument name="force" type="boolean" required="no" default="no">
1272

    
1273
	<cfset var retVal = StructNew()>
1274

    
1275
	<cfif variables.throwOnError or arguments.force>
1276
		<cfthrow detail="#arguments.detail#" message="#arguments.detail#">
1277
	<cfelse>
1278
		<cfset retVal.errorCode = 1>
1279
		<cfset retVal.errorMessage = arguments.detail>
1280
		<cfreturn retVal>
1281
	</cfif>
1282
</cffunction>
1283

    
1284
<cffunction name="debugDump" access="private">
1285
	<cfdump var="#arguments#"><cfabort>
1286
</cffunction>
1287

    
1288
<cffunction name="convertImageObject" access="private" output="false" returnType="any">
1289
	<cfargument name="bImage" type="Any" required="yes">
1290
	<cfargument name="type" type="numeric" required="yes">
1291

    
1292
	<cfscript>
1293
	// convert the image to a specified BufferedImage type and return it
1294

    
1295
	var width = bImage.getWidth();
1296
	var height = bImage.getHeight();
1297
	var newImage = createObject("java","java.awt.image.BufferedImage").init(javacast("int",width), javacast("int",height), javacast("int",type));
1298
	// int[] rgbArray = new int[width*height];
1299
	var rgbArray = variables.arrObj.newInstance(variables.intClass, javacast("int",width*height));
1300

    
1301
	bImage.getRGB(
1302
		javacast("int",0),
1303
		javacast("int",0),
1304
		javacast("int",width),
1305
		javacast("int",height),
1306
		rgbArray,
1307
		javacast("int",0),
1308
		javacast("int",width)
1309
		);
1310
	newImage.setRGB(
1311
		javacast("int",0),
1312
		javacast("int",0),
1313
		javacast("int",width),
1314
		javacast("int",height),
1315
		rgbArray,
1316
		javacast("int",0),
1317
		javacast("int",width)
1318
		);
1319
	return newImage;
1320
	</cfscript>
1321

    
1322
</cffunction>
1323

    
1324
</cfcomponent>
(12-12/13)