diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/api/ImagesApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/api/ImagesApiResource.java index 21453b29a..8ee811448 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/api/ImagesApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/api/ImagesApiResource.java @@ -16,6 +16,7 @@ import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.ResponseBuilder; @@ -106,7 +107,10 @@ public class ImagesApiResource { @GET @Consumes({ MediaType.TEXT_PLAIN, MediaType.TEXT_HTML, MediaType.APPLICATION_JSON }) @Produces({ MediaType.TEXT_PLAIN }) - public String retrieveClientImage(@PathParam("clientId") final Long clientId) { + public Response retrieveClientImage(@PathParam("clientId") final Long clientId, @QueryParam("maxWidth") final Integer maxWidth, + @QueryParam("maxHeight") final Integer maxHeight, @QueryParam("output") final String output) { + if (output != null && (output.equals("octet") || output.equals("inline_octet"))) { return downloadClientImage(clientId, maxWidth, + maxHeight, output); } this.context.authenticatedUser().validateHasReadPermission("CLIENTIMAGE"); @@ -120,21 +124,23 @@ public class ImagesApiResource { imageDataURISuffix = ContentRepositoryUtils.IMAGE_DATA_URI_SUFFIX.PNG.getValue(); } - final String clientImageAsBase64Text = imageDataURISuffix + Base64.encodeBytes(imageData.getContent()); - return clientImageAsBase64Text; + final String clientImageAsBase64Text = imageDataURISuffix + Base64.encodeBytes(imageData.getContentOfSize(maxWidth, maxHeight)); + return Response.ok(clientImageAsBase64Text).build(); } @GET @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_OCTET_STREAM }) - public Response downloadClientImage(@PathParam("clientId") final Long clientId) { + public Response downloadClientImage(@PathParam("clientId") final Long clientId, @QueryParam("maxWidth") final Integer maxWidth, + @QueryParam("maxHeight") final Integer maxHeight, @QueryParam("output") String output) { this.context.authenticatedUser().validateHasReadPermission("CLIENTIMAGE"); final ImageData imageData = this.imageReadPlatformService.retrieveClientImage(clientId); - final ResponseBuilder response = Response.ok(imageData.getContent()); - response.header("Content-Disposition", "attachment; filename=\"" + imageData.getEntityDisplayName() + IMAGE_FILE_EXTENSION.JPEG - + "\""); + final ResponseBuilder response = Response.ok(imageData.getContentOfSize(maxWidth, maxHeight)); + String dispositionType = "inline_octet".equals(output) ? "inline" : "attachment"; + response.header("Content-Disposition", dispositionType + "; filename=\"" + imageData.getEntityDisplayName() + + IMAGE_FILE_EXTENSION.JPEG + "\""); // TODO: Need a better way of determining image type diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/contentrepository/ContentRepositoryUtils.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/contentrepository/ContentRepositoryUtils.java index a938c95d5..cac7d697d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/contentrepository/ContentRepositoryUtils.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/contentrepository/ContentRepositoryUtils.java @@ -28,6 +28,20 @@ public class ContentRepositoryUtils { public String getValue() { return this.value; } + + public static IMAGE_MIME_TYPE fromFileExtension(IMAGE_FILE_EXTENSION fileExtension) { + switch (fileExtension) { + case GIF: + return IMAGE_MIME_TYPE.GIF; + case JPG: + case JPEG: + return IMAGE_MIME_TYPE.JPEG; + case PNG: + return IMAGE_MIME_TYPE.PNG; + default: + throw new IllegalArgumentException(); + } + } } public static enum IMAGE_FILE_EXTENSION { @@ -42,6 +56,23 @@ public class ContentRepositoryUtils { public String getValue() { return this.value; } + + public String getValueWithoutDot() { + return this.value.substring(1); + } + + public IMAGE_FILE_EXTENSION getFileExtension() { + switch (this) { + case GIF: + return IMAGE_FILE_EXTENSION.GIF; + case JPEG: + return IMAGE_FILE_EXTENSION.JPEG; + case PNG: + return IMAGE_FILE_EXTENSION.PNG; + default: + throw new IllegalArgumentException(); + } + } } public static enum IMAGE_DATA_URI_SUFFIX { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/data/ImageData.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/data/ImageData.java index 8990eacf8..ad01e7316 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/data/ImageData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/data/ImageData.java @@ -1,9 +1,17 @@ package org.mifosplatform.infrastructure.documentmanagement.data; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; + +import javax.imageio.ImageIO; import org.apache.commons.lang.StringUtils; import org.apache.poi.util.IOUtils; @@ -19,7 +27,7 @@ public class ImageData { private final String entityDisplayName; private File file; - private String contentType; + private ContentRepositoryUtils.IMAGE_FILE_EXTENSION fileExtension; private InputStream inputStream; public ImageData(final Long imageId, final String location, final Integer storageType, final String entityDisplayName) { @@ -43,23 +51,76 @@ public class ImageData { } } - private String setImageContentType() { - String contentType = ContentRepositoryUtils.IMAGE_MIME_TYPE.JPEG.getValue(); + public byte[] resizeImage(InputStream in, int maxWidth, int maxHeight) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + resizeImage(in, out, maxWidth, maxHeight); + return out.toByteArray(); + } - if (this.file != null) { - final String fileName = this.file.getName(); + public void resizeImage(InputStream in, OutputStream out, int maxWidth, int maxHeight) throws IOException { - if (StringUtils.endsWith(fileName, ContentRepositoryUtils.IMAGE_FILE_EXTENSION.GIF.getValue())) { - contentType = ContentRepositoryUtils.IMAGE_MIME_TYPE.GIF.getValue(); - } else if (StringUtils.endsWith(fileName, ContentRepositoryUtils.IMAGE_FILE_EXTENSION.PNG.getValue())) { - contentType = ContentRepositoryUtils.IMAGE_MIME_TYPE.PNG.getValue(); + BufferedImage src = ImageIO.read(in); + if (src.getWidth() <= maxWidth && src.getHeight() <= maxHeight) { + out.write(getContent()); + return; + } + float widthRatio = (float) src.getWidth() / maxWidth; + float heightRatio = (float) src.getHeight() / maxHeight; + float scaleRatio = widthRatio > heightRatio ? widthRatio : heightRatio; + + // TODO(lindahl): Improve compressed image quality (perhaps quality + // ratio) + + int newWidth = (int) (src.getWidth() / scaleRatio); + int newHeight = (int) (src.getHeight() / scaleRatio); + int colorModel = fileExtension == ContentRepositoryUtils.IMAGE_FILE_EXTENSION.JPEG ? BufferedImage.TYPE_INT_RGB + : BufferedImage.TYPE_INT_ARGB; + BufferedImage target = new BufferedImage(newWidth, newHeight, colorModel); + Graphics2D g = target.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g.drawImage(src, 0, 0, newWidth, newHeight, Color.BLACK, null); + g.dispose(); + ImageIO.write(target, fileExtension != null ? fileExtension.getValueWithoutDot() : "jpeg", out); + } + + public byte[] getContentOfSize(Integer maxWidth, Integer maxHeight) { + if (maxWidth == null && maxHeight == null) { return getContent(); } + FileInputStream fis = null; + try { + fis = new FileInputStream(this.file); + byte[] out = resizeImage(fis, maxWidth != null ? maxWidth : Integer.MAX_VALUE, maxHeight != null ? maxHeight + : Integer.MAX_VALUE); + return out; + } catch (IOException ex) { + return null; + } finally { + if (fis != null) { + try { + fis.close(); + } catch (IOException ex) {} } } - return contentType; + } + + private void setImageContentType(String filename) { + fileExtension = ContentRepositoryUtils.IMAGE_FILE_EXTENSION.JPEG; + + if (StringUtils.endsWith(filename.toLowerCase(), ContentRepositoryUtils.IMAGE_FILE_EXTENSION.GIF.getValue())) { + fileExtension = ContentRepositoryUtils.IMAGE_FILE_EXTENSION.GIF; + } else if (StringUtils.endsWith(filename, ContentRepositoryUtils.IMAGE_FILE_EXTENSION.PNG.getValue())) { + fileExtension = ContentRepositoryUtils.IMAGE_FILE_EXTENSION.PNG; + } + } + + public void updateContent(final File file) { + this.file = file; + if (this.file != null) { + setImageContentType(this.file.getName()); + } } public String contentType() { - return this.contentType; + return ContentRepositoryUtils.IMAGE_MIME_TYPE.fromFileExtension(this.fileExtension).getValue(); } public StorageType storageType() { @@ -78,11 +139,6 @@ public class ImageData { this.inputStream = objectContent; } - public void updateContent(final File file) { - this.file = file; - setImageContentType(); - } - public String getEntityDisplayName() { return this.entityDisplayName; }