基于POI对Word/PPT/PDF/Excel增加文件水印

前言

在Web项目中,对于MS Ofice类型的文档有时会存在加密或权限限制的需求,除了对文档增加限制修改密码外,一般需要增加水印以达到溯源的效果。
本文基于POI完成了对常见MS Offce文档(Word/PPT/PDF/Excel)的水印添加操作。

技术要点

添加文档水印的基本步骤是先根据需求生成对应的图像,再通过对应MS Office文档对象提供的方法将图片插入。
不同的文档提供的接口不同,有的自带写水印的方法,有的需要引用第三方组件,有的需要使用awt生成图像写入。

给Word文档添加水印

XWPFHeaderFooterPolicy 官方文档

public static void addWordWaterMark(String inputPath, String outPath, String markStr) {
    
    
		// 读取原始文件
        File inputFile = new File(inputPath);
        XWPFDocument doc = null;
        try {
    
    
            doc = new XWPFDocument(new FileInputStream(inputFile));
        } catch (FileNotFoundException var24) {
    
    
            throw new RuntimeException("源文件不存在");
        } catch (IOException var25) {
    
    
            throw new RuntimeException("读取源文件IO异常");
        } catch (Exception var26) {
    
    
            throw new RuntimeException("不支持的文档格式");
        }
		// 使用自带工具类完成水印填充
        XWPFHeaderFooterPolicy headerFooterPolicy = doc.getHeaderFooterPolicy();
        headerFooterPolicy.createWatermark(markStr);
        // 设置文档只读
        doc.enforceReadonlyProtection();
        // 生成输出文件
        File file = new File(outPath);
        if (!file.exists()) {
    
    
            try {
    
    
                file.createNewFile();
            } catch (IOException var23) {
    
    
                throw new RuntimeException("创建输出文件失败");
            }
        }
		// 打开输出流,将doc写入输出文件
        OutputStream os = null;
        try {
    
    
            os = new FileOutputStream(outPath);
            doc.write(os);
        } catch (FileNotFoundException var21) {
    
    
            throw new RuntimeException("创建输出文件失败");
        } catch (Exception var22) {
    
    
            throw new RuntimeException("添加文档水印失败");
        } finally {
    
    
            if (os != null) {
    
    
                try {
    
    
                    os.close();
                } catch (IOException var20) {
    
    
                    var20.printStackTrace();
                }
            }
        }
    }

给PPT文档添加水印

使用了国产PPT组件:Spire-Presentation-JAVA,但是从官网下载的组件会有免费版限制,请自行决定要不要使用。

Spire-Presentation-JAVA 官网
官方教程-添加文字水印


