一. 概述
在后台管理系统中, 经常会遇到各种报表开发, 其中比较有名开源的报表框架UReport2, 了解UReport2可以访问官网URport2教程, 本文主要介绍拓展ureport2输出pdf添加水印
二. 说明
通过查看官网介绍, 可得知ExportManager.exportPdf() 是输出pdf的接口方法, 那么就进一步看看这个方法的实现, 然后改造
重写源码结构
2.1 覆盖源码实现类PdfProducer
/*******************************************************************************
* Copyright 2017 Bstek
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
******************************************************************************/
package com.bstek.ureport.export.pdf;
import com.bstek.ureport.build.paging.Page;
import com.bstek.ureport.chart.ChartData;
import com.bstek.ureport.definition.Alignment;
import com.bstek.ureport.definition.CellStyle;
import com.bstek.ureport.definition.Orientation;
import com.bstek.ureport.definition.Paper;
import com.bstek.ureport.exception.ReportComputeException;
import com.bstek.ureport.export.FullPageData;
import com.bstek.ureport.export.PageBuilder;
import com.bstek.ureport.export.Producer;
import com.bstek.ureport.model.Image;
import com.bstek.ureport.model.*;
import com.bstek.ureport.utils.ImageUtils;
import com.bstek.ureport.utils.UnitUtils;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author Jacky.gao
* @since 2017年3月10日
*/
public class PdfProducer implements Producer {
@Override
public void produce(Report report,OutputStream outputStream) {
Paper paper=report.getPaper();
int width=paper.getWidth();
int height=paper.getHeight();
Rectangle pageSize=new RectangleReadOnly(width,height);
if(paper.getOrientation().equals(Orientation.landscape)){
pageSize=pageSize.rotate();
}
int leftMargin=paper.getLeftMargin();
int rightMargin=paper.getRightMargin();
int topMargin=paper.getTopMargin();
int bottomMargin=paper.getBottomMargin();
Document document=new Document(pageSize,leftMargin,rightMargin,topMargin,bottomMargin);
try{
PdfWriter writer=PdfWriter.getInstance(document,outputStream);
PageHeaderFooterEvent headerFooterEvent=new PageHeaderFooterEvent(report);
writer.setPageEvent(headerFooterEvent);
document.open();
List<Column> columns=report.getColumns();
List<Integer> columnsWidthList=new ArrayList<Integer>();
int[] intArr=buildColumnSizeAndTotalWidth(columns,columnsWidthList);
int colSize=intArr[0],totalWidth=intArr[1];
int[] columnsWidth=new int[columnsWidthList.size()];
for(int i=0;i<columnsWidthList.size();i++){
columnsWidth[i]=columnsWidthList.get(i);
}
FullPageData pageData=PageBuilder.buildFullPageData(report);
List<List<Page>> list=pageData.getPageList();
if(list.size()>0){
int columnCount=paper.getColumnCount();
int w=columnCount*totalWidth+(columnCount-1)*paper.getColumnMargin();
int size=columnCount+(columnCount-1);
int[] widths=new int[size];
for(int i=0;i<size;i++){
int mode=(i+1)%2;
if(mode==0){
widths[i]=paper.getColumnMargin();
}else{
widths[i]=totalWidth;
}
}
float tableHeight=pageSize.getHeight()-paper.getTopMargin()-paper.getBottomMargin();
Map<Row, Map<Column, Cell>> cellMap=report.getRowColCellMap();
for(List<Page> pages:list){
PdfPTable table=new PdfPTable(size);
table.setLockedWidth(true);
table.setTotalWidth(w);
table.setWidths(widths);
table.setHorizontalAlignment(Element.ALIGN_LEFT);
int ps=pages.size();
for(int i=0;i<ps;i++){
if(i>0){
PdfPCell pdfMarginCell=new PdfPCell();
pdfMarginCell.setBorder(Rectangle.NO_BORDER);
table.addCell(pdfMarginCell);
}
Page page=pages.get(i);
PdfPTable childTable=new PdfPTable(colSize);
childTable.setLockedWidth(true);
childTable.setTotalWidth(totalWidth);
childTable.setWidths(columnsWidth);
childTable.setHorizontalAlignment(Element.ALIGN_LEFT);
List<Row> rows=page.getRows();
for(Row row:rows){
Map<Column,Cell> colMap=cellMap.get(row);
if(colMap==null){
continue;
}
for(Column col:columns){
if(col.getWidth()<1){
continue;
}
Cell cell=colMap.get(col);
if(cell==null){
continue;
}
int cellHeight=buildCellHeight(cell,rows);
PdfPCell pdfcell=buildPdfPCell(cell,cellHeight);
childTable.addCell(pdfcell);
}
}
float childTableHeight=childTable.calculateHeights();
if(tableHeight>childTableHeight){
for(int j=0;j<columns.size();j++){
PdfPCell lastCell=new PdfPCell();
lastCell.setBorder(Rectangle.NO_BORDER);
childTable.addCell(lastCell);
}
}
PdfPCell pdfContainerCell=new PdfPCell(childTable);
pdfContainerCell.setBorder(Rectangle.NO_BORDER);
table.addCell(pdfContainerCell);
}
if(ps<columnCount){
int left=columnCount-ps;
for(int i=0;i<left;i++){
PdfPCell pdfMarginCell=new PdfPCell();
pdfMarginCell.setBorder(Rectangle.NO_BORDER);
table.addCell(pdfMarginCell);
pdfMarginCell=new PdfPCell();
pdfMarginCell.setBorder(Rectangle.NO_BORDER);
table.addCell(pdfMarginCell);
}
}
// 加入文字水印
Object watermark = report.getContext().getParameters().get("watermark");
writer.setPageEvent(new Watermark(watermark));
// 加入图片水印
writer.setPageEvent(new ImageWatermark());
document.add(table);
document.newPage();
}
}else{
List<Page> pages=report.getPages();
Map<Row, Map<Column, Cell>> cellMap=report.getRowColCellMap();
for(Page page:pages){
PdfPTable table=new PdfPTable(colSize);
table.setLockedWidth(true);
table.setTotalWidth(totalWidth);
table.setWidths(columnsWidth);
table.setHorizontalAlignment(Element.ALIGN_LEFT);
List<Row> rows=page.getRows();
for(Row row:rows){
Map<Column,Cell> colMap=cellMap.get(row);
if(colMap==null){
continue;
}
for(Column col:columns){
if(col.getWidth()<1){
continue;
}
Cell cell=colMap.get(col);
if(cell==null){
continue;
}
int cellHeight=buildCellHeight(cell,rows);
PdfPCell pdfcell=buildPdfPCell(cell,cellHeight);
table.addCell(pdfcell);
}
}
// 加入文字水印
Object watermark = report.getContext().getParameters().get("watermark");
writer.setPageEvent(new Watermark(watermark));
// 加入图片水印
writer.setPageEvent(new ImageWatermark());
document.add(table);
document.newPage();
}
}
document.close();
}catch(Exception ex){
throw new ReportComputeException(ex);
}
}
private int buildCellHeight(Cell cell,List<Row> rows){
int height=cell.getRow().getRealHeight();
int rowSpan=cell.getPageRowSpan();
if(rowSpan>0){
int pos=rows.indexOf(cell.getRow());
int start=pos+1,end=start+rowSpan-1;
for(int i=start;i<end;i++){
height+=rows.get(i).getRealHeight();
}
}
return height;
}
private PdfPCell buildPdfPCell(Cell cellInfo,int cellHeight) throws Exception{
CellStyle style=cellInfo.getCellStyle();
CellStyle customStyle=cellInfo.getCustomCellStyle();
CellStyle rowStyle=cellInfo.getRow().getCustomCellStyle();
CellStyle colStyle=cellInfo.getColumn().getCustomCellStyle();
PdfPCell cell=newPdfCell(cellInfo,cellHeight);
cell.setPadding(0);
cell.setBorder(PdfPCell.NO_BORDER);
cell.setCellEvent(new CellBorderEvent(style,customStyle));
int rowSpan=cellInfo.getPageRowSpan();
if(rowSpan>0){
cell.setRowspan(rowSpan);
}
int colSpan=cellInfo.getColSpan();
if(colSpan>0){
cell.setColspan(colSpan);
}
Alignment align=style.getAlign();
if(customStyle!=null && customStyle.getAlign()!=null){
align=customStyle.getAlign();
}
if(rowStyle!=null && rowStyle.getAlign()!=null){
align=rowStyle.getAlign();
}
if(colStyle!=null && colStyle.getAlign()!=null){
align=colStyle.getAlign();
}
if(align!=null){
if(align.equals(Alignment.left)){
cell.setHorizontalAlignment(Element.ALIGN_LEFT);
}else if(align.equals(Alignment.center)){
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
}else if(align.equals(Alignment.right)){
cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
}
}
Alignment valign=style.getValign();
if(customStyle!=null && customStyle.getValign()!=null){
valign=customStyle.getValign();
}
if(rowStyle!=null && rowStyle.getValign()!=null){
valign=rowStyle.getValign();
}
if(colStyle!=null && colStyle.getValign()!=null){
valign=colStyle.getValign();
}
if(valign!=null){
if(valign.equals(Alignment.top)){
cell.setVerticalAlignment(Element.ALIGN_TOP);
}else if(valign.equals(Alignment.middle)){
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
}else if(valign.equals(Alignment.bottom)){
cell.setVerticalAlignment(Element.ALIGN_BOTTOM);
}
}
String bgcolor=style.getBgcolor();
if(customStyle!=null && StringUtils.isNotBlank(customStyle.getBgcolor())){
bgcolor=customStyle.getBgcolor();
}
if(rowStyle!=null && StringUtils.isNotBlank(rowStyle.getBgcolor())){
bgcolor=rowStyle.getBgcolor();
}
if(colStyle!=null && StringUtils.isNotBlank(colStyle.getBgcolor())){
bgcolor=colStyle.getBgcolor();
}
if(StringUtils.isNotEmpty(bgcolor)){
String[] colors=bgcolor.split(",");
cell.setBackgroundColor(new BaseColor(Integer.valueOf(colors[0]),Integer.valueOf(colors[1]),Integer.valueOf(colors[2])));
}
return cell;
}
private int[] buildColumnSizeAndTotalWidth(List<Column> columns,List<Integer> list){
int count=0,totalWidth=0;
for(int i=0;i<columns.size();i++){
Column col=columns.get(i);
int width=col.getWidth();
if(width<1){
continue;
}
count++;
list.add(width);
totalWidth+=width;
}
return new int[]{count,totalWidth};
}
private PdfPCell newPdfCell(Cell cellInfo,int cellHeight) throws Exception{
PdfPCell cell=null;
Object cellData=cellInfo.getFormatData();
if(cellData instanceof Image){
Image img=(Image)cellData;
cell=new PdfPCell(buildPdfImage(img.getBase64Data(), 0, 0));
}else if(cellData instanceof ChartData){
ChartData chartData=(ChartData)cellData;
String base64Data=chartData.retriveBase64Data();
if(base64Data!=null){
Image img=new Image(base64Data,chartData.getWidth(),chartData.getHeight());
cell=new PdfPCell(buildPdfImage(img.getBase64Data(), 0, 0));
}else{
cell=new PdfPCell();
CellPhrase pargraph=new CellPhrase(cellInfo,"");
cell.setPhrase(pargraph);
cell.setFixedHeight(cellHeight);
}
}else{
cell=new PdfPCell();
CellPhrase pargraph=new CellPhrase(cellInfo,cellData);
cell.setPhrase(pargraph);
cell.setFixedHeight(cellHeight);
}
CellStyle style=cellInfo.getCellStyle();
if(style!=null && style.getLineHeight()>0){
cell.setLeading(style.getLineHeight(), style.getLineHeight());
}
return cell;
}
private com.itextpdf.text.Image buildPdfImage(String base64Data, int width,int height) throws Exception{
com.itextpdf.text.Image pdfImg=null;
InputStream input=ImageUtils.base64DataToInputStream(base64Data);
try{
byte[] bytes=IOUtils.toByteArray(input);
pdfImg=com.itextpdf.text.Image.getInstance(bytes);
float imgWidth=pdfImg.getWidth();
float imgHeight=pdfImg.getHeight();
if(width==0){
width=Float.valueOf(imgWidth).intValue();
}
if(height==0){
height=Float.valueOf(imgHeight).intValue();
}
width=UnitUtils.pixelToPoint(width-2);
height=UnitUtils.pixelToPoint(height-2);
pdfImg.scaleToFit(width,height);
}finally{
IOUtils.closeQuietly(input);
}
return pdfImg;
}
}
说明: 源码就添加了一行
2.2 编写文字水印实现类继承PdfPageEventHelper
package com.bstek.ureport.export.pdf;
import com.itextpdf.text.Document;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.ColumnText;
import com.itextpdf.text.pdf.GrayColor;
import com.itextpdf.text.pdf.PdfPageEventHelper;
import com.itextpdf.text.pdf.PdfWriter;
public class Watermark extends PdfPageEventHelper {
Font FONT = new Font(Font.FontFamily.HELVETICA, 50, Font.BOLD, BaseColor.RED);
private String waterCont;//水印内容
public Watermark() {
}
public Watermark(Object waterCont) {
if (null != waterCont) {
this.waterCont = waterCont.toString();
}
}
@Override
public void onEndPage(PdfWriter writer, Document document) {
PdfContentByte pdfContentByte = writer.getDirectContentUnder();
// 设置水印透明度
PdfGState gs = new PdfGState();
// 设置填充字体不透明度为0.2f
gs.setFillOpacity(0.2f);
pdfContentByte.setGState(gs);
for(int i=0 ; i<5; i++) {
for(int j=0; j<5; j++) {
ColumnText.showTextAligned(pdfContentByte,
Element.ALIGN_CENTER,
new Phrase(this.waterCont == null ? "HELLO" : this.waterCont, FONT),
(60.5f+i*250),
(40.0f+j*150),
writer.getPageNumber() % 2 == 1 ? 45 : -45);
}
}
}
}
2.3 编写图片水印实现类继承PdfPageEventHelper
package com.bstek.ureport.export.pdf;
import com.itextpdf.text.Document;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.ColumnText;
import com.itextpdf.text.pdf.GrayColor;
import com.itextpdf.text.pdf.PdfPageEventHelper;
import com.itextpdf.text.pdf.PdfWriter;
public class ImageWatermark extends PdfPageEventHelper {
public ImageWatermark () {
}
public ImageWatermark (Object waterCont) {
if (null != waterCont) {
this.waterCont = waterCont.toString();
}
}
@Override
public void onEndPage(PdfWriter writer, Document document) {
PdfContentByte pdfContentByte = writer.getDirectContentUnder();
// 设置水印透明度
PdfGState gs = new PdfGState();
// 设置填充字体不透明度为0.3f
gs.setFillOpacity(0.5f);
pdfContentByte.setGState(gs);
try {
// 获取图片
InputStream inputStream = this.getClass().getResourceAsStream("/image/水印.png");
byte[] bytes = toByteArray(inputStream);
com.itextpdf.text.Image img = com.itextpdf.text.Image.getInstance(bytes);// 插入水印
//设置图片水印的位置。
img.setAbsolutePosition(0, 0);
img.scaleToFit(864,612);
pdfContentByte.addImage(img);
} catch (IOException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
}
}
// 输入流转字节数组
public byte[] toByteArray(InputStream input) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buffer = new byte[1024*4];
int n = 0;
while (-1 != (n = input.read(buffer))) {
output.write(buffer, 0, n);
}
return output.toByteArray();
}
}
2.3 测试
三. 不覆盖源码
如果不覆盖源码加水印, 那就要新增一套导出逻辑, 只需要模仿原逻辑, 加一种导出的方法就行
3.1 增加访问入口
- 继承类:
com.bstek.ureport.console.pdf.ExportPdfServletAction
-
新增一个url入口
3.2 加一个生成pdf处理逻辑
在
com.bstek.ureport.console.pdf.ExportPdfServletAction#buildPdf
中可以看到com.bstek.ureport.export.ExportManager#exportPdf
是生成pdf的逻辑, 所以要重写这个逻辑
- 继承类
com.bstek.ureport.export.ExportManagerImpl
, 模仿com.bstek.ureport.export.ExportManagerImpl#exportPdf
重写一个方法exportPdf
- 继承
com.bstek.ureport.export.pdf.PdfProducer
模仿com.midea.cloud.ureport.extension.ExtPdfProducer#produce
重写方法produce