You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

525 lines
18 KiB

package com.fr.plugin.external;
/*
* Copyright(c) 2001-2010, FineReport Inc, All Rights Reserved.
*/
import com.fr.base.Base64;
import com.fr.base.GraphHelper;
import com.fr.base.background.AbstractBackground;
import com.fr.base.background.ImageSerializable;
import com.fr.general.Background;
import com.fr.general.ComparatorUtils;
import com.fr.json.JSONException;
import com.fr.json.JSONObject;
import com.fr.plugin.ExtraClassManager;
import com.fr.stable.BaseConstants;
import com.fr.stable.CheckOut;
import com.fr.stable.CodeUtils;
import com.fr.stable.Constants;
import com.fr.stable.CoreGraphHelper;
import com.fr.stable.StableUtils;
import com.fr.stable.StringUtils;
import com.fr.stable.fun.ImageLayoutDescriptionProcessor;
import com.fr.stable.fun.ServletURLTransformer;
import com.fr.stable.web.Repository;
import com.fr.stable.web.SessionProvider;
import com.fr.stable.xml.XMLPrintWriter;
import com.fr.stable.xml.XMLableReader;
import com.fr.web.RepositoryDeal;
import com.fr.web.core.SessionPoolManager;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Paint;
import java.awt.Shape;
import java.awt.TexturePaint;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.UUID;
/**
* 图片背景,该背景使用一张图片作为填充。
*/
public class ExternalImageBackground extends AbstractBackground {
private transient Image image = null;
private int layout = Constants.IMAGE_CENTER; //layout.
//这个layout只是用来展示的,不保存进xml,因为图片远大于单元格时,会变成空白,需要动态改变layout以展示图片
private int layout4Draw = Constants.IMAGE_CENTER;
private double specifiedImageWidth = -1;
private double specifiedImageHeight = -1;
private String id;
private static final int PAINT_WIDTH_OFFSET = 40;
private static final int PAINT_HEIGHT_OFFSET = 40;
/**
* 默认的构造函数
*/
public ExternalImageBackground() {
this(null, null);
}
/**
* 根据指定图片生成的图片背景
*
* @param image 给定的图片
*/
public ExternalImageBackground(Image image, String id) {
this(image, Constants.IMAGE_TILED, id);
}
/**
* 根据指定的图片和布局方式生成的图片背景
*
* @param image 给定的图片
* @param layout 布局方式
*/
public ExternalImageBackground(Image image, int layout, String id) {
this.setImage(image);
this.setLayout(layout);
this.setId(id);
}
/**
* 获取用于填充背景的图片
*
* @return 图片
*/
public Image getImage() {
if (this.image == null) {
this.image = ImageManager.getInstance().findImageById(this.id);
}
return this.image;
}
/**
* 设置图片背景所使用的图片
*
* @param image 背景所使用的图片
*/
public void setImage(Image image) {
this.image = image;
}
/**
* 获取图片背景的布局方式
* 可能的值包括Constants.IMAGE_DEFAULT、Constants.IMAGE_TILED、
* Constants.IMAGE_CENTER和Constants.IMAGE_EXTEND
* 默认为Constants.IMAGE_DEFAULT
*
* @return 图片背景布局
*/
public int getLayout() {
return this.layout;
}
public int getLayout4Draw() {
return layout4Draw == Constants.IMAGE_CENTER ? this.layout : layout4Draw;
}
public void setLayout4Draw(int layout4Draw) {
this.layout4Draw = layout4Draw;
}
/**
* 设置图片背景的布局方式
*
* @param layout 布局方式
* @see Constants#IMAGE_DEFAULT
* @see Constants#IMAGE_TILED
* @see Constants#IMAGE_CENTER
* @see Constants#IMAGE_EXTEND
*/
public void setLayout(int layout) {
this.layout = layout;
}
/**
* 获取用于指定图片背景中图片宽度的值
*
* @return 自定义的图片宽度,如果该值为-1则使用原始图片的宽度
*/
public double getSpecifiedImageWidth() {
return specifiedImageWidth;
}
/**
* 设置用于指定图片背景中图片宽度的值
*
* @param specifiedImageWidth 自定义的图片宽度,如果想使用图片原始的宽度,则把该值设置为-1
*/
public void setSpecifiedImageWidth(double specifiedImageWidth) {
this.specifiedImageWidth = specifiedImageWidth;
}
/**
* 获取用于指定图片背景中图片高度的值
*
* @return 自定义的图片高度,如果该值为-1则使用原始图片的高度
*/
public double getSpecifiedImageHeight() {
return specifiedImageHeight;
}
/**
* 设置用于指定图片背景中图片高度的值
*
* @param specifiedImageHeight 自定义的图片高度,如果想使用图片原始的高度,则把该值设置为-1
*/
public void setSpecifiedImageHeight(double specifiedImageHeight) {
this.specifiedImageHeight = specifiedImageHeight;
}
public String getId() {
return id;
}
public void setId(String id) {
if (StringUtils.isEmpty(id)) {
id = UUID.randomUUID().toString();
}
this.id = id;
}
/**
* 根据指定的画图对象和几何图形来画图片背景
*
* @param g 画图对象
* @param shape 几何图形
*/
public void paint(Graphics g, Shape shape) {
if (this.getImage() == null || shape == null) {
return;
}
Graphics2D g2d = (Graphics2D) g;
Paint paint = g2d.getPaint();
g2d.setPaint(createPaint(shape, StableUtils.isNotSupportARGB(g2d)));
g2d.fill(shape);
g2d.setPaint(paint);
}
/**
* 根据指定的画图对象和几何图形来画具有渐变色的边框的图片背景
*
* @param g 画图对象
* @param shape 几何图形
*/
public void drawWithGradientLine(Graphics g, Shape shape) {
if (this.getImage() == null || shape == null) {
return;
}
Graphics2D g2d = (Graphics2D) g;
Paint paint = g2d.getPaint();
g2d.setPaint(createPaint(shape, StableUtils.isNotSupportARGB(g2d)));
g2d.draw(shape);
g2d.setPaint(paint);
}
/**
* 布局变化
*
* @param width 背景宽度
* @param height 背景高度
*/
public void layoutDidChange(int width, int height) {
int imageLayout = StableUtils.changeImageLayout4Draw(getImage(), getLayout(), width, height);
setLayout4Draw(imageLayout);
}
private Paint createPaint(Shape shape, boolean isNotSupportARGB) {
Rectangle2D rec2D = shape.getBounds2D();
if ((int) rec2D.getWidth() <= 0) {
rec2D.setRect(rec2D.getX(), rec2D.getY(), rec2D.getWidth() + PAINT_WIDTH_OFFSET, rec2D.getHeight());
}
if ((int) rec2D.getHeight() <= 0) {
rec2D.setRect(rec2D.getX(), rec2D.getY(), rec2D.getWidth(), rec2D.getHeight() + PAINT_HEIGHT_OFFSET);
}
//daniel:查了一下flash的ARGB有BUG,不能使用ARGB,653没有使用TexturePaint直接用GraphHelper画的所以没问题
BufferedImage buffered = new BufferedImage((int) rec2D.getWidth(), (int) rec2D.getHeight(), isNotSupportARGB ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = buffered.createGraphics();
GraphHelper.paintImage(g2, (int) rec2D.getWidth(), (int) rec2D.getHeight(), this.getImage(),
// 默认的话. 图片是从左上开始画
this.getLayout4Draw(), Constants.LEFT, Constants.TOP, (int) this.getSpecifiedImageWidth(), (int) this.getSpecifiedImageHeight(), isNotSupportARGB);
g2.dispose();
return new TexturePaint(buffered, rec2D);
}
/**
* 判断当前对象是否和指定的对象相等
*
* @param obj 指定的对象
* @return 相等则返回true,不相等则返回false
*/
public boolean equals(Object obj) {
if (!(obj instanceof com.fr.plugin.external.ExternalImageBackground)) {
return false;
}
com.fr.plugin.external.ExternalImageBackground newImageBackground = (com.fr.plugin.external.ExternalImageBackground) obj;
return this.getLayout() == newImageBackground.getLayout() &&
ComparatorUtils.equals(this.id, newImageBackground.id) &&
ComparatorUtils.equals(this.getImage(), newImageBackground.getImage());
}
/**
* 将背景输出成JSON对象
*
* @return 表示背景的JSON对象
* @throws JSONException
*/
public JSONObject toJSONObject() throws JSONException {
JSONObject js = super.toJSONObject();
js.put("layout", this.layout);
js.put("specifiedImageHeight", this.specifiedImageHeight);
js.put("specifiedImageWidth", this.specifiedImageWidth);
js.put("image", Base64.encode(this.getImage(), "png"));
if (this.getImage() != null) {
js.put("imgWidth", this.getImage().getWidth(null));
js.put("imgHeight", this.getImage().getHeight(null));
}
return js;
}
@Override
public JSONObject toJSONObject(Repository repo, Dimension size) throws JSONException {
JSONObject jo = toJSONObject();
BufferedImage image = createBufferedImage(size.width, size.height);
if (image != null) {
jo.put("image", Base64.encode(image, "png"));
}
return jo;
}
/**
* 图片背景在web端作为辨识的字符串
*
* @return 返回用于表示图片背景类型的字符串
*/
public String getBackgroundType() {
return "ExternalImageBackground";
}
public Background readAdditionalAttr(XMLableReader reader) {
setSpecifiedImageWidth(reader.getAttrAsDouble("specifiedImageWidth", -1));
setSpecifiedImageHeight(reader.getAttrAsDouble("specifiedImageHeight", -1));
setLayout(reader.getAttrAsInt("layout", Constants.IMAGE_DEFAULT));
this.id = reader.getAttrAsString("id", "");
return this;
}
public void writeAdditionalAttr(XMLPrintWriter writer) {
writer.attr("class", ExternalImageBackground.class.getName());
//width/height
writer.attr("specifiedImageWidth", getSpecifiedImageWidth())
.attr("specifiedImageHeight", getSpecifiedImageHeight())
.attr("layout", getLayout())
.attr("id", this.id);
}
//august:下面两个方法是针对image序列化写的,image都已经transient了,所以必须要手动去实现啊!修复BUG:20299
/**
* Serializable, read.
*/
private void readObject(java.io.ObjectInputStream s) throws ClassNotFoundException, java.io.IOException {
s.defaultReadObject();
Object obj = s.readObject();
if (obj != null) {
if (obj instanceof ImageSerializable) {
this.image = ((ImageSerializable) obj).getImage();
}
}
}
/**
* Serializable, write.
*/
private void writeObject(ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject();
if (image == null) {
image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
}
ImageSerializable wrapper = new ImageSerializable(image);
stream.writeObject(wrapper);
}
public JSONObject createJSONConfig(Repository repo) throws JSONException {
String url;
StringBuffer sb = new StringBuffer();
// 取出图片
url = changeBackgroudImageDisplayMode(repo, this.getDefaultImage(), BaseConstants.CHECKOUTIMAGE);
sb.append("url(").append(url).append(") ");
sb.append(createImageLayoutDescription(this.getLayout()));
int type = this.getLayout();
if (layout == Constants.IMAGE_ADJUST || layout == Constants.IMAGE_EXTEND) {
return adjustBackgroundJson(repo, url, sb, type);
}
return JSONObject.create().put("background", sb.toString());
}
public String changeBackgroudImageDisplayMode(Repository repo, Object source, String type) {
if (repo instanceof RepositoryDeal) {
String prefix = repo.getServletURL();
ServletURLTransformer transformer = ExtraClassManager.getInstance().getSingle(ServletURLTransformer.XML_TAG);
if (source == null && ComparatorUtils.equals(BaseConstants.CHECKOUTIMAGE, type)) {// 返回一个空图片的地址
if (transformer != null) {
prefix = transformer.prefixForImage(repo.getServletURL(), type);
}
return prefix + "?op=resource&resource=/com/fr/web/images/s.gif";
} else if (source instanceof JSONObject) { // 这边的type直接了. 没有判断类型的了.
if (transformer != null) {
prefix = transformer.prefixForWrite(repo.getServletURL(), source, type);
}
JSONObject location = (JSONObject) source;
// 标识一下这个widget是所属什么report的,否则parameterpane里的widget就去显示报表里面去拿值了
return prefix + "?op=widget" + (type == null ? "" : ("&ftype=" + type)) + "&location=" + location.toString() + "&sessionID=" + SessionPoolManager.getSessionIDInfor(repo.getSessionID(), SessionProvider.class).getSessionID();
} else {
CheckOut check = CheckOut.getByValue(type);
if (transformer != null) {
prefix = transformer.prefixForResource(repo.getServletURL(), check);
}
switch (check) {
case CHECKOUTFORMLET:
return prefix + "?formlet=" + CodeUtils.encodeURIComponent(CodeUtils.cjkEncode((String) source));
case CHECKOUTREPORTLET:
return prefix + "?reportlet=" + CodeUtils.encodeURIComponent(CodeUtils.cjkEncode((String) source));
case CHECKOUTWIDGET:
return prefix + "?op=widget&widgetname=" + source + "&sessionID=" + SessionPoolManager.getSessionIDInfor(repo.getSessionID(), SessionProvider.class).getSessionID();
case CHECKOUTIMAGE:
return checkOutImage(source, prefix, repo);
}
return StringUtils.EMPTY;
}
} else {
return repo.checkoutObject(source, type);
}
}
private String checkOutImage(Object source, String prefix, Repository repo) {
if (!(source instanceof Image)) {
return StringUtils.EMPTY;
}
Image image = (Image) source;
if (repo.getDevice().isMobile()) {
return Base64.encode(image, "png");
} else {
return prefix + "?op=fr_attach&cmd=ah_image&id=" + ImageManager.getInstance().findAttachId(id);
}
}
/**
* 按照指定的高度和宽度返回原图片背景的图片
*
* @return 修改过高度和宽度的图片
*/
public Image getDefaultImage() {
int sw = (int) this.specifiedImageWidth;
int sh = (int) this.specifiedImageHeight;
if (sw == -1 && sh == -1) {
return this.getImage();
}
int imageWidth;
if (sw == -1) {
imageWidth = this.getImage().getWidth(null);
} else {
imageWidth = sw;
}
int imageHeight;
if (sh == -1) {
imageHeight = this.getImage().getHeight(null);
} else {
imageHeight = sh;
}
BufferedImage bufferedImage = CoreGraphHelper.createBufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = bufferedImage.createGraphics();
this.paint(g2d, new Rectangle2D.Double(0, 0, imageWidth, imageHeight));
bufferedImage.flush();
g2d.dispose();
return bufferedImage;
}
private JSONObject adjustBackgroundJson(Repository repo, String url, StringBuffer sb, int type) throws JSONException {
JSONObject jo = JSONObject.create();
// 拉伸和自适应需要在web端再做处理
if (repo.getBrowser().isLowIEVersion()) {
jo.put("type", type);
jo.put("url", url);
// 这里生成参数面板背景的repo获取不到boxModel 而ie8杂项需要特别处理
jo.put("background", sb.toString());
jo.put("background-repeat", "no-repeat");
jo.put("background-image", "none");
jo.put("filter", "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + url + "',sizingMethod='scale')");
return jo;
}
jo.put("background", sb.toString());
switch (type) {
case Constants.IMAGE_ADJUST:
jo.put("background-size", "contain");
jo.put("background-position", "center");
break;
case Constants.IMAGE_EXTEND:
jo.put("background-size", "100% 100%");
break;
default:
break;
}
return jo;
}
private static String createImageLayoutDescription(int layout) {
ImageLayoutDescriptionProcessor processor = ExtraClassManager.getInstance().getSingle(ImageLayoutDescriptionProcessor.XML_TAG);
if (processor != null) {
String description = processor.getCustomLayoutDescription(layout);
if (StringUtils.isNotEmpty(description)) {
return description;
}
}
switch (layout) {
case Constants.IMAGE_DEFAULT:
return "no-repeat";
case Constants.IMAGE_ADJUST:
return "no-repeat";
case Constants.IMAGE_CENTER:
return "no-repeat center";
case Constants.IMAGE_TILED:
return "repeat";
case Constants.IMAGE_EXTEND:
return "no-repeat center";
default:
return StringUtils.EMPTY;
}
}
}