JAVA给PDF、图片、视频、Excel、Word、PPT添加水印

下面是新建的工具类,主要功能是在导出系统中的文件流的时候自动加上自定义的水印,其中有用到POI和ItextPdf以及ffmpeg

MAVEN引入

        
        
            org.apache.poi
            poi
            4.1.1
        
        
            org.apache.poi
            ooxml-schemas
            1.4
        
        
            org.apache.poi
            poi-ooxml
            4.1.1
        
        
        
            com.itextpdf
            itextpdf
            5.5.10
        
        
            com.itextpdf
            itext-asian
            5.2.0
        
        
            org.apache.pdfbox
            pdfbox
            2.0.18
        
        
        
            org.bytedeco
            javacv-platform
            1.5
                

工具类

package com.xx.xx.util;

import com.itextpdf.text.Element;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import com.spire.presentation.*;
import com.spire.presentation.drawing.FillFormatType;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.ooxml.POIXMLProperties;
import org.apache.poi.openxml4j.opc.PackagePartName;
import org.apache.poi.openxml4j.opc.PackageRelationship;
import org.apache.poi.openxml4j.opc.TargetMode;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xslf.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFRelation;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFHeader;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.xmlbeans.XmlObject;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.*;
import org.bytedeco.opencv.opencv_core.IplImage;
import sun.font.FontDesignMetrics;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.xml.namespace.QName;
import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.HashMap;

public class WaterMarkUtils {

    public static ByteArrayOutputStream addWaterMarkCommon(String waterMarkContent, String fileName, ByteArrayOutputStream sourceOs) throws IOException {
        ByteArrayOutputStream targetOs;
        fileName = fileName.toLowerCase();
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(sourceOs.toByteArray());
        if (fileName.contains(".pdf")) {
            targetOs = WaterMarkUtils.markPdf(byteArrayInputStream, waterMarkContent);
        } else if (fileName.contains(".jpg")) {
            targetOs = WaterMarkUtils.markImage(byteArrayInputStream, waterMarkContent, "jpg");
        } else if (fileName.contains(".png")) {
            targetOs = WaterMarkUtils.markImage(byteArrayInputStream, waterMarkContent, "png");
        } else if (fileName.contains(".docx") || fileName.contains(".doc")) {
            targetOs = WaterMarkUtils.markWord(byteArrayInputStream, waterMarkContent);
        } else if (fileName.contains(".xlsx") || fileName.contains(".xls")) {
            targetOs = WaterMarkUtils.markExcel(byteArrayInputStream, waterMarkContent);
        } else if (fileName.contains(".ppt") || fileName.contains(".pptx")) {
            targetOs = WaterMarkUtils.markPPT(byteArrayInputStream, waterMarkContent);
        } else if (fileName.contains(".mp4")) {
            targetOs = WaterMarkUtils.markVideo(byteArrayInputStream, waterMarkContent, "mp4");
        } else if (fileName.contains(".wmv")) {
            targetOs = WaterMarkUtils.markVideo(byteArrayInputStream, waterMarkContent, "wmv");
        } else {
            targetOs = sourceOs;
        }

        return targetOs;
    }

