/** * OLAT - Online Learning and Training<br> * http://www.olat.org * <p> * Licensed under the Apache License, Version 2.0 (the "License"); <br> * you may not use this file except in compliance with the License.<br> * You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing,<br> * software distributed under the License is distributed on an "AS IS" BASIS, <br> * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> * See the License for the specific language governing permissions and <br> * limitations under the License. * <p> * Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> * University of Zurich, Switzerland. * <hr> * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * This file has been modified by the OpenOLAT community. Changes are licensed * under the Apache 2.0 license as the original file. * <p> */ package org.olat.core.gui.components.image; import java.io.File; import java.util.Collections; import java.util.UUID; import javax.servlet.http.HttpServletRequest; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.services.image.ImageService; import org.olat.core.commons.services.image.Size; import org.olat.core.commons.services.video.MovieService; import org.olat.core.dispatcher.mapper.Mapper; import org.olat.core.dispatcher.mapper.MapperService; import org.olat.core.dispatcher.mapper.manager.MapperKey; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.AbstractComponent; import org.olat.core.gui.components.ComponentRenderer; import org.olat.core.gui.control.Disposable; import org.olat.core.gui.media.MediaResource; import org.olat.core.gui.render.ValidationResult; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.StringHelper; import org.olat.core.util.UserSession; import org.olat.core.util.WebappHelper; import org.olat.core.util.vfs.LocalFileImpl; import org.olat.core.util.vfs.VFSLeaf; import org.olat.core.util.vfs.VFSMediaResource; /** * Description: <br> * * @author Felix Jost */ public class ImageComponent extends AbstractComponent implements Disposable { private static final ComponentRenderer RENDERER = new ImageRenderer(); private static final OLog log = Tracing.createLoggerFor(ImageComponent.class); private VFSLeaf media; private String mimeType; private String alt; private final MapperKey mapperUrl; private final MediaMapper mapper; // optional in case of video: poster image private VFSLeaf poster; private MapperKey posterMapperUrl; private MediaMapper posterMapper; private Size realSize; private Size scaledSize; private float scalingFactor; private boolean cropSelectionEnabled = false; /** * @param name */ public ImageComponent(UserSession usess, String name) { super(name); mapper = new MediaMapper(); String mapperId = UUID.randomUUID().toString(); mapperUrl = CoreSpringFactory.getImpl(MapperService.class).register(usess, mapperId, mapper); // optional poster frame for videos posterMapper = new MediaMapper(); mapperId = UUID.randomUUID().toString(); posterMapperUrl = CoreSpringFactory.getImpl(MapperService.class).register(usess, mapperId, posterMapper); // renderer provides own DOM ID setDomReplacementWrapperRequired(false); } public String getAlt() { return alt; } public void setAlt(String alt) { this.alt = alt; } /** * @see org.olat.core.gui.components.Component#dispatchRequest(org.olat.core.gui.UserRequest) */ @Override protected void doDispatchRequest(UserRequest ureq) { // } public boolean isCropSelectionEnabled() { return cropSelectionEnabled; } public void setCropSelectionEnabled(boolean enable) { cropSelectionEnabled = enable; } /** * @return Long */ public Size getScaledSize() { return scaledSize; } public Size getRealSize() { if(realSize == null) { String suffix = getSuffix(getMimeType()); if(StringHelper.containsNonWhitespace(suffix)) { if(suffix.equalsIgnoreCase("jpg") || suffix.equalsIgnoreCase("png") || suffix.equalsIgnoreCase("jpeg")) { realSize = CoreSpringFactory.getImpl(ImageService.class).getSize(media, suffix); } else if(suffix.equalsIgnoreCase("mp4") || suffix.equalsIgnoreCase("m4v") || suffix.equalsIgnoreCase("flv")) { realSize = CoreSpringFactory.getImpl(MovieService.class).getSize(media, suffix); } } } return realSize; } public float getScalingFactor() { return scalingFactor; } public VFSLeaf getMedia() { return media; } public VFSLeaf getPoster() { return poster; } @Override public void dispose() { if(mapper != null) { CoreSpringFactory.getImpl(MapperService.class).cleanUp(Collections.<MapperKey>singletonList(mapperUrl)); } if(posterMapper != null) { CoreSpringFactory.getImpl(MapperService.class).cleanUp(Collections.<MapperKey>singletonList(posterMapperUrl)); } } /** * Sets the image to be delivered. The image can be * delivered several times. Don't set a resource which * can be only send once. * * @param mediaResource */ public void setMedia(VFSLeaf media) { setDirty(true); this.media = media; this.mimeType = null; mapper.setMediaFile(media); realSize = null; } public void setMedia(VFSLeaf media, String mimeType) { setDirty(true); this.media = media; this.mimeType = mimeType; mapper.setMediaFile(media); realSize = null; } public void setMedia(File mediaFile) { setDirty(true); setMedia(new LocalFileImpl(mediaFile)); } public void setPoster(VFSLeaf poster) { setDirty(true); this.poster = poster; posterMapper.setMediaFile(poster); } public String getMapperUrl() { return mapperUrl.getUrl(); } public String getPosterMapperUrl() { return posterMapperUrl.getUrl(); } public String getMimeType() { if(mimeType != null) { return mimeType; } if(media == null) { return null; } return WebappHelper.getMimeType(media.getName()); } @Override public ComponentRenderer getHTMLRendererSingleton() { return RENDERER; } @Override public void validate(UserRequest ureq, ValidationResult vr) { super.validate(ureq, vr); if(isCropSelectionEnabled()) { vr.getJsAndCSSAdder().addRequiredStaticJsFile("js/jquery/cropper/cropper.min.js"); } } /** * Call this method to display the image within a given box of width and * height. The method does NOT manipulate the image itself, it does only * adjust the images width and height tag. <br /> * The image will made displayed smaller, it will not enlarge the image since * this always looks bad. The scaling is done in a way to get an image that is * smaller than the maxWidth or smaller than the maxHeight, depending on whith * of the sizes produce a smaller scaling factor. <br /> * To scale an image on the filesystem to another width and height, use the * ImageHelper.scaleImage() method. * * @param maxWidth * @param maxHeight */ public void setMaxWithAndHeightToFitWithin(int maxWidth, int maxHeight) { if (media == null || !media.exists()) { scalingFactor = Float.NaN; realSize = null; scaledSize = null; return; } try { Size size = getRealSize(); if(size == null) { return; } int realWidth = size.getWidth(); int realHeight = size.getHeight(); // calculate scaling factor scalingFactor = 1f; if (realWidth > maxWidth) { float scalingWidth = 1f / realWidth * maxWidth; scalingFactor = (scalingWidth < scalingFactor ? scalingWidth : scalingFactor); } if (realHeight > maxHeight) { float scalingHeight = 1f / realHeight * maxHeight; scalingFactor = (scalingHeight < scalingFactor ? scalingHeight : scalingFactor); } realSize = new Size(realWidth, realHeight, false); scaledSize = new Size(Math.round(realWidth * scalingFactor), Math.round(realHeight * scalingFactor), false); setDirty(true); } catch (Exception e) { // log error, don't do anything else log.error("Problem while setting image size to fit " + maxWidth + "x" + maxHeight + " for resource::" + media, e); } } protected String getSuffix(String contentType) { contentType = contentType.toLowerCase(); if(contentType.indexOf("jpg") >= 0 || contentType.indexOf("jpeg") >= 0) { return "jpg"; } if(contentType.indexOf("gif") >= 0) { return "gif"; } if(contentType.indexOf("png") >= 0) { return "png"; } if(contentType.indexOf("png") >= 0) { return "png"; } if(contentType.indexOf("m4v") >= 0) { return "m4v"; } if(contentType.indexOf("mp4") >= 0) { return "mp4"; } if(contentType.indexOf("flv") >= 0) { return "flv"; } return null; } private static class MediaMapper implements Mapper { private VFSLeaf mediaFile; public void setMediaFile(VFSLeaf mediaFile) { this.mediaFile = mediaFile; } @Override public MediaResource handle(String relPath, HttpServletRequest request) { return new VFSMediaResource(mediaFile); } } }