当我们在Excel导出中添加水印时,我们可以采用一种方法,先将水印文字生成为图片,然后将这个图片设置为Excel的背景。这样的做法涉及到使用Java的Graphics2D来处理图片。但是需要注意的是,这种方式存在一个问题,就是在将导出的Excel再转换为PDF格式时,水印会消失。
步骤
<dependency>
<groupId>com.alibabagroupId>
<artifactId>easyexcelartifactId>
<version>3.3.2version>
dependency>
<dependency>
<groupId>org.apache.poigroupId>
<artifactId>ooxml-schemasartifactId>
<version>1.4version>
dependency>
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-allartifactId>
<version>5.8.20version>
dependency>
下面将用一个简单的案例来介绍Graphics2D的使用。
读取一张图片,在图片中添加一段文字,然后输出一段文字,最后再输出添加文字的图片。
@SpringBootTest
public class PictureTest {
@Test
void contextLoads() {
try {
InputStream inputStream = new FileInputStream("D:\\images/cat.jpg");
BufferedImage bufferedImage = ImageIO.read(inputStream);
Graphics2D graphics2D = bufferedImage.createGraphics();
Font font = new Font("黑体", Font.BOLD, 40);
graphics2D.setFont(font);
graphics2D.drawString("我是一只可爱的小猫", 800, 800);
ImageIO.write(bufferedImage, "jpg", new FileOutputStream("D:\\images/catOutPut.png"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
我们有一个位于D盘的images文件夹,其中包含了一些照片。我们的目标是读取这些照片,然后将它们加工后再输出到D盘的images文件夹中。在处理照片的过程中,我们需要实现在图片中添加文字的功能。为了实现这一目标,我们可以通过获取文件流,然后使用这个文件流创建一个Graphics2D对象。通过Graphics2D对象,我们可以在图片上绘制文本或其他图形,从而实现在图片中添加文字。
在导出Excel文件是添加水印我们也是利用这样的方法。
workbook.addPicture()
方法将水印图片添加到工作簿,并获取图片索引。xssfSheet.getPackagePart().addRelationship()
方法建立工作表与图片的关联关系。xssfSheet.getCTWorksheet().addNewPicture().setId()
方法将水印添加到每个工作表的底层XML表示中。/**
* 水印配置类
*/
@Data
public class WaterMark {
/**
* 水印内容
*/
private String content = "";
/**
* 画笔颜色. 格式为"#RRGGBB",eg: "#C5CBCF"
*/
private String color = "#C5CBCF";
/**
* 字体样式
*/
private Font font = new Font("Microsoft YaHei", Font.BOLD, 40);
/**
* 水印宽度
*/
private int width = 300;
/**
* 水印高度
*/
private int height = 100;
/**
* 水平倾斜度
*/
private double shearX = 0.1;
/**
* 垂直倾斜度
*/
private double shearY = -0.26;
/**
* 字体的y轴位置
*/
private int yAxis = 50;
// Getter and Setter methods...
}
多个工作表导出
public class CustomWaterMarkHandler implements SheetWriteHandler {
private final WaterMark watermark;
public CustomWaterMarkHandler(WaterMark watermark) {
this.watermark = watermark;
}
@Override
public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
}
@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
try {
BufferedImage bufferedImage = createWatermarkImage();
setWaterMarkToExcel((XSSFWorkbook) writeWorkbookHolder.getWorkbook(), bufferedImage);
} catch (Exception e) {
throw new RuntimeException("添加水印出错");
}
}
private BufferedImage createWatermarkImage() {
final Font font = watermark.getFont();
final int width = watermark.getWidth();
final int height = watermark.getHeight();
String[] textArray = watermark.getContent().split(",");
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 背景透明 开始
Graphics2D g = image.createGraphics();
image = g.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
g.dispose();
// 背景透明 结束
g = image.createGraphics();
// 设定画笔颜色
g.setColor(new Color(Integer.parseInt(watermark.getColor().substring(1), 16)));
// 设置画笔字体
g.setFont(font);
// 设定倾斜度
g.shear(watermark.getShearX(), watermark.getShearY());
// 设置字体平滑
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int y = watermark.getYAxis();
for (String s : textArray) {
// 从画框的y轴开始画字符串.假设电脑屏幕中心为0,y轴为正数则在下方
g.drawString(s, 0, y);
y = y + font.getSize();
}
// 释放画笔
g.dispose();
return image;
}
private void setWaterMarkToExcel(XSSFWorkbook workbook, BufferedImage bfi) {
//将图片添加到工作簿
int pictureIdx = workbook.addPicture(ImgUtil.toBytes(bfi, ImgUtil.IMAGE_TYPE_PNG), Workbook.PICTURE_TYPE_PNG);
//建立 sheet 和 图片 的关联关系
XSSFPictureData xssfPictureData = workbook.getAllPictures().get(pictureIdx);
for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
XSSFSheet xssfSheet = workbook.getSheetAt(i);
PackagePartName packagePartName = xssfPictureData.getPackagePart().getPartName();
PackageRelationship packageRelationship = xssfSheet.getPackagePart()
.addRelationship(packagePartName, TargetMode.INTERNAL, XSSFRelation.IMAGES.getRelation(), null);
//添加水印到工作表
xssfSheet.getCTWorksheet().addNewPicture().setId(packageRelationship.getId());
}
}
}
单个工作表导出
@RequiredArgsConstructor
public class WaterMarkHandler implements SheetWriteHandler {
private final WaterMark watermark;
private BufferedImage createWatermarkImage(WaterMark watermark) {
final Font font = this.watermark.getFont();
final int width = this.watermark.getWidth();
final int height = this.watermark.getHeight();
String[] textArray = this.watermark.getContent().split(",");
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 背景透明 开始
Graphics2D g = image.createGraphics();
image = g.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
g.dispose();
// 背景透明 结束
g = image.createGraphics();
// 设定画笔颜色
g.setColor(new Color(Integer.parseInt(this.watermark.getColor().substring(1), 16)));
// 设置画笔字体
g.setFont(font);
// 设定倾斜度
g.shear(this.watermark.getShearX(), this.watermark.getShearY());
// 设置字体平滑
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int y = this.watermark.getYAxis();
for (String s : textArray) {
// 从画框的y轴开始画字符串.假设电脑屏幕中心为0,y轴为正数则在下方
g.drawString(s, 0, y);
y = y + font.getSize();
}
// 释放画笔
g.dispose();
return image;
}
/**
* 为Excel打上水印工具函数
*
* @param sheet excel sheet
* @param bytes 水印图片字节数组
*/
public static void putWaterRemarkToExcel(XSSFSheet sheet, BufferedImage bufferedImage) {
//add relation from sheet to the picture data
XSSFWorkbook workbook = sheet.getWorkbook();
int pictureIdx = workbook.addPicture(ImgUtil.toBytes(bufferedImage, ImgUtil.IMAGE_TYPE_PNG), Workbook.PICTURE_TYPE_PNG);
String rID = sheet.addRelation(null, XSSFRelation.IMAGES, workbook.getAllPictures().get(pictureIdx))
.getRelationship().getId();
//set background picture to sheet
sheet.getCTWorksheet().addNewPicture().setId(rID);
}
@Override
public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
}
@SneakyThrows
@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
BufferedImage waterMark = createWatermarkImage(watermark);
XSSFSheet sheet = (XSSFSheet) writeSheetHolder.getSheet();
putWaterRemarkToExcel(sheet, waterMark);
}
}
@Getter
@Setter
@EqualsAndHashCode
public class DemoData {
@ExcelProperty("字符串标题")
private String string;
@ExcelProperty(value = "日期标题",converter = DateConverter.class)
private Date date;
@ExcelProperty("数字标题")
private Double doubleData;
/**
* 忽略这个字段
*/
@ExcelIgnore
private String ignore;
}
@SpringBootTest
@Slf4j
public class WaterMarkTest {
@Test
void WaterMarkReader() {
WaterMark watermark = new WaterMark();
watermark.setContent("我是sveinn,我热爱Java");
watermark.setWidth(600);
watermark.setHeight(400);
watermark.setYAxis(200);
// 注意文件是要.xlsx
String fileName ="D:/export/waterMark "+ ".xlsx";
//导出
EasyExcel.write(fileName, DemoData.class)
.inMemory(true)
.sheet("sheet1")
.registerWriteHandler(new CustomWaterMarkHandler(watermark))
.doWrite(data());
}
private List<DemoData> data() {
List<DemoData> list = new ArrayList<DemoData>();
for (int i = 0; i < 10; i++) {
DemoData data = new DemoData();
data.setString("字符串" + i);
data.setDate(new Date());
data.setDoubleData(0.56);
list.add(data);
}
return list;
}
}
具体想要导出什么样的水印可以根据水印的配置类配置自己想要的样式,如果想要更好看的水印可以研究以下Graphics2D的使用。
XSSFWorkbook
对象,也就是对应Excel2007的版本,扩展名是.xlsx
。inMemory(true)
,会比较耗内存。本文参考easyexcel实现导出添加文字水印 - 雨叶微枫 - 博客园