Java 水印操作的设计与实现

1 说明

      本文主要针对图片和PDF的水印操作设计了四种场景,下图为几个示例在这里插入图片描述

2 实现

      针对上述四个场景分别实现如下,首先,引入依赖,因为对图片的水印操作只需要使用JDK自带的功能即可,主要引入的依赖是iText的依赖包。其次,设计一个参数类方便调用本服务时参数传递与接收。之后设计了一个文件属性类方便提取MultipartFile对象传递过来的文件名和文件类型。

		<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.5.13</version>
        </dependency>

水印参数类

import com.itextpdf.text.BaseColor;
import lombok.Data;

import java.awt.*;

/**
 * 文字水印参数
 *
 * @author 一朝风月
 * @date 2021/8/3 14:23
 */
@Data
public class WaterMarkerParams {
    /**
     * 水印内容
     */
    private String mark;
    /**
     * 水印颜色
     */
    private Color color;
    /**
     * 字体
     */
    private String font;
    /**
     * 水印字体大小
     */
    private int fontSize;
    /**
     * 水印位置 0:右下角,1:居中,9:自定义坐标
     */
    private int position;
    /**
     * 文字样式, 1:居中,2:铺满
     */
    private int align;
    /**
     * 文字倾斜角度
     */
    private int angle;
    /**
     * x坐标
     */
    private int x;
    /**
     * y坐标
     */
    private int y;
    /**
     * 旋转角度
     */
    private Integer degree;
    /**
     * 透明度
     */
    private float transparency;
    /**
     * 不透明度
     */
    private float opacity;
    /**
     * PDF水印颜色
     */
    private BaseColor baseColor;
    /**
     * 视图宽
     */
    private float fitWidth;
    /**
     * 视图高
     */
    private float fitHeight;
    /**
     * pdf图片放置X轴
     */
    private float pdfX;
    /**
     * pdf图片放置Y轴
     */
    private float pdfY;

    public WaterMarkerParams() {
        this.mark = "";
        this.color = Color.white;
        this.fontSize = 9;
        this.font = "宋体";
        this.position = 0;
        this.transparency = 0.3f;
        this.opacity = 0.2f;
        this.baseColor = BaseColor.BLACK;
        this.fitWidth = 200;
        this.fitHeight = 200;
        this.pdfX = 200;
        this.pdfY = 206;
        this.align = 1;
        this.angle = 30;
    }
}

文件属性类

import lombok.Data;

/**
 * 文件属性
 *
 * @author 一朝风月
 * @date 2021/8/4 16:34
 */
@Data
public class FileProps {
    private String fileName;
    private String fileType;

    public FileProps(String fileSrcName) {
        if (fileSrcName.contains(".") && fileSrcName != null) {
            int location = fileSrcName.lastIndexOf(".");
            this.fileName = fileSrcName.substring(0, location);
            this.fileType = fileSrcName.substring(location + 1);
        } else {
            this.fileName = "";
            this.fileType = "";
        }
    }
}

