Skip to main content
Ben Nadel at InVision In Real Life (IRL) 2019 (Phoenix, AZ) with: Jonathon Wilson
Ben Nadel at InVision In Real Life (IRL) 2019 (Phoenix, AZ) with: Jonathon Wilson ( @chilkari )

Getting Image Width And Height Using GraphicsMagick And Lucee CFML 5.2.9.31

By on
Tags:

Yesterday, I looked at how to get the RGB/HEX color of a pixel using GraphicsMagick and Lucee CFML. Continuing my exploration of how I might use GraphicsMagick to replace ColdFusion's native CFImage functionality, I wanted to look at how I might read an image's Width and Height attributes using Lucee CFML 5.2.9.31.

CAUTION: I am very much a novice when it comes to GraphicsMagick. As such, please take the following as an exploration, not an explanation.

One of the utilities that GraphicsMagick provides is identify. This is used to "describe the format and characteristics of one or more image files as internally supported by the software." One of the options that can be passed to the identify utility is -format. With -format, we can explicitly control which characteristics are provided in the standard-output.

From the documentation, here are the special characters that the -format option will support:

  • %b - file size
  • %c - comment
  • %d - directory
  • %e - filename extension
  • %f - filename
  • %g - page dimensions and offsets
  • %h - height
  • %i - input filename
  • %k - number of unique colors
  • %l - label
  • %m - magick
  • %n - number of scenes
  • %o - output filename
  • %p - page number
  • %q - image bit depth
  • %r - image type description
  • %s - scene number
  • %t - top of filename
  • %u - unique temporary filename
  • %w - width
  • %x - horizontal resolution
  • %y - vertical resolution
  • %A - transparency supported
  • %C - compression type
  • %D - GIF disposal method
  • %G - Original width and height
  • %H - page height
  • %M - original filename specification
  • %O - page offset (x,y)
  • %P - page dimensions (width,height)
  • %Q - compression quality
  • %T - time delay (in centi-seconds)
  • %U - resolution units
  • %W - page width
  • %X - page horizontal offset (x)
  • %Y - page vertical offset (y)
  • %@ - trim bounding box
  • %# - signature
  • \n - newline
  • \r - carriage return
  • %% - %

I definitely don't understand what many of the values represent. However, I can immediately see that we have a width and height option using %w and %h, respectively.

Since the identify utility writes to the standard-output, it means that what we get back from the CFExecute tag is a String. Given the fact that ColdFusion has strong String-based List functions, we can format our output as a comma-delimited list that contains both the width and the height:

-format %w,%h

To see this in action, I've setup a demo that allows the user to select from the images in my test directory. Then, using a given image, I use identify in conjunction with -format to print the width and height to the standard-output, which I then parse into an Array using .listToArray():

<cfscript>

	// NOTE: Defaulting to a valid filename just so we don't have to wrap conditional
	// logic around the rest of the page processing.
	param name="url.filename" type="string" default="boop.jpg";

	startedAt = getTickCount();

	// CAUTION: Since this is a demo, it's OK to just read a file off the file-system
	// based on a user-provided value. However, in a production environment, you would
	// want to treat the given filename with much suspicion!
	inputFilepath = expandPath( "../images/#url.filename#" );

	// All of the image utilities are provided through the GraphicsMagick binary.
	command = "gm";

	// We're going to use the Identify utility to read the image dimensions.
	utility = "identify";

	// We want to be EXPLICIT about which input reader GraphicsMagick should use.
	// If we leave it up to "automatic detection", a malicious actor could fake
	// file-type and potentially exploit a weakness in a given reader.
	// --
	// READ MORE: http://www.graphicsmagick.org/security.html
	utilityReader = listLast( inputFilepath, "." ).lcase();

	// Setup the options for the Convert utility.
	commandOptions = [
		utility,

		// The identify command will write image properties to the standard-output. We
		// can use the -format to determine which properties will be output and in which
		// order. %w = width, %h = height. In this case, we're outputting them as a
		// comma-delimited list so that we can easily parse the output.
		// --
		// Read More: http://www.graphicsmagick.org/GraphicsMagick.html#details-format
		// --
		// CAUTION: I am using a trailing comma because a GIF image will output
		// dimensions for EACH PAGE in the GIF animation. As such, the trailing comma
		// prevents two subsequent pages from mucking with each others readings.
		"-format %w,%h,",

		// Provide the source image to be read with the an explicit reader.
		( utilityReader & ":" & inputFilepath )
	];

	// Execute GraphicsMagick on the command-line.
	execute
		name = command
		arguments = commandOptions.toList( " " )
		variable = "successResult"
		errorVariable = "errorResult"
		timeout = 5
	;

	duration = ( getTickCount() - startedAt );

	// If the error variable has been populated, it means the CFExecute tag ran into
	// an error - let's dump-it-out and halt processing.
	if ( variables.keyExists( "errorVariable" ) && errorVariable.len() ) {

		dump( errorVariable );
		abort;

	}

	// At this point, the successResult will contain the comma-delimited list of image
	// properties. We can simply parse it into an array.
	// --
	// NOTE: GIF images may contain more than two indices. In that case, the first two
	// will pertain to the first page in the animation.
	imageProperties = successResult.listToArray();
	width = imageProperties[ 1 ];
	height = imageProperties[ 2 ];

</cfscript>

<cfoutput>

	<!--- List all of the images in the test directory. --->
	<p style="width: 650px ;">
		<cfloop query="#directoryList( path = '../images', listInfo = 'query', sort = 'name asc' )#">

			<a href="./index.cfm?filename=#encodeForHtmlAttribute( name )#">
				#encodeForHtml( name )#
			</a>,

		</cfloop>
	</p>

	<ul>
		<li><strong>Filename:</strong> #encodeForHtml( url.filename )#</li>
		<li><strong>Width:</strong> #numberFormat( width )# px</li>
		<li><strong>Height:</strong> #numberFormat( height )# px</li>
		<li><strong>Duration:</strong> #numberFormat( duration )# ms</li>
	</ul>

	<p>
		<img
			src="../images/#encodeForHtmlAttribute( url.filename )#"
			width="600"
			style="border: 1px solid ##cccccc ; display: block ;"
		/>
	</p>

</cfoutput>

One thing to note in this approach is that I'm including a trailing-comma in the -format configuration. This is because a GIF image will result in additional output for each page in the GIF animation. As such, I include a trailing-comma in order to differentiate the first page from the second page of readings.

And, if we run this ColdFusion page and I click through various filenames, we get the following output:

GraphicsMagick reporting width and height of image in Lucee CFML.

As you can see, I'm easily able to parse the Width and Height out of the identify results using the .listToArray() function. This provides me with a 2+ length array in which index-1 is the Width and index-2 is the Height.

I know this is pretty basic stuff regarding GraphicsMagick. But, since I'm relatively new to this command-line utility, I have to start small and then build iteratively. I suspect that my next exploration will be quite a bit more interesting.

Want to use code from this post? Check out the license.

Reader Comments

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel