2
image.cfc v2.19, written by Rick Root (rick@webworksllc.com)
3
Derivative of work originally done originally by James Dew.
6
- http://www.opensourcecf.com/imagecfc (home page)
7
- http://www.cfopen.org/projects/imagecfc (project page)
11
Copyright (c) 2007, Rick Root <rick@webworksllc.com>
14
Redistribution and use in source and binary forms, with or
15
without modification, are permitted provided that the
16
following conditions are met:
18
- Redistributions of source code must retain the above
19
copyright notice, this list of conditions and the
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
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.
44
============================================================
45
This is a derivative work. Following is the original
47
============================================================
49
Copyright (c) 2004 James F. Dew <jdew@yggdrasil.ca>
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.
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.
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
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.
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
83
<cfcomponent displayname="Image">
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(".")#">
91
<cfset variables.javanulls = "no">
93
<cfset nullvalue = javacast("null","")>
94
<cfset variables.javanulls = "yes">
96
<cfset variables.javanulls = "no">
97
<!--- javacast null not supported, so filters won't work --->
102
<cfset variables.blurFilter = createObject("component","blurFilter")>
103
<cfset variables.sharpenFilter = createObject("component","sharpenFilter")>
104
<cfset variables.posterizeFilter = createObject("component","posterizeFilter")>
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>
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">
118
<cfset var retVal = StructNew()>
119
<cfset var loadImage = StructNew()>
122
<cfset retVal.errorCode = 0>
123
<cfset retVal.errorMessage = "">
125
<cfif inputFile neq "">
126
<cfset loadImage = readImage(inputFile, "NO")>
127
<cfif loadImage.errorCode is 0>
128
<cfset img = loadImage.img>
130
<cfset retVal = throw(loadImage.errorMessage)>
133
<cfset retVal.metaData = getImageMetadata(loadImage.inFile)>
135
<cfset img = objImage>
136
<cfset retVal.metadata = getImageMetadata("")>
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>
151
<cfset retVal = throw( "#cfcatch.message#: #cfcatch.detail#")>
157
<cffunction name="getImageMetadata" access="private" output="false" returntype="query">
158
<cfargument name="inFile" required="yes" type="Any"><!--- java.io.File --->
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 = "">
174
paths[1] = expandPath("metadata-extractor-2.3.1.jar");
175
loader = createObject("component", "javaloader.JavaLoader").init(paths);
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");
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
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());
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#">
211
<cfreturn flipflop(objImage, inputFile, outputFile, "horizontal", jpegCompression)>
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#">
220
<cfreturn flipflop(objImage, inputFile, outputFile, "vertical", jpegCompression)>
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#">
230
<cfreturn resize(objImage, inputFile, outputFile, newWidth, 0, "false", "false", jpegCompression)>
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#">
240
<cfreturn resize(objImage, inputFile, outputFile, 0, newHeight, "false", "false", jpegCompression)>
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#">
253
<cfset var retVal = StructNew()>
254
<cfset var loadImage = StructNew()>
255
<cfset var saveImage = StructNew()>
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 = "">
269
<cfset var cropImageResult = "">
270
<cfset var cropOffsetX = "">
271
<cfset var cropOffsetY = "">
273
<cfset retVal.errorCode = 0>
274
<cfset retVal.errorMessage = "">
276
<cfif inputFile neq "">
277
<cfset loadImage = readImage(inputFile, "NO")>
278
<cfif loadImage.errorCode is 0>
279
<cfset img = loadImage.img>
281
<cfset retVal = throw(loadImage.errorMessage)>
285
<cfset img = objImage>
287
<cfif img.getType() eq 0>
288
<cfset img = convertImageObject(img,img.TYPE_3BYTE_BGR)>
291
resizedImage = CreateObject("java", "java.awt.image.BufferedImage");
292
at = CreateObject("java", "java.awt.geom.AffineTransform");
293
op = CreateObject("java", "java.awt.image.AffineTransformOp");
298
if (preserveAspect and cropToExact and newHeight gt 0 and newWidth gt 0)
300
if (w / h gt newWidth / newHeight){
302
} else if (w / h lt newWidth / newHeight){
305
} else if (preserveAspect and newHeight gt 0 and newWidth gt 0) {
306
if (w / h gt newWidth / newHeight){
308
} else if (w / h lt newWidth / newHeight){
312
if (newWidth gt 0 and newHeight eq 0) {
313
scale = newWidth / w;
316
} else if (newHeight gt 0 and newWidth eq 0) {
317
scale = newHeight / h;
320
} else if (newHeight gt 0 and newWidth gt 0) {
324
retVal = throw( retVal.errorMessage);
327
resizedImage.init(javacast("int",w),javacast("int",h),img.getType());
329
w = w / img.getWidth();
330
h = h / img.getHeight();
334
op.init(at.getScaleInstance(javacast("double",w),javacast("double",h)), rh);
335
// resizedImage = op.createCompatibleDestImage(img, img.getColorModel());
336
op.filter(img, resizedImage);
338
imgInfo = getimageinfo(resizedImage, "");
339
if (imgInfo.errorCode gt 0)
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
350
preserveAspect and cropToExact
352
(imgInfo.width IS NOT specifiedWidth OR imgInfo.height IS NOT specifiedHeight)
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 );
359
cropImageResult = crop( resizedImage, "", "", cropOffsetX, cropOffsetY, specifiedWidth, specifiedHeight );
360
if ( cropImageResult.errorCode GT 0)
362
return cropImageResult;
364
resizedImage = cropImageResult.img;
367
if (outputFile eq "")
369
retVal.img = resizedImage;
372
saveImage = writeImage(outputFile, resizedImage, jpegCompression);
373
if (saveImage.errorCode gt 0)
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#">
393
<cfset var retVal = StructNew()>
394
<cfset var loadImage = StructNew()>
395
<cfset var saveImage = StructNew()>
396
<cfset var croppedImage = "">
397
<cfset var rh = getRenderingHints()>
400
<cfset retVal.errorCode = 0>
401
<cfset retVal.errorMessage = "">
403
<cfif inputFile neq "">
404
<cfset loadImage = readImage(inputFile, "NO")>
405
<cfif loadImage.errorCode is 0>
406
<cfset img = loadImage.img>
408
<cfset retVal = throw(loadImage.errorMessage)>
412
<cfset img = objImage>
414
<cfif img.getType() eq 0>
415
<cfset img = convertImageObject(img,img.TYPE_3BYTE_BGR)>
418
if (fromX + newWidth gt img.getWidth()
420
fromY + newHeight gt img.getHeight()
423
retval = throw( "The cropped image dimensions go beyond the original image dimensions.");
426
croppedImage = img.getSubimage(javaCast("int", fromX), javaCast("int", fromY), javaCast("int", newWidth), javaCast("int", newHeight) );
427
if (outputFile eq "")
429
retVal.img = croppedImage;
432
saveImage = writeImage(outputFile, croppedImage, jpegCompression);
433
if (saveImage.errorCode gt 0)
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#">
450
<cfset var retVal = StructNew()>
452
<cfset var loadImage = StructNew()>
453
<cfset var saveImage = StructNew()>
462
<cfset var rotatedImage = "">
463
<cfset var rh = getRenderingHints()>
465
<cfset retVal.errorCode = 0>
466
<cfset retVal.errorMessage = "">
468
<cfif inputFile neq "">
469
<cfset loadImage = readImage(inputFile, "NO")>
470
<cfif loadImage.errorCode is 0>
471
<cfset img = loadImage.img>
473
<cfset retVal = throw(loadImage.errorMessage)>
477
<cfset img = objImage>
479
<cfif img.getType() eq 0>
480
<cfset img = convertImageObject(img,img.TYPE_3BYTE_BGR)>
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.")>
488
rotatedImage = CreateObject("java", "java.awt.image.BufferedImage");
489
at = CreateObject("java", "java.awt.geom.AffineTransform");
490
op = CreateObject("java", "java.awt.image.AffineTransformOp");
492
iw = img.getWidth(); h = iw;
493
ih = img.getHeight(); w = ih;
495
if(arguments.degrees eq 180) { w = iw; h = ih; }
500
rotatedImage.init(javacast("int",w),javacast("int",h),img.getType());
502
at.rotate(arguments.degrees * 0.0174532925,w/2,h/2);
506
op.filter(img, rotatedImage);
508
if (outputFile eq "")
510
retVal.img = rotatedImage;
513
saveImage = writeImage(outputFile, rotatedImage, jpegCompression);
514
if (saveImage.errorCode gt 0)
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#">
530
<cfset var retVal = StructNew()>
531
<cfset var loadImage = StructNew()>
532
<cfset var saveImage = StructNew()>
535
<cfset retVal.errorCode = 0>
536
<cfset retVal.errorMessage = "">
538
<cfif inputFile neq "">
539
<cfset loadImage = readImage(inputFile, "NO")>
540
<cfif loadImage.errorCode is 0>
541
<cfset img = loadImage.img>
543
<cfset retVal = throw(loadImage.errorMessage)>
547
<cfset img = objImage>
551
if (outputFile eq "")
553
retVal = throw( "The convert method requires a valid output filename.");
556
saveImage = writeImage(outputFile, img, jpegCompression);
557
if (saveImage.errorCode gt 0)
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">
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>
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">
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]>
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)>
604
<cfif variables.textAntiAliasing>
605
<cfset rh.put(rh.KEY_TEXT_ANTIALIASING, rh.VALUE_TEXT_ANTIALIAS_ON)>
607
<cfset rh.put(rh.KEY_TEXT_ANTIALIASING, rh.VALUE_TEXT_ANTIALIAS_OFF)>
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)>
615
<cfset rh.put(rh.KEY_INTERPOLATION, rh.VALUE_INTERPOLATION_BICUBIC)>
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">
626
<cfreturn readImageFromURL(source, forModification)>
628
<cfreturn readImageFromFile(source, forModification)>
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">
636
<cfset var retVal = StructNew()>
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())>
644
<cfset retVal.errorCode = 0>
645
<cfset retVal.errorMessage = "">
647
<cfif not fileExists(arguments.inputFile)>
648
<cfset retVal = throw("The specified file #Chr(34)##arguments.inputFile##Chr(34)# could not be found.")>
650
<cfelseif listLen(filename,".") lt 2>
651
<cfset retVal = throw("Sorry, image files without extensions cannot be manipulated.")>
653
<cfelseif listFindNoCase(validExtensionsToRead, extension) is 0>
654
<cfset retVal = throw("Java is unable to read #extension# files.")>
656
<cfelseif NOT fileExists(arguments.inputFile)>
657
<cfset retVal = throw("The specified input file does not exist.")>
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)#.")>
668
<cfset img = imageIO.read(inFile)>
670
<cfset retval = throw("An error occurred attempting to read the specified image. #cfcatch.message# - #cfcatch.detail#")>
674
<cfset retVal.img = img>
675
<cfset retVal.inFile = inFile>
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">
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")>
690
<cfset retVal.errorCode = 0>
691
<cfset retVal.errorMessage = "">
694
<cfset inURL.init(arguments.inputURL)>
696
<cfset img = imageIO.read(inURL)>
698
<cfset retval = throw("An error occurred attempting to read the specified image. #cfcatch.message# - #cfcatch.detail#")>
702
<cfset retVal.img = img>
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#">
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 --->
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#">
726
<cfset retVal.errorCode = 0>
727
<cfset retVal.errorMessage = "">
729
<cfif listFindNoCase(validExtensionsToWrite, extension) eq 0>
730
<cfset throw("Java is unable to write #extension# files. Valid formats include: #validExtensionsToWrite#")>
733
<cfif extension neq "jpg" and extension neq "jpeg">
735
Simple output method for non jpeg images
737
<cfset outFile = CreateObject("java", "java.io.File")>
738
<cfset outFile.init(tempOutputFile)>
739
<cfset imageIO.write(img, extension, outFile)>
743
JPEG output method handles compression
745
out = createObject("java", "java.io.BufferedOutputStream");
746
fos = createObject("java", "java.io.FileOutputStream");
747
fos.init(tempOutputFile);
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);
758
<!--- move file to its final destination --->
759
<cffile action="MOVE" source="#tempOutputFile#" destination="#arguments.outputFile#">
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#">
770
<cfset var retVal = StructNew()>
771
<cfset var loadImage = StructNew()>
772
<cfset var saveImage = StructNew()>
773
<cfset var flippedImage = "">
774
<cfset var rh = getRenderingHints()>
777
<cfset retVal.errorCode = 0>
778
<cfset retVal.errorMessage = "">
780
<cfif inputFile neq "">
781
<cfset loadImage = readImage(inputFile, "NO")>
782
<cfif loadImage.errorCode is 0>
783
<cfset img = loadImage.img>
785
<cfset retVal = throw(loadImage.errorMessage)>
789
<cfset img = objImage>
791
<cfif img.getType() eq 0>
792
<cfset img = convertImageObject(img,img.TYPE_3BYTE_BGR)>
795
flippedImage = CreateObject("java", "java.awt.image.BufferedImage");
796
at = CreateObject("java", "java.awt.geom.AffineTransform");
797
op = CreateObject("java", "java.awt.image.AffineTransformOp");
799
flippedImage.init(img.getWidth(), img.getHeight(), img.getType());
801
if (direction eq "horizontal") {
802
at = at.getScaleInstance(-1, 1);
803
at.translate(-img.getWidth(), 0);
805
at = at.getScaleInstance(1,-1);
806
at.translate(0, -img.getHeight());
809
op.filter(img, flippedImage);
811
if (outputFile eq "")
813
retVal.img = flippedImage;
816
saveImage = writeImage(outputFile, flippedImage, jpegCompression);
817
if (saveImage.errorCode gt 0)
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#">
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()>
844
<cfset retVal.errorCode = 0>
845
<cfset retVal.errorMessage = "">
847
<cfif NOT variables.javanulls>
848
<cfset throw("Sorry, but the blur filter is not supported on this platform.")>
850
<cfif inputFile neq "">
851
<cfset loadImage = readImage(inputFile, "NO")>
852
<cfif loadImage.errorCode is 0>
853
<cfset srcImage = loadImage.img>
855
<cfset retVal = throw(loadImage.errorMessage)>
859
<cfset srcImage = objImage>
861
<cfif srcImage.getType() eq 0>
862
<cfset srcImage = convertImageObject(srcImage,srcImage.TYPE_3BYTE_BGR)>
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;
873
for (i=1; i lte iterations; i=i+1)
875
// do the blur i times
876
destImage = variables.blurFilter.filter(destImage);
880
if (outputFile eq "")
882
// return the image object
883
retVal.img = destImage;
886
// write the image object to the specified file.
887
saveImage = writeImage(outputFile, destImage, jpegCompression);
888
if (saveImage.errorCode gt 0)
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#">
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()>
911
<cfset retVal.errorCode = 0>
912
<cfset retVal.errorMessage = "">
914
<cfif NOT variables.javanulls>
915
<cfset throw("Sorry, but the blur filter is not supported on this platform.")>
918
<cfif inputFile neq "">
919
<cfset loadImage = readImage(inputFile, "NO")>
920
<cfif loadImage.errorCode is 0>
921
<cfset srcImage = loadImage.img>
923
<cfset retVal = throw(loadImage.errorMessage)>
927
<cfset srcImage = objImage>
929
<cfif srcImage.getType() eq 0>
930
<cfset srcImage = convertImageObject(srcImage,srcImage.TYPE_3BYTE_BGR)>
934
// initialize the sharpen filter
935
variables.sharpenFilter.init();
937
destImage = variables.sharpenFilter.filter(srcImage);
940
if (outputFile eq "")
942
// return the image object
943
retVal.img = destImage;
946
// write the image object to the specified file.
947
saveImage = writeImage(outputFile, destImage, jpegCompression);
948
if (saveImage.errorCode gt 0)
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#">
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()>
973
<cfset retVal.errorCode = 0>
974
<cfset retVal.errorMessage = "">
976
<cfif NOT variables.javanulls>
977
<cfset throw("Sorry, but the blur filter is not supported on this platform.")>
980
<cfif inputFile neq "">
981
<cfset loadImage = readImage(inputFile, "NO")>
982
<cfif loadImage.errorCode is 0>
983
<cfset srcImage = loadImage.img>
985
<cfset retVal = throw(loadImage.errorMessage)>
989
<cfset srcImage = objImage>
991
<cfif srcImage.getType() eq 0>
992
<cfset srcImage = convertImageObject(srcImage,srcImage.TYPE_3BYTE_BGR)>
994
<cfif srcImage.getType() neq 5>
995
<cfset throw("ImageCFC cannot posterize this image type (#srcImage.getType()#)")>
998
// initialize the posterize filter
999
variables.posterizeFilter.init(arguments.amount);
1001
destImage = variables.posterizeFilter.filter(srcImage);
1004
if (outputFile eq "")
1006
// return the image object
1007
retVal.img = destImage;
1010
// write the image object to the specified file.
1011
saveImage = writeImage(outputFile, destImage, jpegCompression);
1012
if (saveImage.errorCode gt 0)
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#">
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 = "">
1045
<cfset var rgb = "">
1047
<cfset retVal.errorCode = 0>
1048
<cfset retVal.errorMessage = "">
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">
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.")>
1060
<cfset rgb = getRGB(arguments.fontDetails.color)>
1061
<cfcatch type="any">
1062
<cfset retVal = throw("Invalid color #Chr(34)##arguments.fontDetails.color##Chr(34)#")>
1066
<cfif inputFile neq "">
1067
<cfset loadImage = readImage(inputFile, "NO")>
1068
<cfif loadImage.errorCode is 0>
1069
<cfset img = loadImage.img>
1071
<cfset retVal = throw(loadImage.errorMessage)>
1075
<cfset img = objImage>
1077
<cfif img.getType() eq 0>
1078
<cfset img = convertImageObject(img,img.TYPE_3BYTE_BGR)>
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");
1090
// set up basic needs
1091
fontColor = Color.init(javacast("int", rgb.red), javacast("int", rgb.green), javacast("int", rgb.blue));
1093
if (fontDetails.fontFile neq "")
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));
1099
font.init(fontDetails.fontName, evaluate(fontDetails.style), fontDetails.size);
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);
1108
g2 = img.createGraphics();
1109
g2.setRenderingHints(getRenderingHints());
1111
g2.setColor(fontColor);
1112
// in case you want to change the alpha
1113
// g2.setComposite(ac.getInstance(ac.SRC_OVER, 0.50));
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));
1119
if (outputFile eq "")
1124
saveImage = writeImage(outputFile, img, jpegCompression);
1125
if (saveImage.errorCode gt 0)
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#">
1146
<cfset var retVal = StructNew()>
1147
<cfset var loadImage = StructNew()>
1148
<cfset var originalImage = "">
1149
<cfset var wmImage = "">
1150
<cfset var saveImage = StructNew()>
1152
<cfset var rh = getRenderingHints()>
1154
<cfset retVal.errorCode = 0>
1155
<cfset retVal.errorMessage = "">
1157
<cfif inputFile1 neq "">
1158
<cfset loadImage = readImage(inputFile1, "NO")>
1159
<cfif loadImage.errorCode is 0>
1160
<cfset originalImage = loadImage.img>
1162
<cfset retVal = throw(loadImage.errorMessage)>
1166
<cfset originalImage = objImage1>
1168
<cfif originalImage.getType() eq 0>
1169
<cfset originalImage = convertImageObject(originalImage,originalImage.TYPE_3BYTE_BGR)>
1172
<cfif inputFile2 neq "">
1173
<cfset loadImage = readImage(inputFile2, "NO")>
1174
<cfif loadImage.errorCode is 0>
1175
<cfset wmImage = loadImage.img>
1177
<cfset retVal = throw(loadImage.errorMessage)>
1181
<cfset wmImage = objImage2>
1183
<cfif wmImage.getType() eq 0>
1184
<cfset wmImage = convertImageObject(wmImage,wmImage.TYPE_3BYTE_BGR)>
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));
1196
// op.init(at,op.TYPE_BILINEAR);
1199
gfx.drawImage(wmImage, op, javaCast("int",arguments.placeAtX), javacast("int", arguments.placeAtY));
1203
if (outputFile eq "")
1205
retVal.img = originalImage;
1208
saveImage = writeImage(outputFile, originalImage, jpegCompression);
1209
if (saveImage.errorCode gt 0)
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>
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">
1233
<cfset var retVal = structNew()>
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">
1238
<cfset retVal.red = 0>
1239
<cfset retVal.green = 0>
1240
<cfset retVal.blue = 0>
1242
<cfset arguments.color = trim(arguments.color)>
1243
<cfif len(arguments.color) is 0>
1245
<cfelseif listFind(namedColors, arguments.color) gt 0>
1246
<cfset arguments.color = listGetAt(namedColorsHexValues, listFind(namedColors, arguments.color))>
1248
<cfif left(arguments.color,1) eq "##">
1249
<cfset arguments.color = right(arguments.color,len(arguments.color)-1)>
1251
<cfif len(arguments.color) neq 6>
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>
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">
1273
<cfset var retVal = StructNew()>
1275
<cfif variables.throwOnError or arguments.force>
1276
<cfthrow detail="#arguments.detail#" message="#arguments.detail#">
1278
<cfset retVal.errorCode = 1>
1279
<cfset retVal.errorMessage = arguments.detail>
1284
<cffunction name="debugDump" access="private">
1285
<cfdump var="#arguments#"><cfabort>
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">
1293
// convert the image to a specified BufferedImage type and return it
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));
1304
javacast("int",width),
1305
javacast("int",height),
1308
javacast("int",width)
1313
javacast("int",width),
1314
javacast("int",height),
1317
javacast("int",width)