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 * <CODE>IF (Image Format) - Documentation.</CODE><BR /><BR />
015 * <EMBED CLASS="external-html" DATA-FILE-ID="IF">
016 * @see ImageScrape
017 * @see ImageScraper
018 */
019public enum IF
020{
021    /**
022     * Used to indicate a picture using the common {@code '.jpg'} image format.
023     * According to a <B>Yahoo! Search</B> link:
024     * 
025     * <BR /><BR /><CODE> The JPEG file extension is used interchangeably
026     * with JPG. JPEG stands for Joint Photographic Experts Group who created the standard. 
027     * JPG files have 2 sub-formats, JPG/ Exif (often used in digital cameras and photographic 
028     * equipment), and JPG/ JFIF (often used on the World Wide Web).</CODE>
029     * 
030     * <BR /><BR />What is JPG? What Opens a JPG? Exact Link:
031     * 
032     * <BR /><BR /><A HREF="http://whatis.techtarget.com/fileformat/JPG-JPEG-bitmap" TARGET=_blank>
033     * http://whatis.techtarget.com/fileformat/JPG-JPEG-bitmap</A>
034     */
035    JPG("jpg", "jpeg"),
036
037    /** 
038     * Used to indicate a picture using the common ommon {@code '.gif'} image format.
039     * Short for <CODE><B>"Graphics Interchange Format".</B></CODE>
040     */
041    GIF("gif"),
042
043    /**
044     * Used to indicate a picture using the common ommon {@code '.bmp'} image format.
045     * Abbreviation of the word <CODE><B>'Bit Map'</B></CODE>
046     */
047    BMP("bmp"),
048
049    /**
050     * Used to indicate a picture using the common {@code '.png'} image format.
051     * <CODE><B>PNG</B></CODE> stands for <CODE><B>Portable Network Graphics.</B></CODE>
052     * It is an open source file extension for raster graphics files. 
053     */
054    PNG("png");
055
056    /** This is the actual file-name extension saved as a {@code String}.  */
057    public final String extension;
058
059    /**
060     * Convert an instance of this enumerated-type to a {@code String}.
061     * @return The image-format extension as a {@code String}.
062     */
063    public String toString() { return extension; }
064
065    private final String alternateExtension;
066
067    private IF(String extension)
068    {
069        this.extension          = extension;
070        this.alternateExtension = null;
071    }
072
073    private IF(String extension, String alternateExtension)
074    {
075        this.extension          = extension;
076        this.alternateExtension = alternateExtension;
077    }
078
079    private static final IF[] arr = { JPG, GIF, BMP, PNG };
080
081    /**
082     * This will extract the file-extension from an image {@code URL}.  Not all images on the
083     * internet have {@code URL's} that end with the actual image-file-type.  In that case, or in
084     * the case that the {@code 'urlStr'} is a pointer to a non-image-file, {@code 'null'} will
085     * be returned.
086     * @param urlStr Is the {@code url} of the image. 
087     * @return If extension has a file-extension that is listed in the {@code IF[]} Array - that
088     * file-extension will be returned, otherwise {@code 'null'} will be returned.
089     */
090    public static IF getGuess(String urlStr)
091    {
092        if (urlStr == null)                 return null;
093
094        int pos = urlStr.lastIndexOf(".");
095
096        if (pos == -1)                      return null;
097        if (pos == urlStr.length() - 1)     return null;
098
099        String s = urlStr.substring(pos + 1).toLowerCase().trim();
100
101        for (int i=0; i < arr.length; i++)
102            if (arr[i].extension.equals(s))                 return arr[i];
103            else if (arr[i].alternateExtension != null)
104                if (arr[i].alternateExtension.equals(s))    return arr[i];
105
106        return null;        
107    }
108
109    /**
110     * Converts a {@code String} image-extension to an instance this enumerated type.
111     * @param extension A valid image-format extension
112     * @return An instance of this enumeration, if applicable, or {@code 'null'} otherwise.
113     */
114    public static IF get(String extension)
115    {
116        extension = extension.toLowerCase();
117        for (int i=0; i < arr.length; i++)
118            if (arr[i].extension.equals(extension))                 return arr[i];
119            else if (arr[i].alternateExtension != null)
120                if (arr[i].alternateExtension.equals(extension))    return arr[i];
121        return null;
122    }
123
124    /**
125     * This will retrieve the image name from a {@code java.net.URL} object.
126     * @param url The {@code url} of the image. 
127     * @return If this {@code URL} has a file-extension that is listed in the {@code IF[]} Array,
128     * that file-extension will be returned, otherwise {@code 'null'} will be returned.
129     */
130    public static IF getGuess(URL url)
131    { String f = url.getFile(); if (f != null) return getGuess(f); else return null; }
132
133
134    /**
135     * This will parse a {@code 'Base64' String} into two groups using Java's RegEx Tools.
136     *
137     * <BR /><DIV STYLE="EXAMPLE">{@code
138     * Matcher m = B64_INIT_STRING.matcher(base64String);
139     * if (m.find()) 
140     * }</DIV>
141     * 
142     * <BR /><BR /><OL CLASS="JDUL">
143     * <LI>{@code m.group(1) => } Image Encoding Type ({@code gif, jpg,} etc...)<BR /><BR /></LI>
144     * <LI>{@code m.gropu(2) => } Base 64 {@code String}<BR /></LI>
145     * </OL>
146     */
147    public static final Pattern B64_INIT_STRING = Pattern.compile(
148        "^\\s*data:\\s*image\\/\\s*([A-Za-z]{3,4})\\s*;\\s*base64\\s*,(.*)$",
149        Pattern.CASE_INSENSITIVE
150    );
151
152    /**
153     * This will retrieve a Buffered Image from a {@code String} retrieved from a string that
154     * follows this format below.  This is the format usually found inside HTML Image Tags.
155     * 
156     * <BR /><BR /><B>SPECIFICALLY: </B><SPAN STYLE="color: green;">
157     * {@code <IMG SRC="data:image/{png or gif or jpg etc};base64,...">}</SPAN>
158     * 
159     * <BR /><BR />The ellipsis (...) above represents the actual {@code Base-64} encoded
160     * {@code String}.  Many web-sites return HTML image tags with the actual picture/image encoded
161     * into a {@code String} and saved inside the {@code 'SRC'} attribute.  This method will decode
162     * that image-as-a-{@code String} into a {@code java.awt.image.BufferedImage}
163     * 
164     * @param base64EncodedImageWithFormat The best way to obtain this {@code String} is to use the
165     * command [{@code String encoded = imageTag.AV("src"); }], and pass this variable
166     * {@code 'encoded'} to this parameter.  It is important to note that variable
167     * {@code 'imageTag'} must be a {@code public class TagNode}, and that {@code TagNode} must:
168     * 
169     * <BR /><BR /><UL CLASS="JDUL">
170     *  <LI>Have {@code public final String tok} equal to {@code 'img'}
171     *      <BR /><BR /></LI>
172     *  <LI>The {@code <IMG>} represented must have a {@code SRC="..."} which contains a 
173     *      {@code Base-64} encoded image.
174     *      <BR />
175     *  </LI>
176     * </UL>
177     * 
178     * @return A decoded image that can be saved to file, and an instance of {@code IF} that
179     * identifies what type of image was specified.
180     * 
181     * <BR /><BR /><UL CLASS=JDUL>
182     * <LI>{@code Ret2.a} (BufferedImage):} The Converted Image
183     *      <BR /><BR /></LI>
184     * <LI>{@code Ret2.b} (IF):} The Image Type
185     *      <BR /></LI>
186     * </UL>
187     */
188    public static Ret2<BufferedImage, IF>
189        decodeBase64ToImage(String base64EncodedImageWithFormat)
190    {
191        // sourceData = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPAAAADwCAYAAAA+VemSAAAgAEl...==';
192        Matcher m = B64_INIT_STRING.matcher(base64EncodedImageWithFormat);
193
194        // System.out.println("first 50:\t" + base64EncodedImageWithFormat.substring(0, 80));
195
196        if (! m.find()) return null;
197 
198        String  imageFormatStr      = m.group(1);
199        String  base64EncodedImage  = m.group(2);
200        IF      imageFormat         = (imageFormatStr != null) ? IF.get(imageFormatStr) : null;
201
202        // System.out.println("imageFormatStr:\t" + '[' + imageFormatStr + ']');
203        // System.out.println("imageFormat:\t" + '[' + imageFormat + ']');
204
205        if (imageFormat == null)            return null;
206
207        BufferedImage   bi      = decodeBase64ToImage(base64EncodedImage, imageFormat);
208        // BufferedImage   bi   = decodeBase64ToImage_V2(base64EncodedImage, imageFormat);
209
210        if (bi == null)                     return null;
211
212        return new Ret2<BufferedImage, IF>(bi, imageFormat);
213    }
214
215    /**
216     * This will decode a {@code Base-64 String} into an image.  Here, the decoder used is the one
217     * obtained from a call to: {@code java.util.Base64.getDecoder() }.
218     *
219     * <BR /><BR /><B>COPIED FROM</B> {@code java.util.Base64:}
220     * 
221     * <BR /><BR /><B>Basic: </B> Uses "The Base64 Alphabet" as specified in <I><B>Table 1 of RFC
222     * 4648 and RFC 2045</B></I> for encoding and decoding operation. The encoder does not add any
223     * line feed (line separator) character. The decoder rejects data that contains characters
224     * outside the base64 alphabet.
225     *
226     * @return A decoded image that can be saved to file.
227     */
228    public static BufferedImage decodeBase64ToImage(String base64EncodedImage, IF imageFormat)
229    {
230        try
231        {
232            ByteArrayInputStream    bis     = new ByteArrayInputStream(Base64.getDecoder().decode(base64EncodedImage));
233            BufferedImage           image   = ImageIO.read(bis);
234
235            bis.close();
236            return image;
237        }
238        catch (IOException e) { return null; }
239    }
240
241    /**
242     * This will decode a base-64 String into an image.  Here, the decoder used is the one obtained
243     * from a call to: {@code java.util.Base64.getURLDecoder() }.
244     *
245     * <BR /><BR /><B>COPIED FROM</B> {@code java.util.Base64:}
246     * 
247     * <BR /><BR /><B>URL and Filename safe: </B> Uses the "URL and Filename safe Base64 Alphabet"
248     * as specified in <B><I>Table 2 of RFC 4648</B></I> for encoding and decoding. The encoder
249     * does not add any line feed (line separator) character. The decoder rejects data that
250     * contains characters outside the base64 alphabet.
251     *
252     * @return A decoded image that can be saved to file.
253     */
254    public static BufferedImage decodeBase64ToImage_V2(String base64EncodedImage, IF imageFormat)
255    {
256        try
257        {
258            ByteArrayInputStream    bis     = new ByteArrayInputStream(Base64.getUrlDecoder().decode(base64EncodedImage));
259            BufferedImage           image   = ImageIO.read(bis);
260
261            bis.close();
262            return image;
263        } 
264        catch (IOException e) { return null; }
265    }
266}