    public static ByteArrayOutputStream markVideo(ByteArrayInputStream ins, String waterMarkContent, String format) {
        FFmpegFrameGrabber frameGrabber;
        Frame frame;
        FFmpegFrameRecorder recorder;
        try {
            File file = FileKit.writeFile(ins, "D:\\test.mp4");
            String markFilePath = "D:\\test_mark.mp4";
            //抓取视频资源
            frameGrabber = new FFmpegFrameGrabber(file);
            frameGrabber.start();
            FileKit.delete(file);
            recorder = toRecorder(format, frameGrabber, markFilePath);

            recorder.start();
            int count = 0;
            int markCount = 0;
            double randomValue = 0D;
            System.gc();
            while (true) {
                frame = frameGrabber.grabFrame();
                if (frame == null) {
                    break;
                }
                //判断图片帧
                if (frame.image != null) {
                    if (count % 100 == 0) {
                        markCount = 10;
                        randomValue = Math.random();
                    }
                    if (markCount > 0) {
                        markFrame(waterMarkContent, frame, recorder, randomValue);
                        markCount--;
                    } else {
                        recorder.record(frame);
                    }
                }
                //设置音频
                if (frame.samples != null) {
                    recorder.recordSamples(frame.sampleRate, frame.audioChannels, frame.samples);
                }
                count++;
            }
            return closeAndDeleteData(frameGrabber, recorder, markFilePath);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private static ByteArrayOutputStream closeAndDeleteData(FFmpegFrameGrabber frameGrabber, FFmpegFrameRecorder recorder, String markFilePath) throws IOException {
        recorder.stop();
        recorder.release();
        frameGrabber.stop();
        File markFile = new File(markFilePath);
        ByteArrayOutputStream outputStream = FileKit.readFile(markFile);
        FileKit.delete(markFile);
        System.gc();
        return outputStream;
    }

    private static FFmpegFrameRecorder toRecorder(String format, FFmpegFrameGrabber frameGrabber, String markFilePath) {
        FFmpegFrameRecorder recorder;
        recorder = new FFmpegFrameRecorder(markFilePath, frameGrabber.getImageWidth(), frameGrabber.getImageHeight(), frameGrabber.getAudioChannels());
        recorder.setFormat(format);
        recorder.setSampleRate(frameGrabber.getSampleRate());
        recorder.setFrameRate(frameGrabber.getFrameRate());
        recorder.setTimestamp(frameGrabber.getTimestamp());
        recorder.setVideoBitrate(frameGrabber.getVideoBitrate());
        recorder.setVideoCodec(frameGrabber.getVideoCodec());
        return recorder;
    }

    private static void markFrame(String waterMarkContent, Frame frame, FFmpegFrameRecorder recorder, double randomValue) throws FrameRecorder.Exception {
        IplImage iplImage = Java2DFrameUtils.toIplImage(frame);
        BufferedImage buffImg = Java2DFrameUtils.toBufferedImage(iplImage);
        Graphics2D graphics = buffImg.createGraphics();
        graphics.setColor(Color.WHITE);
        graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.3f));
        graphics.setFont(new Font("宋体", Font.BOLD, 20));
        graphics.drawString(waterMarkContent, (int) (iplImage.width() * randomValue), (int) (iplImage.height() * randomValue));
        graphics.dispose();
        Frame newFrame = Java2DFrameUtils.toFrame(buffImg);
        recorder.record(newFrame);
        iplImage.close();
    }