public static void addPptWaterMark(String inputPath, String outPath, String markStr) {
    
    
        //加载PPT源文档
        Presentation ppt = new Presentation();
		// 设置PPT密码
        ppt.encrypt("Ka_ze");
        try {
    
    
            ppt.loadFromFile(inputPath);
            ISlide slide = null;
            //获取指定幻灯片
            for (int o = 0; o < ppt.getSlides().getCount(); o++) {
    
    
                slide = ppt.getSlides().get(o);
                //设置文本水印文本宽和高
                int width= 110;
                int height= 80;

                //起始坐标
                float x = 10;
                float y = 40;
                for (int i = 0; i < 4; i++)
                {
    
    
                    for (int j = 0; j < 4; j++)
                    {
    
    
                        //绘制文本,设置文本格式并将其添加到第一张幻灯片
                        // 定义一个长方形区域
                        Rectangle2D.Double rect = new Rectangle2D.Double(x,y,width, height);
                        // 添加一个shape到定义区域
                        IAutoShape shape = slide.getShapes().appendShape(ShapeType.RECTANGLE, rect);
                        // 设置shape样式
                        shape.getFill().setFillType(FillFormatType.NONE);
                        shape.getShapeStyle().getLineColor().setColor(Color.white);
                        shape.setRotation(-45);
                        shape.getLocking().setSelectionProtection(true);
                        shape.getLine().setFillType(FillFormatType.NONE);
                        // 添加文本到shape
                        shape.getTextFrame().setText(markStr);
                        shape.setShapeArrange(ShapeAlignmentEnum.ShapeArrange.SendToBack);
                        // 设置文本水印样式
                        PortionEx textRange = shape.getTextFrame().getTextRange();
                        textRange.getFill().setFillType(FillFormatType.SOLID);
                        textRange.getFill().getSolidColor().setColor(new Color(Integer.parseInt("#C5CBCF".substring(1), 16)));
                        textRange.setFontHeight(20);
                        // 设置下一个水印的横坐标
                        x += (100 + ppt.getSlideSize().getSize().getWidth()/6);
                    }
                    x = 30;
                    // 设置下一个水印的纵坐标
                    y += (100 + ppt.getSlideSize().getSize().getHeight()/7) ;
                }
            }

            //保存文档
            ppt.saveToFile(outPath, FileFormat.PPTX_2013);
            ppt.dispose();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }

给PDF文档添加水印

使用了开源组件iText,可以用于创建和操作 PDF 文档。

maven仓库
官方文档-itext-v7.0.0
官方Github

<!-- 引入中文字体支持 -->
<dependency>
     <groupId>com.itextpdf</groupId>
     <artifactId>itext-asian</artifactId>
     <version>5.2.0</version>
 </dependency>
 <!-- 引入组件 -->
 <dependency>
     <groupId>com.itextpdf</groupId>
     <artifactId>itextpdf</artifactId>
     <version>5.4.3</version>
 </dependency>

public static void addPDFWaterMark(String input, String output, String waterMarkName) {
    
    
        BufferedOutputStream bos = null;
        try {
    
    
        	// 读取文件,生成reader
            com.itextpdf.text.pdf.PdfReader reader = new com.itextpdf.text.pdf.PdfReader(input);
            // 生成输出文件,开启输出流
            bos = new BufferedOutputStream(new FileOutputStream(new File(output)));
            // 读取输出流,生成stamper(印章)
            com.itextpdf.text.pdf.PdfStamper stamper = new com.itextpdf.text.pdf.PdfStamper(reader, bos);
			// 设置stamper加密
            stamper.setEncryption(null, "Ka_ze".getBytes(StandardCharsets.UTF_8), PdfWriter.ALLOW_PRINTING, PdfWriter.STANDARD_ENCRYPTION_128);

            // 获取总页数 +1, 下面从1开始遍历
            int total = reader.getNumberOfPages() + 1;
            // 使用classpath下面的字体库
            com.itextpdf.text.pdf.BaseFont base = null;
            try {
    
    
                base = com.itextpdf.text.pdf.BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", com.itextpdf.text.pdf.BaseFont.EMBEDDED);
            } catch (Exception e) {
    
    
                // 日志处理
                e.printStackTrace();
            }

            // 间隔
            int interval = -15;
            // 获取水印文字的高度和宽度
            int textH = 0, textW = 0;
            JLabel label = new JLabel();
            label.setText(waterMarkName);
            FontMetrics metrics = label.getFontMetrics(label.getFont());
            textH = metrics.getHeight();
            textW = metrics.stringWidth(label.getText());      

            // 设置水印透明度
            com.itextpdf.text.pdf.PdfGState gs = new com.itextpdf.text.pdf.PdfGState();
            gs.setFillOpacity(0.2f);
            gs.setStrokeOpacity(0.7f);

            com.itextpdf.text.Rectangle pageSizeWithRotation = null;
            PdfContentByte content = null;
            for (int i = 1; i < total; i++) {
    
    
                // 在内容上方加水印
                content = stamper.getOverContent(i);
                // 在内容下方加水印
                // content = stamper.getUnderContent(i);
                content.saveState();
                content.setGState(gs);

                // 设置字体和字体大小
                content.beginText();
                content.setFontAndSize(base, 20);

                // 获取每一页的高度、宽度
                pageSizeWithRotation = reader.getPageSizeWithRotation(i);
                float pageHeight = pageSizeWithRotation.getHeight();
                float pageWidth = pageSizeWithRotation.getWidth();

                // 根据纸张大小多次添加, 水印文字成30度角倾斜
                for (int height = interval + textH; height < pageHeight; height = height + textH * 6) {
    
    
                    for (int width = interval + textW; width < pageWidth + textW; width = width + textW * 2) {
    
    
                        content.showTextAligned(Element.ALIGN_LEFT, waterMarkName, width - textW, height - textH, 30);
                    }
                }
                content.endText();
            }

            // 关流
            stamper.close();
            reader.close();
        } catch (DocumentException | IOException e) {
    
    
            e.printStackTrace();
        }
    }


给Excel文档添加水印

使用awt包的Graphics2D设置图像属性。


private static void addExcelWaterMark(String inputSrc, String outputSrc, String waterMarkName) {
    
    
		 // 读取水印文字
		 String[] textArray = waterMark.split("\n");
		 // 设置字体
		 Font font = new Font("microsoft-yahei", Font.PLAIN, 20);
		 // 单元水印图片宽高
		 int width = 500;
		 int height = 200;
		 
		 // 缓冲区图片对象
		 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("#C5CBCF".substring(1), 16)));
		 // 设置图形字体
		 g.setFont(font);
		 // 设置图形倾斜度
		 g.shear(0.1, -0.26);
		 // 设置字体平滑
		 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		 // 画出字符串
		 int y = 150;
		 for (int i = 0; i < textArray.length; i++) {
    
    
		     g.drawString(textArray[i], 0, y);
		     y = y + font.getSize();
		 }
		 // 释放画笔
		 g.dispose();

        ByteArrayOutputStream os = new ByteArrayOutputStream();
        try {
    
    
        	// 将内存中的图片写入至输出流
            ImageIO.write(image, "png", os);
            // 读取原文档
            XSSFWorkbook workbook = new XSSFWorkbook(new FileInputStream(inputSrc));
            // 将图片输出流写入Excel
            int pictureIdx = workbook.addPicture(os.toByteArray(), Workbook.PICTURE_TYPE_PNG);
            // 获取工作簿中的图片对象
            POIXMLDocumentPart poixmlDocumentPart = workbook.getAllPictures().get(pictureIdx);


            for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
    
    //获取每个Sheet表
                XSSFSheet sheet = workbook.getSheetAt(i);
                PackagePartName ppn = poixmlDocumentPart.getPackagePart().getPartName();
                String relType = XSSFRelation.IMAGES.getRelation();
                // 为当前Sheet添加图片关系
                PackageRelationship pr = sheet.getPackagePart().addRelationship(ppn, TargetMode.INTERNAL, relType, null);
                // 将图片关系添加到Sheet底层结构中
                sheet.getCTWorksheet().addNewPicture().setId(pr.getId());
            }
            try {
    
    
                workbook.write(new FileOutputStream(outputSrc));
            } finally {
    
    
                os.close();
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }

猜你喜欢

转载自blog.csdn.net/Ka__ze/article/details/131881795