forked from neil/plugin-external-background
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
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; |
|
} |
|
} |
|
}
|
|
|