    /**
     * 给图片添加水印文字、可设置水印文字的大小、旋转角度、透明度
     *
     * @param logoText 水印内容
     */
    public static ByteArrayOutputStream markImage(ByteArrayInputStream inputStream, String logoText, String type) throws IOException {

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();


        // 1、读取源图片,Image获取图片宽度、高度
        Image scrImg = ImageIO.read(inputStream);
        BufferedImage buffImg = new BufferedImage(scrImg.getWidth(null), scrImg.getHeight(null), BufferedImage.TYPE_INT_RGB);

        // 2、得到画笔对象
        Graphics2D graphics = buffImg.createGraphics();

        // 3、设置对线段的锯齿状边缘处理
        graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        graphics.drawImage(scrImg.getScaledInstance(scrImg.getWidth(null), scrImg.getHeight(null), Image.SCALE_SMOOTH), 0, 0, null);

        // 4、设置水印倾斜度,这里是在图片的对角线上
        // 对角线长度lengthOfDiagonal
        double lengthOfDiagonal = Math.sqrt(Math.pow(buffImg.getWidth(), 2) + Math.pow(buffImg.getHeight(), 2));
        double v = (Math.pow(buffImg.getWidth(), 2) + Math.pow(lengthOfDiagonal, 2) - Math.pow(buffImg.getHeight(), 2)) / (2 * buffImg.getWidth() * lengthOfDiagonal);
        //get到了一个弧度数
        double acos = Math.acos(v);
        double myDegree = Math.toDegrees(acos);
        //这里的负号决定对角线-Math.toRadians(myDegree)
        graphics.rotate(-Math.toRadians(myDegree),
                (double) buffImg.getWidth() / 2,
                (double) buffImg.getHeight() / 2);

        // 5、设置水印文字颜色
        graphics.setColor(Color.DARK_GRAY);

        // 6、获取源图片的宽度和高度
        int width = scrImg.getWidth(null);
        int heigth = scrImg.getHeight(null);


        graphics.setFont(new Font("微软雅黑", Font.BOLD, 50));

        //8、设置透明度
        graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.3f));

        //9、设置文字位置
        FontDesignMetrics metrics = FontDesignMetrics.getMetrics(graphics.getFont());
        //获取文字宽度
        int strWidth = metrics.stringWidth(logoText);

        int xNum = width / strWidth + 1;

        int yNum = heigth / 50 + 1;

        int split = 50;

        for (int i = 1; i <= 2 * yNum; i++) {
            int y = -heigth + 50 * i + 5 * split * i;
            for (int j = 0; j < xNum; j++) {
                int x = strWidth * j + 3 * split * j;

                graphics.drawString(logoText, x, y);
            }
        }

        //11、释放资源
        graphics.dispose();

        //12、生成图片
        try {
            ImageIO.write(buffImg, type, outputStream);
        } catch (
                FileNotFoundException e) {
            e.printStackTrace();
        }
        return outputStream;
    }

    /**
     * 给PDF添加水印
     *
     * @param inputStream      原文件流
     * @param waterMarkContent 添加水印的内容
     */
    public static ByteArrayOutputStream markPdf(ByteArrayInputStream inputStream, String waterMarkContent) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
            // 水印的高和宽
            int waterMarkHeight = 30;
            int watermarkWeight = 60;

            // 水印间隔距离
            int waterMarkInterval = 100;

            // 读取PDF文件流
            PdfReader pdfReader = new PdfReader(inputStream);

            PdfDocument pdfDocument = new PdfDocument();

            // 创建PDF文件的模板,可以对模板的内容修改,重新生成新PDF文件
            PdfStamper pdfStamper = new PdfStamper(pdfReader, outputStream);

            //添加PDF自定义属性
            HashMap info = pdfReader.getInfo();
            if (StringUtil.isEmpty(info.get("Title"))) {
                info.put("Title", waterMarkContent);
            }
            pdfStamper.setMoreInfo(info);

            // 设置水印字体
            BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);

            // 设置PDF内容的Graphic State 图形状态
            PdfGState pdfGraPhicState = new PdfGState();
            // 填充透明度
            pdfGraPhicState.setFillOpacity(0.1f);
            // 轮廓不透明度
            pdfGraPhicState.setStrokeOpacity(0.1f);

            // PDF页数
            int pdfPageNum = pdfReader.getNumberOfPages() + 1;

            // PDF文件内容字节
            PdfContentByte pdfContent;

            // PDF页面矩形区域
            Rectangle pageRectangle;

            for (int i = 1; i < pdfPageNum; i++) {
                // 获取当前页面矩形区域
                pageRectangle = pdfReader.getPageSizeWithRotation(i);
                // 获取当前页内容,getOverContent表示之后会在页面内容的上方加水印
                pdfContent = pdfStamper.getOverContent(i);

                // 获取当前页内容,getOverContent表示之后会在页面内容的下方加水印
                // pdfContent = pdfStamper.getUnderContent(i);

                pdfContent.saveState();
                // 设置水印透明度
                pdfContent.setGState(pdfGraPhicState);

                // 开启写入文本
                pdfContent.beginText();
                // 设置字体
                pdfContent.setFontAndSize(baseFont, 20);

                // 在高度和宽度维度每隔waterMarkInterval距离添加一个水印
                for (int height = waterMarkHeight; height < pageRectangle.getHeight(); height = height + waterMarkInterval) {
                    for (int width = watermarkWeight; width < pageRectangle.getWidth() + watermarkWeight;
                         width = width + waterMarkInterval) {
                        // 添加水印文字并旋转30度角
                        pdfContent.showTextAligned(Element.ALIGN_LEFT, waterMarkContent, width - watermarkWeight,
                                height - waterMarkHeight, 30);
                    }
                }
                // 停止写入文本
                pdfContent.endText();
            }
            pdfStamper.close();
            pdfReader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return outputStream;
    }

    public static ByteArrayOutputStream markWord(ByteArrayInputStream ins, String markStr) {
        ByteArrayOutputStream ous = new ByteArrayOutputStream();
        try {
            XWPFDocument doc = new XWPFDocument(ins);
            //添加文档属性
            POIXMLProperties.CustomProperties props = doc.getProperties().getCustomProperties();
            props.addProperty("sample", markStr);
            XWPFHeaderFooterPolicy headerFooterPolicy = doc.createHeaderFooterPolicy();
            //添加文字水印
            headerFooterPolicy.createWatermark(markStr);
            XWPFHeader header = headerFooterPolicy.getHeader(XWPFHeaderFooterPolicy.DEFAULT);
            XWPFParagraph paragraph = header.getParagraphArray(0);
            paragraph.getCTP().newCursor();
            XmlObject[] xmlobjects = paragraph.getCTP().getRArray(0).getPictArray(0).selectChildren(new QName("urn:schemas-microsoft-com:vml", "shape"));
            if (xmlobjects.length > 0) {
                com.microsoft.schemas.vml.CTShape ctshape = (com.microsoft.schemas.vml.CTShape) xmlobjects[0];
                // set fill color
                ctshape.setFillcolor("#d8d8d8");
                // set rotation
                ctshape.setStyle(ctshape.getStyle() + ";rotation:315");
            }
            doc.write(ous);
            doc.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return ous;
    }

    /**
     * 创建水印图片
     * excel
     *
     * @param waterMark 水印内容
     * @return
     */
    public static BufferedImage createWaterMarkImage(String waterMark) {
        String[] textArray = waterMark.split("\n");
        Font font = new Font("microsoft-yahei", Font.PLAIN, 20);
        Integer width = 500;
        Integer 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();
        return image;
    }

    public static ByteArrayOutputStream markExcel(ByteArrayInputStream ins, String markStr) {
        ByteArrayOutputStream ous = new ByteArrayOutputStream();
        try {
            BufferedImage image = createWaterMarkImage(markStr);
            // 导出到字节流B
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            ImageIO.write(image, "png", os);
            XSSFWorkbook workbook = new XSSFWorkbook(ins);
            //添加自定义属性
            POIXMLProperties.CustomProperties customProperties = workbook.getProperties().getCustomProperties();
            customProperties.addProperty("sample", markStr);

            int pictureIdx = workbook.addPicture(os.toByteArray(), Workbook.PICTURE_TYPE_PNG);
            POIXMLDocumentPart poixmlDocumentPart = workbook.getAllPictures().get(pictureIdx);

            //获取每个Sheet表
            for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
                XSSFSheet sheet = workbook.getSheetAt(i);
                PackagePartName ppn = poixmlDocumentPart.getPackagePart().getPartName();
                String relType = XSSFRelation.IMAGES.getRelation();
                PackageRelationship pr = sheet.getPackagePart().addRelationship(ppn, TargetMode.INTERNAL, relType, null);
                sheet.getCTWorksheet().addNewPicture().setId(pr.getId());
            }
            workbook.write(ous);
            workbook.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return ous;
    }

    public static ByteArrayOutputStream markPPT(ByteArrayInputStream inputStream, String markStr) {
        ByteArrayOutputStream ous = new ByteArrayOutputStream();
        try {
            XMLSlideShow ppt = new XMLSlideShow(inputStream);
            //添加自定义属性
            POIXMLProperties.CustomProperties customProperties = ppt.getProperties().getCustomProperties();
            customProperties.addProperty("sample", markStr);
            Dimension pageSize = ppt.getPageSize();
            int fontSize = 80;
            int height = fontSize;
            int width = fontSize * (markStr.length() + 1);
            for (XSLFSlide slide : ppt.getSlides()) {
                // 在每一页上添加水印的代码 创建文本框对象
                XSLFTextShape waterMark = slide.createTextBox();
                waterMark.setTextRotation(-15D);
                XSLFTextParagraph paragraph = waterMark.addNewTextParagraph();
                XSLFTextRun run1 = paragraph.addNewTextRun();
                run1.setText(markStr);
                run1.setFontColor(Color.lightGray);
                run1.setFontSize(Double.parseDouble(fontSize + ""));
                run1.setFontFamily("宋体");
                paragraph.addLineBreak();
                // 设置水印位置和大小
                waterMark.setAnchor(new java.awt.Rectangle((int) pageSize.getWidth() / 2 - width / 2,
                        (int) pageSize.getHeight() / 2 - height / 2, width, height));
            }
            ppt.write(ous);
            ppt.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ous;
    }
}

你可能感兴趣的:(JAVA给PDF、图片、视频、Excel、Word、PPT添加水印)