Custom book builders are great rich internet applications that we’ve made at Rain. One core component of custom book builders is allowing users to upload their own photos. The most common user error is uploading and using a photo that cannot be printed at a high quality print resolution. It is our job to let the user know if the image he or she has selected for a particular ImageWell (a rectangle where a photo can be placed) can be printed.
Basics of printing
Printers print according to dpi (dots per inch). Say a 1200 px x 1800 px image is printed onto a 4″ x 6″ sheet of paper. The dpi would then be:
- width: 1200 px / 4 inches = 300 dpi
- height: 1800 px / 6 inches = 300 dpi
Print resolutions can vary anywhere from 150 dpi (low/medium quality) to 300 dpi (high quality) and up.
First we need to define two variables:
- Final print resolution dpi
- Warning dpi
For this example we will use a final print resolution of 300 dpi and we will show a warning if the image resolution is lower than 200 dpi. The user has uploaded a low resolution 400 x 500 photo to our photo book application. The user has placed the photo into an ImageWell that is 1200 x 1200 (square). Then the user scales the image to 200% which crops the photo and lowers the dpi.
Note: By selecting a final print resolution of 300 dpi, this means that the 1200 x 1200 ImageWell is equal to a 4 inch by 4 inch square when printed. If you do not understand this, please reread it until you do.
What the user does
When the user places the 400 x 500 photo into our 1200 x 1200 ImageWell, the image is scaled by 3x to fit the width of the ImageWell resulting in an actual image width of 1200 x 1500.
The user then scales the image inside the ImageWell to 200% (not the ImageWell itself) resulting in an actual image width of 2400 x 3000. This also crops the photo.
How to calculate the Image Resolution
We now need to figure out how much of our image is actually being displayed inside the ImageWell. For width, we will take a ratio of the ImageWell’s width comparative to the actual image width. imageWell.width / image.width or 1200 / 2400 = 50%. For height, 1200 / 3000 = 40%
Now we need to see how many pixels of our original photo we will be using when we print the photo in the ImageWell. Let’s see what 50% of the original uploaded image’s width is. 50% x 400 px = 200 px. For height, 40% x 500 = 200 px.
So the user essentially wants to crop out a 200 x 200 square out of our photo and put it into a 1200 x 1200 ImageWell. Some red flags should be going off in your head. 200 / 1200 = 16.67% of the print resolution.
So to calculate the dpi of the cropped and scaled image, we multiply 16.67% x 300 dpi (final print resolution) which equals 50 dpi. Clearly we can see that 50 dpi is lower than our warning dpi of 200 and we should display a warning message to the user letting them know that the image cannot be printed.
Here is the logic implemented in Flex Actionscript 3.0. This code sample handles image rotation, cropping, and scaling. Special thanks to Aaron Hardy for helping with the implementation of this code.
Note: This code sample is within an extended version of ImageWell.as which is part of Rain’s SVG parsing library. Simply copying and pasting this code into your application will not work. This code is provided for you to learn from and as evidence that the logic works in an actual project. If you wish to use this code, you will need to implement logic and modify variables to get it to work in your application.
* Check image resolution to ensure it meets minimum image resolution requirements.
* If image resolution is too low, call showWarningDisplay()
* @see ConfigVO printPixelsPerInch for print resolution which is 300 dpi
* @see ConfigVO printPixelsPerInchMinimum for minimum print resolution which is 200 dpi
protected function checkImageResolution(e:Event = null):void
// Please Note: PhotoVO (photo) is the photo on the server
// image refers to the photo that has been loaded into this image well
// I only refer to width to simplify. Height is also accounted for.
// A 400 x 500 PhotoVO put into a 1200 x 1200 ImageWell then scaled to 200%
// 1) The image is scaled by 3x to fit into the ImageWell resulting in an image with
// size 1200 x 1500. The image is then scaled 200% to 2400 x 3000.
// unrotatedImageBounds will give us these numbers and account for
// scaling and rotation.
// 2) wellWidthToImageWidth tells us what percentage of the PhotoVO width will actually
// be put into the ImageWell for print. i.e. 1200 / 2400 = 50%
// 3) photoWidthInImageWell tells us how much of the PhotoVO's width will be put into
// the image well for print. i.e. 400 * 50% = 200 px;
// 4) widthDpiScale tells us what percentage the photo width is relative to the
// ImageWell width. i.e. 200 / 1200 = 16.7%
// 5) dpiScale is the minimum value of widthDpiScale and heightDpiScale. i.e. 16.7%
// 6) dpi is then determined by multiplying imageScale * config.printPixelsPerInch
// i.e. 16.7% * 300 = 50 dpi
// Config from Model
var config:ConfigVO = AppModel.instance.config;
// Get the Actual Image Bounds width and height even if the image is rotated and scaled
var unrotatedImageBounds:Rectangle = image.displayObject.getBounds(image.displayObject);
unrotatedImageBounds.width *= Math.abs(image.displayObject.scaleX);
unrotatedImageBounds.height *= Math.abs(image.displayObject.scaleY);
var wellWidthToImageWidth:Number = width / unrotatedImageBounds.width;
var wellHeightToImageHeight:Number = height / unrotatedImageBounds.height;
var photoWidthInImageWell:Number = photoVO.width * wellWidthToImageWidth;
var photoHeightInImageWell:Number = photoVO.height * wellHeightToImageHeight;
var widthDpiScale:Number = photoWidthInImageWell / width;
var heightDpiScale:Number = photoHeightInImageWell / height;
// Calculate dpi in terms of Config's printPixelsPerInch
var dpiScale:Number = Math.min(widthDpiScale, heightDpiScale);
var dpi:Number = Math.round(config.printPixelsPerInch * dpiScale);
// Show warning appropriately if dpi does not meet minimum dpi requirement
if (dpi < config.printPixelsPerInchMinimum)