对图片或者PDF流文件加水印

当业务需要对图片/PDF文件预览或者下载文件加水印实现,当我们获取不到图片路径时(如图片放在阿里云服务器),故我们只能对文件流加水印!!!(文章末尾附完整代码)

fileId为get请求中携带的文件id,我们通过传递的文件id获取文件扩展名判断文件类型,选择调用不同的方法,setWatermark()是给pdf文件加水印,paintWatermarkImage()图片格式加水印

DefaultFile sysFile = fileManager.get(fileId);
 String extensionName = sysFile.getExtensionName();

 if(extensionName.equals("pdf")){
          WaterMarkPdfUtil.setWatermark(response,byteArrayOutputStream);
      }else{
          ImageWatermarkUtil.paintWatermarkImage(response,byteArrayOutputStream,fileId);
      }

setWatermark()给pdf文件加水印

该方法有两个参数,outputStream为pdf文件的文件流

setWatermark(HttpServletResponse response, OutputStream outputStream)
package com.hotent.util;

import com.alibaba.fastjson.JSONObject;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletResponse;
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.text.SimpleDateFormat;


@Slf4j
public class WaterMarkPdfUtil {
	//设置水印内容
    private static final String MARK_TEXT = "公司LOGO\npinyinming\n";
    private static final BaseColor bg = new BaseColor(0x70, 0x70, 0x70);
    private static final int FONT_SIZE = 60;
    private static final float ALPHA = 0.2F;
    private static final String FONT_NAME = "宋体";
    private static final int FONT_STYLE = Font.BOLD;


    /**
     *
     * @param response pdf文件加水印后
     * @param outputStream  pdf原文件流
     * @throws DocumentException
     * @throws IOException
     */
    public static void setWatermark(HttpServletResponse response, OutputStream outputStream, String fileId)
            throws DocumentException, IOException {


        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd:HH:mm");
        String format = simpleDateFormat.format(System.currentTimeMillis());
        //水印内容拼接当前时间
        String MARK = MARK_TEXT+format;


        // 使用"\n"将内容进行分割
        String[] waterMarkContents = MARK.split("\n");
        log.info("水印内容:{}", JSONObject.toJSONString(waterMarkContents));

        InputStream in = new ByteArrayInputStream(((ByteArrayOutputStream)outputStream).toByteArray());
        PdfReader reader = new PdfReader(in);

//

        PdfStamper stamper = new PdfStamper(reader,
                response.getOutputStream());
//                new FileOutputStream("C:\\Users\\HP\\Desktop\\dd.pdf"));

        // 获取总页数 +1, 下面从1开始遍历
        int total = reader.getNumberOfPages() + 1;


        BaseFont base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);

        // 间隔
        int interval = 20;
        // 获取水印文字的最大高度和宽度
        int textH = 0, textW = 0;
        for (int j = 0; j < waterMarkContents.length; j++) {
            JLabel label = new JLabel();
            log.info("waterMarkContents[j]:{}", waterMarkContents[j]);
            label.setText(waterMarkContents[j]);
            FontMetrics metrics = label.getFontMetrics(label.getFont());
            if (textH < metrics.getHeight()) {
                textH = metrics.getHeight();
            }
            if (textW < metrics.stringWidth(label.getText())) {
                textW = metrics.stringWidth(label.getText());
            }


            // 设置水印透明度
            PdfGState gs = new PdfGState();
            gs.setFillOpacity(ALPHA);
            gs.setStrokeOpacity(ALPHA);

            Rectangle pageSizeWithRotation = null;
            PdfContentByte content = null;
            for (int i = 1; i < total; i++) {
                content = stamper.getOverContent(i);
                content.saveState();
                content.setGState(gs);

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


                // 设置颜色
                content.setColorFill(bg);

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


                int lineX =0;
                for (int z = 0; z < waterMarkContents.length; z++) {
                    content.showTextAligned(Element.ALIGN_RIGHT, waterMarkContents[z], (float) (pageWidth*0.7 + textW*(z-1)),
                            (float) (pageHeight*0.7-textW/1.4-(textH + textH+90)*z),45);
                }

                content.endText();
            }

            // 关闭流
            stamper.close();
            reader.close();
        }
    }

}

paintWatermarkImage()方法,对图片格式加水印

paintWatermarkImage(HttpServletResponse response,OutputStream outputStream,String fileId)主要有两个参数,outputStream是我们图片的流数据,response是为了给添加水印后调用response.getOutputStream()返回加水印后的流数据
方法中水印的位置以及字体信息都有相应注释,可找到自行设置,本文设置水印为单个居中。(也可自行加for循环使水印铺满全屏)

package com.hotent.util;

import org.springframework.web.multipart.MultipartHttpServletRequest;
import sun.plugin2.jvm.CircularByteBuffer;

import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.List;

