/*
GNU GENERAL LICENSE
Copyright (C) 2006 The Lobo Project. Copyright (C) 2014 - 2017 Lobo Evolution
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
verion 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General License for more details.
You should have received a copy of the GNU General Public
along with this program. If not, see <http://www.gnu.org/licenses/>.
Contact info: lobochief@users.sourceforge.net; ivan.difrancesco@yahoo.it
*/
/*
* Created on Nov 19, 2005
*/
package org.lobobrowser.html.control;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.SwingUtilities;
import org.apache.batik.transcoder.TranscoderException;
import org.lobobrowser.html.dombl.ImageEvent;
import org.lobobrowser.html.dombl.ImageListener;
import org.lobobrowser.html.dombl.SVGRasterizer;
import org.lobobrowser.html.domimpl.HTMLImageElementImpl;
import org.lobobrowser.html.renderer.HtmlController;
import org.lobobrowser.html.renderer.RElement;
import org.lobobrowser.html.renderer.RenderableSpot;
import org.lobobrowser.html.style.HtmlValues;
import org.lobobrowser.http.UserAgentContext;
import org.lobobrowser.util.SSLCertificate;
import org.lobobrowser.util.Urls;
/**
* The Class ImgControl.
*/
public class ImgControl extends BaseControl implements ImageListener {
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 1L;
/** The Constant logger. */
private static final Logger logger = LogManager.getLogger(ImgControl.class.getName());
/** The image. */
private volatile Image image;
/** The valign. */
private int valign = RElement.VALIGN_BASELINE;
/** The preferred size. */
private Dimension preferredSize;
/** The declared width. */
private int declaredWidth;
/** The declared height. */
private int declaredHeight;
/** The last src. */
private String lastSrc;
/** The align. */
private String align;
/** The alt. */
private String alt;
/** The image height. */
private int imageWidth, imageHeight;
/** The mouse being pressed. */
private boolean mouseBeingPressed;
/** The buffer image. */
private BufferedImage bufferImage;
/**
* Instantiates a new img control.
*
* @param modelNode
* the model node
* @throws MalformedURLException
* @throws TranscoderException
* @throws IOException
*/
public ImgControl(HTMLImageElementImpl modelNode) {
super(modelNode);
align = modelNode.getAlign();
alt = modelNode.getAlt() != null ? modelNode.getAlt() : "";
bufferImage = null;
modelNode.addImageListener(this);
this.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
mouseBeingPressed = true;
repaint();
}
@Override
public void mouseReleased(MouseEvent e) {
mouseBeingPressed = false;
repaint();
HtmlController.getInstance().onPressed(modelNode, e, e.getX(), e.getY());
}
@Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON3) {
HtmlController.getInstance().onContextMenu(modelNode, e, e.getX(), e.getY());
}
}
});
if (modelNode.getSrc() != null) {
URL u = null;
try {
SSLCertificate.setCertificate();
URL baseURL = new URL(modelNode.getOwnerDocument().getBaseURI());
URL scriptURL = Urls.createURL(baseURL, modelNode.getSrc());
String scriptURI = scriptURL == null ? modelNode.getSrc() : scriptURL.toExternalForm();
u = new URL(scriptURI.replace(" ", "%20"));
URLConnection con = u.openConnection();
con.setRequestProperty("User-Agent", UserAgentContext.DEFAULT_USER_AGENT);
if (scriptURI.endsWith(".svg")) {
SVGRasterizer r = new SVGRasterizer(u);
image = r.bufferedImageToImage();
} else if (scriptURI.startsWith("https")) {
image = Toolkit.getDefaultToolkit().createImage(ImageIO.read(con.getInputStream()).getSource());
} else if (scriptURI.endsWith(".gif")) {
try {
image = new ImageIcon(u).getImage();
} catch (Exception e) {
image = ImageIO.read(con.getInputStream());
}
} else if (scriptURI.endsWith(".bmp")) {
try {
image = ImageIO.read(con.getInputStream());
} catch (IOException e) {
logger.error("read error: " + e.getMessage());
}
} else {
image = ImageIO.read(con.getInputStream());
}
} catch (FileNotFoundException | IIOException ex) {
logger.error("ImgControl(): Image not found "+modelNode.getSrc());
} catch (TranscoderException | IOException e1) {
logger.error(e1.getMessage());
} catch (Exception e1) {
logger.error("ImgControl(): Image not found "+modelNode.getSrc());
}
}
if (modelNode.getHeight() > 0) {
imageHeight = modelNode.getHeight();
} else {
if (image != null) {
imageHeight = image.getHeight(this);
}
}
if (modelNode.getWidth() > 0) {
imageWidth = modelNode.getWidth();
} else {
if (image != null) {
imageWidth = image.getWidth(this);
}
}
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (image != null) {
int x = (getWidth() - imageWidth) / 2;
int y = (getHeight() - imageHeight) / 2;
if (bufferImage == null) {
g.drawImage(image, x, y, imageWidth, imageHeight, this);
} else {
g.drawImage(bufferImage, x, y, imageWidth, imageHeight, this);
}
} else {
g.drawString(alt, 10, 10);
}
if (this.mouseBeingPressed) {
Color over = new Color(255, 100, 100, 64);
if (over != null) {
Color oldColor = g.getColor();
try {
g.setColor(over);
g.fillRect(0, 0, imageWidth, imageHeight);
} finally {
g.setColor(oldColor);
}
}
}
}
@Override
public Dimension getPreferredSize() {
if (image == null) {
return super.getPreferredSize();
} else {
return new Dimension(imageWidth, imageHeight);
}
}
@Override
public void reset(int availWidth, int availHeight) {
// Expected in the GUI thread.
int dw = HtmlValues.getPixelSize(String.valueOf(imageWidth), null, -1, availWidth);
int dh = HtmlValues.getPixelSize(String.valueOf(imageHeight), null, -1, availHeight);
this.declaredWidth = dw;
this.declaredHeight = dh;
this.preferredSize = this.createPreferredSize(dw, dh);
int valign;
if (align == null) {
valign = RElement.VALIGN_BASELINE;
} else {
align = align.toLowerCase().trim();
if ("middle".equals(align)) {
valign = RElement.VALIGN_MIDDLE;
} else if ("absmiddle".equals(align)) {
valign = RElement.VALIGN_ABSMIDDLE;
} else if ("top".equals(align)) {
valign = RElement.VALIGN_TOP;
} else if ("bottom".equals(align)) {
valign = RElement.VALIGN_BOTTOM;
} else if ("baseline".equals(align)) {
valign = RElement.VALIGN_BASELINE;
} else if ("absbottom".equals(align)) {
valign = RElement.VALIGN_ABSBOTTOM;
} else {
valign = RElement.VALIGN_BASELINE;
}
}
this.valign = valign;
}
@Override
public int getVAlign() {
return this.valign;
}
/**
* Creates the preferred size.
*
* @param dw
* the dw
* @param dh
* the dh
* @return the dimension
*/
public Dimension createPreferredSize(int dw, int dh) {
Image img = this.image;
if (dw == -1) {
if (dh != -1) {
int iw = img == null ? -1 : img.getWidth(this);
int ih = img == null ? -1 : img.getHeight(this);
if (ih == 0) {
dw = iw == -1 ? 0 : iw;
} else if ((iw == -1) || (ih == -1)) {
dw = 0;
} else {
dw = (dh * iw) / ih;
}
} else {
dw = img == null ? -1 : img.getWidth(this);
if (dw == -1) {
dw = 0;
}
}
}
if (dh == -1) {
if (dw != -1) {
int iw = img == null ? -1 : img.getWidth(this);
int ih = img == null ? -1 : img.getHeight(this);
if (iw == 0) {
dh = ih == -1 ? 0 : ih;
} else if ((iw == -1) || (ih == -1)) {
dh = 0;
} else {
dh = (dw * ih) / iw;
}
} else {
dh = img == null ? -1 : img.getHeight(this);
if (dh == -1) {
dh = 0;
}
}
}
return new Dimension(dw, dh);
}
/**
* Check preferred size change.
*
* @return true, if successful
*/
private final boolean checkPreferredSizeChange() {
Dimension newPs = this.createPreferredSize(this.declaredWidth, this.declaredHeight);
Dimension ps = this.preferredSize;
if (ps == null) {
return true;
}
if ((ps.width != newPs.width) || (ps.height != newPs.height)) {
this.preferredSize = newPs;
return true;
} else {
return false;
}
}
@Override
public boolean imageUpdate(Image img, int infoflags, int x, int y, final int w, final int h) {
if (((infoflags & ImageObserver.ALLBITS) != 0) || ((infoflags & ImageObserver.FRAMEBITS) != 0)) {
if (SwingUtilities.isEventDispatchThread()) {
if (!checkPreferredSizeChange()) {
repaint();
} else {
if (ruicontrol != null) {
ruicontrol.preferredSizeInvalidated();
}
}
} else {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (!checkPreferredSizeChange()) {
repaint();
} else {
if (ruicontrol != null) {
ruicontrol.preferredSizeInvalidated();
}
}
}
});
}
}
return true;
}
/**
* Image update.
*
* @param img
* the img
* @param w
* the w
* @param h
* the h
*/
public void imageUpdate(Image img, final int w, final int h) {
if (SwingUtilities.isEventDispatchThread()) {
if (!checkPreferredSizeChange()) {
repaint();
} else {
if (ruicontrol != null) {
ruicontrol.preferredSizeInvalidated();
}
}
} else {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (!checkPreferredSizeChange()) {
repaint();
} else {
if (ruicontrol != null) {
ruicontrol.preferredSizeInvalidated();
}
}
}
});
}
}
/**
* Paint selection.
*
* @param g
* the g
* @param inSelection
* the in selection
* @param startPoint
* the start point
* @param endPoint
* the end point
* @return true, if successful
*/
public boolean paintSelection(Graphics g, boolean inSelection, RenderableSpot startPoint, RenderableSpot endPoint) {
return inSelection;
}
@Override
public void imageLoaded(ImageEvent event) {
Image image = event.image;
this.image = image;
int width = image.getWidth(this);
int height = image.getHeight(this);
if ((width != -1) && (height != -1)) {
this.imageUpdate(image, width, height);
}
}
@Override
public String toString() {
return "ImgControl[src=" + this.lastSrc + "]";
}
}