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 * 098 * <LI> {@code m.group(1) => } Image Encoding Type-{@code String} ({@code "gif", "jpg",} etc..) 099 * <BR /><BR /> 100 * </LI> 101 * 102 * <LI>{@code m.gropu(2) => } Base 64 Encoded Image-{@code String} 103 * <BR /> 104 * </LI> 105 * </OL> 106 */ 107 public static final Pattern B64_INIT_STRING = Pattern.compile( 108 "^\\s*data:\\s*image\\/\\s*([A-Za-z]{3,4})\\s*;\\s*base64\\s*,(.*)$", 109 Pattern.CASE_INSENSITIVE 110 ); 111 112 private static final IF[] arr = { JPG, GIF, BMP, PNG }; 113 114 115 // ******************************************************************************************** 116 // ******************************************************************************************** 117 // Constructors 118 // ******************************************************************************************** 119 // ******************************************************************************************** 120 121 122 // Used for GIF, BMP & PNG 123 private IF(String extension) 124 { 125 this.extension = extension; 126 this.alternateExtension = null; 127 } 128 129 // Used for JPG 130 private IF(String extension, String alternateExtension) 131 { 132 this.extension = extension; 133 this.alternateExtension = alternateExtension; 134 } 135 136 137 // ******************************************************************************************** 138 // ******************************************************************************************** 139 // "Guess the Extension" Methods 140 // ******************************************************************************************** 141 // ******************************************************************************************** 142 143 144 /** 145 * This will extract the file-extension from an image {@code URL}. Not all images on the 146 * internet have {@code URL's} that end with the actual image-file-type. In that case, or in 147 * the case that the {@code 'uriStr'} is a pointer to a non-image-file, {@code 'null'} will 148 * be returned. 149 * 150 * @param uriStr Is the {@code uri} or File-Name of an image. 151 * 152 * @return If extension has a file-extension that is listed in the {@code IF[]} Array - that 153 * file-extension will be returned, otherwise {@code 'null'} will be returned. 154 */ 155 public static IF getGuess(String uriStr) 156 { 157 if (uriStr == null) return null; 158 159 int pos = uriStr.lastIndexOf("."); 160 161 if (pos == -1) return null; 162 if (pos == uriStr.length() - 1) return null; 163 164 String s = uriStr.substring(pos + 1).toLowerCase().trim(); 165 166 167 // The following array is a private & static array defined above 168 // NOTE: private static final IF[] arr = { JPG, GIF, BMP, PNG }; 169 170 for (int i=0; i < arr.length; i++) 171 172 if (arr[i].extension.equals(s)) return arr[i]; 173 174 else if ( (arr[i].alternateExtension != null) 175 && (arr[i].alternateExtension.equals(s))) 176 177 return arr[i]; 178 179 return null; 180 } 181 182 /** 183 * Invokes {@link #getGuess(String)}, and returns the results - <I>unless the returned result 184 * would be null, in which case a {@link UnrecognizedImageExtException} is thrown instead</I>. 185 * 186 * @param uriStr Is the {@code uri} or File-Name of the image. 187 * 188 * @return The Image-Format of this Image, based on it's File-Name 189 * 190 * @throws UnrecognizedImageExtException If the Image-Type cannot be determined (does not match 191 * any) based on its File-Name Extension. ({@code '.jpg', '.png', '.gif'} etc...) 192 */ 193 public static IF guessOrThrow(String uriStr) 194 { 195 IF ret = getGuess(uriStr); 196 if (ret != null) return ret; 197 198 throw new UnrecognizedImageExtException( 199 "The URI or File-Name\n" + 200 "[" + uriStr + "]\n" + 201 "doesn't have a File-Extension that matches any of the recognized Image-Types " + 202 "('.jpg', '.png', '.gif' etc...)" 203 ); 204 } 205 206 /** 207 * Converts a {@code String} image-extension to an instance this enumerated type. 208 * @param extension A valid image-format extension 209 * @return An instance of this enumeration, if applicable, or {@code 'null'} otherwise. 210 */ 211 public static IF get(String extension) 212 { 213 extension = extension.toLowerCase().trim(); 214 215 // The following array is a private & static array defined above 216 // NOTE: private static final IF[] arr = { JPG, GIF, BMP, PNG }; 217 218 for (int i=0; i < arr.length; i++) 219 220 if (arr[i].extension.equals(extension)) return arr[i]; 221 222 else if ( (arr[i].alternateExtension != null) 223 && (arr[i].alternateExtension.equals(extension))) 224 225 return arr[i]; 226 227 return null; 228 } 229 230 /** 231 * This will retrieve the image name from a {@code java.net.URL} object. 232 * 233 * @param url The {@code url} of the image. 234 * 235 * @return If this {@code URL} has a file-extension that is listed in the {@code IF[]} Array, 236 * that file-extension will be returned, otherwise {@code 'null'} will be returned. 237 */ 238 public static IF getGuess(URL url) 239 { 240 String f = url.getFile(); 241 242 return (f != null) ? getGuess(f) : null; 243 } 244 245 246 // ******************************************************************************************** 247 // ******************************************************************************************** 248 // Decode Base-64 String Methods 249 // ******************************************************************************************** 250 // ******************************************************************************************** 251 252 253 /** 254 * This will retrieve a Buffered Image from a {@code String} retrieved from a string that 255 * follows this format below. This is the format usually found inside HTML Image Tags. 256 * 257 * <BR /><BR /><DIV CLASS=JDHint> 258 * <B STYLE='color:red;'>Specifically:</B> <SPAN STYLE="color: green;"> 259 * {@code <IMG SRC="data:image/{png or gif or jpg etc};base64,...">}</SPAN> 260 * </DIV> 261 * 262 * <BR />The ellipsis (...) above represents the actual {@code Base-64} encoded {@code String}. 263 * Many web-sites return HTML image tags with the actual picture/image encoded into a 264 * {@code String} and saved inside the {@code 'SRC'} attribute. This method will decode that 265 * image-as-a-{@code String} into a {@code java.awt.image.BufferedImage} 266 * 267 * @param base64EncodedImageWithFormat The best way to obtain this {@code String} is to use the 268 * command [{@code String encoded = imageTag.AV("src"); }], and pass this variable 269 * {@code 'encoded'} to this parameter. It is important to note that variable 270 * {@code 'imageTag'} must be a {@code public class TagNode}, and that {@code TagNode} must: 271 * 272 * <BR /><BR /><UL CLASS=JDUL> 273 * <LI> Have {@code public final String tok} equal to {@code 'img'} 274 * <BR /><BR /> 275 * </LI> 276 * 277 * <LI> The {@code <IMG>} represented must have a {@code SRC="..."} which contains a 278 * {@code Base-64} encoded image. 279 * </LI> 280 * </UL> 281 * 282 * @return A decoded image that can be saved to file, and an instance of {@code IF} that 283 * identifies what type of image was specified. 284 * 285 * <BR /><BR /><UL CLASS=JDUL> 286 * 287 * <LI> {@code Ret2.a} (BufferedImage):} The Converted Image 288 * <BR /><BR /> 289 * </LI> 290 * 291 * <LI> {@code Ret2.b} (IF):} The Image Type 292 * </LI> 293 * </UL> 294 */ 295 public static Ret2<BufferedImage, IF> 296 decodeBase64ToImage(String base64EncodedImageWithFormat) 297 { 298 // sourceData = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPAAAADwCAYAAAA+VemSA...=='; 299 final Matcher m = B64_INIT_STRING.matcher(base64EncodedImageWithFormat); 300 301 if (! m.find()) return null; 302 303 final String imageFormatStr = m.group(1); 304 final String base64EncodedImage = m.group(2); 305 306 final IF imageFormat = (imageFormatStr != null) 307 ? IF.get(imageFormatStr) 308 : null; 309 310 if (imageFormat == null) return null; 311 312 final BufferedImage bi = 313 decodeBase64ToImage(base64EncodedImage, imageFormat); 314 315 if (bi == null) return null; 316 317 return new Ret2<BufferedImage, IF>(bi, imageFormat); 318 } 319 320 /** 321 * This will decode a {@code Base-64 String} into an image. Here, the decoder used is the one 322 * obtained from a call to: {@code java.util.Base64.getDecoder() }. 323 * 324 * <BR /><BR /><SPAN CLASS=CopiedJDK>Text copied from class: 325 * {@code java.util.Base64}, <B>JDK 1.8</B></SPAN> 326 * 327 * <BR /><DIV CLASS=JDHint> 328 * <B STYLE='color:red;'>Basic: </B> Uses "The Base64 Alphabet" as specified in <I><B>Table 1 of RFC 329 * 4648 and RFC 2045</B></I> for encoding and decoding operation. The encoder does not add any 330 * line feed (line separator) character. The decoder rejects data that contains characters 331 * outside the base64 alphabet. 332 * </DIV> 333 * 334 * @return A decoded image that can be saved to file. 335 */ 336 public static BufferedImage decodeBase64ToImage(String base64EncodedImage, IF imageFormat) 337 { 338 try 339 (ByteArrayInputStream bis = new ByteArrayInputStream 340 (Base64.getDecoder().decode(base64EncodedImage))) 341 342 { return ImageIO.read(bis); } 343 344 catch (IOException e) 345 { return null; } 346 } 347 348 /** 349 * This will decode a base-64 String into an image. Here, the decoder used is the one obtained 350 * from a call to: {@code java.util.Base64.getURLDecoder() }. 351 * 352 * <BR /><BR /><SPAN CLASS=CopiedJDK>Text copied from class: 353 * {@code java.util.Base64}, <B>JDK 1.8</B></SPAN> 354 * 355 * <BR /><DIV CLASS=JDHint> 356 * <B STYLE='color:red;'>URL and Filename safe:</B> Uses the "URL and Filename safe Base64 357 * Alphabet" as specified in <B><I>Table 2 of RFC 4648</B></I> for encoding and decoding. The 358 * encoder does not add any line feed (line separator) character. The decoder rejects data that 359 * contains characters outside the base64 alphabet. 360 * </DIV> 361 * 362 * @return A decoded image that can be saved to file. 363 */ 364 public static BufferedImage decodeBase64ToImage_V2(String base64EncodedImage, IF imageFormat) 365 { 366 try 367 (ByteArrayInputStream bis = new ByteArrayInputStream 368 (Base64.getUrlDecoder().decode(base64EncodedImage))) 369 370 { return ImageIO.read(bis); } 371 372 catch (IOException e) 373 { return null; } 374 } 375 376 377 // ******************************************************************************************** 378 // ******************************************************************************************** 379 // java.lang.Object 380 // ******************************************************************************************** 381 // ******************************************************************************************** 382 383 384 /** 385 * Convert an instance of this enumerated-type to a {@code String}. 386 * @return The image-format extension as a {@code String}. 387 */ 388 public String toString() { return extension; } 389}