2.1 为图片添加文字水印

	/**
     * 文字水印
     *
     * @param file                图片
     * @param params              水印参数
     * @param httpServletResponse 响应
     * @return 加文字后的图片
     * @throws IOException IO异常
     */
    @PostMapping("text")
    public Result mText(MultipartFile file, WaterMarkerParams params, HttpServletResponse httpServletResponse) throws IOException {
        FileProps fileProps = new FileProps(file.getOriginalFilename());
        if (!"pic".equalsIgnoreCase(ExtService.getCategory(fileProps.getFileType()))) {
            return Result.error("请传入图片格式的文件!");
        }
        Image srcImg = ImageIO.read(file.getInputStream());
        int srcImgWidth = srcImg.getWidth(null);
        int srcImgHeight = srcImg.getHeight(null);

        BufferedImage bufferedImage = new BufferedImage(srcImgWidth, srcImgHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = bufferedImage.createGraphics();
        g.drawImage(srcImg, 0, 0, srcImgWidth, srcImgHeight, null);
        Font font = new Font(params.getFont(), Font.PLAIN, params.getFontSize());
        g.setColor(params.getColor());
        g.setFont(font);

        if (params.getX() > srcImgWidth || params.getY() > srcImgHeight) {
            return Result.error("传入x,y坐标超出图片尺寸!");
        }
        if (params.getPosition() == 0) {
            params.setX(srcImgWidth - getWatermarkLength(params.getMark(), g) - 3);
            params.setY(srcImgHeight - 3);
        }
        if (params.getPosition() == 1) {
            params.setX((srcImgWidth - getWatermarkLength(params.getMark(), g)) / 2);
            params.setY(srcImgHeight / 2);
        }
        g.drawString(params.getMark(), params.getX(), params.getY());
        g.dispose();

        // 输出图片
        httpServletResponse.setContentType("application/*");
        httpServletResponse.setHeader("Content-Disposition", "attachment;filename=\"" + fileProps.getFileName() + "\"");
        ImageIO.write(bufferedImage, fileProps.getFileType(), httpServletResponse.getOutputStream());
        //测试图片代码
//        FileOutputStream outImgStream = new FileOutputStream("D:\\test\\" + UuidUtil.getUuid() + "." + fileType);
//        ImageIO.write(bufferedImage, fileType, outImgStream);
//        outImgStream.flush();
//        outImgStream.close();
        return null;
    }

2.2 为图片添加图片水印

	/**
     * 图片水印
     *
     * @param srcImage            图片
     * @param iconImage           水印图片
     * @param params              水印参数
     * @param httpServletResponse 响应
     * @return 加水印的图片
     * @throws IOException IO异常
     */
    @PostMapping("picture")
    public Result mPicture(MultipartFile srcImage, MultipartFile iconImage, WaterMarkerParams params,
                           HttpServletResponse httpServletResponse) throws IOException {
        FileProps fileProps = new FileProps(srcImage.getOriginalFilename());
        Image srcImg = ImageIO.read(srcImage.getInputStream());
        Image iconImg = ImageIO.read(iconImage.getInputStream());

        BufferedImage buffImg = new BufferedImage(srcImg.getWidth(null), srcImg.getHeight(null),
                BufferedImage.TYPE_INT_RGB);
        Graphics2D g = buffImg.createGraphics();
        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        int srcImgWidth = srcImg.getWidth(null);
        int srcImgHeight = srcImg.getHeight(null);
        int iconImgWidth = iconImg.getWidth(null);
        int iconImgHeight = iconImg.getHeight(null);
        if (params.getX() > srcImgWidth || params.getY() > srcImgHeight) {
            return Result.error("传入x,y坐标超出图片尺寸!");
        }
        if (params.getPosition() == 0) {
            params.setX(srcImgWidth - iconImgWidth);
            params.setY(srcImgHeight - iconImgHeight);
        }
        if (params.getPosition() == 1) {
            params.setX(srcImgWidth / 2 - iconImgWidth / 2);
            params.setY(srcImgHeight / 2 - iconImgHeight / 2);
        }
        if (params.getPosition() == 2) {
            params.setX(0);
            params.setY(0);
        }
        g.drawImage(srcImg.getScaledInstance(srcImgWidth, srcImgHeight, Image.SCALE_SMOOTH), 0,
                0, null);
        if (null != params.getDegree()) {
            g.rotate(Math.toRadians(params.getDegree()), (double) buffImg.getWidth() / 2, (double) buffImg.getHeight() / 2);
        }
        ImageIcon imgIcon = new ImageIcon(iconImage.getBytes());
        Image img = imgIcon.getImage();
        // 透明度
        g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, params.getTransparency()));
        g.drawImage(img, params.getX(), params.getY(), null);
        g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
        g.dispose();

        httpServletResponse.setContentType("application/*");
        httpServletResponse.setHeader("Content-Disposition", "attachment;filename=\"" + fileProps.getFileName() + "\"");
        ImageIO.write(buffImg, fileProps.getFileType(), httpServletResponse.getOutputStream());

        // 生成图片
