UReport导出PDF添加文字水印和图片水印

一. 概述

在后台管理系统中, 经常会遇到各种报表开发, 其中比较有名开源的报表框架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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343