Java 实现添加office水印

因为spire免费版无法整合springboot项目上会自动注入然后编译解析错误(好恶心)所以自己用POI写了个简易的

引入依赖

org.apache.poi:poi:5.2.1

直接上代码


import cn.hutool.core.io.FileUtil;
import com.asiainfo.business.tower.service.impl.WaterMarkServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ooxml.POIXMLProperties;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.sl.usermodel.PictureData;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.util.IOUtils;
import org.apache.poi.wp.usermodel.HeaderFooterType;
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.usermodel.*;
import org.apache.xmlbeans.XmlException;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STHdrFtr;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.yaml.snakeyaml.Yaml;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
@Component
public class OfficeWatermarkerUtils{


    private static String defaultColor="#000000";

    private static int DEFAULTCOLOR=0x000000;

    public static final File FONT_HEI = new File("D:\\dll\\simhei.ttf");

    public static void main(String[] args) {
        try {
            Font font = Font.createFont(Font.TRUETYPE_FONT,new FileInputStream(FONT_HEI)).deriveFont(Font.BOLD,50);
            BufferedImage bufferedImage = waterMarkByText(600,900,"156-17376552318",new Color(DEFAULTCOLOR),font,315.0,0.5f);
            File outputfile = new File("E:\\download\\111.png");
            ImageIO.write(bufferedImage, "png", outputfile);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 添加word水印
     * @param filePath 文件路径
     * @param watermark 水印
     * @param cipher 自定义属性
     * @param outPath 输出路径
     * @throws IOException
     * @throws XmlException
     */
    public static boolean addWordWaterMark(String filePath, String watermark, String cipher, String outPath) throws IOException, InvalidFormatException, FontFormatException {
        File file = new File(filePath);
        InputStream in = new FileInputStream(file);
        XWPFDocument document = new XWPFDocument(in);
        POIXMLProperties.CustomProperties cp = document.getProperties().getCustomProperties();
        OutputStream out = new FileOutputStream(file);
        //添加自定义属性
        if (cp.getProperty("cipher")!=null)
            cp.addProperty("cipher",cipher);
//        没有header.xml添加xml
        if (document.getHeaderFooterPolicy().getHeader(STHdrFtr.DEFAULT)==null
                && document.getHeaderFooterPolicy().getHeader(STHdrFtr.FIRST)==null
                && document.getHeaderFooterPolicy().getHeader(STHdrFtr.EVEN)==null)
            document.createHeader(HeaderFooterType.DEFAULT);
//        生成图片水印
        Font font = Font.createFont(Font.TRUETYPE_FONT,new FileInputStream(FONT_HEI)).deriveFont(Font.BOLD,50);
        BufferedImage bufferedImage = waterMarkByText(1200,1600,watermark,new Color(DEFAULTCOLOR),font,315.0,0.5f);
        File watermarkPic = new File(outPath+File.separator+"wordWatermark.png");
        ImageIO.write(bufferedImage, "png", watermarkPic);
        document.addPictureData(new FileInputStream(watermarkPic), XWPFDocument.PICTURE_TYPE_PNG);
        document.write(out);
        out.flush();
        File outFile = new File(outPath+file.getName());
        FileUtils.copyFile(file, outFile);
        editWordHeader(outFile);//添加水印
        log.info("明水印增加完成:{}",file.getName());
        if (in != null) {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (document != null) {
            document.close();
        }
        if (out != null) {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return true;
    }

    /**
     * 添加excel水印
     * @param path 路径
     * @param fileName 文件名
     * @param watermark 水印
     * @param cipher 密文
     * @param outPath 输出路径
     * @throws Exception
     */
    public static boolean addExcelWatermark(String path,String fileName,String watermark,String cipher,String outPath) throws Exception {
        //创建水印图片
        String bg = "excelWatermark.png";
        File bgFile = new File(path+bg);
        log.info("生成图片路径:{}",bgFile.getAbsolutePath());
        Font font = Font.createFont(Font.TRUETYPE_FONT,new FileInputStream(FONT_HEI)).deriveFont(Font.BOLD,50);
        createImage(watermark, font, bgFile, 0.2f);

        File file = new File(path + fileName);
        XSSFWorkbook workbook = new XSSFWorkbook(new FileInputStream(file));
        POIXMLProperties.CustomProperties cp = workbook.getProperties().getCustomProperties();
        InputStream is;
        byte[] bytes;
        int pictureIdx;
        is = new FileInputStream(path + bg);
        bytes = IOUtils.toByteArray(is);
        pictureIdx = workbook.addPicture(bytes, Workbook.PICTURE_TYPE_PNG);
        is.close();

        for (int index = 0; index < workbook.getNumberOfSheets(); index++) {
            XSSFSheet sheet = workbook.getSheetAt(index);
            String rID = sheet.addRelation(null, XSSFRelation.IMAGES,workbook
                    .getAllPictures().get(pictureIdx)).getRelationship().getId();
            sheet.getCTWorksheet().addNewPicture().setId(rID);
        }
        //添加自定义属性
        if (cp.getProperty("cipher")!=null)
            cp.addProperty("cipher",cipher);
        FileOutputStream out = new FileOutputStream(file);
        workbook.write(out);
        out.flush();
        FileUtils.copyFile(file, new File(outPath+file.getName()));
        workbook.close();
        log.info("明水印增加完成:{}",fileName);
        if (out != null) {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return true;
    }

    /**
     * 给幻灯片母版添加文本达成水印效果
     * @param path
     * @param fileName
     * @param watermark
     * @param cipher
     * @param outPath 输出路径
     * @throws Exception
     */
    public static boolean addPPTWatermark(String path, String fileName, String watermark, String cipher,String outPath) throws Exception{
        File file = new File(path + fileName);

        XMLSlideShow slideShow = new XMLSlideShow(new FileInputStream(file));
        POIXMLProperties.CustomProperties cp = slideShow.getProperties().getCustomProperties();
        Dimension dimension = slideShow.getPageSize();
        Font font = Font.createFont(Font.TRUETYPE_FONT,new FileInputStream(FONT_HEI)).deriveFont(Font.BOLD,50);
        BufferedImage bufferedImage = waterMarkByText(1920,1080,watermark,new Color(DEFAULTCOLOR),font,315.0,0.2f);
        File outputfile = new File(outPath+File.separator+"pptWatermark.png");
        ImageIO.write(bufferedImage, "png", outputfile);
        byte[] pictureData = IOUtils.toByteArray(new FileInputStream(outputfile));
        XSLFPictureData pd = slideShow.addPicture(pictureData, PictureData.PictureType.PNG);

        //不判断,直接给所有母版添加水印
        for (XSLFSlideMaster master : slideShow.getSlideMasters()) {
            XSLFPictureShape pictShape = master.createPicture(pd);
            Rectangle rect = new Rectangle(0, 0, (int)dimension.getWidth(), (int)dimension.getHeight());
            pictShape.setAnchor(rect);
        }
        //添加自定义属性
        if (cp.getProperty("cipher")!=null)
            cp.addProperty("cipher",cipher);
        FileOutputStream out = new FileOutputStream(file);
        slideShow.write(out);
        out.flush();
        FileUtils.copyFile(file, new File(outPath+file.getName()));
        if (out != null) {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        slideShow.close();
        log.info("明水印增加完成:{}",file.getName());
        return true;
    }

    private static final String watermarkXml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
            "<w:hdr xmlns:wpc=\"http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas\" xmlns:cx=\"http://schemas.microsoft.com/office/drawing/2014/chartex\" xmlns:cx1=\"http://schemas.microsoft.com/office/drawing/2015/9/8/chartex\" xmlns:cx2=\"http://schemas.microsoft.com/office/drawing/2015/10/21/chartex\" xmlns:cx3=\"http://schemas.microsoft.com/office/drawing/2016/5/9/chartex\" xmlns:cx4=\"http://schemas.microsoft.com/office/drawing/2016/5/10/chartex\" xmlns:cx5=\"http://schemas.microsoft.com/office/drawing/2016/5/11/chartex\" xmlns:cx6=\"http://schemas.microsoft.com/office/drawing/2016/5/12/chartex\" xmlns:cx7=\"http://schemas.microsoft.com/office/drawing/2016/5/13/chartex\" xmlns:cx8=\"http://schemas.microsoft.com/office/drawing/2016/5/14/chartex\" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" xmlns:aink=\"http://schemas.microsoft.com/office/drawing/2016/ink\" xmlns:am3d=\"http://schemas.microsoft.com/office/drawing/2017/model3d\" xmlns:o=\"urn:schemas-microsoft-com:office:office\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\" xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:wp14=\"http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing\" xmlns:wp=\"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing\" xmlns:w10=\"urn:schemas-microsoft-com:office:word\" xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" xmlns:w14=\"http://schemas.microsoft.com/office/word/2010/wordml\" xmlns:w15=\"http://schemas.microsoft.com/office/word/2012/wordml\" xmlns:w16cex=\"http://schemas.microsoft.com/office/word/2018/wordml/cex\" xmlns:w16cid=\"http://schemas.microsoft.com/office/word/2016/wordml/cid\" xmlns:w16=\"http://schemas.microsoft.com/office/word/2018/wordml\" xmlns:w16sdtdh=\"http://schemas.microsoft.com/office/word/2020/wordml/sdtdatahash\" xmlns:w16se=\"http://schemas.microsoft.com/office/word/2015/wordml/symex\" xmlns:wpg=\"http://schemas.microsoft.com/office/word/2010/wordprocessingGroup\" xmlns:wpi=\"http://schemas.microsoft.com/office/word/2010/wordprocessingInk\" xmlns:wne=\"http://schemas.microsoft.com/office/word/2006/wordml\" xmlns:wps=\"http://schemas.microsoft.com/office/word/2010/wordprocessingShape\" mc:Ignorable=\"w14 w15 w16se w16cid w16 w16cex w16sdtdh wp14\"><w:p w14:paraId=\"2FD6A8B5\" w14:textId=\"7B4DEC51\" w:rsidR=\"007C48C4\" w:rsidRDefault=\"003B2BB3\">WPHEADER<w:r><w:rPr><w:noProof/></w:rPr><w:pict w14:anchorId=\"776FC76C\"><v:shapetype id=\"_x0000_t75\" coordsize=\"21600,21600\" o:spt=\"75\" o:preferrelative=\"t\" path=\"m@4@5l@4@11@9@11@9@5xe\" filled=\"f\" stroked=\"f\"><v:stroke joinstyle=\"miter\"/><v:formulas><v:f eqn=\"if lineDrawn pixelLineWidth 0\"/><v:f eqn=\"sum @0 1 0\"/><v:f eqn=\"sum 0 0 @1\"/><v:f eqn=\"prod @2 1 2\"/><v:f eqn=\"prod @3 21600 pixelWidth\"/><v:f eqn=\"prod @3 21600 pixelHeight\"/><v:f eqn=\"sum @0 0 1\"/><v:f eqn=\"prod @6 1 2\"/><v:f eqn=\"prod @7 21600 pixelWidth\"/><v:f eqn=\"sum @8 21600 0\"/><v:f eqn=\"prod @7 21600 pixelHeight\"/><v:f eqn=\"sum @10 21600 0\"/></v:formulas><v:path o:extrusionok=\"f\" gradientshapeok=\"t\" o:connecttype=\"rect\"/><o:lock v:ext=\"edit\" aspectratio=\"t\"/></v:shapetype><v:shape id=\"WordPictureWatermark\" o:spid=\"_x0000_s1032\" type=\"#_x0000_t75\" style=\"position:absolute;left:0;text-align:left;margin-left:0;margin-top:0;width:600pt;height:850pt;z-index:-251657216;mso-position-horizontal:center;mso-position-horizontal-relative:margin;mso-position-vertical:center;mso-position-vertical-relative:margin\" o:allowincell=\"f\"><v:imagedata r:id=\"r_id\" o:title=\"background\" gain=\"19661f\" blacklevel=\"22938f\"/></v:shape></w:pict></w:r><w:r w:rsidR=\"00995F1F\"><w:rPr><w:noProof/></w:rPr><w:pict w14:anchorId=\"51BBCB7B\"><v:shapetype id=\"_x0000_t136\" coordsize=\"21600,21600\" o:spt=\"136\" adj=\"10800\" path=\"m@7,l@8,m@5,21600l@6,21600e\"><v:formulas><v:f eqn=\"sum #0 0 10800\"/><v:f eqn=\"prod #0 2 1\"/><v:f eqn=\"sum 21600 0 @1\"/><v:f eqn=\"sum 0 0 @2\"/><v:f eqn=\"sum 21600 0 @3\"/><v:f eqn=\"if @0 @3 0\"/><v:f eqn=\"if @0 21600 @1\"/><v:f eqn=\"if @0 0 @2\"/><v:f eqn=\"if @0 @4 21600\"/><v:f eqn=\"mid @5 @6\"/><v:f eqn=\"mid @8 @5\"/><v:f eqn=\"mid @7 @8\"/><v:f eqn=\"mid @6 @7\"/><v:f eqn=\"sum @6 0 @5\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" o:connectlocs=\"@9,0;@10,10800;@11,21600;@12,10800\" o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\"/><v:handles><v:h position=\"#0,bottomRight\" xrange=\"6629,14971\"/></v:handles><o:lock v:ext=\"edit\" text=\"t\" shapetype=\"t\"/></v:shapetype><v:shape id=\"_x0000_s1030\" type=\"#_x0000_t136\" style=\"position:absolute;left:0;text-align:left;margin-left:0;margin-top:0;width:50pt;height:50pt;z-index:251655168;visibility:hidden\"><o:lock v:ext=\"edit\" selection=\"t\" text=\"f\" shapetype=\"f\"/></v:shape></w:pict></w:r></w:p></w:hdr>";
    private static final String watermarkRels = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
            "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"><Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image\" Target=\"media/PICTNAME\"/></Relationships>";
    /**
     * 为word添加图片水印
     * 对文档以压缩包文件打开,编辑下行目录中header.xml文件,header文件管理水印的生成,并将生成的水印图片添加到资源目录
     * @param file 输出的文件
     * @throws IOException
     */
    public static void editWordHeader(File file) throws IOException {
        List<File> headers,rels,picts;
        File outPath = new File(file.getParent()+"\\"+file.getName().split("\\.")[0]);
        ZipUtils.unZip(file,outPath.getAbsolutePath());
        File wordDirectory = new File(outPath.getAbsolutePath()+File.separator+"word");
        File _relsDirectory = new File(outPath.getAbsolutePath()+File.separator+"word"+File.separator+"_rels");
        File mediaDirectory = new File(outPath.getAbsolutePath()+File.separator+"word"+File.separator+"media");
        //编辑header.xml文件
        if (!wordDirectory.exists())
            wordDirectory.mkdirs();
        headers = List.of(wordDirectory.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return pathname.getName().matches("header[0-9]*\\.xml");
            }
        }));
        if (headers.size()==0) {//没有header文件则创建
            File newHeader = createHeader(wordDirectory.getAbsolutePath());
            headers.add(newHeader);
        }
        editHeader(headers);
        //确认新增的水印图片ID
        picts = List.of(mediaDirectory.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return pathname.getName().matches("image[0-9]*\\.png");
            }
        }));
        int pictId = 0;
        for (File pict : picts){
            int nav = Integer.parseInt(pict.getName().replace("image","").split("\\.")[0]);
            if (nav > pictId)
                pictId  = nav;
        }
        String pictName = "image"+pictId+".png";
        //编辑header.xml.rels文件
        if (!_relsDirectory.exists())
            _relsDirectory.mkdirs();
        rels = List.of(_relsDirectory.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return pathname.getName().matches("header[0-9]*\\.xml\\.rels");
            }
        }));
        if (rels.size()==0 || rels.size()!=headers.size())
            rels = createRels(_relsDirectory.getAbsolutePath(),headers.size());
        editRels(rels,pictName);

        ZipUtils.zip(outPath,new File(file.getAbsolutePath()));
        if (outPath.exists())
            FileUtil.del(outPath);
    }

    private static void editHeader(List<File> files) throws IOException {
        //保留原文档页眉部分,w:pPr记录页眉样式,w:r记录页眉文本
        Pattern pattern = Pattern.compile("<w:pPr[\s>].*</w:r>");
        Matcher matcher;
        for (File header : files){
            String headerContent="";
            String str = FileUtils.readFileToString(header, "UTF-8");
            matcher = pattern.matcher(str);
            while (matcher.find()){
                headerContent+=matcher.group();
            }
            //直接覆盖header文件,不对存在的页眉做处理
            FileUtils.writeStringToFile(header,watermarkXml.replace("WordPictureWatermark","WordPictureWatermark"+new Date().getTime()%10000000)
                    .replace("r_id","rId1").replace("WPHEADER",headerContent));
        }
    }

    private static void editRels(List<File> files,String pictName) throws IOException {
        for (File rels : files){
            FileUtils.writeStringToFile(rels,watermarkRels.replace("r_id","rId1").replace("PICTNAME",pictName));
        }
    }

    private static File createHeader(String path){
        File file = new File(path+File.separator+"header1.xml");
        try {
            file.createNewFile();
//            FileUtils.writeStringToFile(file,watermarkXml.replace("WordPictureWatermark","WordPictureWatermark"+new Date().getTime()%10000000)
//                    .replace("r_id","rId1"));
        }catch (IOException e) {
            log.info("文件创建失败:{}",file.getName());
        }
        return file;
    }

    private static List<File> createRels(String path,int fileCount){
        List<File> files= new ArrayList<File>();
        for (int i = 1; i <= fileCount; i++) {
            File file = new File(path+File.separator+"header"+i+".xml.rels");
            try {
                file.createNewFile();
                files.add(file);
//                FileUtils.writeStringToFile(file,watermarkRels.replace("r_id","rId1"));
            }catch (IOException e) {
                log.info("文件创建失败:{}",file.getName());
            }
        }
        return files;
    }

    //获取ASCII长度,统一中英文长度不一样的情况
    public static int getStringLength(String s){
        int length = 0;
        for(int i = 0; i < s.length(); i++)
        {
            int ascii = Character.codePointAt(s, i);
            if(ascii >= 0 && ascii <=255)
                length++;
            else
                length += 2;

        }
        return length;
    }

    /**
     * 计算Font文本样式的文字宽和高
     * @param text
     * @param font
     * @return
     */
    private static int[] getWidthAndHeight(String text, Font font) {
        Rectangle2D r = font.getStringBounds(text, new FontRenderContext(AffineTransform.getScaleInstance(1, 1), false, false));
        int unitHeight = (int) Math.floor(r.getHeight());//
        // 获取整个str用了font样式的宽度这里用四舍五入后+1保证宽度绝对能容纳这个字符串作为图片的宽度
        int width = (int) Math.round(r.getWidth()) + 1;
        //把单个字符的高度+3保证高度绝对能容纳字符串作为图片的高度
        int height = unitHeight + 3;
        return new int[]{width, height};
    }

    /**
     * 创建文字图片水印
     * @param text
     * @param font
     * @param outFile
     * @throws Exception
     */
    public static void createImage(String text, Font font, File outFile, float alpha) throws Exception {
        int[] arr = getWidthAndHeight(text, font);
        int width=800,height = 600;
        //创建图片
        BufferedImage image = new BufferedImage(width, height,
                BufferedImage.TYPE_INT_BGR);//创建图片画布

        Graphics2D g = (Graphics2D) image.getGraphics();
        //先用白色填充整张图片,也就是背景
        g.setColor(Color.WHITE);
        //画出矩形区域,以便于在矩形区域内写入文字
        g.fillRect(0, 0, width, height);
        //再换成黑色,以便于写入文字
        g.setColor(new Color(DEFAULTCOLOR));
        g.setFont(font);
        // 设置透明度:1.0f为透明度 ,值从0-1.0,依次变得不透明
        g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
//        g.shear(0,-0.3);
        g.rotate(Math.toRadians(-45));
        //画出一行字符串并使其画布居中
        g.drawString(text, (float) -(width-arr[0]-arr[1])/2, (float) ((Math.sqrt(2))*(height-arr[1])/2)+arr[1]*5/2);
        g.dispose();
        ImageIO.write(image, "png", outFile);//输出png图片

    }

    /**
     * 生成背景透明的 文字水印
     *
     * @param width     生成图片宽度
     * @param height    生成图片高度
     * @param text      水印文字
     * @param color     颜色对象
     * @param font      awt字体
     * @param degree    水印文字旋转角度
     * @param alpha     水印不透明度0f-1.0f
     */
    public static BufferedImage waterMarkByText(int width, int height, String text, Color color, Font font, Double degree, float alpha) {
        BufferedImage buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        // 得到画笔对象
        Graphics2D g2d = buffImg.createGraphics();
        // 增加下面的代码使得背景透明
        buffImg = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
        g2d.dispose();
        g2d = buffImg.createGraphics();

        // 设置对线段的锯齿状边缘处理
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);

        // 设置水印旋转
        if (null != degree) {
            //注意rotate函数参数theta,为弧度制,故需用Math.toRadians转换一下
            //以矩形区域中央为圆心旋转
            g2d.rotate(Math.toRadians(degree), (double) buffImg.getWidth() / 2, (double) buffImg.getHeight() / 2);
        }

        // 设置颜色
        g2d.setColor(color);

        // 设置 Font
        g2d.setFont(font);

        // 设置透明度:1.0f为透明度 ,值从0-1.0,依次变得不透明
        g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));

        // 获取真实宽度
        float realWidth = 22.5f*getStringLength(text);

        // 计算绘图偏移x、y,使得使得水印文字在图片中居中,x、y坐标是基于Graphics2D.rotate过后的坐标系