public class ImageWatermarkUtil {
    private static final double ROTATE_ANGLE_RADIANS = Math.toRadians(30);
    private static final double SIN_ROTATE_ANGLE = Math.sin(ROTATE_ANGLE_RADIANS);
    private static final double COS_ROTATE_ANGLE = Math.cos(ROTATE_ANGLE_RADIANS);

    //定义水印文字样式
    private static final Color FONT_COLOR = new Color(0x70, 0x70, 0x70);
    //宋体  微软雅黑
    private static final String FONT_NAME = "宋体";
    private static final int FONT_STYLE = Font.BOLD;
    private static final float ALPHA = 0.2F;

//    public static void main(String[] args) throws IOException {
//        ImageWatermarkUtil.paintWatermarkImage(inputPath, outPutPath);
//    }

    /**
     *
     * @param outputStream 图片原始
     * @param response 图片加水印之后
     */
    public static void paintWatermarkImage(HttpServletResponse response,OutputStream outputStream,String fileId) {
        String text = "公司LOGO\npinyinming\n";
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd:HH:mm");
        String format = simpleDateFormat.format(System.currentTimeMillis());
        String MARK = text+format;

        Image image = null;
        try {
            InputStream in = new ByteArrayInputStream(((ByteArrayOutputStream)outputStream).toByteArray());
            image = ImageIO.read(in);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //计算原始图片宽度长度
        int width = image.getWidth(null);
        int height = image.getHeight(null);

        // 三角函数算出来的最小宽高,不浪费内存
        width = (int) (width * COS_ROTATE_ANGLE + height * SIN_ROTATE_ANGLE);
        height = (int) (width * SIN_ROTATE_ANGLE + height * COS_ROTATE_ANGLE);
        BufferedImage bufferedImage =null;
        try {
            //创建图片缓存对象
            bufferedImage= new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            //创建java绘图工具对象
            Graphics2D g = bufferedImage.createGraphics();
            //参数主要是,原图,坐标,宽高
            g.drawImage(image, 0, 0, width, height, null);
            // 考虑到大尺寸图片,水印在手机上显示比较小,字体大小根据图片宽度来设置
            g.setFont(new Font(FONT_NAME, FONT_STYLE, (width / MARK.length())*4));
            g.setColor(FONT_COLOR);
            // 设置旋转角度,透明度
            g.rotate(-ROTATE_ANGLE_RADIANS, bufferedImage.getWidth() / 2, bufferedImage.getHeight() / 2);
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, ALPHA));

            g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            List<String> watermarkTextLines = Arrays.asList(MARK.split("\n"));
            int blockWidth = getTextBlockWidth(g, watermarkTextLines);
            int blockHeight = getTextBlockHeight(g, watermarkTextLines);
            int evenStartX = -(blockWidth + 480) / 2;  // 偶数行的起始位置
            evenStartX = 0;
            int lineNum = 0;  // 当前行号
//            for (int y = 20; y < height; y += (blockHeight*3+ blockHeight)) {
//                // 水印交错排列
//                lineNum++;
//                int startX = lineNum % 2 == 0 ? evenStartX : 0;
//
//                for (int x = startX; x < width; x += (blockWidth*3+ blockWidth)) {
//                    drawString(g, blockWidth, x, y, watermarkTextLines);
//                }
//            }
            drawString(g, blockWidth, (int) (width*0.4), (int) (height*0.4), watermarkTextLines);
            g.dispose();
            ImageIO.write(bufferedImage, "jpg", response.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    private static int getTextBlockHeight(Graphics g, List<String> watermarkTextLines) {
        return g.getFontMetrics().getHeight() * watermarkTextLines.size();
    }

    private static int getTextBlockWidth(Graphics g, List<String> watermarkTextLines) {
        int blockWidth = 0;  // 整个文本块的宽度
        for (String line : watermarkTextLines) {
            int lineWidth = g.getFontMetrics().stringWidth(line);
            if (lineWidth > blockWidth) {
                blockWidth = lineWidth;
            }
        }
        return blockWidth;
    }

    // 绘制一个水印块,多行文本居中对齐
    private static void  drawString(Graphics g, int blockWidth, int x, int y, List<String> watermarkTextLines) {
        int midX = x + blockWidth / 2;  // 中线的x坐标
        for (String line : watermarkTextLines) {
            int lineWidth = g.getFontMetrics().stringWidth(line);
            int lineX = midX - lineWidth / 2;
            // 对齐
            lineX = lineX - (int) Math.round(y * SIN_ROTATE_ANGLE / COS_ROTATE_ANGLE);
            g.drawString(line, lineX, y);
            y += g.getFontMetrics().getHeight()*2;
        }
    }

}

最后我们通过调用请求接口可得到结果

PDF文件加水印后对比

加水印之前

加水印后
链接: https://blog.csdn.net/m0_63279291/article/details/124323317

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值