diff options
Diffstat (limited to 'org.eclipse.tips.core/src/org/eclipse/tips/core/TipImage.java')
-rw-r--r-- | org.eclipse.tips.core/src/org/eclipse/tips/core/TipImage.java | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/org.eclipse.tips.core/src/org/eclipse/tips/core/TipImage.java b/org.eclipse.tips.core/src/org/eclipse/tips/core/TipImage.java new file mode 100644 index 000000000..18a12e791 --- /dev/null +++ b/org.eclipse.tips.core/src/org/eclipse/tips/core/TipImage.java @@ -0,0 +1,298 @@ +/**************************************************************************** + * Copyright (c) 2017, 2018 Remain Software + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wim Jongman <wim.jongman@remainsoftware.com> - initial API and implementation + *****************************************************************************/ +package org.eclipse.tips.core; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Base64; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.tips.core.internal.ImageUtil; + +/** + * Provides more information about the image to be used in the tip. The image + * aspect ratio must be around 3:2 to be comfortably displayed in the Tip UI. + * + */ +public class TipImage { + + private static final double THREE_TO_TWO = 1.5; + + /** + * Value to indicate that the height or width are to be determined by the Tip + * framework. + */ + public static final int UNDEFINED = -1; + + private String fExtension = null; + private int fMaxWidth = UNDEFINED; + private int fMaxHeight = UNDEFINED; + final private URL fURL; + private double fAspectRatio = THREE_TO_TWO; + + final private String fBase64Image; + + private static final int _4KB = 4096; + + /** + * Creates a new TipImage with the specified URL which gets read into a base 64 + * string. + * + * @param url + * the image URL which may not be null + * @throws IOException + * in case the stream of the passed URL could not be opened or read. + * + */ + public TipImage(URL url) throws IOException { + Assert.isNotNull(url); + fURL = url; + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] chunk = new byte[_4KB]; + int bytesRead; + InputStream stream = url.openStream(); + while ((bytesRead = stream.read(chunk)) > 0) { + outputStream.write(chunk, 0, bytesRead); + } + fBase64Image = "data:image/" // + + getExtension() // + + ";base64," // + + Base64.getEncoder().encodeToString(outputStream.toByteArray()); + } + + /** + * @return true if the URL constructor was used and not the base64 constructor. + */ + public boolean isURLSet() { + return fURL != null; + } + + /** + * Creates a new {@link TipImage} with the specified base64 string which must be + * a valid RFC-2397 string thats begins with + * <code>"data:image/<subtype>;base64"</code> where <code>subtype</code> + * must be a valid image type (pmg, bmp, gif, etc..). + * + * @param base64Image + * the non-null base64 encoded image according to RFC-2397. + * + * @throws RuntimeException + * if the string is not valid + * @see TipImage + * @see <a href="https://tools.ietf.org/search/rfc2397">RFC-2397 + * (https://tools.ietf.org/search/rfc2397)</a> + * + */ + public TipImage(String base64Image) { + Assert.isNotNull(base64Image); + fURL = null; + if (base64Image.matches("^data:image\\/.*?;base64,.*$")) { + fBase64Image = base64Image; + int from = base64Image.indexOf("/") + 1; + int to = base64Image.indexOf(";"); + setExtension(base64Image.substring(from, to).trim()); + setExtension(base64Image.substring(from, to).trim()); + } else { + int length = base64Image.length(); + throw new RuntimeException("Wrong base64 data " + base64Image.substring(0, length < 50 ? length : 50)); + } + } + + /** + * Sets the maximum height that this image can display. For example, if you have + * a 32x32 image the framework will blow it up to a larger size which will not + * work for the image and you might want to pass 64 to indicate that the image + * cannot be resized passed 64 pixels. If the height is not set or set to + * {@link #UNDEFINED}, then it is automatically resized based on aspect ratio + * and maximum width. + * + * @param maxHeight + * the maximum height for this image or {@link #UNDEFINED} + * @return this + * @see #setAspectRatio(double) + * @see #setAspectRatio(int, int, boolean) + * @see #setMaxWidth(int) + */ + public TipImage setMaxHeight(int maxHeight) { + fMaxHeight = maxHeight; + return this; + } + + /** + * Sets the maximum width that this image can display. For example, if you have + * a 32x32 image the framework will blow it up to a larger size which will not + * work for the image and you might want to pass 64 to indicate that the image + * cannot be resized passed 64 pixels. If the width is not set or set to + * {@link #UNDEFINED}, it is automatically resized based on aspect ratio and + * maximum height. + * + * @param maxWidth + * the maximum width for this image or {@link #UNDEFINED} + * @return this + * @see #setAspectRatio(double) + * @see #setAspectRatio(int, int, boolean) + * @see #setMaxHeight(int) + */ + public TipImage setMaxWidth(int maxWidth) { + fMaxWidth = maxWidth; + return this; + } + + /** + * Sets the aspect ratio of this image. If the image is 300 wide and 600 high + * then the aspect ratio is 300/600 = 0,5 (1:2). If your image is 1200 wide and + * 250 high then the aspect ratio (1200/250) = 4,8. With the supplied values the + * best dimensions for the image can be calculated give the available space in + * the UI. + * <p> + * In case you pass true for <code>pSetAsMax</code> then the image can not be + * up-scaled beyond the specified size. So if your image is 200x200 and you want + * a maximum up-scale of 2 then pass 400x400 to this method to maintain the + * aspect ratio but allow the image to be resized to maximum it's double size. + * <p> + * The recommended aspect ratio is around 3:2 (1.5) to be comfortably displayed + * in the Tip UI. + * + * @param width + * the width of the image, must be greater than 0 + * @param height + * the height of the image, must be greater than 0 + * @param setAsMax + * true to set the passed width and height as the maximum width and + * height for the image + * @return this + * @see #setAspectRatio(double) + * @see #getIMGAttributes(int, int) + * @see #setMaxHeight(int) + * @see #setMaxWidth(int) + */ + public TipImage setAspectRatio(int width, int height, boolean setAsMax) { + Assert.isTrue(width > 0); + Assert.isTrue(height > 0); + fAspectRatio = (double) width / (double) height; + if (setAsMax) { + setMaxHeight(height); + setMaxWidth(width); + } + return this; + } + + /** + * Sets the aspect ratio of your image which is defined by width divided by + * height. + * <p> + * The recommended aspect ratio is around 3:2 (1.5) to be comfortably displayed + * in the Tip UI. + * + * + * @param aspectRatio + * the aspect ration + * @return this + * @see #setAspectRatio(int, int, boolean) + * @see #getIMGAttributes(int, int) + */ + public TipImage setAspectRatio(double aspectRatio) { + fAspectRatio = aspectRatio; + return this; + } + + /** + * Changes the default value "null" to the passed value which commonly is "png", + * "gif" and such. + * + * @param extension + * the extension of this file + * @return this + * @see #getExtension() + */ + public TipImage setExtension(String extension) { + fExtension = extension; + return this; + } + + /** + * Returns the base64 encoded image string according to RFC-2397 or null. The + * recommended aspect ratio is around 3:2. + * + * @return the base64 encoded image string according to RFC-2397 or null + */ + public String getBase64Image() { + return fBase64Image; + } + + /** + * Returns the width and height attributes of the HTML IMG tag. + * + * <pre> + * <img src="smiley.gif" height="42" width="42"> + * </pre> + * + * The available space in the UI is passed and with it the best size of the + * image will be calculated based on the aspect ratio of this image. + * + * Clients may override if they can provide better information. + * + * @param widthHint + * the available width which must be greater than 0 + * @param heightHint + * the available height which must be greater than 0 + * @return the attributes in the HTML img tag + * @see TipImage#setAspectRatio(double) + * @see TipImage#setAspectRatio(int, int, boolean) + * @see TipImage#setMaxHeight(int) + * @see TipImage#setMaxWidth(int) + */ + public String getIMGAttributes(int widthHint, int heightHint) { + + int myWidthHint = (fMaxWidth == UNDEFINED) ? widthHint : Math.min(widthHint, fMaxWidth); + int myHeightHint = (fMaxHeight == UNDEFINED) ? heightHint : Math.min(heightHint, fMaxHeight); + + int width = ImageUtil.getWidth(fAspectRatio, myWidthHint, myHeightHint); + int height = ImageUtil.getHeight(fAspectRatio, myWidthHint, myHeightHint); + + String result = ""; + if (fMaxWidth == UNDEFINED) { + result += " width=\"" + width + "\""; + } else { + result += " width=\"" + Math.min(fMaxWidth, width) + "\""; + } + + if (fMaxHeight == UNDEFINED) { + result += " height=\"" + height + "\""; + } else { + result += " height=\"" + Math.min(fMaxHeight, height) + "\""; + } + return result; + } + + /** + * Returns the image extension for use in the IMG tag for the data attribute + * (<code>data:image/???</code>). If the extension is not set in this object, + * then the URL is examined to find the extension. If that can not be determined + * then "png" is returned. + * + * @return the extension + */ + private String getExtension() { + if (fExtension != null) { + return fExtension; + } + String[] split = fURL.getPath().split("\\."); + if (split.length > 1) { + fExtension = split[split.length - 1]; + } else { + fExtension = "png"; + } + return fExtension; + } +}
\ No newline at end of file |