//        FileOutputStream outImgStream = new FileOutputStream("D:\\test\\" + UuidUtil.getUuid() + "." + fileType);
//        ImageIO.write(buffImg, fileType, outImgStream);
//        outImgStream.flush();
//        outImgStream.close();
        return null;
    }

2.3 PDF添加文字水印

 	/**
     * PDF增加水印
     *
     * @return 加文字后的PDF
     */
    @PostMapping("pdfText")
    public Result mPdfText(MultipartFile file, WaterMarkerParams params,
                           HttpServletResponse httpServletResponse) throws IOException, DocumentException {
        FileProps fileProps = new FileProps(file.getOriginalFilename());
        PdfReader reader = new PdfReader(file.getInputStream());
//        FileOutputStream outImgStream = new FileOutputStream("D:\\test\\" + UuidUtil.getUuid() + "."
//                + fileProps.getFileType());
        httpServletResponse.setContentType("application/*");
        httpServletResponse.setHeader("Content-Disposition", "attachment;filename=\"" + fileProps.getFileName() + "\"");
        PdfStamper stamper = new PdfStamper(reader, httpServletResponse.getOutputStream());

        int total = reader.getNumberOfPages() + 1;
        PdfContentByte content;
        BaseFont base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
        PdfGState gs = new PdfGState();
        for (int i = 1; i < total; i++) {
            content = stamper.getOverContent(i);
            gs.setFillOpacity(params.getOpacity());
            content.beginText();
            content.setColorFill(BaseColor.LIGHT_GRAY);
            content.setFontAndSize(base, params.getFontSize());
            if (params.getAlign() == 1) {
                content.setTextMatrix(70, 200);
                content.showTextAligned(Element.ALIGN_CENTER, params.getMark(), 300, 350, params.getAngle());
            }
            if (params.getAlign() == 2) {
                // 间隔
                int interval = -5;
                // 获取水印文字的高度和宽度
                int textH, textW;
                JLabel label = new JLabel();
                label.setText(params.getMark());
                FontMetrics metrics = label.getFontMetrics(label.getFont());
                textH = metrics.getHeight();
                textW = metrics.stringWidth(label.getText());
                // 获取每一页的高度、宽度
                com.itextpdf.text.Rectangle pageSizeWithRotation = reader.getPageSizeWithRotation(i);
                float pageHeight = pageSizeWithRotation.getHeight();
                float pageWidth = pageSizeWithRotation.getWidth();

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

2.4 PDF添加图片水印

 	/**
     * PDF增加图片水印
     *
     * @return 加图片水印后的PDF
     */
    @PostMapping("pdfPicture")
    public Result mPdfPicture(MultipartFile file, MultipartFile iconImage, WaterMarkerParams params,
                              HttpServletResponse httpServletResponse) throws IOException, DocumentException {
        FileProps fileProps = new FileProps(file.getOriginalFilename());
        PdfReader reader = new PdfReader(file.getInputStream());
        httpServletResponse.setContentType("application/*");
        httpServletResponse.setHeader("Content-Disposition", "attachment;filename=\"" + fileProps.getFileName() + "\"");
        PdfStamper stamper = new PdfStamper(reader, httpServletResponse.getOutputStream());

        int total = reader.getNumberOfPages() + 1;
        PdfContentByte content;
        PdfGState gs = new PdfGState();
        for (int i = 1; i < total; i++) {
            content = stamper.getOverContent(i);
            gs.setFillOpacity(params.getOpacity());
            content.beginText();
            com.itextpdf.text.Image image = com.itextpdf.text.Image.getInstance(iconImage.getBytes());
            image.setAbsolutePosition(params.getPdfX(), params.getPdfY());
            image.scaleToFit(params.getFitWidth(), params.getFitHeight());
            content.addImage(image);
            content.endText();
        }
        stamper.close();
        return null;
    }

2.5 如何有既加文字又加图片的情况,那就调用两次接口吧

3 测试

      测试后的图和说明中的那幅图一样。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值