//        float x = 0.5f * width - 0.5f * fontSize * realWidth;
//        float y = 0.5f * height + 0.5f * fontSize;

        // 取绘制的字串宽度、高度中间点进行偏移,使得文字在图片坐标中居中
        for (float x = -1000,i = 0;x < width + 1400;x+=realWidth+300,i++){
            for (float y = -1000 + i*125;y < height + 1400; y+=250){//对半错列
                g2d.drawString(text, x, y);
            }
        }
        // 释放资源
        g2d.dispose();
        return buffImg;
    }


}

ZipUtils


import java.io.*;
import java.util.HashSet;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

public class ZipUtils {

    /**
     * 初始化压缩包信息并开始进行压缩
     *
     * @param inputFile  需要压缩的文件或文件夹
     * @param outputFile 压缩后的文件
     */
    public static void zip(File inputFile, File outputFile) {
        ZipOutputStream zos = null;
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(outputFile);
            zos = new ZipOutputStream(out);
            // 设置压缩包注释
//            zos.setComment("From Log");
            zipFile(zos, inputFile, null);
            System.err.println("压缩完成!");
        } catch (IOException e) {
            e.printStackTrace();
            System.err.println("压缩失败!");
        } finally {
            if (zos != null) {
                try {
                    zos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (out != null){
                try {
                    out.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 如果是单个文件,那么就直接进行压缩。如果是文件夹,那么递归压缩所有文件夹里的文件
     *
     * @param zos       压缩输出流
     * @param inputFile 需要压缩的文件
     * @param path      需要压缩的文件在压缩包里的路径
     */
    public static void zipFile(ZipOutputStream zos, File inputFile, String path) {
        if (inputFile.isDirectory()) {
            // 记录压缩包中文件的全路径
            String p = null;
            File[] fileList = inputFile.listFiles();
            for (int i = 0; i < fileList.length; i++) {
                File file = fileList[i];
                // 如果路径为空,说明是根目录
                if (path == null || path.isEmpty()) {
                    p = file.getName();
                } else {
                    p = path + "/" + file.getName();
                    // 打印路径
                }
                // 如果是目录递归调用,直到遇到文件为止
                zipFile(zos, file, p);
            }
        } else {
            zipSingleFile(zos, inputFile, path);
        }
    }

    /**
     * 压缩单个文件到指定压缩流里
     *
     * @param zos       压缩输出流
     * @param inputFile 需要压缩的文件
     * @param path      需要压缩的文件在压缩包里的路径
     * @throws FileNotFoundException
     */
    public static void zipSingleFile(ZipOutputStream zos, File inputFile, String path) {
        try {
            InputStream in = new FileInputStream(inputFile);
            zos.putNextEntry(new ZipEntry(path));
            write(in, zos);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * zip解压
     *
     * @param srcFile     zip源文件
     * @param destDirPath 解压后的目标文件夹
     * @throws RuntimeException 解压失败会抛出运行时异常
     */
    public static HashSet<String> unZip(File srcFile, String destDirPath) throws RuntimeException {
        File destDir = new File(destDirPath);
        if (!destDir.exists())
            destDir.mkdirs();
        HashSet<String> fileList = new HashSet<String>();
        // 判断源文件是否存在
        if (!srcFile.exists()) {
            throw new RuntimeException(srcFile.getPath() + "所指文件不存在");
        }
        // 开始解压
        ZipFile zipFile = null;
        //生成XML的参数
        try {
            zipFile = new ZipFile(srcFile);
            FileInputStream fis = new FileInputStream(srcFile);
            ZipInputStream zin = new ZipInputStream(fis);
            ZipEntry entry = null;
            while ((entry = zin.getNextEntry()) != null) {
                String fName1 = entry.getName();
                fName1 = fName1.replace("\\", "/");
                String fName = fName1.substring(fName1.lastIndexOf("/") + 1);
                if (fName != null && fName.trim().length() > 0) {
                    fileList.add(fName);
                }
                // 如果是文件夹,就创建个文件夹
                if (entry.isDirectory()) {
                    String dirPath = destDirPath + File.separator + fName1;
                    File dir = new File(dirPath);
                    dir.mkdirs();
                } else {
                    // 如果是文件,就先创建一个文件,然后用io流把内容copy过去
                    File targetFile = new File(destDirPath + File.separator + fName1);
                    // 保证这个文件的父文件夹必须要存在
                    if (!targetFile.getParentFile().exists()) {
                        targetFile.getParentFile().mkdirs();
                    }
                    targetFile.createNewFile();
                    // 将压缩文件内容写入到这个文件中
                    InputStream is = zipFile.getInputStream(entry);
                    FileOutputStream fos = new FileOutputStream(targetFile);
                    int len;
                    byte[] buf = new byte[1024];
                    while ((len = is.read(buf)) != -1) {
                        fos.write(buf, 0, len);
                    }
                    // 关流顺序,先打开的后关闭
                    fos.close();
                    is.close();
                }
            }
            fis.close();
        } catch (Exception e) {
            throw new RuntimeException("unzip error from ZipUtils", e);
        } finally {
            if (zipFile != null) {
                try {
                    zipFile.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return fileList;
    }

    /**
     * 从输入流写入到输出流的方便方法 【注意】这个函数只会关闭输入流,且读写完成后会调用输出流的flush()函数,但不会关闭输出流!
     *
     * @param input
     * @param output
     */
    private static void write(InputStream input, OutputStream output) {
        int len = -1;
        byte[] buff = new byte[1024];
        try {
            while ((len = input.read(buff)) != -1) {
                output.write(buff, 0, len);
            }
            output.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                input.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        File srcFile = new File("E:\\download\\test_after.zip");
        unZip(srcFile,srcFile.getParent()+"\\"+srcFile.getName().split("\\.")[0]);
    }
}
注:下文中的 *** 代表文件名中的版本号。 # 【poi-***.jar中文文档.zip】 中包含: 中文文档:【poi-***-javadoc-API文档-中文(简体)版.zip】 jar包下载地址:【poi-***.jar下载地址(官方地址+国内镜像地址).txt】 Maven依赖:【poi-***.jar Maven依赖信息(可用于项目pom.xml).txt】 Gradle依赖:【poi-***.jar Gradle依赖信息(可用于项目build.gradle).txt】 源代码下载地址:【poi-***-sources.jar下载地址(官方地址+国内镜像地址).txt】 # 本文件关键字: poi-***.jar中文文档.zip,java,poi-***.jar,org.apache.poi,poi,***,jar包,Maven,第三方jar包,组件,开源组件,第三方组件,Gradle,apache,中文API文档,手册,开发手册,使用手册,参考手册 # 使用方法: 解压 【poi-***.jar中文文档.zip】,再解压其中的 【poi-***-javadoc-API文档-中文(简体)版.zip】,双击 【index.html】 文件,即可用浏览器打开、进行查看。 # 特殊说明: ·本文档为人性化翻译,精心制作,请放心使用。 ·只翻译了该翻译的内容,如:注释、说明、描述、用法讲解 等; ·不该翻译的内容保持原样,如:类名、方法名、包名、类型、关键字、代码 等。 # 温馨提示: (1)为了防止解压后路径太长导致浏览器无法打开,推荐在解压时选择“解压到当前文件夹”(放心,自带文件夹,文件不会散落一地); (2)有时,一套Java组件会有多个jar,所以在下载前,请仔细阅读本篇描述,以确保这就是你需要的文件; # Maven依赖: ``` <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>***</version> </dependency> ``` # Gradle依赖: ``` Gradle: implementation group: 'org.apache.poi', name: 'poi', version: '***' Gradle (Short): implementation 'org.apache.poi:poi:***' Gradle (Kotlin): implementation("org.apache.poi:poi:***") ``` # 含有的 Java package(包)(此处仅列举3个): ``` org.apache.poi org.apache.poi.common org.apache.poi.common.usermodel ...... ``` # 含有的 Java class(类)(此处仅列举3个): ``` org.apache.poi.EmptyFileException org.apache.poi.EncryptedDocumentException org.apache.poi.OldFileFormatException ...... ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值