001package Torello.HTML.Tools.Images; 002 003import java.util.regex.*; 004import java.io.*; 005 006import java.awt.image.BufferedImage; 007import javax.imageio.ImageIO; 008import java.util.Base64; 009import java.net.URL; 010 011import Torello.Java.Additional.Ret2; 012 013/** 014 * An enumeration of the primary image-types available on the internet. 015 * 016 * <BR /><BR />This is just an enumerated-type used to ensure proper parameter-requests when 017 * downloading images. The type provides a simple means for storing words such as <code>'jpg,' 018 * 'png,' 'gif,' etc...</code> when attempting to download images. 019 * 020 * @see ImageScrape 021 * @see ImageScraper 022 */ 023public enum IF 024{ 025 // ******************************************************************************************** 026 // ******************************************************************************************** 027 // The Constants 028 // ******************************************************************************************** 029 // ******************************************************************************************** 030 031 032 /** 033 * Used to indicate a picture using the common {@code '.jpg'} image format. 034 * According to a <B>Yahoo! Search</B> link: 035 * 036 * <BR /><BR /><CODE> The JPEG file extension is used interchangeably 037 * with JPG. JPEG stands for Joint Photographic Experts Group who created the standard. 038 * JPG files have 2 sub-formats, JPG/ Exif (often used in digital cameras and photographic 039 * equipment), and JPG/ JFIF (often used on the World Wide Web).</CODE> 040 * 041 * <BR /><BR />What is JPG? What Opens a JPG? Exact Link: 042 * 043 * <BR /><BR /><A HREF="http://whatis.techtarget.com/fileformat/JPG-JPEG-bitmap" TARGET=_blank> 044 * http://whatis.techtarget.com/fileformat/JPG-JPEG-bitmap</A> 045 */ 046 JPG("jpg", "jpeg"), 047 048 /** 049 * Used to indicate a picture using the common ommon {@code '.gif'} image format. 050 * Short for <CODE><B>"Graphics Interchange Format".</B></CODE> 051 */ 052 GIF("gif"), 053 054 /** 055 * Used to indicate a picture using the common ommon {@code '.bmp'} image format. 056 * Abbreviation of the word <CODE><B>'Bit Map'</B></CODE> 057 */ 058 BMP("bmp"), 059 060 /** 061 * Used to indicate a picture using the common {@code '.png'} image format. 062 * <CODE><B>PNG</B></CODE> stands for <CODE><B>Portable Network Graphics.</B></CODE> 063 * It is an open source file extension for raster graphics files. 064 */ 065 PNG("png"); 066 067 068 // ******************************************************************************************** 069 // ******************************************************************************************** 070 // Fields 071 // ******************************************************************************************** 072 // ******************************************************************************************** 073 074 075 /** This is the actual file-name extension saved as a {@code String}. */ 076 public final String extension; 077 078 /** 079 * This field is always just null, except for the case of the {@code 'JPG'} Enumeration 080 * Constant. For that Image-Format this simply evaluates to the {@code String 'jpeg'}. 081 */ 082 public final String alternateExtension; 083 084 /** 085 * This will parse a {@code 'Base64' String} into two groups using Java's RegEx Tools. 086 * 087 * <BR /><DIV CLASS=EXAMPLE>{@code 088 * import java.util.regex.Matcher; 089 * ... 090 * 091 * Matcher m = IF.B64_INIT_STRING.matcher(base64String); 092 * if (m.find()) 093 * { ... } 094 * }</DIV> 095 * 096 * <BR /><BR /><OL CLASS=JDOL> 097 * <LI> {@code m.group(1) => } Image Encoding Type-{@code String} ({@code "gif", "jpg",} etc..) 098 * <BR /><BR /> 099 * </LI> 100 * <LI>{@code m.gropu(2) => } Base 64 Encoded Image-{@code String} 101 * <BR /> 102 * </LI> 103 * </OL> 104 */ 105 public static final Pattern B64_INIT_STRING = Pattern.compile( 106 "^\\s*data:\\s*image\\/\\s*([A-Za-z]{3,4})\\s*;\\s*base64\\s*,(.*)$", 107 Pattern.CASE_INSENSITIVE 108 ); 109 110 private static final IF[] arr = { JPG, GIF, BMP, PNG }; 111 112 113 // ******************************************************************************************** 114 // ******************************************************************************************** 115 // Constructors 116 // ******************************************************************************************** 117 // ******************************************************************************************** 118 119 120 // Used for GIF, BMP & PNG 121 private IF(String extension) 122 { 123 this.extension = extension; 124 this.alternateExtension = null; 125 } 126 127 // Used for JPG 128 private IF(String extension, String alternateExtension) 129 { 130 this.extension = extension; 131 this.alternateExtension = alternateExtension; 132 } 133 134 135 // ******************************************************************************************** 136 // ******************************************************************************************** 137 // "Guess the Extension" Methods 138 // ******************************************************************************************** 139 // ******************************************************************************************** 140 141 142 /** 143 * This will extract the file-extension from an image {@code URL}. Not all images on the 144 * internet have {@code URL's} that end with the actual image-file-type. In that case, or in 145 * the case that the {@code 'uriStr'} is a pointer to a non-image-file, {@code 'null'} will 146 * be returned. 147 * 148 * @param uriStr Is the {@code uri} or File-Name of an image. 149 * 150 * @return If extension has a file-extension that is listed in the {@code IF[]} Array - that 151 * file-extension will be returned, otherwise {@code 'null'} will be returned. 152 */ 153 public static IF getGuess(String uriStr) 154 { 155 if (uriStr == null) return null; 156 157 int pos = uriStr.lastIndexOf("."); 158 159 if (pos == -1) return null; 160 if (pos == uriStr.length() - 1) return null; 161 162 String s = uriStr.substring(pos + 1).toLowerCase().trim(); 163 164 // The following array is a private & static array defined above 165 // NOTE: private static final IF[] arr = { JPG, GIF, BMP, PNG }; 166 167 for (int i=0; i < arr.length; i++) 168 169 if (arr[i].extension.equals(s)) return arr[i]; 170 171 else if ( (arr[i].alternateExtension != null) 172 && (arr[i].alternateExtension.equals(s))) 173 174 return arr[i]; 175 176 return null; 177 } 178 179 /** 180 * Invokes {@link #getGuess(String)}, and returns the results - <I>unless the returned result 181 * would be null, in which case a {@link UnrecognizedImageExtException} is thrown instead</I>. 182 * 183 * @param uriStr Is the {@code uri} or File-Name of the image. 184 * 185 * @return The Image-Format of this Image, based on it's File-Name 186 * 187 * @throws UnrecognizedImageExtException If the Image-Type cannot be determined (does not match 188 * any) based on its File-Name Extension. ({@code '.jpg', '.png', '.gif'} etc...) 189 */ 190 public static IF guessOrThrow(String uriStr) 191 { 192 IF ret = getGuess(uriStr); 193 if (ret != null) return ret; 194 195 throw new UnrecognizedImageExtException( 196 "The URI or File-Name\n" + 197 "[" + uriStr + "]\n" + 198 "doesn't have a File-Extension that matches any of the recognized Image-Types " + 199 "('.jpg', '.png', '.gif' etc...)" 200 ); 201 } 202 203 /** 204 * Converts a {@code String} image-extension to an instance this enumerated type. 205 * @param extension A valid image-format extension 206 * @return An instance of this enumeration, if applicable, or {@code 'null'} otherwise. 207 */ 208 public static IF get(String extension) 209 { 210 extension = extension.toLowerCase().trim(); 211 212 // The following array is a private & static array defined above 213 // NOTE: private static final IF[] arr = { JPG, GIF, BMP, PNG }; 214 215 for (int i=0; i < arr.length; i++) 216 217 if (arr[i].extension.equals(extension)) return arr[i]; 218 219 else if ( (arr[i].alternateExtension != null) 220 && (arr[i].alternateExtension.equals(extension))) 221 222 return arr[i]; 223 224 return null; 225 } 226 227 /** 228 * This will retrieve the image name from a {@code java.net.URL} object. 229 * 230 * @param url The {@code url} of the image. 231 * 232 * @return If this {@code URL} has a file-extension that is listed in the {@code IF[]} Array, 233 * that file-extension will be returned, otherwise {@code 'null'} will be returned. 234 */ 235 public static IF getGuess(URL url) 236 { 237 String f = url.getFile(); 238 239 return (f != null) ? getGuess(f) : null; 240 } 241 242 243 // ******************************************************************************************** 244 // ******************************************************************************************** 245 // Decode Base-64 String Methods 246 // ******************************************************************************************** 247 // ******************************************************************************************** 248 249 250 /** 251 * This will retrieve a Buffered Image from a {@code String} retrieved from a string that 252 * follows this format below. This is the format usually found inside HTML Image Tags. 253 * 254 * <BR /><BR /><B>SPECIFICALLY: </B><SPAN STYLE="color: green;"> 255 * {@code <IMG SRC="data:image/{png or gif or jpg etc};base64,...">}</SPAN> 256 * 257 * <BR /><BR />The ellipsis (...) above represents the actual {@code Base-64} encoded 258 * {@code String}. Many web-sites return HTML image tags with the actual picture/image encoded 259 * into a {@code String} and saved inside the {@code 'SRC'} attribute. This method will decode 260 * that image-as-a-{@code String} into a {@code java.awt.image.BufferedImage} 261 * 262 * @param base64EncodedImageWithFormat The best way to obtain this {@code String} is to use the 263 * command [{@code String encoded = imageTag.AV("src"); }], and pass this variable 264 * {@code 'encoded'} to this parameter. It is important to note that variable 265 * {@code 'imageTag'} must be a {@code public class TagNode}, and that {@code TagNode} must: 266 * 267 * <BR /><BR /><UL CLASS=JDUL> 268 * <LI> Have {@code public final String tok} equal to {@code 'img'} 269 * <BR /><BR /> 270 * </LI> 271 * 272 * <LI> The {@code <IMG>} represented must have a {@code SRC="..."} which contains a 273 * {@code Base-64} encoded image. 274 * </LI> 275 * </UL> 276 * 277 * @return A decoded image that can be saved to file, and an instance of {@code IF} that 278 * identifies what type of image was specified. 279 * 280 * <BR /><BR /><UL CLASS=JDUL> 281 * <LI> {@code Ret2.a} (BufferedImage):} The Converted Image 282 * <BR /><BR /> 283 * </LI> 284 * 285 * <LI> {@code Ret2.b} (IF):} The Image Type 286 * </LI> 287 * </UL> 288 */ 289 public static Ret2<BufferedImage, IF> 290 decodeBase64ToImage(String base64EncodedImageWithFormat) 291 { 292 // sourceData = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPAAAADwCAYAAAA+VemSA...=='; 293 Matcher m = B64_INIT_STRING.matcher(base64EncodedImageWithFormat); 294 295 if (! m.find()) return null; 296 297 String imageFormatStr = m.group(1); 298 String base64EncodedImage = m.group(2); 299 300 IF imageFormat = (imageFormatStr != null) ? IF.get(imageFormatStr) : null; 301 302 if (imageFormat == null) return null; 303 304 BufferedImage bi = decodeBase64ToImage(base64EncodedImage, imageFormat); 305 306 if (bi == null) return null; 307 308 return new Ret2<BufferedImage, IF>(bi, imageFormat); 309 } 310 311 /** 312 * This will decode a {@code Base-64 String} into an image. Here, the decoder used is the one 313 * obtained from a call to: {@code java.util.Base64.getDecoder() }. 314 * 315 * <BR /><BR /><SPAN CLASS=CopiedJDK>Text copied from class: 316 * {@code java.util.Base64}, <B>JDK 1.8</B></SPAN> 317 * 318 * <BR /><BR /><B>Basic: </B> Uses "The Base64 Alphabet" as specified in <I><B>Table 1 of RFC 319 * 4648 and RFC 2045</B></I> for encoding and decoding operation. The encoder does not add any 320 * line feed (line separator) character. The decoder rejects data that contains characters 321 * outside the base64 alphabet. 322 * 323 * @return A decoded image that can be saved to file. 324 */ 325 public static BufferedImage decodeBase64ToImage(String base64EncodedImage, IF imageFormat) 326 { 327 try 328 (ByteArrayInputStream bis = new ByteArrayInputStream 329 (Base64.getDecoder().decode(base64EncodedImage))) 330 331 { return ImageIO.read(bis); } 332 333 catch (IOException e) { return null; } 334 } 335 336 /** 337 * This will decode a base-64 String into an image. Here, the decoder used is the one obtained 338 * from a call to: {@code java.util.Base64.getURLDecoder() }. 339 * 340 * <BR /><BR /><SPAN CLASS=CopiedJDK>Text copied from class: 341 * {@code java.util.Base64}, <B>JDK 1.8</B></SPAN> 342 * 343 * <BR /><BR /><B>URL and Filename safe: </B> Uses the "URL and Filename safe Base64 Alphabet" 344 * as specified in <B><I>Table 2 of RFC 4648</B></I> for encoding and decoding. The encoder 345 * does not add any line feed (line separator) character. The decoder rejects data that 346 * contains characters outside the base64 alphabet. 347 * 348 * @return A decoded image that can be saved to file. 349 */ 350 public static BufferedImage decodeBase64ToImage_V2(String base64EncodedImage, IF imageFormat) 351 { 352 try 353 (ByteArrayInputStream bis = new ByteArrayInputStream 354 (Base64.getUrlDecoder().decode(base64EncodedImage))) 355 356 { return ImageIO.read(bis); } 357 358 catch (IOException e) { return null; } 359 } 360 361 362 // ******************************************************************************************** 363 // ******************************************************************************************** 364 // java.lang.Object 365 // ******************************************************************************************** 366 // ******************************************************************************************** 367 368 369 /** 370 * Convert an instance of this enumerated-type to a {@code String}. 371 * @return The image-format extension as a {@code String}. 372 */ 373 public String toString() { return extension; } 374}